Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
3e5c69a
fix: Squash multiple critical and high severity bugs before testing
claude Nov 14, 2025
4fad508
feat: Complete all incomplete features and add comprehensive tests
claude Nov 14, 2025
0246e52
feat(api): Complete all remaining stubbed features
claude Nov 14, 2025
2641db2
feat(api): Add production-ready enhancements and comprehensive testing
claude Nov 15, 2025
e54e3ab
docs: Add comprehensive production-ready summary
claude Nov 15, 2025
13040af
docs: Add comprehensive competitive feature analysis and roadmap
claude Nov 15, 2025
36008ab
docs: Add comprehensive SaaS architecture and planning document
claude Nov 15, 2025
1703227
feat(api): Add competitive features - search, favorites, audit logs, …
claude Nov 15, 2025
ac666b7
feat(api): Add session activity logging for compliance and analytics
claude Nov 15, 2025
f6ff994
feat(api): Add secure API key management system for programmatic access
claude Nov 15, 2025
242bf6f
feat(websocket): Add real-time event-driven notifications for sessions
claude Nov 15, 2025
8664ad8
feat(rbac): Add comprehensive team-based RBAC system
claude Nov 15, 2025
bb3a9a4
docs: Add comprehensive feature completion documentation
claude Nov 15, 2025
aa0cb64
feat(analytics,preferences): Add comprehensive analytics and user pre…
claude Nov 15, 2025
7afc2ff
feat(notifications,search,snapshots): Add comprehensive notification,…
claude Nov 15, 2025
62747c0
docs: Update FEATURES_COMPLETED.md with latest features
claude Nov 15, 2025
5176a31
feat(api): Add session templates and batch operations
claude Nov 15, 2025
d4cb8a8
feat(api): Add advanced monitoring with comprehensive metrics
claude Nov 15, 2025
2a3ca94
feat(api): Add resource quotas and limits enforcement
claude Nov 15, 2025
03ed9df
feat(api): Add WebSocket real-time updates and cost management billing
claude Nov 15, 2025
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
938 changes: 938 additions & 0 deletions COMPETITIVE_FEATURES.md

Large diffs are not rendered by default.

1,122 changes: 1,122 additions & 0 deletions FEATURES_COMPLETED.md

Large diffs are not rendered by default.

664 changes: 664 additions & 0 deletions PRODUCTION_READY_SUMMARY.md

Large diffs are not rendered by default.

1,045 changes: 1,045 additions & 0 deletions SAAS_ARCHITECTURE.md

Large diffs are not rendered by default.

594 changes: 594 additions & 0 deletions api/API_REFERENCE.md

Large diffs are not rendered by default.

228 changes: 217 additions & 11 deletions api/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,21 @@ func main() {
gin.SetMode(gin.ReleaseMode)
}
router := gin.New()
router.Use(gin.Logger())

// Add request ID middleware for distributed tracing
router.Use(middleware.RequestID())

// Add recovery middleware (must be early in chain)
router.Use(gin.Recovery())

// Add structured logging with request IDs
loggerConfig := middleware.DefaultStructuredLoggerConfig()
router.Use(middleware.StructuredLoggerWithConfigFunc(loggerConfig))

// SECURITY: Add request timeout to prevent slow loris attacks
timeoutConfig := middleware.DefaultTimeoutConfig()
router.Use(middleware.Timeout(timeoutConfig))

// SECURITY: Restrict HTTP methods to prevent abuse
router.Use(middleware.AllowedHTTPMethods())

Expand Down Expand Up @@ -210,15 +222,47 @@ func main() {
}
jwtManager := auth.NewJWTManager(jwtConfig)

// Initialize SAML authentication (optional)
var samlAuth *auth.SAMLAuthenticator
samlEnabled := os.Getenv("SAML_ENABLED")
if samlEnabled == "true" {
log.Println("SAML authentication is enabled")
// NOTE: SAML configuration would be loaded from environment or config file
// For now, we set samlAuth to nil since full SAML setup requires certificates
// Users can enable SAML by setting SAML_ENABLED=true and providing:
// - SAML_ENTITY_ID, SAML_METADATA_URL, SAML_CERT_PATH, SAML_KEY_PATH
log.Println("WARNING: SAML is enabled but configuration is incomplete. SAML endpoints will return 503.")
samlAuth = nil
} else {
log.Println("SAML authentication is disabled (set SAML_ENABLED=true to enable)")
samlAuth = nil
}

