feat(auth): implement master key authentication system with child key issuance

Add admin and master authentication layers with JWT support. Replace direct
key creation with hierarchical master/child key system. Update database
schema to support master accounts with configurable limits and epoch-based
key revocation. Add health check endpoint with system status monitoring.

BREAKING CHANGE: Removed direct POST /keys endpoint in favor of master-based
key issuance through /v1/tokens. Database migration requires dropping old User
table and creating Master table with new relationships.
This commit is contained in:
zenfun
2025-12-05 00:16:47 +08:00
parent 5360cc6f1a
commit 8645b22b83
16 changed files with 618 additions and 229 deletions

View File

@@ -11,6 +11,7 @@ import (
"github.com/ez-api/ez-api/internal/api"
"github.com/ez-api/ez-api/internal/config"
"github.com/ez-api/ez-api/internal/middleware"
"github.com/ez-api/ez-api/internal/model"
"github.com/ez-api/ez-api/internal/service"
"github.com/gin-gonic/gin"
@@ -57,7 +58,7 @@ func main() {
log.Println("Connected to PostgreSQL successfully")
// Auto Migrate
if err := db.AutoMigrate(&model.User{}, &model.Provider{}, &model.Key{}, &model.Model{}, &model.LogRecord{}); err != nil {
if err := db.AutoMigrate(&model.Master{}, &model.Key{}, &model.Provider{}, &model.Model{}, &model.LogRecord{}); err != nil {
log.Fatalf("Failed to auto migrate: %v", err)
}
@@ -67,7 +68,17 @@ func main() {
logCtx, cancelLogs := context.WithCancel(context.Background())
defer cancelLogs()
logWriter.Start(logCtx)
adminService, err := service.NewAdminService()
if err != nil {
log.Fatalf("Failed to create admin service: %v", err)
}
masterService := service.NewMasterService(db)
healthService := service.NewHealthCheckService(db, rdb)
handler := api.NewHandler(db, syncService, logWriter)
adminHandler := api.NewAdminHandler(masterService)
masterHandler := api.NewMasterHandler(masterService)
// 4.1 Prime Redis snapshots so DP can start with data
if err := syncService.SyncAll(db); err != nil {
@@ -77,17 +88,52 @@ func main() {
// 5. Setup Gin Router
r := gin.Default()
// CORS Middleware
r.Use(func(c *gin.Context) {
c.Writer.Header().Set("Access-Control-Allow-Origin", "*") // TODO: Restrict this in production
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
})
// Health Check Endpoint
r.GET("/health", func(c *gin.Context) {
c.String(http.StatusOK, "OK")
status := healthService.Check(c.Request.Context())
httpStatus := http.StatusOK
if status.Status == "down" {
httpStatus = http.StatusServiceUnavailable
}
c.JSON(httpStatus, status)
})
// API Routes
r.POST("/providers", handler.CreateProvider)
r.POST("/keys", handler.CreateKey)
r.POST("/models", handler.CreateModel)
r.GET("/models", handler.ListModels)
r.POST("/sync/snapshot", handler.SyncSnapshot)
// Admin Routes
adminGroup := r.Group("/admin")
adminGroup.Use(middleware.AdminAuthMiddleware(adminService))
{
adminGroup.POST("/masters", adminHandler.CreateMaster)
// Other admin routes for managing providers, models, etc.
adminGroup.POST("/providers", handler.CreateProvider)
adminGroup.POST("/models", handler.CreateModel)
adminGroup.GET("/models", handler.ListModels)
adminGroup.POST("/sync/snapshot", handler.SyncSnapshot)
}
// Master Routes
masterGroup := r.Group("/v1")
masterGroup.Use(middleware.MasterAuthMiddleware(masterService))
{
masterGroup.POST("/tokens", masterHandler.IssueChildKey)
}
// Public/General Routes (if any)
r.POST("/logs", handler.IngestLog)
srv := &http.Server{