add server api get and update service control endpoints

This commit is contained in:
Fran Jurmanović
2025-07-29 20:50:44 +02:00
parent 44acb170a7
commit 647f4f7487
27 changed files with 424 additions and 2025 deletions

View File

@@ -2,6 +2,7 @@ package api
import (
"acc-server-manager/local/controller"
"acc-server-manager/local/middleware"
"acc-server-manager/local/utl/common"
"acc-server-manager/local/utl/configs"
"acc-server-manager/local/utl/logging"
@@ -29,8 +30,12 @@ func Init(di *dig.Container, app *fiber.App) {
Lookup: groups.Group("/lookup"),
StateHistory: serverIdGroup.Group("/state-history"),
Membership: groups.Group("/membership"),
System: groups.Group("/system"),
}
accessKeyMiddleware := middleware.NewAccessKeyMiddleware()
routeGroups.Api.Use(accessKeyMiddleware.Authenticate)
err := di.Provide(func() *common.RouteGroups {
return routeGroups
})

View File

@@ -1,149 +0,0 @@
package controller
import (
"acc-server-manager/local/middleware"
"acc-server-manager/local/service"
"acc-server-manager/local/utl/common"
"acc-server-manager/local/utl/error_handler"
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
)
type ApiController struct {
service *service.ApiService
errorHandler *error_handler.ControllerErrorHandler
}
// NewApiController
// Initializes ApiController.
//
// Args:
// *services.ApiService: API service
// *Fiber.RouterGroup: Fiber Router Group
// Returns:
// *ApiController: Controller for "api" interactions
func NewApiController(as *service.ApiService, routeGroups *common.RouteGroups, auth *middleware.AuthMiddleware) *ApiController {
ac := &ApiController{
service: as,
errorHandler: error_handler.NewControllerErrorHandler(),
}
apiGroup := routeGroups.Api
apiGroup.Use(auth.Authenticate)
apiGroup.Get("/", ac.getFirst)
apiGroup.Get("/:service", ac.getStatus)
apiGroup.Post("/start", ac.startServer)
apiGroup.Post("/stop", ac.stopServer)
apiGroup.Post("/restart", ac.restartServer)
return ac
}
// getFirst returns API
//
// @Summary Return API
// @Description Return API
// @Tags api
// @Success 200 {array} string
// @Router /v1/api [get]
func (ac *ApiController) getFirst(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusOK)
}
// getStatus returns service status
//
// @Summary Return service status
// @Description Returns service status
// @Param service path string true "required"
// @Tags api
// @Success 200 {array} string
// @Router /v1/api/{service} [get]
func (ac *ApiController) getStatus(c *fiber.Ctx) error {
service := c.Params("service")
if service == "" {
serverId := c.Params("service")
if _, err := uuid.Parse(serverId); err != nil {
return ac.errorHandler.HandleUUIDError(c, "server ID")
}
c.Locals("serverId", serverId)
} else {
c.Locals("service", service)
}
apiModel, err := ac.service.GetStatus(c)
if err != nil {
return ac.errorHandler.HandleServiceError(c, err)
}
return c.SendString(string(apiModel))
}
// startServer starts service
//
// @Summary Start service
// @Description Starts service
// @Param name body string true "required"
// @Tags api
// @Success 200 {array} string
// @Router /v1/api/start [post]
func (ac *ApiController) startServer(c *fiber.Ctx) error {
model := new(Service)
if err := c.BodyParser(model); err != nil {
return ac.errorHandler.HandleParsingError(c, err)
}
c.Locals("service", model.Name)
c.Locals("serverId", model.ServerId)
apiModel, err := ac.service.ApiStartServer(c)
if err != nil {
return ac.errorHandler.HandleServiceError(c, err)
}
return c.SendString(apiModel)
}
// stopServer stops service
//
// @Summary Stop service
// @Description Stops service
// @Param name body string true "required"
// @Tags api
// @Success 200 {array} string
// @Router /v1/api/stop [post]
func (ac *ApiController) stopServer(c *fiber.Ctx) error {
model := new(Service)
if err := c.BodyParser(model); err != nil {
return ac.errorHandler.HandleParsingError(c, err)
}
c.Locals("service", model.Name)
c.Locals("serverId", model.ServerId)
apiModel, err := ac.service.ApiStopServer(c)
if err != nil {
return ac.errorHandler.HandleServiceError(c, err)
}
return c.SendString(apiModel)
}
// restartServer returns API
//
// @Summary Restart service
// @Description Restarts service
// @Param name body string true "required"
// @Tags api
// @Success 200 {array} string
// @Router /v1/api/restart [post]
func (ac *ApiController) restartServer(c *fiber.Ctx) error {
model := new(Service)
if err := c.BodyParser(model); err != nil {
return ac.errorHandler.HandleParsingError(c, err)
}
c.Locals("service", model.Name)
c.Locals("serverId", model.ServerId)
apiModel, err := ac.service.ApiRestartServer(c)
if err != nil {
return ac.errorHandler.HandleServiceError(c, err)
}
return c.SendString(apiModel)
}
type Service struct {
Name string `json:"name" xml:"name" form:"name"`
ServerId string `json:"serverId" xml:"serverId" form:"serverId"`
}

