Refactor authentication module: Introduce AuthController and endpoints, implement login, registration, password reset, and token management functionalities. Update routes and services to utilize new structures and improve code organization. Enhance user management with detailed error handling and session management. Update API response types and ensure consistent naming conventions across the application.
This commit is contained in:
parent
6920d7ae95
commit
36fca2af6c
10
README.md
10
README.md
|
|
@ -1,3 +1,11 @@
|
|||
# go-quasar-partial-ssr
|
||||
|
||||
bakend in GO frontend quasar framework con generazione delle pagine statiche per la parte public
|
||||
bakend in GO frontend quasar framework con generazione delle pagine statiche per la parte public
|
||||
|
||||
|
||||
internal
|
||||
auth
|
||||
model
|
||||
controller
|
||||
service
|
||||
endpoint
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
//
|
||||
// This file was generated by github.com/millevolte/ts-rpc
|
||||
//
|
||||
// Mar 17, 2026 18:16:42 UTC
|
||||
// Apr 05, 2026 17:08:11 UTC
|
||||
//
|
||||
|
||||
export interface ApiRestResponse {
|
||||
|
|
@ -280,17 +280,74 @@ export type Nullable<T> = T | null;
|
|||
|
||||
export type Record<K extends string | number | symbol, T> = { [P in K]: T };
|
||||
|
||||
//
|
||||
// package model
|
||||
//
|
||||
|
||||
export interface RefreshRequest {
|
||||
refresh_token: string;
|
||||
}
|
||||
|
||||
export interface TokenPair {
|
||||
access_token: string;
|
||||
refresh_token: string;
|
||||
}
|
||||
|
||||
export interface ForgotPasswordRequest {
|
||||
email: string;
|
||||
}
|
||||
|
||||
export interface LoginRequest {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface ResetPasswordRequest {
|
||||
token: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
//
|
||||
// package controllers
|
||||
//
|
||||
|
||||
export interface BlockUserRequest {
|
||||
action: string;
|
||||
}
|
||||
|
||||
export interface ListUsersRequest {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
}
|
||||
|
||||
export interface SimpleResponse {
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface UpdateUserRequest {
|
||||
name: string;
|
||||
email: string;
|
||||
password: string;
|
||||
roles: models.UserRoles;
|
||||
status: models.UserStatus;
|
||||
types: models.UserTypes;
|
||||
avatar: Nullable<string>;
|
||||
details: Nullable<models.UserDetailsShort>;
|
||||
preferences: Nullable<models.UserPreferencesShort>;
|
||||
}
|
||||
|
||||
//
|
||||
// package routes
|
||||
//
|
||||
|
||||
// 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<string> }> => {
|
||||
return (await api.GET(`/users/${uuid}`)) as {
|
||||
data: UserProfile;
|
||||
// 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<string>;
|
||||
}> => {
|
||||
return (await api.GET("/metrics")) as {
|
||||
data: string;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
|
@ -307,38 +364,13 @@ export const mailDebug = async (): Promise<{
|
|||
};
|
||||
};
|
||||
|
||||
// Typescript: TSEndpoint= path=/auth/login; name=login; method=POST; request=controllers.LoginRequest; response=auth.TokenPair
|
||||
// internal/http/routes/auth_routes.go Line: 22
|
||||
|
||||
export const login = async (
|
||||
data: LoginRequest,
|
||||
): Promise<{ data: TokenPair; error: Nullable<string> }> => {
|
||||
return (await api.POST("/auth/login", data)) as {
|
||||
data: TokenPair;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
// Typescript: TSEndpoint= path=/auth/register; name=register; method=POST; request=models.UserCreateInput; response=models.UserShort
|
||||
// internal/http/routes/auth_routes.go Line: 31
|
||||
|
||||
export const register = async (
|
||||
data: UserCreateInput,
|
||||
): Promise<{ data: UserShort; error: Nullable<string> }> => {
|
||||
return (await api.POST("/auth/register", data)) as {
|
||||
data: UserShort;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
// 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<string>;
|
||||
}> => {
|
||||
return (await api.GET("/metrics")) as {
|
||||
data: string;
|
||||
// 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<string> }> => {
|
||||
return (await api.GET(`/users/${uuid}`)) as {
|
||||
data: UserProfile;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
|
@ -355,90 +387,6 @@ export const updateUser = async (
|
|||
};
|
||||
};
|
||||
|
||||
// Typescript: TSEndpoint= path=/auth/password/valid; name=validToken; method=POST; request=string; response=controllers.SimpleResponse
|
||||
// internal/http/routes/auth_routes.go Line: 40
|
||||
|
||||
export const validToken = async (
|
||||
data: string,
|
||||
): Promise<{ data: SimpleResponse; error: Nullable<string> }> => {
|
||||
return (await api.POST("/auth/password/valid", data)) as {
|
||||
data: SimpleResponse;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
// 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<string>;
|
||||
}> => {
|
||||
return (await api.GET("/health")) as {
|
||||
data: string;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
// 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<string> }> => {
|
||||
return (await api.DELETE(`/users/${uuid}`)) as {
|
||||
data: SimpleResponse;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
// Typescript: TSEndpoint= path=/auth/password/reset; name=resetPassword; method=POST; request=controllers.ResetPasswordRequest; response=controllers.SimpleResponse
|
||||
// internal/http/routes/auth_routes.go Line: 37
|
||||
|
||||
export const resetPassword = async (
|
||||
data: ResetPasswordRequest,
|
||||
): Promise<{ data: SimpleResponse; error: Nullable<string> }> => {
|
||||
return (await api.POST("/auth/password/reset", data)) as {
|
||||
data: SimpleResponse;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
// 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<string> }> => {
|
||||
return (await api.POST("/users", data)) as {
|
||||
data: UserProfile;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
// Typescript: TSEndpoint= path=/auth/refresh; name=refresh; method=POST; request=controllers.RefreshRequest; response=auth.TokenPair
|
||||
// internal/http/routes/auth_routes.go Line: 25
|
||||
|
||||
export const refresh = async (
|
||||
data: RefreshRequest,
|
||||
): Promise<{ data: TokenPair; error: Nullable<string> }> => {
|
||||
return (await api.POST("/auth/refresh", data)) as {
|
||||
data: TokenPair;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
// Typescript: TSEndpoint= path=/auth/me; name=me; method=GET; response=models.UserShort
|
||||
// internal/http/routes/auth_routes.go Line: 28
|
||||
export const me = async (): Promise<{
|
||||
data: UserShort;
|
||||
error: Nullable<string>;
|
||||
}> => {
|
||||
return (await api.GET("/auth/me")) as {
|
||||
data: UserShort;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
// Typescript: TSEndpoint= path=/admin/users; name=listUsers; method=POST; request=controllers.ListUsersRequest; response=models.[]UserShort
|
||||
// internal/http/routes/admin_routes.go Line: 12
|
||||
|
||||
|
|
@ -463,18 +411,42 @@ export const blockUser = async (
|
|||
};
|
||||
};
|
||||
|
||||
// Typescript: TSEndpoint= path=/auth/password/forgot; name=forgotPassword; method=POST; request=controllers.ForgotPasswordRequest; response=controllers.SimpleResponse
|
||||
// internal/http/routes/auth_routes.go Line: 34
|
||||
// Typescript: TSEndpoint= path=/users; name=createUser; method=POST; request=models.UserCreateInput; response=models.UserProfile
|
||||
// internal/http/routes/user_routes.go Line: 16
|
||||
|
||||
export const forgotPassword = async (
|
||||
data: ForgotPasswordRequest,
|
||||
export const createUser = async (
|
||||
data: UserCreateInput,
|
||||
): Promise<{ data: UserProfile; error: Nullable<string> }> => {
|
||||
return (await api.POST("/users", data)) as {
|
||||
data: UserProfile;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
// 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<string> }> => {
|
||||
return (await api.POST("/auth/password/forgot", data)) as {
|
||||
return (await api.DELETE(`/users/${uuid}`)) as {
|
||||
data: SimpleResponse;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
// 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<string>;
|
||||
}> => {
|
||||
return (await api.GET("/health")) as {
|
||||
data: string;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
export interface FormRequest {
|
||||
req: string;
|
||||
count: number;
|
||||
|
|
@ -489,6 +461,94 @@ export interface MailDebugItem {
|
|||
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<string> }> => {
|
||||
return (await api.POST("/auth/password/forgot", data)) as {
|
||||
data: SimpleResponse;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
// 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<string> }> => {
|
||||
return (await api.POST("/auth/password/reset", data)) as {
|
||||
data: SimpleResponse;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
// 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<string> }> => {
|
||||
return (await api.POST("/auth/password/valid", data)) as {
|
||||
data: SimpleResponse;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
// 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<string> }> => {
|
||||
return (await api.POST("/auth/login", data)) as {
|
||||
data: TokenPair;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
// 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<string> }> => {
|
||||
return (await api.POST("/auth/refresh", data)) as {
|
||||
data: TokenPair;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
// 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<string>;
|
||||
}> => {
|
||||
return (await api.GET("/auth/me")) as {
|
||||
data: UserShort;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
// 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<string> }> => {
|
||||
return (await api.POST("/auth/register", data)) as {
|
||||
data: UserShort;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
//
|
||||
// package models
|
||||
//
|
||||
|
|
@ -516,17 +576,6 @@ export interface UserDetailsShort {
|
|||
phone: string;
|
||||
}
|
||||
|
||||
export interface UserShort {
|
||||
email: string;
|
||||
name: string;
|
||||
roles: UserRoles;
|
||||
status: UserStatus;
|
||||
uuid: string;
|
||||
details: Nullable<UserDetailsShort>;
|
||||
preferences: Nullable<UserPreferencesShort>;
|
||||
avatar: Nullable<string>;
|
||||
}
|
||||
|
||||
export interface UserPreferencesShort {
|
||||
useIdle: boolean;
|
||||
idleTimeout: number;
|
||||
|
|
@ -538,7 +587,16 @@ export interface UserPreferencesShort {
|
|||
language: string;
|
||||
}
|
||||
|
||||
export type UsersShort = UserShort[];
|
||||
export interface UserShort {
|
||||
email: string;
|
||||
name: string;
|
||||
roles: UserRoles;
|
||||
status: UserStatus;
|
||||
uuid: string;
|
||||
details: Nullable<UserDetailsShort>;
|
||||
preferences: Nullable<UserPreferencesShort>;
|
||||
avatar: Nullable<string>;
|
||||
}
|
||||
|
||||
export type UserRoles = string[];
|
||||
|
||||
|
|
@ -546,64 +604,10 @@ export type UserStatus = (typeof EnumUserStatus)[keyof typeof EnumUserStatus];
|
|||
|
||||
export type UserTypes = string[];
|
||||
|
||||
export type UsersShort = UserShort[];
|
||||
|
||||
export const EnumUserStatus = {
|
||||
UserStatusPending: "pending",
|
||||
UserStatusActive: "active",
|
||||
UserStatusDisabled: "disabled",
|
||||
} as const;
|
||||
|
||||
//
|
||||
// package controllers
|
||||
//
|
||||
|
||||
export interface ResetPasswordRequest {
|
||||
token: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface RefreshRequest {
|
||||
refresh_token: string;
|
||||
}
|
||||
|
||||
export interface SimpleResponse {
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface UpdateUserRequest {
|
||||
name: string;
|
||||
email: string;
|
||||
password: string;
|
||||
roles: models.UserRoles;
|
||||
status: models.UserStatus;
|
||||
types: models.UserTypes;
|
||||
avatar: Nullable<string>;
|
||||
details: Nullable<models.UserDetailsShort>;
|
||||
preferences: Nullable<models.UserPreferencesShort>;
|
||||
}
|
||||
|
||||
export interface BlockUserRequest {
|
||||
action: string;
|
||||
}
|
||||
|
||||
export interface ForgotPasswordRequest {
|
||||
email: string;
|
||||
}
|
||||
|
||||
export interface ListUsersRequest {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
}
|
||||
|
||||
export interface LoginRequest {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
//
|
||||
// package auth
|
||||
//
|
||||
|
||||
export interface TokenPair {
|
||||
access_token: string;
|
||||
refresh_token: string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ import (
|
|||
"syscall"
|
||||
"time"
|
||||
|
||||
"server/internal/auth"
|
||||
authmodel "server/internal/auth/model"
|
||||
authservice "server/internal/auth/service"
|
||||
"server/internal/config"
|
||||
"server/internal/db"
|
||||
"server/internal/http/controllers"
|
||||
|
|
@ -54,7 +55,7 @@ func main() {
|
|||
log.Fatalf("init db: %v", err)
|
||||
}
|
||||
|
||||
authService, err := auth.New(auth.Config{
|
||||
authService, err := authservice.New(authmodel.Config{
|
||||
Secret: cfg.Auth.Secret,
|
||||
Issuer: cfg.Auth.Issuer,
|
||||
AccessTokenExpiry: time.Duration(cfg.Auth.AccessTokenExpiryMinutes) * time.Minute,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package controllers
|
||||
package controller
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
|
|
@ -10,65 +10,39 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"server/internal/auth"
|
||||
authmodel "server/internal/auth/model"
|
||||
authservice "server/internal/auth/service"
|
||||
"server/internal/http/controllers"
|
||||
"server/internal/mail"
|
||||
"server/internal/models"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type AuthController struct {
|
||||
authService *auth.Service
|
||||
authService *authservice.Service
|
||||
mailService *mail.Service
|
||||
}
|
||||
|
||||
// Typescript: interface
|
||||
type SimpleResponse struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func NewAuthController(authService *auth.Service, mailService *mail.Service) *AuthController {
|
||||
func New(authService *authservice.Service, mailService *mail.Service) *AuthController {
|
||||
return &AuthController{
|
||||
authService: authService,
|
||||
mailService: mailService,
|
||||
}
|
||||
}
|
||||
|
||||
// Typescript: interface
|
||||
type LoginRequest struct {
|
||||
Username string `json:"username" validate:"required,email"`
|
||||
Password string `json:"password" validate:"required,min=8,max=128"`
|
||||
}
|
||||
|
||||
// Typescript: interface
|
||||
type RefreshRequest struct {
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
}
|
||||
|
||||
// Typescript: interface
|
||||
type ForgotPasswordRequest struct {
|
||||
Email string `json:"email" validate:"required,email"`
|
||||
}
|
||||
|
||||
// Typescript: interface
|
||||
type ResetPasswordRequest struct {
|
||||
Token string `json:"token" validate:"required,min=20,max=255"`
|
||||
Password string `json:"password" validate:"required,min=8,max=128"`
|
||||
}
|
||||
|
||||
// Login authenticates a user and issues an access/refresh token pair.
|
||||
func (ac *AuthController) Login(c fiber.Ctx) error {
|
||||
var req LoginRequest
|
||||
var req authmodel.LoginRequest
|
||||
if err := c.Bind().Body(&req); err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "invalid payload")
|
||||
}
|
||||
if err := validateStruct(&req); err != nil {
|
||||
if err := controllers.ValidateStruct(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
db, err := dbFromCtx(c)
|
||||
db, err := controllers.DBFromCtx(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -80,7 +54,7 @@ func (ac *AuthController) Login(c fiber.Ctx) error {
|
|||
}
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "failed to fetch user")
|
||||
}
|
||||
match, err := auth.VerifyPassword(user.Password, req.Password)
|
||||
match, err := authservice.VerifyPassword(user.Password, req.Password)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "failed to verify credentials")
|
||||
}
|
||||
|
|
@ -102,8 +76,8 @@ func (ac *AuthController) Login(c fiber.Ctx) error {
|
|||
session := models.Session{
|
||||
UserID: &userID,
|
||||
Username: user.Email,
|
||||
AccessTokenHash: hashToken(tokens.AccessToken),
|
||||
RefreshTokenHash: hashToken(tokens.RefreshToken),
|
||||
AccessTokenHash: controllers.HashToken(tokens.AccessToken),
|
||||
RefreshTokenHash: controllers.HashToken(tokens.RefreshToken),
|
||||
ExpiresAt: now.Add(ac.authService.RefreshExpiry()),
|
||||
IPAddress: c.IP(),
|
||||
UserAgent: c.Get("User-Agent"),
|
||||
|
|
@ -113,15 +87,14 @@ func (ac *AuthController) Login(c fiber.Ctx) error {
|
|||
return fiber.NewError(fiber.StatusInternalServerError, "failed to record session")
|
||||
}
|
||||
|
||||
//c.Set("Auth-Token", tokens.AccessToken)
|
||||
c.Response().Header.Set("Auth-Token", tokens.AccessToken)
|
||||
|
||||
return c.JSON(success(tokens))
|
||||
return c.JSON(controllers.Success(tokens))
|
||||
}
|
||||
|
||||
// Refresh renews an access/refresh token pair using a valid refresh token.
|
||||
func (ac *AuthController) Refresh(c fiber.Ctx) error {
|
||||
var req RefreshRequest
|
||||
var req authmodel.RefreshRequest
|
||||
if err := c.Bind().Body(&req); err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "invalid payload")
|
||||
}
|
||||
|
|
@ -133,17 +106,17 @@ func (ac *AuthController) Refresh(c fiber.Ctx) error {
|
|||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusUnauthorized, err.Error())
|
||||
}
|
||||
return c.JSON(success(tokens))
|
||||
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 := auth.ClaimsFromCtx(c)
|
||||
claims, ok := authservice.ClaimsFromCtx(c)
|
||||
if !ok {
|
||||
return fiber.NewError(fiber.StatusUnauthorized, "missing claims")
|
||||
}
|
||||
|
||||
db, err := dbFromCtx(c)
|
||||
db, err := controllers.DBFromCtx(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -156,7 +129,7 @@ func (ac *AuthController) Me(c fiber.Ctx) error {
|
|||
return fiber.NewError(fiber.StatusInternalServerError, "failed to load user")
|
||||
}
|
||||
|
||||
return c.JSON(success(models.ToUserShort(&user)))
|
||||
return c.JSON(controllers.Success(models.ToUserShort(&user)))
|
||||
}
|
||||
|
||||
// Register creates a new user with optional roles/types/preferences.
|
||||
|
|
@ -165,11 +138,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 := validateStruct(&req); err != nil {
|
||||
if err := controllers.ValidateStruct(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
db, err := dbFromCtx(c)
|
||||
db, err := controllers.DBFromCtx(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -182,7 +155,7 @@ func (ac *AuthController) Register(c fiber.Ctx) error {
|
|||
}
|
||||
|
||||
now := time.Now().UTC()
|
||||
hashedPassword, err := auth.HashPassword(req.Password)
|
||||
hashedPassword, err := authservice.HashPassword(req.Password)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "failed to secure password")
|
||||
}
|
||||
|
|
@ -210,7 +183,7 @@ func (ac *AuthController) Register(c fiber.Ctx) error {
|
|||
}(),
|
||||
Avatar: req.Avatar,
|
||||
UUID: uuid.NewString(),
|
||||
Details: toUserDetails(req.Details),
|
||||
Details: controllers.ToUserDetails(req.Details),
|
||||
Preferences: func() *models.UserPreferences {
|
||||
if req.Preferences == nil {
|
||||
return nil
|
||||
|
|
@ -247,19 +220,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(success(models.ToUserShort(&user)))
|
||||
return c.Status(fiber.StatusCreated).JSON(controllers.Success(models.ToUserShort(&user)))
|
||||
}
|
||||
|
||||
func (ac *AuthController) ForgotPassword(c fiber.Ctx) error {
|
||||
var req ForgotPasswordRequest
|
||||
var req authmodel.ForgotPasswordRequest
|
||||
if err := c.Bind().Body(&req); err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "invalid payload")
|
||||
}
|
||||
if err := validateStruct(&req); err != nil {
|
||||
if err := controllers.ValidateStruct(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
db, err := dbFromCtx(c)
|
||||
db, err := controllers.DBFromCtx(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -267,13 +240,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(success(fiber.Map{"sent": true}))
|
||||
return c.JSON(controllers.Success(fiber.Map{"sent": true}))
|
||||
}
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "failed to load user")
|
||||
}
|
||||
|
||||
if user.Status == models.UserStatusDisabled {
|
||||
return c.JSON(success(fiber.Map{"sent": true}))
|
||||
return c.JSON(controllers.Success(fiber.Map{"sent": true}))
|
||||
}
|
||||
|
||||
resetToken, err := generateSecureToken()
|
||||
|
|
@ -284,7 +257,7 @@ func (ac *AuthController) ForgotPassword(c fiber.Ctx) error {
|
|||
now := time.Now().UTC()
|
||||
record := models.PasswordResetToken{
|
||||
UserID: user.ID,
|
||||
TokenHash: hashToken(resetToken),
|
||||
TokenHash: controllers.HashToken(resetToken),
|
||||
ExpiresAt: now.Add(30 * time.Minute),
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
|
|
@ -315,30 +288,30 @@ func (ac *AuthController) ForgotPassword(c fiber.Ctx) error {
|
|||
return fiber.NewError(fiber.StatusInternalServerError, "failed to send reset email")
|
||||
}
|
||||
|
||||
return c.JSON(success(SimpleResponse{Message: "password reset email sent"}))
|
||||
return c.JSON(controllers.Success(controllers.SimpleResponse{Message: "password reset email sent"}))
|
||||
}
|
||||
|
||||
func (ac *AuthController) ResetPassword(c fiber.Ctx) error {
|
||||
var req ResetPasswordRequest
|
||||
var req authmodel.ResetPasswordRequest
|
||||
if err := c.Bind().Body(&req); err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "invalid payload")
|
||||
}
|
||||
if err := validateStruct(&req); err != nil {
|
||||
if err := controllers.ValidateStruct(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
db, err := dbFromCtx(c)
|
||||
db, err := controllers.DBFromCtx(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hashedPassword, err := auth.HashPassword(req.Password)
|
||||
hashedPassword, err := authservice.HashPassword(req.Password)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "failed to secure password")
|
||||
}
|
||||
|
||||
now := time.Now().UTC()
|
||||
tokenHash := hashToken(req.Token)
|
||||
tokenHash := controllers.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 {
|
||||
|
|
@ -378,7 +351,7 @@ func (ac *AuthController) ResetPassword(c fiber.Ctx) error {
|
|||
return fiber.NewError(fiber.StatusInternalServerError, "failed to reset password")
|
||||
}
|
||||
|
||||
return c.JSON(success(SimpleResponse{Message: "password reset successful"}))
|
||||
return c.JSON(controllers.Success(controllers.SimpleResponse{Message: "password reset successful"}))
|
||||
}
|
||||
|
||||
func (ac *AuthController) ValidToken(c fiber.Ctx) error {
|
||||
|
|
@ -387,7 +360,6 @@ func (ac *AuthController) ValidToken(c fiber.Ctx) error {
|
|||
return fiber.NewError(fiber.StatusBadRequest, "token is required")
|
||||
}
|
||||
|
||||
// Accept both plain text token payload and JSON string payload.
|
||||
token := raw
|
||||
if strings.HasPrefix(raw, "\"") && strings.HasSuffix(raw, "\"") {
|
||||
if err := json.Unmarshal([]byte(raw), &token); err != nil {
|
||||
|
|
@ -399,13 +371,13 @@ func (ac *AuthController) ValidToken(c fiber.Ctx) error {
|
|||
return fiber.NewError(fiber.StatusBadRequest, "token is required")
|
||||
}
|
||||
|
||||
db, err := dbFromCtx(c)
|
||||
db, err := controllers.DBFromCtx(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
now := time.Now().UTC()
|
||||
tokenHash := hashToken(token)
|
||||
tokenHash := controllers.HashToken(token)
|
||||
var resetToken models.PasswordResetToken
|
||||
if err := db.Where("token_hash = ?", tokenHash).First(&resetToken).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
|
|
@ -418,7 +390,7 @@ func (ac *AuthController) ValidToken(c fiber.Ctx) error {
|
|||
return fiber.NewError(fiber.StatusBadRequest, "invalid or expired reset token")
|
||||
}
|
||||
|
||||
return c.JSON(success(SimpleResponse{Message: "valid reset token"}))
|
||||
return c.JSON(controllers.Success(controllers.SimpleResponse{Message: "valid reset token"}))
|
||||
}
|
||||
|
||||
func generateSecureToken() (string, error) {
|
||||
|
|
@ -1,28 +1,28 @@
|
|||
package routes
|
||||
package endpoint
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"server/internal/auth"
|
||||
"server/internal/http/controllers"
|
||||
authcontroller "server/internal/auth/controller"
|
||||
authservice "server/internal/auth/service"
|
||||
"server/internal/mail"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"github.com/gofiber/fiber/v3/middleware/limiter"
|
||||
)
|
||||
|
||||
func registerAuthRoutes(app *fiber.App, authService *auth.Service, mailService *mail.Service) {
|
||||
authController := controllers.NewAuthController(authService, mailService)
|
||||
func Register(app *fiber.App, authService *authservice.Service, mailService *mail.Service) {
|
||||
authController := authcontroller.New(authService, mailService)
|
||||
authRateLimiter := limiter.New(limiter.Config{
|
||||
Max: 10,
|
||||
Expiration: time.Minute,
|
||||
LimiterMiddleware: limiter.SlidingWindow{},
|
||||
})
|
||||
|
||||
// Typescript: TSEndpoint= path=/auth/login; name=login; method=POST; request=controllers.LoginRequest; response=auth.TokenPair
|
||||
// Typescript: TSEndpoint= path=/auth/login; name=login; method=POST; request=model.LoginRequest; response=model.TokenPair
|
||||
app.Post("/auth/login", authRateLimiter, authController.Login)
|
||||
|
||||
// Typescript: TSEndpoint= path=/auth/refresh; name=refresh; method=POST; request=controllers.RefreshRequest; response=auth.TokenPair
|
||||
// Typescript: TSEndpoint= path=/auth/refresh; name=refresh; method=POST; request=model.RefreshRequest; response=model.TokenPair
|
||||
app.Post("/auth/refresh", authService.Middleware(), authController.Refresh)
|
||||
|
||||
// Typescript: TSEndpoint= path=/auth/me; name=me; method=GET; response=models.UserShort
|
||||
|
|
@ -31,10 +31,10 @@ func registerAuthRoutes(app *fiber.App, authService *auth.Service, mailService *
|
|||
// Typescript: TSEndpoint= path=/auth/register; name=register; method=POST; request=models.UserCreateInput; response=models.UserShort
|
||||
app.Post("/auth/register", authRateLimiter, authController.Register)
|
||||
|
||||
// Typescript: TSEndpoint= path=/auth/password/forgot; name=forgotPassword; method=POST; request=controllers.ForgotPasswordRequest; response=controllers.SimpleResponse
|
||||
// Typescript: TSEndpoint= path=/auth/password/forgot; name=forgotPassword; method=POST; request=model.ForgotPasswordRequest; response=controllers.SimpleResponse
|
||||
app.Post("/auth/password/forgot", authRateLimiter, authController.ForgotPassword)
|
||||
|
||||
// Typescript: TSEndpoint= path=/auth/password/reset; name=resetPassword; method=POST; request=controllers.ResetPasswordRequest; response=controllers.SimpleResponse
|
||||
// Typescript: TSEndpoint= path=/auth/password/reset; name=resetPassword; method=POST; request=model.ResetPasswordRequest; response=controllers.SimpleResponse
|
||||
app.Post("/auth/password/reset", authRateLimiter, authController.ResetPassword)
|
||||
|
||||
// Typescript: TSEndpoint= path=/auth/password/valid; name=validToken; method=POST; request=string; response=controllers.SimpleResponse
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Secret string
|
||||
Issuer string
|
||||
AccessTokenExpiry time.Duration
|
||||
RefreshTokenExpiry time.Duration
|
||||
}
|
||||
|
||||
type Claims struct {
|
||||
Username string `json:"username"`
|
||||
TokenType string `json:"type"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
// Typescript: interface
|
||||
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<<iota - 1)
|
||||
ManagerPermission
|
||||
UserPermission
|
||||
GuestPermission
|
||||
)
|
||||
|
||||
var Roles = []Role{
|
||||
{"admin", AdminPermission},
|
||||
{"manager", ManagerPermission},
|
||||
{"user", UserPermission},
|
||||
{"guest", GuestPermission},
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package model
|
||||
|
||||
// Typescript: interface
|
||||
type LoginRequest struct {
|
||||
Username string `json:"username" validate:"required,email"`
|
||||
Password string `json:"password" validate:"required,min=8,max=128"`
|
||||
}
|
||||
|
||||
// Typescript: interface
|
||||
type RefreshRequest struct {
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
}
|
||||
|
||||
// Typescript: interface
|
||||
type ForgotPasswordRequest struct {
|
||||
Email string `json:"email" validate:"required,email"`
|
||||
}
|
||||
|
||||
// Typescript: interface
|
||||
type ResetPasswordRequest struct {
|
||||
Token string `json:"token" validate:"required,min=20,max=255"`
|
||||
Password string `json:"password" validate:"required,min=8,max=128"`
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
package auth
|
||||
|
||||
type Permission int
|
||||
type Role struct {
|
||||
Name string
|
||||
Permissions Permission
|
||||
}
|
||||
|
||||
const (
|
||||
AdminPermission Permission = 0xff - (1<<iota - 1)
|
||||
ManagerPermission
|
||||
UserPermission
|
||||
GuestPermission
|
||||
)
|
||||
|
||||
var Roles = []Role{
|
||||
{"admin", AdminPermission},
|
||||
{"manager", ManagerPermission},
|
||||
{"user", UserPermission},
|
||||
{"guest", GuestPermission},
|
||||
}
|
||||
|
|
@ -1,46 +1,28 @@
|
|||
package auth
|
||||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
|
||||
authmodel "server/internal/auth/model"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Secret string
|
||||
Issuer string
|
||||
AccessTokenExpiry time.Duration
|
||||
RefreshTokenExpiry time.Duration
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
cfg Config
|
||||
cfg authmodel.Config
|
||||
secret []byte
|
||||
accessExpiry time.Duration
|
||||
refreshExpiry time.Duration
|
||||
}
|
||||
|
||||
type Claims struct {
|
||||
Username string `json:"username"`
|
||||
TokenType string `json:"type"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
// Typescript: interface
|
||||
type TokenPair struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
}
|
||||
|
||||
const (
|
||||
tokenTypeAccess = "access"
|
||||
tokenTypeRefresh = "refresh"
|
||||
)
|
||||
|
||||
func New(cfg Config) (*Service, error) {
|
||||
func New(cfg authmodel.Config) (*Service, error) {
|
||||
if cfg.Secret == "" {
|
||||
return nil, errors.New("jwt secret is required")
|
||||
}
|
||||
|
|
@ -59,29 +41,27 @@ func New(cfg Config) (*Service, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (s *Service) GenerateTokenPair(username string) (TokenPair, error) {
|
||||
func (s *Service) GenerateTokenPair(username string) (authmodel.TokenPair, error) {
|
||||
access, err := s.generateToken(username, tokenTypeAccess, s.accessExpiry)
|
||||
if err != nil {
|
||||
return TokenPair{}, err
|
||||
return authmodel.TokenPair{}, err
|
||||
}
|
||||
|
||||
refresh, err := s.generateToken(username, tokenTypeRefresh, s.refreshExpiry)
|
||||
if err != nil {
|
||||
return TokenPair{}, err
|
||||
return authmodel.TokenPair{}, err
|
||||
}
|
||||
|
||||
return TokenPair{
|
||||
return authmodel.TokenPair{
|
||||
AccessToken: access,
|
||||
RefreshToken: refresh,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// AccessExpiry returns the configured access token lifetime.
|
||||
func (s *Service) AccessExpiry() time.Duration {
|
||||
return s.accessExpiry
|
||||
}
|
||||
|
||||
// RefreshExpiry returns the configured refresh token lifetime.
|
||||
func (s *Service) RefreshExpiry() time.Duration {
|
||||
return s.refreshExpiry
|
||||
}
|
||||
|
|
@ -106,19 +86,18 @@ func (s *Service) Middleware() fiber.Handler {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *Service) Refresh(refreshToken string) (TokenPair, error) {
|
||||
func (s *Service) Refresh(refreshToken string) (authmodel.TokenPair, error) {
|
||||
claims, err := s.parseToken(refreshToken)
|
||||
if err != nil {
|
||||
return TokenPair{}, err
|
||||
return authmodel.TokenPair{}, err
|
||||
}
|
||||
if claims.TokenType != tokenTypeRefresh {
|
||||
return TokenPair{}, errors.New("refresh token required")
|
||||
return authmodel.TokenPair{}, errors.New("refresh token required")
|
||||
}
|
||||
return s.GenerateTokenPair(claims.Username)
|
||||
}
|
||||
|
||||
// ValidateAccessToken parses and validates an access token string, ensuring type=access.
|
||||
func (s *Service) ValidateAccessToken(tokenString string) (*Claims, error) {
|
||||
func (s *Service) ValidateAccessToken(tokenString string) (*authmodel.Claims, error) {
|
||||
claims, err := s.parseToken(tokenString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -129,8 +108,17 @@ func (s *Service) ValidateAccessToken(tokenString string) (*Claims, error) {
|
|||
return claims, nil
|
||||
}
|
||||
|
||||
func (s *Service) parseToken(tokenString string) (*Claims, error) {
|
||||
claims := &Claims{}
|
||||
func ClaimsFromCtx(c fiber.Ctx) (*authmodel.Claims, bool) {
|
||||
val := c.Locals("authClaims")
|
||||
if val == nil {
|
||||
return nil, false
|
||||
}
|
||||
claims, ok := val.(*authmodel.Claims)
|
||||
return claims, ok
|
||||
}
|
||||
|
||||
func (s *Service) parseToken(tokenString string) (*authmodel.Claims, error) {
|
||||
claims := &authmodel.Claims{}
|
||||
token, err := jwt.ParseWithClaims(tokenString, claims, func(t *jwt.Token) (interface{}, error) {
|
||||
if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fiber.ErrUnauthorized
|
||||
|
|
@ -148,7 +136,7 @@ func (s *Service) parseToken(tokenString string) (*Claims, error) {
|
|||
}
|
||||
|
||||
func (s *Service) generateToken(username, tokenType string, expiry time.Duration) (string, error) {
|
||||
claims := Claims{
|
||||
claims := authmodel.Claims{
|
||||
Username: username,
|
||||
TokenType: tokenType,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
|
|
@ -161,27 +149,3 @@ func (s *Service) generateToken(username, tokenType string, expiry time.Duration
|
|||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
return token.SignedString(s.secret)
|
||||
}
|
||||
|
||||
func bearerToken(header string) (string, error) {
|
||||
if header == "" {
|
||||
return "", errors.New("missing Auth-Token header")
|
||||
}
|
||||
if !strings.HasPrefix(header, "Bearer ") {
|
||||
return "", errors.New("invalid Authorization header format")
|
||||
}
|
||||
|
||||
token := strings.TrimSpace(strings.TrimPrefix(header, "Bearer "))
|
||||
if token == "" {
|
||||
return "", errors.New("empty bearer token")
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func ClaimsFromCtx(c fiber.Ctx) (*Claims, bool) {
|
||||
val := c.Locals("authClaims")
|
||||
if val == nil {
|
||||
return nil, false
|
||||
}
|
||||
claims, ok := val.(*Claims)
|
||||
return claims, ok
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package auth
|
||||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
|
@ -10,7 +10,7 @@ import (
|
|||
"github.com/gofiber/fiber/v3"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"server/internal/auth"
|
||||
authservice "server/internal/auth/service"
|
||||
"server/internal/models"
|
||||
)
|
||||
|
||||
|
|
@ -128,7 +128,7 @@ func (r *RoleResolver) RoleDefined(role string) bool {
|
|||
// RequireRole ensures the authenticated user has the specified role (with inheritance).
|
||||
func RequireRole(resolver *RoleResolver, role string) fiber.Handler {
|
||||
return func(c fiber.Ctx) error {
|
||||
claims, ok := auth.ClaimsFromCtx(c)
|
||||
claims, ok := authservice.ClaimsFromCtx(c)
|
||||
if !ok {
|
||||
return fiber.NewError(fiber.StatusUnauthorized, "missing claims")
|
||||
}
|
||||
|
|
@ -156,7 +156,7 @@ func RequireRole(resolver *RoleResolver, role string) fiber.Handler {
|
|||
// RequirePermission ensures the authenticated user has the given permission.
|
||||
func RequirePermission(resolver *RoleResolver, perm string) fiber.Handler {
|
||||
return func(c fiber.Ctx) error {
|
||||
claims, ok := auth.ClaimsFromCtx(c)
|
||||
claims, ok := authservice.ClaimsFromCtx(c)
|
||||
if !ok {
|
||||
return fiber.NewError(fiber.StatusUnauthorized, "missing claims")
|
||||
}
|
||||
|
|
@ -183,7 +183,7 @@ func RequirePermission(resolver *RoleResolver, perm string) fiber.Handler {
|
|||
|
||||
// RequireEndpointPermission enforces permission mapping defined in role config.
|
||||
// If the endpoint is not configured, or mapped to "*", it allows the request.
|
||||
func RequireEndpointPermission(resolver *RoleResolver, authService *auth.Service) fiber.Handler {
|
||||
func RequireEndpointPermission(resolver *RoleResolver, authService *authservice.Service) fiber.Handler {
|
||||
return func(c fiber.Ctx) error {
|
||||
perm, ok := resolver.PermissionForEndpoint(c.Method(), c.Path())
|
||||
if !ok || perm == "*" {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,10 @@ func dbFromCtx(c fiber.Ctx) (*gorm.DB, error) {
|
|||
return db, nil
|
||||
}
|
||||
|
||||
func DBFromCtx(c fiber.Ctx) (*gorm.DB, error) {
|
||||
return dbFromCtx(c)
|
||||
}
|
||||
|
||||
func toUserDetails(d *models.UserDetailsShort) *models.UserDetails {
|
||||
if d == nil {
|
||||
return nil
|
||||
|
|
@ -33,6 +37,10 @@ func toUserDetails(d *models.UserDetailsShort) *models.UserDetails {
|
|||
}
|
||||
}
|
||||
|
||||
func ToUserDetails(d *models.UserDetailsShort) *models.UserDetails {
|
||||
return toUserDetails(d)
|
||||
}
|
||||
|
||||
func toUserPreferences(p *models.UserPreferencesShort) *models.UserPreferences {
|
||||
if p == nil {
|
||||
return nil
|
||||
|
|
@ -48,3 +56,7 @@ func toUserPreferences(p *models.UserPreferencesShort) *models.UserPreferences {
|
|||
Language: p.Language,
|
||||
}
|
||||
}
|
||||
|
||||
func ToUserPreferences(p *models.UserPreferencesShort) *models.UserPreferences {
|
||||
return toUserPreferences(p)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,3 +9,7 @@ func success(data any) fiber.Map {
|
|||
"error": nil,
|
||||
}
|
||||
}
|
||||
|
||||
func Success(data any) fiber.Map {
|
||||
return success(data)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
package controllers
|
||||
|
||||
// Typescript: interface
|
||||
type SimpleResponse struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
|
@ -9,3 +9,7 @@ func hashToken(token string) string {
|
|||
sum := sha256.Sum256([]byte(token))
|
||||
return hex.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
func HashToken(token string) string {
|
||||
return hashToken(token)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import (
|
|||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"server/internal/auth"
|
||||
authservice "server/internal/auth/service"
|
||||
"server/internal/models"
|
||||
)
|
||||
|
||||
|
|
@ -63,7 +63,7 @@ func (uc *UserController) CreateUser(c fiber.Ctx) error {
|
|||
return fiber.NewError(fiber.StatusInternalServerError, "failed to check user")
|
||||
}
|
||||
|
||||
hashedPassword, err := auth.HashPassword(req.Password)
|
||||
hashedPassword, err := authservice.HashPassword(req.Password)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "failed to secure password")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,3 +26,7 @@ func validateStruct(payload any) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ValidateStruct(payload any) error {
|
||||
return validateStruct(payload)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
package routes
|
||||
|
||||
import (
|
||||
"server/internal/auth"
|
||||
authendpoint "server/internal/auth/endpoint"
|
||||
authservice "server/internal/auth/service"
|
||||
"server/internal/mail"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
|
|
@ -18,9 +19,9 @@ type FormResponse struct {
|
|||
Test string `json:"test"`
|
||||
}
|
||||
|
||||
func Register(app *fiber.App, authService *auth.Service, mailService *mail.Service) {
|
||||
func Register(app *fiber.App, authService *authservice.Service, mailService *mail.Service) {
|
||||
registerSystemRoutes(app)
|
||||
registerAuthRoutes(app, authService, mailService)
|
||||
authendpoint.Register(app, authService, mailService)
|
||||
registerUserRoutes(app, authService)
|
||||
registerAdminRoutes(app)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
package routes
|
||||
|
||||
import (
|
||||
"server/internal/auth"
|
||||
authservice "server/internal/auth/service"
|
||||
"server/internal/http/controllers"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
)
|
||||
|
||||
func registerUserRoutes(app *fiber.App, authService *auth.Service) {
|
||||
func registerUserRoutes(app *fiber.App, authService *authservice.Service) {
|
||||
userController := controllers.NewUserController()
|
||||
|
||||
// Typescript: TSEndpoint= path=/users/:uuid; name=getUser; method=GET; response=models.UserProfile
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import (
|
|||
"github.com/brianvoe/gofakeit/v6"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"server/internal/auth"
|
||||
authservice "server/internal/auth/service"
|
||||
"server/internal/models"
|
||||
)
|
||||
|
||||
|
|
@ -38,7 +38,7 @@ func SeedUsers(db *gorm.DB, n int) ([]models.User, []Credential, error) {
|
|||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("generate password: %w", err)
|
||||
}
|
||||
passwordHash, err := auth.HashPassword(pw)
|
||||
passwordHash, err := authservice.HashPassword(pw)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("hash seed password: %w", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
//
|
||||
// This file was generated by github.com/millevolte/ts-rpc
|
||||
//
|
||||
// Mar 15, 2026 16:33:29 UTC
|
||||
// Apr 05, 2026 17:08:11 UTC
|
||||
//
|
||||
|
||||
export interface ApiRestResponse {
|
||||
|
|
@ -185,6 +185,30 @@ export default class Api {
|
|||
}
|
||||
}
|
||||
|
||||
async PUT(
|
||||
url: string,
|
||||
data: unknown,
|
||||
timeout?: number,
|
||||
): Promise<{
|
||||
data: unknown;
|
||||
error: string | null;
|
||||
}> {
|
||||
try {
|
||||
const upload = url.includes("/upload/");
|
||||
const result = await this.request(
|
||||
"PUT",
|
||||
this.apiUrl + url,
|
||||
data,
|
||||
timeout,
|
||||
upload,
|
||||
);
|
||||
|
||||
return this.processResult(result);
|
||||
} catch (error: unknown) {
|
||||
return this.processError(error);
|
||||
}
|
||||
}
|
||||
|
||||
async GET(
|
||||
url: string,
|
||||
timeout?: number,
|
||||
|
|
@ -205,22 +229,6 @@ export default class Api {
|
|||
}
|
||||
}
|
||||
|
||||
async PUT(
|
||||
url: string,
|
||||
data: unknown,
|
||||
timeout?: number,
|
||||
): Promise<{
|
||||
data: unknown;
|
||||
error: string | null;
|
||||
}> {
|
||||
try {
|
||||
const result = await this.request("PUT", this.apiUrl + url, data, timeout);
|
||||
return this.processResult(result);
|
||||
} catch (error: unknown) {
|
||||
return this.processError(error);
|
||||
}
|
||||
}
|
||||
|
||||
async DELETE(
|
||||
url: string,
|
||||
timeout?: number,
|
||||
|
|
@ -229,7 +237,12 @@ export default class Api {
|
|||
error: string | null;
|
||||
}> {
|
||||
try {
|
||||
const result = await this.request("DELETE", this.apiUrl + url, null, timeout);
|
||||
const result = await this.request(
|
||||
"DELETE",
|
||||
this.apiUrl + url,
|
||||
null,
|
||||
timeout,
|
||||
);
|
||||
return this.processResult(result);
|
||||
} catch (error: unknown) {
|
||||
return this.processError(error);
|
||||
|
|
@ -268,178 +281,73 @@ export type Nullable<T> = T | null;
|
|||
export type Record<K extends string | number | symbol, T> = { [P in K]: T };
|
||||
|
||||
//
|
||||
// package controllers
|
||||
// package model
|
||||
//
|
||||
|
||||
export interface LoginRequest {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface RefreshRequest {
|
||||
refresh_token: string;
|
||||
}
|
||||
|
||||
export interface SimpleResponse {
|
||||
message: string;
|
||||
export interface TokenPair {
|
||||
access_token: string;
|
||||
refresh_token: string;
|
||||
}
|
||||
|
||||
export interface ForgotPasswordRequest {
|
||||
email: string;
|
||||
}
|
||||
|
||||
export interface LoginRequest {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface ResetPasswordRequest {
|
||||
token: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface ListUsersRequest {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
}
|
||||
|
||||
export interface ListUsersResponse {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
items: UserShort[];
|
||||
}
|
||||
//
|
||||
// package controllers
|
||||
//
|
||||
|
||||
export interface BlockUserRequest {
|
||||
action: string;
|
||||
}
|
||||
|
||||
//
|
||||
// package models
|
||||
//
|
||||
|
||||
export interface UserPreferencesShort {
|
||||
useIdle: boolean;
|
||||
idleTimeout: number;
|
||||
useIdlePassword: boolean;
|
||||
idlePin: string;
|
||||
useDirectLogin: boolean;
|
||||
useQuadcodeLogin: boolean;
|
||||
sendNoticesMail: boolean;
|
||||
language: string;
|
||||
export interface ListUsersRequest {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
}
|
||||
|
||||
export interface UserPreferences {
|
||||
id: number;
|
||||
userId: number;
|
||||
useIdle: boolean;
|
||||
idleTimeout: number;
|
||||
useIdlePassword: boolean;
|
||||
idlePin: string;
|
||||
useDirectLogin: boolean;
|
||||
useQuadcodeLogin: boolean;
|
||||
sendNoticesMail: boolean;
|
||||
language: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface UserShort {
|
||||
email: string;
|
||||
name: string;
|
||||
roles: UserRoles;
|
||||
status: UserStatus;
|
||||
uuid: string;
|
||||
details: Nullable<UserDetailsShort>;
|
||||
preferences: Nullable<UserPreferencesShort>;
|
||||
avatar: Nullable<string>;
|
||||
}
|
||||
|
||||
export interface UserProfile {
|
||||
id: number;
|
||||
email: string;
|
||||
name: string;
|
||||
roles: UserRoles;
|
||||
types: UserTypes;
|
||||
status: UserStatus;
|
||||
activatedAt: Nullable<string>;
|
||||
uuid: string;
|
||||
details: Nullable<UserDetails>;
|
||||
preferences: Nullable<UserPreferences>;
|
||||
avatar: Nullable<string>;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface UserCreateInput {
|
||||
name: string;
|
||||
email: string;
|
||||
password: string;
|
||||
roles: UserRoles;
|
||||
status: UserStatus;
|
||||
types: UserTypes;
|
||||
avatar: Nullable<string>;
|
||||
details: Nullable<UserDetailsShort>;
|
||||
preferences: Nullable<UserPreferencesShort>;
|
||||
export interface SimpleResponse {
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface UpdateUserRequest {
|
||||
name: string;
|
||||
email: string;
|
||||
password: string;
|
||||
roles: UserRoles;
|
||||
status: UserStatus;
|
||||
types: UserTypes;
|
||||
roles: models.UserRoles;
|
||||
status: models.UserStatus;
|
||||
types: models.UserTypes;
|
||||
avatar: Nullable<string>;
|
||||
details: Nullable<UserDetailsShort>;
|
||||
preferences: Nullable<UserPreferencesShort>;
|
||||
details: Nullable<models.UserDetailsShort>;
|
||||
preferences: Nullable<models.UserPreferencesShort>;
|
||||
}
|
||||
|
||||
export interface UserDetails {
|
||||
id: number;
|
||||
userId: number;
|
||||
title: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
address: string;
|
||||
city: string;
|
||||
zipCode: string;
|
||||
country: string;
|
||||
phone: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface UserDetailsShort {
|
||||
title: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
address: string;
|
||||
city: string;
|
||||
zipCode: string;
|
||||
country: string;
|
||||
phone: string;
|
||||
}
|
||||
|
||||
export type UserRoles = string[];
|
||||
|
||||
export type UserStatus = (typeof EnumUserStatus)[keyof typeof EnumUserStatus];
|
||||
|
||||
export type UserTypes = string[];
|
||||
|
||||
export type UsersShort = UserShort[];
|
||||
|
||||
export const EnumUserStatus = {
|
||||
UserStatusPending: "pending",
|
||||
UserStatusActive: "active",
|
||||
UserStatusDisabled: "disabled",
|
||||
} as const;
|
||||
|
||||
//
|
||||
// package routes
|
||||
//
|
||||
|
||||
// Typescript: TSEndpoint= path=/auth/password/valid; name=validToken; method=POST; request=string; response=controllers.SimpleResponse
|
||||
// internal/http/routes/auth_routes.go Line: 40
|
||||
export const validToken = async (
|
||||
data: string,
|
||||
): Promise<{ data: SimpleResponse; error: Nullable<string> }> => {
|
||||
return (await api.POST("/auth/password/valid", data)) as {
|
||||
data: SimpleResponse;
|
||||
// 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<string>;
|
||||
}> => {
|
||||
return (await api.GET("/metrics")) as {
|
||||
data: string;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
|
@ -456,89 +364,72 @@ export const mailDebug = async (): Promise<{
|
|||
};
|
||||
};
|
||||
|
||||
// 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<string> }> => {
|
||||
return (await api.GET(`/users/${uuid}`)) as {
|
||||
data: UserProfile;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
// 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<string> }> => {
|
||||
return (await api.PUT("/users/:uuid", data)) as {
|
||||
data: UserProfile;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
// 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: ListUsersResponse; error: Nullable<string> }> => {
|
||||
): Promise<{ data: UserShort[]; error: Nullable<string> }> => {
|
||||
return (await api.POST("/admin/users", data)) as {
|
||||
data: ListUsersResponse;
|
||||
data: UserShort[];
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
// 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 (
|
||||
uuid: string,
|
||||
data: BlockUserRequest,
|
||||
): Promise<{ data: UserShort; error: Nullable<string> }> => {
|
||||
return (await api.PUT(`/admin/users/${uuid}/block`, data)) as {
|
||||
return (await api.PUT("/admin/users/:uuid/block", data)) as {
|
||||
data: UserShort;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
// Typescript: TSEndpoint= path=/auth/register; name=register; method=POST; request=models.UserCreateInput; response=models.UserShort
|
||||
// internal/http/routes/auth_routes.go Line: 31
|
||||
export const register = async (
|
||||
// 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: UserShort; error: Nullable<string> }> => {
|
||||
return (await api.POST("/auth/register", data)) as {
|
||||
data: UserShort;
|
||||
): Promise<{ data: UserProfile; error: Nullable<string> }> => {
|
||||
return (await api.POST("/users", data)) as {
|
||||
data: UserProfile;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
// 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<string>;
|
||||
}> => {
|
||||
return (await api.GET("/metrics")) as {
|
||||
data: string;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
// Typescript: TSEndpoint= path=/users/:uuid; name=deleteUser; method=DELETE; response=controllers.SimpleResponse
|
||||
// internal/http/routes/user_routes.go Line: 22
|
||||
|
||||
// Typescript: TSEndpoint= path=/auth/login; name=login; method=POST; request=controllers.LoginRequest; response=auth.TokenPair
|
||||
// internal/http/routes/auth_routes.go Line: 22
|
||||
export const login = async (
|
||||
data: LoginRequest,
|
||||
): Promise<{ data: TokenPair; error: Nullable<string> }> => {
|
||||
return (await api.POST("/auth/login", data)) as {
|
||||
data: TokenPair;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
// Typescript: TSEndpoint= path=/auth/refresh; name=refresh; method=POST; request=controllers.RefreshRequest; response=auth.TokenPair
|
||||
// internal/http/routes/auth_routes.go Line: 25
|
||||
export const refresh = async (
|
||||
data: RefreshRequest,
|
||||
): Promise<{ data: TokenPair; error: Nullable<string> }> => {
|
||||
return (await api.POST("/auth/refresh", data)) as {
|
||||
data: TokenPair;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
// Typescript: TSEndpoint= path=/auth/password/forgot; name=forgotPassword; method=POST; request=controllers.ForgotPasswordRequest; response=controllers.SimpleResponse
|
||||
// internal/http/routes/auth_routes.go Line: 34
|
||||
export const forgotPassword = async (
|
||||
data: ForgotPasswordRequest,
|
||||
export const deleteUser = async (
|
||||
uuid: string,
|
||||
): Promise<{ data: SimpleResponse; error: Nullable<string> }> => {
|
||||
return (await api.POST("/auth/password/forgot", data)) as {
|
||||
data: SimpleResponse;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
// Typescript: TSEndpoint= path=/auth/password/reset; name=resetPassword; method=POST; request=controllers.ResetPasswordRequest; response=controllers.SimpleResponse
|
||||
// internal/http/routes/auth_routes.go Line: 37
|
||||
export const resetPassword = async (
|
||||
data: ResetPasswordRequest,
|
||||
): Promise<{ data: SimpleResponse; error: Nullable<string> }> => {
|
||||
return (await api.POST("/auth/password/reset", data)) as {
|
||||
return (await api.DELETE(`/users/${uuid}`)) as {
|
||||
data: SimpleResponse;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
|
|
@ -556,65 +447,6 @@ export const health = async (): Promise<{
|
|||
};
|
||||
};
|
||||
|
||||
// Typescript: TSEndpoint= path=/auth/me; name=me; method=GET; response=models.UserShort
|
||||
// internal/http/routes/auth_routes.go Line: 28
|
||||
export const me = async (): Promise<{
|
||||
data: UserShort;
|
||||
error: Nullable<string>;
|
||||
}> => {
|
||||
return (await api.GET("/auth/me")) as {
|
||||
data: UserShort;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
export const listUsersCrud = async (): Promise<{
|
||||
data: UserProfile[];
|
||||
error: Nullable<string>;
|
||||
}> => {
|
||||
return (await api.GET("/users")) as {
|
||||
data: UserProfile[];
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
export const getUser = async (
|
||||
uuid: string,
|
||||
): Promise<{ data: UserProfile; error: Nullable<string> }> => {
|
||||
return (await api.GET(`/users/${uuid}`)) as {
|
||||
data: UserProfile;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
export const createUser = async (
|
||||
data: UserCreateInput,
|
||||
): Promise<{ data: UserProfile; error: Nullable<string> }> => {
|
||||
return (await api.POST("/users", data)) as {
|
||||
data: UserProfile;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
export const updateUser = async (
|
||||
uuid: string,
|
||||
data: UpdateUserRequest,
|
||||
): Promise<{ data: UserProfile; error: Nullable<string> }> => {
|
||||
return (await api.PUT(`/users/${uuid}`, data)) as {
|
||||
data: UserProfile;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
export const deleteUser = async (
|
||||
uuid: string,
|
||||
): Promise<{ data: SimpleResponse; error: Nullable<string> }> => {
|
||||
return (await api.DELETE(`/users/${uuid}`)) as {
|
||||
data: SimpleResponse;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
export interface FormRequest {
|
||||
req: string;
|
||||
count: number;
|
||||
|
|
@ -630,10 +462,152 @@ export interface MailDebugItem {
|
|||
}
|
||||
|
||||
//
|
||||
// package auth
|
||||
// package endpoint
|
||||
//
|
||||
|
||||
export interface TokenPair {
|
||||
access_token: string;
|
||||
refresh_token: string;
|
||||
// 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<string> }> => {
|
||||
return (await api.POST("/auth/password/forgot", data)) as {
|
||||
data: SimpleResponse;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
// 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<string> }> => {
|
||||
return (await api.POST("/auth/password/reset", data)) as {
|
||||
data: SimpleResponse;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
// 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<string> }> => {
|
||||
return (await api.POST("/auth/password/valid", data)) as {
|
||||
data: SimpleResponse;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
// 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<string> }> => {
|
||||
return (await api.POST("/auth/login", data)) as {
|
||||
data: TokenPair;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
// 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<string> }> => {
|
||||
return (await api.POST("/auth/refresh", data)) as {
|
||||
data: TokenPair;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
// 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<string>;
|
||||
}> => {
|
||||
return (await api.GET("/auth/me")) as {
|
||||
data: UserShort;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
// 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<string> }> => {
|
||||
return (await api.POST("/auth/register", data)) as {
|
||||
data: UserShort;
|
||||
error: Nullable<string>;
|
||||
};
|
||||
};
|
||||
|
||||
//
|
||||
// package models
|
||||
//
|
||||
|
||||
export interface UserCreateInput {
|
||||
name: string;
|
||||
email: string;
|
||||
password: string;
|
||||
roles: UserRoles;
|
||||
status: UserStatus;
|
||||
types: UserTypes;
|
||||
avatar: Nullable<string>;
|
||||
details: Nullable<UserDetailsShort>;
|
||||
preferences: Nullable<UserPreferencesShort>;
|
||||
}
|
||||
|
||||
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;
|
||||
useIdlePassword: boolean;
|
||||
idlePin: string;
|
||||
useDirectLogin: boolean;
|
||||
useQuadcodeLogin: boolean;
|
||||
sendNoticesMail: boolean;
|
||||
language: string;
|
||||
}
|
||||
|
||||
export interface UserShort {
|
||||
email: string;
|
||||
name: string;
|
||||
roles: UserRoles;
|
||||
status: UserStatus;
|
||||
uuid: string;
|
||||
details: Nullable<UserDetailsShort>;
|
||||
preferences: Nullable<UserPreferencesShort>;
|
||||
avatar: Nullable<string>;
|
||||
}
|
||||
|
||||
export type UserRoles = string[];
|
||||
|
||||
export type UserStatus = (typeof EnumUserStatus)[keyof typeof EnumUserStatus];
|
||||
|
||||
export type UserTypes = string[];
|
||||
|
||||
export type UsersShort = UserShort[];
|
||||
|
||||
export const EnumUserStatus = {
|
||||
UserStatusPending: "pending",
|
||||
UserStatusActive: "active",
|
||||
UserStatusDisabled: "disabled",
|
||||
} as const;
|
||||
|
|
|
|||
Loading…
Reference in New Issue