mirror of
https://github.com/Dvorinka/Trackeep.git
synced 2026-06-03 20:12:58 +00:00
954a1a1080
- Replace Redis with DragonflyDB for better performance and memory efficiency - Remove redundant environment variables (POSTGRES_*, ENCRYPTION_KEY, OAUTH_SERVICE_URL) - Consolidate database configuration to use single DB_* variables - Use JWT_SECRET for both JWT tokens and encryption - Remove PORT variable redundancy, use BACKEND_PORT consistently - Clean up docker-compose configurations for dev/prod consistency - Add DragonflyDB configuration with optimized memory usage - Remove redis.conf as it's no longer needed - Update health checks to use Redis-compatible CLI for DragonflyDB - Add missing VITE_API_URL to production frontend - Fix GitHub Actions to use correct go.sum path - Clean up development directories and unused files
869 lines
32 KiB
Go
869 lines
32 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/go-redis/redis/v8"
|
|
"github.com/joho/godotenv"
|
|
"github.com/trackeep/backend/config"
|
|
"github.com/trackeep/backend/handlers"
|
|
"github.com/trackeep/backend/middleware"
|
|
"github.com/trackeep/backend/models"
|
|
"github.com/trackeep/backend/utils"
|
|
)
|
|
|
|
func IsDemoMode() bool {
|
|
return os.Getenv("VITE_DEMO_MODE") == "true"
|
|
}
|
|
|
|
func initializeSecuritySecrets() error {
|
|
// Only set JWT_SECRET if not already provided in environment
|
|
if os.Getenv("JWT_SECRET") == "" {
|
|
jwtSecret, err := utils.GetOrCreateJWTSecret()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
os.Setenv("JWT_SECRET", jwtSecret)
|
|
log.Println("JWT secret initialized from file")
|
|
} else {
|
|
log.Println("JWT secret found in environment variable")
|
|
}
|
|
|
|
// Only set ENCRYPTION_KEY if not already provided in environment
|
|
if os.Getenv("ENCRYPTION_KEY") == "" {
|
|
encryptionKey, err := utils.GetOrCreateEncryptionKey()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
os.Setenv("ENCRYPTION_KEY", encryptionKey)
|
|
log.Println("Encryption key initialized from file")
|
|
} else {
|
|
log.Println("Encryption key found in environment variable")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// initializeDragonflyDB initializes DragonflyDB (Redis-compatible) connection
|
|
func initializeDragonflyDB() *redis.Client {
|
|
dragonflyAddr := os.Getenv("DRAGONFLY_ADDR")
|
|
dragonflyPassword := os.Getenv("DRAGONFLY_PASSWORD")
|
|
|
|
if dragonflyAddr == "" {
|
|
log.Println("DRAGONFLY_ADDR not set, using default: localhost:6379")
|
|
dragonflyAddr = "localhost:6379"
|
|
}
|
|
|
|
rdb := redis.NewClient(&redis.Options{
|
|
Addr: dragonflyAddr,
|
|
Password: dragonflyPassword,
|
|
DialTimeout: 5 * time.Second,
|
|
ReadTimeout: 3 * time.Second,
|
|
WriteTimeout: 3 * time.Second,
|
|
PoolSize: 20,
|
|
})
|
|
|
|
// Test connection
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
|
|
_, err := rdb.Ping(ctx).Result()
|
|
if err != nil {
|
|
log.Printf("Warning: Failed to connect to DragonflyDB at %s: %v", dragonflyAddr, err)
|
|
log.Println("Falling back to in-memory cache and sessions")
|
|
return nil
|
|
}
|
|
|
|
log.Printf("Successfully connected to DragonflyDB at %s", dragonflyAddr)
|
|
return rdb
|
|
}
|
|
|
|
func main() {
|
|
os.Setenv("APP_VERSION", "1.0.0")
|
|
|
|
envPaths := []string{".env", "../.env", "/app/.env"}
|
|
envLoaded := false
|
|
|
|
for _, path := range envPaths {
|
|
if err := godotenv.Load(path); err == nil {
|
|
log.Printf("Loaded .env from: %s", path)
|
|
envLoaded = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !envLoaded {
|
|
log.Println("No .env file found, using environment variables only")
|
|
}
|
|
|
|
cfg := config.Load()
|
|
if err := cfg.Validate(); err != nil {
|
|
log.Printf("Configuration warning: %v", err)
|
|
}
|
|
|
|
if !cfg.App.DemoMode {
|
|
config.InitDatabase()
|
|
models.InitDB()
|
|
models.AutoMigrate()
|
|
} else {
|
|
log.Println("Demo mode enabled, skipping database initialization")
|
|
}
|
|
|
|
// Initialize security secrets
|
|
if err := initializeSecuritySecrets(); err != nil {
|
|
log.Fatal("Failed to initialize security secrets:", err)
|
|
}
|
|
|
|
// Initialize DragonflyDB
|
|
dragonflyClient := initializeDragonflyDB()
|
|
|
|
// Initialize session store with DragonflyDB
|
|
middleware.InitSessionStore(dragonflyClient)
|
|
log.Println("Session store initialized successfully")
|
|
|
|
// Initialize cache middleware with DragonflyDB
|
|
var cacheConfig middleware.CacheConfig
|
|
if dragonflyClient != nil {
|
|
cacheConfig = middleware.CacheConfig{
|
|
Duration: 5 * time.Minute,
|
|
KeyPrefix: "trackeep:",
|
|
Enabled: true,
|
|
RedisClient: dragonflyClient,
|
|
}
|
|
log.Println("DragonflyDB cache middleware initialized")
|
|
} else {
|
|
cacheConfig = middleware.DefaultCacheConfig()
|
|
log.Println("Using in-memory cache fallback")
|
|
}
|
|
|
|
// Seed demo data in background
|
|
// go func() {
|
|
// SeedData()
|
|
// }()
|
|
|
|
// Set Gin mode
|
|
if os.Getenv("GIN_MODE") == "release" || os.Getenv("GIN_MODE") == "production" {
|
|
gin.SetMode(gin.ReleaseMode)
|
|
}
|
|
|
|
// Initialize router
|
|
r := gin.Default()
|
|
|
|
// Middleware
|
|
r.Use(gin.Logger())
|
|
r.Use(gin.Recovery())
|
|
r.Use(middleware.CORSMiddleware())
|
|
r.Use(middleware.CacheMiddleware(cacheConfig)) // Add DragonflyDB cache middleware
|
|
r.Use(middleware.CacheInvalidationMiddleware(dragonflyClient)) // Add cache invalidation
|
|
r.Use(middleware.SessionMiddleware()) // Add session middleware
|
|
r.Use(middleware.AuditMiddleware())
|
|
r.Use(middleware.InputValidationMiddleware())
|
|
|
|
// Initialize rate limiters
|
|
rateLimitConfig := middleware.DefaultRateLimitConfig()
|
|
rateLimiters := middleware.RateLimit(rateLimitConfig)
|
|
|
|
// Apply general rate limiting to all endpoints
|
|
r.Use(middleware.GeneralRateLimit(rateLimiters["general"]))
|
|
|
|
r.GET("/health", handlers.HealthCheck)
|
|
r.GET("/ready", handlers.ReadinessCheck)
|
|
r.GET("/live", handlers.LivenessCheck)
|
|
r.GET("/api/v1/config", handlers.GetAPIConfig)
|
|
|
|
// Demo status endpoint
|
|
r.GET("/api/demo/status", handlers.DemoStatus)
|
|
|
|
// Update endpoints
|
|
r.GET("/api/updates/check", handlers.CheckForUpdates)
|
|
r.POST("/api/updates/install", handlers.InstallUpdate)
|
|
r.GET("/api/updates/progress", handlers.GetUpdateProgress)
|
|
r.GET("/api/updates/ws", handlers.UpdateProgressWebSocket)
|
|
|
|
// Serve static files (frontend)
|
|
r.Static("/assets", "../frontend/dist/assets")
|
|
r.StaticFile("/", "../frontend/dist/index.html")
|
|
|
|
// Serve browser extension download
|
|
r.GET("/browser-extension", handlers.DownloadBrowserExtension)
|
|
|
|
r.NoRoute(func(c *gin.Context) {
|
|
c.File("../frontend/dist/index.html")
|
|
})
|
|
|
|
// Version endpoint
|
|
r.GET("/api/version", handlers.GetVersionHandler)
|
|
|
|
// Initialize handlers
|
|
memberHandler := handlers.NewMemberHandler(config.GetDB())
|
|
timeEntryHandler := handlers.NewTimeEntryHandler(config.GetDB())
|
|
calendarHandler := handlers.NewCalendarHandler(config.GetDB())
|
|
integrationHandler := handlers.NewIntegrationHandler(config.GetDB())
|
|
analyticsHandler := handlers.NewAnalyticsHandler(config.GetDB())
|
|
learningProgressHandler := handlers.NewLearningProgressHandler(config.GetDB())
|
|
webScrapingHandler := handlers.NewWebScrapingHandler(config.GetDB())
|
|
knowledgeBaseHandler := handlers.NewKnowledgeBaseHandler(config.GetDB())
|
|
goalsHabitsHandler := handlers.NewGoalsHabitsHandler(config.GetDB())
|
|
socialHandler := handlers.NewSocialHandler(config.GetDB())
|
|
teamsHandler := handlers.NewTeamsHandler(config.GetDB())
|
|
aiRecommendationHandler := handlers.NewAIRecommendationHandler(config.GetDB())
|
|
marketplaceHandler := handlers.NewMarketplaceHandler(config.GetDB())
|
|
communityHandler := handlers.NewCommunityHandler(config.GetDB())
|
|
performanceHandler := handlers.NewPerformanceHandler(config.GetDB())
|
|
|
|
// API v1 routes
|
|
v1 := r.Group("/api/v1")
|
|
{
|
|
// Auth routes
|
|
auth := v1.Group("/auth")
|
|
auth.Use(middleware.AuthRateLimit(rateLimiters["auth"]))
|
|
{
|
|
auth.GET("/check-users", handlers.CheckUsers)
|
|
auth.POST("/register", handlers.Register)
|
|
auth.POST("/login", handlers.Login)
|
|
auth.POST("/login-totp", handlers.LoginWithTOTP)
|
|
auth.POST("/logout", handlers.Logout)
|
|
auth.GET("/me", handlers.AuthMiddleware(), handlers.GetCurrentUserWithGitHub)
|
|
auth.POST("/password-reset", handlers.RequestPasswordReset)
|
|
auth.POST("/password-reset/confirm", handlers.ConfirmPasswordReset)
|
|
|
|
// GitHub OAuth routes
|
|
auth.GET("/github", handlers.GitHubLogin)
|
|
auth.GET("/github/callback", handlers.GitHubCallback)
|
|
auth.GET("/oauth/callback", handlers.HandleOAuthCallback)
|
|
}
|
|
|
|
// GitHub App callback (public for GitHub redirect)
|
|
v1.GET("/github/app/callback", handlers.GitHubAppInstallCallback)
|
|
|
|
// GitHub routes (protected)
|
|
github := v1.Group("/github")
|
|
github.Use(handlers.AuthMiddleware())
|
|
{
|
|
github.GET("/repos", handlers.GetGitHubRepos)
|
|
github.GET("/app/status", handlers.GetGitHubAppStatus)
|
|
github.GET("/app/install-url", handlers.GetGitHubAppInstallURL)
|
|
github.GET("/app/repos", handlers.GetGitHubAppRepos)
|
|
github.GET("/backups", handlers.GetGitHubBackups)
|
|
github.POST("/backups", handlers.BackupGitHubRepositories)
|
|
}
|
|
|
|
v1.POST("/youtube-search-test", handlers.YouTubeSearchTest)
|
|
|
|
// Protected auth routes (with demo mode protection)
|
|
authProtected := v1.Group("/auth")
|
|
authProtected.Use(handlers.AuthMiddleware())
|
|
authProtected.Use(middleware.DemoModeMiddleware())
|
|
{
|
|
authProtected.PUT("/profile", handlers.UpdateProfile)
|
|
authProtected.PUT("/password", handlers.ChangePassword)
|
|
|
|
// 2FA routes
|
|
authProtected.POST("/2fa/setup", handlers.SetupTOTP)
|
|
authProtected.POST("/2fa/verify", handlers.VerifyTOTP)
|
|
authProtected.POST("/2fa/enable", handlers.EnableTOTP)
|
|
authProtected.POST("/2fa/disable", handlers.DisableTOTP)
|
|
authProtected.GET("/2fa/status", handlers.GetTOTPStatus)
|
|
authProtected.POST("/2fa/backup-codes/verify", handlers.VerifyBackupCode)
|
|
authProtected.POST("/2fa/backup-codes/regenerate", handlers.RegenerateBackupCodes)
|
|
|
|
// Encryption routes
|
|
authProtected.POST("/encrypt/content", handlers.EncryptNoteContent)
|
|
authProtected.POST("/decrypt/content", handlers.DecryptNoteContent)
|
|
authProtected.GET("/encryption/status", handlers.GetEncryptionStatus)
|
|
|
|
// AI Settings routes
|
|
authProtected.GET("/ai/settings", handlers.GetAISettings)
|
|
authProtected.PUT("/ai/settings", handlers.UpdateAISettings)
|
|
authProtected.POST("/ai/test-connection", handlers.TestAIConnection)
|
|
|
|
// Search Settings routes
|
|
authProtected.GET("/search/settings", handlers.GetSearchSettings)
|
|
authProtected.PUT("/search/settings", handlers.UpdateSearchSettings)
|
|
|
|
// Update Settings routes
|
|
authProtected.GET("/update/settings", handlers.GetUpdateSettings)
|
|
authProtected.PUT("/update/settings", handlers.UpdateUpdateSettings)
|
|
}
|
|
|
|
// Test AI settings without auth
|
|
v1.GET("/test-ai-settings", handlers.GetAISettings)
|
|
|
|
// Test search and update settings without auth (for demo mode)
|
|
v1.GET("/test-search-settings", handlers.GetTestSearchSettings)
|
|
v1.GET("/test-update-settings", handlers.GetTestUpdateSettings)
|
|
|
|
// Dashboard routes (protected)
|
|
dashboard := v1.Group("/dashboard")
|
|
dashboard.Use(handlers.AuthMiddleware())
|
|
{
|
|
dashboard.GET("/stats", handlers.GetDashboardStats)
|
|
}
|
|
|
|
// Bookmark routes (protected)
|
|
bookmarks := v1.Group("/bookmarks")
|
|
bookmarks.Use(handlers.AuthMiddleware())
|
|
bookmarks.Use(middleware.DemoModeMiddleware())
|
|
{
|
|
bookmarks.GET("", handlers.GetBookmarks)
|
|
bookmarks.POST("", handlers.CreateBookmark)
|
|
bookmarks.GET("/:id", handlers.GetBookmark)
|
|
bookmarks.PUT("/:id", handlers.UpdateBookmark)
|
|
bookmarks.DELETE("/:id", handlers.DeleteBookmark)
|
|
bookmarks.POST("/:id/refresh-metadata", handlers.RefreshBookmarkMetadata)
|
|
bookmarks.POST("/metadata", handlers.GetBookmarkMetadata)
|
|
bookmarks.POST("/content", handlers.GetBookmarkContent)
|
|
}
|
|
|
|
// Task routes (protected)
|
|
tasks := v1.Group("/tasks")
|
|
tasks.Use(handlers.AuthMiddleware())
|
|
tasks.Use(middleware.DemoModeMiddleware())
|
|
{
|
|
tasks.GET("", handlers.GetTasks)
|
|
tasks.POST("", handlers.CreateTask)
|
|
tasks.GET("/:id", handlers.GetTask)
|
|
tasks.PUT("/:id", handlers.UpdateTask)
|
|
tasks.DELETE("/:id", handlers.DeleteTask)
|
|
}
|
|
|
|
// File routes (protected)
|
|
files := v1.Group("/files")
|
|
files.Use(handlers.AuthMiddleware())
|
|
files.Use(middleware.DemoModeMiddleware())
|
|
{
|
|
files.GET("", handlers.GetFiles)
|
|
files.POST("/upload", handlers.UploadFile)
|
|
files.GET("/:id", handlers.GetFile)
|
|
files.GET("/:id/download", handlers.DownloadFile)
|
|
files.DELETE("/:id", handlers.DeleteFile)
|
|
|
|
// Encrypted files
|
|
files.POST("/upload/encrypted", handlers.UploadEncryptedFile)
|
|
files.GET("/:id/download/encrypted", handlers.DownloadEncryptedFile)
|
|
}
|
|
|
|
// Admin routes (admin only)
|
|
admin := v1.Group("/admin")
|
|
admin.Use(handlers.AuthMiddleware())
|
|
admin.Use(handlers.AdminMiddleware())
|
|
{
|
|
// Learning paths management
|
|
admin.GET("/learning-paths", handlers.AdminGetAllLearningPaths)
|
|
admin.PUT("/learning-paths/:id/review", handlers.AdminReviewLearningPath)
|
|
admin.DELETE("/learning-paths/:id", handlers.AdminDeleteLearningPath)
|
|
|
|
// User management
|
|
admin.GET("/users", handlers.AdminGetUsers)
|
|
admin.PUT("/users/:id/role", handlers.AdminUpdateUserRole)
|
|
|
|
// Admin stats
|
|
admin.GET("/stats", handlers.AdminGetStats)
|
|
|
|
// Audit logs
|
|
admin.GET("/audit-logs", handlers.GetAuditLogs)
|
|
admin.GET("/audit-logs/stats", handlers.GetAuditLogStats)
|
|
admin.GET("/audit-logs/:id", handlers.GetAuditLog)
|
|
admin.GET("/audit-logs/export", handlers.ExportAuditLogs)
|
|
admin.DELETE("/audit-logs/cleanup", handlers.CleanupAuditLogs)
|
|
}
|
|
|
|
// Learning paths categories endpoint (public)
|
|
v1.GET("/learning-paths/categories", handlers.GetLearningPathCategories)
|
|
|
|
// Learning paths routes (protected)
|
|
learningPaths := v1.Group("/learning-paths")
|
|
learningPaths.Use(middleware.DemoModeMiddleware())
|
|
learningPaths.Use(handlers.AuthMiddleware())
|
|
{
|
|
learningPaths.GET("", handlers.GetLearningPaths)
|
|
learningPaths.POST("", handlers.CreateLearningPath)
|
|
learningPaths.GET("/:id", handlers.GetLearningPath)
|
|
learningPaths.PUT("/:id", handlers.UpdateLearningPath)
|
|
learningPaths.DELETE("/:id", handlers.DeleteLearningPath)
|
|
learningPaths.POST("/:id/enroll", handlers.EnrollInLearningPath)
|
|
learningPaths.GET("/:id/courses", handlers.GetLearningPathCourses)
|
|
}
|
|
|
|
// Courses routes (protected)
|
|
courses := v1.Group("/courses")
|
|
courses.Use(handlers.AuthMiddleware())
|
|
{
|
|
courses.GET("", handlers.GetCourses)
|
|
courses.GET("/featured", handlers.GetFeaturedCourses)
|
|
courses.GET("/ztm", handlers.GetZTMCourses)
|
|
courses.GET("/categories", handlers.GetCourseCategories)
|
|
courses.POST("/search", handlers.SearchCourses)
|
|
courses.GET("/:id", handlers.GetCourse)
|
|
courses.GET("/slug/:slug", handlers.GetCourseBySlug)
|
|
}
|
|
|
|
// Enrollments routes (protected)
|
|
enrollments := v1.Group("/enrollments")
|
|
enrollments.Use(handlers.AuthMiddleware())
|
|
{
|
|
enrollments.GET("", handlers.GetUserEnrollments)
|
|
enrollments.PUT("/:id/progress", handlers.UpdateProgress)
|
|
enrollments.POST("/:id/rate", handlers.RateLearningPath)
|
|
}
|
|
|
|
// Notes routes (protected)
|
|
notes := v1.Group("/notes")
|
|
notes.Use(handlers.AuthMiddleware())
|
|
notes.Use(middleware.DemoModeMiddleware())
|
|
{
|
|
notes.GET("", handlers.GetNotes)
|
|
notes.POST("", handlers.CreateNote)
|
|
notes.GET("/:id", handlers.GetNote)
|
|
notes.PUT("/:id", handlers.UpdateNote)
|
|
notes.DELETE("/:id", handlers.DeleteNote)
|
|
notes.GET("/stats", handlers.GetNoteStats)
|
|
|
|
// Encrypted notes
|
|
notes.POST("/encrypted", handlers.CreateEncryptedNote)
|
|
notes.GET("/:id/encrypted", handlers.GetEncryptedNote)
|
|
}
|
|
|
|
// Chat routes (protected)
|
|
chat := v1.Group("/chat")
|
|
chat.Use(handlers.AuthMiddleware())
|
|
{
|
|
chat.POST("/send", handlers.SendMessage)
|
|
chat.GET("/sessions", handlers.GetSessions)
|
|
chat.GET("/sessions/:id/messages", handlers.GetSessionMessages)
|
|
chat.DELETE("/sessions/:id", handlers.DeleteSession)
|
|
}
|
|
|
|
// Messaging routes (Discord-like user communication)
|
|
messages := v1.Group("/messages")
|
|
messages.Use(handlers.AuthMiddleware())
|
|
{
|
|
messages.GET("/conversations", handlers.GetConversations)
|
|
messages.POST("/conversations", handlers.CreateConversation)
|
|
messages.GET("/conversations/:id", handlers.GetConversation)
|
|
messages.PATCH("/conversations/:id", handlers.UpdateConversation)
|
|
messages.POST("/conversations/:id/members", handlers.AddConversationMember)
|
|
messages.DELETE("/conversations/:id/members/:userId", handlers.RemoveConversationMember)
|
|
messages.GET("/conversations/:id/messages", handlers.GetConversationMessages)
|
|
messages.POST("/conversations/:id/messages", handlers.CreateConversationMessage)
|
|
messages.PATCH("/messages/:id", handlers.UpdateMessage)
|
|
messages.DELETE("/messages/:id", handlers.DeleteMessage)
|
|
messages.POST("/messages/:id/reactions", handlers.AddMessageReaction)
|
|
messages.DELETE("/messages/:id/reactions/:emoji", handlers.RemoveMessageReaction)
|
|
messages.POST("/messages/search", handlers.SearchMessages)
|
|
messages.GET("/messages/:id/suggestions", handlers.GetMessageSuggestions)
|
|
messages.POST("/messages/:id/suggestions/:suggestionId/accept", handlers.AcceptMessageSuggestion)
|
|
messages.POST("/messages/:id/suggestions/:suggestionId/dismiss", handlers.DismissMessageSuggestion)
|
|
messages.POST("/messages/:id/reveal-sensitive", handlers.RevealSensitiveMessage)
|
|
messages.GET("/ws", handlers.MessagesWebSocket)
|
|
|
|
messages.GET("/password-vault/items", handlers.GetPasswordVaultItems)
|
|
messages.POST("/password-vault/items", handlers.CreatePasswordVaultItem)
|
|
messages.POST("/password-vault/items/:id/share", handlers.SharePasswordVaultItem)
|
|
messages.POST("/password-vault/items/:id/reveal", handlers.RevealPasswordVaultItem)
|
|
messages.POST("/password-vault/items/:id/unshare", handlers.UnsharePasswordVaultItem)
|
|
}
|
|
|
|
// Member routes (protected)
|
|
members := v1.Group("/members")
|
|
members.Use(handlers.AuthMiddleware())
|
|
{
|
|
members.GET("", memberHandler.GetMembers)
|
|
members.GET("/stats", memberHandler.GetMemberStats)
|
|
}
|
|
|
|
// YouTube routes (protected)
|
|
youtube := v1.Group("/youtube")
|
|
youtube.Use(handlers.AuthMiddleware())
|
|
{
|
|
youtube.POST("/search", handlers.SearchYouTube)
|
|
youtube.POST("/video-details", handlers.GetYouTubeVideoDetails)
|
|
youtube.POST("/channel-videos", handlers.GetYouTubeChannelVideos)
|
|
youtube.POST("/channel-from-url", handlers.GetYouTubeChannelVideosFromURL)
|
|
youtube.GET("/trending", handlers.GetYouTubeTrending)
|
|
youtube.GET("/predefined-channels", handlers.GetPredefinedChannelVideos)
|
|
|
|
// YouTube Channel routes
|
|
youtube.GET("/fireship", handlers.GetFireshipVideos)
|
|
youtube.GET("/network-chuck", handlers.GetNetworkChuckVideos)
|
|
youtube.POST("/channel", handlers.GetChannelVideos)
|
|
}
|
|
|
|
videoBookmarks := v1.Group("/video-bookmarks")
|
|
videoBookmarks.Use(handlers.AuthMiddleware())
|
|
{
|
|
videoBookmarkHandler := handlers.NewVideoBookmarkHandler()
|
|
videoBookmarks.POST("", videoBookmarkHandler.SaveVideoBookmark)
|
|
videoBookmarks.GET("", videoBookmarkHandler.GetUserBookmarks)
|
|
videoBookmarks.GET("/search", videoBookmarkHandler.SearchBookmarks)
|
|
videoBookmarks.GET("/stats", videoBookmarkHandler.GetBookmarkStats)
|
|
videoBookmarks.GET("/:id", videoBookmarkHandler.GetBookmarkByID)
|
|
videoBookmarks.PUT("/:id", videoBookmarkHandler.UpdateBookmark)
|
|
videoBookmarks.DELETE("/:id", videoBookmarkHandler.DeleteBookmark)
|
|
videoBookmarks.POST("/:id/toggle-watched", videoBookmarkHandler.ToggleWatched)
|
|
videoBookmarks.POST("/:id/toggle-favorite", videoBookmarkHandler.ToggleFavorite)
|
|
}
|
|
|
|
// Search routes (protected)
|
|
search := v1.Group("/search")
|
|
search.Use(handlers.AuthMiddleware())
|
|
{
|
|
search.POST("/web", handlers.SearchWeb)
|
|
search.POST("/news", handlers.SearchNews)
|
|
search.GET("/suggestions", handlers.GetSearchSuggestions)
|
|
|
|
// Enhanced search features
|
|
search.POST("/enhanced", handlers.EnhancedSearch)
|
|
search.POST("/save", handlers.SaveSearch)
|
|
search.GET("/analytics", handlers.GetSearchAnalytics)
|
|
|
|
// Saved searches management
|
|
savedSearches := search.Group("/saved")
|
|
{
|
|
savedSearches.POST("", handlers.CreateSavedSearch)
|
|
savedSearches.GET("", handlers.GetUserSavedSearches)
|
|
savedSearches.GET("/:id", handlers.GetSavedSearch)
|
|
savedSearches.PUT("/:id", handlers.UpdateSavedSearch)
|
|
savedSearches.DELETE("/:id", handlers.DeleteSavedSearch)
|
|
savedSearches.POST("/:id/run", handlers.RunSavedSearch)
|
|
savedSearches.GET("/tags", handlers.GetSavedSearchTags)
|
|
}
|
|
|
|
// Semantic search features
|
|
search.POST("/semantic", handlers.SemanticSearch)
|
|
search.POST("/embeddings/generate", handlers.GenerateEmbedding)
|
|
search.POST("/reindex", handlers.ReindexContent)
|
|
}
|
|
|
|
// Time tracking routes (protected)
|
|
timeEntries := v1.Group("/time-entries")
|
|
timeEntries.Use(handlers.AuthMiddleware())
|
|
timeEntries.Use(middleware.DemoModeMiddleware())
|
|
{
|
|
timeEntries.GET("", timeEntryHandler.GetTimeEntries)
|
|
timeEntries.POST("", timeEntryHandler.CreateTimeEntry)
|
|
timeEntries.GET("/stats", timeEntryHandler.GetTimeStats)
|
|
timeEntries.GET("/:id", timeEntryHandler.GetTimeEntry)
|
|
timeEntries.PUT("/:id", timeEntryHandler.UpdateTimeEntry)
|
|
timeEntries.POST("/:id/stop", timeEntryHandler.StopTimeEntry)
|
|
timeEntries.DELETE("/:id", timeEntryHandler.DeleteTimeEntry)
|
|
}
|
|
|
|
// Calendar routes (protected)
|
|
calendar := v1.Group("/calendar")
|
|
calendar.Use(handlers.AuthMiddleware())
|
|
{
|
|
calendar.GET("", calendarHandler.GetEvents)
|
|
calendar.GET("/:id", calendarHandler.GetEvent)
|
|
calendar.POST("", calendarHandler.CreateEvent)
|
|
calendar.PUT("/:id", calendarHandler.UpdateEvent)
|
|
calendar.DELETE("/:id", calendarHandler.DeleteEvent)
|
|
calendar.GET("/upcoming", calendarHandler.GetUpcomingEvents)
|
|
calendar.GET("/today", calendarHandler.GetTodayEvents)
|
|
calendar.GET("/deadlines", calendarHandler.GetDeadlines)
|
|
calendar.PUT("/:id/toggle-complete", calendarHandler.ToggleEventCompletion)
|
|
}
|
|
|
|
// AI Features routes (protected)
|
|
ai := v1.Group("/ai")
|
|
ai.Use(handlers.AuthMiddleware())
|
|
{
|
|
// AI providers
|
|
ai.GET("/providers", handlers.GetAIProviders)
|
|
|
|
// Content summarization
|
|
ai.POST("/summarize", handlers.SummarizeContent)
|
|
ai.GET("/summaries", handlers.GetAISummaries)
|
|
|
|
// Task suggestions
|
|
ai.POST("/tasks/suggest", handlers.GetTaskSuggestions)
|
|
ai.GET("/tasks/suggestions", handlers.GetTaskSuggestionsList)
|
|
ai.POST("/tasks/suggestions/:id/accept", handlers.AcceptTaskSuggestion)
|
|
ai.POST("/tasks/suggestions/:id/dismiss", handlers.DismissTaskSuggestion)
|
|
|
|
// Tag suggestions
|
|
ai.POST("/tags/suggest", handlers.GenerateTagSuggestions)
|
|
|
|
// Content generation
|
|
ai.POST("/content/generate", handlers.GenerateContent)
|
|
}
|
|
|
|
// Integration routes (protected)
|
|
integrations := v1.Group("/integrations")
|
|
integrations.Use(handlers.AuthMiddleware())
|
|
{
|
|
integrations.GET("", integrationHandler.GetIntegrations)
|
|
integrations.POST("", integrationHandler.CreateIntegration)
|
|
integrations.GET("/:id", integrationHandler.GetIntegration)
|
|
integrations.PUT("/:id", integrationHandler.UpdateIntegration)
|
|
integrations.DELETE("/:id", integrationHandler.DeleteIntegration)
|
|
integrations.POST("/:id/authorize", integrationHandler.AuthorizeIntegration)
|
|
integrations.POST("/:id/sync", integrationHandler.SyncIntegration)
|
|
integrations.GET("/:id/sync-logs", integrationHandler.GetSyncLogs)
|
|
}
|
|
|
|
// OAuth callback route (public)
|
|
v1.GET("/integrations/oauth/callback", integrationHandler.OAuthCallback)
|
|
|
|
// Analytics routes (protected)
|
|
analytics := v1.Group("/analytics")
|
|
analytics.Use(handlers.AuthMiddleware())
|
|
{
|
|
analytics.GET("/dashboard", analyticsHandler.GetDashboardAnalytics)
|
|
analytics.GET("/productivity", analyticsHandler.GetProductivityMetrics)
|
|
analytics.GET("/learning", analyticsHandler.GetLearningAnalytics)
|
|
analytics.GET("/content", analyticsHandler.GetContentAnalytics)
|
|
analytics.GET("/github", analyticsHandler.GetGitHubAnalytics)
|
|
analytics.GET("/goals", analyticsHandler.GetGoals)
|
|
analytics.POST("/goals", analyticsHandler.CreateGoal)
|
|
analytics.PUT("/goals/:id", analyticsHandler.UpdateGoal)
|
|
analytics.DELETE("/goals/:id", analyticsHandler.DeleteGoal)
|
|
analytics.POST("/reports", analyticsHandler.GenerateAnalyticsReport)
|
|
analytics.POST("/generate-daily", analyticsHandler.GenerateDailyAnalytics)
|
|
}
|
|
|
|
// Learning progress routes (protected)
|
|
learning := v1.Group("/learning")
|
|
learning.Use(handlers.AuthMiddleware())
|
|
{
|
|
learning.POST("/progress", learningProgressHandler.UpdateLearningProgress)
|
|
learning.GET("/progress", learningProgressHandler.GetLearningProgress)
|
|
learning.GET("/progress/:courseId", learningProgressHandler.GetCourseProgress)
|
|
learning.POST("/progress/:courseId/complete", learningProgressHandler.MarkCourseCompleted)
|
|
}
|
|
|
|
// Web scraping routes (protected)
|
|
webScraping := v1.Group("/web-scraping")
|
|
webScraping.Use(handlers.AuthMiddleware())
|
|
{
|
|
// Scraping jobs
|
|
webScraping.POST("/jobs", webScrapingHandler.CreateScrapingJob)
|
|
webScraping.GET("/jobs", webScrapingHandler.GetScrapingJobs)
|
|
webScraping.GET("/jobs/:id", webScrapingHandler.GetScrapingJob)
|
|
webScraping.DELETE("/jobs/:id", webScrapingHandler.DeleteScrapingJob)
|
|
|
|
// Scraped content
|
|
webScraping.GET("/content", webScrapingHandler.GetScrapedContentList)
|
|
webScraping.GET("/content/:id", webScrapingHandler.GetScrapedContent)
|
|
webScraping.DELETE("/content/:id", webScrapingHandler.DeleteScrapedContent)
|
|
webScraping.GET("/search", webScrapingHandler.SearchScrapedContent)
|
|
}
|
|
|
|
// Knowledge base routes (protected)
|
|
knowledgeBase := v1.Group("/knowledge-base")
|
|
knowledgeBase.Use(handlers.AuthMiddleware())
|
|
{
|
|
// Wiki pages
|
|
knowledgeBase.POST("/pages", knowledgeBaseHandler.CreateWikiPage)
|
|
knowledgeBase.GET("/pages", knowledgeBaseHandler.GetWikiPages)
|
|
knowledgeBase.GET("/pages/search", knowledgeBaseHandler.SearchWikiPages)
|
|
knowledgeBase.GET("/pages/:id", knowledgeBaseHandler.GetWikiPage)
|
|
knowledgeBase.PUT("/pages/:id", knowledgeBaseHandler.UpdateWikiPage)
|
|
knowledgeBase.DELETE("/pages/:id", knowledgeBaseHandler.DeleteWikiPage)
|
|
|
|
// Categories
|
|
knowledgeBase.POST("/categories", knowledgeBaseHandler.CreateCategory)
|
|
knowledgeBase.GET("/categories", knowledgeBaseHandler.GetCategories)
|
|
}
|
|
|
|
// Goals and habits routes (protected)
|
|
goalsHabits := v1.Group("/goals-habits")
|
|
goalsHabits.Use(handlers.AuthMiddleware())
|
|
{
|
|
// Goals
|
|
goalsHabits.POST("/goals", goalsHabitsHandler.CreateGoal)
|
|
goalsHabits.GET("/goals", goalsHabitsHandler.GetGoals)
|
|
goalsHabits.GET("/goals/:id", goalsHabitsHandler.GetGoal)
|
|
goalsHabits.PUT("/goals/:id", goalsHabitsHandler.UpdateGoal)
|
|
goalsHabits.DELETE("/goals/:id", goalsHabitsHandler.DeleteGoal)
|
|
|
|
// Habits
|
|
goalsHabits.POST("/habits", goalsHabitsHandler.CreateHabit)
|
|
goalsHabits.GET("/habits", goalsHabitsHandler.GetHabits)
|
|
goalsHabits.GET("/habits/:id", goalsHabitsHandler.GetHabit)
|
|
goalsHabits.PUT("/habits/:id", goalsHabitsHandler.UpdateHabit)
|
|
goalsHabits.DELETE("/habits/:id", goalsHabitsHandler.DeleteHabit)
|
|
|
|
// Habit entries
|
|
goalsHabits.POST("/habit-entries", goalsHabitsHandler.CreateHabitEntry)
|
|
goalsHabits.GET("/habits/:id/entries", goalsHabitsHandler.GetHabitEntries)
|
|
|
|
// Dashboard stats
|
|
goalsHabits.GET("/dashboard/stats", goalsHabitsHandler.GetDashboardStats)
|
|
}
|
|
|
|
// Social features routes (protected)
|
|
social := v1.Group("/social")
|
|
social.Use(handlers.AuthMiddleware())
|
|
{
|
|
// User profiles
|
|
social.GET("/users/:id", socialHandler.GetProfile)
|
|
social.PUT("/profile", socialHandler.UpdateProfile)
|
|
social.GET("/users/search", socialHandler.SearchUsers)
|
|
|
|
// Following system
|
|
social.POST("/users/:id/follow", socialHandler.FollowUser)
|
|
social.GET("/users/:id/followers", socialHandler.GetFollowers)
|
|
social.GET("/users/:id/following", socialHandler.GetFollowing)
|
|
}
|
|
|
|
// Team workspaces routes (protected)
|
|
teams := v1.Group("/teams")
|
|
teams.Use(handlers.AuthMiddleware())
|
|
{
|
|
// Team management
|
|
teams.GET("", teamsHandler.GetTeams)
|
|
teams.POST("", teamsHandler.CreateTeam)
|
|
teams.GET("/:id", teamsHandler.GetTeam)
|
|
teams.PUT("/:id", teamsHandler.UpdateTeam)
|
|
teams.DELETE("/:id", teamsHandler.DeleteTeam)
|
|
|
|
// Team members
|
|
teams.GET("/:id/members", teamsHandler.GetTeamMembers)
|
|
teams.DELETE("/:id/members/:memberId", teamsHandler.RemoveMember)
|
|
|
|
// Team invitations
|
|
teams.POST("/:id/invite", teamsHandler.InviteMember)
|
|
teams.POST("/invitations/:token/accept", teamsHandler.AcceptInvitation)
|
|
|
|
// Team activity and stats
|
|
teams.GET("/:id/activity", teamsHandler.GetTeamActivity)
|
|
teams.GET("/:id/stats", teamsHandler.GetTeamStats)
|
|
}
|
|
|
|
// AI Recommendations routes (protected)
|
|
recommendations := v1.Group("/recommendations")
|
|
recommendations.Use(handlers.AuthMiddleware())
|
|
{
|
|
recommendations.GET("", aiRecommendationHandler.GetRecommendations)
|
|
recommendations.GET("/stats", aiRecommendationHandler.GetRecommendationStats)
|
|
recommendations.PUT("/preferences", aiRecommendationHandler.UpdatePreferences)
|
|
recommendations.GET("/history", aiRecommendationHandler.GetRecommendationHistory)
|
|
recommendations.GET("/insights", aiRecommendationHandler.GetInsights)
|
|
recommendations.POST("/:id/interaction", aiRecommendationHandler.RecordInteraction)
|
|
recommendations.DELETE("/:id", aiRecommendationHandler.DeleteRecommendation)
|
|
}
|
|
|
|
// Marketplace routes (protected)
|
|
marketplace := v1.Group("/marketplace")
|
|
marketplace.Use(handlers.AuthMiddleware())
|
|
{
|
|
// Public marketplace items
|
|
marketplace.GET("/items", marketplaceHandler.GetMarketplaceItems)
|
|
marketplace.GET("/items/:id", marketplaceHandler.GetMarketplaceItem)
|
|
marketplace.GET("/items/:id/reviews", marketplaceHandler.GetMarketplaceReviews)
|
|
marketplace.GET("/stats", marketplaceHandler.GetMarketplaceStats)
|
|
|
|
// User's marketplace items
|
|
marketplace.GET("/my-items", marketplaceHandler.GetMyMarketplaceItems)
|
|
marketplace.POST("/items", marketplaceHandler.CreateMarketplaceItem)
|
|
marketplace.PUT("/items/:id", marketplaceHandler.UpdateMarketplaceItem)
|
|
marketplace.DELETE("/items/:id", marketplaceHandler.DeleteMarketplaceItem)
|
|
|
|
// Reviews
|
|
marketplace.POST("/items/:id/reviews", marketplaceHandler.CreateMarketplaceReview)
|
|
|
|
// Content sharing
|
|
marketplace.GET("/shares", marketplaceHandler.GetMyContentShares)
|
|
marketplace.POST("/shares", marketplaceHandler.CreateContentShare)
|
|
marketplace.DELETE("/shares/:id", marketplaceHandler.DeleteContentShare)
|
|
}
|
|
|
|
// Public content sharing routes (no auth required)
|
|
v1.GET("/shared/:token", marketplaceHandler.GetContentShare)
|
|
|
|
// Community routes (protected)
|
|
community := v1.Group("/community")
|
|
community.Use(handlers.AuthMiddleware())
|
|
{
|
|
// Challenges
|
|
community.GET("/challenges", communityHandler.GetChallenges)
|
|
community.GET("/challenges/:id", communityHandler.GetChallenge)
|
|
community.POST("/challenges", communityHandler.CreateChallenge)
|
|
community.POST("/challenges/:id/join", communityHandler.JoinChallenge)
|
|
community.PUT("/challenges/:id/progress", communityHandler.UpdateChallengeProgress)
|
|
community.GET("/my-challenges", communityHandler.GetMyChallenges)
|
|
|
|
// Mentorship
|
|
community.GET("/mentorship/requests", communityHandler.GetMentorshipRequests)
|
|
community.POST("/mentorship/requests", communityHandler.CreateMentorshipRequest)
|
|
community.PUT("/mentorship/requests/:id/respond", communityHandler.RespondToMentorshipRequest)
|
|
community.GET("/mentorship/my-mentorships", communityHandler.GetMyMentorships)
|
|
community.POST("/mentorship/:id/sessions", communityHandler.CreateMentorshipSession)
|
|
community.GET("/mentorship/:id/sessions", communityHandler.GetMentorshipSessions)
|
|
|
|
// Community stats
|
|
community.GET("/stats", communityHandler.GetCommunityStats)
|
|
}
|
|
|
|
// Performance routes (admin only)
|
|
performance := v1.Group("/performance")
|
|
performance.Use(handlers.AuthMiddleware())
|
|
performance.Use(handlers.AdminMiddleware())
|
|
{
|
|
performance.GET("/stats", performanceHandler.GetDatabaseStats)
|
|
performance.GET("/monitor", performanceHandler.MonitorPerformance)
|
|
performance.POST("/optimize", performanceHandler.OptimizeDatabase)
|
|
performance.POST("/cleanup-audit-logs", performanceHandler.CleanupOldAuditLogs)
|
|
}
|
|
|
|
// Browser Extension API routes
|
|
browserExt := v1.Group("/browser-extension")
|
|
browserExt.Use(handlers.AuthMiddleware())
|
|
{
|
|
// API Key management
|
|
browserExt.POST("/api-keys/generate", handlers.GenerateAPIKey)
|
|
browserExt.GET("/api-keys", handlers.GetAPIKeys)
|
|
browserExt.DELETE("/api-keys/:id", handlers.RevokeAPIKey)
|
|
|
|
// Extension registration and validation
|
|
browserExt.POST("/register", handlers.RegisterBrowserExtension)
|
|
browserExt.GET("/extensions", handlers.GetBrowserExtensions)
|
|
browserExt.DELETE("/extensions/:id", handlers.RevokeBrowserExtension)
|
|
|
|
// Public endpoints (for extension validation)
|
|
browserExt.GET("/validate", handlers.ValidateAPIKey)
|
|
}
|
|
}
|
|
|
|
srv := &http.Server{
|
|
Addr: ":" + cfg.Server.Port,
|
|
Handler: r,
|
|
ReadTimeout: cfg.Server.ReadTimeout,
|
|
WriteTimeout: cfg.Server.WriteTimeout,
|
|
IdleTimeout: cfg.Server.IdleTimeout,
|
|
}
|
|
|
|
go func() {
|
|
log.Printf("Server starting on port %s", cfg.Server.Port)
|
|
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
|
log.Fatal("Failed to start server:", err)
|
|
}
|
|
}()
|
|
|
|
quit := make(chan os.Signal, 1)
|
|
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
|
<-quit
|
|
log.Println("Shutting down server...")
|
|
|
|
middleware.CleanupSessionsOnShutdown()
|
|
log.Println("Sessions cleaned up")
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), cfg.Server.ShutdownTimeout)
|
|
defer cancel()
|
|
|
|
// Attempt graceful shutdown
|
|
if err := srv.Shutdown(ctx); err != nil {
|
|
log.Printf("Server forced to shutdown: %v", err)
|
|
} else {
|
|
log.Println("Server shutdown complete")
|
|
}
|
|
}
|