Skip to content

Commit

Permalink
adding api
Browse files Browse the repository at this point in the history
this adds the preliminary rough draft for the third party client api, solves #8
  • Loading branch information
donuts-are-good committed Apr 21, 2023
1 parent 05dd863 commit 5770afc
Showing 1 changed file with 243 additions and 0 deletions.
243 changes: 243 additions & 0 deletions api.go
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)
}

0 comments on commit 5770afc

Please sign in to comment.