-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
this adds the preliminary rough draft for the third party client api, solves #8
- Loading branch information
1 parent
05dd863
commit 5770afc
Showing
1 changed file
with
243 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,243 @@ | ||
package main | ||
|
||
import ( | ||
"crypto/rand" | ||
"encoding/base32" | ||
"encoding/json" | ||
"fmt" | ||
"log" | ||
"net/http" | ||
"strconv" | ||
"strings" | ||
"time" | ||
|
||
"github.com/jmoiron/sqlx" | ||
"golang.org/x/term" | ||
) | ||
|
||
type APIResponse struct { | ||
Success bool `json:"success"` | ||
Data interface{} `json:"data,omitempty"` | ||
Error string `json:"error,omitempty"` | ||
} | ||
|
||
func initAPISchema(db *sqlx.DB) { | ||
schema := ` | ||
CREATE TABLE IF NOT EXISTS auth_tokens ( | ||
id INTEGER PRIMARY KEY, | ||
user_hash TEXT NOT NULL, | ||
token TEXT NOT NULL UNIQUE, | ||
created_at TIMESTAMP NOT NULL | ||
); | ||
` | ||
_, err := db.Exec(schema) | ||
if err != nil { | ||
log.Fatalln(err) | ||
} | ||
} | ||
|
||
func api(db *sqlx.DB) { | ||
initAPISchema(db) | ||
|
||
http.HandleFunc("/chat/messages", chatMessagesHandler) | ||
http.HandleFunc("/chat/create", chatCreateHandler) | ||
http.HandleFunc("/chat/direct/create", directMessageHandler) | ||
|
||
http.HandleFunc("/posts/list", func(w http.ResponseWriter, r *http.Request) { | ||
postsListHandler(w, r, db) | ||
}) | ||
|
||
http.HandleFunc("/posts/replies", func(w http.ResponseWriter, r *http.Request) { | ||
repliesListHandler(w, r, db) | ||
}) | ||
|
||
http.HandleFunc("/posts/reply", func(w http.ResponseWriter, r *http.Request) { | ||
replyCreateHandler(w, r, db) | ||
}) | ||
|
||
http.ListenAndServe(":8080", nil) | ||
} | ||
|
||
func chatMessagesHandler(w http.ResponseWriter, r *http.Request) { | ||
messages := getLast100ChatMessages() | ||
resp := APIResponse{Success: true, Data: messages} | ||
json.NewEncoder(w).Encode(resp) | ||
} | ||
|
||
func chatCreateHandler(w http.ResponseWriter, r *http.Request) { | ||
senderHash := r.FormValue("sender_hash") | ||
message := r.FormValue("message") | ||
if err := createChatMessage(senderHash, message); err == nil { | ||
json.NewEncoder(w).Encode(APIResponse{Success: true}) | ||
} else { | ||
json.NewEncoder(w).Encode(APIResponse{Success: false, Error: err.Error()}) | ||
} | ||
} | ||
|
||
func directMessageHandler(w http.ResponseWriter, r *http.Request) { | ||
senderHash := r.FormValue("sender") | ||
recipientHash := r.FormValue("recipient") | ||
message := r.FormValue("message") | ||
err := createDirectMessage(senderHash, recipientHash, message) | ||
if err == nil { | ||
json.NewEncoder(w).Encode(APIResponse{Success: true}) | ||
} else { | ||
json.NewEncoder(w).Encode(APIResponse{Success: false, Error: err.Error()}) | ||
} | ||
} | ||
|
||
func postsListHandler(w http.ResponseWriter, r *http.Request, db *sqlx.DB) { | ||
posts := listPosts(db) | ||
resp := APIResponse{Success: true, Data: posts} | ||
json.NewEncoder(w).Encode(resp) | ||
} | ||
|
||
func repliesListHandler(w http.ResponseWriter, r *http.Request, db *sqlx.DB) { | ||
postID, _ := strconv.Atoi(r.FormValue("post_id")) | ||
replies := getReplies(db, postID) | ||
resp := APIResponse{Success: true, Data: replies} | ||
json.NewEncoder(w).Encode(resp) | ||
} | ||
|
||
func replyCreateHandler(w http.ResponseWriter, r *http.Request, db *sqlx.DB) { | ||
postID, _ := strconv.Atoi(r.FormValue("post_id")) | ||
authorHash := r.FormValue("author_hash") | ||
replyBody := r.FormValue("reply") | ||
if err := createReply(db, postID, authorHash, replyBody); err == nil { | ||
json.NewEncoder(w).Encode(APIResponse{Success: true}) | ||
} else { | ||
json.NewEncoder(w).Encode(APIResponse{Success: false, Error: err.Error()}) | ||
} | ||
} | ||
|
||
func createChatMessage(senderHash, message string) error { | ||
broadcast(senderHash, message) | ||
return nil | ||
} | ||
|
||
func listPosts(db *sqlx.DB) []discussion { | ||
var posts []discussion | ||
err := db.Select(&posts, ` | ||
SELECT id, author, message | ||
FROM discussions | ||
ORDER BY id DESC | ||
`) | ||
if err != nil { | ||
log.Printf("Error retrieving posts: %v", err) | ||
return nil | ||
} | ||
return posts | ||
} | ||
|
||
func getReplies(db *sqlx.DB, postID int) []*reply { | ||
var replies []*reply | ||
err := db.Select(&replies, "SELECT author, message FROM replies WHERE discussion_id = ?", postID) | ||
if err != nil { | ||
log.Printf("Error retrieving replies: %v", err) | ||
return nil | ||
} | ||
return replies | ||
} | ||
func getLast100ChatMessages() []string { | ||
var messages []string | ||
for e := messageCache.Front(); e != nil; e = e.Next() { | ||
messages = append(messages, e.Value.(string)) | ||
} | ||
return messages | ||
} | ||
|
||
func createDirectMessage(senderHash, recipientHash, message string) error { | ||
usersMutex.Lock() | ||
defer usersMutex.Unlock() | ||
recipient, ok := users[recipientHash] | ||
if !ok { | ||
return fmt.Errorf("user not found") | ||
} | ||
if recipient.Conn == nil { | ||
return fmt.Errorf("user connection is not available") | ||
} | ||
formattedMessage := fmt.Sprintf("[DM from %s] %s\n", senderHash, message) | ||
fmt.Fprintln(recipient.Conn, formattedMessage) | ||
return nil | ||
} | ||
func handleTokenNew(db *sqlx.DB, term *term.Terminal, userHash string) { | ||
token, err := createToken(db, userHash) | ||
if err != nil { | ||
term.Write([]byte("Error generating token: " + err.Error() + "\n")) | ||
} else { | ||
term.Write([]byte("New token created: " + token + "\n")) | ||
} | ||
} | ||
|
||
func handleTokenList(db *sqlx.DB, term *term.Terminal, userHash string) { | ||
tokens, err := listTokens(db, userHash) | ||
if err != nil { | ||
term.Write([]byte("Error listing tokens: " + err.Error() + "\n")) | ||
} else { | ||
term.Write([]byte("Your tokens:\n")) | ||
for _, token := range tokens { | ||
term.Write([]byte(" - " + token + "\n")) | ||
} | ||
} | ||
} | ||
|
||
func handleTokenRevoke(db *sqlx.DB, input string, term *term.Terminal, userHash string) { | ||
parts := strings.Split(input, " ") | ||
if len(parts) < 3 { | ||
term.Write([]byte("Usage: /tokens revoke <token>\n")) | ||
} else { | ||
token := parts[2] | ||
err := revokeToken(db, userHash, token) | ||
if err != nil { | ||
term.Write([]byte("Error revoking token: " + err.Error() + "\n")) | ||
} else { | ||
term.Write([]byte("Token revoked successfully.\n")) | ||
} | ||
} | ||
} | ||
|
||
func createToken(db *sqlx.DB, userHash string) (string, error) { | ||
token := generateRandomToken() // Implement a function to generate a unique random token | ||
_, err := db.Exec("INSERT INTO auth_tokens (user_hash, token, created_at) VALUES (?, ?, ?)", userHash, token, time.Now()) | ||
if err != nil { | ||
return "", err | ||
} | ||
return token, nil | ||
} | ||
|
||
func listTokens(db *sqlx.DB, userHash string) ([]string, error) { | ||
var tokens []string | ||
err := db.Select(&tokens, "SELECT token FROM auth_tokens WHERE user_hash = ?", userHash) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return tokens, nil | ||
} | ||
|
||
func revokeToken(db *sqlx.DB, userHash, token string) error { | ||
result, err := db.Exec("DELETE FROM auth_tokens WHERE user_hash = ? AND token = ?", userHash, token) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
rowsAffected, err := result.RowsAffected() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if rowsAffected == 0 { | ||
return fmt.Errorf("token not found or not owned by the user") | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func generateRandomToken() string { | ||
token := make([]byte, 20) | ||
_, err := rand.Read(token) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
return base32.StdEncoding.EncodeToString(token) | ||
} |