A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.
This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).
Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
144 lines
3.8 KiB
Go
144 lines
3.8 KiB
Go
package main
|
|
|
|
import (
|
|
"log"
|
|
|
|
"github.com/breakpilot/billing-service/internal/config"
|
|
"github.com/breakpilot/billing-service/internal/database"
|
|
"github.com/breakpilot/billing-service/internal/handlers"
|
|
"github.com/breakpilot/billing-service/internal/middleware"
|
|
"github.com/breakpilot/billing-service/internal/services"
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
func main() {
|
|
// Load configuration
|
|
cfg, err := config.Load()
|
|
if err != nil {
|
|
log.Fatalf("Failed to load config: %v", err)
|
|
}
|
|
|
|
// Initialize database
|
|
db, err := database.Connect(cfg.DatabaseURL)
|
|
if err != nil {
|
|
log.Fatalf("Failed to connect to database: %v", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
// Run migrations
|
|
if err := database.Migrate(db); err != nil {
|
|
log.Fatalf("Failed to run migrations: %v", err)
|
|
}
|
|
|
|
// Setup Gin router
|
|
if cfg.Environment == "production" {
|
|
gin.SetMode(gin.ReleaseMode)
|
|
}
|
|
|
|
router := gin.Default()
|
|
|
|
// Global middleware
|
|
router.Use(middleware.CORS())
|
|
router.Use(middleware.RequestLogger())
|
|
router.Use(middleware.RateLimiter())
|
|
|
|
// Health check (no auth required)
|
|
router.GET("/health", func(c *gin.Context) {
|
|
c.JSON(200, gin.H{
|
|
"status": "healthy",
|
|
"service": "billing-service",
|
|
"version": "1.0.0",
|
|
})
|
|
})
|
|
|
|
// Initialize services
|
|
subscriptionService := services.NewSubscriptionService(db)
|
|
|
|
// Create Stripe service (mock or real depending on config)
|
|
var stripeService *services.StripeService
|
|
if cfg.IsMockMode() {
|
|
log.Println("Starting in MOCK MODE - Stripe API calls will be simulated")
|
|
stripeService = services.NewMockStripeService(
|
|
cfg.BillingSuccessURL,
|
|
cfg.BillingCancelURL,
|
|
cfg.TrialPeriodDays,
|
|
subscriptionService,
|
|
)
|
|
} else {
|
|
stripeService = services.NewStripeService(
|
|
cfg.StripeSecretKey,
|
|
cfg.StripeWebhookSecret,
|
|
cfg.BillingSuccessURL,
|
|
cfg.BillingCancelURL,
|
|
cfg.TrialPeriodDays,
|
|
subscriptionService,
|
|
)
|
|
}
|
|
|
|
entitlementService := services.NewEntitlementService(db, subscriptionService)
|
|
usageService := services.NewUsageService(db, entitlementService)
|
|
|
|
// Initialize handlers
|
|
billingHandler := handlers.NewBillingHandler(
|
|
db,
|
|
subscriptionService,
|
|
stripeService,
|
|
entitlementService,
|
|
usageService,
|
|
)
|
|
webhookHandler := handlers.NewWebhookHandler(
|
|
db,
|
|
cfg.StripeWebhookSecret,
|
|
subscriptionService,
|
|
entitlementService,
|
|
)
|
|
|
|
// API v1 routes
|
|
v1 := router.Group("/api/v1/billing")
|
|
{
|
|
// Stripe webhook (no auth - uses Stripe signature)
|
|
v1.POST("/webhook", webhookHandler.HandleStripeWebhook)
|
|
|
|
// =============================================
|
|
// User Endpoints (require JWT auth)
|
|
// =============================================
|
|
user := v1.Group("")
|
|
user.Use(middleware.AuthMiddleware(cfg.JWTSecret))
|
|
{
|
|
// Subscription status and management
|
|
user.GET("/status", billingHandler.GetBillingStatus)
|
|
user.GET("/plans", billingHandler.GetPlans)
|
|
user.POST("/trial/start", billingHandler.StartTrial)
|
|
user.POST("/change-plan", billingHandler.ChangePlan)
|
|
user.POST("/cancel", billingHandler.CancelSubscription)
|
|
user.GET("/portal", billingHandler.GetCustomerPortal)
|
|
}
|
|
|
|
// =============================================
|
|
// Internal Endpoints (service-to-service)
|
|
// =============================================
|
|
internal := v1.Group("")
|
|
internal.Use(middleware.InternalAPIKeyMiddleware(cfg.InternalAPIKey))
|
|
{
|
|
// Entitlements
|
|
internal.GET("/entitlements/:userId", billingHandler.GetEntitlements)
|
|
internal.GET("/entitlements/check/:userId/:feature", billingHandler.CheckEntitlement)
|
|
|
|
// Usage tracking
|
|
internal.POST("/usage/track", billingHandler.TrackUsage)
|
|
internal.GET("/usage/check/:userId/:type", billingHandler.CheckUsage)
|
|
}
|
|
}
|
|
|
|
// Start server
|
|
port := cfg.Port
|
|
if port == "" {
|
|
port = "8083"
|
|
}
|
|
|
|
log.Printf("Starting Billing Service on port %s", port)
|
|
if err := router.Run(":" + port); err != nil {
|
|
log.Fatalf("Failed to start server: %v", err)
|
|
}
|
|
}
|