Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 16 additions & 8 deletions backend/utils/populate.go → backend/cmd/seeder/main.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package utils
package main

import (
"context"
"log"
"time"

"arguehub/config"
Expand All @@ -12,23 +13,29 @@ import (
"go.mongodb.org/mongo-driver/bson"
)

// PopulateTestUsers creates test users with Glicko-2 ratings
func PopulateTestUsers() {
cfg, err := config.LoadConfig("./config/config.prod.yml")
func main() {
cfg, err := config.LoadConfig("../../config/config.prod.yml")
if err != nil {
panic("Failed to load config: " + err.Error())
log.Fatal("Failed to load config: " + err.Error())
}

// Initialize DB
err = db.ConnectMongoDB(cfg.Database.URI)
if err != nil {
log.Fatal("Failed to connect to DB: " + err.Error())
}

collection := db.MongoDatabase.Collection("users")
count, _ := collection.CountDocuments(context.Background(), bson.M{})

if count > 0 {
log.Println("Users already exist, skipping seed.")
return
}
Comment on lines 28 to 34
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Ignored error from CountDocuments can cause unexpected behavior.

If CountDocuments fails (e.g., network issue, permissions), count defaults to 0 and the seeder proceeds to insert, potentially causing duplicate key errors or masking the real failure.

🐛 Proposed fix
 	collection := db.MongoDatabase.Collection("users")
-	count, _ := collection.CountDocuments(context.Background(), bson.M{})
+	count, err := collection.CountDocuments(context.Background(), bson.M{})
+	if err != nil {
+		log.Fatal("Failed to count users: " + err.Error())
+	}
 
 	if count > 0 {
 		log.Println("Users already exist, skipping seed.")
 		return
 	}
🤖 Prompt for AI Agents
In `@backend/cmd/seeder/main.go` around lines 28 - 34, The call to
collection.CountDocuments in main.go ignores its returned error which can make
the seeder proceed on failures; update the seeding logic around the
CountDocuments call (the variable named collection and the
CountDocuments(context.Background(), bson.M{}) invocation) to capture the
returned error, check if err != nil, and on error log the detailed error (via
log.Printf or similar) and abort/return instead of continuing; only proceed to
check count and seed when err is nil.


// Initialize rating system (no return value)
// Initialize rating system
services.InitRatingService(cfg)
ratingSystem := services.GetRatingSystem() // <- add a getter in services package
ratingSystem := services.GetRatingSystem()

testUsers := []models.User{
{
Expand Down Expand Up @@ -58,6 +65,7 @@ func PopulateTestUsers() {

_, err = collection.InsertMany(context.Background(), documents)
if err != nil {
return
log.Fatal("Failed to seed users: " + err.Error())
}
log.Println("Seeded users successfully.")
}
104 changes: 2 additions & 102 deletions backend/cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ import (
"arguehub/services"
"arguehub/utils"
"arguehub/websocket"

"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
)

func main() {
Expand Down Expand Up @@ -64,113 +61,16 @@ func main() {

// Seed initial debate-related data
utils.SeedDebateData()
utils.PopulateTestUsers()


// Create uploads directory
os.MkdirAll("uploads", os.ModePerm)

// Set up the Gin router and configure routes
router := setupRouter(cfg)
router := routes.SetupRouter(cfg)
port := strconv.Itoa(cfg.Server.Port)

if err := router.Run(":" + port); err != nil {
panic("Failed to start server: " + err.Error())
}
}

func setupRouter(cfg *config.Config) *gin.Engine {
router := gin.Default()

// Set trusted proxies (adjust as needed)
router.SetTrustedProxies([]string{"127.0.0.1", "localhost"})

// Configure CORS for your frontend (e.g., localhost:5173 for Vite)
router.Use(cors.New(cors.Config{
AllowOrigins: []string{"http://localhost:5173"},
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowHeaders: []string{"Origin", "Content-Type", "Authorization"},
ExposeHeaders: []string{"Content-Length"},
AllowCredentials: true,
}))
router.OPTIONS("/*path", func(c *gin.Context) { c.Status(204) })

// Public routes for authentication
router.POST("/signup", routes.SignUpRouteHandler)
router.POST("/verifyEmail", routes.VerifyEmailRouteHandler)
router.POST("/login", routes.LoginRouteHandler)
router.POST("/googleLogin", routes.GoogleLoginRouteHandler)
router.POST("/forgotPassword", routes.ForgotPasswordRouteHandler)
router.POST("/confirmForgotPassword", routes.VerifyForgotPasswordRouteHandler)
router.POST("/verifyToken", routes.VerifyTokenRouteHandler)

// Debug endpoint for matchmaking pool status
router.GET("/debug/matchmaking-pool", routes.GetMatchmakingPoolStatusHandler)

// WebSocket routes (handle auth internally)
router.GET("/ws/matchmaking", websocket.MatchmakingHandler)
router.GET("/ws/gamification", websocket.GamificationWebSocketHandler)

// Protected routes (JWT auth)
auth := router.Group("/")
auth.Use(middlewares.AuthMiddleware("./config/config.prod.yml"))
{
auth.GET("/user/fetchprofile", routes.GetProfileRouteHandler)
auth.PUT("/user/updateprofile", routes.UpdateProfileRouteHandler)
auth.GET("/leaderboard", routes.GetLeaderboardRouteHandler)
auth.POST("/debate/result", routes.UpdateRatingAfterDebateRouteHandler)

// Gamification routes
auth.POST("/api/award-badge", routes.AwardBadgeRouteHandler)
auth.POST("/api/update-score", routes.UpdateScoreRouteHandler)
auth.GET("/api/leaderboard", routes.GetGamificationLeaderboardRouteHandler)

routes.SetupDebateVsBotRoutes(auth)

// WebSocket signaling endpoint (handles auth internally)
router.GET("/ws", websocket.WebsocketHandler)

// Set up transcript routes
routes.SetupTranscriptRoutes(auth)

auth.GET("/coach/strengthen-argument/weak-statement", routes.GetWeakStatement)
auth.POST("/coach/strengthen-argument/evaluate", routes.EvaluateStrengthenedArgument)

// Add Room routes.
auth.GET("/rooms", routes.GetRoomsHandler)
auth.POST("/rooms", routes.CreateRoomHandler)
auth.POST("/rooms/:id/join", routes.JoinRoomHandler)
auth.GET("/rooms/:id/participants", routes.GetRoomParticipantsHandler)

// Chat functionality is now handled by the main WebSocket handler

// Team routes
routes.SetupTeamRoutes(auth)
routes.SetupTeamDebateRoutes(auth)
routes.SetupTeamChatRoutes(auth)
routes.SetupTeamMatchmakingRoutes(auth)
log.Println("Team routes registered")

// Community routes
routes.SetupCommunityRoutes(auth)
log.Println("Community routes registered")

// Notification routes
auth.GET("/notifications", routes.GetNotificationsRouteHandler)
auth.PUT("/notifications/:id/read", routes.MarkNotificationAsReadRouteHandler)
auth.PUT("/notifications/read-all", routes.MarkAllNotificationsAsReadRouteHandler)
auth.DELETE("/notifications/:id", routes.DeleteNotificationRouteHandler)
}

// Team WebSocket handler
router.GET("/ws/team", websocket.TeamWebsocketHandler)

// Admin routes
routes.SetupAdminRoutes(router, "./config/config.prod.yml")
log.Println("Admin routes registered")

// Debate spectator WebSocket handler (no auth required for anonymous spectators)
// FIX: Use websocket.DebateWebsocketHandler (moved to websocket package)
router.GET("/ws/debate/:debateID", websocket.DebateWebsocketHandler)

return router
}
Loading