// Initialize API handlers
apiHandler := api.NewHandler(database, k8sClient, connTracker, syncService, wsManager, quotaEnforcer)
userHandler := handlers.NewUserHandler(userDB)
groupHandler := handlers.NewGroupHandler(groupDB, userDB)
authHandler := auth.NewAuthHandler(userDB, jwtManager)
authHandler := auth.NewAuthHandler(userDB, jwtManager, samlAuth)
activityHandler := handlers.NewActivityHandler(k8sClient, activityTracker)
catalogHandler := handlers.NewCatalogHandler(database)
sharingHandler := handlers.NewSharingHandler(database)
pluginHandler := handlers.NewPluginHandler(database)
auditLogHandler := handlers.NewAuditLogHandler(database)
dashboardHandler := handlers.NewDashboardHandler(database, k8sClient)
sessionActivityHandler := handlers.NewSessionActivityHandler(database)
apiKeyHandler := handlers.NewAPIKeyHandler(database)
teamHandler := handlers.NewTeamHandler(database)
analyticsHandler := handlers.NewAnalyticsHandler(database)
preferencesHandler := handlers.NewPreferencesHandler(database)
notificationsHandler := handlers.NewNotificationsHandler(database)
searchHandler := handlers.NewSearchHandler(database)
snapshotsHandler := handlers.NewSnapshotsHandler(database)
sessionTemplatesHandler := handlers.NewSessionTemplatesHandler(database)
batchHandler := handlers.NewBatchHandler(database)
monitoringHandler := handlers.NewMonitoringHandler(database)
quotasHandler := handlers.NewQuotasHandler(database)
websocketHandler := handlers.NewWebSocketHandler(database)
billingHandler := handlers.NewBillingHandler(database)