View File

@@ -31,10 +31,28 @@ func NewServerController(ss *service.ServerService, routeGroups *common.RouteGro
serverRoutes.Post("/", auth.HasPermission(model.ServerCreate), ac.CreateServer)
serverRoutes.Put("/:id", auth.HasPermission(model.ServerUpdate), ac.UpdateServer)
serverRoutes.Delete("/:id", auth.HasPermission(model.ServerDelete), ac.DeleteServer)
apiServerRoutes := routeGroups.Api.Group("/server")
apiServerRoutes.Get("/", auth.HasPermission(model.ServerView), ac.GetAllApi)
return ac
}
// GetAll returns Servers
func (ac *ServerController) GetAllApi(c *fiber.Ctx) error {
var filter model.ServerFilter
if err := common.ParseQueryFilter(c, &filter); err != nil {
return ac.errorHandler.HandleValidationError(c, err, "query_filter")
}
ServerModel, err := ac.service.GetAll(c, &filter)
if err != nil {
return ac.errorHandler.HandleServiceError(c, err)
}
var apiServers []model.ServerAPI
for _, server := range *ServerModel {
apiServers = append(apiServers, *server.ToServerAPI())
}
return c.JSON(apiServers)
}
func (ac *ServerController) GetAll(c *fiber.Ctx) error {
var filter model.ServerFilter
if err := common.ParseQueryFilter(c, &filter); err != nil {

View File

@@ -0,0 +1,148 @@
package controller
import (
"acc-server-manager/local/middleware"
"acc-server-manager/local/service"
"acc-server-manager/local/utl/common"
"acc-server-manager/local/utl/error_handler"
"github.com/gofiber/fiber/v2"
)
type ServiceControlController struct {
service *service.ServiceControlService
errorHandler *error_handler.ControllerErrorHandler
}
// NewServiceControlController
// Initializes ServiceControlController.
//
// Args:
// *services.ServiceControlService: Service control service
// *Fiber.RouterGroup: Fiber Router Group
// Returns:
// *ServiceControlController: Controller for service control interactions
func NewServiceControlController(as *service.ServiceControlService, routeGroups *common.RouteGroups, auth *middleware.AuthMiddleware) *ServiceControlController {
ac := &ServiceControlController{
service: as,
errorHandler: error_handler.NewControllerErrorHandler(),
}
serviceRoutes := routeGroups.Server.Group("/service")
serviceRoutes.Get("/:service", ac.getStatus)
serviceRoutes.Post("/start", ac.startServer)
serviceRoutes.Post("/stop", ac.stopServer)
serviceRoutes.Post("/restart", ac.restartServer)
return ac
}
// getStatus returns service status
//
// @Summary Get service status
// @Description Get the current status of a Windows service
// @Tags Service Control
// @Accept json
// @Produce json
// @Param service path string true "Service name"
// @Success 200 {object} object{status=string,state=string} "Service status information"
// @Failure 400 {object} error_handler.ErrorResponse "Invalid service name"
// @Failure 401 {object} error_handler.ErrorResponse "Unauthorized"
// @Failure 404 {object} error_handler.ErrorResponse "Service not found"
// @Failure 500 {object} error_handler.ErrorResponse "Internal server error"
// @Security BearerAuth
// @Router /v1/service-control/{service} [get]
func (ac *ServiceControlController) getStatus(c *fiber.Ctx) error {
id := c.Params("id")
c.Locals("serverId", id)
apiModel, err := ac.service.GetStatus(c)
if err != nil {
return ac.errorHandler.HandleServiceError(c, err)
}
return c.SendString(string(apiModel))
}
// startServer starts service
//
// @Summary Start a Windows service
// @Description Start a stopped Windows service for an ACC server
// @Tags Service Control
// @Accept json
// @Produce json
// @Param service body object{name=string} true "Service name to start"
// @Success 200 {object} object{message=string} "Service started successfully"
// @Failure 400 {object} error_handler.ErrorResponse "Invalid request body"
// @Failure 401 {object} error_handler.ErrorResponse "Unauthorized"
// @Failure 403 {object} error_handler.ErrorResponse "Insufficient permissions"
// @Failure 404 {object} error_handler.ErrorResponse "Service not found"
// @Failure 409 {object} error_handler.ErrorResponse "Service already running"
// @Failure 500 {object} error_handler.ErrorResponse "Internal server error"
// @Security BearerAuth
// @Router /v1/service-control/start [post]
func (ac *ServiceControlController) startServer(c *fiber.Ctx) error {
id := c.Params("id")
c.Locals("serverId", id)
apiModel, err := ac.service.ServiceControlStartServer(c)
if err != nil {
return ac.errorHandler.HandleServiceError(c, err)
}
return c.SendString(apiModel)
}
// stopServer stops service
//
// @Summary Stop a Windows service
// @Description Stop a running Windows service for an ACC server
// @Tags Service Control
// @Accept json
// @Produce json
// @Param service body object{name=string} true "Service name to stop"
// @Success 200 {object} object{message=string} "Service stopped successfully"
// @Failure 400 {object} error_handler.ErrorResponse "Invalid request body"
// @Failure 401 {object} error_handler.ErrorResponse "Unauthorized"
// @Failure 403 {object} error_handler.ErrorResponse "Insufficient permissions"
// @Failure 404 {object} error_handler.ErrorResponse "Service not found"
// @Failure 409 {object} error_handler.ErrorResponse "Service already stopped"
// @Failure 500 {object} error_handler.ErrorResponse "Internal server error"
// @Security BearerAuth
// @Router /v1/service-control/stop [post]
func (ac *ServiceControlController) stopServer(c *fiber.Ctx) error {
id := c.Params("id")
c.Locals("serverId", id)
apiModel, err := ac.service.ServiceControlStopServer(c)
if err != nil {
return ac.errorHandler.HandleServiceError(c, err)
}
return c.SendString(apiModel)
}
// restartServer restarts service
//
// @Summary Restart a Windows service
// @Description Stop and start a Windows service for an ACC server
// @Tags Service Control
// @Accept json
// @Produce json
// @Param service body object{name=string} true "Service name to restart"
// @Success 200 {object} object{message=string} "Service restarted successfully"
// @Failure 400 {object} error_handler.ErrorResponse "Invalid request body"
// @Failure 401 {object} error_handler.ErrorResponse "Unauthorized"
// @Failure 403 {object} error_handler.ErrorResponse "Insufficient permissions"
// @Failure 404 {object} error_handler.ErrorResponse "Service not found"
// @Failure 500 {object} error_handler.ErrorResponse "Internal server error"
// @Security BearerAuth
// @Router /v1/service-control/restart [post]
func (ac *ServiceControlController) restartServer(c *fiber.Ctx) error {
id := c.Params("id")
c.Locals("serverId", id)
apiModel, err := ac.service.ServiceControlRestartServer(c)
if err != nil {
return ac.errorHandler.HandleServiceError(c, err)
}
return c.SendString(apiModel)
}
type Service struct {
Name string `json:"name" xml:"name" form:"name"`
ServerId string `json:"serverId" xml:"serverId" form:"serverId"`
}

View File

@@ -0,0 +1,38 @@
package controller
import (
"acc-server-manager/local/utl/common"
"github.com/gofiber/fiber/v2"
)
type SystemController struct {
}
// NewSystemController
// Initializes SystemController.
//
// Args:
// *services.SystemService: Service control service
// *Fiber.RouterGroup: Fiber Router Group
// Returns:
// *SystemController: Controller for service control interactions
func NewSystemController(routeGroups *common.RouteGroups) *SystemController {
ac := &SystemController{}
apiGroup := routeGroups.System
apiGroup.Get("/health", ac.getFirst)
return ac
}
// getFirst returns service control status
//
// @Summary Return service control status
// @Description Return service control status
// @Tags service-control
// @Success 200 {array} string
// @Router /v1/service-control [get]
func (ac *SystemController) getFirst(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusOK)
}

View File

@@ -0,0 +1,59 @@
package middleware
import (
"acc-server-manager/local/utl/configs"
"acc-server-manager/local/utl/logging"
"time"
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
)
// AccessKeyMiddleware provides authentication and permission middleware.
type AccessKeyMiddleware struct {
userInfo CachedUserInfo
}
// NewAccessKeyMiddleware creates a new AccessKeyMiddleware.
func NewAccessKeyMiddleware() *AccessKeyMiddleware {
auth := &AccessKeyMiddleware{
userInfo: CachedUserInfo{UserID: uuid.New().String(), Username: "access_key", RoleName: "Admin", Permissions: make(map[string]bool), CachedAt: time.Now()},
}
return auth
}
// Authenticate is a middleware for JWT authentication with enhanced security.
func (m *AccessKeyMiddleware) Authenticate(ctx *fiber.Ctx) error {
// Log authentication attempt
ip := ctx.IP()
userAgent := ctx.Get("User-Agent")
authHeader := ctx.Get("Access-Key")
if authHeader == "" {
logging.Error("Authentication failed: missing Access-Key header from IP %s", ip)
return ctx.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
"error": "Missing or malformed JWT",
})
}
if len(authHeader) < 10 || len(authHeader) > 2048 {
logging.Error("Authentication failed: invalid token length from IP %s", ip)
return ctx.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
"error": "Invalid or expired JWT",
})
}
if authHeader != configs.AccessKey {
logging.Error("Authentication failed: invalid token from IP %s, User-Agent: %s", ip, userAgent)
return ctx.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
"error": "Invalid or expired JWT",
})
}
ctx.Locals("userID", m.userInfo.UserID)
ctx.Locals("userInfo", m.userInfo)
ctx.Locals("authTime", time.Now())
logging.InfoWithContext("AUTH", "User %s authenticated successfully from IP %s", m.userInfo.UserID, ip)
return ctx.Next()
}

