152 lines
4.0 KiB
Go
152 lines
4.0 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
"time"
|
|
|
|
docs "gitea.d3m0k1d.ru/HellreigN/Control-plane/docs"
|
|
"gitea.d3m0k1d.ru/HellreigN/Control-plane/internal/auth"
|
|
"gitea.d3m0k1d.ru/HellreigN/Control-plane/internal/config"
|
|
"gitea.d3m0k1d.ru/HellreigN/Control-plane/internal/middleware"
|
|
"gitea.d3m0k1d.ru/HellreigN/Control-plane/internal/org"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/pressly/goose/v3"
|
|
swaggerFiles "github.com/swaggo/files"
|
|
ginSwagger "github.com/swaggo/gin-swagger"
|
|
"gorm.io/driver/postgres"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// @title AegisGuard API
|
|
// @version 1.0
|
|
// @description API для AegisGuard control plane
|
|
// @schemes http
|
|
//
|
|
// @securityDefinitions.apikey Bearer
|
|
// @in header
|
|
// @name Authorization
|
|
// @description Type "Bearer" followed by a space and the JWT token.
|
|
|
|
func main() {
|
|
cfg, err := config.Load()
|
|
if err != nil {
|
|
log.Fatalf("failed to load config: %v", err)
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
gormDB, err := gorm.Open(postgres.Open(cfg.DatabaseURL), &gorm.Config{})
|
|
if err != nil {
|
|
log.Fatalf("failed to connect to postgres: %v", err)
|
|
}
|
|
|
|
sqlDB, err := gormDB.DB()
|
|
if err != nil {
|
|
log.Fatalf("failed to get underlying sql.DB: %v", err)
|
|
}
|
|
|
|
if err := sqlDB.PingContext(ctx); err != nil {
|
|
log.Fatalf("failed to ping postgres: %v", err)
|
|
}
|
|
log.Println("connected to postgres")
|
|
|
|
if err := goose.Up(sqlDB, "migrations"); err != nil {
|
|
log.Fatalf("failed to run migrations: %v", err)
|
|
}
|
|
log.Println("migrations applied")
|
|
|
|
repo := auth.NewRepository(gormDB)
|
|
orgRepo := org.NewRepository(gormDB)
|
|
|
|
svc := auth.NewService(repo, cfg.JWTSecret, cfg.JWTExpiration, cfg.JWTRefreshExpiration)
|
|
handler := auth.NewHandler(svc)
|
|
|
|
orgSvc := org.NewService(orgRepo)
|
|
orgHandler := org.NewHandler(orgSvc)
|
|
|
|
loginLimiter := middleware.NewRateLimiter(10, time.Minute)
|
|
authMW := auth.AuthMiddleware([]byte(cfg.JWTSecret))
|
|
|
|
go func() {
|
|
ticker := time.NewTicker(30 * time.Minute)
|
|
defer ticker.Stop()
|
|
for range ticker.C {
|
|
cleanupCtx, cleanupCancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
if err := repo.DeleteExpiredRefreshTokens(cleanupCtx); err != nil {
|
|
log.Printf("failed to cleanup expired tokens: %v", err)
|
|
}
|
|
cleanupCancel()
|
|
}
|
|
}()
|
|
|
|
gin.SetMode(gin.ReleaseMode)
|
|
r := gin.New()
|
|
r.Use(gin.Logger(), gin.Recovery())
|
|
|
|
docs.SwaggerInfo.Title = "AegisGuard API"
|
|
docs.SwaggerInfo.Version = "1.0"
|
|
docs.SwaggerInfo.Description = "API for AegisGuard"
|
|
docs.SwaggerInfo.Schemes = []string{"http"}
|
|
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
|
r.GET("/health", func(c *gin.Context) {
|
|
c.JSON(200, gin.H{"status": "ok"})
|
|
})
|
|
|
|
api := r.Group("/api/auth")
|
|
{
|
|
api.POST("/register", handler.Register)
|
|
api.POST("/login", loginLimiter.Middleware(), handler.Login)
|
|
api.POST("/refresh", handler.Refresh)
|
|
api.POST("/logout", handler.Logout)
|
|
api.GET("/me", authMW, handler.Me)
|
|
api.PUT("/me", authMW, handler.UpdateProfile)
|
|
api.PUT("/password", authMW, handler.ChangePassword)
|
|
}
|
|
|
|
orgs := r.Group("/api/organizations", authMW)
|
|
{
|
|
orgs.POST("", orgHandler.Create)
|
|
orgs.GET("", orgHandler.List)
|
|
orgs.GET("/:id", orgHandler.GetByID)
|
|
orgs.PUT("/:id", orgHandler.Update)
|
|
orgs.DELETE("/:id", orgHandler.Delete)
|
|
}
|
|
|
|
srv := &http.Server{
|
|
Addr: ":" + cfg.ServerPort,
|
|
Handler: r,
|
|
ReadHeaderTimeout: 10 * time.Second,
|
|
}
|
|
|
|
go func() {
|
|
log.Printf("server starting on :%s", cfg.ServerPort)
|
|
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
|
log.Fatalf("failed to start server: %v", err)
|
|
}
|
|
}()
|
|
|
|
quit := make(chan os.Signal, 1)
|
|
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
|
<-quit
|
|
|
|
log.Println("shutting down server...")
|
|
|
|
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer shutdownCancel()
|
|
|
|
if err := srv.Shutdown(shutdownCtx); err != nil {
|
|
log.Fatalf("server forced to shutdown: %v", err)
|
|
}
|
|
|
|
loginLimiter.Stop()
|
|
_ = sqlDB.Close()
|
|
|
|
log.Println("server stopped")
|
|
}
|