add membership
This commit is contained in:
@@ -2,13 +2,14 @@ package api
|
||||
|
||||
import (
|
||||
"acc-server-manager/local/controller"
|
||||
"acc-server-manager/local/service"
|
||||
"acc-server-manager/local/utl/common"
|
||||
"acc-server-manager/local/utl/configs"
|
||||
"acc-server-manager/local/utl/logging"
|
||||
"os"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/basicauth"
|
||||
"go.uber.org/dig"
|
||||
)
|
||||
|
||||
@@ -18,25 +19,28 @@ import (
|
||||
// Args:
|
||||
// *fiber.App: Fiber Application
|
||||
func Init(di *dig.Container, app *fiber.App) {
|
||||
// Setup initial data for membership
|
||||
di.Invoke(func(membershipService *service.MembershipService) {
|
||||
if err := membershipService.SetupInitialData(context.Background()); err != nil {
|
||||
logging.Panic(fmt.Sprintf("failed to setup initial data: %v", err))
|
||||
}
|
||||
})
|
||||
|
||||
// Protected routes
|
||||
groups := app.Group(configs.Prefix)
|
||||
|
||||
basicAuthConfig := basicauth.New(basicauth.Config{
|
||||
Users: map[string]string{
|
||||
"admin": os.Getenv("PASSWORD"),
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
serverIdGroup := groups.Group("/server/:id")
|
||||
routeGroups := &common.RouteGroups{
|
||||
Api: groups.Group("/api"),
|
||||
Server: groups.Group("/server"),
|
||||
Config: serverIdGroup.Group("/config"),
|
||||
Lookup: groups.Group("/lookup"),
|
||||
Api: groups.Group("/api"),
|
||||
Auth: app.Group("/auth"),
|
||||
Server: groups.Group("/server"),
|
||||
Config: serverIdGroup.Group("/config"),
|
||||
Lookup: groups.Group("/lookup"),
|
||||
StateHistory: serverIdGroup.Group("/state-history"),
|
||||
}
|
||||
|
||||
groups.Use(basicAuthConfig)
|
||||
|
||||
err := di.Provide(func() *common.RouteGroups {
|
||||
return routeGroups
|
||||
})
|
||||
|
||||
@@ -27,9 +27,9 @@ func NewConfigController(as *service.ConfigService, routeGroups *common.RouteGro
|
||||
apiService: as2,
|
||||
}
|
||||
|
||||
routeGroups.Config.Put("/:file", ac.updateConfig)
|
||||
routeGroups.Config.Get("/:file", ac.getConfig)
|
||||
routeGroups.Config.Get("/", ac.getConfigs)
|
||||
routeGroups.Config.Put("/:file", ac.UpdateConfig)
|
||||
routeGroups.Config.Get("/:file", ac.GetConfig)
|
||||
routeGroups.Config.Get("/", ac.GetConfigs)
|
||||
|
||||
return ac
|
||||
}
|
||||
@@ -44,7 +44,7 @@ func NewConfigController(as *service.ConfigService, routeGroups *common.RouteGro
|
||||
// @Tags Config
|
||||
// @Success 200 {array} string
|
||||
// @Router /v1/server/{id}/config/{file} [put]
|
||||
func (ac *ConfigController) updateConfig(c *fiber.Ctx) error {
|
||||
func (ac *ConfigController) UpdateConfig(c *fiber.Ctx) error {
|
||||
restart := c.QueryBool("restart")
|
||||
serverID, _ := c.ParamsInt("id")
|
||||
c.Locals("serverId", serverID)
|
||||
@@ -79,7 +79,7 @@ func (ac *ConfigController) updateConfig(c *fiber.Ctx) error {
|
||||
// @Tags Config
|
||||
// @Success 200 {array} string
|
||||
// @Router /v1/server/{id}/config/{file} [get]
|
||||
func (ac *ConfigController) getConfig(c *fiber.Ctx) error {
|
||||
func (ac *ConfigController) GetConfig(c *fiber.Ctx) error {
|
||||
Model, err := ac.service.GetConfig(c)
|
||||
if err != nil {
|
||||
logging.Error(err.Error())
|
||||
@@ -96,7 +96,7 @@ func (ac *ConfigController) getConfig(c *fiber.Ctx) error {
|
||||
// @Tags Config
|
||||
// @Success 200 {array} string
|
||||
// @Router /v1/server/{id}/config [get]
|
||||
func (ac *ConfigController) getConfigs(c *fiber.Ctx) error {
|
||||
func (ac *ConfigController) GetConfigs(c *fiber.Ctx) error {
|
||||
Model, err := ac.service.GetConfigs(c)
|
||||
if err != nil {
|
||||
logging.Error(err.Error())
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"acc-server-manager/local/middleware"
|
||||
"acc-server-manager/local/service"
|
||||
"acc-server-manager/local/utl/logging"
|
||||
|
||||
@@ -15,6 +16,10 @@ import (
|
||||
func InitializeControllers(c *dig.Container) {
|
||||
service.InitializeServices(c)
|
||||
|
||||
if err := c.Provide(middleware.NewAuthMiddleware); err != nil {
|
||||
logging.Panic("unable to initialize auth middleware")
|
||||
}
|
||||
|
||||
err := c.Invoke(NewApiController)
|
||||
if err != nil {
|
||||
logging.Panic("unable to initialize api controller")
|
||||
@@ -39,4 +44,9 @@ func InitializeControllers(c *dig.Container) {
|
||||
if err != nil {
|
||||
logging.Panic("unable to initialize stateHistory controller")
|
||||
}
|
||||
|
||||
err = c.Invoke(NewMembershipController)
|
||||
if err != nil {
|
||||
logging.Panic("unable to initialize membership controller")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,11 +23,11 @@ func NewLookupController(as *service.LookupService, routeGroups *common.RouteGro
|
||||
ac := &LookupController{
|
||||
service: as,
|
||||
}
|
||||
routeGroups.Lookup.Get("/tracks", ac.getTracks)
|
||||
routeGroups.Lookup.Get("/car-models", ac.getCarModels)
|
||||
routeGroups.Lookup.Get("/driver-categories", ac.getDriverCategories)
|
||||
routeGroups.Lookup.Get("/cup-categories", ac.getCupCategories)
|
||||
routeGroups.Lookup.Get("/session-types", ac.getSessionTypes)
|
||||
routeGroups.Lookup.Get("/tracks", ac.GetTracks)
|
||||
routeGroups.Lookup.Get("/car-models", ac.GetCarModels)
|
||||
routeGroups.Lookup.Get("/driver-categories", ac.GetDriverCategories)
|
||||
routeGroups.Lookup.Get("/cup-categories", ac.GetCupCategories)
|
||||
routeGroups.Lookup.Get("/session-types", ac.GetSessionTypes)
|
||||
|
||||
return ac
|
||||
}
|
||||
@@ -39,7 +39,7 @@ func NewLookupController(as *service.LookupService, routeGroups *common.RouteGro
|
||||
// @Tags Lookup
|
||||
// @Success 200 {array} string
|
||||
// @Router /v1/lookup/tracks [get]
|
||||
func (ac *LookupController) getTracks(c *fiber.Ctx) error {
|
||||
func (ac *LookupController) GetTracks(c *fiber.Ctx) error {
|
||||
result, err := ac.service.GetTracks(c)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||
@@ -56,7 +56,7 @@ func (ac *LookupController) getTracks(c *fiber.Ctx) error {
|
||||
// @Tags Lookup
|
||||
// @Success 200 {array} string
|
||||
// @Router /v1/lookup/car-models [get]
|
||||
func (ac *LookupController) getCarModels(c *fiber.Ctx) error {
|
||||
func (ac *LookupController) GetCarModels(c *fiber.Ctx) error {
|
||||
result, err := ac.service.GetCarModels(c)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||
@@ -73,7 +73,7 @@ func (ac *LookupController) getCarModels(c *fiber.Ctx) error {
|
||||
// @Tags Lookup
|
||||
// @Success 200 {array} string
|
||||
// @Router /v1/lookup/driver-categories [get]
|
||||
func (ac *LookupController) getDriverCategories(c *fiber.Ctx) error {
|
||||
func (ac *LookupController) GetDriverCategories(c *fiber.Ctx) error {
|
||||
result, err := ac.service.GetDriverCategories(c)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||
@@ -90,7 +90,7 @@ func (ac *LookupController) getDriverCategories(c *fiber.Ctx) error {
|
||||
// @Tags Lookup
|
||||
// @Success 200 {array} string
|
||||
// @Router /v1/lookup/cup-categories [get]
|
||||
func (ac *LookupController) getCupCategories(c *fiber.Ctx) error {
|
||||
func (ac *LookupController) GetCupCategories(c *fiber.Ctx) error {
|
||||
result, err := ac.service.GetCupCategories(c)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||
@@ -107,7 +107,7 @@ func (ac *LookupController) getCupCategories(c *fiber.Ctx) error {
|
||||
// @Tags Lookup
|
||||
// @Success 200 {array} string
|
||||
// @Router /v1/lookup/session-types [get]
|
||||
func (ac *LookupController) getSessionTypes(c *fiber.Ctx) error {
|
||||
func (ac *LookupController) GetSessionTypes(c *fiber.Ctx) error {
|
||||
result, err := ac.service.GetSessionTypes(c)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||
|
||||
142
local/controller/membership.go
Normal file
142
local/controller/membership.go
Normal file
@@ -0,0 +1,142 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"acc-server-manager/local/middleware"
|
||||
"acc-server-manager/local/model"
|
||||
"acc-server-manager/local/service"
|
||||
"acc-server-manager/local/utl/common"
|
||||
"acc-server-manager/local/utl/jwt"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// MembershipController handles API requests for membership.
|
||||
type MembershipController struct {
|
||||
service *service.MembershipService
|
||||
auth *middleware.AuthMiddleware
|
||||
}
|
||||
|
||||
// NewMembershipController creates a new MembershipController.
|
||||
func NewMembershipController(service *service.MembershipService, auth *middleware.AuthMiddleware, routeGroups *common.RouteGroups) *MembershipController {
|
||||
mc := &MembershipController{
|
||||
service: service,
|
||||
auth: auth,
|
||||
}
|
||||
|
||||
routeGroups.Auth.Post("/login", mc.Login)
|
||||
|
||||
usersGroup := routeGroups.Api.Group("/users", mc.auth.Authenticate)
|
||||
usersGroup.Post("/", mc.auth.HasPermission(model.MembershipCreate), mc.CreateUser)
|
||||
usersGroup.Get("/", mc.auth.HasPermission(model.MembershipView), mc.ListUsers)
|
||||
usersGroup.Get("/:id", mc.auth.HasPermission(model.MembershipView), mc.GetUser)
|
||||
usersGroup.Put("/:id", mc.auth.HasPermission(model.MembershipEdit), mc.UpdateUser)
|
||||
|
||||
routeGroups.Api.Get("/me", mc.auth.Authenticate, mc.GetMe)
|
||||
|
||||
return mc
|
||||
}
|
||||
|
||||
// Login handles user login.
|
||||
func (c *MembershipController) Login(ctx *fiber.Ctx) error {
|
||||
type request struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
var req request
|
||||
if err := ctx.BodyParser(&req); err != nil {
|
||||
return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request"})
|
||||
}
|
||||
|
||||
token, err := c.service.Login(ctx.UserContext(), req.Username, req.Password)
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
return ctx.JSON(fiber.Map{"token": token})
|
||||
}
|
||||
|
||||
// CreateUser creates a new user.
|
||||
func (mc *MembershipController) CreateUser(c *fiber.Ctx) error {
|
||||
type request struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Role string `json:"role"`
|
||||
}
|
||||
|
||||
var req request
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request"})
|
||||
}
|
||||
|
||||
user, err := mc.service.CreateUser(c.UserContext(), req.Username, req.Password, req.Role)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
return c.JSON(user)
|
||||
}
|
||||
|
||||
// ListUsers lists all users.
|
||||
func (mc *MembershipController) ListUsers(c *fiber.Ctx) error {
|
||||
users, err := mc.service.ListUsers(c.UserContext())
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
return c.JSON(users)
|
||||
}
|
||||
|
||||
// GetUser gets a single user by ID.
|
||||
func (mc *MembershipController) GetUser(c *fiber.Ctx) error {
|
||||
id, err := uuid.Parse(c.Params("id"))
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid user ID"})
|
||||
}
|
||||
|
||||
user, err := mc.service.GetUser(c.UserContext(), id)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"})
|
||||
}
|
||||
|
||||
return c.JSON(user)
|
||||
}
|
||||
|
||||
// GetMe returns the currently authenticated user's details.
|
||||
func (mc *MembershipController) GetMe(c *fiber.Ctx) error {
|
||||
claims, ok := c.Locals("user").(*jwt.Claims)
|
||||
if !ok || claims == nil {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Unauthorized"})
|
||||
}
|
||||
|
||||
user, err := mc.service.GetUserWithPermissions(c.UserContext(), claims.UserID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"})
|
||||
}
|
||||
|
||||
// Sanitize the user object to not expose password
|
||||
user.Password = ""
|
||||
|
||||
return c.JSON(user)
|
||||
}
|
||||
|
||||
// UpdateUser updates a user.
|
||||
func (mc *MembershipController) UpdateUser(c *fiber.Ctx) error {
|
||||
id, err := uuid.Parse(c.Params("id"))
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid user ID"})
|
||||
}
|
||||
|
||||
var req service.UpdateUserRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request"})
|
||||
}
|
||||
|
||||
user, err := mc.service.UpdateUser(c.UserContext(), id, req)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
return c.JSON(user)
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"acc-server-manager/local/middleware"
|
||||
"acc-server-manager/local/model"
|
||||
"acc-server-manager/local/service"
|
||||
"acc-server-manager/local/utl/common"
|
||||
@@ -12,34 +13,26 @@ type ServerController struct {
|
||||
service *service.ServerService
|
||||
}
|
||||
|
||||
// NewServerController
|
||||
// Initializes ServerController.
|
||||
//
|
||||
// Args:
|
||||
// *services.ServerService: Server service
|
||||
// *Fiber.RouterGroup: Fiber Router Group
|
||||
// Returns:
|
||||
// *ServerController: Controller for "Server" interactions
|
||||
func NewServerController(as *service.ServerService, routeGroups *common.RouteGroups,) *ServerController {
|
||||
// NewServerController initializes ServerController.
|
||||
func NewServerController(ss *service.ServerService, routeGroups *common.RouteGroups, auth *middleware.AuthMiddleware) *ServerController {
|
||||
ac := &ServerController{
|
||||
service: as,
|
||||
service: ss,
|
||||
}
|
||||
|
||||
routeGroups.Server.Get("/", ac.getAll)
|
||||
routeGroups.Server.Get("/:id", ac.getById)
|
||||
routeGroups.Server.Post("/", ac.createServer)
|
||||
serverRoutes := routeGroups.Server
|
||||
serverRoutes.Use(auth.Authenticate)
|
||||
|
||||
serverRoutes.Get("/", auth.HasPermission(model.ServerView), ac.GetAll)
|
||||
serverRoutes.Get("/:id", auth.HasPermission(model.ServerView), ac.GetById)
|
||||
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)
|
||||
return ac
|
||||
}
|
||||
|
||||
// getAll returns Servers
|
||||
//
|
||||
// @Summary Return Servers
|
||||
// @Description Return Servers
|
||||
// @Tags Server
|
||||
// @Success 200 {array} string
|
||||
// @Router /v1/server [get]
|
||||
func (ac *ServerController) getAll(c *fiber.Ctx) error {
|
||||
var filter model.ServerFilter
|
||||
// GetAll returns Servers
|
||||
func (ac *ServerController) GetAll(c *fiber.Ctx) error {
|
||||
var filter model.ServerFilter
|
||||
if err := common.ParseQueryFilter(c, &filter); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"error": err.Error(),
|
||||
@@ -52,14 +45,8 @@ func (ac *ServerController) getAll(c *fiber.Ctx) error {
|
||||
return c.JSON(ServerModel)
|
||||
}
|
||||
|
||||
// getById returns Servers
|
||||
//
|
||||
// @Summary Return Servers
|
||||
// @Description Return Servers
|
||||
// @Tags Server
|
||||
// @Success 200 {array} string
|
||||
// @Router /v1/server [get]
|
||||
func (ac *ServerController) getById(c *fiber.Ctx) error {
|
||||
// GetById returns a single server by its ID
|
||||
func (ac *ServerController) GetById(c *fiber.Ctx) error {
|
||||
serverID, _ := c.ParamsInt("id")
|
||||
ServerModel, err := ac.service.GetById(c, serverID)
|
||||
if err != nil {
|
||||
@@ -68,14 +55,8 @@ func (ac *ServerController) getById(c *fiber.Ctx) error {
|
||||
return c.JSON(ServerModel)
|
||||
}
|
||||
|
||||
// createServer creates a new server
|
||||
//
|
||||
// @Summary Create a new server
|
||||
// @Description Create a new server
|
||||
// @Tags Server
|
||||
// @Success 200 {array} string
|
||||
// @Router /v1/server [post]
|
||||
func (ac *ServerController) createServer(c *fiber.Ctx) error {
|
||||
// CreateServer creates a new server
|
||||
func (ac *ServerController) CreateServer(c *fiber.Ctx) error {
|
||||
server := new(model.Server)
|
||||
if err := c.BodyParser(server); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
@@ -89,4 +70,37 @@ func (ac *ServerController) createServer(c *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
return c.JSON(server)
|
||||
}
|
||||
|
||||
// UpdateServer updates an existing server
|
||||
func (ac *ServerController) UpdateServer(c *fiber.Ctx) error {
|
||||
serverID, _ := c.ParamsInt("id")
|
||||
server := new(model.Server)
|
||||
if err := c.BodyParser(server); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"error": err.Error(),
|
||||
})
|
||||
}
|
||||
server.ID = uint(serverID)
|
||||
|
||||
if err := ac.service.UpdateServer(c, server); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"error": err.Error(),
|
||||
})
|
||||
}
|
||||
return c.JSON(server)
|
||||
}
|
||||
|
||||
// DeleteServer deletes a server
|
||||
func (ac *ServerController) DeleteServer(c *fiber.Ctx) error {
|
||||
serverID, err := c.ParamsInt("id")
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid server ID"})
|
||||
}
|
||||
|
||||
if err := ac.service.DeleteServer(c, serverID); err != nil {
|
||||
return c.Status(500).SendString(err.Error())
|
||||
}
|
||||
|
||||
return c.SendStatus(204)
|
||||
}
|
||||
@@ -25,20 +25,20 @@ func NewStateHistoryController(as *service.StateHistoryService, routeGroups *com
|
||||
service: as,
|
||||
}
|
||||
|
||||
routeGroups.StateHistory.Get("/", ac.getAll)
|
||||
routeGroups.StateHistory.Get("/statistics", ac.getStatistics)
|
||||
routeGroups.StateHistory.Get("/", ac.GetAll)
|
||||
routeGroups.StateHistory.Get("/statistics", ac.GetStatistics)
|
||||
|
||||
return ac
|
||||
}
|
||||
|
||||
// getAll returns StateHistorys
|
||||
// GetAll returns StateHistorys
|
||||
//
|
||||
// @Summary Return StateHistorys
|
||||
// @Description Return StateHistorys
|
||||
// @Tags StateHistory
|
||||
// @Success 200 {array} string
|
||||
// @Router /v1/state-history [get]
|
||||
func (ac *StateHistoryController) getAll(c *fiber.Ctx) error {
|
||||
func (ac *StateHistoryController) GetAll(c *fiber.Ctx) error {
|
||||
var filter model.StateHistoryFilter
|
||||
if err := common.ParseQueryFilter(c, &filter); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
@@ -63,7 +63,7 @@ func (ac *StateHistoryController) getAll(c *fiber.Ctx) error {
|
||||
// @Tags StateHistory
|
||||
// @Success 200 {array} string
|
||||
// @Router /v1/state-history/statistics [get]
|
||||
func (ac *StateHistoryController) getStatistics(c *fiber.Ctx) error {
|
||||
func (ac *StateHistoryController) GetStatistics(c *fiber.Ctx) error {
|
||||
var filter model.StateHistoryFilter
|
||||
if err := common.ParseQueryFilter(c, &filter); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
|
||||
60
local/middleware/auth.go
Normal file
60
local/middleware/auth.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"acc-server-manager/local/service"
|
||||
"acc-server-manager/local/utl/jwt"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// AuthMiddleware provides authentication and permission middleware.
|
||||
type AuthMiddleware struct {
|
||||
membershipService *service.MembershipService
|
||||
}
|
||||
|
||||
// NewAuthMiddleware creates a new AuthMiddleware.
|
||||
func NewAuthMiddleware(ms *service.MembershipService) *AuthMiddleware {
|
||||
return &AuthMiddleware{
|
||||
membershipService: ms,
|
||||
}
|
||||
}
|
||||
|
||||
// Authenticate is a middleware for JWT authentication.
|
||||
func (m *AuthMiddleware) Authenticate(ctx *fiber.Ctx) error {
|
||||
authHeader := ctx.Get("Authorization")
|
||||
if authHeader == "" {
|
||||
return ctx.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Missing or malformed JWT"})
|
||||
}
|
||||
|
||||
parts := strings.Split(authHeader, " ")
|
||||
if len(parts) != 2 || parts[0] != "Bearer" {
|
||||
return ctx.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Missing or malformed JWT"})
|
||||
}
|
||||
|
||||
claims, err := jwt.ValidateToken(parts[1])
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid or expired JWT"})
|
||||
}
|
||||
|
||||
ctx.Locals("userID", claims.UserID)
|
||||
return ctx.Next()
|
||||
}
|
||||
|
||||
// HasPermission is a middleware for checking user permissions.
|
||||
func (m *AuthMiddleware) HasPermission(requiredPermission string) fiber.Handler {
|
||||
return func(ctx *fiber.Ctx) error {
|
||||
userID, ok := ctx.Locals("userID").(uuid.UUID)
|
||||
if !ok {
|
||||
return ctx.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Unauthorized"})
|
||||
}
|
||||
|
||||
has, err := m.membershipService.HasPermission(ctx.UserContext(), userID, requiredPermission)
|
||||
if err != nil || !has {
|
||||
return ctx.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "Forbidden"})
|
||||
}
|
||||
|
||||
return ctx.Next()
|
||||
}
|
||||
}
|
||||
19
local/model/permission.go
Normal file
19
local/model/permission.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Permission represents an action that can be performed in the system.
|
||||
type Permission struct {
|
||||
ID uuid.UUID `json:"id" gorm:"type:uuid;primary_key;"`
|
||||
Name string `json:"name" gorm:"unique_index;not null"`
|
||||
}
|
||||
|
||||
// BeforeCreate is a GORM hook that runs before creating new credentials
|
||||
func (s *Permission) BeforeCreate(tx *gorm.DB) error {
|
||||
s.ID = uuid.New()
|
||||
|
||||
return nil
|
||||
}
|
||||
53
local/model/permissions.go
Normal file
53
local/model/permissions.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package model
|
||||
|
||||
// Permission constants
|
||||
const (
|
||||
ServerView = "server.view"
|
||||
ServerCreate = "server.create"
|
||||
ServerUpdate = "server.update"
|
||||
ServerDelete = "server.delete"
|
||||
ServerStart = "server.start"
|
||||
ServerStop = "server.stop"
|
||||
|
||||
ConfigView = "config.view"
|
||||
ConfigUpdate = "config.update"
|
||||
|
||||
UserView = "user.view"
|
||||
UserCreate = "user.create"
|
||||
UserUpdate = "user.update"
|
||||
UserDelete = "user.delete"
|
||||
|
||||
RoleView = "role.view"
|
||||
RoleCreate = "role.create"
|
||||
RoleUpdate = "role.update"
|
||||
RoleDelete = "role.delete"
|
||||
|
||||
MembershipCreate = "membership.create"
|
||||
MembershipView = "membership.view"
|
||||
MembershipEdit = "membership.edit"
|
||||
)
|
||||
|
||||
// AllPermissions returns a slice of all permission strings.
|
||||
func AllPermissions() []string {
|
||||
return []string{
|
||||
ServerView,
|
||||
ServerCreate,
|
||||
ServerUpdate,
|
||||
ServerDelete,
|
||||
ServerStart,
|
||||
ServerStop,
|
||||
ConfigView,
|
||||
ConfigUpdate,
|
||||
UserView,
|
||||
UserCreate,
|
||||
UserUpdate,
|
||||
UserDelete,
|
||||
RoleView,
|
||||
RoleCreate,
|
||||
RoleUpdate,
|
||||
RoleDelete,
|
||||
MembershipCreate,
|
||||
MembershipView,
|
||||
MembershipEdit,
|
||||
}
|
||||
}
|
||||
20
local/model/role.go
Normal file
20
local/model/role.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Role represents a user role in the system.
|
||||
type Role struct {
|
||||
ID uuid.UUID `json:"id" gorm:"type:uuid;primary_key;"`
|
||||
Name string `json:"name" gorm:"unique_index;not null"`
|
||||
Permissions []Permission `json:"permissions" gorm:"many2many:role_permissions;"`
|
||||
}
|
||||
|
||||
// BeforeCreate is a GORM hook that runs before creating new credentials
|
||||
func (s *Role) BeforeCreate(tx *gorm.DB) error {
|
||||
s.ID = uuid.New()
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -24,7 +24,7 @@ type Server struct {
|
||||
Port int `gorm:"not null" json:"-"`
|
||||
Path string `gorm:"not null" json:"path"` // e.g. "/acc/servers/server1/"
|
||||
ServiceName string `gorm:"not null" json:"serviceName"` // Windows service name
|
||||
State ServerState `gorm:"-" json:"state"`
|
||||
State *ServerState `gorm:"-" json:"state"`
|
||||
DateCreated time.Time `json:"dateCreated"`
|
||||
FromSteamCMD bool `gorm:"not null; default:true" json:"-"`
|
||||
}
|
||||
|
||||
70
local/model/user.go
Normal file
70
local/model/user.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// User represents a user account in the system.
|
||||
type User struct {
|
||||
ID uuid.UUID `json:"id" gorm:"type:uuid;primary_key;"`
|
||||
Username string `json:"username" gorm:"unique_index;not null"`
|
||||
Password string `json:"password" gorm:"not null"`
|
||||
RoleID uuid.UUID `json:"role_id" gorm:"type:uuid"`
|
||||
Role Role `json:"role"`
|
||||
}
|
||||
|
||||
|
||||
// BeforeCreate is a GORM hook that runs before creating new credentials
|
||||
func (s *User) BeforeCreate(tx *gorm.DB) error {
|
||||
s.ID = uuid.New()
|
||||
// Encrypt password before saving
|
||||
encrypted, err := EncryptPassword(s.Password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Password = encrypted
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// BeforeUpdate is a GORM hook that runs before updating credentials
|
||||
func (s *User) BeforeUpdate(tx *gorm.DB) error {
|
||||
|
||||
// Only encrypt if password field is being updated
|
||||
if tx.Statement.Changed("Password") {
|
||||
encrypted, err := EncryptPassword(s.Password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Password = encrypted
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AfterFind is a GORM hook that runs after fetching credentials
|
||||
func (s *User) AfterFind(tx *gorm.DB) error {
|
||||
// Decrypt password after fetching
|
||||
if s.Password != "" {
|
||||
decrypted, err := DecryptPassword(s.Password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Password = decrypted
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate checks if the credentials are valid
|
||||
func (s *User) Validate() error {
|
||||
if s.Username == "" {
|
||||
return errors.New("username is required")
|
||||
}
|
||||
if s.Password == "" {
|
||||
return errors.New("password is required")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
142
local/repository/membership.go
Normal file
142
local/repository/membership.go
Normal file
@@ -0,0 +1,142 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"acc-server-manager/local/model"
|
||||
"context"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// MembershipRepository handles database operations for users, roles, and permissions.
|
||||
type MembershipRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewMembershipRepository creates a new MembershipRepository.
|
||||
func NewMembershipRepository(db *gorm.DB) *MembershipRepository {
|
||||
return &MembershipRepository{db: db}
|
||||
}
|
||||
|
||||
// FindUserByUsername finds a user by their username.
|
||||
// It preloads the user's role and the role's permissions.
|
||||
func (r *MembershipRepository) FindUserByUsername(ctx context.Context, username string) (*model.User, error) {
|
||||
var user model.User
|
||||
db := r.db.WithContext(ctx)
|
||||
err := db.Preload("Role.Permissions").Where("username = ?", username).First(&user).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// FindUserByIDWithPermissions finds a user by their ID and preloads Role and Permissions.
|
||||
func (r *MembershipRepository) FindUserByIDWithPermissions(ctx context.Context, userID uuid.UUID) (*model.User, error) {
|
||||
var user model.User
|
||||
db := r.db.WithContext(ctx)
|
||||
err := db.Preload("Role.Permissions").First(&user, "id = ?", userID).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
|
||||
// CreateUser creates a new user.
|
||||
func (r *MembershipRepository) CreateUser(ctx context.Context, user *model.User) error {
|
||||
db := r.db.WithContext(ctx)
|
||||
return db.Create(user).Error
|
||||
}
|
||||
|
||||
// FindRoleByName finds a role by its name.
|
||||
func (r *MembershipRepository) FindRoleByName(ctx context.Context, name string) (*model.Role, error) {
|
||||
var role model.Role
|
||||
db := r.db.WithContext(ctx)
|
||||
err := db.Where("name = ?", name).First(&role).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &role, nil
|
||||
}
|
||||
|
||||
// CreateRole creates a new role.
|
||||
func (r *MembershipRepository) CreateRole(ctx context.Context, role *model.Role) error {
|
||||
db := r.db.WithContext(ctx)
|
||||
return db.Create(role).Error
|
||||
}
|
||||
|
||||
// FindPermissionByName finds a permission by its name.
|
||||
func (r *MembershipRepository) FindPermissionByName(ctx context.Context, name string) (*model.Permission, error) {
|
||||
var permission model.Permission
|
||||
db := r.db.WithContext(ctx)
|
||||
err := db.Where("name = ?", name).First(&permission).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &permission, nil
|
||||
}
|
||||
|
||||
// CreatePermission creates a new permission.
|
||||
func (r *MembershipRepository) CreatePermission(ctx context.Context, permission *model.Permission) error {
|
||||
db := r.db.WithContext(ctx)
|
||||
return db.Create(permission).Error
|
||||
}
|
||||
|
||||
// AssignPermissionsToRole assigns a set of permissions to a role.
|
||||
func (r *MembershipRepository) AssignPermissionsToRole(ctx context.Context, role *model.Role, permissions []model.Permission) error {
|
||||
db := r.db.WithContext(ctx)
|
||||
return db.Model(role).Association("Permissions").Replace(permissions)
|
||||
}
|
||||
|
||||
// GetUserPermissions retrieves all permissions for a given user ID.
|
||||
func (r *MembershipRepository) GetUserPermissions(ctx context.Context, userID uuid.UUID) ([]string, error) {
|
||||
var user model.User
|
||||
db := r.db.WithContext(ctx)
|
||||
|
||||
if err := db.Preload("Role.Permissions").First(&user, "id = ?", userID).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
permissions := make([]string, len(user.Role.Permissions))
|
||||
for i, p := range user.Role.Permissions {
|
||||
permissions[i] = p.Name
|
||||
}
|
||||
|
||||
return permissions, nil
|
||||
}
|
||||
|
||||
// ListUsers retrieves all users.
|
||||
func (r *MembershipRepository) ListUsers(ctx context.Context) ([]*model.User, error) {
|
||||
var users []*model.User
|
||||
db := r.db.WithContext(ctx)
|
||||
err := db.Preload("Role").Find(&users).Error
|
||||
return users, err
|
||||
}
|
||||
|
||||
// FindUserByID finds a user by their ID.
|
||||
func (r *MembershipRepository) FindUserByID(ctx context.Context, userID uuid.UUID) (*model.User, error) {
|
||||
var user model.User
|
||||
db := r.db.WithContext(ctx)
|
||||
err := db.Preload("Role").First(&user, "id = ?", userID).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// UpdateUser updates a user's details in the database.
|
||||
func (r *MembershipRepository) UpdateUser(ctx context.Context, user *model.User) error {
|
||||
db := r.db.WithContext(ctx)
|
||||
return db.Save(user).Error
|
||||
}
|
||||
|
||||
// FindRoleByID finds a role by its ID.
|
||||
func (r *MembershipRepository) FindRoleByID(ctx context.Context, roleID uuid.UUID) (*model.Role, error) {
|
||||
var role model.Role
|
||||
db := r.db.WithContext(ctx)
|
||||
err := db.First(&role, "id = ?", roleID).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &role, nil
|
||||
}
|
||||
@@ -17,4 +17,5 @@ func InitializeRepositories(c *dig.Container) {
|
||||
c.Provide(NewLookupRepository)
|
||||
c.Provide(NewSteamCredentialsRepository)
|
||||
c.Provide(NewSystemConfigRepository)
|
||||
c.Provide(NewMembershipRepository)
|
||||
}
|
||||
|
||||
173
local/service/membership.go
Normal file
173
local/service/membership.go
Normal file
@@ -0,0 +1,173 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"acc-server-manager/local/model"
|
||||
"acc-server-manager/local/repository"
|
||||
"acc-server-manager/local/utl/jwt"
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// MembershipService provides business logic for membership-related operations.
|
||||
type MembershipService struct {
|
||||
repo *repository.MembershipRepository
|
||||
}
|
||||
|
||||
// NewMembershipService creates a new MembershipService.
|
||||
func NewMembershipService(repo *repository.MembershipRepository) *MembershipService {
|
||||
return &MembershipService{repo: repo}
|
||||
}
|
||||
|
||||
// Login authenticates a user and returns a JWT.
|
||||
func (s *MembershipService) Login(ctx context.Context, username, password string) (string, error) {
|
||||
user, err := s.repo.FindUserByUsername(ctx, username)
|
||||
if err != nil {
|
||||
return "", errors.New("invalid credentials")
|
||||
}
|
||||
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil {
|
||||
return "", errors.New("invalid credentials")
|
||||
}
|
||||
|
||||
return jwt.GenerateToken(user)
|
||||
}
|
||||
|
||||
// CreateUser creates a new user.
|
||||
func (s *MembershipService) CreateUser(ctx context.Context, username, password, roleName string) (*model.User, error) {
|
||||
|
||||
role, err := s.repo.FindRoleByName(ctx, roleName)
|
||||
if err != nil {
|
||||
return nil, errors.New("role not found")
|
||||
}
|
||||
|
||||
user := &model.User{
|
||||
Username: username,
|
||||
Password: password,
|
||||
RoleID: role.ID,
|
||||
}
|
||||
|
||||
if err := s.repo.CreateUser(ctx, user); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// ListUsers retrieves all users.
|
||||
func (s *MembershipService) ListUsers(ctx context.Context) ([]*model.User, error) {
|
||||
return s.repo.ListUsers(ctx)
|
||||
}
|
||||
|
||||
// GetUser retrieves a single user by ID.
|
||||
func (s *MembershipService) GetUser(ctx context.Context, userID uuid.UUID) (*model.User, error) {
|
||||
return s.repo.FindUserByID(ctx, userID)
|
||||
}
|
||||
|
||||
// GetUserWithPermissions retrieves a single user by ID with their role and permissions.
|
||||
func (s *MembershipService) GetUserWithPermissions(ctx context.Context, userID uuid.UUID) (*model.User, error) {
|
||||
return s.repo.FindUserByIDWithPermissions(ctx, userID)
|
||||
}
|
||||
|
||||
// UpdateUserRequest defines the request body for updating a user.
|
||||
type UpdateUserRequest struct {
|
||||
Username *string `json:"username"`
|
||||
Password *string `json:"password"`
|
||||
RoleID *uuid.UUID `json:"roleId"`
|
||||
}
|
||||
|
||||
// UpdateUser updates a user's details.
|
||||
func (s *MembershipService) UpdateUser(ctx context.Context, userID uuid.UUID, req UpdateUserRequest) (*model.User, error) {
|
||||
user, err := s.repo.FindUserByID(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.New("user not found")
|
||||
}
|
||||
|
||||
if req.Username != nil {
|
||||
user.Username = *req.Username
|
||||
}
|
||||
|
||||
if req.Password != nil && *req.Password != "" {
|
||||
user.Password = *req.Password
|
||||
}
|
||||
|
||||
if req.RoleID != nil {
|
||||
// Check if role exists
|
||||
_, err := s.repo.FindRoleByID(ctx, *req.RoleID)
|
||||
if err != nil {
|
||||
return nil, errors.New("role not found")
|
||||
}
|
||||
user.RoleID = *req.RoleID
|
||||
}
|
||||
|
||||
if err := s.repo.UpdateUser(ctx, user); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// HasPermission checks if a user has a specific permission.
|
||||
func (s *MembershipService) HasPermission(ctx context.Context, userID uuid.UUID, permissionName string) (bool, error) {
|
||||
user, err := s.repo.FindUserByIDWithPermissions(ctx, userID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Super admin has all permissions
|
||||
if user.Role.Name == "Super Admin" {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
for _, p := range user.Role.Permissions {
|
||||
if p.Name == permissionName {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// SetupInitialData creates the initial roles and permissions.
|
||||
func (s *MembershipService) SetupInitialData(ctx context.Context) error {
|
||||
// Define all permissions
|
||||
permissions := model.AllPermissions()
|
||||
|
||||
createdPermissions := make([]model.Permission, 0)
|
||||
for _, pName := range permissions {
|
||||
perm, err := s.repo.FindPermissionByName(ctx, pName)
|
||||
if err != nil { // Assuming error means not found
|
||||
perm = &model.Permission{Name: pName}
|
||||
if err := s.repo.CreatePermission(ctx, perm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
createdPermissions = append(createdPermissions, *perm)
|
||||
}
|
||||
|
||||
// Create Super Admin role with all permissions
|
||||
superAdminRole, err := s.repo.FindRoleByName(ctx, "Super Admin")
|
||||
if err != nil {
|
||||
superAdminRole = &model.Role{Name: "Super Admin"}
|
||||
if err := s.repo.CreateRole(ctx, superAdminRole); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := s.repo.AssignPermissionsToRole(ctx, superAdminRole, createdPermissions); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create a default admin user if one doesn't exist
|
||||
_, err = s.repo.FindUserByUsername(ctx, "admin")
|
||||
if err != nil {
|
||||
_, err = s.CreateUser(ctx, "admin", os.Getenv("PASSWORD"), "Super Admin") // Default password, should be changed
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -294,7 +294,7 @@ func (s *ServerService) GetAll(ctx *fiber.Ctx, filter *model.ServerFilter) (*[]m
|
||||
} else {
|
||||
serverInstance := instance.(*tracking.AccServerInstance)
|
||||
if serverInstance.State != nil {
|
||||
(*server).State = *serverInstance.State
|
||||
server.State = serverInstance.State
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -325,7 +325,7 @@ func (as *ServerService) GetById(ctx *fiber.Ctx, serverID int) (*model.Server, e
|
||||
} else {
|
||||
serverInstance := instance.(*tracking.AccServerInstance)
|
||||
if (serverInstance.State != nil) {
|
||||
(*server).State = *serverInstance.State
|
||||
server.State = serverInstance.State
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ func InitializeServices(c *dig.Container) {
|
||||
c.Provide(NewSteamService)
|
||||
c.Provide(NewWindowsService)
|
||||
c.Provide(NewFirewallService)
|
||||
c.Provide(NewMembershipService)
|
||||
|
||||
logging.Debug("Initializing service dependencies")
|
||||
err := c.Invoke(func(server *ServerService, api *ApiService, config *ConfigService, systemConfig *SystemConfigService) {
|
||||
|
||||
@@ -3,7 +3,7 @@ package service
|
||||
import (
|
||||
"acc-server-manager/local/model"
|
||||
"acc-server-manager/local/repository"
|
||||
"acc-server-manager/pkg/logging"
|
||||
"acc-server-manager/local/utl/logging"
|
||||
"sync"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
|
||||
@@ -17,10 +17,11 @@ import (
|
||||
)
|
||||
|
||||
type RouteGroups struct {
|
||||
Api fiber.Router
|
||||
Server fiber.Router
|
||||
Config fiber.Router
|
||||
Lookup fiber.Router
|
||||
Api fiber.Router
|
||||
Auth fiber.Router
|
||||
Server fiber.Router
|
||||
Config fiber.Router
|
||||
Lookup fiber.Router
|
||||
StateHistory fiber.Router
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,9 @@ func Migrate(db *gorm.DB) {
|
||||
&model.StateHistory{},
|
||||
&model.SteamCredentials{},
|
||||
&model.SystemConfig{},
|
||||
&model.User{},
|
||||
&model.Role{},
|
||||
&model.Permission{},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
|
||||
54
local/utl/jwt/jwt.go
Normal file
54
local/utl/jwt/jwt.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"acc-server-manager/local/model"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// SecretKey is the secret key for signing the JWT.
|
||||
// It is recommended to use a long, complex string for this.
|
||||
// In a production environment, this should be loaded from a secure configuration source.
|
||||
var SecretKey = []byte("your-secret-key")
|
||||
|
||||
// Claims represents the JWT claims.
|
||||
type Claims struct {
|
||||
UserID uuid.UUID `json:"user_id"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
// GenerateToken generates a new JWT for a given user.
|
||||
func GenerateToken(user *model.User) (string, error) {
|
||||
expirationTime := time.Now().Add(24 * time.Hour)
|
||||
claims := &Claims{
|
||||
UserID: user.ID,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
ExpiresAt: jwt.NewNumericDate(expirationTime),
|
||||
},
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
return token.SignedString(SecretKey)
|
||||
}
|
||||
|
||||
// ValidateToken validates a JWT and returns the claims if the token is valid.
|
||||
func ValidateToken(tokenString string) (*Claims, error) {
|
||||
claims := &Claims{}
|
||||
|
||||
token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
|
||||
return SecretKey, nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !token.Valid {
|
||||
return nil, errors.New("invalid token")
|
||||
}
|
||||
|
||||
return claims, nil
|
||||
}
|
||||
Reference in New Issue
Block a user