ALL 0.1.0 Code
This commit is contained in:
246
server/cmd/api/main.go
Normal file
246
server/cmd/api/main.go
Normal file
@@ -0,0 +1,246 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/fpmb/server/internal/database"
|
||||
"github.com/fpmb/server/internal/handlers"
|
||||
"github.com/fpmb/server/internal/middleware"
|
||||
"github.com/fpmb/server/internal/models"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||
"github.com/gofiber/fiber/v2/middleware/logger"
|
||||
"github.com/gofiber/websocket/v2"
|
||||
"github.com/joho/godotenv"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
func startDueDateReminder() {
|
||||
ticker := time.NewTicker(1 * time.Hour)
|
||||
go func() {
|
||||
runDueDateReminder()
|
||||
for range ticker.C {
|
||||
runDueDateReminder()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func runDueDateReminder() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
now := time.Now()
|
||||
thresholds := []int{1, 3}
|
||||
|
||||
for _, days := range thresholds {
|
||||
windowStart := now.Add(time.Duration(days)*24*time.Hour - 30*time.Minute)
|
||||
windowEnd := now.Add(time.Duration(days)*24*time.Hour + 30*time.Minute)
|
||||
|
||||
cursor, err := database.GetCollection("cards").Find(ctx, bson.M{
|
||||
"due_date": bson.M{"$gte": windowStart, "$lte": windowEnd},
|
||||
})
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var cards []models.Card
|
||||
cursor.All(ctx, &cards)
|
||||
cursor.Close(ctx)
|
||||
|
||||
for _, card := range cards {
|
||||
for _, email := range card.Assignees {
|
||||
var user models.User
|
||||
if err := database.GetCollection("users").FindOne(ctx, bson.M{"email": email}).Decode(&user); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
cutoff := now.Add(-24 * time.Hour)
|
||||
count, _ := database.GetCollection("notifications").CountDocuments(ctx, bson.M{
|
||||
"user_id": user.ID,
|
||||
"type": "due_soon",
|
||||
"card_id": card.ID,
|
||||
"created_at": bson.M{"$gte": cutoff},
|
||||
})
|
||||
if count > 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
msg := fmt.Sprintf("Task \"%s\" is due in %d day(s)", card.Title, days)
|
||||
n := &models.Notification{
|
||||
ID: primitive.NewObjectID(),
|
||||
UserID: user.ID,
|
||||
Type: "due_soon",
|
||||
Message: msg,
|
||||
ProjectID: card.ProjectID,
|
||||
CardID: card.ID,
|
||||
Read: false,
|
||||
CreatedAt: now,
|
||||
}
|
||||
database.GetCollection("notifications").InsertOne(ctx, n)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
err := godotenv.Load()
|
||||
if err != nil {
|
||||
log.Println("No .env file found, using system environment variables")
|
||||
}
|
||||
|
||||
database.Connect()
|
||||
startDueDateReminder()
|
||||
|
||||
app := fiber.New(fiber.Config{
|
||||
AppName: "FPMB API",
|
||||
})
|
||||
|
||||
app.Use(logger.New(logger.Config{
|
||||
Next: func(c *fiber.Ctx) bool {
|
||||
return len(c.Path()) >= 5 && c.Path()[:5] == "/_app" ||
|
||||
c.Path() == "/favicon.ico" ||
|
||||
len(c.Path()) >= 7 && c.Path()[:7] == "/fonts/"
|
||||
},
|
||||
}))
|
||||
app.Use(cors.New(cors.Config{
|
||||
AllowOrigins: "*",
|
||||
AllowHeaders: "Origin, Content-Type, Accept, Authorization",
|
||||
}))
|
||||
|
||||
api := app.Group("/api")
|
||||
|
||||
api.Get("/health", func(c *fiber.Ctx) error {
|
||||
return c.JSON(fiber.Map{"status": "ok", "message": "FPMB API is running"})
|
||||
})
|
||||
|
||||
auth := api.Group("/auth")
|
||||
auth.Post("/register", handlers.Register)
|
||||
auth.Post("/login", handlers.Login)
|
||||
auth.Post("/refresh", handlers.RefreshToken)
|
||||
auth.Post("/logout", middleware.Protected(), handlers.Logout)
|
||||
|
||||
users := api.Group("/users", middleware.Protected())
|
||||
users.Get("/me", handlers.GetMe)
|
||||
users.Put("/me", handlers.UpdateMe)
|
||||
users.Put("/me/password", handlers.ChangePassword)
|
||||
users.Get("/search", handlers.SearchUsers)
|
||||
users.Post("/me/avatar", handlers.UploadUserAvatar)
|
||||
users.Get("/me/avatar", handlers.ServeUserAvatar)
|
||||
users.Get("/me/files", handlers.ListUserFiles)
|
||||
users.Post("/me/files/folder", handlers.CreateUserFolder)
|
||||
users.Post("/me/files/upload", handlers.UploadUserFile)
|
||||
users.Get("/me/api-keys", handlers.ListAPIKeys)
|
||||
users.Post("/me/api-keys", handlers.CreateAPIKey)
|
||||
users.Delete("/me/api-keys/:keyId", handlers.RevokeAPIKey)
|
||||
|
||||
teams := api.Group("/teams", middleware.Protected())
|
||||
teams.Get("/", handlers.ListTeams)
|
||||
teams.Post("/", handlers.CreateTeam)
|
||||
teams.Get("/:teamId", handlers.GetTeam)
|
||||
teams.Put("/:teamId", handlers.UpdateTeam)
|
||||
teams.Delete("/:teamId", handlers.DeleteTeam)
|
||||
teams.Get("/:teamId/members", handlers.ListTeamMembers)
|
||||
teams.Post("/:teamId/members/invite", handlers.InviteTeamMember)
|
||||
teams.Put("/:teamId/members/:userId", handlers.UpdateTeamMemberRole)
|
||||
teams.Delete("/:teamId/members/:userId", handlers.RemoveTeamMember)
|
||||
teams.Get("/:teamId/projects", handlers.ListTeamProjects)
|
||||
teams.Post("/:teamId/projects", handlers.CreateProject)
|
||||
teams.Get("/:teamId/events", handlers.ListTeamEvents)
|
||||
teams.Post("/:teamId/events", handlers.CreateTeamEvent)
|
||||
teams.Get("/:teamId/docs", handlers.ListDocs)
|
||||
teams.Post("/:teamId/docs", handlers.CreateDoc)
|
||||
teams.Get("/:teamId/files", handlers.ListTeamFiles)
|
||||
teams.Post("/:teamId/files/folder", handlers.CreateTeamFolder)
|
||||
teams.Post("/:teamId/files/upload", handlers.UploadTeamFile)
|
||||
teams.Post("/:teamId/avatar", handlers.UploadTeamAvatar)
|
||||
teams.Get("/:teamId/avatar", handlers.ServeTeamAvatar)
|
||||
teams.Post("/:teamId/banner", handlers.UploadTeamBanner)
|
||||
teams.Get("/:teamId/banner", handlers.ServeTeamBanner)
|
||||
teams.Get("/:teamId/chat", handlers.ListChatMessages)
|
||||
|
||||
projects := api.Group("/projects", middleware.Protected())
|
||||
projects.Get("/", handlers.ListProjects)
|
||||
projects.Post("/", handlers.CreatePersonalProject)
|
||||
projects.Get("/:projectId", handlers.GetProject)
|
||||
projects.Put("/:projectId", handlers.UpdateProject)
|
||||
projects.Put("/:projectId/archive", handlers.ArchiveProject)
|
||||
projects.Delete("/:projectId", handlers.DeleteProject)
|
||||
projects.Get("/:projectId/members", handlers.ListProjectMembers)
|
||||
projects.Post("/:projectId/members", handlers.AddProjectMember)
|
||||
projects.Put("/:projectId/members/:userId", handlers.UpdateProjectMemberRole)
|
||||
projects.Delete("/:projectId/members/:userId", handlers.RemoveProjectMember)
|
||||
projects.Get("/:projectId/board", handlers.GetBoard)
|
||||
projects.Post("/:projectId/columns", handlers.CreateColumn)
|
||||
projects.Put("/:projectId/columns/:columnId", handlers.UpdateColumn)
|
||||
projects.Put("/:projectId/columns/:columnId/position", handlers.ReorderColumn)
|
||||
projects.Delete("/:projectId/columns/:columnId", handlers.DeleteColumn)
|
||||
projects.Post("/:projectId/columns/:columnId/cards", handlers.CreateCard)
|
||||
projects.Get("/:projectId/events", handlers.ListProjectEvents)
|
||||
projects.Post("/:projectId/events", handlers.CreateProjectEvent)
|
||||
projects.Get("/:projectId/files", handlers.ListFiles)
|
||||
projects.Post("/:projectId/files/folder", handlers.CreateFolder)
|
||||
projects.Post("/:projectId/files/upload", handlers.UploadFile)
|
||||
projects.Get("/:projectId/webhooks", handlers.ListWebhooks)
|
||||
projects.Post("/:projectId/webhooks", handlers.CreateWebhook)
|
||||
projects.Get("/:projectId/whiteboard", handlers.GetWhiteboard)
|
||||
projects.Put("/:projectId/whiteboard", handlers.SaveWhiteboard)
|
||||
|
||||
cards := api.Group("/cards", middleware.Protected())
|
||||
cards.Put("/:cardId", handlers.UpdateCard)
|
||||
cards.Put("/:cardId/move", handlers.MoveCard)
|
||||
cards.Delete("/:cardId", handlers.DeleteCard)
|
||||
|
||||
events := api.Group("/events", middleware.Protected())
|
||||
events.Put("/:eventId", handlers.UpdateEvent)
|
||||
events.Delete("/:eventId", handlers.DeleteEvent)
|
||||
|
||||
notifications := api.Group("/notifications", middleware.Protected())
|
||||
notifications.Get("/", handlers.ListNotifications)
|
||||
notifications.Put("/read-all", handlers.MarkAllNotificationsRead)
|
||||
notifications.Put("/:notifId/read", handlers.MarkNotificationRead)
|
||||
notifications.Delete("/:notifId", handlers.DeleteNotification)
|
||||
|
||||
docs := api.Group("/docs", middleware.Protected())
|
||||
docs.Get("/:docId", handlers.GetDoc)
|
||||
docs.Put("/:docId", handlers.UpdateDoc)
|
||||
docs.Delete("/:docId", handlers.DeleteDoc)
|
||||
|
||||
files := api.Group("/files", middleware.Protected())
|
||||
files.Get("/:fileId/download", handlers.DownloadFile)
|
||||
files.Delete("/:fileId", handlers.DeleteFile)
|
||||
|
||||
webhooks := api.Group("/webhooks", middleware.Protected())
|
||||
webhooks.Put("/:webhookId", handlers.UpdateWebhook)
|
||||
webhooks.Put("/:webhookId/toggle", handlers.ToggleWebhook)
|
||||
webhooks.Delete("/:webhookId", handlers.DeleteWebhook)
|
||||
|
||||
app.Use("/ws", func(c *fiber.Ctx) error {
|
||||
if websocket.IsWebSocketUpgrade(c) {
|
||||
return c.Next()
|
||||
}
|
||||
return fiber.ErrUpgradeRequired
|
||||
})
|
||||
app.Get("/ws/whiteboard/:id", websocket.New(handlers.WhiteboardWS))
|
||||
app.Get("/ws/team/:id/chat", websocket.New(handlers.TeamChatWS))
|
||||
|
||||
app.Static("/", "../build")
|
||||
app.Get("/*", func(c *fiber.Ctx) error {
|
||||
if len(c.Path()) > 4 && c.Path()[:4] == "/api" {
|
||||
return c.Status(404).JSON(fiber.Map{"error": "Not Found"})
|
||||
}
|
||||
return c.SendFile("../build/index.html")
|
||||
})
|
||||
|
||||
port := os.Getenv("PORT")
|
||||
if port == "" {
|
||||
port = "8080"
|
||||
}
|
||||
|
||||
log.Printf("Server starting on port %s", port)
|
||||
log.Fatal(app.Listen(":" + port))
|
||||
}
|
||||
Reference in New Issue
Block a user