// SECURITY: Initialize webhook authentication
webhookSecret := os.Getenv("WEBHOOK_SECRET")
Expand All @@ -234,12 +278,21 @@ func main() {
authRateLimiter := middleware.NewRateLimiter(5, 10) // 5 req/sec with burst of 10

// Setup routes
setupRoutes(router, apiHandler, userHandler, groupHandler, authHandler, activityHandler, catalogHandler, sharingHandler, pluginHandler, jwtManager, userDB, redisCache, webhookSecret, csrfProtection, authRateLimiter)
setupRoutes(router, apiHandler, userHandler, groupHandler, authHandler, activityHandler, catalogHandler, sharingHandler, pluginHandler, auditLogHandler, dashboardHandler, sessionActivityHandler, apiKeyHandler, teamHandler, analyticsHandler, preferencesHandler, notificationsHandler, searchHandler, snapshotsHandler, sessionTemplatesHandler, batchHandler, monitoringHandler, quotasHandler, websocketHandler, billingHandler, jwtManager, userDB, redisCache, webhookSecret, csrfProtection, authRateLimiter)

// Create HTTP server
// Create HTTP server with security timeouts
srv := &http.Server{
Addr: fmt.Sprintf(":%s", port),
Handler: router,

// SECURITY: Prevent slow loris attacks and resource exhaustion
ReadTimeout: 15 * time.Second, // Time to read request headers + body
ReadHeaderTimeout: 5 * time.Second, // Time to read request headers only
WriteTimeout: 30 * time.Second, // Time to write response
IdleTimeout: 120 * time.Second, // Keep-alive timeout

// SECURITY: Limit header size to prevent memory exhaustion
MaxHeaderBytes: 1 << 20, // 1 MB
}

// Start server in goroutine
Expand All @@ -253,22 +306,60 @@ func main() {
// Wait for interrupt signal
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
sig := <-quit

log.Printf("Received shutdown signal: %v", sig)
log.Println("Starting graceful shutdown...")

log.Println("Shutting down server...")
// Create shutdown context with timeout
shutdownTimeout := 30 * time.Second
if timeoutEnv := os.Getenv("SHUTDOWN_TIMEOUT"); timeoutEnv != "" {
if duration, err := time.ParseDuration(timeoutEnv); err == nil {
shutdownTimeout = duration
}
}

// Graceful shutdown
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
defer cancel()

// Shutdown HTTP server (stops accepting new connections)
log.Println("Shutting down HTTP server...")
if err := srv.Shutdown(ctx); err != nil {
log.Printf("Server forced to shutdown: %v", err)
log.Printf("HTTP server forced to shutdown: %v", err)
} else {
log.Println("HTTP server stopped gracefully")
}

// Close WebSocket connections
log.Println("Closing WebSocket connections...")
if wsManager != nil {
wsManager.CloseAll()
}

log.Println("Server stopped")
// Close database connections
log.Println("Closing database connections...")
if database != nil {
if err := database.Close(); err != nil {
log.Printf("Error closing database: %v", err)
} else {
log.Println("Database connections closed")
}
}

// Close Redis cache
log.Println("Closing Redis cache...")
if redisCache != nil {
if err := redisCache.Close(); err != nil {
log.Printf("Error closing Redis cache: %v", err)
} else {
log.Println("Redis cache closed")
}
}

log.Println("Graceful shutdown completed")
}

func setupRoutes(router *gin.Engine, h *api.Handler, userHandler *handlers.UserHandler, groupHandler *handlers.GroupHandler, authHandler *auth.AuthHandler, activityHandler *handlers.ActivityHandler, catalogHandler *handlers.CatalogHandler, sharingHandler *handlers.SharingHandler, pluginHandler *handlers.PluginHandler, jwtManager *auth.JWTManager, userDB *db.UserDB, redisCache *cache.Cache, webhookSecret string, csrfProtection *middleware.CSRFProtection, authRateLimiter *middleware.RateLimiter) {
func setupRoutes(router *gin.Engine, h *api.Handler, userHandler *handlers.UserHandler, groupHandler *handlers.GroupHandler, authHandler *auth.AuthHandler, activityHandler *handlers.ActivityHandler, catalogHandler *handlers.CatalogHandler, sharingHandler *handlers.SharingHandler, pluginHandler *handlers.PluginHandler, auditLogHandler *handlers.AuditLogHandler, dashboardHandler *handlers.DashboardHandler, sessionActivityHandler *handlers.SessionActivityHandler, apiKeyHandler *handlers.APIKeyHandler, teamHandler *handlers.TeamHandler, analyticsHandler *handlers.AnalyticsHandler, preferencesHandler *handlers.PreferencesHandler, notificationsHandler *handlers.NotificationsHandler, searchHandler *handlers.SearchHandler, snapshotsHandler *handlers.SnapshotsHandler, sessionTemplatesHandler *handlers.SessionTemplatesHandler, batchHandler *handlers.BatchHandler, monitoringHandler *handlers.MonitoringHandler, quotasHandler *handlers.QuotasHandler, websocketHandler *handlers.WebSocketHandler, billingHandler *handlers.BillingHandler, jwtManager *auth.JWTManager, userDB *db.UserDB, redisCache *cache.Cache, webhookSecret string, csrfProtection *middleware.CSRFProtection, authRateLimiter *middleware.RateLimiter) {
// SECURITY: Create authentication middleware
authMiddleware := auth.Middleware(jwtManager, userDB)
adminMiddleware := auth.RequireRole("admin")
Expand Down Expand Up @@ -323,7 +414,15 @@ func setupRoutes(router *gin.Engine, h *api.Handler, userHandler *handlers.UserH
{
// Cache template lists for 5 minutes (rarely changing)
templates.GET("", cache.CacheMiddleware(redisCache, 5*time.Minute), h.ListTemplates)

// User favorites (all authenticated users) - MUST be before /:id routes
templates.GET("/favorites", cache.CacheMiddleware(redisCache, 30*time.Second), h.ListUserFavoriteTemplates)

// Template details and favorite operations
templates.GET("/:id", cache.CacheMiddleware(redisCache, 5*time.Minute), h.GetTemplate)
templates.POST("/:id/favorite", cache.InvalidateCacheMiddleware(redisCache, cache.UserFavoritesPattern()), h.AddTemplateFavorite)
templates.DELETE("/:id/favorite", cache.InvalidateCacheMiddleware(redisCache, cache.UserFavoritesPattern()), h.RemoveTemplateFavorite)
templates.GET("/:id/favorite", cache.CacheMiddleware(redisCache, 30*time.Second), h.CheckTemplateFavorite)

// Write operations require operator role
templatesWrite := templates.Group("")
Expand Down Expand Up @@ -396,6 +495,110 @@ func setupRoutes(router *gin.Engine, h *api.Handler, userHandler *handlers.UserH
// Plugin system - using dedicated handler
pluginHandler.RegisterRoutes(protected)

// Team-based RBAC - using dedicated handler
teamHandler.RegisterRoutes(protected)

// Analytics - using dedicated handler (operators and admins)
analyticsProtected := protected.Group("")
analyticsProtected.Use(operatorMiddleware)
{
analyticsHandler.RegisterRoutes(analyticsProtected)
}

// Audit logs (admins only for viewing, operators can view their own)
audit := protected.Group("/audit")
{
// Admin can view all audit logs with advanced filtering
audit.GET("/logs", adminMiddleware, cache.CacheMiddleware(redisCache, 30*time.Second), auditLogHandler.ListAuditLogs)
audit.GET("/stats", adminMiddleware, cache.CacheMiddleware(redisCache, 1*time.Minute), auditLogHandler.GetAuditLogStats)

// Users can view their own audit logs
audit.GET("/users/:userId/logs", auditLogHandler.GetUserAuditLogs)
}

// Dashboard and resource usage (operators and admins can view platform stats)
dashboard := protected.Group("/dashboard")
{
// Personal dashboard (all users)
dashboard.GET("/me", cache.CacheMiddleware(redisCache, 30*time.Second), dashboardHandler.GetUserDashboard)

// Platform-wide stats (operators/admins only)
dashboard.GET("/platform", operatorMiddleware, cache.CacheMiddleware(redisCache, 1*time.Minute), dashboardHandler.GetPlatformStats)
dashboard.GET("/resources", operatorMiddleware, cache.CacheMiddleware(redisCache, 1*time.Minute), dashboardHandler.GetResourceUsage)
dashboard.GET("/users", operatorMiddleware, cache.CacheMiddleware(redisCache, 2*time.Minute), dashboardHandler.GetUserUsageStats)
dashboard.GET("/templates", operatorMiddleware, cache.CacheMiddleware(redisCache, 5*time.Minute), dashboardHandler.GetTemplateUsageStats)
dashboard.GET("/timeline", operatorMiddleware, cache.CacheMiddleware(redisCache, 5*time.Minute), dashboardHandler.GetActivityTimeline)
}

// Session activity recording and queries
sessionActivity := protected.Group("/sessions/:sessionId/activity")
{
// Log new activity event (for internal API use)
sessionActivity.POST("/log", sessionActivityHandler.LogActivityEvent)

// Get session activity log
sessionActivity.GET("", cache.CacheMiddleware(redisCache, 30*time.Second), sessionActivityHandler.GetSessionActivity)

// Get session timeline (chronological view)
sessionActivity.GET("/timeline", cache.CacheMiddleware(redisCache, 1*time.Minute), sessionActivityHandler.GetSessionTimeline)
}

// Activity statistics and user activity (admins/operators)
activity := protected.Group("/activity")
{
// Activity statistics (admins/operators only)
activity.GET("/stats", operatorMiddleware, cache.CacheMiddleware(redisCache, 2*time.Minute), sessionActivityHandler.GetActivityStats)

// User activity across all sessions (users can view their own)
activity.GET("/users/:userId", sessionActivityHandler.GetUserSessionActivity)
}

// API Key management (users can manage their own keys)
apiKeys := protected.Group("/api-keys")
{
// Create new API key (returns full key only once)
apiKeys.POST("", apiKeyHandler.CreateAPIKey)

// List user's API keys (does not return full keys)
apiKeys.GET("", cache.CacheMiddleware(redisCache, 1*time.Minute), apiKeyHandler.ListAPIKeys)

// Revoke an API key (soft delete - sets is_active to false)
apiKeys.POST("/:id/revoke", apiKeyHandler.RevokeAPIKey)

// Permanently delete an API key
apiKeys.DELETE("/:id", apiKeyHandler.DeleteAPIKey)

// Get usage statistics for an API key
apiKeys.GET("/:id/usage", cache.CacheMiddleware(redisCache, 30*time.Second), apiKeyHandler.GetAPIKeyUsage)
}

// User preferences and settings - using dedicated handler (all authenticated users)
preferencesHandler.RegisterRoutes(protected)

// Notifications system - using dedicated handler (all authenticated users)
notificationsHandler.RegisterRoutes(protected)

// Advanced search and filtering - using dedicated handler (all authenticated users)
searchHandler.RegisterRoutes(protected)

// Session snapshots and restore - using dedicated handler (all authenticated users)
snapshotsHandler.RegisterRoutes(protected)

// Session templates and presets - using dedicated handler (all authenticated users)
sessionTemplatesHandler.RegisterRoutes(protected)

// Batch operations for sessions - using dedicated handler (all authenticated users)
batchHandler.RegisterRoutes(protected)

// Advanced monitoring and metrics - using dedicated handler (operators/admins only)
monitoringHandler.RegisterRoutes(protected.Group("", operatorMiddleware))

// Resource quotas and limits enforcement - using dedicated handler (operators/admins only)
quotasHandler.RegisterRoutes(protected.Group("", operatorMiddleware))

// Cost management and billing - using dedicated handler (all authenticated users)
billingHandler.RegisterRoutes(protected)

// Metrics (operators/admins only)
protected.GET("/metrics", operatorMiddleware, h.GetMetrics)
}
Expand All @@ -410,6 +613,9 @@ func setupRoutes(router *gin.Engine, h *api.Handler, userHandler *handlers.UserH
ws.GET("/logs/:namespace/:pod", operatorMiddleware, h.LogsWebSocket)
}

// Real-time updates via WebSocket - using dedicated handler (all authenticated users)
websocketHandler.RegisterRoutes(router.Group("/api/v1", authMiddleware))

// Webhook endpoints (HMAC signature validation required)
webhooks := router.Group("/webhooks")
{
Expand Down
Loading
Loading