View File

@@ -35,9 +35,9 @@ type ConfigFilter struct {
}
// ApiFilter defines filtering options for Api queries
type ApiFilter struct {
type ServiceControlFilter struct {
BaseFilter
Api string `query:"api"`
ServiceControl string `query:"serviceControl"`
}
// MembershipFilter defines filtering options for User queries

View File

@@ -16,6 +16,25 @@ const (
ServiceNamePrefix = "ACC-Server"
)
// Server represents an ACC server instance
type ServerAPI struct {
Name string `json:"name"`
Status ServiceStatus `json:"status"`
State *ServerState `json:"state"`
PlayerCount int `json:"playerCount"`
Track string `json:"track"`
}
func (s *Server) ToServerAPI() *ServerAPI {
return &ServerAPI{
Name: s.Name,
Status: s.Status,
State: s.State,
PlayerCount: s.State.PlayerCount,
Track: s.State.Track,
}
}
// Server represents an ACC server instance
type Server struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;" json:"id"`

View File

@@ -104,6 +104,6 @@ func (s ServiceStatus) Value() (driver.Value, error) {
return s.String(), nil
}
type ApiModel struct {
Api string `json:"api"`
type ServiceControlModel struct {
ServiceControl string `json:"serviceControl"`
}

View File

@@ -1,17 +0,0 @@
package repository
import (
"acc-server-manager/local/model"
"gorm.io/gorm"
)
type ApiRepository struct {
*BaseRepository[model.ApiModel, model.ApiFilter]
}
func NewApiRepository(db *gorm.DB) *ApiRepository {
return &ApiRepository{
BaseRepository: NewBaseRepository[model.ApiModel, model.ApiFilter](db, model.ApiModel{}),
}
}

View File

@@ -10,7 +10,7 @@ import (
// Args:
// *dig.Container: Dig Container
func InitializeRepositories(c *dig.Container) {
c.Provide(NewApiRepository)
c.Provide(NewServiceControlRepository)
c.Provide(NewStateHistoryRepository)
c.Provide(NewServerRepository)
c.Provide(NewConfigRepository)

View File

@@ -0,0 +1,17 @@
package repository
import (
"acc-server-manager/local/model"
"gorm.io/gorm"
)
type ServiceControlRepository struct {
*BaseRepository[model.ServiceControlModel, model.ServiceControlFilter]
}
func NewServiceControlRepository(db *gorm.DB) *ServiceControlRepository {
return &ServiceControlRepository{
BaseRepository: NewBaseRepository[model.ServiceControlModel, model.ServiceControlFilter](db, model.ServiceControlModel{}),
}
}

View File

@@ -26,7 +26,7 @@ const (
type ServerService struct {
repository *repository.ServerRepository
stateHistoryRepo *repository.StateHistoryRepository
apiService *ApiService
apiService *ServiceControlService
configService *ConfigService
steamService *SteamService
windowsService *WindowsService
@@ -63,7 +63,7 @@ func (s *ServerService) ensureLogTailing(server *model.Server, instance *trackin
func NewServerService(
repository *repository.ServerRepository,
stateHistoryRepo *repository.StateHistoryRepository,
apiService *ApiService,
apiService *ServiceControlService,
configService *ConfigService,
steamService *SteamService,
windowsService *WindowsService,

View File

@@ -20,7 +20,7 @@ func InitializeServices(c *dig.Container) {
// Provide services
c.Provide(NewServerService)
c.Provide(NewStateHistoryService)
c.Provide(NewApiService)
c.Provide(NewServiceControlService)
c.Provide(NewConfigService)
c.Provide(NewLookupService)
c.Provide(NewSteamService)
@@ -29,7 +29,7 @@ func InitializeServices(c *dig.Container) {
c.Provide(NewMembershipService)
logging.Debug("Initializing service dependencies")
err := c.Invoke(func(server *ServerService, api *ApiService, config *ConfigService) {
err := c.Invoke(func(server *ServerService, api *ServiceControlService, config *ConfigService) {
logging.Debug("Setting up service cross-references")
api.SetServerService(server)
config.SetServerService(server)

View File

@@ -10,17 +10,17 @@ import (
"github.com/gofiber/fiber/v2"
)
type ApiService struct {
repository *repository.ApiRepository
type ServiceControlService struct {
repository *repository.ServiceControlRepository
serverRepository *repository.ServerRepository
serverService *ServerService
statusCache *model.ServerStatusCache
windowsService *WindowsService
}
func NewApiService(repository *repository.ApiRepository,
serverRepository *repository.ServerRepository) *ApiService {
return &ApiService{
func NewServiceControlService(repository *repository.ServiceControlRepository,
serverRepository *repository.ServerRepository) *ServiceControlService {
return &ServiceControlService{
repository: repository,
serverRepository: serverRepository,
statusCache: model.NewServerStatusCache(model.CacheConfig{
@@ -32,11 +32,11 @@ func NewApiService(repository *repository.ApiRepository,
}
}
func (as *ApiService) SetServerService(serverService *ServerService) {
func (as *ServiceControlService) SetServerService(serverService *ServerService) {
as.serverService = serverService
}
func (as *ApiService) GetStatus(ctx *fiber.Ctx) (string, error) {
func (as *ServiceControlService) GetStatus(ctx *fiber.Ctx) (string, error) {
serviceName, err := as.GetServiceName(ctx)
if err != nil {
return "", err
@@ -59,7 +59,7 @@ func (as *ApiService) GetStatus(ctx *fiber.Ctx) (string, error) {
return status.String(), nil
}
func (as *ApiService) ApiStartServer(ctx *fiber.Ctx) (string, error) {
func (as *ServiceControlService) ServiceControlStartServer(ctx *fiber.Ctx) (string, error) {
serviceName, err := as.GetServiceName(ctx)
if err != nil {
return "", err
@@ -83,7 +83,7 @@ func (as *ApiService) ApiStartServer(ctx *fiber.Ctx) (string, error) {
return status.String(), nil
}
func (as *ApiService) ApiStopServer(ctx *fiber.Ctx) (string, error) {
func (as *ServiceControlService) ServiceControlStopServer(ctx *fiber.Ctx) (string, error) {
serviceName, err := as.GetServiceName(ctx)
if err != nil {
return "", err
@@ -107,7 +107,7 @@ func (as *ApiService) ApiStopServer(ctx *fiber.Ctx) (string, error) {
return status.String(), nil
}
func (as *ApiService) ApiRestartServer(ctx *fiber.Ctx) (string, error) {
func (as *ServiceControlService) ServiceControlRestartServer(ctx *fiber.Ctx) (string, error) {
serviceName, err := as.GetServiceName(ctx)
if err != nil {
return "", err
@@ -131,12 +131,12 @@ func (as *ApiService) ApiRestartServer(ctx *fiber.Ctx) (string, error) {
return status.String(), nil
}
func (as *ApiService) StatusServer(serviceName string) (string, error) {
func (as *ServiceControlService) StatusServer(serviceName string) (string, error) {
return as.windowsService.Status(context.Background(), serviceName)
}
// GetCachedStatus gets the cached status for a service name without requiring fiber context
func (as *ApiService) GetCachedStatus(serviceName string) (string, error) {
func (as *ServiceControlService) GetCachedStatus(serviceName string) (string, error) {
// Try to get status from cache
if status, shouldCheck := as.statusCache.GetStatus(serviceName); !shouldCheck {
return status.String(), nil
@@ -154,7 +154,7 @@ func (as *ApiService) GetCachedStatus(serviceName string) (string, error) {
return status.String(), nil
}
func (as *ApiService) StartServer(serviceName string) (string, error) {
func (as *ServiceControlService) StartServer(serviceName string) (string, error) {
status, err := as.windowsService.Start(context.Background(), serviceName)
if err != nil {
return "", err
@@ -168,7 +168,7 @@ func (as *ApiService) StartServer(serviceName string) (string, error) {
return status, err
}
func (as *ApiService) StopServer(serviceName string) (string, error) {
func (as *ServiceControlService) StopServer(serviceName string) (string, error) {
status, err := as.windowsService.Stop(context.Background(), serviceName)
if err != nil {
return "", err
@@ -183,7 +183,7 @@ func (as *ApiService) StopServer(serviceName string) (string, error) {
return status, err
}
func (as *ApiService) RestartServer(serviceName string) (string, error) {
func (as *ServiceControlService) RestartServer(serviceName string) (string, error) {
status, err := as.windowsService.Restart(context.Background(), serviceName)
if err != nil {
return "", err
@@ -197,7 +197,7 @@ func (as *ApiService) RestartServer(serviceName string) (string, error) {
return status, err
}
func (as *ApiService) GetServiceName(ctx *fiber.Ctx) (string, error) {
func (as *ServiceControlService) GetServiceName(ctx *fiber.Ctx) (string, error) {
var server *model.Server
var err error
serviceName, ok := ctx.Locals("service").(string)

View File

@@ -24,6 +24,7 @@ type RouteGroups struct {
Lookup fiber.Router
StateHistory fiber.Router
Membership fiber.Router
System fiber.Router
}
func CheckError(err error) {

View File

@@ -13,14 +13,16 @@ var (
Secret string
SecretCode string
EncryptionKey string
AccessKey string
)
func init() {
func Init() {
godotenv.Load()
// Fail fast if critical environment variables are missing
Secret = getEnvRequired("APP_SECRET")
SecretCode = getEnvRequired("APP_SECRET_CODE")
EncryptionKey = getEnvRequired("ENCRYPTION_KEY")
AccessKey = getEnvRequired("ACCESS_KEY")
if len(EncryptionKey) != 32 {
log.Fatal("ENCRYPTION_KEY must be exactly 32 bytes long for AES-256")

View File

@@ -35,7 +35,7 @@ func Migrate(db *gorm.DB) {
// Run GORM AutoMigrate for all models
err := db.AutoMigrate(
&model.ApiModel{},
&model.ServiceControlModel{},
&model.Config{},
&model.Track{},
&model.CarModel{},
@@ -55,7 +55,7 @@ func Migrate(db *gorm.DB) {
// Don't panic, just log the error as custom migrations may have handled this
}
db.FirstOrCreate(&model.ApiModel{Api: "Works"})
db.FirstOrCreate(&model.ServiceControlModel{ServiceControl: "Works"})
Seed(db)
}

View File

@@ -22,7 +22,7 @@ type Claims struct {
}
// init initializes the JWT secret key from environment variable
func init() {
func Init() {
jwtSecret := os.Getenv("JWT_SECRET")
if jwtSecret == "" {
log.Fatal("JWT_SECRET environment variable is required and cannot be empty")