summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/cmd/main.go68
-rw-r--r--app/go.mod1
-rw-r--r--app/go.sum2
-rw-r--r--app/internal/middleware.go67
-rw-r--r--app/internal/utils/json.go22
-rw-r--r--devenv.lock4
6 files changed, 129 insertions, 35 deletions
diff --git a/app/cmd/main.go b/app/cmd/main.go
index 3c1e9e2..95dab38 100644
--- a/app/cmd/main.go
+++ b/app/cmd/main.go
@@ -13,6 +13,7 @@ import (
"github.com/arangodb/go-driver/v2/arangodb"
"github.com/arangodb/go-driver/v2/connection"
"github.com/ketsuna-org/bot-creator-api/internal"
+ "github.com/ketsuna-org/bot-creator-api/internal/utils"
)
var botList = make(map[string]*internal.Bot)
@@ -61,6 +62,7 @@ func main() {
})
// Route POST /create/{bot_token}
+ //![TODO] : Every bot_token will be replaced by the bot id. to be compliant with Discord TOS, and Token will be send as a secured Header.
mux.HandleFunc("POST /create/{bot_token}", func(w http.ResponseWriter, r *http.Request) {
// Extraire le token du bot de l'URL
botToken := r.PathValue("bot_token")
@@ -76,7 +78,7 @@ func main() {
col, err := database.GetCollection(context.Background(), "bots", nil)
if err != nil {
log.Printf("[SERVER] Error getting collection: %v", err)
- http.Error(w, "Error getting collection", http.StatusInternalServerError)
+ utils.RespondWithError(w, http.StatusInternalServerError, "Error getting collection")
return
}
// let's check if the bot already exists
@@ -84,7 +86,7 @@ func main() {
// Let's check if this discord bot exists
if _, ok := botList[botToken]; ok {
log.Printf("[SERVER] Bot already running: %s", botToken)
- http.Error(w, "Bot already running", http.StatusConflict)
+ utils.RespondWithError(w, http.StatusConflict, "Bot already running")
return
}
@@ -93,27 +95,27 @@ func main() {
req, err := http.NewRequest("GET", "https://discord.com/api/v10/users/@me", nil)
if err != nil {
log.Printf("[SERVER] Error creating request: %v", err)
- http.Error(w, "Error creating request", http.StatusInternalServerError)
+ utils.RespondWithError(w, http.StatusInternalServerError, "Error creating request")
return
}
req.Header.Set("Authorization", "Bot "+botToken)
resp, err := httpClient.Do(req)
if err != nil {
log.Printf("[SERVER] Error sending request: %v", err)
- http.Error(w, "Error sending request", http.StatusInternalServerError)
+ utils.RespondWithError(w, http.StatusInternalServerError, "Error sending request")
return
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
log.Printf("[SERVER] Bot not found: %s", botToken)
- http.Error(w, "Bot not found", http.StatusNotFound)
+ utils.RespondWithError(w, http.StatusNotFound, "Bot not found")
return
}
var botData map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&botData); err != nil {
log.Printf("[SERVER] Error decoding JSON: %v", err)
- http.Error(w, "Error decoding JSON", http.StatusInternalServerError)
+ utils.RespondWithError(w, http.StatusBadRequest, "Invalid JSON")
return
}
@@ -123,7 +125,7 @@ func main() {
exist, err := col.DocumentExists(context.Background(), id)
if err != nil {
log.Printf("[SERVER] Error checking document: %v", err)
- http.Error(w, "Error checking document", http.StatusInternalServerError)
+ utils.RespondWithError(w, http.StatusInternalServerError, "Error checking document")
return
}
@@ -131,7 +133,7 @@ func main() {
_, err = col.CreateDocument(context.Background(), botData)
if err != nil {
log.Printf("[SERVER] Error creating bot: %v", err)
- http.Error(w, "Error creating document", http.StatusInternalServerError)
+ utils.RespondWithError(w, http.StatusInternalServerError, "Error creating bot")
return
}
log.Printf("[SERVER] Bot created: %s", botToken)
@@ -140,7 +142,7 @@ func main() {
_, err = col.UpdateDocument(context.Background(), botData["id"].(string), botData)
if err != nil {
log.Printf("[SERVER] Error updating document: %v", err)
- http.Error(w, "Error updating document", http.StatusInternalServerError)
+ utils.RespondWithError(w, http.StatusInternalServerError, "Error updating document")
return
}
log.Printf("[SERVER] Bot updated: %s", botToken)
@@ -151,7 +153,7 @@ func main() {
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
log.Printf("[SERVER] Error decoding JSON: %v", err)
- http.Error(w, "Invalid JSON", http.StatusBadRequest)
+ utils.RespondWithError(w, http.StatusBadRequest, "Invalid JSON")
return
}
@@ -169,7 +171,7 @@ func main() {
dataToUse, ok := body["data"].(map[string]interface{})
if !ok {
log.Printf("[SERVER] Data is not a map")
- http.Error(w, "Data is not a map", http.StatusBadRequest)
+ utils.RespondWithError(w, http.StatusBadRequest, "Data is not a map")
return
}
// let's check if the data is a map
@@ -179,7 +181,7 @@ func main() {
valueToUse, ok := value.(map[string]interface{})
if !ok {
log.Printf("[SERVER] Value is not a map")
- http.Error(w, "Value is not a map", http.StatusBadRequest)
+ utils.RespondWithError(w, http.StatusBadRequest, "Value is not a map")
return
}
@@ -189,7 +191,7 @@ func main() {
// let's check if the name is set
if err != nil {
log.Printf("[SERVER] Error getting collection: %v", err)
- http.Error(w, "Error getting collection", http.StatusInternalServerError)
+ utils.RespondWithError(w, http.StatusInternalServerError, "Error getting collection")
return
}
@@ -197,7 +199,7 @@ func main() {
exist, err := col.DocumentExists(context.Background(), key)
if err != nil {
log.Printf("[SERVER] Error checking document: %v", err)
- http.Error(w, "Error checking document", http.StatusInternalServerError)
+ utils.RespondWithError(w, http.StatusInternalServerError, "Error checking document")
return
}
if !exist {
@@ -206,7 +208,7 @@ func main() {
_, err = col.CreateDocument(context.Background(), valueToUse)
if err != nil {
log.Printf("[SERVER] Error creating document: %v", err)
- http.Error(w, "Error creating document", http.StatusInternalServerError)
+ utils.RespondWithError(w, http.StatusInternalServerError, "Error creating document")
return
}
log.Printf("[SERVER] Command created: %s", key)
@@ -215,7 +217,7 @@ func main() {
_, err = col.UpdateDocument(context.Background(), key, valueToUse)
if err != nil {
log.Printf("[SERVER] Error updating document: %v", err)
- http.Error(w, "Error updating document", http.StatusInternalServerError)
+ utils.RespondWithError(w, http.StatusInternalServerError, "Error updating document")
return
}
log.Printf("[SERVER] Command updated: %s", key)
@@ -225,7 +227,7 @@ func main() {
edgeCol, err := database.GetCollection(context.Background(), "bots_commands", nil)
if err != nil {
log.Printf("[SERVER] Error getting collection: %v", err)
- http.Error(w, "Error getting collection", http.StatusInternalServerError)
+ utils.RespondWithError(w, http.StatusInternalServerError, "Error getting collection")
return
}
// let's check if the edge already exists
@@ -246,7 +248,7 @@ func main() {
_, err = edgeCol.CreateDocument(context.Background(), edge)
if err != nil {
log.Printf("[SERVER] Error creating document: %v", err)
- http.Error(w, "Error creating document", http.StatusInternalServerError)
+ utils.RespondWithError(w, http.StatusInternalServerError, "Error creating document")
return
}
log.Printf("[SERVER] Edge created: %s", edgeID)
@@ -259,7 +261,7 @@ func main() {
_, err = edgeCol.UpdateDocument(context.Background(), edgeID, edge)
if err != nil {
log.Printf("[SERVER] Error updating document: %v", err)
- http.Error(w, "Error updating document", http.StatusInternalServerError)
+ utils.RespondWithError(w, http.StatusInternalServerError, "Error updating document")
return
}
log.Printf("[SERVER] Edge updated: %s", edgeID)
@@ -271,7 +273,7 @@ func main() {
data, err := json.Marshal(body["data"])
if err != nil {
log.Printf("[SERVER] Error marshaling JSON: %v", err)
- http.Error(w, "Error marshaling JSON", http.StatusInternalServerError)
+ utils.RespondWithError(w, http.StatusInternalServerError, "Error marshaling JSON")
return
}
@@ -280,65 +282,65 @@ func main() {
bot, err = internal.Start(bot, string(data), fmt.Sprint(body["intents"]))
if err != nil {
log.Printf("[SERVER] Error starting bot: %v", err)
- http.Error(w, "Error starting bot", http.StatusInternalServerError)
+ utils.RespondWithError(w, http.StatusInternalServerError, "Error starting bot")
return
}
botList[botToken] = bot
log.Printf("[SERVER] Bot started successfully")
- w.WriteHeader(http.StatusOK)
- w.Write([]byte("Bot started successfully"))
+ utils.RespondWithJSON(w, bot, http.StatusOK)
})
// Route POST /stop/{bot_token}
+ //![TODO] : Every bot_token will be replaced by the bot id. to be compliant with Discord TOS, and Token will be send as a secured Header.
mux.HandleFunc("POST /stop/{bot_token}", func(w http.ResponseWriter, r *http.Request) {
// Extraire le token du bot de l'URL
botToken := r.PathValue("bot_token")
bot, ok := botList[botToken]
if !ok {
- http.Error(w, "Bot not found", http.StatusNotFound)
+ log.Printf("[SERVER] Bot not found: %s", botToken)
+ utils.RespondWithError(w, http.StatusNotFound, "Bot not found")
return
}
if err := bot.Stop(); err != nil {
log.Printf("[SERVER] Error stopping bot: %v", err)
- http.Error(w, "Error stopping bot", http.StatusInternalServerError)
+ utils.RespondWithError(w, http.StatusInternalServerError, "Error stopping bot")
return
}
delete(botList, botToken)
log.Printf("[SERVER] Bot stopped successfully")
- w.WriteHeader(http.StatusOK)
- w.Write([]byte("Bot stopped successfully"))
+ utils.RespondWithJSON(w, bot, http.StatusOK)
})
// Route POST /update/{bot_token}
+ //![TODO] : Every bot_token will be replaced by the bot id. to be compliant with Discord TOS, and Token will be send as a secured Header.
mux.HandleFunc("POST /update/{bot_token}", func(w http.ResponseWriter, r *http.Request) {
// Extraire le token du bot de l'URL
botToken := r.PathValue("bot_token")
bot, ok := botList[botToken]
if !ok {
- http.Error(w, "Bot not found", http.StatusNotFound)
+ utils.RespondWithError(w, http.StatusNotFound, "Bot not found")
return
}
body := make(map[string]interface{})
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
log.Printf("[SERVER] Error decoding JSON: %v", err)
- http.Error(w, "Invalid JSON", http.StatusBadRequest)
+ utils.RespondWithError(w, http.StatusBadRequest, "Invalid JSON")
return
}
data, err := json.Marshal(body)
if err != nil {
log.Printf("[SERVER] Error marshaling JSON: %v", err)
- http.Error(w, "Error marshaling JSON", http.StatusInternalServerError)
+ utils.RespondWithError(w, http.StatusInternalServerError, "Error marshaling JSON")
return
}
if err := bot.SendMessage(string(data)); err != nil {
log.Printf("[SERVER] Error sending message: %v", err)
- http.Error(w, "Error sending message", http.StatusInternalServerError)
+ utils.RespondWithError(w, http.StatusInternalServerError, "Error sending message")
return
}
log.Printf("[SERVER] Bot updated successfully")
- w.WriteHeader(http.StatusOK)
- w.Write([]byte("Bot updated successfully"))
+ utils.RespondWithJSON(w, bot, http.StatusOK)
})
// Gestion des signaux pour l'arrĂȘt propre
diff --git a/app/go.mod b/app/go.mod
index 2e86acd..f2620e2 100644
--- a/app/go.mod
+++ b/app/go.mod
@@ -6,6 +6,7 @@ require (
github.com/arangodb/go-driver/v2 v2.1.3 // indirect
github.com/arangodb/go-velocypack v0.0.0-20200318135517-5af53c29c67e // indirect
github.com/dchest/siphash v1.2.3 // indirect
+ github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/kkdai/maglev v0.2.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
diff --git a/app/go.sum b/app/go.sum
index b206855..cd33dc3 100644
--- a/app/go.sum
+++ b/app/go.sum
@@ -8,6 +8,8 @@ github.com/dchest/siphash v1.2.2/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBl
github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA=
github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
+github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/kkdai/maglev v0.2.0 h1:w6DCW0kAA6fstZqXkrBrlgIC3jeIRXkjOYea/m6EK/Y=
diff --git a/app/internal/middleware.go b/app/internal/middleware.go
new file mode 100644
index 0000000..2af90c8
--- /dev/null
+++ b/app/internal/middleware.go
@@ -0,0 +1,67 @@
+package internal
+
+import (
+ "log"
+ "net/http"
+ "os"
+ "time"
+
+ "github.com/golang-jwt/jwt/v5"
+ "github.com/ketsuna-org/bot-creator-api/internal/utils"
+)
+
+func LogMiddleware(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ start := time.Now()
+ next.ServeHTTP(w, r)
+ duration := time.Since(start)
+
+ log.Printf("Request: %s %s took %f seconds", r.Method, r.URL.Path, duration.Seconds())
+ })
+}
+
+/**
+ * AuthMiddleware is a placeholder for authentication middleware.
+ * In a real application, this would check for valid authentication tokens or sessions.
+ * For now, it just calls the next handler.
+ */
+func AuthMiddleware(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ authHeader := r.Header.Get("Authorization")
+ if authHeader == "" {
+ utils.RespondWithError(w, http.StatusUnauthorized, "Missing authorization header")
+ return
+ }
+ tokenString := authHeader[len("Bearer "):]
+ token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
+ // Validate the algorithm
+ if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
+ return nil, http.ErrNotSupported
+ }
+ // Return the secret key for validation
+ return []byte(os.Getenv("JWT_SECRET")), nil
+ })
+ if err != nil {
+ utils.RespondWithError(w, http.StatusUnauthorized, "Invalid token")
+ return
+ }
+ if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
+ // You can access claims here
+ log.Printf("User ID: %v", claims["user_id"])
+ } else {
+ utils.RespondWithError(w, http.StatusUnauthorized, "Invalid token claims")
+ return
+ }
+
+ next.ServeHTTP(w, r)
+ })
+}
+
+func Chain(middlewares ...func(http.Handler) http.Handler) func(http.Handler) http.Handler {
+ return func(next http.Handler) http.Handler {
+ for _, middleware := range middlewares {
+ next = middleware(next)
+ }
+ return next
+ }
+}
diff --git a/app/internal/utils/json.go b/app/internal/utils/json.go
new file mode 100644
index 0000000..d3c6b71
--- /dev/null
+++ b/app/internal/utils/json.go
@@ -0,0 +1,22 @@
+package utils
+
+import (
+ "encoding/json"
+ "net/http"
+)
+
+func RespondWithError(w http.ResponseWriter, code int, message string) {
+ RespondWithJSON(w, map[string]string{"error": message}, code)
+}
+
+func RespondWithJSON(w http.ResponseWriter, payload interface{}, code ...int) {
+ var c int = http.StatusOK
+ if len(code) > 0 {
+ c = code[0]
+ }
+ response, _ := json.Marshal(payload)
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(c)
+ w.Write(response)
+}
diff --git a/devenv.lock b/devenv.lock
index f92d9c0..b772a34 100644
--- a/devenv.lock
+++ b/devenv.lock
@@ -3,10 +3,10 @@
"devenv": {
"locked": {
"dir": "src/modules",
- "lastModified": 1745750098,
+ "lastModified": 1746276326,
"owner": "cachix",
"repo": "devenv",
- "rev": "312f0eb723d9b2a1539f175bca9f11f1e4f0a673",
+ "rev": "0133867ecb206f1d9cedc3c5872d2a4eb2d8ff29",
"type": "github"
},
"original": {