diff --git a/README.md b/README.md index d25075b..76f4321 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,3 @@ bakend in GO frontend quasar framework con generazione delle pagine statiche per la parte public - -internal - auth - model - controller - service - endpoint diff --git a/backend/GeneratedCode/generatedTypescript.ts b/backend/GeneratedCode/generatedTypescript.ts index 4d24791..1a0ff9d 100644 --- a/backend/GeneratedCode/generatedTypescript.ts +++ b/backend/GeneratedCode/generatedTypescript.ts @@ -4,7 +4,7 @@ // // This file was generated by github.com/millevolte/ts-rpc // -// Apr 05, 2026 17:08:11 UTC +// Apr 05, 2026 20:12:24 UTC // export interface ApiRestResponse { @@ -281,11 +281,178 @@ export type Nullable = T | null; export type Record = { [P in K]: T }; // -// package model +// package systemUtils // -export interface RefreshRequest { - refresh_token: string; +// Typescript: TSEndpoint= path=/metrics; name=metrics; method=GET; response=string +// internal/systemUtils/routes.go Line: 37 +export const metrics = async (): Promise<{ + data: string; + error: Nullable; +}> => { + return (await api.GET("/metrics")) as { + data: string; + error: Nullable; + }; +}; + +// Typescript: TSEndpoint= path=/maildebug; name=mailDebug; method=GET; response=routes.[]MailDebugItem +// internal/systemUtils/routes.go Line: 48 +export const mailDebug = async (): Promise<{ + data: MailDebugItem[]; + error: Nullable; +}> => { + return (await api.GET("/maildebug")) as { + data: MailDebugItem[]; + error: Nullable; + }; +}; + +// Typescript: TSEndpoint= path=/health; name=health; method=GET; response=string +// internal/systemUtils/routes.go Line: 34 +export const health = async (): Promise<{ + data: string; + error: Nullable; +}> => { + return (await api.GET("/health")) as { + data: string; + error: Nullable; + }; +}; + +export interface MailDebugItem { + name: string; + content: string; +} + +// +// package admin +// + +// Typescript: TSEndpoint= path=/admin/users; name=listUsers; method=POST; request=admin.ListUsersRequest; response=models.[]UserShort +// internal/admin/routes.go Line: 12 + +export const listUsers = async ( + data: ListUsersRequest, +): Promise<{ data: UserShort[]; error: Nullable }> => { + return (await api.POST("/admin/users", data)) as { + data: UserShort[]; + error: Nullable; + }; +}; + +// Typescript: TSEndpoint= path=/admin/users/:uuid/block; name=blockUser; method=PUT; request=admin.BlockUserRequest; response=models.UserShort +// internal/admin/routes.go Line: 16 + +export const blockUser = async ( + data: BlockUserRequest, +): Promise<{ data: UserShort; error: Nullable }> => { + return (await api.PUT("/admin/users/:uuid/block", data)) as { + data: UserShort; + error: Nullable; + }; +}; + +export interface BlockUserRequest { + action: string; +} + +export interface ListUsersRequest { + page: number; + pageSize: number; +} + +// +// package auth +// + +// Typescript: TSEndpoint= path=/auth/refresh; name=refresh; method=POST; request=model.RefreshRequest; response=model.TokenPair +// internal/auth/routes.go Line: 23 + +export const refresh = async ( + data: RefreshRequest, +): Promise<{ data: TokenPair; error: Nullable }> => { + return (await api.POST("/auth/refresh", data)) as { + data: TokenPair; + error: Nullable; + }; +}; + +// Typescript: TSEndpoint= path=/auth/me; name=me; method=GET; response=models.UserShort +// internal/auth/routes.go Line: 26 +export const me = async (): Promise<{ + data: UserShort; + error: Nullable; +}> => { + return (await api.GET("/auth/me")) as { + data: UserShort; + error: Nullable; + }; +}; + +// Typescript: TSEndpoint= path=/auth/register; name=register; method=POST; request=models.UserCreateInput; response=models.UserShort +// internal/auth/routes.go Line: 29 + +export const register = async ( + data: UserCreateInput, +): Promise<{ data: UserShort; error: Nullable }> => { + return (await api.POST("/auth/register", data)) as { + data: UserShort; + error: Nullable; + }; +}; + +// Typescript: TSEndpoint= path=/auth/password/forgot; name=forgotPassword; method=POST; request=model.ForgotPasswordRequest; response=controllers.SimpleResponse +// internal/auth/routes.go Line: 32 + +export const forgotPassword = async ( + data: ForgotPasswordRequest, +): Promise<{ data: SimpleResponse; error: Nullable }> => { + return (await api.POST("/auth/password/forgot", data)) as { + data: SimpleResponse; + error: Nullable; + }; +}; + +// Typescript: TSEndpoint= path=/auth/password/reset; name=resetPassword; method=POST; request=model.ResetPasswordRequest; response=controllers.SimpleResponse +// internal/auth/routes.go Line: 35 + +export const resetPassword = async ( + data: ResetPasswordRequest, +): Promise<{ data: SimpleResponse; error: Nullable }> => { + return (await api.POST("/auth/password/reset", data)) as { + data: SimpleResponse; + error: Nullable; + }; +}; + +// Typescript: TSEndpoint= path=/auth/password/valid; name=validToken; method=POST; request=string; response=controllers.SimpleResponse +// internal/auth/routes.go Line: 38 + +export const validToken = async ( + data: string, +): Promise<{ data: SimpleResponse; error: Nullable }> => { + return (await api.POST("/auth/password/valid", data)) as { + data: SimpleResponse; + error: Nullable; + }; +}; + +// Typescript: TSEndpoint= path=/auth/login; name=login; method=POST; request=model.LoginRequest; response=model.TokenPair +// internal/auth/routes.go Line: 20 + +export const login = async ( + data: LoginRequest, +): Promise<{ data: TokenPair; error: Nullable }> => { + return (await api.POST("/auth/login", data)) as { + data: TokenPair; + error: Nullable; + }; +}; + +export interface ResetPasswordRequest { + token: string; + password: string; } export interface TokenPair { @@ -302,27 +469,60 @@ export interface LoginRequest { password: string; } -export interface ResetPasswordRequest { - token: string; - password: string; +export interface RefreshRequest { + refresh_token: string; } // -// package controllers +// package user // -export interface BlockUserRequest { - action: string; -} +// Typescript: TSEndpoint= path=/users/:uuid; name=updateUser; method=PUT; request=controllers.UpdateUserRequest; response=models.UserProfile +// internal/user/routes.go Line: 18 -export interface ListUsersRequest { - page: number; - pageSize: number; -} +export const updateUser = async ( + data: UpdateUserRequest, +): Promise<{ data: UserProfile; error: Nullable }> => { + return (await api.PUT("/users/:uuid", data)) as { + data: UserProfile; + error: Nullable; + }; +}; -export interface SimpleResponse { - message: string; -} +// Typescript: TSEndpoint= path=/users/:uuid; name=deleteUser; method=DELETE; response=controllers.SimpleResponse +// internal/user/routes.go Line: 21 + +export const deleteUser = async ( + uuid: string, +): Promise<{ data: SimpleResponse; error: Nullable }> => { + return (await api.DELETE(`/users/${uuid}`)) as { + data: SimpleResponse; + error: Nullable; + }; +}; + +// Typescript: TSEndpoint= path=/users/:uuid; name=getUser; method=GET; response=models.UserProfile +// internal/user/routes.go Line: 12 +export const getUser = async ( + uuid: string, +): Promise<{ data: UserProfile; error: Nullable }> => { + return (await api.GET(`/users/${uuid}`)) as { + data: UserProfile; + error: Nullable; + }; +}; + +// Typescript: TSEndpoint= path=/users; name=createUser; method=POST; request=models.UserCreateInput; response=models.UserProfile +// internal/user/routes.go Line: 15 + +export const createUser = async ( + data: UserCreateInput, +): Promise<{ data: UserProfile; error: Nullable }> => { + return (await api.POST("/users", data)) as { + data: UserProfile; + error: Nullable; + }; +}; export interface UpdateUserRequest { name: string; @@ -337,115 +537,16 @@ export interface UpdateUserRequest { } // -// package routes +// package responses // -// Typescript: TSEndpoint= path=/metrics; name=metrics; method=GET; response=string -// internal/http/routes/system_routes.go Line: 37 -export const metrics = async (): Promise<{ - data: string; - error: Nullable; -}> => { - return (await api.GET("/metrics")) as { - data: string; - error: Nullable; - }; -}; +export interface SimpleResponse { + message: string; +} -// Typescript: TSEndpoint= path=/maildebug; name=mailDebug; method=GET; response=routes.[]MailDebugItem -// internal/http/routes/system_routes.go Line: 48 -export const mailDebug = async (): Promise<{ - data: MailDebugItem[]; - error: Nullable; -}> => { - return (await api.GET("/maildebug")) as { - data: MailDebugItem[]; - error: Nullable; - }; -}; - -// Typescript: TSEndpoint= path=/users/:uuid; name=getUser; method=GET; response=models.UserProfile -// internal/http/routes/user_routes.go Line: 13 -export const getUser = async ( - uuid: string, -): Promise<{ data: UserProfile; error: Nullable }> => { - return (await api.GET(`/users/${uuid}`)) as { - data: UserProfile; - error: Nullable; - }; -}; - -// Typescript: TSEndpoint= path=/users/:uuid; name=updateUser; method=PUT; request=controllers.UpdateUserRequest; response=models.UserProfile -// internal/http/routes/user_routes.go Line: 19 - -export const updateUser = async ( - data: UpdateUserRequest, -): Promise<{ data: UserProfile; error: Nullable }> => { - return (await api.PUT("/users/:uuid", data)) as { - data: UserProfile; - error: Nullable; - }; -}; - -// Typescript: TSEndpoint= path=/admin/users; name=listUsers; method=POST; request=controllers.ListUsersRequest; response=models.[]UserShort -// internal/http/routes/admin_routes.go Line: 12 - -export const listUsers = async ( - data: ListUsersRequest, -): Promise<{ data: UserShort[]; error: Nullable }> => { - return (await api.POST("/admin/users", data)) as { - data: UserShort[]; - error: Nullable; - }; -}; - -// Typescript: TSEndpoint= path=/admin/users/:uuid/block; name=blockUser; method=PUT; request=controllers.BlockUserRequest; response=models.UserShort -// internal/http/routes/admin_routes.go Line: 15 - -export const blockUser = async ( - data: BlockUserRequest, -): Promise<{ data: UserShort; error: Nullable }> => { - return (await api.PUT("/admin/users/:uuid/block", data)) as { - data: UserShort; - error: Nullable; - }; -}; - -// Typescript: TSEndpoint= path=/users; name=createUser; method=POST; request=models.UserCreateInput; response=models.UserProfile -// internal/http/routes/user_routes.go Line: 16 - -export const createUser = async ( - data: UserCreateInput, -): Promise<{ data: UserProfile; error: Nullable }> => { - return (await api.POST("/users", data)) as { - data: UserProfile; - error: Nullable; - }; -}; - -// Typescript: TSEndpoint= path=/users/:uuid; name=deleteUser; method=DELETE; response=controllers.SimpleResponse -// internal/http/routes/user_routes.go Line: 22 - -export const deleteUser = async ( - uuid: string, -): Promise<{ data: SimpleResponse; error: Nullable }> => { - return (await api.DELETE(`/users/${uuid}`)) as { - data: SimpleResponse; - error: Nullable; - }; -}; - -// Typescript: TSEndpoint= path=/health; name=health; method=GET; response=string -// internal/http/routes/system_routes.go Line: 34 -export const health = async (): Promise<{ - data: string; - error: Nullable; -}> => { - return (await api.GET("/health")) as { - data: string; - error: Nullable; - }; -}; +// +// package routes +// export interface FormRequest { req: string; @@ -456,99 +557,6 @@ export interface FormResponse { test: string; } -export interface MailDebugItem { - name: string; - content: string; -} - -// -// package endpoint -// - -// Typescript: TSEndpoint= path=/auth/password/forgot; name=forgotPassword; method=POST; request=model.ForgotPasswordRequest; response=controllers.SimpleResponse -// internal/auth/endpoint/routes.go Line: 34 - -export const forgotPassword = async ( - data: ForgotPasswordRequest, -): Promise<{ data: SimpleResponse; error: Nullable }> => { - return (await api.POST("/auth/password/forgot", data)) as { - data: SimpleResponse; - error: Nullable; - }; -}; - -// Typescript: TSEndpoint= path=/auth/password/reset; name=resetPassword; method=POST; request=model.ResetPasswordRequest; response=controllers.SimpleResponse -// internal/auth/endpoint/routes.go Line: 37 - -export const resetPassword = async ( - data: ResetPasswordRequest, -): Promise<{ data: SimpleResponse; error: Nullable }> => { - return (await api.POST("/auth/password/reset", data)) as { - data: SimpleResponse; - error: Nullable; - }; -}; - -// Typescript: TSEndpoint= path=/auth/password/valid; name=validToken; method=POST; request=string; response=controllers.SimpleResponse -// internal/auth/endpoint/routes.go Line: 40 - -export const validToken = async ( - data: string, -): Promise<{ data: SimpleResponse; error: Nullable }> => { - return (await api.POST("/auth/password/valid", data)) as { - data: SimpleResponse; - error: Nullable; - }; -}; - -// Typescript: TSEndpoint= path=/auth/login; name=login; method=POST; request=model.LoginRequest; response=model.TokenPair -// internal/auth/endpoint/routes.go Line: 22 - -export const login = async ( - data: LoginRequest, -): Promise<{ data: TokenPair; error: Nullable }> => { - return (await api.POST("/auth/login", data)) as { - data: TokenPair; - error: Nullable; - }; -}; - -// Typescript: TSEndpoint= path=/auth/refresh; name=refresh; method=POST; request=model.RefreshRequest; response=model.TokenPair -// internal/auth/endpoint/routes.go Line: 25 - -export const refresh = async ( - data: RefreshRequest, -): Promise<{ data: TokenPair; error: Nullable }> => { - return (await api.POST("/auth/refresh", data)) as { - data: TokenPair; - error: Nullable; - }; -}; - -// Typescript: TSEndpoint= path=/auth/me; name=me; method=GET; response=models.UserShort -// internal/auth/endpoint/routes.go Line: 28 -export const me = async (): Promise<{ - data: UserShort; - error: Nullable; -}> => { - return (await api.GET("/auth/me")) as { - data: UserShort; - error: Nullable; - }; -}; - -// Typescript: TSEndpoint= path=/auth/register; name=register; method=POST; request=models.UserCreateInput; response=models.UserShort -// internal/auth/endpoint/routes.go Line: 31 - -export const register = async ( - data: UserCreateInput, -): Promise<{ data: UserShort; error: Nullable }> => { - return (await api.POST("/auth/register", data)) as { - data: UserShort; - error: Nullable; - }; -}; - // // package models // @@ -565,17 +573,6 @@ export interface UserCreateInput { preferences: Nullable; } -export interface UserDetailsShort { - title: string; - firstName: string; - lastName: string; - address: string; - city: string; - zipCode: string; - country: string; - phone: string; -} - export interface UserPreferencesShort { useIdle: boolean; idleTimeout: number; @@ -587,6 +584,17 @@ export interface UserPreferencesShort { language: string; } +export interface UserDetailsShort { + title: string; + firstName: string; + lastName: string; + address: string; + city: string; + zipCode: string; + country: string; + phone: string; +} + export interface UserShort { email: string; name: string; @@ -598,14 +606,14 @@ export interface UserShort { avatar: Nullable; } -export type UserRoles = string[]; - -export type UserStatus = (typeof EnumUserStatus)[keyof typeof EnumUserStatus]; - export type UserTypes = string[]; export type UsersShort = UserShort[]; +export type UserRoles = string[]; + +export type UserStatus = (typeof EnumUserStatus)[keyof typeof EnumUserStatus]; + export const EnumUserStatus = { UserStatusPending: "pending", UserStatusActive: "active", diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go index b17541f..46bbf9e 100644 --- a/backend/cmd/server/main.go +++ b/backend/cmd/server/main.go @@ -11,14 +11,13 @@ import ( "syscall" "time" - authmodel "server/internal/auth/model" - authservice "server/internal/auth/service" + "server/internal/auth" + "server/internal/authorization" "server/internal/config" "server/internal/db" - "server/internal/http/controllers" - "server/internal/http/routes" + "server/internal/routes" + "server/internal/mail" - "server/internal/roles" "server/internal/seed" "github.com/gofiber/fiber/v3" @@ -55,7 +54,7 @@ func main() { log.Fatalf("init db: %v", err) } - authService, err := authservice.New(authmodel.Config{ + authService, err := auth.NewAuthService(auth.Config{ Secret: cfg.Auth.Secret, Issuer: cfg.Auth.Issuer, AccessTokenExpiry: time.Duration(cfg.Auth.AccessTokenExpiryMinutes) * time.Minute, @@ -84,19 +83,6 @@ func main() { log.Fatalf("setup mail: %v", err) } - roleConfigPath := cfg.RolesConfigPath - if envRoleConfig := os.Getenv("ROLES_CONFIG_PATH"); envRoleConfig != "" { - roleConfigPath = envRoleConfig - } - if roleConfigPath == "" { - roleConfigPath = envOrDefault("ROLES_CONFIG_PATH", "configs/roles.json") - } - roleResolver, err := controllers.LoadRoleConfig(roleConfigPath) - if err != nil { - log.Fatalf("load role config: %v", err) - } - roles.CheckUserRoleConsistency(dbConn, roleResolver) - app := fiber.New(fiber.Config{ AppName: cfg.AppName, ReadTimeout: time.Duration(cfg.ReadTimeoutSeconds) * time.Second, @@ -139,7 +125,8 @@ func main() { return c.Next() }) - app.Use(controllers.RequireEndpointPermission(roleResolver, authService)) + app.Use(authorization.RequireEndpointPermission(authService, dbConn)) + routes.Register(app, authService, mailService) port := envOrDefault("PORT", "3000") diff --git a/backend/configs/config.json b/backend/configs/config.json index 2050b34..dc6d4af 100644 --- a/backend/configs/config.json +++ b/backend/configs/config.json @@ -14,7 +14,8 @@ "mode": "file", "from": "noreply@example.local", "debug_dir": "data/mail-debug", - "templates_dir": "internal/http/templates", + "templates_dir": "templates", + "mail_templates_dir": "templates/mailTemplates", "frontend_base_url": "http://localhost:9000", "reset_password_path": "/#reset-password", "smtp": { diff --git a/backend/data/data.db b/backend/data/data.db index 6d803aa..8f896a6 100644 Binary files a/backend/data/data.db and b/backend/data/data.db differ diff --git a/backend/internal/http/controllers/admin.go b/backend/internal/admin/controller.go similarity index 88% rename from backend/internal/http/controllers/admin.go rename to backend/internal/admin/controller.go index 6c37491..a8f5f7c 100644 --- a/backend/internal/http/controllers/admin.go +++ b/backend/internal/admin/controller.go @@ -1,4 +1,4 @@ -package controllers +package admin import ( "errors" @@ -7,7 +7,10 @@ import ( "github.com/gofiber/fiber/v3" "gorm.io/gorm" + "server/internal/helpers" "server/internal/models" + "server/internal/responses" + "server/internal/validation" ) type AdminController struct{} @@ -33,7 +36,7 @@ func (ac *AdminController) ListUsers(c fiber.Ctx) error { if err := c.Bind().Body(&req); err != nil { return fiber.NewError(fiber.StatusBadRequest, "invalid payload") } - if err := validateStruct(&req); err != nil { + if err := validation.ValidateStruct(&req); err != nil { return err } if req.Page <= 0 { @@ -43,7 +46,7 @@ func (ac *AdminController) ListUsers(c fiber.Ctx) error { req.PageSize = 20 } - db, err := dbFromCtx(c) + db, err := helpers.DBFromCtx(c) if err != nil { return err } @@ -79,11 +82,11 @@ func (ac *AdminController) BlockUser(c fiber.Ctx) error { if err := c.Bind().Body(&req); err != nil { return fiber.NewError(fiber.StatusBadRequest, "invalid payload") } - if err := validateStruct(&req); err != nil { + if err := validation.ValidateStruct(&req); err != nil { return err } - db, err := dbFromCtx(c) + db, err := helpers.DBFromCtx(c) if err != nil { return err } @@ -115,5 +118,5 @@ func (ac *AdminController) BlockUser(c fiber.Ctx) error { return fiber.NewError(fiber.StatusInternalServerError, "failed to update user status") } - return c.JSON(success(models.ToUserShort(&user))) + return c.JSON(responses.Success(models.ToUserShort(&user))) } diff --git a/backend/internal/admin/routes.go b/backend/internal/admin/routes.go new file mode 100644 index 0000000..ad88ccf --- /dev/null +++ b/backend/internal/admin/routes.go @@ -0,0 +1,19 @@ +package admin + +import ( + "server/internal/authorization" + + "github.com/gofiber/fiber/v3" +) + +func RegisterAdminRoutes(app *fiber.App) { + adminController := NewAdminController() + + // Typescript: TSEndpoint= path=/admin/users; name=listUsers; method=POST; request=admin.ListUsersRequest; response=models.[]UserShort + app.Post("/admin/users", adminController.ListUsers) + authorization.RegisterEndpoint("POST/admin/users", int(authorization.AdminPermission)) + + // Typescript: TSEndpoint= path=/admin/users/:uuid/block; name=blockUser; method=PUT; request=admin.BlockUserRequest; response=models.UserShort + app.Put("/admin/users/:uuid/block", adminController.BlockUser) + authorization.RegisterEndpoint("PUT/admin/users/:uuid/block", int(authorization.AdminPermission)) +} diff --git a/backend/internal/auth/controller/auth.go b/backend/internal/auth/controller.go similarity index 77% rename from backend/internal/auth/controller/auth.go rename to backend/internal/auth/controller.go index 31cff12..3e0dd02 100644 --- a/backend/internal/auth/controller/auth.go +++ b/backend/internal/auth/controller.go @@ -1,4 +1,4 @@ -package controller +package auth import ( "crypto/rand" @@ -6,26 +6,27 @@ import ( "encoding/json" "errors" "fmt" + + "server/internal/helpers" + "server/internal/mail" + "server/internal/models" + "server/internal/responses" + "server/internal/tokens" + "server/internal/validation" "strings" "time" "github.com/gofiber/fiber/v3" "github.com/google/uuid" "gorm.io/gorm" - - authmodel "server/internal/auth/model" - authservice "server/internal/auth/service" - "server/internal/http/controllers" - "server/internal/mail" - "server/internal/models" ) type AuthController struct { - authService *authservice.Service + authService *AuthService mailService *mail.Service } -func New(authService *authservice.Service, mailService *mail.Service) *AuthController { +func New(authService *AuthService, mailService *mail.Service) *AuthController { return &AuthController{ authService: authService, mailService: mailService, @@ -34,15 +35,15 @@ func New(authService *authservice.Service, mailService *mail.Service) *AuthContr // Login authenticates a user and issues an access/refresh token pair. func (ac *AuthController) Login(c fiber.Ctx) error { - var req authmodel.LoginRequest + var req LoginRequest if err := c.Bind().Body(&req); err != nil { return fiber.NewError(fiber.StatusBadRequest, "invalid payload") } - if err := controllers.ValidateStruct(&req); err != nil { + if err := validation.ValidateStruct(&req); err != nil { return err } - db, err := controllers.DBFromCtx(c) + db, err := helpers.DBFromCtx(c) if err != nil { return err } @@ -54,7 +55,7 @@ func (ac *AuthController) Login(c fiber.Ctx) error { } return fiber.NewError(fiber.StatusInternalServerError, "failed to fetch user") } - match, err := authservice.VerifyPassword(user.Password, req.Password) + match, err := VerifyPassword(user.Password, req.Password) if err != nil { return fiber.NewError(fiber.StatusInternalServerError, "failed to verify credentials") } @@ -62,7 +63,7 @@ func (ac *AuthController) Login(c fiber.Ctx) error { return fiber.NewError(fiber.StatusUnauthorized, "invalid credentials") } - tokens, err := ac.authService.GenerateTokenPair(user.Email) + token, err := ac.authService.GenerateTokenPair(user.Email) if err != nil { return fiber.NewError(fiber.StatusInternalServerError, "failed to issue token") } @@ -76,8 +77,8 @@ func (ac *AuthController) Login(c fiber.Ctx) error { session := models.Session{ UserID: &userID, Username: user.Email, - AccessTokenHash: controllers.HashToken(tokens.AccessToken), - RefreshTokenHash: controllers.HashToken(tokens.RefreshToken), + AccessTokenHash: tokens.HashToken(token.AccessToken), + RefreshTokenHash: tokens.HashToken(token.RefreshToken), ExpiresAt: now.Add(ac.authService.RefreshExpiry()), IPAddress: c.IP(), UserAgent: c.Get("User-Agent"), @@ -87,14 +88,14 @@ func (ac *AuthController) Login(c fiber.Ctx) error { return fiber.NewError(fiber.StatusInternalServerError, "failed to record session") } - c.Response().Header.Set("Auth-Token", tokens.AccessToken) + c.Response().Header.Set("Auth-Token", token.AccessToken) - return c.JSON(controllers.Success(tokens)) + return c.JSON(responses.Success(token)) } // Refresh renews an access/refresh token pair using a valid refresh token. func (ac *AuthController) Refresh(c fiber.Ctx) error { - var req authmodel.RefreshRequest + var req RefreshRequest if err := c.Bind().Body(&req); err != nil { return fiber.NewError(fiber.StatusBadRequest, "invalid payload") } @@ -106,30 +107,7 @@ func (ac *AuthController) Refresh(c fiber.Ctx) error { if err != nil { return fiber.NewError(fiber.StatusUnauthorized, err.Error()) } - return c.JSON(controllers.Success(tokens)) -} - -// Me returns the authenticated user's profile (short format). -func (ac *AuthController) Me(c fiber.Ctx) error { - claims, ok := authservice.ClaimsFromCtx(c) - if !ok { - return fiber.NewError(fiber.StatusUnauthorized, "missing claims") - } - - db, err := controllers.DBFromCtx(c) - if err != nil { - return err - } - - var user models.User - if err := db.Preload("Details").Preload("Preferences").Where("email = ?", claims.Username).First(&user).Error; err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - return fiber.NewError(fiber.StatusNotFound, "user not found") - } - return fiber.NewError(fiber.StatusInternalServerError, "failed to load user") - } - - return c.JSON(controllers.Success(models.ToUserShort(&user))) + return c.JSON(responses.Success(tokens)) } // Register creates a new user with optional roles/types/preferences. @@ -138,11 +116,11 @@ func (ac *AuthController) Register(c fiber.Ctx) error { if err := c.Bind().Body(&req); err != nil { return fiber.NewError(fiber.StatusBadRequest, "invalid payload") } - if err := controllers.ValidateStruct(&req); err != nil { + if err := validation.ValidateStruct(&req); err != nil { return err } - db, err := controllers.DBFromCtx(c) + db, err := helpers.DBFromCtx(c) if err != nil { return err } @@ -155,7 +133,7 @@ func (ac *AuthController) Register(c fiber.Ctx) error { } now := time.Now().UTC() - hashedPassword, err := authservice.HashPassword(req.Password) + hashedPassword, err := HashPassword(req.Password) if err != nil { return fiber.NewError(fiber.StatusInternalServerError, "failed to secure password") } @@ -183,7 +161,7 @@ func (ac *AuthController) Register(c fiber.Ctx) error { }(), Avatar: req.Avatar, UUID: uuid.NewString(), - Details: controllers.ToUserDetails(req.Details), + Details: helpers.ToUserDetails(req.Details), Preferences: func() *models.UserPreferences { if req.Preferences == nil { return nil @@ -220,19 +198,19 @@ func (ac *AuthController) Register(c fiber.Ctx) error { return fiber.NewError(fiber.StatusInternalServerError, "failed to send registration email") } - return c.Status(fiber.StatusCreated).JSON(controllers.Success(models.ToUserShort(&user))) + return c.Status(fiber.StatusCreated).JSON(responses.Success(models.ToUserShort(&user))) } func (ac *AuthController) ForgotPassword(c fiber.Ctx) error { - var req authmodel.ForgotPasswordRequest + var req ForgotPasswordRequest if err := c.Bind().Body(&req); err != nil { return fiber.NewError(fiber.StatusBadRequest, "invalid payload") } - if err := controllers.ValidateStruct(&req); err != nil { + if err := validation.ValidateStruct(&req); err != nil { return err } - db, err := controllers.DBFromCtx(c) + db, err := helpers.DBFromCtx(c) if err != nil { return err } @@ -240,13 +218,13 @@ func (ac *AuthController) ForgotPassword(c fiber.Ctx) error { var user models.User if err := db.Where("email = ?", req.Email).First(&user).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { - return c.JSON(controllers.Success(fiber.Map{"sent": true})) + return c.JSON(responses.Success(fiber.Map{"sent": true})) } return fiber.NewError(fiber.StatusInternalServerError, "failed to load user") } if user.Status == models.UserStatusDisabled { - return c.JSON(controllers.Success(fiber.Map{"sent": true})) + return c.JSON(responses.Success(fiber.Map{"sent": true})) } resetToken, err := generateSecureToken() @@ -257,7 +235,7 @@ func (ac *AuthController) ForgotPassword(c fiber.Ctx) error { now := time.Now().UTC() record := models.PasswordResetToken{ UserID: user.ID, - TokenHash: controllers.HashToken(resetToken), + TokenHash: tokens.HashToken(resetToken), ExpiresAt: now.Add(30 * time.Minute), CreatedAt: now, UpdatedAt: now, @@ -288,30 +266,30 @@ func (ac *AuthController) ForgotPassword(c fiber.Ctx) error { return fiber.NewError(fiber.StatusInternalServerError, "failed to send reset email") } - return c.JSON(controllers.Success(controllers.SimpleResponse{Message: "password reset email sent"})) + return c.JSON(responses.Success(responses.SimpleResponse{Message: "password reset email sent"})) } func (ac *AuthController) ResetPassword(c fiber.Ctx) error { - var req authmodel.ResetPasswordRequest + var req ResetPasswordRequest if err := c.Bind().Body(&req); err != nil { return fiber.NewError(fiber.StatusBadRequest, "invalid payload") } - if err := controllers.ValidateStruct(&req); err != nil { + if err := validation.ValidateStruct(&req); err != nil { return err } - db, err := controllers.DBFromCtx(c) + db, err := helpers.DBFromCtx(c) if err != nil { return err } - hashedPassword, err := authservice.HashPassword(req.Password) + hashedPassword, err := HashPassword(req.Password) if err != nil { return fiber.NewError(fiber.StatusInternalServerError, "failed to secure password") } now := time.Now().UTC() - tokenHash := controllers.HashToken(req.Token) + tokenHash := tokens.HashToken(req.Token) if err := db.Transaction(func(tx *gorm.DB) error { var resetToken models.PasswordResetToken if err := tx.Where("token_hash = ?", tokenHash).First(&resetToken).Error; err != nil { @@ -351,7 +329,7 @@ func (ac *AuthController) ResetPassword(c fiber.Ctx) error { return fiber.NewError(fiber.StatusInternalServerError, "failed to reset password") } - return c.JSON(controllers.Success(controllers.SimpleResponse{Message: "password reset successful"})) + return c.JSON(responses.Success(responses.SimpleResponse{Message: "password reset successful"})) } func (ac *AuthController) ValidToken(c fiber.Ctx) error { @@ -371,13 +349,13 @@ func (ac *AuthController) ValidToken(c fiber.Ctx) error { return fiber.NewError(fiber.StatusBadRequest, "token is required") } - db, err := controllers.DBFromCtx(c) + db, err := helpers.DBFromCtx(c) if err != nil { return err } now := time.Now().UTC() - tokenHash := controllers.HashToken(token) + tokenHash := tokens.HashToken(token) var resetToken models.PasswordResetToken if err := db.Where("token_hash = ?", tokenHash).First(&resetToken).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { @@ -390,7 +368,7 @@ func (ac *AuthController) ValidToken(c fiber.Ctx) error { return fiber.NewError(fiber.StatusBadRequest, "invalid or expired reset token") } - return c.JSON(controllers.Success(controllers.SimpleResponse{Message: "valid reset token"})) + return c.JSON(responses.Success(responses.SimpleResponse{Message: "valid reset token"})) } func generateSecureToken() (string, error) { diff --git a/backend/internal/auth/model/auth.go b/backend/internal/auth/model.go similarity index 55% rename from backend/internal/auth/model/auth.go rename to backend/internal/auth/model.go index 2ec124d..2cbcc7a 100644 --- a/backend/internal/auth/model/auth.go +++ b/backend/internal/auth/model.go @@ -1,4 +1,4 @@ -package model +package auth import ( "time" @@ -24,24 +24,3 @@ type TokenPair struct { AccessToken string `json:"access_token"` RefreshToken string `json:"refresh_token"` } - -type Permission int - -type Role struct { - Name string - Permissions Permission -} - -const ( - AdminPermission Permission = 0xff - (1<