From 17aafacbfd6e4c665c2bfb70309338643d5ee53b Mon Sep 17 00:00:00 2001 From: AzenKain Date: Mon, 27 Apr 2026 20:31:01 +0700 Subject: [PATCH] UPDATE: Fix bug --- db/query/users.sql | 8 + docs/docs.go | 80 ++++++++ docs/swagger.json | 80 ++++++++ docs/swagger.yaml | 54 ++++++ internal/controllers/authController.go | 36 ++-- internal/controllers/commitController.go | 12 +- internal/controllers/entityController.go | 8 +- internal/controllers/geometryController.go | 8 +- internal/controllers/mediaController.go | 40 ++-- internal/controllers/projectController.go | 36 ++-- internal/controllers/rasterTileController.go | 20 +- internal/controllers/roleController.go | 8 +- internal/controllers/submissionController.go | 22 +-- internal/controllers/tileController.go | 20 +- internal/controllers/userController.go | 94 +++++++--- .../controllers/verificationController.go | 20 +- internal/controllers/wikiController.go | 8 +- internal/dtos/request/user.go | 9 +- internal/dtos/response/user.go | 2 + internal/gen/sqlc/users.sql.go | 24 +++ internal/models/user.go | 2 + internal/repositories/userRepository.go | 16 ++ internal/routes/userRoute.go | 7 + internal/services/authService.go | 156 ++++++++-------- internal/services/commitService.go | 25 +-- internal/services/entityService.go | 12 +- internal/services/geometryService.go | 18 +- internal/services/mediaService.go | 72 ++++---- internal/services/projectService.go | 62 +++---- internal/services/rasterTileService.go | 12 +- internal/services/roleService.go | 12 +- internal/services/submissionService.go | 26 ++- internal/services/tileService.go | 12 +- internal/services/userService.go | 174 +++++++++++++----- internal/services/verificationService.go | 82 ++++----- internal/services/wikiService.go | 12 +- 36 files changed, 842 insertions(+), 447 deletions(-) diff --git a/db/query/users.sql b/db/query/users.sql index 183414a..261442a 100644 --- a/db/query/users.sql +++ b/db/query/users.sql @@ -72,6 +72,8 @@ SELECT u.password_hash, u.token_version, u.refresh_token, + u.auth_provider, + u.google_id, u.is_deleted, u.created_at, u.updated_at, @@ -113,6 +115,8 @@ SELECT u.password_hash, u.token_version, u.refresh_token, + u.auth_provider, + u.google_id, u.is_deleted, u.created_at, u.updated_at, @@ -163,6 +167,8 @@ SELECT u.email, u.password_hash, u.token_version, + u.auth_provider, + u.google_id, u.is_deleted, u.created_at, u.updated_at, @@ -311,6 +317,8 @@ SELECT u.email, u.password_hash, u.token_version, + u.auth_provider, + u.google_id, u.is_deleted, u.created_at, u.updated_at, diff --git a/docs/docs.go b/docs/docs.go index 801acab..f081d3e 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -2763,6 +2763,55 @@ const docTemplate = `{ } } } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a new user account with specified roles", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Create a new user (Admin only)", + "parameters": [ + { + "description": "Create User request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/history-api_internal_dtos_request.CreateUserDto" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/history-api_internal_dtos_response.CommonResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/history-api_internal_dtos_response.CommonResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/history-api_internal_dtos_response.CommonResponse" + } + } + } } }, "/users/current": { @@ -3580,6 +3629,37 @@ const docTemplate = `{ } } }, + "history-api_internal_dtos_request.CreateUserDto": { + "type": "object", + "required": [ + "display_name", + "email", + "password", + "role_ids" + ], + "properties": { + "display_name": { + "type": "string", + "maxLength": 50, + "minLength": 2 + }, + "email": { + "type": "string" + }, + "password": { + "type": "string", + "maxLength": 64, + "minLength": 8 + }, + "role_ids": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + } + } + }, "history-api_internal_dtos_request.CreateUserVerificationDto": { "type": "object", "required": [ diff --git a/docs/swagger.json b/docs/swagger.json index 253e880..66d09ff 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -2756,6 +2756,55 @@ } } } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a new user account with specified roles", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Create a new user (Admin only)", + "parameters": [ + { + "description": "Create User request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/history-api_internal_dtos_request.CreateUserDto" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/history-api_internal_dtos_response.CommonResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/history-api_internal_dtos_response.CommonResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/history-api_internal_dtos_response.CommonResponse" + } + } + } } }, "/users/current": { @@ -3573,6 +3622,37 @@ } } }, + "history-api_internal_dtos_request.CreateUserDto": { + "type": "object", + "required": [ + "display_name", + "email", + "password", + "role_ids" + ], + "properties": { + "display_name": { + "type": "string", + "maxLength": 50, + "minLength": 2 + }, + "email": { + "type": "string" + }, + "password": { + "type": "string", + "maxLength": 64, + "minLength": 8 + }, + "role_ids": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + } + } + }, "history-api_internal_dtos_request.CreateUserVerificationDto": { "type": "object", "required": [ diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 6218bf4..e13c4b8 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -100,6 +100,29 @@ definitions: - email - token_type type: object + history-api_internal_dtos_request.CreateUserDto: + properties: + display_name: + maxLength: 50 + minLength: 2 + type: string + email: + type: string + password: + maxLength: 64 + minLength: 8 + type: string + role_ids: + items: + type: string + minItems: 1 + type: array + required: + - display_name + - email + - password + - role_ids + type: object history-api_internal_dtos_request.CreateUserVerificationDto: properties: content: @@ -2129,6 +2152,37 @@ paths: summary: Search users tags: - Users + post: + consumes: + - application/json + description: Create a new user account with specified roles + parameters: + - description: Create User request + in: body + name: request + required: true + schema: + $ref: '#/definitions/history-api_internal_dtos_request.CreateUserDto' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/history-api_internal_dtos_response.CommonResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/history-api_internal_dtos_response.CommonResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/history-api_internal_dtos_response.CommonResponse' + security: + - BearerAuth: [] + summary: Create a new user (Admin only) + tags: + - Users /users/{id}: delete: consumes: diff --git a/internal/controllers/authController.go b/internal/controllers/authController.go index e3c6640..bfad853 100644 --- a/internal/controllers/authController.go +++ b/internal/controllers/authController.go @@ -54,9 +54,9 @@ func (h *AuthController) Signin(c fiber.Ctx) error { res, err := h.service.Signin(ctx, dto) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } c.Cookie(&fiber.Cookie{ @@ -108,9 +108,9 @@ func (h *AuthController) Signup(c fiber.Ctx) error { res, err := h.service.Signup(ctx, dto) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } @@ -172,9 +172,9 @@ func (h *AuthController) RefreshToken(c fiber.Ctx) error { res, err := h.service.RefreshToken(ctx, c.Locals("uid").(string), tokenJwt) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } @@ -227,9 +227,9 @@ func (h *AuthController) VerifyToken(c fiber.Ctx) error { res, err := h.service.VerifyToken(ctx, dto) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } @@ -264,9 +264,9 @@ func (h *AuthController) CreateToken(c fiber.Ctx) error { err := h.service.CreateToken(ctx, dto) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } @@ -301,9 +301,9 @@ func (h *AuthController) ForgotPassword(c fiber.Ctx) error { err := h.service.ForgotPassword(ctx, dto) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } @@ -411,11 +411,11 @@ func (h *AuthController) GoogleCallback(c fiber.Ctx) error { Picture: payload.Claims["picture"].(string), } - res, err := h.service.SigninWithGoogle(ctx, &googleUser) - if err != nil { - return c.Status(500).JSON(response.CommonResponse{ + res, err2 := h.service.SigninWithGoogle(ctx, &googleUser) + if err2 != nil { + return c.Status(err2.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err2.Message, }) } @@ -468,9 +468,9 @@ func (h *AuthController) Logout(c fiber.Ctx) error { err := h.service.Logout(ctx, userId) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } diff --git a/internal/controllers/commitController.go b/internal/controllers/commitController.go index b4c03ed..af5756e 100644 --- a/internal/controllers/commitController.go +++ b/internal/controllers/commitController.go @@ -51,9 +51,9 @@ func (h *CommitController) CreateCommit(c fiber.Ctx) error { uid := c.Locals("uid").(string) res, err := h.service.CreateCommit(ctx, uid, projectID, dto) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } @@ -94,9 +94,9 @@ func (h *CommitController) RestoreCommit(c fiber.Ctx) error { uid := c.Locals("uid").(string) err := h.service.RestoreCommit(ctx, uid, projectID, dto) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } @@ -125,9 +125,9 @@ func (h *CommitController) GetProjectCommits(c fiber.Ctx) error { projectID := c.Params("id") res, err := h.service.GetProjectCommits(ctx, projectID) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } diff --git a/internal/controllers/entityController.go b/internal/controllers/entityController.go index 9fb08dc..9a14c86 100644 --- a/internal/controllers/entityController.go +++ b/internal/controllers/entityController.go @@ -35,9 +35,9 @@ func (h *EntityController) GetEntityById(c fiber.Ctx) error { id := c.Params("id") res, err := h.service.GetEntityByID(ctx, id) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } return c.Status(fiber.StatusOK).JSON(response.CommonResponse{ @@ -70,9 +70,9 @@ func (h *EntityController) SearchEntities(c fiber.Ctx) error { res, err := h.service.SearchEntities(ctx, dto) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } diff --git a/internal/controllers/geometryController.go b/internal/controllers/geometryController.go index 981e0ef..4647a8d 100644 --- a/internal/controllers/geometryController.go +++ b/internal/controllers/geometryController.go @@ -35,9 +35,9 @@ func (h *GeometryController) GetGeometryById(c fiber.Ctx) error { id := c.Params("id") res, err := h.service.GetGeometryByID(ctx, id) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } return c.Status(fiber.StatusOK).JSON(response.CommonResponse{ @@ -70,9 +70,9 @@ func (h *GeometryController) SearchGeometries(c fiber.Ctx) error { res, err := h.service.SearchGeometries(ctx, dto) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } diff --git a/internal/controllers/mediaController.go b/internal/controllers/mediaController.go index 0d5d0b7..3e75c77 100644 --- a/internal/controllers/mediaController.go +++ b/internal/controllers/mediaController.go @@ -36,9 +36,9 @@ func (m *MediaController) GetMediaByID(c fiber.Ctx) error { mediaId := c.Params("id") res, err := m.service.GetMediaByID(ctx, mediaId) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } return c.Status(fiber.StatusOK).JSON(response.CommonResponse{ @@ -66,15 +66,15 @@ func (m *MediaController) SearchMedia(c fiber.Ctx) error { dto := &request.SearchMediaDto{} if err := validator.ValidateQueryDto(c, dto); err != nil { return c.Status(fiber.StatusBadRequest).JSON(response.CommonResponse{ - Status: false, + Status: false, Errors: err, }) } res, err := m.service.SearchMedia(ctx, dto) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } return c.Status(fiber.StatusOK).JSON(res) @@ -113,9 +113,9 @@ func (m *MediaController) DeleteMedia(c fiber.Ctx) error { mediaId := c.Params("id") err := m.service.DeleteMedia(ctx, claims, mediaId) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } return c.Status(fiber.StatusOK).JSON(response.CommonResponse{ @@ -157,16 +157,16 @@ func (m *MediaController) BulkDeleteMedia(c fiber.Ctx) error { dto := &request.MediaBulkDeleteDto{} if err := validator.ValidateBodyDto(c, dto); err != nil { return c.Status(fiber.StatusBadRequest).JSON(response.CommonResponse{ - Status: false, + Status: false, Errors: err, }) } err := m.service.BulkDeleteMedia(ctx, claims, dto) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } return c.Status(fiber.StatusOK).JSON(response.CommonResponse{ @@ -198,11 +198,11 @@ func (m *MediaController) UploadServerSide(c fiber.Ctx) error { }) } - url, err := m.service.UploadServerSide(ctx, c.Locals("uid").(string), fileHeader) - if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + url, err2 := m.service.UploadServerSide(ctx, c.Locals("uid").(string), fileHeader) + if err2 != nil { + return c.Status(err2.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err2.Message, }) } @@ -232,15 +232,15 @@ func (m *MediaController) GeneratePresignedURL(c fiber.Ctx) error { dto := &request.PreSignedDto{} if err := validator.ValidateQueryDto(c, dto); err != nil { return c.Status(fiber.StatusBadRequest).JSON(response.CommonResponse{ - Status: false, + Status: false, Errors: err, }) } res, err := m.service.GeneratePresignedURL(ctx, c.Locals("uid").(string), dto) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } return c.Status(fiber.StatusOK).JSON(res) @@ -265,15 +265,15 @@ func (m *MediaController) PreSignedCompleted(c fiber.Ctx) error { dto := &request.PreSignedCompleteDto{} if err := validator.ValidateBodyDto(c, dto); err != nil { return c.Status(fiber.StatusBadRequest).JSON(response.CommonResponse{ - Status: false, + Status: false, Errors: err, }) } res, err := m.service.PreSignedCompleted(ctx, c.Locals("uid").(string), dto) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } return c.Status(fiber.StatusOK).JSON(res) diff --git a/internal/controllers/projectController.go b/internal/controllers/projectController.go index 8b91fdc..0f679b2 100644 --- a/internal/controllers/projectController.go +++ b/internal/controllers/projectController.go @@ -42,9 +42,9 @@ func (h *ProjectController) GetProjectByID(c fiber.Ctx) error { projectID := c.Params("id") res, err := h.service.GetProjectByID(ctx, projectID) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } @@ -80,9 +80,9 @@ func (h *ProjectController) SearchProject(c fiber.Ctx) error { res, err := h.service.SearchProject(ctx, dto) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } @@ -116,9 +116,9 @@ func (h *ProjectController) CreateProject(c fiber.Ctx) error { uid := c.Locals("uid").(string) res, err := h.service.CreateProject(ctx, uid, dto) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } @@ -157,9 +157,9 @@ func (h *ProjectController) UpdateProject(c fiber.Ctx) error { res, err := h.service.UpdateProject(ctx, projectID, dto) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } @@ -189,9 +189,9 @@ func (h *ProjectController) DeleteProject(c fiber.Ctx) error { projectID := c.Params("id") err := h.service.DeleteProject(ctx, projectID) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } @@ -232,9 +232,9 @@ func (h *ProjectController) AddMember(c fiber.Ctx) error { uid := c.Locals("uid").(string) res, err := h.service.AddMember(ctx, uid, projectID, dto) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } @@ -277,9 +277,9 @@ func (h *ProjectController) UpdateMemberRole(c fiber.Ctx) error { uid := c.Locals("uid").(string) res, err := h.service.UpdateMemberRole(ctx, uid, projectID, memberUserID, dto) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } @@ -314,9 +314,9 @@ func (h *ProjectController) RemoveMember(c fiber.Ctx) error { err := h.service.RemoveMember(ctx, uid, projectID, memberUserID) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } @@ -356,9 +356,9 @@ func (h *ProjectController) ChangeOwner(c fiber.Ctx) error { uid := c.Locals("uid").(string) res, err := h.service.ChangeOwner(ctx, uid, projectID, dto.NewOwnerID) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } diff --git a/internal/controllers/rasterTileController.go b/internal/controllers/rasterTileController.go index 7a5f3bb..dc0c37f 100644 --- a/internal/controllers/rasterTileController.go +++ b/internal/controllers/rasterTileController.go @@ -34,9 +34,9 @@ func (h *RasterTileController) GetMetadata(c fiber.Ctx) error { res, err := h.service.GetMetadata(ctx) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } @@ -62,19 +62,19 @@ func (h *RasterTileController) GetTile(c fiber.Ctx) error { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - z, x, y, err := h.parseTileParams(c) - if err != nil { + z, x, y, pErr := h.parseTileParams(c) + if pErr != nil { return c.Status(fiber.StatusBadRequest).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: pErr.Error(), }) } data, headers, err := h.service.GetTile(ctx, z, x, y) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } @@ -88,17 +88,17 @@ func (h *RasterTileController) GetTile(c fiber.Ctx) error { func (h *RasterTileController) parseTileParams(c fiber.Ctx) (int, int, int, error) { z, err := strconv.Atoi(c.Params("z")) if err != nil { - return 0, 0, 0, fmt.Errorf("invalid z") + return 0, 0, 0, fmt.Errorf("invalid z coordinate") } x, err := strconv.Atoi(c.Params("x")) if err != nil { - return 0, 0, 0, fmt.Errorf("invalid x") + return 0, 0, 0, fmt.Errorf("invalid x coordinate") } y, err := strconv.Atoi(c.Params("y")) if err != nil { - return 0, 0, 0, fmt.Errorf("invalid y") + return 0, 0, 0, fmt.Errorf("invalid y coordinate") } if z < 0 || x < 0 || y < 0 { diff --git a/internal/controllers/roleController.go b/internal/controllers/roleController.go index 6f2961f..655eb80 100644 --- a/internal/controllers/roleController.go +++ b/internal/controllers/roleController.go @@ -34,9 +34,9 @@ func (h *RoleController) GetRoleById(c fiber.Ctx) error { RoleId := c.Params("id") res, err := h.service.GetRoleByID(ctx, RoleId) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } return c.Status(fiber.StatusOK).JSON(response.CommonResponse{ @@ -61,9 +61,9 @@ func (h *RoleController) GetAllRole(c fiber.Ctx) error { defer cancel() res, err := h.service.GetAllRole(ctx) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } return c.Status(fiber.StatusOK).JSON(response.CommonResponse{ diff --git a/internal/controllers/submissionController.go b/internal/controllers/submissionController.go index 23f7efd..1ab20b5 100644 --- a/internal/controllers/submissionController.go +++ b/internal/controllers/submissionController.go @@ -53,9 +53,9 @@ func (s *submissionController) CreateSubmission(c fiber.Ctx) error { res, err := s.submissionService.CreateSubmission(ctx, c.Locals("uid").(string), dto) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } @@ -92,9 +92,9 @@ func (s *submissionController) UpdateSubmissionStatus(c fiber.Ctx) error { res, err := s.submissionService.UpdateSubmissionStatus(ctx, uid, id, dto) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } @@ -119,9 +119,9 @@ func (s *submissionController) GetSubmissionByID(c fiber.Ctx) error { id := c.Params("id") res, err := s.submissionService.GetSubmissionByID(ctx, id) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } @@ -155,9 +155,9 @@ func (s *submissionController) SearchSubmissions(c fiber.Ctx) error { res, err := s.submissionService.SearchSubmissions(ctx, dto) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } @@ -200,14 +200,14 @@ func (s *submissionController) DeleteSubmission(c fiber.Ctx) error { err := s.submissionService.DeleteSubmission(ctx, c.Locals("uid").(string), id, claims) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } return c.Status(fiber.StatusOK).JSON(response.CommonResponse{ - Status: true, + Status: true, Message: "Submission deleted successfully", }) } diff --git a/internal/controllers/tileController.go b/internal/controllers/tileController.go index e3f6af5..622bd88 100644 --- a/internal/controllers/tileController.go +++ b/internal/controllers/tileController.go @@ -34,9 +34,9 @@ func (h *TileController) GetMetadata(c fiber.Ctx) error { res, err := h.service.GetMetadata(ctx) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } @@ -62,19 +62,19 @@ func (h *TileController) GetTile(c fiber.Ctx) error { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - z, x, y, err := h.parseTileParams(c) - if err != nil { + z, x, y, pErr := h.parseTileParams(c) + if pErr != nil { return c.Status(fiber.StatusBadRequest).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: pErr.Error(), }) } data, headers, err := h.service.GetTile(ctx, z, x, y) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } @@ -88,17 +88,17 @@ func (h *TileController) GetTile(c fiber.Ctx) error { func (h *TileController) parseTileParams(c fiber.Ctx) (int, int, int, error) { z, err := strconv.Atoi(c.Params("z")) if err != nil { - return 0, 0, 0, fmt.Errorf("invalid z") + return 0, 0, 0, fmt.Errorf("invalid z coordinate") } x, err := strconv.Atoi(c.Params("x")) if err != nil { - return 0, 0, 0, fmt.Errorf("invalid x") + return 0, 0, 0, fmt.Errorf("invalid x coordinate") } y, err := strconv.Atoi(c.Params("y")) if err != nil { - return 0, 0, 0, fmt.Errorf("invalid y") + return 0, 0, 0, fmt.Errorf("invalid y coordinate") } if z < 0 || x < 0 || y < 0 { diff --git a/internal/controllers/userController.go b/internal/controllers/userController.go index 20c121e..63b84dc 100644 --- a/internal/controllers/userController.go +++ b/internal/controllers/userController.go @@ -32,6 +32,44 @@ func NewUserController( } } +// CreateUser godoc +// @Summary Create a new user (Admin only) +// @Description Create a new user account with specified roles +// @Tags Users +// @Accept json +// @Produce json +// @Security BearerAuth +// @Param request body request.CreateUserDto true "Create User request" +// @Success 200 {object} response.CommonResponse +// @Failure 400 {object} response.CommonResponse +// @Failure 500 {object} response.CommonResponse +// @Router /users [post] +func (h *UserController) CreateUser(c fiber.Ctx) error { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + dto := &request.CreateUserDto{} + if err := validator.ValidateBodyDto(c, dto); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(response.CommonResponse{ + Status: false, + Errors: err, + }) + } + + res, err := h.service.CreateUser(ctx, dto) + if err != nil { + return c.Status(err.Code).JSON(response.CommonResponse{ + Status: false, + Message: err.Message, + }) + } + + return c.Status(fiber.StatusOK).JSON(response.CommonResponse{ + Status: true, + Data: res, + }) +} + // GetUserCurrent godoc // @Summary Get current user profile // @Description Retrieve the profile information of the currently authenticated user @@ -48,9 +86,9 @@ func (h *UserController) GetUserCurrent(c fiber.Ctx) error { res, err := h.service.GetUserByID(ctx, c.Locals("uid").(string)) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } @@ -76,9 +114,9 @@ func (h *UserController) GetUserMedia(c fiber.Ctx) error { res, err := h.mediaService.GetMediaByUserID(ctx, c.Locals("uid").(string)) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } @@ -104,9 +142,9 @@ func (h *UserController) GetUserApplication(c fiber.Ctx) error { res, err := h.verificationService.GetVerificationByUserID(ctx, c.Locals("uid").(string)) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } @@ -132,9 +170,9 @@ func (h *UserController) GetMediaByUserID(c fiber.Ctx) error { userId := c.Params("id") res, err := h.mediaService.GetMediaByUserID(ctx, userId) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } return c.Status(fiber.StatusOK).JSON(response.CommonResponse{ @@ -159,9 +197,9 @@ func (h *UserController) GetVerificationByUserID(c fiber.Ctx) error { userId := c.Params("id") res, err := h.verificationService.GetVerificationByUserID(ctx, userId) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } return c.Status(fiber.StatusOK).JSON(response.CommonResponse{ @@ -196,9 +234,9 @@ func (h *UserController) UpdateProfile(c fiber.Ctx) error { res, err := h.service.UpdateProfile(ctx, c.Locals("uid").(string), dto) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } return c.Status(fiber.StatusOK).JSON(response.CommonResponse{ @@ -231,9 +269,9 @@ func (h *UserController) ChangePassword(c fiber.Ctx) error { } err := h.service.ChangePassword(ctx, c.Locals("uid").(string), dto) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } return c.Status(fiber.StatusOK).JSON(response.CommonResponse{ @@ -259,9 +297,9 @@ func (h *UserController) RestoreUser(c fiber.Ctx) error { userId := c.Params("id") res, err := h.service.RestoreUser(ctx, userId) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } return c.Status(fiber.StatusOK).JSON(response.CommonResponse{ @@ -287,9 +325,9 @@ func (h *UserController) DeleteUser(c fiber.Ctx) error { userId := c.Params("id") err := h.service.DeleteUser(ctx, userId) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } return c.Status(fiber.StatusOK).JSON(response.CommonResponse{ @@ -340,9 +378,9 @@ func (h *UserController) ChangeRoleUser(c fiber.Ctx) error { userId := c.Params("id") user, err := h.service.ChangeRoleUser(ctx, userId, claims, dto) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } return c.Status(fiber.StatusOK).JSON(response.CommonResponse{ @@ -368,9 +406,9 @@ func (h *UserController) GetUserById(c fiber.Ctx) error { userId := c.Params("id") res, err := h.service.GetUserByID(ctx, userId) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } return c.Status(fiber.StatusOK).JSON(response.CommonResponse{ @@ -404,9 +442,9 @@ func (h *UserController) SearchUser(c fiber.Ctx) error { } res, err := h.service.SearchUser(ctx, dto) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } return c.Status(fiber.StatusOK).JSON(res) @@ -438,9 +476,9 @@ func (h *UserController) GetUserProject(c fiber.Ctx) error { res, err := h.projectService.GetProjectByUserID(ctx, c.Locals("uid").(string), dto) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } @@ -477,9 +515,9 @@ func (h *UserController) GetProjectByUserID(c fiber.Ctx) error { res, err := h.projectService.GetProjectByUserID(ctx, userID, dto) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } diff --git a/internal/controllers/verificationController.go b/internal/controllers/verificationController.go index 246fd95..5c045b2 100644 --- a/internal/controllers/verificationController.go +++ b/internal/controllers/verificationController.go @@ -34,9 +34,9 @@ func (m *VerificationController) GetVerificationByID(c fiber.Ctx) error { verificationId := c.Params("id") res, err := m.service.GetVerificationByID(ctx, verificationId) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } return c.Status(fiber.StatusOK).JSON(response.CommonResponse{ @@ -69,9 +69,9 @@ func (m *VerificationController) SearchVerification(c fiber.Ctx) error { } res, err := m.service.SearchVerification(ctx, dto) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } return c.Status(fiber.StatusOK).JSON(res) @@ -108,9 +108,9 @@ func (m *VerificationController) DeleteVerification(c fiber.Ctx) error { verificationId := c.Params("id") err := m.service.DeleteVerification(ctx, claims, verificationId) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } return c.Status(fiber.StatusOK).JSON(response.CommonResponse{ @@ -143,9 +143,9 @@ func (m *VerificationController) CreateVerification(c fiber.Ctx) error { } res, err := m.service.CreateVerification(ctx, c.Locals("uid").(string), dto) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } return c.Status(fiber.StatusOK).JSON(response.CommonResponse{ @@ -180,9 +180,9 @@ func (m *VerificationController) UpdateVerificationStatus(c fiber.Ctx) error { verificationId := c.Params("id") res, err := m.service.UpdateStatusVerification(ctx, c.Locals("uid").(string), verificationId, dto) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } return c.Status(fiber.StatusOK).JSON(response.CommonResponse{ diff --git a/internal/controllers/wikiController.go b/internal/controllers/wikiController.go index 85e9a9e..0bf109f 100644 --- a/internal/controllers/wikiController.go +++ b/internal/controllers/wikiController.go @@ -35,9 +35,9 @@ func (h *WikiController) GetWikiById(c fiber.Ctx) error { id := c.Params("id") res, err := h.service.GetWikiByID(ctx, id) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } return c.Status(fiber.StatusOK).JSON(response.CommonResponse{ @@ -70,9 +70,9 @@ func (h *WikiController) SearchWikis(c fiber.Ctx) error { res, err := h.service.SearchWikis(ctx, dto) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + return c.Status(err.Code).JSON(response.CommonResponse{ Status: false, - Message: err.Error(), + Message: err.Message, }) } diff --git a/internal/dtos/request/user.go b/internal/dtos/request/user.go index 60f9342..c784498 100644 --- a/internal/dtos/request/user.go +++ b/internal/dtos/request/user.go @@ -19,7 +19,7 @@ type ChangePasswordDto struct { } type ChangeRoleDto struct { - Roles []string `json:"role_ids" validate:"required,min=1,dive,required,uuid"` + Roles []string `json:"role_ids" validate:"required,min=1,dive,required,uuid"` } type PaginationDto struct { @@ -38,3 +38,10 @@ type SearchUserDto struct { CreatedFrom *time.Time `json:"created_from" query:"created_from" validate:"omitempty"` CreatedTo *time.Time `json:"created_to" query:"created_to" validate:"omitempty"` } + +type CreateUserDto struct { + Email string `json:"email" validate:"required,email"` + Password string `json:"password" validate:"required,min=8,max=64"` + DisplayName string `json:"display_name" validate:"required,min=2,max=50"` + Roles []string `json:"role_ids" validate:"required,min=1,dive,required,uuid"` +} diff --git a/internal/dtos/response/user.go b/internal/dtos/response/user.go index 1aff1a0..d9843ea 100644 --- a/internal/dtos/response/user.go +++ b/internal/dtos/response/user.go @@ -8,6 +8,8 @@ type UserResponse struct { Profile *UserProfileSimpleResponse `json:"profile,omitempty"` TokenVersion int32 `json:"token_version,omitempty"` IsDeleted bool `json:"is_deleted,omitempty"` + AuthProvider string `json:"auth_provider,omitempty"` + GoogleID string `json:"google_id,omitempty"` CreatedAt *time.Time `json:"created_at,omitempty"` UpdatedAt *time.Time `json:"updated_at,omitempty"` Roles []*RoleSimpleResponse `json:"roles,omitempty"` diff --git a/internal/gen/sqlc/users.sql.go b/internal/gen/sqlc/users.sql.go index 407eaf8..b628a98 100644 --- a/internal/gen/sqlc/users.sql.go +++ b/internal/gen/sqlc/users.sql.go @@ -133,6 +133,8 @@ SELECT u.email, u.password_hash, u.token_version, + u.auth_provider, + u.google_id, u.is_deleted, u.created_at, u.updated_at, @@ -171,6 +173,8 @@ type GetUserByEmailRow struct { Email string `json:"email"` PasswordHash pgtype.Text `json:"password_hash"` TokenVersion int32 `json:"token_version"` + AuthProvider string `json:"auth_provider"` + GoogleID pgtype.Text `json:"google_id"` IsDeleted bool `json:"is_deleted"` CreatedAt pgtype.Timestamptz `json:"created_at"` UpdatedAt pgtype.Timestamptz `json:"updated_at"` @@ -186,6 +190,8 @@ func (q *Queries) GetUserByEmail(ctx context.Context, email string) (GetUserByEm &i.Email, &i.PasswordHash, &i.TokenVersion, + &i.AuthProvider, + &i.GoogleID, &i.IsDeleted, &i.CreatedAt, &i.UpdatedAt, @@ -202,6 +208,8 @@ SELECT u.password_hash, u.token_version, u.refresh_token, + u.auth_provider, + u.google_id, u.is_deleted, u.created_at, u.updated_at, @@ -243,6 +251,8 @@ type GetUserByIDRow struct { PasswordHash pgtype.Text `json:"password_hash"` TokenVersion int32 `json:"token_version"` RefreshToken pgtype.Text `json:"refresh_token"` + AuthProvider string `json:"auth_provider"` + GoogleID pgtype.Text `json:"google_id"` IsDeleted bool `json:"is_deleted"` CreatedAt pgtype.Timestamptz `json:"created_at"` UpdatedAt pgtype.Timestamptz `json:"updated_at"` @@ -259,6 +269,8 @@ func (q *Queries) GetUserByID(ctx context.Context, id pgtype.UUID) (GetUserByIDR &i.PasswordHash, &i.TokenVersion, &i.RefreshToken, + &i.AuthProvider, + &i.GoogleID, &i.IsDeleted, &i.CreatedAt, &i.UpdatedAt, @@ -275,6 +287,8 @@ SELECT u.password_hash, u.token_version, u.refresh_token, + u.auth_provider, + u.google_id, u.is_deleted, u.created_at, u.updated_at, @@ -316,6 +330,8 @@ type GetUserByIDWithoutDeletedRow struct { PasswordHash pgtype.Text `json:"password_hash"` TokenVersion int32 `json:"token_version"` RefreshToken pgtype.Text `json:"refresh_token"` + AuthProvider string `json:"auth_provider"` + GoogleID pgtype.Text `json:"google_id"` IsDeleted bool `json:"is_deleted"` CreatedAt pgtype.Timestamptz `json:"created_at"` UpdatedAt pgtype.Timestamptz `json:"updated_at"` @@ -332,6 +348,8 @@ func (q *Queries) GetUserByIDWithoutDeleted(ctx context.Context, id pgtype.UUID) &i.PasswordHash, &i.TokenVersion, &i.RefreshToken, + &i.AuthProvider, + &i.GoogleID, &i.IsDeleted, &i.CreatedAt, &i.UpdatedAt, @@ -347,6 +365,8 @@ SELECT u.email, u.password_hash, u.token_version, + u.auth_provider, + u.google_id, u.is_deleted, u.created_at, u.updated_at, @@ -382,6 +402,8 @@ type GetUsersByIDsRow struct { Email string `json:"email"` PasswordHash pgtype.Text `json:"password_hash"` TokenVersion int32 `json:"token_version"` + AuthProvider string `json:"auth_provider"` + GoogleID pgtype.Text `json:"google_id"` IsDeleted bool `json:"is_deleted"` CreatedAt pgtype.Timestamptz `json:"created_at"` UpdatedAt pgtype.Timestamptz `json:"updated_at"` @@ -403,6 +425,8 @@ func (q *Queries) GetUsersByIDs(ctx context.Context, dollar_1 []pgtype.UUID) ([] &i.Email, &i.PasswordHash, &i.TokenVersion, + &i.AuthProvider, + &i.GoogleID, &i.IsDeleted, &i.CreatedAt, &i.UpdatedAt, diff --git a/internal/models/user.go b/internal/models/user.go index 85a3405..4908814 100644 --- a/internal/models/user.go +++ b/internal/models/user.go @@ -68,6 +68,8 @@ func (u *UserEntity) ToResponse() *response.UserResponse { IsDeleted: u.IsDeleted, CreatedAt: u.CreatedAt, UpdatedAt: u.UpdatedAt, + AuthProvider: u.AuthProvider, + GoogleID: u.GoogleID, Roles: RolesToResponse(u.Roles), Profile: u.Profile.ToResponse(), } diff --git a/internal/repositories/userRepository.go b/internal/repositories/userRepository.go index 09f32bb..8d4b850 100644 --- a/internal/repositories/userRepository.go +++ b/internal/repositories/userRepository.go @@ -143,6 +143,8 @@ func (r *userRepository) GetByID(ctx context.Context, id pgtype.UUID) (*models.U Email: row.Email, PasswordHash: convert.TextToString(row.PasswordHash), TokenVersion: row.TokenVersion, + AuthProvider: row.AuthProvider, + GoogleID: convert.TextToString(row.GoogleID), IsDeleted: row.IsDeleted, CreatedAt: convert.TimeToPtr(row.CreatedAt), UpdatedAt: convert.TimeToPtr(row.UpdatedAt), @@ -180,6 +182,8 @@ func (r *userRepository) GetByIDWithoutDeleted(ctx context.Context, id pgtype.UU PasswordHash: convert.TextToString(row.PasswordHash), TokenVersion: row.TokenVersion, IsDeleted: row.IsDeleted, + AuthProvider: row.AuthProvider, + GoogleID: convert.TextToString(row.GoogleID), CreatedAt: convert.TimeToPtr(row.CreatedAt), UpdatedAt: convert.TimeToPtr(row.UpdatedAt), } @@ -217,6 +221,8 @@ func (r *userRepository) GetByEmail(ctx context.Context, email string) (*models. PasswordHash: convert.TextToString(row.PasswordHash), TokenVersion: row.TokenVersion, IsDeleted: row.IsDeleted, + AuthProvider: row.AuthProvider, + GoogleID: convert.TextToString(row.GoogleID), CreatedAt: convert.TimeToPtr(row.CreatedAt), UpdatedAt: convert.TimeToPtr(row.UpdatedAt), } @@ -251,6 +257,8 @@ func (r *userRepository) UpsertUser(ctx context.Context, params sqlc.UpsertUserP PasswordHash: convert.TextToString(row.PasswordHash), TokenVersion: row.TokenVersion, IsDeleted: row.IsDeleted, + AuthProvider: row.AuthProvider, + GoogleID: convert.TextToString(row.GoogleID), CreatedAt: convert.TimeToPtr(row.CreatedAt), UpdatedAt: convert.TimeToPtr(row.UpdatedAt), Roles: make([]*models.RoleSimple, 0), @@ -325,6 +333,8 @@ func (r *userRepository) Search(ctx context.Context, params sqlc.SearchUsersPara PasswordHash: convert.TextToString(row.PasswordHash), TokenVersion: row.TokenVersion, IsDeleted: row.IsDeleted, + AuthProvider: row.AuthProvider, + GoogleID: convert.TextToString(row.GoogleID), CreatedAt: convert.TimeToPtr(row.CreatedAt), UpdatedAt: convert.TimeToPtr(row.UpdatedAt), } @@ -386,6 +396,7 @@ func (r *userRepository) Delete(ctx context.Context, id pgtype.UUID) error { ) go func() { bgCtx := context.Background() + _ = r.c.DelByPattern(bgCtx, "user:search*") _ = r.c.DelByPattern(bgCtx, "user:count*") }() return nil @@ -402,6 +413,11 @@ func (r *userRepository) Restore(ctx context.Context, id pgtype.UUID) error { fmt.Sprintf("user:email:%s", convert.UUIDToString(id)), fmt.Sprintf("user:id:%s", convert.UUIDToString(id)), ) + go func() { + bgCtx := context.Background() + _ = r.c.DelByPattern(bgCtx, "user:search*") + _ = r.c.DelByPattern(bgCtx, "user:count*") + }() return nil } diff --git a/internal/routes/userRoute.go b/internal/routes/userRoute.go index 652c5e6..58dd2d1 100644 --- a/internal/routes/userRoute.go +++ b/internal/routes/userRoute.go @@ -104,4 +104,11 @@ func UserRoutes(app *fiber.App, controller *controllers.UserController, userRepo controller.SearchUser, ) + route.Post( + "/", + middlewares.JwtAccess(userRepo), + middlewares.RequireAnyRole(constants.RoleTypeAdmin, constants.RoleTypeMod), + controller.CreateUser, + ) + } diff --git a/internal/services/authService.go b/internal/services/authService.go index e629603..b2c8cff 100644 --- a/internal/services/authService.go +++ b/internal/services/authService.go @@ -32,14 +32,14 @@ import ( ) type AuthService interface { - Signin(ctx context.Context, dto *request.SignInDto) (*response.AuthResponse, error) - Signup(ctx context.Context, dto *request.SignUpDto) (*response.AuthResponse, error) - Logout(ctx context.Context, userId string) error - ForgotPassword(ctx context.Context, dto *request.ForgotPasswordDto) error - VerifyToken(ctx context.Context, dto *request.VerifyTokenDto) (*response.VerifyTokenResponse, error) - CreateToken(ctx context.Context, dto *request.CreateTokenDto) error - SigninWithGoogle(ctx context.Context, dto *request.SigninWithGoogleDto) (*response.AuthResponse, error) - RefreshToken(ctx context.Context, id string, refreshToken string) (*response.AuthResponse, error) + Signin(ctx context.Context, dto *request.SignInDto) (*response.AuthResponse, *fiber.Error) + Signup(ctx context.Context, dto *request.SignUpDto) (*response.AuthResponse, *fiber.Error) + Logout(ctx context.Context, userId string) *fiber.Error + ForgotPassword(ctx context.Context, dto *request.ForgotPasswordDto) *fiber.Error + VerifyToken(ctx context.Context, dto *request.VerifyTokenDto) (*response.VerifyTokenResponse, *fiber.Error) + CreateToken(ctx context.Context, dto *request.CreateTokenDto) *fiber.Error + SigninWithGoogle(ctx context.Context, dto *request.SigninWithGoogleDto) (*response.AuthResponse, *fiber.Error) + RefreshToken(ctx context.Context, id string, refreshToken string) (*response.AuthResponse, *fiber.Error) } type authService struct { @@ -69,15 +69,15 @@ func NewAuthService( func (a *authService) genToken(user *models.UserEntity) (*response.AuthResponse, error) { jwtSecret, err := config.GetConfig("JWT_SECRET") if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, "missing JWT_SECRET in environment") + return nil, err } jwtRefreshSecret, err := config.GetConfig("JWT_REFRESH_SECRET") if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, "missing JWT_REFRESH_SECRET in environment") + return nil, err } if jwtSecret == "" || jwtRefreshSecret == "" { - return nil, fiber.NewError(fiber.StatusInternalServerError, "missing JWT secrets in environment") + return nil, errors.New("missing JWT secrets in environment") } claimsAccess := &response.JWTClaims{ @@ -117,9 +117,9 @@ func (a *authService) genToken(user *models.UserEntity) (*response.AuthResponse, return &res, nil } -func (a *authService) Signin(ctx context.Context, dto *request.SignInDto) (*response.AuthResponse, error) { +func (a *authService) Signin(ctx context.Context, dto *request.SignInDto) (*response.AuthResponse, *fiber.Error) { if !constants.EMAIL_REGEX.MatchString(dto.Email) { - return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid email") + return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid email format") } err := constants.ValidatePassword(dto.Password) @@ -129,7 +129,7 @@ func (a *authService) Signin(ctx context.Context, dto *request.SignInDto) (*resp user, err := a.userRepo.GetByEmail(ctx, dto.Email) if err != nil || user == nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusUnauthorized, "Invalid email or password") } if user.AuthProvider != constants.ProviderTypeLocal.String() && user.PasswordHash == "" { @@ -137,17 +137,17 @@ func (a *authService) Signin(ctx context.Context, dto *request.SignInDto) (*resp } if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(dto.Password)); err != nil { - return nil, fiber.NewError(fiber.StatusUnauthorized, "Invalid identity or password!") + return nil, fiber.NewError(fiber.StatusUnauthorized, "Invalid email or password") } data, err := a.genToken(user) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to generate security tokens") } pgID, err := convert.StringToUUID(user.ID) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Invalid user ID internal format") } err = a.userRepo.UpdateRefreshToken( ctx, @@ -160,14 +160,14 @@ func (a *authService) Signin(ctx context.Context, dto *request.SignInDto) (*resp }, ) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to update refresh token") } return data, nil } -func (a *authService) Logout(ctx context.Context, userId string) error { +func (a *authService) Logout(ctx context.Context, userId string) *fiber.Error { tx, err := a.db.Begin(ctx) if err != nil { return fiber.NewError(fiber.StatusInternalServerError, "Failed to start transaction") @@ -178,11 +178,11 @@ func (a *authService) Logout(ctx context.Context, userId string) error { pgID, err := convert.StringToUUID(userId) if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return fiber.NewError(fiber.StatusBadRequest, "Invalid user ID format") } user, err := a.userRepo.GetByID(ctx, pgID) if err != nil || user == nil { - return fiber.NewError(fiber.StatusInternalServerError, "Invalid user data") + return fiber.NewError(fiber.StatusNotFound, "User not found") } err = uRepoTx.UpdateTokenVersion(ctx, sqlc.UpdateTokenVersionParams{ @@ -190,7 +190,7 @@ func (a *authService) Logout(ctx context.Context, userId string) error { TokenVersion: user.TokenVersion + 1, }) if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return fiber.NewError(fiber.StatusInternalServerError, "Failed to revoke sessions") } err = uRepoTx.UpdateRefreshToken(ctx, sqlc.UpdateUserRefreshTokenParams{ @@ -201,39 +201,39 @@ func (a *authService) Logout(ctx context.Context, userId string) error { }, }) if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return fiber.NewError(fiber.StatusInternalServerError, "Failed to clear refresh token") } err = tx.Commit(ctx) if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return fiber.NewError(fiber.StatusInternalServerError, "Failed to commit logout") } return nil } -func (a *authService) RefreshToken(ctx context.Context, id string, refreshToken string) (*response.AuthResponse, error) { +func (a *authService) RefreshToken(ctx context.Context, id string, refreshToken string) (*response.AuthResponse, *fiber.Error) { var pgID pgtype.UUID err := pgID.Scan(id) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid user ID format") } user, err := a.userRepo.GetByID(ctx, pgID) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, "Invalid user data") + return nil, fiber.NewError(fiber.StatusNotFound, "User not found") } if user.RefreshToken != refreshToken { - return nil, fiber.NewError(fiber.StatusUnauthorized, "Invalid refresh token") + return nil, fiber.NewError(fiber.StatusUnauthorized, "Invalid or expired refresh token") } roles := models.RolesEntityToRoleConstant(user.Roles) if slices.Contains(roles, constants.RoleTypeBanned) { - return nil, fiber.NewError(fiber.StatusUnauthorized, "User is banned!") + return nil, fiber.NewError(fiber.StatusUnauthorized, "Your account has been banned") } data, err := a.genToken(user) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to generate new tokens") } err = a.userRepo.UpdateRefreshToken( @@ -247,13 +247,13 @@ func (a *authService) RefreshToken(ctx context.Context, id string, refreshToken }, ) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to update refresh token") } return data, nil } -func (a *authService) Signup(ctx context.Context, dto *request.SignUpDto) (*response.AuthResponse, error) { +func (a *authService) Signup(ctx context.Context, dto *request.SignUpDto) (*response.AuthResponse, *fiber.Error) { tx, err := a.db.Begin(ctx) if err != nil { return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to start transaction") @@ -264,7 +264,7 @@ func (a *authService) Signup(ctx context.Context, dto *request.SignUpDto) (*resp rRepoTx := a.roleRepo.WithTx(tx) if !constants.EMAIL_REGEX.MatchString(dto.Email) { - return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid email") + return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid email format") } err = constants.ValidatePassword(dto.Password) if err != nil { @@ -273,24 +273,24 @@ func (a *authService) Signup(ctx context.Context, dto *request.SignUpDto) (*resp ok, err := a.tokenRepo.CheckVerified(ctx, dto.Email, constants.TokenTypeEmailVerify, dto.TokenID) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to verify registration token") } if !ok { - return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid or expired token") + return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid or expired verification token") } user, err := a.userRepo.GetByEmail(ctx, dto.Email) if err != nil && !errors.Is(err, sql.ErrNoRows) { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to check existing user") } if user != nil { - return nil, fiber.NewError(fiber.StatusBadRequest, "User already exists") + return nil, fiber.NewError(fiber.StatusConflict, "Email is already registered") } hashed, err := bcrypt.GenerateFromPassword([]byte(dto.Password), bcrypt.DefaultCost) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to hash password") } user, err = uRepoTx.UpsertUser( @@ -305,12 +305,12 @@ func (a *authService) Signup(ctx context.Context, dto *request.SignUpDto) (*resp }, ) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to create user account") } userId, err := convert.StringToUUID(user.ID) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Invalid user ID internal format") } _, err = uRepoTx.CreateProfile( ctx, @@ -323,16 +323,16 @@ func (a *authService) Signup(ctx context.Context, dto *request.SignUpDto) (*resp }, ) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to create user profile") } role, err := a.roleRepo.GetByName(ctx, constants.RoleTypeUser.String()) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Default role not found") } roleId, err := convert.StringToUUID(role.ID) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Invalid role ID internal format") } err = rRepoTx.CreateUserRole( @@ -343,12 +343,12 @@ func (a *authService) Signup(ctx context.Context, dto *request.SignUpDto) (*resp }, ) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to assign user role") } data, err := a.genToken(user) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to generate security tokens") } err = uRepoTx.UpdateRefreshToken( @@ -362,40 +362,40 @@ func (a *authService) Signup(ctx context.Context, dto *request.SignUpDto) (*resp }, ) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to update refresh token") } err = tx.Commit(ctx) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to commit signup") } return data, nil } -func (a *authService) ForgotPassword(ctx context.Context, dto *request.ForgotPasswordDto) error { +func (a *authService) ForgotPassword(ctx context.Context, dto *request.ForgotPasswordDto) *fiber.Error { ok, err := a.tokenRepo.CheckVerified(ctx, dto.Email, constants.TokenTypePasswordReset, dto.TokenID) if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return fiber.NewError(fiber.StatusInternalServerError, "Failed to verify reset token") } if !ok { - return fiber.NewError(fiber.StatusBadRequest, "Invalid or expired token") + return fiber.NewError(fiber.StatusBadRequest, "Invalid or expired reset token") } user, err := a.userRepo.GetByEmail(ctx, dto.Email) if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return fiber.NewError(fiber.StatusNotFound, "User not found") } if user == nil { - return fiber.NewError(fiber.StatusBadRequest, "User not found") + return fiber.NewError(fiber.StatusNotFound, "User not found") } hashed, err := bcrypt.GenerateFromPassword([]byte(dto.NewPassword), bcrypt.DefaultCost) if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return fiber.NewError(fiber.StatusInternalServerError, "Failed to hash new password") } userId, err := convert.StringToUUID(user.ID) if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return fiber.NewError(fiber.StatusInternalServerError, "Invalid user ID format") } err = a.userRepo.UpdatePassword(ctx, sqlc.UpdateUserPasswordParams{ ID: userId, @@ -405,12 +405,12 @@ func (a *authService) ForgotPassword(ctx context.Context, dto *request.ForgotPas }, }) if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return fiber.NewError(fiber.StatusInternalServerError, "Failed to update password") } return nil } -func (a *authService) SigninWithGoogle(ctx context.Context, dto *request.SigninWithGoogleDto) (*response.AuthResponse, error) { +func (a *authService) SigninWithGoogle(ctx context.Context, dto *request.SigninWithGoogleDto) (*response.AuthResponse, *fiber.Error) { tx, err := a.db.Begin(ctx) if err != nil { return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to start transaction") @@ -422,17 +422,17 @@ func (a *authService) SigninWithGoogle(ctx context.Context, dto *request.SigninW user, err := a.userRepo.GetByEmail(ctx, dto.Email) if err != nil && !errors.Is(err, sql.ErrNoRows) { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch user data") } if user != nil { userId, err := convert.StringToUUID(user.ID) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Invalid user ID format") } data, err := a.genToken(user) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to generate security tokens") } err = uRepoTx.UpdateRefreshToken( ctx, @@ -445,7 +445,7 @@ func (a *authService) SigninWithGoogle(ctx context.Context, dto *request.SigninW }, ) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to update refresh token") } return data, nil } @@ -462,11 +462,11 @@ func (a *authService) SigninWithGoogle(ctx context.Context, dto *request.SigninW }, ) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to create user account") } userId, err := convert.StringToUUID(user.ID) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Invalid user ID format") } _, err = uRepoTx.CreateProfile( ctx, @@ -483,16 +483,16 @@ func (a *authService) SigninWithGoogle(ctx context.Context, dto *request.SigninW }, ) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to create user profile") } role, err := a.roleRepo.GetByName(ctx, constants.RoleTypeUser.String()) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Default role not found") } roleId, err := convert.StringToUUID(role.ID) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Invalid role ID format") } err = rRepoTx.CreateUserRole( @@ -503,12 +503,12 @@ func (a *authService) SigninWithGoogle(ctx context.Context, dto *request.SigninW }, ) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to assign user role") } data, err := a.genToken(user) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to generate security tokens") } err = uRepoTx.UpdateRefreshToken( ctx, @@ -521,11 +521,11 @@ func (a *authService) SigninWithGoogle(ctx context.Context, dto *request.SigninW }, ) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to update refresh token") } err = tx.Commit(ctx) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to commit Google signin") } return data, nil } @@ -540,19 +540,19 @@ func (a *authService) GenerateOTP() (string, error) { return fmt.Sprintf("%06d", otp), nil } -func (a *authService) CreateToken(ctx context.Context, dto *request.CreateTokenDto) error { +func (a *authService) CreateToken(ctx context.Context, dto *request.CreateTokenDto) *fiber.Error { ok, err := a.tokenRepo.CheckCooldown(ctx, dto.Email, dto.TokenType) if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, "Internal Server Error") + return fiber.NewError(fiber.StatusInternalServerError, "Failed to check request cooldown") } if ok { - return fiber.NewError(fiber.StatusBadRequest, "Too many requests. Please try again later.") + return fiber.NewError(fiber.StatusTooManyRequests, "Too many requests. Please try again later.") } user, err := a.userRepo.GetByEmail(ctx, dto.Email) if err != nil && !errors.Is(err, sql.ErrNoRows) { - return fiber.NewError(fiber.StatusInternalServerError, "Internal Server Error") + return fiber.NewError(fiber.StatusInternalServerError, "Failed to check user existence") } shouldSend := true @@ -564,7 +564,7 @@ func (a *authService) CreateToken(ctx context.Context, dto *request.CreateTokenD if shouldSend { otp, err := a.GenerateOTP() if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, "Internal Server Error") + return fiber.NewError(fiber.StatusInternalServerError, "Failed to generate OTP") } hash := sha256.Sum256([]byte(otp)) hashString := hex.EncodeToString(hash[:]) @@ -575,7 +575,7 @@ func (a *authService) CreateToken(ctx context.Context, dto *request.CreateTokenD } err = a.tokenRepo.Create(ctx, token) if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, "Internal Server Error") + return fiber.NewError(fiber.StatusInternalServerError, "Failed to save verification token") } token.Token = otp @@ -585,7 +585,7 @@ func (a *authService) CreateToken(ctx context.Context, dto *request.CreateTokenD return nil } -func (a *authService) VerifyToken(ctx context.Context, dto *request.VerifyTokenDto) (*response.VerifyTokenResponse, error) { +func (a *authService) VerifyToken(ctx context.Context, dto *request.VerifyTokenDto) (*response.VerifyTokenResponse, *fiber.Error) { genericError := fiber.NewError(fiber.StatusBadRequest, "Invalid or expired token") token, err := a.tokenRepo.Get(ctx, dto.Email, dto.TokenType) if err != nil || token == nil { @@ -607,7 +607,7 @@ func (a *authService) VerifyToken(ctx context.Context, dto *request.VerifyTokenD user, err := a.userRepo.GetByEmail(ctx, dto.Email) if err != nil && !errors.Is(err, sql.ErrNoRows) { - return nil, fiber.NewError(fiber.StatusInternalServerError, "Internal Server Error") + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to check user existence") } if (dto.TokenType == constants.TokenTypeEmailVerify && user != nil) || @@ -618,7 +618,7 @@ func (a *authService) VerifyToken(ctx context.Context, dto *request.VerifyTokenD tokenId := uuid.New().String() err = a.tokenRepo.CreateVerified(ctx, dto.Email, dto.TokenType, tokenId) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, "Internal Server Error") + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to create verified token record") } _ = a.tokenRepo.Delete(ctx, dto.Email, dto.TokenType) diff --git a/internal/services/commitService.go b/internal/services/commitService.go index ac3148b..a745ca3 100644 --- a/internal/services/commitService.go +++ b/internal/services/commitService.go @@ -16,9 +16,9 @@ import ( ) type CommitService interface { - CreateCommit(ctx context.Context, userID string, projectID string, dto *request.CreateCommitDto) (*response.CommitResponse, error) - RestoreCommit(ctx context.Context, userID string, projectID string, dto *request.RestoreCommitDto) error - GetProjectCommits(ctx context.Context, projectID string) ([]*response.CommitResponse, error) + CreateCommit(ctx context.Context, userID string, projectID string, dto *request.CreateCommitDto) (*response.CommitResponse, *fiber.Error) + RestoreCommit(ctx context.Context, userID string, projectID string, dto *request.RestoreCommitDto) *fiber.Error + GetProjectCommits(ctx context.Context, projectID string) ([]*response.CommitResponse, *fiber.Error) } type commitService struct { @@ -38,7 +38,8 @@ func NewCommitService( projectRepo: projectRepo, } } -func (s *commitService) checkWritePermission(ctx context.Context, userID string, projectUUID pgtype.UUID) error { + +func (s *commitService) checkWritePermission(ctx context.Context, userID string, projectUUID pgtype.UUID) *fiber.Error { project, err := s.projectRepo.GetByID(ctx, projectUUID) if err != nil { return fiber.NewError(fiber.StatusNotFound, "Project not found") @@ -65,7 +66,7 @@ func (s *commitService) checkWritePermission(ctx context.Context, userID string, return nil } -func (s *commitService) CreateCommit(ctx context.Context, userID string, projectID string, dto *request.CreateCommitDto) (*response.CommitResponse, error) { +func (s *commitService) CreateCommit(ctx context.Context, userID string, projectID string, dto *request.CreateCommitDto) (*response.CommitResponse, *fiber.Error) { tx, err := s.db.Begin(ctx) if err != nil { return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to start transaction") @@ -80,8 +81,8 @@ func (s *commitService) CreateCommit(ctx context.Context, userID string, project return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid project ID") } - if err := s.checkWritePermission(ctx, userID, projectUUID); err != nil { - return nil, err + if fErr := s.checkWritePermission(ctx, userID, projectUUID); fErr != nil { + return nil, fErr } userUUID, err := convert.StringToUUID(userID) @@ -101,7 +102,7 @@ func (s *commitService) CreateCommit(ctx context.Context, userID string, project commitUUID, err := convert.StringToUUID(commit.ID) if err != nil { - return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid commit ID") + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to process commit ID") } err = pRepoTx.UpdateLatestCommit(ctx, sqlc.UpdateLatestCommitParams{ @@ -119,14 +120,14 @@ func (s *commitService) CreateCommit(ctx context.Context, userID string, project return commit.ToResponse(), nil } -func (s *commitService) RestoreCommit(ctx context.Context, userID string, projectID string, dto *request.RestoreCommitDto) error { +func (s *commitService) RestoreCommit(ctx context.Context, userID string, projectID string, dto *request.RestoreCommitDto) *fiber.Error { projectUUID, err := convert.StringToUUID(projectID) if err != nil { return fiber.NewError(fiber.StatusBadRequest, "Invalid project ID") } - if err := s.checkWritePermission(ctx, userID, projectUUID); err != nil { - return err + if fErr := s.checkWritePermission(ctx, userID, projectUUID); fErr != nil { + return fErr } commitUUID, err := convert.StringToUUID(dto.CommitID) @@ -153,7 +154,7 @@ func (s *commitService) RestoreCommit(ctx context.Context, userID string, projec return nil } -func (s *commitService) GetProjectCommits(ctx context.Context, projectID string) ([]*response.CommitResponse, error) { +func (s *commitService) GetProjectCommits(ctx context.Context, projectID string) ([]*response.CommitResponse, *fiber.Error) { projectUUID, err := convert.StringToUUID(projectID) if err != nil { return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid project ID") diff --git a/internal/services/entityService.go b/internal/services/entityService.go index 46498cb..0878314 100644 --- a/internal/services/entityService.go +++ b/internal/services/entityService.go @@ -13,8 +13,8 @@ import ( ) type EntityService interface { - GetEntityByID(ctx context.Context, id string) (*response.EntityResponse, error) - SearchEntities(ctx context.Context, req *request.SearchEntityDto) ([]*response.EntityResponse, error) + GetEntityByID(ctx context.Context, id string) (*response.EntityResponse, *fiber.Error) + SearchEntities(ctx context.Context, req *request.SearchEntityDto) ([]*response.EntityResponse, *fiber.Error) } type entityService struct { @@ -27,10 +27,10 @@ func NewEntityService(entityRepo repositories.EntityRepository) EntityService { } } -func (s *entityService) GetEntityByID(ctx context.Context, id string) (*response.EntityResponse, error) { +func (s *entityService) GetEntityByID(ctx context.Context, id string) (*response.EntityResponse, *fiber.Error) { entityId, err := convert.StringToUUID(id) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid entity ID format") } entity, err := s.entityRepo.GetByID(ctx, entityId) if err != nil { @@ -40,7 +40,7 @@ func (s *entityService) GetEntityByID(ctx context.Context, id string) (*response return entity.ToResponse(), nil } -func (s *entityService) SearchEntities(ctx context.Context, req *request.SearchEntityDto) ([]*response.EntityResponse, error) { +func (s *entityService) SearchEntities(ctx context.Context, req *request.SearchEntityDto) ([]*response.EntityResponse, *fiber.Error) { limit := int32(25) if req.Limit > 0 { limit = int32(req.Limit) @@ -61,7 +61,7 @@ func (s *entityService) SearchEntities(ctx context.Context, req *request.SearchE entities, err := s.entityRepo.Search(ctx, params) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to search entities") } return models.EntitiesEntityToResponse(entities), nil diff --git a/internal/services/geometryService.go b/internal/services/geometryService.go index 0d1c3e7..2374c57 100644 --- a/internal/services/geometryService.go +++ b/internal/services/geometryService.go @@ -14,8 +14,8 @@ import ( ) type GeometryService interface { - GetGeometryByID(ctx context.Context, id string) (*response.GeometryResponse, error) - SearchGeometries(ctx context.Context, req *request.SearchGeometryDto) ([]*response.GeometryResponse, error) + GetGeometryByID(ctx context.Context, id string) (*response.GeometryResponse, *fiber.Error) + SearchGeometries(ctx context.Context, req *request.SearchGeometryDto) ([]*response.GeometryResponse, *fiber.Error) } type geometryService struct { @@ -28,10 +28,10 @@ func NewGeometryService(geometryRepo repositories.GeometryRepository) GeometrySe } } -func (s *geometryService) GetGeometryByID(ctx context.Context, id string) (*response.GeometryResponse, error) { +func (s *geometryService) GetGeometryByID(ctx context.Context, id string) (*response.GeometryResponse, *fiber.Error) { geometryId, err := convert.StringToUUID(id) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid geometry ID format") } geometry, err := s.geometryRepo.GetByID(ctx, geometryId) if err != nil { @@ -41,24 +41,24 @@ func (s *geometryService) GetGeometryByID(ctx context.Context, id string) (*resp return geometry.ToResponse(), nil } -func (s *geometryService) SearchGeometries(ctx context.Context, req *request.SearchGeometryDto) ([]*response.GeometryResponse, error) { +func (s *geometryService) SearchGeometries(ctx context.Context, req *request.SearchGeometryDto) ([]*response.GeometryResponse, *fiber.Error) { params := sqlc.SearchGeometriesParams{} if req.MinLng != nil && req.MinLat != nil && req.MaxLng != nil && req.MaxLat != nil { if *req.MaxLng < *req.MinLng || *req.MaxLat < *req.MinLat { - return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid bounding box") + return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid bounding box coordinates") } params.SearchMinLng = pgtype.Float8{Float64: *req.MinLng, Valid: true} params.SearchMinLat = pgtype.Float8{Float64: *req.MinLat, Valid: true} params.SearchMaxLng = pgtype.Float8{Float64: *req.MaxLng, Valid: true} params.SearchMaxLat = pgtype.Float8{Float64: *req.MaxLat, Valid: true} } else { - return nil, fiber.NewError(fiber.StatusBadRequest, "Must provid Bounding box!") + return nil, fiber.NewError(fiber.StatusBadRequest, "Bounding box coordinates are required") } if req.TimePoint != nil { if *req.TimePoint < 0 { - return nil, fiber.NewError(fiber.StatusBadRequest, "Time point must be non-negative!") + return nil, fiber.NewError(fiber.StatusBadRequest, "Time point must be non-negative") } params.TimePoint = pgtype.Int4{Int32: *req.TimePoint, Valid: true} } @@ -72,7 +72,7 @@ func (s *geometryService) SearchGeometries(ctx context.Context, req *request.Sea geometries, err := s.geometryRepo.Search(ctx, params) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to search geometries") } return models.GeometriesEntityToResponse(geometries), nil diff --git a/internal/services/mediaService.go b/internal/services/mediaService.go index e5947f4..0667303 100644 --- a/internal/services/mediaService.go +++ b/internal/services/mediaService.go @@ -28,14 +28,14 @@ import ( ) type MediaService interface { - GetMediaByID(ctx context.Context, mediaId string) (*response.MediaResponse, error) - GetMediaByUserID(ctx context.Context, userId string) ([]*response.MediaResponse, error) - SearchMedia(ctx context.Context, dto *request.SearchMediaDto) (*response.PaginatedResponse, error) - DeleteMedia(ctx context.Context, claims *response.JWTClaims, mediaId string) error - BulkDeleteMedia(ctx context.Context, claims *response.JWTClaims, dto *request.MediaBulkDeleteDto) error - UploadServerSide(ctx context.Context, userId string, fileHeader *multipart.FileHeader) (*response.MediaResponse, error) - GeneratePresignedURL(ctx context.Context, userId string, dto *request.PreSignedDto) (*response.PreSignedResponse, error) - PreSignedCompleted(ctx context.Context, userId string, dto *request.PreSignedCompleteDto) (*response.MediaResponse, error) + GetMediaByID(ctx context.Context, mediaId string) (*response.MediaResponse, *fiber.Error) + GetMediaByUserID(ctx context.Context, userId string) ([]*response.MediaResponse, *fiber.Error) + SearchMedia(ctx context.Context, dto *request.SearchMediaDto) (*response.PaginatedResponse, *fiber.Error) + DeleteMedia(ctx context.Context, claims *response.JWTClaims, mediaId string) *fiber.Error + BulkDeleteMedia(ctx context.Context, claims *response.JWTClaims, dto *request.MediaBulkDeleteDto) *fiber.Error + UploadServerSide(ctx context.Context, userId string, fileHeader *multipart.FileHeader) (*response.MediaResponse, *fiber.Error) + GeneratePresignedURL(ctx context.Context, userId string, dto *request.PreSignedDto) (*response.PreSignedResponse, *fiber.Error) + PreSignedCompleted(ctx context.Context, userId string, dto *request.PreSignedCompleteDto) (*response.MediaResponse, *fiber.Error) } type mediaService struct { @@ -59,15 +59,15 @@ func NewMediaService( } } -func (m *mediaService) DeleteMedia(ctx context.Context, claims *response.JWTClaims, mediaId string) error { +func (m *mediaService) DeleteMedia(ctx context.Context, claims *response.JWTClaims, mediaId string) *fiber.Error { mediaIdUUID, err := convert.StringToUUID(mediaId) if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return fiber.NewError(fiber.StatusBadRequest, "Invalid media ID format") } media, err := m.mediaRepo.GetByID(ctx, mediaIdUUID) if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return fiber.NewError(fiber.StatusNotFound, "Media not found") } shoudDelete := false @@ -81,7 +81,7 @@ func (m *mediaService) DeleteMedia(ctx context.Context, claims *response.JWTClai err = m.mediaRepo.Delete(ctx, mediaIdUUID) if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return fiber.NewError(fiber.StatusInternalServerError, "Failed to delete media from database") } m.c.PublishTask(ctx, constants.StreamStorageName, constants.TaskTypeDeleteMedia, media.ToStorageEntity()) @@ -89,24 +89,24 @@ func (m *mediaService) DeleteMedia(ctx context.Context, claims *response.JWTClai return nil } -func (m *mediaService) BulkDeleteMedia(ctx context.Context, claims *response.JWTClaims, dto *request.MediaBulkDeleteDto) error { +func (m *mediaService) BulkDeleteMedia(ctx context.Context, claims *response.JWTClaims, dto *request.MediaBulkDeleteDto) *fiber.Error { listMedia, err := m.mediaRepo.GetByIDs(ctx, dto.MediaIDs) if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch media list") } shoudDelete := false if slices.Contains(claims.Roles, constants.RoleTypeAdmin) || slices.Contains(claims.Roles, constants.RoleTypeMod) { shoudDelete = true } - listMediaIds := make([]pgtype.UUID, len(listMedia)) - listMediaStorageEntities := make([]*models.MediaStorageEntity, len(listMedia)) + listMediaIds := make([]pgtype.UUID, 0) + listMediaStorageEntities := make([]*models.MediaStorageEntity, 0) for _, media := range listMedia { if media.UserID != claims.UId && !shoudDelete { - return fiber.NewError(fiber.StatusForbidden, "You don't have permission to delete this media") + return fiber.NewError(fiber.StatusForbidden, "You don't have permission to delete media "+media.ID) } id, err := convert.StringToUUID(media.ID) if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + continue } listMediaIds = append(listMediaIds, id) listMediaStorageEntities = append(listMediaStorageEntities, media.ToStorageEntity()) @@ -114,7 +114,7 @@ func (m *mediaService) BulkDeleteMedia(ctx context.Context, claims *response.JWT err = m.mediaRepo.BulkDelete(ctx, listMediaIds) if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return fiber.NewError(fiber.StatusInternalServerError, "Failed to bulk delete media from database") } m.c.PublishTask(ctx, constants.StreamStorageName, constants.TaskTypeBulkDeleteMedia, listMediaStorageEntities) @@ -122,26 +122,26 @@ func (m *mediaService) BulkDeleteMedia(ctx context.Context, claims *response.JWT return nil } -func (m *mediaService) GetMediaByID(ctx context.Context, id string) (*response.MediaResponse, error) { +func (m *mediaService) GetMediaByID(ctx context.Context, id string) (*response.MediaResponse, *fiber.Error) { mediaId, err := convert.StringToUUID(id) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid media ID format") } media, err := m.mediaRepo.GetByID(ctx, mediaId) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusNotFound, "Media not found") } return media.ToResponse(), nil } -func (m *mediaService) GetMediaByUserID(ctx context.Context, id string) ([]*response.MediaResponse, error) { +func (m *mediaService) GetMediaByUserID(ctx context.Context, id string) ([]*response.MediaResponse, *fiber.Error) { userId, err := convert.StringToUUID(id) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid user ID format") } medias, err := m.mediaRepo.GetByUserID(ctx, userId) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch user media") } return models.MediaEntitiesToResponse(medias), nil } @@ -183,7 +183,7 @@ func (m *mediaService) fillSearchArgs(arg *sqlc.SearchMediasParams, dto *request } } -func (m *mediaService) SearchMedia(ctx context.Context, dto *request.SearchMediaDto) (*response.PaginatedResponse, error) { +func (m *mediaService) SearchMedia(ctx context.Context, dto *request.SearchMediaDto) (*response.PaginatedResponse, *fiber.Error) { if dto.Page < 1 { dto.Page = 1 } @@ -224,17 +224,17 @@ func (m *mediaService) SearchMedia(ctx context.Context, dto *request.SearchMedia }) if err := g.Wait(); err != nil { - return nil, err + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to search media") } media := models.MediaEntitiesToResponse(rows) return response.BuildPaginatedResponse(media, totalRecords, dto.Page, dto.Limit), nil } -func (m *mediaService) UploadServerSide(ctx context.Context, userId string, fileHeader *multipart.FileHeader) (*response.MediaResponse, error) { +func (m *mediaService) UploadServerSide(ctx context.Context, userId string, fileHeader *multipart.FileHeader) (*response.MediaResponse, *fiber.Error) { userIdUUID, err := convert.StringToUUID(userId) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid user ID format") } file, err := fileHeader.Open() if err != nil { @@ -279,7 +279,7 @@ func (m *mediaService) UploadServerSide(ctx context.Context, userId string, file }) if err != nil { log.Err(err).Msg("Failed to upload file to storage") - return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to upload file") + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to upload file to storage") } media, err := m.mediaRepo.Create(ctx, sqlc.CreateMediaParams{ @@ -291,12 +291,12 @@ func (m *mediaService) UploadServerSide(ctx context.Context, userId string, file FileMetadata: mdByte, }) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to create media record") } return media.ToResponse(), nil } -func (m *mediaService) GeneratePresignedURL(ctx context.Context, userId string, dto *request.PreSignedDto) (*response.PreSignedResponse, error) { +func (m *mediaService) GeneratePresignedURL(ctx context.Context, userId string, dto *request.PreSignedDto) (*response.PreSignedResponse, *fiber.Error) { fileExt := filepath.Ext(dto.FileName) mid, err := uuid.NewV7() if err != nil { @@ -346,7 +346,7 @@ func (m *mediaService) GeneratePresignedURL(ctx context.Context, userId string, ) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, "Internal Server Error") + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to create upload token") } return &response.PreSignedResponse{ @@ -360,7 +360,7 @@ func (m *mediaService) GeneratePresignedURL(ctx context.Context, userId string, }, nil } -func (m *mediaService) PreSignedCompleted(ctx context.Context, userId string, dto *request.PreSignedCompleteDto) (*response.MediaResponse, error) { +func (m *mediaService) PreSignedCompleted(ctx context.Context, userId string, dto *request.PreSignedCompleteDto) (*response.MediaResponse, *fiber.Error) { token, err := m.tokenRepo.GetUploadToken(ctx, userId, dto.TokenID) if err != nil { return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to get upload token") @@ -370,7 +370,7 @@ func (m *mediaService) PreSignedCompleted(ctx context.Context, userId string, dt } userIdUUID, err := convert.StringToUUID(userId) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid user ID format") } err = m.s.Move( @@ -386,7 +386,7 @@ func (m *mediaService) PreSignedCompleted(ctx context.Context, userId string, dt ) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to move file to main storage") } media, err := m.mediaRepo.Create(ctx, sqlc.CreateMediaParams{ diff --git a/internal/services/projectService.go b/internal/services/projectService.go index e79350d..ea76db5 100644 --- a/internal/services/projectService.go +++ b/internal/services/projectService.go @@ -17,16 +17,16 @@ import ( ) type ProjectService interface { - GetProjectByID(ctx context.Context, id string) (*response.ProjectResponse, error) - GetProjectByUserID(ctx context.Context, userID string, dto *request.GetProjectsByUserDto) ([]*response.ProjectResponse, error) - SearchProject(ctx context.Context, dto *request.SearchProjectDto) (*response.PaginatedResponse, error) - DeleteProject(ctx context.Context, id string) error - CreateProject(ctx context.Context, userID string, dto *request.CreateProjectDto) (*response.ProjectResponse, error) - UpdateProject(ctx context.Context, id string, dto *request.UpdateProjectDto) (*response.ProjectResponse, error) - AddMember(ctx context.Context, callerID string, projectID string, dto *request.AddProjectMemberDto) (*response.ProjectResponse, error) - UpdateMemberRole(ctx context.Context, callerID string, projectID string, memberUserID string, dto *request.UpdateProjectMemberDto) (*response.ProjectResponse, error) - RemoveMember(ctx context.Context, callerID string, projectID string, memberUserID string) error - ChangeOwner(ctx context.Context, callerID string, projectID string, newOwnerID string) (*response.ProjectResponse, error) + GetProjectByID(ctx context.Context, id string) (*response.ProjectResponse, *fiber.Error) + GetProjectByUserID(ctx context.Context, userID string, dto *request.GetProjectsByUserDto) ([]*response.ProjectResponse, *fiber.Error) + SearchProject(ctx context.Context, dto *request.SearchProjectDto) (*response.PaginatedResponse, *fiber.Error) + DeleteProject(ctx context.Context, id string) *fiber.Error + CreateProject(ctx context.Context, userID string, dto *request.CreateProjectDto) (*response.ProjectResponse, *fiber.Error) + UpdateProject(ctx context.Context, id string, dto *request.UpdateProjectDto) (*response.ProjectResponse, *fiber.Error) + AddMember(ctx context.Context, callerID string, projectID string, dto *request.AddProjectMemberDto) (*response.ProjectResponse, *fiber.Error) + UpdateMemberRole(ctx context.Context, callerID string, projectID string, memberUserID string, dto *request.UpdateProjectMemberDto) (*response.ProjectResponse, *fiber.Error) + RemoveMember(ctx context.Context, callerID string, projectID string, memberUserID string) *fiber.Error + ChangeOwner(ctx context.Context, callerID string, projectID string, newOwnerID string) (*response.ProjectResponse, *fiber.Error) } type projectService struct { @@ -39,7 +39,7 @@ func NewProjectService(projectRepo repositories.ProjectRepository) ProjectServic } } -func (s *projectService) checkCallerIsOwner(ctx context.Context, callerID string, projectUUID pgtype.UUID) error { +func (s *projectService) checkCallerIsOwner(ctx context.Context, callerID string, projectUUID pgtype.UUID) *fiber.Error { project, err := s.projectRepo.GetByID(ctx, projectUUID) if err != nil { return fiber.NewError(fiber.StatusNotFound, "Project not found") @@ -58,7 +58,7 @@ func (s *projectService) checkCallerIsOwner(ctx context.Context, callerID string return nil } -func (s *projectService) GetProjectByID(ctx context.Context, id string) (*response.ProjectResponse, error) { +func (s *projectService) GetProjectByID(ctx context.Context, id string) (*response.ProjectResponse, *fiber.Error) { projectUUID, err := convert.StringToUUID(id) if err != nil { return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid project ID format") @@ -72,7 +72,7 @@ func (s *projectService) GetProjectByID(ctx context.Context, id string) (*respon return project.ToResponse(), nil } -func (s *projectService) GetProjectByUserID(ctx context.Context, userID string, dto *request.GetProjectsByUserDto) ([]*response.ProjectResponse, error) { +func (s *projectService) GetProjectByUserID(ctx context.Context, userID string, dto *request.GetProjectsByUserDto) ([]*response.ProjectResponse, *fiber.Error) { userUUID, err := convert.StringToUUID(userID) if err != nil { return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid user ID format") @@ -96,7 +96,7 @@ func (s *projectService) GetProjectByUserID(ctx context.Context, userID string, projects, err := s.projectRepo.GetByUserID(ctx, arg) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch user projects") } return models.ProjectsEntityToResponse(projects), nil @@ -143,7 +143,7 @@ func (s *projectService) fillSearchArgs(arg *sqlc.SearchProjectsParams, dto *req } } -func (s *projectService) SearchProject(ctx context.Context, dto *request.SearchProjectDto) (*response.PaginatedResponse, error) { +func (s *projectService) SearchProject(ctx context.Context, dto *request.SearchProjectDto) (*response.PaginatedResponse, *fiber.Error) { if dto.Page < 1 { dto.Page = 1 } @@ -184,7 +184,7 @@ func (s *projectService) SearchProject(ctx context.Context, dto *request.SearchP }) if err := g.Wait(); err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to search projects") } projects := models.ProjectsEntityToResponse(rows) @@ -192,7 +192,7 @@ func (s *projectService) SearchProject(ctx context.Context, dto *request.SearchP return response.BuildPaginatedResponse(projects, totalRecords, dto.Page, dto.Limit), nil } -func (s *projectService) CreateProject(ctx context.Context, userID string, dto *request.CreateProjectDto) (*response.ProjectResponse, error) { +func (s *projectService) CreateProject(ctx context.Context, userID string, dto *request.CreateProjectDto) (*response.ProjectResponse, *fiber.Error) { userUUID, err := convert.StringToUUID(userID) if err != nil { return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid user ID format") @@ -221,7 +221,7 @@ func (s *projectService) CreateProject(ctx context.Context, userID string, dto * return project.ToResponse(), nil } -func (s *projectService) UpdateProject(ctx context.Context, id string, dto *request.UpdateProjectDto) (*response.ProjectResponse, error) { +func (s *projectService) UpdateProject(ctx context.Context, id string, dto *request.UpdateProjectDto) (*response.ProjectResponse, *fiber.Error) { projectUUID, err := convert.StringToUUID(id) if err != nil { return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid project ID format") @@ -254,7 +254,7 @@ func (s *projectService) UpdateProject(ctx context.Context, id string, dto *requ return project.ToResponse(), nil } -func (s *projectService) DeleteProject(ctx context.Context, id string) error { +func (s *projectService) DeleteProject(ctx context.Context, id string) *fiber.Error { projectUUID, err := convert.StringToUUID(id) if err != nil { return fiber.NewError(fiber.StatusBadRequest, "Invalid project ID format") @@ -273,14 +273,14 @@ func (s *projectService) DeleteProject(ctx context.Context, id string) error { return nil } -func (s *projectService) AddMember(ctx context.Context, callerID string, projectID string, dto *request.AddProjectMemberDto) (*response.ProjectResponse, error) { +func (s *projectService) AddMember(ctx context.Context, callerID string, projectID string, dto *request.AddProjectMemberDto) (*response.ProjectResponse, *fiber.Error) { projectUUID, err := convert.StringToUUID(projectID) if err != nil { return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid project ID format") } - if err := s.checkCallerIsOwner(ctx, callerID, projectUUID); err != nil { - return nil, err + if fErr := s.checkCallerIsOwner(ctx, callerID, projectUUID); fErr != nil { + return nil, fErr } memberUUID, err := convert.StringToUUID(dto.UserID) @@ -314,14 +314,14 @@ func (s *projectService) AddMember(ctx context.Context, callerID string, project return project.ToResponse(), nil } -func (s *projectService) UpdateMemberRole(ctx context.Context, callerID string, projectID string, memberUserID string, dto *request.UpdateProjectMemberDto) (*response.ProjectResponse, error) { +func (s *projectService) UpdateMemberRole(ctx context.Context, callerID string, projectID string, memberUserID string, dto *request.UpdateProjectMemberDto) (*response.ProjectResponse, *fiber.Error) { projectUUID, err := convert.StringToUUID(projectID) if err != nil { return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid project ID format") } - if err := s.checkCallerIsOwner(ctx, callerID, projectUUID); err != nil { - return nil, err + if fErr := s.checkCallerIsOwner(ctx, callerID, projectUUID); fErr != nil { + return nil, fErr } memberUUID, err := convert.StringToUUID(memberUserID) @@ -348,14 +348,14 @@ func (s *projectService) UpdateMemberRole(ctx context.Context, callerID string, return project.ToResponse(), nil } -func (s *projectService) RemoveMember(ctx context.Context, callerID string, projectID string, memberUserID string) error { +func (s *projectService) RemoveMember(ctx context.Context, callerID string, projectID string, memberUserID string) *fiber.Error { projectUUID, err := convert.StringToUUID(projectID) if err != nil { return fiber.NewError(fiber.StatusBadRequest, "Invalid project ID format") } - if err := s.checkCallerIsOwner(ctx, callerID, projectUUID); err != nil { - return err + if fErr := s.checkCallerIsOwner(ctx, callerID, projectUUID); fErr != nil { + return fErr } memberUUID, err := convert.StringToUUID(memberUserID) @@ -378,14 +378,14 @@ func (s *projectService) RemoveMember(ctx context.Context, callerID string, proj return nil } -func (s *projectService) ChangeOwner(ctx context.Context, callerID string, projectID string, newOwnerID string) (*response.ProjectResponse, error) { +func (s *projectService) ChangeOwner(ctx context.Context, callerID string, projectID string, newOwnerID string) (*response.ProjectResponse, *fiber.Error) { projectUUID, err := convert.StringToUUID(projectID) if err != nil { return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid project ID format") } - if err := s.checkCallerIsOwner(ctx, callerID, projectUUID); err != nil { - return nil, err + if fErr := s.checkCallerIsOwner(ctx, callerID, projectUUID); fErr != nil { + return nil, fErr } if callerID == newOwnerID { diff --git a/internal/services/rasterTileService.go b/internal/services/rasterTileService.go index c3dfd50..5b79109 100644 --- a/internal/services/rasterTileService.go +++ b/internal/services/rasterTileService.go @@ -8,8 +8,8 @@ import ( ) type RasterTileService interface { - GetMetadata(ctx context.Context) (map[string]string, error) - GetTile(ctx context.Context, z, x, y int) ([]byte, map[string]string, error) + GetMetadata(ctx context.Context) (map[string]string, *fiber.Error) + GetTile(ctx context.Context, z, x, y int) ([]byte, map[string]string, *fiber.Error) } type rasterTileService struct { @@ -24,21 +24,21 @@ func NewRasterTileService( } } -func (t *rasterTileService) GetMetadata(ctx context.Context) (map[string]string, error) { +func (t *rasterTileService) GetMetadata(ctx context.Context) (map[string]string, *fiber.Error) { metaData, err := t.tileRepo.GetMetadata(ctx) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch map metadata") } return metaData, nil } -func (t *rasterTileService) GetTile(ctx context.Context, z, x, y int) ([]byte, map[string]string, error) { +func (t *rasterTileService) GetTile(ctx context.Context, z, x, y int) ([]byte, map[string]string, *fiber.Error) { contentType := make(map[string]string) data, format, err := t.tileRepo.GetTile(ctx, z, x, y) if err != nil { - return nil, contentType, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, contentType, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch tile data") } switch format { diff --git a/internal/services/roleService.go b/internal/services/roleService.go index bc3bbf2..8948921 100644 --- a/internal/services/roleService.go +++ b/internal/services/roleService.go @@ -11,8 +11,8 @@ import ( ) type RoleService interface { - GetRoleByID(ctx context.Context, id string) (*response.RoleResponse, error) - GetAllRole(ctx context.Context) ([]*response.RoleResponse, error) + GetRoleByID(ctx context.Context, id string) (*response.RoleResponse, *fiber.Error) + GetAllRole(ctx context.Context) ([]*response.RoleResponse, *fiber.Error) } type roleService struct { @@ -27,19 +27,19 @@ func NewRoleService( } } -func (r *roleService) GetAllRole(ctx context.Context) ([]*response.RoleResponse, error) { +func (r *roleService) GetAllRole(ctx context.Context) ([]*response.RoleResponse, *fiber.Error) { roles, err := r.roleRepo.All(ctx) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch roles") } return models.RolesEntityToResponse(roles), nil } -func (r *roleService) GetRoleByID(ctx context.Context, id string) (*response.RoleResponse, error) { +func (r *roleService) GetRoleByID(ctx context.Context, id string) (*response.RoleResponse, *fiber.Error) { roleId, err := convert.StringToUUID(id) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid role ID format") } role, err := r.roleRepo.GetByID(ctx, roleId) if err != nil { diff --git a/internal/services/submissionService.go b/internal/services/submissionService.go index d70984b..a94e3c0 100644 --- a/internal/services/submissionService.go +++ b/internal/services/submissionService.go @@ -20,11 +20,11 @@ import ( ) type SubmissionService interface { - CreateSubmission(ctx context.Context, userID string, dto *request.CreateSubmissionDto) (*response.SubmissionResponse, error) - UpdateSubmissionStatus(ctx context.Context, reviewerID string, submissionID string, dto *request.UpdateSubmissionStatusDto) (*response.SubmissionResponse, error) - GetSubmissionByID(ctx context.Context, id string) (*response.SubmissionResponse, error) - SearchSubmissions(ctx context.Context, dto *request.SearchSubmissionDto) (*response.PaginatedResponse, error) - DeleteSubmission(ctx context.Context, userID string, id string, claims *response.JWTClaims) error + CreateSubmission(ctx context.Context, userID string, dto *request.CreateSubmissionDto) (*response.SubmissionResponse, *fiber.Error) + UpdateSubmissionStatus(ctx context.Context, reviewerID string, submissionID string, dto *request.UpdateSubmissionStatusDto) (*response.SubmissionResponse, *fiber.Error) + GetSubmissionByID(ctx context.Context, id string) (*response.SubmissionResponse, *fiber.Error) + SearchSubmissions(ctx context.Context, dto *request.SearchSubmissionDto) (*response.PaginatedResponse, *fiber.Error) + DeleteSubmission(ctx context.Context, userID string, id string, claims *response.JWTClaims) *fiber.Error } type submissionService struct { @@ -54,7 +54,7 @@ func NewSubmissionService( } } -func (s *submissionService) CreateSubmission(ctx context.Context, userID string, dto *request.CreateSubmissionDto) (*response.SubmissionResponse, error) { +func (s *submissionService) CreateSubmission(ctx context.Context, userID string, dto *request.CreateSubmissionDto) (*response.SubmissionResponse, *fiber.Error) { projectUUID, err := convert.StringToUUID(dto.ProjectID) if err != nil { return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid project ID") @@ -94,7 +94,7 @@ func (s *submissionService) CreateSubmission(ctx context.Context, userID string, return submission.ToResponse(), nil } -func (s *submissionService) UpdateSubmissionStatus(ctx context.Context, reviewerID string, submissionID string, dto *request.UpdateSubmissionStatusDto) (*response.SubmissionResponse, error) { +func (s *submissionService) UpdateSubmissionStatus(ctx context.Context, reviewerID string, submissionID string, dto *request.UpdateSubmissionStatusDto) (*response.SubmissionResponse, *fiber.Error) { submissionUUID, err := convert.StringToUUID(submissionID) if err != nil { return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid submission ID") @@ -110,7 +110,6 @@ func (s *submissionService) UpdateSubmissionStatus(ctx context.Context, reviewer return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid status") } - submission, err := s.submissionRepo.GetByID(ctx, submissionUUID) if err != nil { return nil, fiber.NewError(fiber.StatusNotFound, "Submission not found") @@ -132,8 +131,7 @@ func (s *submissionService) UpdateSubmissionStatus(ctx context.Context, reviewer return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to update submission status") } - _ = s.c.Del(ctx, fmt.Sprintf("proejct:id:%s", submission.ProjectID)) - + _ = s.c.Del(ctx, fmt.Sprintf("project:id:%s", submission.ProjectID)) return updatedSubmission.ToResponse(), nil } @@ -185,7 +183,7 @@ func (m *submissionService) fillSearchArgs(arg *sqlc.SearchSubmissionsParams, dt } } -func (s *submissionService) GetSubmissionByID(ctx context.Context, id string) (*response.SubmissionResponse, error) { +func (s *submissionService) GetSubmissionByID(ctx context.Context, id string) (*response.SubmissionResponse, *fiber.Error) { submissionUUID, err := convert.StringToUUID(id) if err != nil { return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid submission ID") @@ -199,7 +197,7 @@ func (s *submissionService) GetSubmissionByID(ctx context.Context, id string) (* return submission.ToResponse(), nil } -func (s *submissionService) SearchSubmissions(ctx context.Context, dto *request.SearchSubmissionDto) (*response.PaginatedResponse, error) { +func (s *submissionService) SearchSubmissions(ctx context.Context, dto *request.SearchSubmissionDto) (*response.PaginatedResponse, *fiber.Error) { if dto.Page < 1 { dto.Page = 1 } @@ -241,7 +239,7 @@ func (s *submissionService) SearchSubmissions(ctx context.Context, dto *request. }) if err := g.Wait(); err != nil { - return nil, err + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to search submissions") } submissions := models.SubmissionsEntityToResponse(rows) @@ -249,7 +247,7 @@ func (s *submissionService) SearchSubmissions(ctx context.Context, dto *request. return response.BuildPaginatedResponse(submissions, totalRecords, dto.Page, dto.Limit), nil } -func (s *submissionService) DeleteSubmission(ctx context.Context, userID string, id string, claims *response.JWTClaims) error { +func (s *submissionService) DeleteSubmission(ctx context.Context, userID string, id string, claims *response.JWTClaims) *fiber.Error { submissionUUID, err := convert.StringToUUID(id) if err != nil { return fiber.NewError(fiber.StatusBadRequest, "Invalid submission ID") diff --git a/internal/services/tileService.go b/internal/services/tileService.go index 51c325a..d877679 100644 --- a/internal/services/tileService.go +++ b/internal/services/tileService.go @@ -8,8 +8,8 @@ import ( ) type TileService interface { - GetMetadata(ctx context.Context) (map[string]string, error) - GetTile(ctx context.Context, z, x, y int) ([]byte, map[string]string, error) + GetMetadata(ctx context.Context) (map[string]string, *fiber.Error) + GetTile(ctx context.Context, z, x, y int) ([]byte, map[string]string, *fiber.Error) } type tileService struct { @@ -24,21 +24,21 @@ func NewTileService( } } -func (t *tileService) GetMetadata(ctx context.Context) (map[string]string, error) { +func (t *tileService) GetMetadata(ctx context.Context) (map[string]string, *fiber.Error) { metaData, err := t.tileRepo.GetMetadata(ctx) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch map metadata") } return metaData, nil } -func (t *tileService) GetTile(ctx context.Context, z, x, y int) ([]byte, map[string]string, error) { +func (t *tileService) GetTile(ctx context.Context, z, x, y int) ([]byte, map[string]string, *fiber.Error) { contentType := make(map[string]string) data, format, isPBF, err := t.tileRepo.GetTile(ctx, z, x, y) if err != nil { - return nil, contentType, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, contentType, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch tile data") } switch format { case "pbf": diff --git a/internal/services/userService.go b/internal/services/userService.go index 5bd1a74..86311d1 100644 --- a/internal/services/userService.go +++ b/internal/services/userService.go @@ -2,6 +2,8 @@ package services import ( "context" + "database/sql" + "errors" "fmt" "history-api/internal/dtos/request" "history-api/internal/dtos/response" @@ -22,15 +24,16 @@ import ( type UserService interface { //user - UpdateProfile(ctx context.Context, userId string, dto *request.UpdateProfileDto) (*response.UserResponse, error) - ChangePassword(ctx context.Context, userId string, dto *request.ChangePasswordDto) error + UpdateProfile(ctx context.Context, userId string, dto *request.UpdateProfileDto) (*response.UserResponse, *fiber.Error) + ChangePassword(ctx context.Context, userId string, dto *request.ChangePasswordDto) *fiber.Error //admin - DeleteUser(ctx context.Context, userId string) error - ChangeRoleUser(ctx context.Context, userId string, claims *response.JWTClaims, dto *request.ChangeRoleDto) (*response.UserResponse, error) - RestoreUser(ctx context.Context, userId string) (*response.UserResponse, error) - GetUserByID(ctx context.Context, userId string) (*response.UserResponse, error) - SearchUser(ctx context.Context, dto *request.SearchUserDto) (*response.PaginatedResponse, error) + CreateUser(ctx context.Context, dto *request.CreateUserDto) (*response.UserResponse, *fiber.Error) + DeleteUser(ctx context.Context, userId string) *fiber.Error + ChangeRoleUser(ctx context.Context, userId string, claims *response.JWTClaims, dto *request.ChangeRoleDto) (*response.UserResponse, *fiber.Error) + RestoreUser(ctx context.Context, userId string) (*response.UserResponse, *fiber.Error) + GetUserByID(ctx context.Context, userId string) (*response.UserResponse, *fiber.Error) + SearchUser(ctx context.Context, dto *request.SearchUserDto) (*response.PaginatedResponse, *fiber.Error) } type userService struct { @@ -45,7 +48,6 @@ func NewUserService( roleRepo repositories.RoleRepository, c cache.Cache, db *pgxpool.Pool, - ) UserService { return &userService{ userRepo: userRepo, @@ -55,7 +57,80 @@ func NewUserService( } } -func (u *userService) ChangePassword(ctx context.Context, userId string, dto *request.ChangePasswordDto) error { +func (u *userService) CreateUser(ctx context.Context, dto *request.CreateUserDto) (*response.UserResponse, *fiber.Error) { + tx, err := u.db.Begin(ctx) + if err != nil { + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to start transaction") + } + defer tx.Rollback(ctx) + + uRepo := u.userRepo.WithTx(tx) + rRepo := u.roleRepo.WithTx(tx) + + existingUser, err := u.userRepo.GetByEmail(ctx, dto.Email) + if err != nil && !errors.Is(err, sql.ErrNoRows) { + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to check existing user") + } + if existingUser != nil { + return nil, fiber.NewError(fiber.StatusBadRequest, "User already exists") + } + + hashed, err := bcrypt.GenerateFromPassword([]byte(dto.Password), bcrypt.DefaultCost) + if err != nil { + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to hash password") + } + + user, err := uRepo.UpsertUser(ctx, sqlc.UpsertUserParams{ + Email: dto.Email, + PasswordHash: pgtype.Text{String: string(hashed), Valid: true}, + AuthProvider: constants.ProviderTypeLocal.String(), + }) + if err != nil { + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to create user") + } + + userUUID, err := convert.StringToUUID(user.ID) + if err != nil { + return nil, fiber.NewError(fiber.StatusInternalServerError, "Invalid user ID") + } + + _, err = uRepo.CreateProfile(ctx, sqlc.CreateUserProfileParams{ + UserID: userUUID, + DisplayName: pgtype.Text{String: dto.DisplayName, Valid: true}, + }) + if err != nil { + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to create user profile") + } + + var roleIdList []pgtype.UUID + for _, rId := range dto.Roles { + rid, err := convert.StringToUUID(rId) + if err == nil { + roleIdList = append(roleIdList, rid) + } + } + + err = rRepo.CreateUserRole(ctx, sqlc.CreateUserRoleParams{ + UserID: userUUID, + Column2: roleIdList, + }) + if err != nil { + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to assign roles") + } + + if err := tx.Commit(ctx); err != nil { + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to commit transaction") + } + + finalUser, err := u.userRepo.GetByID(ctx, userUUID) + if err != nil { + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch created user") + } + + return finalUser.ToResponse(), nil +} + +func (u *userService) ChangePassword(ctx context.Context, userId string, dto *request.ChangePasswordDto) *fiber.Error { tx, err := u.db.Begin(ctx) if err != nil { return fiber.NewError(fiber.StatusInternalServerError, "Failed to start transaction") @@ -66,11 +141,11 @@ func (u *userService) ChangePassword(ctx context.Context, userId string, dto *re pgID, err := convert.StringToUUID(userId) if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return fiber.NewError(fiber.StatusInternalServerError, "Invalid user ID") } user, err := u.userRepo.GetByID(ctx, pgID) if err != nil { - return fiber.NewError(fiber.StatusNotFound, err.Error()) + return fiber.NewError(fiber.StatusNotFound, "Failed to fetch user") } if user == nil { return fiber.NewError(fiber.StatusNotFound, "User not found") @@ -81,15 +156,15 @@ func (u *userService) ChangePassword(ctx context.Context, userId string, dto *re return fiber.NewError(fiber.StatusBadRequest, "Old password required") } if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(dto.OldPassword)); err != nil { - return fiber.NewError(fiber.StatusUnauthorized, "Invalid password!") + return fiber.NewError(fiber.StatusUnauthorized, "Invalid old password") } } else if user.PasswordHash == "" && dto.OldPassword != "" { - return fiber.NewError(fiber.StatusBadRequest, "Invalid request") + return fiber.NewError(fiber.StatusBadRequest, "Invalid request: user has no password") } hashPassword, err := bcrypt.GenerateFromPassword([]byte(dto.NewPassword), bcrypt.DefaultCost) if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return fiber.NewError(fiber.StatusInternalServerError, "Failed to hash new password") } err = uRepo.UpdatePassword(ctx, sqlc.UpdateUserPasswordParams{ @@ -97,7 +172,7 @@ func (u *userService) ChangePassword(ctx context.Context, userId string, dto *re PasswordHash: pgtype.Text{String: string(hashPassword), Valid: true}, }) if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return fiber.NewError(fiber.StatusInternalServerError, "Failed to update password") } err = uRepo.UpdateTokenVersion(ctx, sqlc.UpdateTokenVersionParams{ @@ -105,7 +180,7 @@ func (u *userService) ChangePassword(ctx context.Context, userId string, dto *re TokenVersion: user.TokenVersion + 1, }) if err != nil { - return err + return fiber.NewError(fiber.StatusInternalServerError, "Failed to update token version") } if err := tx.Commit(ctx); err != nil { @@ -114,7 +189,7 @@ func (u *userService) ChangePassword(ctx context.Context, userId string, dto *re return nil } -func (u *userService) ChangeRoleUser(ctx context.Context, userId string, claims *response.JWTClaims, dto *request.ChangeRoleDto) (*response.UserResponse, error) { +func (u *userService) ChangeRoleUser(ctx context.Context, userId string, claims *response.JWTClaims, dto *request.ChangeRoleDto) (*response.UserResponse, *fiber.Error) { tx, err := u.db.Begin(ctx) if err != nil { return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to start transaction") @@ -126,12 +201,12 @@ func (u *userService) ChangeRoleUser(ctx context.Context, userId string, claims userUUID, err := convert.StringToUUID(userId) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Invalid user ID") } user, err := u.userRepo.GetByID(ctx, userUUID) if err != nil { - return nil, fiber.NewError(fiber.StatusNotFound, err.Error()) + return nil, fiber.NewError(fiber.StatusNotFound, "Failed to fetch user") } if user == nil { return nil, fiber.NewError(fiber.StatusNotFound, "User not found") @@ -139,7 +214,7 @@ func (u *userService) ChangeRoleUser(ctx context.Context, userId string, claims newListRole, err := u.roleRepo.GetByIDs(ctx, dto.Roles) if err != nil { - return nil, err + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch roles") } hasUserRole := false @@ -163,20 +238,20 @@ func (u *userService) ChangeRoleUser(ctx context.Context, userId string, claims } if !hasUserRole { - return nil, fiber.NewError(fiber.StatusNotFound, "User must have the USER role") + return nil, fiber.NewError(fiber.StatusForbidden, "User must have the USER role") } if slices.Contains(claims.Roles, constants.RoleTypeMod) && !slices.Contains(claims.Roles, constants.RoleTypeAdmin) { if hasAdminRole { - return nil, fiber.NewError(fiber.StatusForbidden, "MOD cannot assign ADMIN role to any user") + return nil, fiber.NewError(fiber.StatusForbidden, "MOD cannot assign ADMIN role") } if userId == claims.UId && !hasModRole { - return nil, fiber.NewError(fiber.StatusForbidden, "You can't remove MOD role of yourself") + return nil, fiber.NewError(fiber.StatusForbidden, "You cannot remove your own MOD role") } if userId == claims.UId && hasBannedRole { - return nil, fiber.NewError(fiber.StatusForbidden, "You can't assign BANNED role to yourself") + return nil, fiber.NewError(fiber.StatusForbidden, "You cannot ban yourself") } isTargetAdminOrMod := false for _, r := range user.Roles { @@ -186,17 +261,17 @@ func (u *userService) ChangeRoleUser(ctx context.Context, userId string, claims } } if isTargetAdminOrMod && hasBannedRole { - return nil, fiber.NewError(fiber.StatusForbidden, "MOD cannot assign BANNED role to an ADMIN or MOD user") + return nil, fiber.NewError(fiber.StatusForbidden, "MOD cannot ban an ADMIN or MOD") } } if slices.Contains(claims.Roles, constants.RoleTypeAdmin) { if userId == claims.UId && hasBannedRole { - return nil, fiber.NewError(fiber.StatusForbidden, "You can't assign BANNED role to yourself") + return nil, fiber.NewError(fiber.StatusForbidden, "You cannot ban yourself") } if userId == claims.UId && !hasAdminRole { - return nil, fiber.NewError(fiber.StatusForbidden, "You can't remove ADMIN role of yourself") + return nil, fiber.NewError(fiber.StatusForbidden, "You cannot remove your own ADMIN role") } } @@ -213,7 +288,7 @@ func (u *userService) ChangeRoleUser(ctx context.Context, userId string, claims err = rRepo.BulkDeleteRolesFromUser(ctx, userUUID) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to clear old roles") } err = rRepo.CreateUserRole(ctx, sqlc.CreateUserRoleParams{ @@ -221,7 +296,7 @@ func (u *userService) ChangeRoleUser(ctx context.Context, userId string, claims Column2: roleIdList, }) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to create new roles") } err = uRepo.UpdateTokenVersion(ctx, sqlc.UpdateTokenVersionParams{ @@ -229,13 +304,13 @@ func (u *userService) ChangeRoleUser(ctx context.Context, userId string, claims TokenVersion: user.TokenVersion + 1, }) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to update token version") } user.TokenVersion += 1 err = tx.Commit(ctx) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to commit transaction") } mapCache := map[string]any{ @@ -247,33 +322,33 @@ func (u *userService) ChangeRoleUser(ctx context.Context, userId string, claims return user.ToResponse(), nil } -func (u *userService) DeleteUser(ctx context.Context, userId string) error { +func (u *userService) DeleteUser(ctx context.Context, userId string) *fiber.Error { pgID, err := convert.StringToUUID(userId) if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return fiber.NewError(fiber.StatusInternalServerError, "Invalid user ID") } user, err := u.userRepo.GetByID(ctx, pgID) if err != nil { - return fiber.NewError(fiber.StatusNotFound, err.Error()) + return fiber.NewError(fiber.StatusNotFound, "Failed to fetch user") } if user == nil { return fiber.NewError(fiber.StatusNotFound, "User not found") } err = u.userRepo.Delete(ctx, pgID) if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return fiber.NewError(fiber.StatusInternalServerError, "Failed to delete user") } return nil } -func (u *userService) UpdateProfile(ctx context.Context, userId string, dto *request.UpdateProfileDto) (*response.UserResponse, error) { +func (u *userService) UpdateProfile(ctx context.Context, userId string, dto *request.UpdateProfileDto) (*response.UserResponse, *fiber.Error) { pgID, err := convert.StringToUUID(userId) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Invalid user ID") } user, err := u.userRepo.GetByID(ctx, pgID) if err != nil { - return nil, fiber.NewError(fiber.StatusNotFound, err.Error()) + return nil, fiber.NewError(fiber.StatusNotFound, "Failed to fetch user") } if user == nil { return nil, fiber.NewError(fiber.StatusNotFound, "User not found") @@ -294,20 +369,20 @@ func (u *userService) UpdateProfile(ctx context.Context, userId string, dto *req }, ) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to update profile") } return newUser.ToResponse(), nil } -func (u *userService) RestoreUser(ctx context.Context, userId string) (*response.UserResponse, error) { +func (u *userService) RestoreUser(ctx context.Context, userId string) (*response.UserResponse, *fiber.Error) { pgID, err := convert.StringToUUID(userId) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Invalid user ID") } user, err := u.userRepo.GetByIDWithoutDeleted(ctx, pgID) if err != nil { - return nil, fiber.NewError(fiber.StatusNotFound, err.Error()) + return nil, fiber.NewError(fiber.StatusNotFound, "Failed to fetch user") } if user == nil { return nil, fiber.NewError(fiber.StatusNotFound, "User not found") @@ -315,7 +390,7 @@ func (u *userService) RestoreUser(ctx context.Context, userId string) (*response err = u.userRepo.Restore(ctx, pgID) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to restore user") } user.IsDeleted = false return user.ToResponse(), nil @@ -362,7 +437,7 @@ func (m *userService) fillSearchArgs(arg *sqlc.SearchUsersParams, dto *request.S } } -func (u *userService) SearchUser(ctx context.Context, dto *request.SearchUserDto) (*response.PaginatedResponse, error) { +func (u *userService) SearchUser(ctx context.Context, dto *request.SearchUserDto) (*response.PaginatedResponse, *fiber.Error) { if dto.Page < 1 { dto.Page = 1 } @@ -404,7 +479,7 @@ func (u *userService) SearchUser(ctx context.Context, dto *request.SearchUserDto }) if err := g.Wait(); err != nil { - return nil, err + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to search users") } users := models.UsersEntityToResponse(rows) @@ -412,14 +487,17 @@ func (u *userService) SearchUser(ctx context.Context, dto *request.SearchUserDto return response.BuildPaginatedResponse(users, totalRecords, dto.Page, dto.Limit), nil } -func (u *userService) GetUserByID(ctx context.Context, userId string) (*response.UserResponse, error) { +func (u *userService) GetUserByID(ctx context.Context, userId string) (*response.UserResponse, *fiber.Error) { pgID, err := convert.StringToUUID(userId) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Invalid user ID") } user, err := u.userRepo.GetByID(ctx, pgID) if err != nil { - return nil, fiber.NewError(fiber.StatusNotFound, err.Error()) + return nil, fiber.NewError(fiber.StatusNotFound, "Failed to fetch user") + } + if user == nil { + return nil, fiber.NewError(fiber.StatusNotFound, "User not found") } return user.ToResponse(), nil } diff --git a/internal/services/verificationService.go b/internal/services/verificationService.go index d26aaed..09d8152 100644 --- a/internal/services/verificationService.go +++ b/internal/services/verificationService.go @@ -20,12 +20,12 @@ import ( ) type VerificationService interface { - GetVerificationByID(ctx context.Context, verificationId string) (*response.UserVerificationResponse, error) - GetVerificationByUserID(ctx context.Context, userId string) ([]*response.UserVerificationResponse, error) - SearchVerification(ctx context.Context, dto *request.SearchUserVerificationDto) (*response.PaginatedResponse, error) - DeleteVerification(ctx context.Context, claims *response.JWTClaims, verificationId string) error - CreateVerification(ctx context.Context, userId string, dto *request.CreateUserVerificationDto) (*response.UserVerificationResponse, error) - UpdateStatusVerification(ctx context.Context, userId string, verificationId string, dto *request.UpdateVerificationStatusDto) (*response.UserVerificationResponse, error) + GetVerificationByID(ctx context.Context, verificationId string) (*response.UserVerificationResponse, *fiber.Error) + GetVerificationByUserID(ctx context.Context, userId string) ([]*response.UserVerificationResponse, *fiber.Error) + SearchVerification(ctx context.Context, dto *request.SearchUserVerificationDto) (*response.PaginatedResponse, *fiber.Error) + DeleteVerification(ctx context.Context, claims *response.JWTClaims, verificationId string) *fiber.Error + CreateVerification(ctx context.Context, userId string, dto *request.CreateUserVerificationDto) (*response.UserVerificationResponse, *fiber.Error) + UpdateStatusVerification(ctx context.Context, userId string, verificationId string, dto *request.UpdateVerificationStatusDto) (*response.UserVerificationResponse, *fiber.Error) } type verificationService struct { @@ -55,24 +55,24 @@ func NewVerificationService( } } -func (v *verificationService) CreateVerification(ctx context.Context, userId string, dto *request.CreateUserVerificationDto) (*response.UserVerificationResponse, error) { +func (v *verificationService) CreateVerification(ctx context.Context, userId string, dto *request.CreateUserVerificationDto) (*response.UserVerificationResponse, *fiber.Error) { verifyType := constants.ParseVerifyTypeText(dto.VerifyType) if verifyType == constants.VerifyTypeUnknown { - return nil, fiber.NewError(fiber.StatusInternalServerError, "Unknown verify type!") + return nil, fiber.NewError(fiber.StatusBadRequest, "Unknown verify type!") } pgID, err := convert.StringToUUID(userId) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid user ID format") } mediaList, err := v.mediaRepo.GetByIDs(ctx, dto.MediaIDs) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch media") } if len(mediaList) != len(dto.MediaIDs) { - return nil, fiber.NewError(fiber.StatusInternalServerError, "Some media IDs are invalid!") + return nil, fiber.NewError(fiber.StatusBadRequest, "Some media IDs are invalid!") } item, err := v.verificationRepo.Create( @@ -84,19 +84,19 @@ func (v *verificationService) CreateVerification(ctx context.Context, userId str }, ) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to create verification") } itemId, err := convert.StringToUUID(item.ID) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Invalid verification ID") } mediaIdList := make([]pgtype.UUID, 0) for _, it := range mediaList { mediaId, err := convert.StringToUUID(it.ID) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + continue } mediaIdList = append(mediaIdList, mediaId) item.Media = append(item.Media, it.ToSimpleEntity()) @@ -110,21 +110,21 @@ func (v *verificationService) CreateVerification(ctx context.Context, userId str }, ) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to link media to verification") } return item.ToResponse(), nil } -func (v *verificationService) DeleteVerification(ctx context.Context, claims *response.JWTClaims, verificationId string) error { +func (v *verificationService) DeleteVerification(ctx context.Context, claims *response.JWTClaims, verificationId string) *fiber.Error { verificationIdUUID, err := convert.StringToUUID(verificationId) if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return fiber.NewError(fiber.StatusBadRequest, "Invalid verification ID format") } verification, err := v.verificationRepo.GetByID(ctx, verificationIdUUID) if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return fiber.NewError(fiber.StatusNotFound, "Verification not found") } shoudDelete := false @@ -142,32 +142,32 @@ func (v *verificationService) DeleteVerification(ctx context.Context, claims *re err = v.verificationRepo.Delete(ctx, verificationIdUUID) if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return fiber.NewError(fiber.StatusInternalServerError, "Failed to delete verification") } return nil } -func (v *verificationService) GetVerificationByID(ctx context.Context, verificationId string) (*response.UserVerificationResponse, error) { +func (v *verificationService) GetVerificationByID(ctx context.Context, verificationId string) (*response.UserVerificationResponse, *fiber.Error) { verificationUUID, err := convert.StringToUUID(verificationId) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid verification ID format") } verification, err := v.verificationRepo.GetByID(ctx, verificationUUID) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusNotFound, "Verification not found") } return verification.ToResponse(), nil } -func (v *verificationService) GetVerificationByUserID(ctx context.Context, userId string) ([]*response.UserVerificationResponse, error) { +func (v *verificationService) GetVerificationByUserID(ctx context.Context, userId string) ([]*response.UserVerificationResponse, *fiber.Error) { userUUID, err := convert.StringToUUID(userId) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid user ID format") } verifications, err := v.verificationRepo.GetByUserID(ctx, userUUID) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch user verifications") } return models.UserVerificationsEntitiesToResponse(verifications), nil } @@ -227,7 +227,7 @@ func (m *verificationService) fillSearchArgs(arg *sqlc.SearchUserVerificationsPa } } -func (v *verificationService) SearchVerification(ctx context.Context, dto *request.SearchUserVerificationDto) (*response.PaginatedResponse, error) { +func (v *verificationService) SearchVerification(ctx context.Context, dto *request.SearchUserVerificationDto) (*response.PaginatedResponse, *fiber.Error) { if dto.Page < 1 { dto.Page = 1 } @@ -270,7 +270,7 @@ func (v *verificationService) SearchVerification(ctx context.Context, dto *reque }) if err := g.Wait(); err != nil { - return nil, err + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to search verifications") } verifications := models.UserVerificationsEntitiesToResponse(rows) @@ -278,7 +278,7 @@ func (v *verificationService) SearchVerification(ctx context.Context, dto *reque return response.BuildPaginatedResponse(verifications, totalRecords, dto.Page, dto.Limit), nil } -func (v *verificationService) UpdateStatusVerification(ctx context.Context, userId string, verificationId string, dto *request.UpdateVerificationStatusDto) (*response.UserVerificationResponse, error) { +func (v *verificationService) UpdateStatusVerification(ctx context.Context, userId string, verificationId string, dto *request.UpdateVerificationStatusDto) (*response.UserVerificationResponse, *fiber.Error) { tx, err := v.db.Begin(ctx) if err != nil { return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to start transaction") @@ -287,45 +287,45 @@ func (v *verificationService) UpdateStatusVerification(ctx context.Context, user statusType := constants.ParseStatusTypeText(dto.Status) if statusType == constants.StatusTypeUnknown { - return nil, fiber.NewError(fiber.StatusInternalServerError, "Unknown status type!") + return nil, fiber.NewError(fiber.StatusBadRequest, "Unknown status type!") } verificationUUID, err := convert.StringToUUID(verificationId) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid verification ID format") } userAdminUUID, err := convert.StringToUUID(userId) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid reviewer ID format") } historianRole, err := v.roleRepo.GetByName(ctx, constants.RoleTypeHistorian.String()) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch historian role") } historianRoleID, err := convert.StringToUUID(historianRole.ID) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Invalid historian role ID") } verification, err := v.verificationRepo.GetByID(ctx, verificationUUID) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusNotFound, "Verification not found") } if verification.Status != constants.StatusTypePending { - return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid status!") + return nil, fiber.NewError(fiber.StatusBadRequest, "Verification already processed!") } userVerificationUUID, err := convert.StringToUUID(verification.User.ID) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Invalid user ID in verification") } userVerification, err := v.userRepo.GetByID(ctx, userVerificationUUID) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch user data") } vRepoTx := v.verificationRepo.WithTx(tx) @@ -342,7 +342,7 @@ func (v *verificationService) UpdateStatusVerification(ctx context.Context, user }, ) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to update status") } verification.Status = statusType @@ -370,7 +370,7 @@ func (v *verificationService) UpdateStatusVerification(ctx context.Context, user err = rRepoTx.BulkDeleteRolesFromUser(ctx, userVerificationUUID) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to clear old roles") } err = rRepoTx.CreateUserRole(ctx, sqlc.CreateUserRoleParams{ @@ -378,7 +378,7 @@ func (v *verificationService) UpdateStatusVerification(ctx context.Context, user Column2: roleIdList, }) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to create new roles") } err = uRepoTx.UpdateTokenVersion(ctx, sqlc.UpdateTokenVersionParams{ @@ -386,7 +386,7 @@ func (v *verificationService) UpdateStatusVerification(ctx context.Context, user TokenVersion: userVerification.TokenVersion + 1, }) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to update token version") } userVerification.TokenVersion += 1 diff --git a/internal/services/wikiService.go b/internal/services/wikiService.go index fc49f17..bd19d89 100644 --- a/internal/services/wikiService.go +++ b/internal/services/wikiService.go @@ -13,8 +13,8 @@ import ( ) type WikiService interface { - GetWikiByID(ctx context.Context, id string) (*response.WikiResponse, error) - SearchWikis(ctx context.Context, req *request.SearchWikiDto) ([]*response.WikiResponse, error) + GetWikiByID(ctx context.Context, id string) (*response.WikiResponse, *fiber.Error) + SearchWikis(ctx context.Context, req *request.SearchWikiDto) ([]*response.WikiResponse, *fiber.Error) } type wikiService struct { @@ -27,10 +27,10 @@ func NewWikiService(wikiRepo repositories.WikiRepository) WikiService { } } -func (s *wikiService) GetWikiByID(ctx context.Context, id string) (*response.WikiResponse, error) { +func (s *wikiService) GetWikiByID(ctx context.Context, id string) (*response.WikiResponse, *fiber.Error) { wikiId, err := convert.StringToUUID(id) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid wiki ID format") } wiki, err := s.wikiRepo.GetByID(ctx, wikiId) if err != nil { @@ -40,7 +40,7 @@ func (s *wikiService) GetWikiByID(ctx context.Context, id string) (*response.Wik return wiki.ToResponse(), nil } -func (s *wikiService) SearchWikis(ctx context.Context, req *request.SearchWikiDto) ([]*response.WikiResponse, error) { +func (s *wikiService) SearchWikis(ctx context.Context, req *request.SearchWikiDto) ([]*response.WikiResponse, *fiber.Error) { limit := int32(25) if req.Limit > 0 { limit = int32(req.Limit) @@ -67,7 +67,7 @@ func (s *wikiService) SearchWikis(ctx context.Context, req *request.SearchWikiDt wikis, err := s.wikiRepo.Search(ctx, params) if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error()) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to search wikis") } return models.WikisEntityToResponse(wikis), nil