From 8e9afcf6aae88c0bd131099fca1c91dedb836a2e Mon Sep 17 00:00:00 2001 From: Jeremy Mouton Date: Sun, 15 Mar 2026 16:39:30 +0100 Subject: [PATCH] Refactor error handling in document, drawing, space, user, and webhook handlers to use typed errors; update DTOs to use 'any' type for elements and app state; clean up unused code and comments. --- application/action/delete_action.go | 3 +- application/action/dto/common.go | 4 +- application/action/execute_actions.go | 8 +- application/action/get_action.go | 5 +- application/action/get_runs.go | 3 +- application/action/update_action.go | 3 +- application/apikey/delete_apikey.go | 3 +- application/apikey/list_apikeys.go | 2 +- application/apikey/update_apikey.go | 3 +- application/apikey/validate_apikey.go | 2 +- application/auth/authenticate.go | 5 +- application/database/bulk_delete_rows.go | 3 +- application/database/create_database.go | 3 +- application/database/create_row.go | 3 +- application/database/create_view.go | 3 +- application/database/delete_database.go | 3 +- application/database/delete_row.go | 5 +- application/database/delete_view.go | 3 +- application/database/dto/common.go | 28 ++-- application/database/dto/create_row.go | 6 +- application/database/dto/create_view.go | 4 +- application/database/dto/get_row.go | 4 +- application/database/dto/update_row.go | 4 +- application/database/dto/update_view.go | 2 +- application/database/get_database.go | 3 +- application/database/get_row.go | 9 +- application/database/list_databases.go | 3 +- application/database/list_rows.go | 19 +-- application/database/move_database.go | 3 +- application/database/search.go | 3 +- application/database/update_database.go | 3 +- application/database/update_row.go | 5 +- application/database/update_view.go | 3 +- application/document/comment.go | 5 +- application/document/create.go | 5 +- application/document/get.go | 5 +- application/document/public.go | 5 +- application/document/search.go | 3 +- application/document/update.go | 3 +- application/document/version.go | 5 +- application/drawing/drawing.go | 19 +-- application/drawing/dto/create_drawing.go | 6 +- application/drawing/dto/get_drawing.go | 6 +- application/drawing/dto/update_drawing.go | 6 +- application/favorite/dto/common.go | 8 +- .../permission/delete_database_permission.go | 3 +- .../delete_document_user_permission.go | 5 +- .../delete_drawing_user_permission.go | 5 +- .../delete_space_group_permission.go | 3 +- .../delete_space_user_permission.go | 5 +- .../permission/list_database_permissions.go | 3 +- .../permission/list_document_permissions.go | 7 +- .../permission/list_drawing_permissions.go | 5 +- .../permission/list_space_permissions.go | 7 +- .../permission/upsert_database_permission.go | 3 +- .../upsert_document_user_permission.go | 5 +- .../upsert_drawing_user_permission.go | 5 +- .../upsert_space_group_permission.go | 3 +- .../upsert_space_user_permission.go | 5 +- application/session/validate.go | 9 +- application/space/admin.go | 1 - application/space/delete.go | 9 +- application/space/update.go | 7 +- application/user/update.go | 7 +- application/webhook/delete_webhook.go | 3 +- application/webhook/dto/trigger_webhook.go | 2 +- application/webhook/get_deliveries.go | 3 +- application/webhook/get_webhook.go | 5 +- application/webhook/list_webhooks.go | 2 +- application/webhook/trigger_webhooks.go | 6 +- application/webhook/update_webhook.go | 3 +- domain/api_key.go | 2 +- domain/database.go | 2 +- domain/webhook.go | 2 +- infrastructure/helpers/apperrors/errors.go | 41 ++++++ .../mapper/map_struct_by_field_names.go | 2 +- infrastructure/helpers/tokenutil/tokenutil.go | 2 +- infrastructure/persistence/action_pers.go | 9 +- infrastructure/persistence/api_key_pers.go | 8 + infrastructure/persistence/comment_pers.go | 5 + infrastructure/persistence/database_pers.go | 13 +- infrastructure/persistence/document_pers.go | 37 +++-- .../persistence/document_version_pers.go | 7 +- infrastructure/persistence/drawing_pers.go | 6 + infrastructure/persistence/favorite_pers.go | 6 + infrastructure/persistence/group_pers.go | 8 +- infrastructure/persistence/permission_pers.go | 9 ++ infrastructure/persistence/session_pers.go | 6 + infrastructure/persistence/space_pers.go | 6 + infrastructure/persistence/user_pers.go | 14 +- infrastructure/persistence/webhook_pers.go | 9 +- interfaces/http/v1/action/dtos/action.go | 66 ++++----- interfaces/http/v1/action/handlers.go | 19 +-- interfaces/http/v1/admin/handlers.go | 2 +- interfaces/http/v1/apikey/handlers.go | 11 +- interfaces/http/v1/auth/handlers.go | 16 +- interfaces/http/v1/database/dtos/database.go | 138 +++++++++--------- interfaces/http/v1/database/handlers.go | 61 ++++---- .../http/v1/database/handlers_permissions.go | 46 +++--- .../v1/document/dtos/move_document_request.go | 4 - interfaces/http/v1/document/handlers.go | 74 +++++----- interfaces/http/v1/drawing/dtos/drawing.go | 54 +++---- interfaces/http/v1/drawing/handlers.go | 41 +++--- .../v1/space/dtos/create_space_request.go | 14 +- interfaces/http/v1/space/dtos/permissions.go | 34 ++--- interfaces/http/v1/space/handlers.go | 33 +++-- .../dtos/update_favorite_position_request.go | 12 +- .../v1/user/dtos/update_profile_request.go | 6 +- interfaces/http/v1/user/handlers.go | 7 +- interfaces/http/v1/webhook/handlers.go | 19 +-- 110 files changed, 700 insertions(+), 511 deletions(-) create mode 100644 infrastructure/helpers/apperrors/errors.go diff --git a/application/action/delete_action.go b/application/action/delete_action.go index 1ee60a0..63981e5 100644 --- a/application/action/delete_action.go +++ b/application/action/delete_action.go @@ -3,6 +3,7 @@ package action import ( "fmt" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/action/dto" ) @@ -13,7 +14,7 @@ func (app *ActionApplication) DeleteAction(input dto.DeleteActionInput) error { } if action.UserId != input.UserId { - return fmt.Errorf("access denied") + return apperrors.ErrAccessDenied } if err := app.ActionPers.Delete(input.ActionId); err != nil { diff --git a/application/action/dto/common.go b/application/action/dto/common.go index 35ab881..4d7ceab 100644 --- a/application/action/dto/common.go +++ b/application/action/dto/common.go @@ -4,8 +4,8 @@ import "time" // Step configuration type ActionStep struct { - Type string `json:"type"` - Config map[string]interface{} `json:"config"` + Type string `json:"type"` + Config map[string]any `json:"config"` } type ActionItem struct { diff --git a/application/action/execute_actions.go b/application/action/execute_actions.go index a6d7df7..fb2313c 100644 --- a/application/action/execute_actions.go +++ b/application/action/execute_actions.go @@ -25,7 +25,7 @@ func (app *ActionApplication) ExecuteActions(input dto.ExecuteActionInput) { } } -func (app *ActionApplication) executeAction(action domain.Action, triggerData map[string]interface{}) { +func (app *ActionApplication) executeAction(action domain.Action, triggerData map[string]any) { logger := app.Logger.With(). Str("component", "action.execute"). Str("action_id", action.Id). @@ -45,12 +45,12 @@ func (app *ActionApplication) executeAction(action domain.Action, triggerData ma } // Execute each step - stepsResult := make([]map[string]interface{}, len(steps)) + stepsResult := make([]map[string]any, len(steps)) var execError error for i, step := range steps { result, err := app.executeStep(step, triggerData) - stepsResult[i] = map[string]interface{}{ + stepsResult[i] = map[string]any{ "step": i + 1, "type": step.Type, "success": err == nil, @@ -90,7 +90,7 @@ func (app *ActionApplication) executeAction(action domain.Action, triggerData ma } } -func (app *ActionApplication) executeStep(step dto.ActionStep, triggerData map[string]interface{}) (interface{}, error) { +func (app *ActionApplication) executeStep(step dto.ActionStep, triggerData map[string]any) (any, error) { // This is a simplified implementation - in production, you'd have actual step executors switch domain.ActionStepType(step.Type) { case domain.StepSendWebhook: diff --git a/application/action/get_action.go b/application/action/get_action.go index b5d092c..b2490ce 100644 --- a/application/action/get_action.go +++ b/application/action/get_action.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/action/dto" ) @@ -14,7 +15,7 @@ func (app *ActionApplication) GetAction(input dto.GetActionInput) (*dto.GetActio } if action.UserId != input.UserId { - return nil, fmt.Errorf("access denied") + return nil, apperrors.ErrAccessDenied } // Parse steps @@ -31,7 +32,7 @@ func (app *ActionApplication) GetAction(input dto.GetActionInput) (*dto.GetActio SpaceId: action.SpaceId, DatabaseId: action.DatabaseId, TriggerType: string(action.TriggerType), - TriggerConfig: map[string]interface{}(action.TriggerConfig), + TriggerConfig: map[string]any(action.TriggerConfig), Steps: steps, Active: action.Active, LastRunAt: action.LastRunAt, diff --git a/application/action/get_runs.go b/application/action/get_runs.go index cda14d4..fd08e7a 100644 --- a/application/action/get_runs.go +++ b/application/action/get_runs.go @@ -3,6 +3,7 @@ package action import ( "fmt" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/action/dto" ) @@ -14,7 +15,7 @@ func (app *ActionApplication) GetRuns(input dto.GetRunsInput) (*dto.GetRunsOutpu } if action.UserId != input.UserId { - return nil, fmt.Errorf("access denied") + return nil, apperrors.ErrAccessDenied } limit := input.Limit diff --git a/application/action/update_action.go b/application/action/update_action.go index 3e68d02..0d005a3 100644 --- a/application/action/update_action.go +++ b/application/action/update_action.go @@ -5,6 +5,7 @@ import ( "fmt" "time" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/action/dto" "github.com/labbs/nexo/domain" ) @@ -16,7 +17,7 @@ func (app *ActionApplication) UpdateAction(input dto.UpdateActionInput) error { } if action.UserId != input.UserId { - return fmt.Errorf("access denied") + return apperrors.ErrAccessDenied } if input.Name != nil { diff --git a/application/apikey/delete_apikey.go b/application/apikey/delete_apikey.go index 20380cb..fa379c8 100644 --- a/application/apikey/delete_apikey.go +++ b/application/apikey/delete_apikey.go @@ -3,6 +3,7 @@ package apikey import ( "fmt" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/apikey/dto" ) @@ -14,7 +15,7 @@ func (app *ApiKeyApplication) DeleteApiKey(input dto.DeleteApiKeyInput) error { // Verify ownership if apiKey.UserId != input.UserId { - return fmt.Errorf("access denied") + return apperrors.ErrAccessDenied } if err := app.ApiKeyPers.Delete(input.ApiKeyId); err != nil { diff --git a/application/apikey/list_apikeys.go b/application/apikey/list_apikeys.go index a97611f..c487bfa 100644 --- a/application/apikey/list_apikeys.go +++ b/application/apikey/list_apikeys.go @@ -19,7 +19,7 @@ func (app *ApiKeyApplication) ListApiKeys(input dto.ListApiKeysInput) (*dto.List for i, k := range apiKeys { var scopes []string if k.Permissions != nil { - if s, ok := k.Permissions["scopes"].([]interface{}); ok { + if s, ok := k.Permissions["scopes"].([]any); ok { for _, scope := range s { if str, ok := scope.(string); ok { scopes = append(scopes, str) diff --git a/application/apikey/update_apikey.go b/application/apikey/update_apikey.go index 4bec255..d8e2592 100644 --- a/application/apikey/update_apikey.go +++ b/application/apikey/update_apikey.go @@ -4,6 +4,7 @@ import ( "fmt" "time" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/apikey/dto" "github.com/labbs/nexo/domain" ) @@ -16,7 +17,7 @@ func (app *ApiKeyApplication) UpdateApiKey(input dto.UpdateApiKeyInput) error { // Verify ownership if apiKey.UserId != input.UserId { - return fmt.Errorf("access denied") + return apperrors.ErrAccessDenied } if input.Name != nil { diff --git a/application/apikey/validate_apikey.go b/application/apikey/validate_apikey.go index bcd268e..8850ef1 100644 --- a/application/apikey/validate_apikey.go +++ b/application/apikey/validate_apikey.go @@ -27,7 +27,7 @@ func (app *ApiKeyApplication) ValidateApiKey(input dto.ValidateApiKeyInput) (*dt // Extract scopes var scopes []string if apiKey.Permissions != nil { - if s, ok := apiKey.Permissions["scopes"].([]interface{}); ok { + if s, ok := apiKey.Permissions["scopes"].([]any); ok { for _, scope := range s { if str, ok := scope.(string); ok { scopes = append(scopes, str) diff --git a/application/auth/authenticate.go b/application/auth/authenticate.go index 994b376..b70ca9d 100644 --- a/application/auth/authenticate.go +++ b/application/auth/authenticate.go @@ -4,6 +4,7 @@ import ( "fmt" "time" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/auth/dto" s "github.com/labbs/nexo/application/session/dto" u "github.com/labbs/nexo/application/user/dto" @@ -21,13 +22,13 @@ func (c *AuthApplication) Authenticate(input dto.AuthenticateInput) (*dto.Authen if !resp.User.Active { logger.Warn().Str("email", input.Email).Msg("attempt to authenticate inactive user") - return nil, fmt.Errorf("user is not active") + return nil, apperrors.ErrUserNotActive } err = bcrypt.CompareHashAndPassword([]byte(resp.User.Password), []byte(input.Password)) if err != nil { logger.Warn().Str("email", input.Email).Msg("invalid password attempt") - return nil, fmt.Errorf("invalid credentials") + return nil, apperrors.ErrInvalidCredentials } sessionResult, err := c.SessionApplication.Create(s.CreateSessionInput{ diff --git a/application/database/bulk_delete_rows.go b/application/database/bulk_delete_rows.go index b641c3f..dec36ca 100644 --- a/application/database/bulk_delete_rows.go +++ b/application/database/bulk_delete_rows.go @@ -3,6 +3,7 @@ package database import ( "fmt" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/database/dto" spaceDto "github.com/labbs/nexo/application/space/dto" ) @@ -20,7 +21,7 @@ func (app *DatabaseApplication) BulkDeleteRows(input dto.BulkDeleteRowsInput) er } if spaceResult.Space.GetUserRole(input.UserId) == nil { - return fmt.Errorf("access denied") + return apperrors.ErrAccessDenied } if err := app.DatabaseRowPers.BulkDelete(input.RowIds); err != nil { diff --git a/application/database/create_database.go b/application/database/create_database.go index fd773da..a2c83ad 100644 --- a/application/database/create_database.go +++ b/application/database/create_database.go @@ -6,6 +6,7 @@ import ( "time" "github.com/google/uuid" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/database/dto" permissionDto "github.com/labbs/nexo/application/permission/dto" spaceDto "github.com/labbs/nexo/application/space/dto" @@ -20,7 +21,7 @@ func (app *DatabaseApplication) CreateDatabase(input dto.CreateDatabaseInput) (* } if spaceResult.Space.GetUserRole(input.UserId) == nil { - return nil, fmt.Errorf("access denied") + return nil, apperrors.ErrAccessDenied } // Determine database type (default to spreadsheet) diff --git a/application/database/create_row.go b/application/database/create_row.go index 7cd8e4e..56802da 100644 --- a/application/database/create_row.go +++ b/application/database/create_row.go @@ -5,6 +5,7 @@ import ( "time" "github.com/google/uuid" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/database/dto" spaceDto "github.com/labbs/nexo/application/space/dto" "github.com/labbs/nexo/domain" @@ -23,7 +24,7 @@ func (app *DatabaseApplication) CreateRow(input dto.CreateRowInput) (*dto.Create } if spaceResult.Space.GetUserRole(input.UserId) == nil { - return nil, fmt.Errorf("access denied") + return nil, apperrors.ErrAccessDenied } row := &domain.DatabaseRow{ diff --git a/application/database/create_view.go b/application/database/create_view.go index 23cf5e5..6b526aa 100644 --- a/application/database/create_view.go +++ b/application/database/create_view.go @@ -6,6 +6,7 @@ import ( "time" "github.com/google/uuid" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/database/dto" spaceDto "github.com/labbs/nexo/application/space/dto" "github.com/labbs/nexo/domain" @@ -24,7 +25,7 @@ func (app *DatabaseApplication) CreateView(input dto.CreateViewInput) (*dto.Crea } if spaceResult.Space.GetUserRole(input.UserId) == nil { - return nil, fmt.Errorf("access denied") + return nil, apperrors.ErrAccessDenied } // Parse existing views diff --git a/application/database/delete_database.go b/application/database/delete_database.go index da615da..7925b7a 100644 --- a/application/database/delete_database.go +++ b/application/database/delete_database.go @@ -3,6 +3,7 @@ package database import ( "fmt" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/database/dto" spaceDto "github.com/labbs/nexo/application/space/dto" ) @@ -20,7 +21,7 @@ func (app *DatabaseApplication) DeleteDatabase(input dto.DeleteDatabaseInput) er } if spaceResult.Space.GetUserRole(input.UserId) == nil { - return fmt.Errorf("access denied") + return apperrors.ErrAccessDenied } if err := app.DatabasePers.Delete(input.DatabaseId); err != nil { diff --git a/application/database/delete_row.go b/application/database/delete_row.go index a7f9c0c..bd024b0 100644 --- a/application/database/delete_row.go +++ b/application/database/delete_row.go @@ -3,6 +3,7 @@ package database import ( "fmt" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/database/dto" spaceDto "github.com/labbs/nexo/application/space/dto" ) @@ -14,7 +15,7 @@ func (app *DatabaseApplication) DeleteRow(input dto.DeleteRowInput) error { } if row.DatabaseId != input.DatabaseId { - return fmt.Errorf("row not found in this database") + return apperrors.ErrRowNotFound } database, err := app.DatabasePers.GetById(input.DatabaseId) @@ -29,7 +30,7 @@ func (app *DatabaseApplication) DeleteRow(input dto.DeleteRowInput) error { } if spaceResult.Space.GetUserRole(input.UserId) == nil { - return fmt.Errorf("access denied") + return apperrors.ErrAccessDenied } if err := app.DatabaseRowPers.Delete(input.RowId); err != nil { diff --git a/application/database/delete_view.go b/application/database/delete_view.go index 84f2a96..9065321 100644 --- a/application/database/delete_view.go +++ b/application/database/delete_view.go @@ -5,6 +5,7 @@ import ( "fmt" "time" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/database/dto" spaceDto "github.com/labbs/nexo/application/space/dto" "github.com/labbs/nexo/domain" @@ -23,7 +24,7 @@ func (app *DatabaseApplication) DeleteView(input dto.DeleteViewInput) error { } if spaceResult.Space.GetUserRole(input.UserId) == nil { - return fmt.Errorf("access denied") + return apperrors.ErrAccessDenied } // Parse existing views diff --git a/application/database/dto/common.go b/application/database/dto/common.go index d9a0a3f..5db0d11 100644 --- a/application/database/dto/common.go +++ b/application/database/dto/common.go @@ -11,22 +11,22 @@ type UserInfo struct { // Property schema type PropertySchema struct { - Id string `json:"id"` - Name string `json:"name"` - Type string `json:"type"` - Options map[string]interface{} `json:"options,omitempty"` + Id string `json:"id"` + Name string `json:"name"` + Type string `json:"type"` + Options map[string]any `json:"options,omitempty"` } // View configuration type ViewConfig struct { - Id string `json:"id"` - Name string `json:"name"` - Type string `json:"type"` - Filter map[string]interface{} `json:"filter,omitempty"` - Sort []SortConfig `json:"sort,omitempty"` - Columns []string `json:"columns,omitempty"` - HiddenColumns []string `json:"hidden_columns,omitempty"` - GroupBy string `json:"group_by,omitempty"` + Id string `json:"id"` + Name string `json:"name"` + Type string `json:"type"` + Filter map[string]any `json:"filter,omitempty"` + Sort []SortConfig `json:"sort,omitempty"` + Columns []string `json:"columns,omitempty"` + HiddenColumns []string `json:"hidden_columns,omitempty"` + GroupBy string `json:"group_by,omitempty"` } type SortConfig struct { @@ -49,8 +49,8 @@ type DatabaseItem struct { type RowItem struct { Id string - Properties map[string]interface{} - Content map[string]interface{} + Properties map[string]any + Content map[string]any ShowInSidebar bool CreatedBy string CreatedByUser *UserInfo diff --git a/application/database/dto/create_row.go b/application/database/dto/create_row.go index c2aefee..cec54d0 100644 --- a/application/database/dto/create_row.go +++ b/application/database/dto/create_row.go @@ -5,13 +5,13 @@ import "time" type CreateRowInput struct { UserId string DatabaseId string - Properties map[string]interface{} - Content map[string]interface{} + Properties map[string]any + Content map[string]any ShowInSidebar bool } type CreateRowOutput struct { Id string - Properties map[string]interface{} + Properties map[string]any CreatedAt time.Time } diff --git a/application/database/dto/create_view.go b/application/database/dto/create_view.go index 747dd56..e86ea8c 100644 --- a/application/database/dto/create_view.go +++ b/application/database/dto/create_view.go @@ -5,7 +5,7 @@ type CreateViewInput struct { DatabaseId string Name string Type string - Filter map[string]interface{} + Filter map[string]any Sort []SortConfig Columns []string } @@ -14,7 +14,7 @@ type CreateViewOutput struct { Id string Name string Type string - Filter map[string]interface{} + Filter map[string]any Sort []SortConfig Columns []string } diff --git a/application/database/dto/get_row.go b/application/database/dto/get_row.go index ae941be..d3594c0 100644 --- a/application/database/dto/get_row.go +++ b/application/database/dto/get_row.go @@ -11,8 +11,8 @@ type GetRowInput struct { type GetRowOutput struct { Id string DatabaseId string - Properties map[string]interface{} - Content map[string]interface{} + Properties map[string]any + Content map[string]any ShowInSidebar bool CreatedBy string CreatedByUser *UserInfo diff --git a/application/database/dto/update_row.go b/application/database/dto/update_row.go index 2e4a610..1dd98d6 100644 --- a/application/database/dto/update_row.go +++ b/application/database/dto/update_row.go @@ -4,7 +4,7 @@ type UpdateRowInput struct { UserId string DatabaseId string RowId string - Properties map[string]interface{} - Content map[string]interface{} + Properties map[string]any + Content map[string]any ShowInSidebar *bool } diff --git a/application/database/dto/update_view.go b/application/database/dto/update_view.go index 0a3e8fc..d1ea33f 100644 --- a/application/database/dto/update_view.go +++ b/application/database/dto/update_view.go @@ -6,7 +6,7 @@ type UpdateViewInput struct { ViewId string Name *string Type *string - Filter map[string]interface{} + Filter map[string]any Sort []SortConfig Columns []string HiddenColumns []string diff --git a/application/database/get_database.go b/application/database/get_database.go index d9f544d..5c4b779 100644 --- a/application/database/get_database.go +++ b/application/database/get_database.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/database/dto" spaceDto "github.com/labbs/nexo/application/space/dto" ) @@ -21,7 +22,7 @@ func (app *DatabaseApplication) GetDatabase(input dto.GetDatabaseInput) (*dto.Ge } if spaceResult.Space.GetUserRole(input.UserId) == nil { - return nil, fmt.Errorf("access denied") + return nil, apperrors.ErrAccessDenied } // Parse schema diff --git a/application/database/get_row.go b/application/database/get_row.go index 296f0d7..3bab14e 100644 --- a/application/database/get_row.go +++ b/application/database/get_row.go @@ -3,6 +3,7 @@ package database import ( "fmt" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/database/dto" spaceDto "github.com/labbs/nexo/application/space/dto" ) @@ -14,7 +15,7 @@ func (app *DatabaseApplication) GetRow(input dto.GetRowInput) (*dto.GetRowOutput } if row.DatabaseId != input.DatabaseId { - return nil, fmt.Errorf("row not found in this database") + return nil, apperrors.ErrRowNotFound } database, err := app.DatabasePers.GetById(input.DatabaseId) @@ -29,14 +30,14 @@ func (app *DatabaseApplication) GetRow(input dto.GetRowInput) (*dto.GetRowOutput } if spaceResult.Space.GetUserRole(input.UserId) == nil { - return nil, fmt.Errorf("access denied") + return nil, apperrors.ErrAccessDenied } output := &dto.GetRowOutput{ Id: row.Id, DatabaseId: row.DatabaseId, - Properties: map[string]interface{}(row.Properties), - Content: map[string]interface{}(row.Content), + Properties: map[string]any(row.Properties), + Content: map[string]any(row.Content), ShowInSidebar: row.ShowInSidebar, CreatedBy: row.CreatedBy, UpdatedBy: row.UpdatedBy, diff --git a/application/database/list_databases.go b/application/database/list_databases.go index f9c3427..8182c38 100644 --- a/application/database/list_databases.go +++ b/application/database/list_databases.go @@ -3,6 +3,7 @@ package database import ( "fmt" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/database/dto" spaceDto "github.com/labbs/nexo/application/space/dto" ) @@ -15,7 +16,7 @@ func (app *DatabaseApplication) ListDatabases(input dto.ListDatabasesInput) (*dt } if spaceResult.Space.GetUserRole(input.UserId) == nil { - return nil, fmt.Errorf("access denied") + return nil, apperrors.ErrAccessDenied } databases, err := app.DatabasePers.GetBySpaceId(input.SpaceId) diff --git a/application/database/list_rows.go b/application/database/list_rows.go index d89ab20..c77ece0 100644 --- a/application/database/list_rows.go +++ b/application/database/list_rows.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/database/dto" spaceDto "github.com/labbs/nexo/application/space/dto" "github.com/labbs/nexo/domain" @@ -22,7 +23,7 @@ func (app *DatabaseApplication) ListRows(input dto.ListRowsInput) (*dto.ListRows } if spaceResult.Space.GetUserRole(input.UserId) == nil { - return nil, fmt.Errorf("access denied") + return nil, apperrors.ErrAccessDenied } limit := input.Limit @@ -97,8 +98,8 @@ func (app *DatabaseApplication) ListRows(input dto.ListRowsInput) (*dto.ListRows for i, row := range rows { rowItem := dto.RowItem{ Id: row.Id, - Properties: map[string]interface{}(row.Properties), - Content: map[string]interface{}(row.Content), + Properties: map[string]any(row.Properties), + Content: map[string]any(row.Content), ShowInSidebar: row.ShowInSidebar, CreatedBy: row.CreatedBy, UpdatedBy: row.UpdatedBy, @@ -126,7 +127,7 @@ func (app *DatabaseApplication) ListRows(input dto.ListRowsInput) (*dto.ListRows } // convertFilterConfigToDomain converts the DTO filter config to domain filter config -func convertFilterConfigToDomain(filter map[string]interface{}) *domain.FilterConfig { +func convertFilterConfigToDomain(filter map[string]any) *domain.FilterConfig { if filter == nil { return nil } @@ -134,9 +135,9 @@ func convertFilterConfigToDomain(filter map[string]interface{}) *domain.FilterCo result := &domain.FilterConfig{} // Handle "and" filters - if andRules, ok := filter["and"].([]interface{}); ok { + if andRules, ok := filter["and"].([]any); ok { for _, r := range andRules { - if rule, ok := r.(map[string]interface{}); ok { + if rule, ok := r.(map[string]any); ok { result.And = append(result.And, domain.FilterRule{ Property: getString(rule, "property"), Condition: getString(rule, "condition"), @@ -147,9 +148,9 @@ func convertFilterConfigToDomain(filter map[string]interface{}) *domain.FilterCo } // Handle "or" filters - if orRules, ok := filter["or"].([]interface{}); ok { + if orRules, ok := filter["or"].([]any); ok { for _, r := range orRules { - if rule, ok := r.(map[string]interface{}); ok { + if rule, ok := r.(map[string]any); ok { result.Or = append(result.Or, domain.FilterRule{ Property: getString(rule, "property"), Condition: getString(rule, "condition"), @@ -162,7 +163,7 @@ func convertFilterConfigToDomain(filter map[string]interface{}) *domain.FilterCo return result } -func getString(m map[string]interface{}, key string) string { +func getString(m map[string]any, key string) string { if v, ok := m[key].(string); ok { return v } diff --git a/application/database/move_database.go b/application/database/move_database.go index 3b8bc57..c49275b 100644 --- a/application/database/move_database.go +++ b/application/database/move_database.go @@ -4,6 +4,7 @@ import ( "fmt" "time" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/database/dto" spaceDto "github.com/labbs/nexo/application/space/dto" ) @@ -21,7 +22,7 @@ func (app *DatabaseApplication) MoveDatabase(input dto.MoveDatabaseInput) (*dto. } if spaceResult.Space.GetUserRole(input.UserId) == nil { - return nil, fmt.Errorf("access denied") + return nil, apperrors.ErrAccessDenied } database.DocumentId = input.DocumentId diff --git a/application/database/search.go b/application/database/search.go index 66fac81..b15b3c8 100644 --- a/application/database/search.go +++ b/application/database/search.go @@ -3,12 +3,13 @@ package database import ( "fmt" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/database/dto" ) func (app *DatabaseApplication) Search(input dto.SearchDatabasesInput) (*dto.SearchDatabasesOutput, error) { if len(input.Query) < 2 { - return nil, fmt.Errorf("query must be at least 2 characters") + return nil, apperrors.ErrInvalidInput } databases, err := app.DatabasePers.Search(input.Query, input.UserId, input.SpaceId, input.Limit) diff --git a/application/database/update_database.go b/application/database/update_database.go index bf869c3..51032c7 100644 --- a/application/database/update_database.go +++ b/application/database/update_database.go @@ -5,6 +5,7 @@ import ( "fmt" "time" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/database/dto" spaceDto "github.com/labbs/nexo/application/space/dto" "github.com/labbs/nexo/domain" @@ -23,7 +24,7 @@ func (app *DatabaseApplication) UpdateDatabase(input dto.UpdateDatabaseInput) er } if spaceResult.Space.GetUserRole(input.UserId) == nil { - return fmt.Errorf("access denied") + return apperrors.ErrAccessDenied } if input.Name != nil { diff --git a/application/database/update_row.go b/application/database/update_row.go index 25f39de..8a59ac1 100644 --- a/application/database/update_row.go +++ b/application/database/update_row.go @@ -4,6 +4,7 @@ import ( "fmt" "time" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/database/dto" spaceDto "github.com/labbs/nexo/application/space/dto" "github.com/labbs/nexo/domain" @@ -16,7 +17,7 @@ func (app *DatabaseApplication) UpdateRow(input dto.UpdateRowInput) error { } if row.DatabaseId != input.DatabaseId { - return fmt.Errorf("row not found in this database") + return apperrors.ErrRowNotFound } database, err := app.DatabasePers.GetById(input.DatabaseId) @@ -31,7 +32,7 @@ func (app *DatabaseApplication) UpdateRow(input dto.UpdateRowInput) error { } if spaceResult.Space.GetUserRole(input.UserId) == nil { - return fmt.Errorf("access denied") + return apperrors.ErrAccessDenied } if input.Properties != nil { diff --git a/application/database/update_view.go b/application/database/update_view.go index 9463abd..3ad9d8c 100644 --- a/application/database/update_view.go +++ b/application/database/update_view.go @@ -5,6 +5,7 @@ import ( "fmt" "time" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/database/dto" spaceDto "github.com/labbs/nexo/application/space/dto" "github.com/labbs/nexo/domain" @@ -23,7 +24,7 @@ func (app *DatabaseApplication) UpdateView(input dto.UpdateViewInput) error { } if spaceResult.Space.GetUserRole(input.UserId) == nil { - return fmt.Errorf("access denied") + return apperrors.ErrAccessDenied } // Parse existing views diff --git a/application/document/comment.go b/application/document/comment.go index 429a703..f6303b7 100644 --- a/application/document/comment.go +++ b/application/document/comment.go @@ -5,6 +5,7 @@ import ( "time" "github.com/google/uuid" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/document/dto" "github.com/labbs/nexo/domain" ) @@ -78,7 +79,7 @@ func (app *DocumentApplication) UpdateComment(input dto.UpdateCommentInput) erro // Only the comment author can update it if comment.UserId != input.UserId { - return fmt.Errorf("access denied: only the comment author can update it") + return apperrors.ErrAccessDenied } comment.Content = input.Content @@ -99,7 +100,7 @@ func (app *DocumentApplication) DeleteComment(input dto.DeleteCommentInput) erro // Only the comment author can delete it if comment.UserId != input.UserId { - return fmt.Errorf("access denied: only the comment author can delete it") + return apperrors.ErrAccessDenied } if err := app.CommentPers.Delete(input.CommentId); err != nil { diff --git a/application/document/create.go b/application/document/create.go index 1823289..36af983 100644 --- a/application/document/create.go +++ b/application/document/create.go @@ -5,6 +5,7 @@ import ( "github.com/gofiber/fiber/v2/utils" "github.com/gosimple/slug" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/document/dto" permissionDto "github.com/labbs/nexo/application/permission/dto" spaceDto "github.com/labbs/nexo/application/space/dto" @@ -32,12 +33,12 @@ func (a *DocumentApplication) CreateDocument(input dto.CreateDocumentInput) (*dt if !parent.HasPermission(input.UserId, domain.PermissionRoleEditor) { logger.Error().Msg("user does not have permission to create document under the specified parent") - return nil, fmt.Errorf("user does not have permission to create document under the specified parent") + return nil, apperrors.ErrAccessDenied } } else { if !spaceDetail.HasPermission(input.UserId, "editor") { logger.Error().Msg("user does not have permission to create document in the specified space") - return nil, fmt.Errorf("user does not have permission to create document in the specified space") + return nil, apperrors.ErrAccessDenied } } diff --git a/application/document/get.go b/application/document/get.go index a87c891..f3c790a 100644 --- a/application/document/get.go +++ b/application/document/get.go @@ -1,8 +1,7 @@ package document import ( - "fmt" - + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/document/dto" "github.com/labbs/nexo/domain" ) @@ -11,7 +10,7 @@ func (a *DocumentApplication) GetDocumentWithSpace(input dto.GetDocumentWithSpac logger := a.Logger.With().Str("component", "application.document.get_document").Logger() if input.DocumentId == nil && input.Slug == nil { - return nil, fmt.Errorf("either documentId or slug must be provided") + return nil, apperrors.ErrInvalidInput } document, err := a.DocumentPers.GetDocumentByIdOrSlugWithUserPermissions(input.SpaceId, input.DocumentId, input.Slug, input.UserId) diff --git a/application/document/public.go b/application/document/public.go index 5266258..9ec6c31 100644 --- a/application/document/public.go +++ b/application/document/public.go @@ -1,8 +1,7 @@ package document import ( - "fmt" - + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/document/dto" ) @@ -24,7 +23,7 @@ func (c *DocumentApplication) GetPublicDocument(input dto.GetPublicDocumentInput doc, err := c.DocumentPers.GetPublicDocument(input.SpaceId, input.DocumentId, input.Slug) if err != nil { logger.Error().Err(err).Msg("failed to get public document") - return nil, fmt.Errorf("document not found or not public") + return nil, apperrors.ErrDocumentNotFound } return &dto.GetPublicDocumentOutput{Document: doc}, nil diff --git a/application/document/search.go b/application/document/search.go index c5c678a..7bf4b3b 100644 --- a/application/document/search.go +++ b/application/document/search.go @@ -3,12 +3,13 @@ package document import ( "fmt" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/document/dto" ) func (app *DocumentApplication) Search(input dto.SearchInput) (*dto.SearchOutput, error) { if len(input.Query) < 2 { - return nil, fmt.Errorf("query must be at least 2 characters") + return nil, apperrors.ErrInvalidInput } docs, err := app.DocumentPers.Search(input.Query, input.UserId, input.SpaceId, input.Limit) diff --git a/application/document/update.go b/application/document/update.go index ca7ae8d..6854ce6 100644 --- a/application/document/update.go +++ b/application/document/update.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/gosimple/slug" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/document/dto" "github.com/labbs/nexo/domain" "github.com/labbs/nexo/infrastructure/helpers/shortuuid" @@ -20,7 +21,7 @@ func (a *DocumentApplication) UpdateDocument(input dto.UpdateDocumentInput) (*dt if !document.HasPermission(input.UserId, domain.PermissionRoleEditor) { logger.Error().Msg("user does not have permission to update document") - return nil, fmt.Errorf("user does not have permission to update document") + return nil, apperrors.ErrAccessDenied } // Update name only if provided diff --git a/application/document/version.go b/application/document/version.go index 673f35b..15ebf5d 100644 --- a/application/document/version.go +++ b/application/document/version.go @@ -6,6 +6,7 @@ import ( "time" "github.com/google/uuid" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/document/dto" "github.com/labbs/nexo/domain" ) @@ -101,7 +102,7 @@ func (app *DocumentApplication) RestoreVersion(input dto.RestoreVersionInput) er } if !doc.HasPermission(input.UserId, domain.PermissionRoleEditor) { - return fmt.Errorf("access denied: insufficient permissions") + return apperrors.ErrAccessDenied } // Create a new version before restoring (to preserve current state) @@ -129,7 +130,7 @@ func (app *DocumentApplication) CreateVersion(input dto.CreateVersionInput) (*dt } if !doc.HasPermission(input.UserId, domain.PermissionRoleEditor) { - return nil, fmt.Errorf("access denied: insufficient permissions") + return nil, apperrors.ErrAccessDenied } version, err := app.createVersionFromDocument(doc, input.UserId, input.Description) diff --git a/application/drawing/drawing.go b/application/drawing/drawing.go index c939562..114b494 100644 --- a/application/drawing/drawing.go +++ b/application/drawing/drawing.go @@ -6,6 +6,7 @@ import ( "time" "github.com/google/uuid" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/drawing/dto" permissionDto "github.com/labbs/nexo/application/permission/dto" "github.com/labbs/nexo/application/ports" @@ -39,7 +40,7 @@ func (app *DrawingApplication) CreateDrawing(input dto.CreateDrawingInput) (*dto } if spaceResult.Space.GetUserRole(input.UserId) == nil { - return nil, fmt.Errorf("access denied") + return nil, apperrors.ErrAccessDenied } // Convert elements to JSONBArray @@ -124,7 +125,7 @@ func (app *DrawingApplication) ListDrawings(input dto.ListDrawingsInput) (*dto.L } if spaceResult.Space.GetUserRole(input.UserId) == nil { - return nil, fmt.Errorf("access denied") + return nil, apperrors.ErrAccessDenied } drawings, err := app.DrawingPers.GetBySpaceId(input.SpaceId) @@ -165,23 +166,23 @@ func (app *DrawingApplication) GetDrawing(input dto.GetDrawingInput) (*dto.GetDr } if spaceResult.Space.GetUserRole(input.UserId) == nil { - return nil, fmt.Errorf("access denied") + return nil, apperrors.ErrAccessDenied } // Convert JSONB to slices/maps - var elements []interface{} + var elements []any if drawing.Elements != nil { elementsJSON, _ := json.Marshal(drawing.Elements) json.Unmarshal(elementsJSON, &elements) } - var appState map[string]interface{} + var appState map[string]any if drawing.AppState != nil { appStateJSON, _ := json.Marshal(drawing.AppState) json.Unmarshal(appStateJSON, &appState) } - var files map[string]interface{} + var files map[string]any if drawing.Files != nil { filesJSON, _ := json.Marshal(drawing.Files) json.Unmarshal(filesJSON, &files) @@ -216,7 +217,7 @@ func (app *DrawingApplication) UpdateDrawing(input dto.UpdateDrawingInput) error } if spaceResult.Space.GetUserRole(input.UserId) == nil { - return fmt.Errorf("access denied") + return apperrors.ErrAccessDenied } if input.Name != nil { @@ -283,7 +284,7 @@ func (app *DrawingApplication) MoveDrawing(input dto.MoveDrawingInput) (*dto.Mov } if spaceResult.Space.GetUserRole(input.UserId) == nil { - return nil, fmt.Errorf("access denied") + return nil, apperrors.ErrAccessDenied } drawing.DocumentId = input.DocumentId @@ -312,7 +313,7 @@ func (app *DrawingApplication) DeleteDrawing(input dto.DeleteDrawingInput) error } if spaceResult.Space.GetUserRole(input.UserId) == nil { - return fmt.Errorf("access denied") + return apperrors.ErrAccessDenied } if err := app.DrawingPers.Delete(input.DrawingId); err != nil { diff --git a/application/drawing/dto/create_drawing.go b/application/drawing/dto/create_drawing.go index 368aeaf..ab9c22d 100644 --- a/application/drawing/dto/create_drawing.go +++ b/application/drawing/dto/create_drawing.go @@ -8,9 +8,9 @@ type CreateDrawingInput struct { DocumentId *string Name string Icon string - Elements []interface{} - AppState map[string]interface{} - Files map[string]interface{} + Elements []any + AppState map[string]any + Files map[string]any Thumbnail string } diff --git a/application/drawing/dto/get_drawing.go b/application/drawing/dto/get_drawing.go index 970278c..f876b1b 100644 --- a/application/drawing/dto/get_drawing.go +++ b/application/drawing/dto/get_drawing.go @@ -13,9 +13,9 @@ type GetDrawingOutput struct { DocumentId *string Name string Icon string - Elements []interface{} - AppState map[string]interface{} - Files map[string]interface{} + Elements []any + AppState map[string]any + Files map[string]any Thumbnail string CreatedBy string CreatedAt time.Time diff --git a/application/drawing/dto/update_drawing.go b/application/drawing/dto/update_drawing.go index 09a495e..c6a6a08 100644 --- a/application/drawing/dto/update_drawing.go +++ b/application/drawing/dto/update_drawing.go @@ -5,8 +5,8 @@ type UpdateDrawingInput struct { DrawingId string Name *string Icon *string - Elements []interface{} - AppState map[string]interface{} - Files map[string]interface{} + Elements []any + AppState map[string]any + Files map[string]any Thumbnail *string } diff --git a/application/favorite/dto/common.go b/application/favorite/dto/common.go index 81f30b3..18f1e58 100644 --- a/application/favorite/dto/common.go +++ b/application/favorite/dto/common.go @@ -17,8 +17,8 @@ type Favorite struct { // FavoriteDocument contains minimal document info for favorites list type FavoriteDocument struct { - Id string - Name string - Slug string - Icon string + Id string + Name string + Slug string + Icon string } diff --git a/application/permission/delete_database_permission.go b/application/permission/delete_database_permission.go index 4ee1cba..9d6b610 100644 --- a/application/permission/delete_database_permission.go +++ b/application/permission/delete_database_permission.go @@ -3,6 +3,7 @@ package permission import ( "fmt" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" databaseDto "github.com/labbs/nexo/application/database/dto" spaceDto "github.com/labbs/nexo/application/space/dto" "github.com/labbs/nexo/domain" @@ -25,7 +26,7 @@ func (app *PermissionApplication) DeleteDatabasePermission(input databaseDto.Del spaceRole := spaceResult.Space.GetUserRole(input.UserId) if spaceRole == nil { - return fmt.Errorf("access denied") + return apperrors.ErrAccessDenied } // Only creator or space admin/owner can manage permissions diff --git a/application/permission/delete_document_user_permission.go b/application/permission/delete_document_user_permission.go index f6181ff..d44e5f6 100644 --- a/application/permission/delete_document_user_permission.go +++ b/application/permission/delete_document_user_permission.go @@ -3,6 +3,7 @@ package permission import ( "fmt" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" documentDto "github.com/labbs/nexo/application/document/dto" "github.com/labbs/nexo/domain" ) @@ -17,12 +18,12 @@ func (app *PermissionApplication) DeleteDocumentUserPermission(input documentDto UserId: input.RequesterId, }) if err != nil || docResult.Document == nil { - return fmt.Errorf("not_found") + return apperrors.ErrNotFound } // User must be able to manage permissions (owner of document OR admin of space) if !docResult.Document.CanManagePermissions(input.RequesterId) { - return fmt.Errorf("forbidden") + return apperrors.ErrForbidden } // Prevent removing the space owner from documents in personal/private spaces diff --git a/application/permission/delete_drawing_user_permission.go b/application/permission/delete_drawing_user_permission.go index 22eb9a8..5fa3db0 100644 --- a/application/permission/delete_drawing_user_permission.go +++ b/application/permission/delete_drawing_user_permission.go @@ -3,6 +3,7 @@ package permission import ( "fmt" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" drawingDto "github.com/labbs/nexo/application/drawing/dto" spaceDto "github.com/labbs/nexo/application/space/dto" "github.com/labbs/nexo/domain" @@ -14,7 +15,7 @@ func (app *PermissionApplication) DeleteDrawingUserPermission(input drawingDto.D // Get the drawing drawingResult, err := app.DrawingApplication.GetDrawingById(drawingDto.GetDrawingByIdInput{DrawingId: input.DrawingId}) if err != nil { - return fmt.Errorf("not_found") + return apperrors.ErrNotFound } // Verify user has access to the space @@ -26,7 +27,7 @@ func (app *PermissionApplication) DeleteDrawingUserPermission(input drawingDto.D // User must be admin/owner of space to manage permissions role := spaceResult.Space.GetUserRole(input.RequesterId) if role == nil || (*role != "owner" && *role != "admin") { - return fmt.Errorf("forbidden") + return apperrors.ErrForbidden } // Prevent removing the space owner in personal/private spaces diff --git a/application/permission/delete_space_group_permission.go b/application/permission/delete_space_group_permission.go index 1c7a35b..2b61251 100644 --- a/application/permission/delete_space_group_permission.go +++ b/application/permission/delete_space_group_permission.go @@ -3,6 +3,7 @@ package permission import ( "fmt" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" spaceDto "github.com/labbs/nexo/application/space/dto" "github.com/labbs/nexo/domain" ) @@ -17,7 +18,7 @@ func (app *PermissionApplication) DeleteSpaceGroupPermission(input spaceDto.Dele role := space.Space.GetUserRole(input.RequesterId) if role == nil || (*role != "owner" && *role != "admin") { - return fmt.Errorf("forbidden") + return apperrors.ErrForbidden } return app.PermissionPers.DeleteGroup(domain.PermissionTypeSpace, input.SpaceId, input.GroupId) diff --git a/application/permission/delete_space_user_permission.go b/application/permission/delete_space_user_permission.go index 2d6b91e..d5ea0bd 100644 --- a/application/permission/delete_space_user_permission.go +++ b/application/permission/delete_space_user_permission.go @@ -3,6 +3,7 @@ package permission import ( "fmt" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" spaceDto "github.com/labbs/nexo/application/space/dto" "github.com/labbs/nexo/domain" ) @@ -12,10 +13,10 @@ import ( func (app *PermissionApplication) DeleteSpaceUserPermission(input spaceDto.DeleteSpaceUserPermissionInput) error { spaceResult, err := app.SpaceApplication.GetSpaceById(spaceDto.GetSpaceByIdInput{SpaceId: input.SpaceId}) if err != nil || spaceResult.Space == nil { - return fmt.Errorf("not_found") + return apperrors.ErrNotFound } if !spaceResult.Space.HasPermission(input.RequesterId, "admin") { - return fmt.Errorf("forbidden") + return apperrors.ErrForbidden } // Prevent removing the owner from personal/private spaces diff --git a/application/permission/list_database_permissions.go b/application/permission/list_database_permissions.go index b40a1cf..e6601ed 100644 --- a/application/permission/list_database_permissions.go +++ b/application/permission/list_database_permissions.go @@ -3,6 +3,7 @@ package permission import ( "fmt" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" databaseDto "github.com/labbs/nexo/application/database/dto" spaceDto "github.com/labbs/nexo/application/space/dto" "github.com/labbs/nexo/domain" @@ -23,7 +24,7 @@ func (app *PermissionApplication) ListDatabasePermissions(input databaseDto.List } if spaceResult.Space.GetUserRole(input.UserId) == nil { - return nil, fmt.Errorf("access denied") + return nil, apperrors.ErrAccessDenied } perms, err := app.PermissionPers.ListByResource(domain.PermissionTypeDatabase, input.DatabaseId) diff --git a/application/permission/list_document_permissions.go b/application/permission/list_document_permissions.go index b47e049..f2fd40c 100644 --- a/application/permission/list_document_permissions.go +++ b/application/permission/list_document_permissions.go @@ -1,8 +1,7 @@ package permission import ( - "fmt" - + "github.com/labbs/nexo/infrastructure/helpers/apperrors" documentDto "github.com/labbs/nexo/application/document/dto" "github.com/labbs/nexo/domain" ) @@ -17,12 +16,12 @@ func (app *PermissionApplication) ListDocumentPermissions(input documentDto.List UserId: input.RequesterId, }) if err != nil || docResult.Document == nil { - return nil, fmt.Errorf("not_found") + return nil, apperrors.ErrNotFound } // User must have at least viewer access to the document to see permissions if !docResult.Document.HasPermission(input.RequesterId, "viewer") { - return nil, fmt.Errorf("forbidden") + return nil, apperrors.ErrForbidden } permissions, err := app.PermissionPers.ListByResource(domain.PermissionTypeDocument, input.DocumentId) diff --git a/application/permission/list_drawing_permissions.go b/application/permission/list_drawing_permissions.go index fbe2ae3..f324fd7 100644 --- a/application/permission/list_drawing_permissions.go +++ b/application/permission/list_drawing_permissions.go @@ -3,6 +3,7 @@ package permission import ( "fmt" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" drawingDto "github.com/labbs/nexo/application/drawing/dto" spaceDto "github.com/labbs/nexo/application/space/dto" "github.com/labbs/nexo/domain" @@ -14,7 +15,7 @@ func (app *PermissionApplication) ListDrawingPermissions(input drawingDto.ListDr // Get the drawing drawingResult, err := app.DrawingApplication.GetDrawingById(drawingDto.GetDrawingByIdInput{DrawingId: input.DrawingId}) if err != nil { - return nil, fmt.Errorf("not_found") + return nil, apperrors.ErrNotFound } // Verify user has access to the space @@ -24,7 +25,7 @@ func (app *PermissionApplication) ListDrawingPermissions(input drawingDto.ListDr } if spaceResult.Space.GetUserRole(input.RequesterId) == nil { - return nil, fmt.Errorf("forbidden") + return nil, apperrors.ErrForbidden } permissions, err := app.PermissionPers.ListByResource(domain.PermissionTypeDrawing, input.DrawingId) diff --git a/application/permission/list_space_permissions.go b/application/permission/list_space_permissions.go index 21f1010..df950f7 100644 --- a/application/permission/list_space_permissions.go +++ b/application/permission/list_space_permissions.go @@ -1,8 +1,7 @@ package permission import ( - "fmt" - + "github.com/labbs/nexo/infrastructure/helpers/apperrors" spaceDto "github.com/labbs/nexo/application/space/dto" "github.com/labbs/nexo/domain" ) @@ -12,10 +11,10 @@ import ( func (app *PermissionApplication) ListSpacePermissions(input spaceDto.ListSpacePermissionsInput) (*spaceDto.ListSpacePermissionsOutput, error) { spaceResult, err := app.SpaceApplication.GetSpaceById(spaceDto.GetSpaceByIdInput{SpaceId: input.SpaceId}) if err != nil || spaceResult.Space == nil { - return nil, fmt.Errorf("not_found") + return nil, apperrors.ErrNotFound } if !spaceResult.Space.HasPermission(input.UserId, "admin") { - return nil, fmt.Errorf("forbidden") + return nil, apperrors.ErrForbidden } permissions, err := app.PermissionPers.ListByResource(domain.PermissionTypeSpace, input.SpaceId) if err != nil { diff --git a/application/permission/upsert_database_permission.go b/application/permission/upsert_database_permission.go index 79fd176..2b86640 100644 --- a/application/permission/upsert_database_permission.go +++ b/application/permission/upsert_database_permission.go @@ -3,6 +3,7 @@ package permission import ( "fmt" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" databaseDto "github.com/labbs/nexo/application/database/dto" spaceDto "github.com/labbs/nexo/application/space/dto" "github.com/labbs/nexo/domain" @@ -25,7 +26,7 @@ func (app *PermissionApplication) UpsertDatabasePermission(input databaseDto.Ups spaceRole := spaceResult.Space.GetUserRole(input.UserId) if spaceRole == nil { - return fmt.Errorf("access denied") + return apperrors.ErrAccessDenied } // Only creator or space admin/owner can manage permissions diff --git a/application/permission/upsert_document_user_permission.go b/application/permission/upsert_document_user_permission.go index 0cdfab6..eec3918 100644 --- a/application/permission/upsert_document_user_permission.go +++ b/application/permission/upsert_document_user_permission.go @@ -3,6 +3,7 @@ package permission import ( "fmt" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" documentDto "github.com/labbs/nexo/application/document/dto" "github.com/labbs/nexo/domain" ) @@ -17,12 +18,12 @@ func (app *PermissionApplication) UpsertDocumentUserPermission(input documentDto UserId: input.RequesterId, }) if err != nil || docResult.Document == nil { - return fmt.Errorf("not_found") + return apperrors.ErrNotFound } // User must be able to manage permissions (owner of document OR admin of space) if !docResult.Document.CanManagePermissions(input.RequesterId) { - return fmt.Errorf("forbidden") + return apperrors.ErrForbidden } // Prevent changing the owner's role on documents in personal/private spaces diff --git a/application/permission/upsert_drawing_user_permission.go b/application/permission/upsert_drawing_user_permission.go index b950143..2fc15ee 100644 --- a/application/permission/upsert_drawing_user_permission.go +++ b/application/permission/upsert_drawing_user_permission.go @@ -3,6 +3,7 @@ package permission import ( "fmt" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" drawingDto "github.com/labbs/nexo/application/drawing/dto" spaceDto "github.com/labbs/nexo/application/space/dto" "github.com/labbs/nexo/domain" @@ -14,7 +15,7 @@ func (app *PermissionApplication) UpsertDrawingUserPermission(input drawingDto.U // Get the drawing drawingResult, err := app.DrawingApplication.GetDrawingById(drawingDto.GetDrawingByIdInput{DrawingId: input.DrawingId}) if err != nil { - return fmt.Errorf("not_found") + return apperrors.ErrNotFound } // Verify user has access to the space @@ -26,7 +27,7 @@ func (app *PermissionApplication) UpsertDrawingUserPermission(input drawingDto.U // User must be admin/owner of space to manage permissions role := spaceResult.Space.GetUserRole(input.RequesterId) if role == nil || (*role != "owner" && *role != "admin") { - return fmt.Errorf("forbidden") + return apperrors.ErrForbidden } // Prevent changing the owner's role in personal/private spaces diff --git a/application/permission/upsert_space_group_permission.go b/application/permission/upsert_space_group_permission.go index a531fbc..5dd7ce3 100644 --- a/application/permission/upsert_space_group_permission.go +++ b/application/permission/upsert_space_group_permission.go @@ -3,6 +3,7 @@ package permission import ( "fmt" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" spaceDto "github.com/labbs/nexo/application/space/dto" "github.com/labbs/nexo/domain" ) @@ -17,7 +18,7 @@ func (app *PermissionApplication) UpsertSpaceGroupPermission(input spaceDto.Upse role := space.Space.GetUserRole(input.RequesterId) if role == nil || (*role != "owner" && *role != "admin") { - return fmt.Errorf("forbidden") + return apperrors.ErrForbidden } return app.PermissionPers.UpsertGroup(domain.PermissionTypeSpace, input.SpaceId, input.GroupId, domain.PermissionRole(input.Role)) diff --git a/application/permission/upsert_space_user_permission.go b/application/permission/upsert_space_user_permission.go index 5669ffd..718c17f 100644 --- a/application/permission/upsert_space_user_permission.go +++ b/application/permission/upsert_space_user_permission.go @@ -3,6 +3,7 @@ package permission import ( "fmt" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" spaceDto "github.com/labbs/nexo/application/space/dto" "github.com/labbs/nexo/domain" ) @@ -12,10 +13,10 @@ import ( func (app *PermissionApplication) UpsertSpaceUserPermission(input spaceDto.UpsertSpaceUserPermissionInput) error { spaceResult, err := app.SpaceApplication.GetSpaceById(spaceDto.GetSpaceByIdInput{SpaceId: input.SpaceId}) if err != nil || spaceResult.Space == nil { - return fmt.Errorf("not_found") + return apperrors.ErrNotFound } if !spaceResult.Space.HasPermission(input.RequesterId, "admin") { - return fmt.Errorf("forbidden") + return apperrors.ErrForbidden } // Prevent changing the owner's role on personal/private spaces diff --git a/application/session/validate.go b/application/session/validate.go index ebab72e..433e46d 100644 --- a/application/session/validate.go +++ b/application/session/validate.go @@ -5,6 +5,7 @@ import ( "time" fiberoapi "github.com/labbs/fiber-oapi" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/session/dto" "github.com/labbs/nexo/infrastructure/helpers/tokenutil" ) @@ -15,23 +16,23 @@ func (c *SessionApplication) ValidateToken(input dto.ValidateTokenInput) (*dto.V sessionId, err := tokenutil.GetSessionIdFromToken(input.Token, c.Config) if err != nil { logger.Error().Err(err).Str("token", input.Token).Msg("failed to get session id from token") - return nil, fmt.Errorf("invalid token") + return nil, apperrors.ErrInvalidToken } session, err := c.SessionPers.GetById(sessionId) if err != nil { logger.Error().Err(err).Str("token", input.Token).Msg("failed to get session by token") - return nil, fmt.Errorf("invalid token") + return nil, apperrors.ErrInvalidToken } if session.ExpiresAt.Before(time.Now()) { logger.Warn().Str("token", input.Token).Msg("session has expired") - return nil, fmt.Errorf("session has expired") + return nil, apperrors.ErrSessionExpired } ctx := &fiberoapi.AuthContext{ UserID: session.UserId, - Claims: map[string]interface{}{ + Claims: map[string]any{ "session_id": session.Id, }, } diff --git a/application/space/admin.go b/application/space/admin.go index da7dd13..d43762f 100644 --- a/application/space/admin.go +++ b/application/space/admin.go @@ -81,4 +81,3 @@ func (c *SpaceApplication) AdminDeleteSpace(spaceId string) error { return nil } - diff --git a/application/space/delete.go b/application/space/delete.go index 2aa8d2f..99e87ec 100644 --- a/application/space/delete.go +++ b/application/space/delete.go @@ -1,8 +1,7 @@ package space import ( - "fmt" - + "github.com/labbs/nexo/infrastructure/helpers/apperrors" docDto "github.com/labbs/nexo/application/document/dto" "github.com/labbs/nexo/application/space/dto" "github.com/labbs/nexo/domain" @@ -13,12 +12,12 @@ func (c *SpaceApplication) DeleteSpace(input dto.DeleteSpaceInput) error { space, err := c.SpacePres.GetSpaceById(input.SpaceId) if err != nil || space == nil { - return fmt.Errorf("not_found") + return apperrors.ErrSpaceNotFound } // Only owner can delete (MVP policy) if !space.HasPermission(input.UserId, domain.PermissionRoleOwner) { - return fmt.Errorf("forbidden") + return apperrors.ErrForbidden } // Guard: forbid delete if there are active documents in space (MVP: check root docs) @@ -27,7 +26,7 @@ func (c *SpaceApplication) DeleteSpace(input dto.DeleteSpaceInput) error { UserId: input.UserId, }) if derr == nil && docResult.HasDocuments { - return fmt.Errorf("conflict_children") + return apperrors.ErrConflictChildren } if err := c.SpacePres.Delete(input.SpaceId); err != nil { diff --git a/application/space/update.go b/application/space/update.go index 65b10f9..979ae8a 100644 --- a/application/space/update.go +++ b/application/space/update.go @@ -1,9 +1,8 @@ package space import ( - "fmt" - "github.com/gosimple/slug" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/space/dto" "github.com/labbs/nexo/domain" "github.com/labbs/nexo/infrastructure/helpers/shortuuid" @@ -14,12 +13,12 @@ func (c *SpaceApplication) UpdateSpace(input dto.UpdateSpaceInput) (*dto.UpdateS space, err := c.SpacePres.GetSpaceById(input.SpaceId) if err != nil || space == nil { - return nil, fmt.Errorf("not_found") + return nil, apperrors.ErrSpaceNotFound } // Require admin to update if !space.HasPermission(input.UserId, domain.PermissionRoleAdmin) { - return nil, fmt.Errorf("forbidden") + return nil, apperrors.ErrForbidden } // Apply updates diff --git a/application/user/update.go b/application/user/update.go index b9aed8b..8d8fc3a 100644 --- a/application/user/update.go +++ b/application/user/update.go @@ -3,6 +3,7 @@ package user import ( "fmt" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/user/dto" "golang.org/x/crypto/bcrypt" ) @@ -13,7 +14,7 @@ func (c *UserApplication) UpdateProfile(input dto.UpdateProfileInput) (*dto.Upda user, err := c.UserPres.GetById(input.UserId) if err != nil { logger.Error().Err(err).Str("user_id", input.UserId).Msg("failed to get user") - return nil, fmt.Errorf("user not found") + return nil, apperrors.ErrUserNotFound } if input.Username != nil { @@ -41,14 +42,14 @@ func (c *UserApplication) ChangePassword(input dto.ChangePasswordInput) error { user, err := c.UserPres.GetById(input.UserId) if err != nil { logger.Error().Err(err).Str("user_id", input.UserId).Msg("failed to get user") - return fmt.Errorf("user not found") + return apperrors.ErrUserNotFound } // Verify current password err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(input.CurrentPassword)) if err != nil { logger.Warn().Str("user_id", input.UserId).Msg("invalid current password") - return fmt.Errorf("invalid current password") + return apperrors.ErrInvalidPassword } // Hash new password diff --git a/application/webhook/delete_webhook.go b/application/webhook/delete_webhook.go index 5a38d88..17856a0 100644 --- a/application/webhook/delete_webhook.go +++ b/application/webhook/delete_webhook.go @@ -3,6 +3,7 @@ package webhook import ( "fmt" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/webhook/dto" ) @@ -13,7 +14,7 @@ func (app *WebhookApplication) DeleteWebhook(input dto.DeleteWebhookInput) error } if webhook.UserId != input.UserId { - return fmt.Errorf("access denied") + return apperrors.ErrAccessDenied } if err := app.WebhookPers.Delete(input.WebhookId); err != nil { diff --git a/application/webhook/dto/trigger_webhook.go b/application/webhook/dto/trigger_webhook.go index c111620..67c6b0c 100644 --- a/application/webhook/dto/trigger_webhook.go +++ b/application/webhook/dto/trigger_webhook.go @@ -3,5 +3,5 @@ package dto type TriggerWebhookInput struct { Event string SpaceId *string - Payload map[string]interface{} + Payload map[string]any } diff --git a/application/webhook/get_deliveries.go b/application/webhook/get_deliveries.go index 05f3b9e..8ea5758 100644 --- a/application/webhook/get_deliveries.go +++ b/application/webhook/get_deliveries.go @@ -3,6 +3,7 @@ package webhook import ( "fmt" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/webhook/dto" ) @@ -14,7 +15,7 @@ func (app *WebhookApplication) GetDeliveries(input dto.GetDeliveriesInput) (*dto } if webhook.UserId != input.UserId { - return nil, fmt.Errorf("access denied") + return nil, apperrors.ErrAccessDenied } limit := input.Limit diff --git a/application/webhook/get_webhook.go b/application/webhook/get_webhook.go index fa06330..44bacff 100644 --- a/application/webhook/get_webhook.go +++ b/application/webhook/get_webhook.go @@ -3,6 +3,7 @@ package webhook import ( "fmt" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/webhook/dto" ) @@ -13,12 +14,12 @@ func (app *WebhookApplication) GetWebhook(input dto.GetWebhookInput) (*dto.GetWe } if webhook.UserId != input.UserId { - return nil, fmt.Errorf("access denied") + return nil, apperrors.ErrAccessDenied } var events []string if webhook.Events != nil { - if e, ok := webhook.Events["events"].([]interface{}); ok { + if e, ok := webhook.Events["events"].([]any); ok { for _, ev := range e { if str, ok := ev.(string); ok { events = append(events, str) diff --git a/application/webhook/list_webhooks.go b/application/webhook/list_webhooks.go index 2269df2..b650784 100644 --- a/application/webhook/list_webhooks.go +++ b/application/webhook/list_webhooks.go @@ -19,7 +19,7 @@ func (app *WebhookApplication) ListWebhooks(input dto.ListWebhooksInput) (*dto.L for i, w := range webhooks { var events []string if w.Events != nil { - if e, ok := w.Events["events"].([]interface{}); ok { + if e, ok := w.Events["events"].([]any); ok { for _, ev := range e { if str, ok := ev.(string); ok { events = append(events, str) diff --git a/application/webhook/trigger_webhooks.go b/application/webhook/trigger_webhooks.go index 9123e81..9662357 100644 --- a/application/webhook/trigger_webhooks.go +++ b/application/webhook/trigger_webhooks.go @@ -27,7 +27,7 @@ func (app *WebhookApplication) TriggerWebhooks(input dto.TriggerWebhookInput) { } } -func (app *WebhookApplication) deliverWebhook(webhook domain.Webhook, event string, payload map[string]interface{}) { +func (app *WebhookApplication) deliverWebhook(webhook domain.Webhook, event string, payload map[string]any) { logger := app.Logger.With(). Str("component", "webhook.deliver"). Str("webhook_id", webhook.Id). @@ -35,7 +35,7 @@ func (app *WebhookApplication) deliverWebhook(webhook domain.Webhook, event stri Logger() // Build the webhook payload - webhookPayload := map[string]interface{}{ + webhookPayload := map[string]any{ "id": uuid.New().String(), "event": event, "timestamp": time.Now().UTC().Format(time.RFC3339), @@ -100,7 +100,7 @@ func (app *WebhookApplication) deliverWebhook(webhook domain.Webhook, event stri } } -func (app *WebhookApplication) recordDelivery(webhookId, event string, payload map[string]interface{}, statusCode int, response string, success bool, duration int) { +func (app *WebhookApplication) recordDelivery(webhookId, event string, payload map[string]any, statusCode int, response string, success bool, duration int) { delivery := &domain.WebhookDelivery{ Id: uuid.New().String(), WebhookId: webhookId, diff --git a/application/webhook/update_webhook.go b/application/webhook/update_webhook.go index f3fda24..9c4ee4f 100644 --- a/application/webhook/update_webhook.go +++ b/application/webhook/update_webhook.go @@ -4,6 +4,7 @@ import ( "fmt" "time" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/application/webhook/dto" "github.com/labbs/nexo/domain" ) @@ -15,7 +16,7 @@ func (app *WebhookApplication) UpdateWebhook(input dto.UpdateWebhookInput) error } if webhook.UserId != input.UserId { - return fmt.Errorf("access denied") + return apperrors.ErrAccessDenied } if input.Name != nil { diff --git a/domain/api_key.go b/domain/api_key.go index 1acfae0..c33851b 100644 --- a/domain/api_key.go +++ b/domain/api_key.go @@ -47,7 +47,7 @@ func (k *ApiKey) HasScope(scope ApiKeyScope) bool { if k.Permissions == nil { return false } - scopes, ok := k.Permissions["scopes"].([]interface{}) + scopes, ok := k.Permissions["scopes"].([]any) if !ok { return false } diff --git a/domain/database.go b/domain/database.go index b5782a2..f4dba10 100644 --- a/domain/database.go +++ b/domain/database.go @@ -137,7 +137,7 @@ func (r *DatabaseRow) TableName() string { type FilterRule struct { Property string `json:"property"` Condition string `json:"condition"` // eq, neq, gt, lt, gte, lte, contains, is_empty, is_not_empty - Value interface{} `json:"value,omitempty"` + Value any `json:"value,omitempty"` } // FilterConfig defines the filter configuration with AND/OR groups diff --git a/domain/webhook.go b/domain/webhook.go index 6973b1a..779c146 100644 --- a/domain/webhook.go +++ b/domain/webhook.go @@ -56,7 +56,7 @@ func (w *Webhook) HasEvent(event WebhookEvent) bool { if w.Events == nil { return false } - events, ok := w.Events["events"].([]interface{}) + events, ok := w.Events["events"].([]any) if !ok { return false } diff --git a/infrastructure/helpers/apperrors/errors.go b/infrastructure/helpers/apperrors/errors.go new file mode 100644 index 0000000..3f0f60d --- /dev/null +++ b/infrastructure/helpers/apperrors/errors.go @@ -0,0 +1,41 @@ +package apperrors + +import "errors" + +// Sentinel errors for cross-layer error handling. +// This package has no dependencies on domain, infrastructure, or interfaces, +// so it can be safely imported from any layer. +var ( + // Access & authentication + ErrForbidden = errors.New("forbidden") + ErrAccessDenied = errors.New("access denied") + ErrUnauthorized = errors.New("unauthorized") + ErrInvalidCredentials = errors.New("invalid credentials") + ErrInvalidPassword = errors.New("invalid current password") + ErrUserNotActive = errors.New("user is not active") + + // Not found + ErrNotFound = errors.New("not found") + ErrUserNotFound = errors.New("user not found") + ErrSpaceNotFound = errors.New("space not found") + ErrDocumentNotFound = errors.New("document not found") + ErrDatabaseNotFound = errors.New("database not found") + ErrDrawingNotFound = errors.New("drawing not found") + ErrRowNotFound = errors.New("row not found") + ErrWebhookNotFound = errors.New("webhook not found") + ErrActionNotFound = errors.New("action not found") + ErrVersionNotFound = errors.New("version not found") + ErrFavoriteNotFound = errors.New("favorite not found") + + // Conflict / validation + ErrConflict = errors.New("conflict") + ErrConflictChildren = errors.New("conflict: space has active documents") + ErrDuplicate = errors.New("already exists") + ErrInvalidInput = errors.New("invalid input") + ErrInvalidMove = errors.New("invalid move") + ErrDocumentNotDeleted = errors.New("document is not deleted") + + // Session / token + ErrInvalidToken = errors.New("invalid token") + ErrSessionExpired = errors.New("session has expired") +) diff --git a/infrastructure/helpers/mapper/map_struct_by_field_names.go b/infrastructure/helpers/mapper/map_struct_by_field_names.go index 808fe1f..508a4a4 100644 --- a/infrastructure/helpers/mapper/map_struct_by_field_names.go +++ b/infrastructure/helpers/mapper/map_struct_by_field_names.go @@ -7,7 +7,7 @@ import ( // MapStructByFieldNames maps fields from source struct to destination struct // based on matching field names (case sensitive). // Both src and dst should be pointers to structs. -func MapStructByFieldNames(src interface{}, dst interface{}) error { +func MapStructByFieldNames(src any, dst any) error { srcValue := reflect.ValueOf(src) dstValue := reflect.ValueOf(dst) diff --git a/infrastructure/helpers/tokenutil/tokenutil.go b/infrastructure/helpers/tokenutil/tokenutil.go index 8216a52..d7f0c32 100644 --- a/infrastructure/helpers/tokenutil/tokenutil.go +++ b/infrastructure/helpers/tokenutil/tokenutil.go @@ -26,7 +26,7 @@ func CreateAccessToken(user_id, sessionId string, config config.Config) (accessT } func GetSessionIdFromToken(tokenString string, config config.Config) (string, error) { - token, err := jwt.ParseWithClaims(tokenString, &JwtCustomClaims{}, func(token *jwt.Token) (interface{}, error) { + token, err := jwt.ParseWithClaims(tokenString, &JwtCustomClaims{}, func(token *jwt.Token) (any, error) { return []byte(config.Session.SecretKey), nil }) if err != nil { diff --git a/infrastructure/persistence/action_pers.go b/infrastructure/persistence/action_pers.go index 41df8e5..fd5a03b 100644 --- a/infrastructure/persistence/action_pers.go +++ b/infrastructure/persistence/action_pers.go @@ -1,8 +1,10 @@ package persistence import ( + "errors" "time" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/domain" "gorm.io/gorm" ) @@ -27,6 +29,9 @@ func (p *actionPers) GetById(id string) (*domain.Action, error) { Where("id = ?", id). First(&action).Error if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, apperrors.ErrActionNotFound + } return nil, err } return &action, nil @@ -75,7 +80,7 @@ func (p *actionPers) Delete(id string) error { func (p *actionPers) IncrementSuccess(id string) error { return p.db.Model(&domain.Action{}). Where("id = ?", id). - Updates(map[string]interface{}{ + Updates(map[string]any{ "success_count": gorm.Expr("success_count + 1"), "run_count": gorm.Expr("run_count + 1"), "last_error": "", @@ -85,7 +90,7 @@ func (p *actionPers) IncrementSuccess(id string) error { func (p *actionPers) RecordFailure(id string, errorMsg string) error { return p.db.Model(&domain.Action{}). Where("id = ?", id). - Updates(map[string]interface{}{ + Updates(map[string]any{ "failure_count": gorm.Expr("failure_count + 1"), "run_count": gorm.Expr("run_count + 1"), "last_error": errorMsg, diff --git a/infrastructure/persistence/api_key_pers.go b/infrastructure/persistence/api_key_pers.go index d8ead38..5f44110 100644 --- a/infrastructure/persistence/api_key_pers.go +++ b/infrastructure/persistence/api_key_pers.go @@ -1,8 +1,10 @@ package persistence import ( + "errors" "time" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/domain" "gorm.io/gorm" ) @@ -23,6 +25,9 @@ func (p *apiKeyPers) GetById(id string) (*domain.ApiKey, error) { var apiKey domain.ApiKey err := p.db.Where("id = ?", id).First(&apiKey).Error if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, apperrors.ErrNotFound + } return nil, err } return &apiKey, nil @@ -35,6 +40,9 @@ func (p *apiKeyPers) GetByKeyHash(keyHash string) (*domain.ApiKey, error) { Where("key_hash = ? AND deleted_at IS NULL", keyHash). First(&apiKey).Error if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, apperrors.ErrNotFound + } return nil, err } return &apiKey, nil diff --git a/infrastructure/persistence/comment_pers.go b/infrastructure/persistence/comment_pers.go index be7a23e..6aaffbb 100644 --- a/infrastructure/persistence/comment_pers.go +++ b/infrastructure/persistence/comment_pers.go @@ -1,8 +1,10 @@ package persistence import ( + "errors" "fmt" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/domain" "gorm.io/gorm" ) @@ -27,6 +29,9 @@ func (p *commentPers) GetById(commentId string) (*domain.Comment, error) { Where("id = ?", commentId). First(&comment).Error if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, apperrors.ErrNotFound + } return nil, err } return &comment, nil diff --git a/infrastructure/persistence/database_pers.go b/infrastructure/persistence/database_pers.go index c75d624..7a7f519 100644 --- a/infrastructure/persistence/database_pers.go +++ b/infrastructure/persistence/database_pers.go @@ -1,6 +1,9 @@ package persistence import ( + "errors" + + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/domain" "gorm.io/gorm" ) @@ -25,6 +28,9 @@ func (p *databasePers) GetById(id string) (*domain.Database, error) { Where("id = ?", id). First(&database).Error if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, apperrors.ErrDatabaseNotFound + } return nil, err } return &database, nil @@ -171,6 +177,9 @@ func (p *databaseRowPers) GetById(id string) (*domain.DatabaseRow, error) { Where("id = ?", id). First(&row).Error if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, apperrors.ErrRowNotFound + } return nil, err } return &row, nil @@ -311,9 +320,9 @@ func (p *databaseRowPers) applyFilterRule(query *gorm.DB, rule domain.FilterRule } return query.Where(propertyPath+" LIKE ? COLLATE NOCASE", "%"+strValue) case "is_empty": - return query.Where(propertyPath+" IS NULL OR "+propertyPath+" = ''") + return query.Where(propertyPath + " IS NULL OR " + propertyPath + " = ''") case "is_not_empty": - return query.Where(propertyPath+" IS NOT NULL AND "+propertyPath+" != ''") + return query.Where(propertyPath + " IS NOT NULL AND " + propertyPath + " != ''") default: return query } diff --git a/infrastructure/persistence/document_pers.go b/infrastructure/persistence/document_pers.go index b0dc1aa..4158bc4 100644 --- a/infrastructure/persistence/document_pers.go +++ b/infrastructure/persistence/document_pers.go @@ -1,8 +1,10 @@ package persistence import ( + "errors" "fmt" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/domain" "gorm.io/gorm" ) @@ -26,6 +28,9 @@ func (p *documentPers) GetDocumentWithPermissions(documentId, userId string) (*d Where("id = ?", documentId). First(&doc).Error if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, apperrors.ErrDocumentNotFound + } return nil, err } return &doc, nil @@ -56,12 +61,15 @@ func (p *documentPers) GetDocumentByIdOrSlugWithUserPermissions(spaceId string, err := query.First(&doc).Error if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, apperrors.ErrDocumentNotFound + } return nil, err } // Verify if the user has at least viewer permissions if !doc.HasPermission(userId, domain.PermissionRoleViewer) { - return nil, fmt.Errorf("access denied: insufficient permissions") + return nil, apperrors.ErrAccessDenied } return &doc, nil @@ -139,7 +147,7 @@ func (p *documentPers) Create(document *domain.Document, userId string) error { // Check if the user can edit the parent document if !parentDoc.HasPermission(userId, domain.PermissionRoleEditor) { - return fmt.Errorf("access denied: insufficient permissions to create document in parent") + return apperrors.ErrAccessDenied } } else { // If it's a root document, check permissions on the space @@ -155,7 +163,7 @@ func (p *documentPers) Create(document *domain.Document, userId string) error { // Check if the user can edit in the space if !space.HasPermission(userId, domain.PermissionRoleEditor) { - return fmt.Errorf("access denied: insufficient permissions to create document in space") + return apperrors.ErrAccessDenied } } @@ -179,7 +187,7 @@ func (p *documentPers) Update(document *domain.Document, userId string) error { // Check if the user can edit the document if !existingDoc.HasPermission(userId, domain.PermissionRoleEditor) { - return fmt.Errorf("access denied: insufficient permissions to update document") + return apperrors.ErrAccessDenied } // Perform the update @@ -195,7 +203,7 @@ func (p *documentPers) Delete(documentId, userId string) error { // Check if the user can edit/delete the document if !existingDoc.HasPermission(userId, domain.PermissionRoleEditor) { - return fmt.Errorf("access denied: insufficient permissions to delete document") + return apperrors.ErrAccessDenied } // Prevent delete if there are active children @@ -223,12 +231,12 @@ func (p *documentPers) Move(documentId string, newParentId *string, userId strin } if !doc.HasPermission(userId, domain.PermissionRoleEditor) { - return nil, fmt.Errorf("access denied: insufficient permissions to move document") + return nil, apperrors.ErrAccessDenied } // Prevent self-parenting if newParentId != nil && *newParentId == documentId { - return nil, fmt.Errorf("invalid move: cannot set document as its own parent") + return nil, apperrors.ErrInvalidMove } // If moving under a parent, check permissions on parent and same space @@ -238,10 +246,10 @@ func (p *documentPers) Move(documentId string, newParentId *string, userId strin return nil, fmt.Errorf("failed to get parent document: %w", err) } if !parent.HasPermission(userId, domain.PermissionRoleEditor) { - return nil, fmt.Errorf("access denied: insufficient permissions on target parent") + return nil, apperrors.ErrAccessDenied } if parent.SpaceId != doc.SpaceId { - return nil, fmt.Errorf("invalid move: parent must be in the same space") + return nil, apperrors.ErrInvalidMove } doc.ParentId = newParentId } else { @@ -306,12 +314,12 @@ func (p *documentPers) Restore(documentId, userId string) error { // Check if user has editor permission on the space if !doc.Space.HasPermission(userId, domain.PermissionRoleEditor) { - return fmt.Errorf("access denied: insufficient permissions to restore document") + return apperrors.ErrAccessDenied } // Check if document is actually deleted if !doc.DeletedAt.Valid { - return fmt.Errorf("document is not deleted") + return apperrors.ErrDocumentNotDeleted } // If document had a parent, check if parent still exists @@ -337,7 +345,7 @@ func (p *documentPers) SetPublic(documentId string, public bool, userId string) // Check if user has editor permission if !doc.HasPermission(userId, domain.PermissionRoleEditor) { - return fmt.Errorf("access denied: insufficient permissions") + return apperrors.ErrAccessDenied } return p.db.Model(&domain.Document{}).Where("id = ?", documentId).Update("public", public).Error @@ -361,6 +369,9 @@ func (p *documentPers) GetPublicDocument(spaceId string, id *string, slug *strin err := query.First(&doc).Error if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, apperrors.ErrDocumentNotFound + } return nil, err } @@ -457,7 +468,7 @@ func (p *documentPers) Reorder(spaceId string, items []domain.ReorderItem, userI return fmt.Errorf("failed to get space: %w", err) } if !space.HasPermission(userId, domain.PermissionRoleEditor) { - return fmt.Errorf("access denied: insufficient permissions to reorder documents") + return apperrors.ErrAccessDenied } // Update positions in a transaction diff --git a/infrastructure/persistence/document_version_pers.go b/infrastructure/persistence/document_version_pers.go index 9794ce5..752b201 100644 --- a/infrastructure/persistence/document_version_pers.go +++ b/infrastructure/persistence/document_version_pers.go @@ -1,8 +1,10 @@ package persistence import ( + "errors" "fmt" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/domain" "gorm.io/gorm" ) @@ -49,6 +51,9 @@ func (p *documentVersionPers) GetById(versionId string) (*domain.DocumentVersion Where("id = ?", versionId). First(&version).Error if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, apperrors.ErrVersionNotFound + } return nil, err } return &version, nil @@ -61,7 +66,7 @@ func (p *documentVersionPers) GetLatestVersion(documentId string) (*domain.Docum Order("version DESC"). First(&version).Error if err != nil { - if err == gorm.ErrRecordNotFound { + if errors.Is(err, gorm.ErrRecordNotFound) { return nil, nil } return nil, err diff --git a/infrastructure/persistence/drawing_pers.go b/infrastructure/persistence/drawing_pers.go index 2c138e9..31e5285 100644 --- a/infrastructure/persistence/drawing_pers.go +++ b/infrastructure/persistence/drawing_pers.go @@ -1,6 +1,9 @@ package persistence import ( + "errors" + + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/domain" "gorm.io/gorm" ) @@ -25,6 +28,9 @@ func (p *drawingPers) GetById(id string) (*domain.Drawing, error) { Where("id = ?", id). First(&drawing).Error if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, apperrors.ErrDrawingNotFound + } return nil, err } return &drawing, nil diff --git a/infrastructure/persistence/favorite_pers.go b/infrastructure/persistence/favorite_pers.go index b87d786..5d984a6 100644 --- a/infrastructure/persistence/favorite_pers.go +++ b/infrastructure/persistence/favorite_pers.go @@ -1,6 +1,9 @@ package persistence import ( + "errors" + + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/domain" "gorm.io/gorm" ) @@ -53,6 +56,9 @@ func (f *favoritePers) GetFavoriteByIdAndUserId(favoriteId, userId string) (*dom var favorite domain.Favorite err := f.db.Where("id = ? AND user_id = ?", favoriteId, userId).First(&favorite).Error if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, apperrors.ErrFavoriteNotFound + } return nil, err } return &favorite, nil diff --git a/infrastructure/persistence/group_pers.go b/infrastructure/persistence/group_pers.go index 3bf068d..447a975 100644 --- a/infrastructure/persistence/group_pers.go +++ b/infrastructure/persistence/group_pers.go @@ -1,7 +1,10 @@ package persistence import ( + "errors" + "github.com/google/uuid" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/domain" "gorm.io/gorm" ) @@ -25,6 +28,9 @@ func (g *groupPers) GetById(groupId string) (*domain.Group, error) { var group domain.Group err := g.db.Preload("Members").Preload("OwnerUser").Where("id = ?", groupId).First(&group).Error if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, apperrors.ErrNotFound + } return nil, err } return &group, nil @@ -50,7 +56,7 @@ func (g *groupPers) GetAll(limit, offset int) ([]domain.Group, int64, error) { } func (g *groupPers) Update(group *domain.Group) error { - return g.db.Model(group).Updates(map[string]interface{}{ + return g.db.Model(group).Updates(map[string]any{ "name": group.Name, "description": group.Description, "role": group.Role, diff --git a/infrastructure/persistence/permission_pers.go b/infrastructure/persistence/permission_pers.go index ee3c326..2407276 100644 --- a/infrastructure/persistence/permission_pers.go +++ b/infrastructure/persistence/permission_pers.go @@ -1,7 +1,10 @@ package persistence import ( + "errors" + "github.com/gofiber/fiber/v2/utils" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/domain" "gorm.io/gorm" ) @@ -53,6 +56,9 @@ func (p *permissionPers) GetByResourceAndUser(resourceType domain.PermissionType err := p.db.Where("type = ? AND "+column+" = ? AND user_id = ? AND deleted_at IS NULL", resourceType, resourceId, userId). First(&perm).Error if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, apperrors.ErrNotFound + } return nil, err } return &perm, nil @@ -68,6 +74,9 @@ func (p *permissionPers) GetByResourceAndGroup(resourceType domain.PermissionTyp err := p.db.Where("type = ? AND "+column+" = ? AND group_id = ? AND deleted_at IS NULL", resourceType, resourceId, groupId). First(&perm).Error if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, apperrors.ErrNotFound + } return nil, err } return &perm, nil diff --git a/infrastructure/persistence/session_pers.go b/infrastructure/persistence/session_pers.go index 5d70a0b..9ba60d3 100644 --- a/infrastructure/persistence/session_pers.go +++ b/infrastructure/persistence/session_pers.go @@ -1,6 +1,9 @@ package persistence import ( + "errors" + + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/domain" "gorm.io/gorm" ) @@ -21,6 +24,9 @@ func (s *sessionPers) GetById(id string) (*domain.Session, error) { var session domain.Session err := s.db.Where("id = ?", id).First(&session).Error if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, apperrors.ErrNotFound + } return nil, err } return &session, nil diff --git a/infrastructure/persistence/space_pers.go b/infrastructure/persistence/space_pers.go index 7a9c4fe..57a9ae0 100644 --- a/infrastructure/persistence/space_pers.go +++ b/infrastructure/persistence/space_pers.go @@ -1,6 +1,9 @@ package persistence import ( + "errors" + + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/domain" "gorm.io/gorm" ) @@ -48,6 +51,9 @@ func (s *spacePers) GetSpaceById(spaceId string) (*domain.Space, error) { First(&space, "id = ?", spaceId).Error if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, apperrors.ErrSpaceNotFound + } return nil, err } diff --git a/infrastructure/persistence/user_pers.go b/infrastructure/persistence/user_pers.go index afc4cd6..978fc0f 100644 --- a/infrastructure/persistence/user_pers.go +++ b/infrastructure/persistence/user_pers.go @@ -1,6 +1,9 @@ package persistence import ( + "errors" + + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/domain" "gorm.io/gorm" ) @@ -16,12 +19,18 @@ func NewUserPers(db *gorm.DB) *userPers { func (u *userPers) GetByUsername(username string) (domain.User, error) { var user domain.User err := u.db.Debug().Where("username = ?", username).First(&user).Error + if errors.Is(err, gorm.ErrRecordNotFound) { + return user, apperrors.ErrUserNotFound + } return user, err } func (u *userPers) GetByEmail(email string) (domain.User, error) { var user domain.User err := u.db.Debug().Where("email = ?", email).First(&user).Error + if errors.Is(err, gorm.ErrRecordNotFound) { + return user, apperrors.ErrUserNotFound + } return user, err } @@ -33,11 +42,14 @@ func (u *userPers) Create(user domain.User) (domain.User, error) { func (u *userPers) GetById(id string) (domain.User, error) { var user domain.User err := u.db.Debug().Where("id = ?", id).First(&user).Error + if errors.Is(err, gorm.ErrRecordNotFound) { + return user, apperrors.ErrUserNotFound + } return user, err } func (u *userPers) Update(user *domain.User) error { - return u.db.Model(user).Updates(map[string]interface{}{ + return u.db.Model(user).Updates(map[string]any{ "username": user.Username, "avatar_url": user.AvatarUrl, "preferences": user.Preferences, diff --git a/infrastructure/persistence/webhook_pers.go b/infrastructure/persistence/webhook_pers.go index 52681e9..2c229f7 100644 --- a/infrastructure/persistence/webhook_pers.go +++ b/infrastructure/persistence/webhook_pers.go @@ -1,8 +1,10 @@ package persistence import ( + "errors" "time" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" "github.com/labbs/nexo/domain" "gorm.io/gorm" ) @@ -27,6 +29,9 @@ func (p *webhookPers) GetById(id string) (*domain.Webhook, error) { Where("id = ?", id). First(&webhook).Error if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, apperrors.ErrWebhookNotFound + } return nil, err } return &webhook, nil @@ -83,7 +88,7 @@ func (p *webhookPers) Delete(id string) error { func (p *webhookPers) IncrementSuccess(id string) error { return p.db.Model(&domain.Webhook{}). Where("id = ?", id). - Updates(map[string]interface{}{ + Updates(map[string]any{ "success_count": gorm.Expr("success_count + 1"), "last_error": "", "last_error_at": nil, @@ -94,7 +99,7 @@ func (p *webhookPers) RecordFailure(id string, errorMsg string) error { now := time.Now() return p.db.Model(&domain.Webhook{}). Where("id = ?", id). - Updates(map[string]interface{}{ + Updates(map[string]any{ "failure_count": gorm.Expr("failure_count + 1"), "last_error": errorMsg, "last_error_at": &now, diff --git a/interfaces/http/v1/action/dtos/action.go b/interfaces/http/v1/action/dtos/action.go index fd59a07..a004433 100644 --- a/interfaces/http/v1/action/dtos/action.go +++ b/interfaces/http/v1/action/dtos/action.go @@ -7,18 +7,18 @@ import "time" type EmptyRequest struct{} type ActionStep struct { - Type string `json:"type"` - Config map[string]interface{} `json:"config"` + Type string `json:"type"` + Config map[string]any `json:"config"` } type CreateActionRequest struct { - SpaceId *string `json:"space_id,omitempty"` - DatabaseId *string `json:"database_id,omitempty"` - Name string `json:"name"` - Description string `json:"description,omitempty"` - TriggerType string `json:"trigger_type"` - TriggerConfig map[string]interface{} `json:"trigger_config,omitempty"` - Steps []ActionStep `json:"steps"` + SpaceId *string `json:"space_id,omitempty"` + DatabaseId *string `json:"database_id,omitempty"` + Name string `json:"name"` + Description string `json:"description,omitempty"` + TriggerType string `json:"trigger_type"` + TriggerConfig map[string]any `json:"trigger_config,omitempty"` + Steps []ActionStep `json:"steps"` } type GetActionRequest struct { @@ -26,13 +26,13 @@ type GetActionRequest struct { } type UpdateActionRequest struct { - ActionId string `path:"action_id"` - Name *string `json:"name,omitempty"` - Description *string `json:"description,omitempty"` - TriggerType *string `json:"trigger_type,omitempty"` - TriggerConfig map[string]interface{} `json:"trigger_config,omitempty"` - Steps *[]ActionStep `json:"steps,omitempty"` - Active *bool `json:"active,omitempty"` + ActionId string `path:"action_id"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + TriggerType *string `json:"trigger_type,omitempty"` + TriggerConfig map[string]any `json:"trigger_config,omitempty"` + Steps *[]ActionStep `json:"steps,omitempty"` + Active *bool `json:"active,omitempty"` } type DeleteActionRequest struct { @@ -81,23 +81,23 @@ type ListActionsResponse struct { } type GetActionResponse struct { - Id string `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - SpaceId *string `json:"space_id,omitempty"` - SpaceName *string `json:"space_name,omitempty"` - DatabaseId *string `json:"database_id,omitempty"` - TriggerType string `json:"trigger_type"` - TriggerConfig map[string]interface{} `json:"trigger_config,omitempty"` - Steps []ActionStep `json:"steps"` - Active bool `json:"active"` - LastRunAt *time.Time `json:"last_run_at,omitempty"` - LastError string `json:"last_error,omitempty"` - RunCount int `json:"run_count"` - SuccessCount int `json:"success_count"` - FailureCount int `json:"failure_count"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` + Id string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + SpaceId *string `json:"space_id,omitempty"` + SpaceName *string `json:"space_name,omitempty"` + DatabaseId *string `json:"database_id,omitempty"` + TriggerType string `json:"trigger_type"` + TriggerConfig map[string]any `json:"trigger_config,omitempty"` + Steps []ActionStep `json:"steps"` + Active bool `json:"active"` + LastRunAt *time.Time `json:"last_run_at,omitempty"` + LastError string `json:"last_error,omitempty"` + RunCount int `json:"run_count"` + SuccessCount int `json:"success_count"` + FailureCount int `json:"failure_count"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` } type RunItem struct { diff --git a/interfaces/http/v1/action/handlers.go b/interfaces/http/v1/action/handlers.go index 438ecdf..47fa0f9 100644 --- a/interfaces/http/v1/action/handlers.go +++ b/interfaces/http/v1/action/handlers.go @@ -1,10 +1,11 @@ package action import ( - "strings" + "errors" "github.com/gofiber/fiber/v2" fiberoapi "github.com/labbs/fiber-oapi" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" actionDto "github.com/labbs/nexo/application/action/dto" "github.com/labbs/nexo/interfaces/http/v1/action/dtos" ) @@ -121,10 +122,10 @@ func (ctrl *Controller) GetAction(ctx *fiber.Ctx, req dtos.GetActionRequest) (*d ActionId: req.ActionId, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } - if strings.Contains(err.Error(), "not found") { + if errors.Is(err, apperrors.ErrNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Action not found", Type: "NOT_FOUND"} } logger.Error().Err(err).Msg("failed to get action") @@ -193,10 +194,10 @@ func (ctrl *Controller) UpdateAction(ctx *fiber.Ctx, req dtos.UpdateActionReques Active: req.Active, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } - if strings.Contains(err.Error(), "not found") { + if errors.Is(err, apperrors.ErrNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Action not found", Type: "NOT_FOUND"} } logger.Error().Err(err).Msg("failed to update action") @@ -221,10 +222,10 @@ func (ctrl *Controller) DeleteAction(ctx *fiber.Ctx, req dtos.DeleteActionReques ActionId: req.ActionId, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } - if strings.Contains(err.Error(), "not found") { + if errors.Is(err, apperrors.ErrNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Action not found", Type: "NOT_FOUND"} } logger.Error().Err(err).Msg("failed to delete action") @@ -255,10 +256,10 @@ func (ctrl *Controller) GetRuns(ctx *fiber.Ctx, req dtos.GetRunsRequest) (*dtos. Limit: limit, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } - if strings.Contains(err.Error(), "not found") { + if errors.Is(err, apperrors.ErrNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Action not found", Type: "NOT_FOUND"} } logger.Error().Err(err).Msg("failed to get runs") diff --git a/interfaces/http/v1/admin/handlers.go b/interfaces/http/v1/admin/handlers.go index 50231d9..214020d 100644 --- a/interfaces/http/v1/admin/handlers.go +++ b/interfaces/http/v1/admin/handlers.go @@ -288,7 +288,7 @@ func (ctrl *Controller) ListAllApiKeys(ctx *fiber.Ctx, req dtos.ListAllApiKeysRe // Extract permissions as string array var permissions []string if k.Permissions != nil { - if scopes, ok := k.Permissions["scopes"].([]interface{}); ok { + if scopes, ok := k.Permissions["scopes"].([]any); ok { for _, s := range scopes { if str, ok := s.(string); ok { permissions = append(permissions, str) diff --git a/interfaces/http/v1/apikey/handlers.go b/interfaces/http/v1/apikey/handlers.go index e8bd5ae..960cdf1 100644 --- a/interfaces/http/v1/apikey/handlers.go +++ b/interfaces/http/v1/apikey/handlers.go @@ -1,10 +1,11 @@ package apikey import ( - "strings" + "errors" "github.com/gofiber/fiber/v2" fiberoapi "github.com/labbs/fiber-oapi" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" apikeyDto "github.com/labbs/nexo/application/apikey/dto" "github.com/labbs/nexo/interfaces/http/v1/apikey/dtos" ) @@ -100,10 +101,10 @@ func (ctrl *Controller) UpdateApiKey(ctx *fiber.Ctx, req dtos.UpdateApiKeyReques Scopes: req.Scopes, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } - if strings.Contains(err.Error(), "not found") { + if errors.Is(err, apperrors.ErrNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "API key not found", Type: "NOT_FOUND"} } logger.Error().Err(err).Msg("failed to update API key") @@ -128,10 +129,10 @@ func (ctrl *Controller) DeleteApiKey(ctx *fiber.Ctx, req dtos.DeleteApiKeyReques ApiKeyId: req.ApiKeyId, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } - if strings.Contains(err.Error(), "not found") { + if errors.Is(err, apperrors.ErrNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "API key not found", Type: "NOT_FOUND"} } logger.Error().Err(err).Msg("failed to delete API key") diff --git a/interfaces/http/v1/auth/handlers.go b/interfaces/http/v1/auth/handlers.go index 8ccd25a..4ba3dea 100644 --- a/interfaces/http/v1/auth/handlers.go +++ b/interfaces/http/v1/auth/handlers.go @@ -1,8 +1,11 @@ package auth import ( + "errors" + "github.com/gofiber/fiber/v2" fiberoapi "github.com/labbs/fiber-oapi" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" authDto "github.com/labbs/nexo/application/auth/dto" "github.com/labbs/nexo/interfaces/http/v1/auth/dtos" ) @@ -18,10 +21,17 @@ func (ctrl Controller) Login(ctx *fiber.Ctx, req dtos.LoginRequest) (*dtos.Login }) if err != nil { logger.Error().Err(err).Str("email", req.Email).Msg("failed to authenticate user") + if errors.Is(err, apperrors.ErrInvalidCredentials) || errors.Is(err, apperrors.ErrUserNotActive) { + return nil, &fiberoapi.ErrorResponse{ + Code: fiber.StatusUnauthorized, + Details: err.Error(), + Type: "AUTHENTICATION_FAILED", + } + } return nil, &fiberoapi.ErrorResponse{ - Code: fiber.StatusUnauthorized, - Details: err.Error(), - Type: "AUTHENTICATION_FAILED", + Code: fiber.StatusInternalServerError, + Details: "Authentication failed", + Type: "INTERNAL_SERVER_ERROR", } } return &dtos.LoginResponse{Token: resp.Token}, nil diff --git a/interfaces/http/v1/database/dtos/database.go b/interfaces/http/v1/database/dtos/database.go index 8836c19..f970ef0 100644 --- a/interfaces/http/v1/database/dtos/database.go +++ b/interfaces/http/v1/database/dtos/database.go @@ -7,21 +7,21 @@ import "time" type EmptyRequest struct{} type PropertySchema struct { - Id string `json:"id"` - Name string `json:"name"` - Type string `json:"type"` - Options map[string]interface{} `json:"options,omitempty"` + Id string `json:"id"` + Name string `json:"name"` + Type string `json:"type"` + Options map[string]any `json:"options,omitempty"` } type ViewConfig struct { - Id string `json:"id"` - Name string `json:"name"` - Type string `json:"type"` - Filter map[string]interface{} `json:"filter,omitempty"` - Sort []SortConfig `json:"sort,omitempty"` - Columns []string `json:"columns,omitempty"` - HiddenColumns []string `json:"hidden_columns,omitempty"` - GroupBy string `json:"group_by,omitempty"` + Id string `json:"id"` + Name string `json:"name"` + Type string `json:"type"` + Filter map[string]any `json:"filter,omitempty"` + Sort []SortConfig `json:"sort,omitempty"` + Columns []string `json:"columns,omitempty"` + HiddenColumns []string `json:"hidden_columns,omitempty"` + GroupBy string `json:"group_by,omitempty"` } type SortConfig struct { @@ -62,10 +62,10 @@ type DeleteDatabaseRequest struct { // Row requests type CreateRowRequest struct { - DatabaseId string `path:"database_id"` - Properties map[string]interface{} `json:"properties"` - Content map[string]interface{} `json:"content,omitempty"` - ShowInSidebar bool `json:"show_in_sidebar,omitempty"` + DatabaseId string `path:"database_id"` + Properties map[string]any `json:"properties"` + Content map[string]any `json:"content,omitempty"` + ShowInSidebar bool `json:"show_in_sidebar,omitempty"` } type ListRowsRequest struct { @@ -81,11 +81,11 @@ type GetRowRequest struct { } type UpdateRowRequest struct { - DatabaseId string `path:"database_id"` - RowId string `path:"row_id"` - Properties map[string]interface{} `json:"properties,omitempty"` - Content map[string]interface{} `json:"content,omitempty"` - ShowInSidebar *bool `json:"show_in_sidebar,omitempty"` + DatabaseId string `path:"database_id"` + RowId string `path:"row_id"` + Properties map[string]any `json:"properties,omitempty"` + Content map[string]any `json:"content,omitempty"` + ShowInSidebar *bool `json:"show_in_sidebar,omitempty"` } type DeleteRowRequest struct { @@ -100,24 +100,24 @@ type BulkDeleteRowsRequest struct { // View requests type CreateViewRequest struct { - DatabaseId string `path:"database_id"` - Name string `json:"name" validate:"required"` - Type string `json:"type" validate:"required,oneof=table board calendar gallery list timeline"` - Filter map[string]interface{} `json:"filter,omitempty"` - Sort []SortConfig `json:"sort,omitempty"` - Columns []string `json:"columns,omitempty"` + DatabaseId string `path:"database_id"` + Name string `json:"name" validate:"required"` + Type string `json:"type" validate:"required,oneof=table board calendar gallery list timeline"` + Filter map[string]any `json:"filter,omitempty"` + Sort []SortConfig `json:"sort,omitempty"` + Columns []string `json:"columns,omitempty"` } type UpdateViewRequest struct { - DatabaseId string `path:"database_id"` - ViewId string `path:"view_id"` - Name *string `json:"name,omitempty"` - Type *string `json:"type,omitempty"` - Filter map[string]interface{} `json:"filter,omitempty"` - Sort []SortConfig `json:"sort,omitempty"` - Columns []string `json:"columns,omitempty"` - HiddenColumns []string `json:"hidden_columns,omitempty"` - GroupBy *string `json:"group_by,omitempty"` + DatabaseId string `path:"database_id"` + ViewId string `path:"view_id"` + Name *string `json:"name,omitempty"` + Type *string `json:"type,omitempty"` + Filter map[string]any `json:"filter,omitempty"` + Sort []SortConfig `json:"sort,omitempty"` + Columns []string `json:"columns,omitempty"` + HiddenColumns []string `json:"hidden_columns,omitempty"` + GroupBy *string `json:"group_by,omitempty"` } type DeleteViewRequest struct { @@ -138,9 +138,9 @@ type MoveDatabaseResponse struct { // Filter rule for querying rows type FilterRule struct { - Property string `json:"property"` - Condition string `json:"condition"` // eq, neq, gt, lt, gte, lte, contains, is_empty, is_not_empty - Value interface{} `json:"value,omitempty"` + Property string `json:"property"` + Condition string `json:"condition"` // eq, neq, gt, lt, gte, lte, contains, is_empty, is_not_empty + Value any `json:"value,omitempty"` } type FilterConfig struct { @@ -206,22 +206,22 @@ type GetDatabaseResponse struct { } type CreateRowResponse struct { - Id string `json:"id"` - Properties map[string]interface{} `json:"properties"` - CreatedAt time.Time `json:"created_at"` + Id string `json:"id"` + Properties map[string]any `json:"properties"` + CreatedAt time.Time `json:"created_at"` } type RowItem struct { - Id string `json:"id"` - Properties map[string]interface{} `json:"properties"` - Content map[string]interface{} `json:"content,omitempty"` - ShowInSidebar bool `json:"show_in_sidebar"` - CreatedBy string `json:"created_by"` - CreatedByUser *UserInfo `json:"created_by_user,omitempty"` - UpdatedBy string `json:"updated_by,omitempty"` - UpdatedByUser *UserInfo `json:"updated_by_user,omitempty"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` + Id string `json:"id"` + Properties map[string]any `json:"properties"` + Content map[string]any `json:"content,omitempty"` + ShowInSidebar bool `json:"show_in_sidebar"` + CreatedBy string `json:"created_by"` + CreatedByUser *UserInfo `json:"created_by_user,omitempty"` + UpdatedBy string `json:"updated_by,omitempty"` + UpdatedByUser *UserInfo `json:"updated_by_user,omitempty"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` } type ListRowsResponse struct { @@ -230,27 +230,27 @@ type ListRowsResponse struct { } type GetRowResponse struct { - Id string `json:"id"` - DatabaseId string `json:"database_id"` - Properties map[string]interface{} `json:"properties"` - Content map[string]interface{} `json:"content,omitempty"` - ShowInSidebar bool `json:"show_in_sidebar"` - CreatedBy string `json:"created_by"` - CreatedByUser *UserInfo `json:"created_by_user,omitempty"` - UpdatedBy string `json:"updated_by,omitempty"` - UpdatedByUser *UserInfo `json:"updated_by_user,omitempty"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` + Id string `json:"id"` + DatabaseId string `json:"database_id"` + Properties map[string]any `json:"properties"` + Content map[string]any `json:"content,omitempty"` + ShowInSidebar bool `json:"show_in_sidebar"` + CreatedBy string `json:"created_by"` + CreatedByUser *UserInfo `json:"created_by_user,omitempty"` + UpdatedBy string `json:"updated_by,omitempty"` + UpdatedByUser *UserInfo `json:"updated_by_user,omitempty"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` } // View responses type CreateViewResponse struct { - Id string `json:"id"` - Name string `json:"name"` - Type string `json:"type"` - Filter map[string]interface{} `json:"filter,omitempty"` - Sort []SortConfig `json:"sort,omitempty"` - Columns []string `json:"columns,omitempty"` + Id string `json:"id"` + Name string `json:"name"` + Type string `json:"type"` + Filter map[string]any `json:"filter,omitempty"` + Sort []SortConfig `json:"sort,omitempty"` + Columns []string `json:"columns,omitempty"` } // Available property types diff --git a/interfaces/http/v1/database/handlers.go b/interfaces/http/v1/database/handlers.go index 7042940..64e5f9b 100644 --- a/interfaces/http/v1/database/handlers.go +++ b/interfaces/http/v1/database/handlers.go @@ -1,10 +1,11 @@ package database import ( - "strings" + "errors" "github.com/gofiber/fiber/v2" fiberoapi "github.com/labbs/fiber-oapi" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" databaseDto "github.com/labbs/nexo/application/database/dto" "github.com/labbs/nexo/interfaces/http/v1/database/dtos" ) @@ -49,7 +50,7 @@ func (ctrl *Controller) CreateDatabase(ctx *fiber.Ctx, req dtos.CreateDatabaseRe Type: req.Type, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } logger.Error().Err(err).Msg("failed to create database") @@ -97,7 +98,7 @@ func (ctrl *Controller) ListDatabases(ctx *fiber.Ctx, req dtos.ListDatabasesRequ SpaceId: req.SpaceId, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } logger.Error().Err(err).Msg("failed to list databases") @@ -138,10 +139,10 @@ func (ctrl *Controller) GetDatabase(ctx *fiber.Ctx, req dtos.GetDatabaseRequest) DatabaseId: req.DatabaseId, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } - if strings.Contains(err.Error(), "not found") { + if errors.Is(err, apperrors.ErrNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Database not found", Type: "NOT_FOUND"} } logger.Error().Err(err).Msg("failed to get database") @@ -229,10 +230,10 @@ func (ctrl *Controller) UpdateDatabase(ctx *fiber.Ctx, req dtos.UpdateDatabaseRe DefaultView: req.DefaultView, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } - if strings.Contains(err.Error(), "not found") { + if errors.Is(err, apperrors.ErrNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Database not found", Type: "NOT_FOUND"} } logger.Error().Err(err).Msg("failed to update database") @@ -257,10 +258,10 @@ func (ctrl *Controller) DeleteDatabase(ctx *fiber.Ctx, req dtos.DeleteDatabaseRe DatabaseId: req.DatabaseId, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } - if strings.Contains(err.Error(), "not found") { + if errors.Is(err, apperrors.ErrNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Database not found", Type: "NOT_FOUND"} } logger.Error().Err(err).Msg("failed to delete database") @@ -336,10 +337,10 @@ func (ctrl *Controller) CreateView(ctx *fiber.Ctx, req dtos.CreateViewRequest) ( Columns: req.Columns, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } - if strings.Contains(err.Error(), "not found") { + if errors.Is(err, apperrors.ErrNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Database not found", Type: "NOT_FOUND"} } logger.Error().Err(err).Msg("failed to create view") @@ -399,10 +400,10 @@ func (ctrl *Controller) UpdateView(ctx *fiber.Ctx, req dtos.UpdateViewRequest) ( GroupBy: req.GroupBy, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } - if strings.Contains(err.Error(), "not found") { + if errors.Is(err, apperrors.ErrNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "View not found", Type: "NOT_FOUND"} } logger.Error().Err(err).Msg("failed to update view") @@ -428,13 +429,13 @@ func (ctrl *Controller) DeleteView(ctx *fiber.Ctx, req dtos.DeleteViewRequest) ( ViewId: req.ViewId, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } - if strings.Contains(err.Error(), "not found") { + if errors.Is(err, apperrors.ErrNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "View not found", Type: "NOT_FOUND"} } - if strings.Contains(err.Error(), "cannot delete last view") { + if errors.Is(err, apperrors.ErrConflict) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusBadRequest, Details: "Cannot delete the last view", Type: "BAD_REQUEST"} } logger.Error().Err(err).Msg("failed to delete view") @@ -464,10 +465,10 @@ func (ctrl *Controller) CreateRow(ctx *fiber.Ctx, req dtos.CreateRowRequest) (*d ShowInSidebar: req.ShowInSidebar, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } - if strings.Contains(err.Error(), "not found") { + if errors.Is(err, apperrors.ErrNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Database not found", Type: "NOT_FOUND"} } logger.Error().Err(err).Msg("failed to create row") @@ -499,10 +500,10 @@ func (ctrl *Controller) ListRows(ctx *fiber.Ctx, req dtos.ListRowsRequest) (*dto Offset: req.Offset, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } - if strings.Contains(err.Error(), "not found") { + if errors.Is(err, apperrors.ErrNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Database not found", Type: "NOT_FOUND"} } logger.Error().Err(err).Msg("failed to list rows") @@ -560,10 +561,10 @@ func (ctrl *Controller) GetRow(ctx *fiber.Ctx, req dtos.GetRowRequest) (*dtos.Ge RowId: req.RowId, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } - if strings.Contains(err.Error(), "not found") { + if errors.Is(err, apperrors.ErrNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Row not found", Type: "NOT_FOUND"} } logger.Error().Err(err).Msg("failed to get row") @@ -617,10 +618,10 @@ func (ctrl *Controller) UpdateRow(ctx *fiber.Ctx, req dtos.UpdateRowRequest) (*d ShowInSidebar: req.ShowInSidebar, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } - if strings.Contains(err.Error(), "not found") { + if errors.Is(err, apperrors.ErrNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Row not found", Type: "NOT_FOUND"} } logger.Error().Err(err).Msg("failed to update row") @@ -646,10 +647,10 @@ func (ctrl *Controller) DeleteRow(ctx *fiber.Ctx, req dtos.DeleteRowRequest) (*d RowId: req.RowId, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } - if strings.Contains(err.Error(), "not found") { + if errors.Is(err, apperrors.ErrNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Row not found", Type: "NOT_FOUND"} } logger.Error().Err(err).Msg("failed to delete row") @@ -675,10 +676,10 @@ func (ctrl *Controller) MoveDatabase(ctx *fiber.Ctx, req dtos.MoveDatabaseReques DocumentId: req.DocumentId, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } - if strings.Contains(err.Error(), "not found") { + if errors.Is(err, apperrors.ErrNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Database not found", Type: "NOT_FOUND"} } logger.Error().Err(err).Msg("failed to move database") @@ -711,10 +712,10 @@ func (ctrl *Controller) BulkDeleteRows(ctx *fiber.Ctx, req dtos.BulkDeleteRowsRe RowIds: req.RowIds, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } - if strings.Contains(err.Error(), "not found") { + if errors.Is(err, apperrors.ErrNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Database not found", Type: "NOT_FOUND"} } logger.Error().Err(err).Msg("failed to delete rows") diff --git a/interfaces/http/v1/database/handlers_permissions.go b/interfaces/http/v1/database/handlers_permissions.go index abf49ea..7849eb9 100644 --- a/interfaces/http/v1/database/handlers_permissions.go +++ b/interfaces/http/v1/database/handlers_permissions.go @@ -1,10 +1,11 @@ package database import ( - "strings" + "errors" "github.com/gofiber/fiber/v2" fiberoapi "github.com/labbs/fiber-oapi" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" databaseDto "github.com/labbs/nexo/application/database/dto" "github.com/labbs/nexo/interfaces/http/v1/database/dtos" ) @@ -24,14 +25,15 @@ func (ctrl *Controller) ListDatabasePermissions(ctx *fiber.Ctx, req dtos.ListDat DatabaseId: req.DatabaseId, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + switch { + case errors.Is(err, apperrors.ErrAccessDenied) || errors.Is(err, apperrors.ErrForbidden): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} - } - if strings.Contains(err.Error(), "not found") { + case errors.Is(err, apperrors.ErrNotFound) || errors.Is(err, apperrors.ErrDatabaseNotFound): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Database not found", Type: "NOT_FOUND"} + default: + logger.Error().Err(err).Msg("failed to list permissions") + return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusInternalServerError, Details: "Failed to list permissions", Type: "INTERNAL_SERVER_ERROR"} } - logger.Error().Err(err).Msg("failed to list permissions") - return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusInternalServerError, Details: "Failed to list permissions", Type: "INTERNAL_SERVER_ERROR"} } permissions := make([]dtos.DatabasePermissionItem, len(result.Permissions)) @@ -69,17 +71,17 @@ func (ctrl *Controller) UpsertDatabasePermission(ctx *fiber.Ctx, req dtos.Upsert Role: req.Role, }) if err != nil { - if strings.Contains(err.Error(), "access denied") || strings.Contains(err.Error(), "only creator") { - return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: err.Error(), Type: "FORBIDDEN"} - } - if strings.Contains(err.Error(), "not found") { + switch { + case errors.Is(err, apperrors.ErrAccessDenied) || errors.Is(err, apperrors.ErrForbidden): + return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} + case errors.Is(err, apperrors.ErrNotFound) || errors.Is(err, apperrors.ErrDatabaseNotFound): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Database not found", Type: "NOT_FOUND"} - } - if strings.Contains(err.Error(), "invalid role") || strings.Contains(err.Error(), "must provide") { + case errors.Is(err, apperrors.ErrInvalidInput): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusBadRequest, Details: err.Error(), Type: "BAD_REQUEST"} + default: + logger.Error().Err(err).Msg("failed to upsert permission") + return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusInternalServerError, Details: "Failed to upsert permission", Type: "INTERNAL_SERVER_ERROR"} } - logger.Error().Err(err).Msg("failed to upsert permission") - return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusInternalServerError, Details: "Failed to upsert permission", Type: "INTERNAL_SERVER_ERROR"} } return &dtos.UpsertDatabasePermissionResponse{ @@ -104,17 +106,17 @@ func (ctrl *Controller) DeleteDatabasePermission(ctx *fiber.Ctx, req dtos.Delete GroupId: req.GroupId, }) if err != nil { - if strings.Contains(err.Error(), "access denied") || strings.Contains(err.Error(), "only creator") { - return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: err.Error(), Type: "FORBIDDEN"} - } - if strings.Contains(err.Error(), "not found") { + switch { + case errors.Is(err, apperrors.ErrAccessDenied) || errors.Is(err, apperrors.ErrForbidden): + return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} + case errors.Is(err, apperrors.ErrNotFound) || errors.Is(err, apperrors.ErrDatabaseNotFound): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Database not found", Type: "NOT_FOUND"} - } - if strings.Contains(err.Error(), "must provide") { + case errors.Is(err, apperrors.ErrInvalidInput): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusBadRequest, Details: err.Error(), Type: "BAD_REQUEST"} + default: + logger.Error().Err(err).Msg("failed to delete permission") + return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusInternalServerError, Details: "Failed to delete permission", Type: "INTERNAL_SERVER_ERROR"} } - logger.Error().Err(err).Msg("failed to delete permission") - return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusInternalServerError, Details: "Failed to delete permission", Type: "INTERNAL_SERVER_ERROR"} } return &dtos.DeleteDatabasePermissionResponse{ diff --git a/interfaces/http/v1/document/dtos/move_document_request.go b/interfaces/http/v1/document/dtos/move_document_request.go index fbca905..2e46e07 100644 --- a/interfaces/http/v1/document/dtos/move_document_request.go +++ b/interfaces/http/v1/document/dtos/move_document_request.go @@ -11,7 +11,3 @@ type MoveDocumentResponse struct { ParentId *string `json:"parent_id,omitempty"` SpaceId string `json:"space_id"` } - - - - diff --git a/interfaces/http/v1/document/handlers.go b/interfaces/http/v1/document/handlers.go index bf02411..4007ee1 100644 --- a/interfaces/http/v1/document/handlers.go +++ b/interfaces/http/v1/document/handlers.go @@ -1,10 +1,11 @@ package document import ( - "strings" + "errors" "github.com/gofiber/fiber/v2" fiberoapi "github.com/labbs/fiber-oapi" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" docDto "github.com/labbs/nexo/application/document/dto" "github.com/labbs/nexo/domain" "github.com/labbs/nexo/infrastructure/helpers/mapper" @@ -374,14 +375,12 @@ func (ctrl *Controller) DeleteDocument(ctx *fiber.Ctx, req dtos.DeleteDocumentRe DocumentId: id, Slug: slug, }); err != nil { - // Best-effort error typing - details := err.Error() switch { - case strings.Contains(details, "insufficient permissions") || strings.Contains(details, "permission"): + case errors.Is(err, apperrors.ErrAccessDenied) || errors.Is(err, apperrors.ErrForbidden): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} - case strings.Contains(details, "child") || strings.Contains(details, "children"): + case errors.Is(err, apperrors.ErrConflictChildren): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusConflict, Details: "Document has child pages", Type: "CONFLICT"} - case strings.Contains(details, "not found"): + case errors.Is(err, apperrors.ErrDocumentNotFound) || errors.Is(err, apperrors.ErrNotFound): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Document not found", Type: "DOCUMENT_NOT_FOUND"} default: logger.Error().Err(err).Msg("failed to delete document") @@ -409,13 +408,12 @@ func (ctrl *Controller) MoveDocument(ctx *fiber.Ctx, req dtos.MoveDocumentReques NewParentId: req.ParentId, }) if err != nil { - details := err.Error() switch { - case strings.Contains(details, "insufficient permissions"): + case errors.Is(err, apperrors.ErrAccessDenied) || errors.Is(err, apperrors.ErrForbidden): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} - case strings.Contains(details, "invalid move"): - return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusBadRequest, Details: details, Type: "BAD_REQUEST"} - case strings.Contains(details, "not found"): + case errors.Is(err, apperrors.ErrInvalidMove): + return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusBadRequest, Details: err.Error(), Type: "BAD_REQUEST"} + case errors.Is(err, apperrors.ErrDocumentNotFound) || errors.Is(err, apperrors.ErrNotFound): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Document or parent not found", Type: "NOT_FOUND"} default: logger.Error().Err(err).Msg("failed to move document") @@ -473,10 +471,10 @@ func (ctrl *Controller) ListDocumentPermissions(ctx *fiber.Ctx, req dtos.ListDoc DocumentId: req.DocumentId, }) if err != nil { - switch err.Error() { - case "forbidden": + switch { + case errors.Is(err, apperrors.ErrForbidden): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} - case "not_found": + case errors.Is(err, apperrors.ErrDocumentNotFound) || errors.Is(err, apperrors.ErrNotFound): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Document not found", Type: "DOCUMENT_NOT_FOUND"} default: logger.Error().Err(err).Msg("failed to list document permissions") @@ -524,10 +522,10 @@ func (ctrl *Controller) UpsertDocumentUserPermission(ctx *fiber.Ctx, req dtos.Up TargetUserId: req.UserId, Role: role, }); err != nil { - switch err.Error() { - case "forbidden": + switch { + case errors.Is(err, apperrors.ErrForbidden): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} - case "not_found": + case errors.Is(err, apperrors.ErrDocumentNotFound) || errors.Is(err, apperrors.ErrNotFound): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Document not found", Type: "DOCUMENT_NOT_FOUND"} default: logger.Error().Err(err).Msg("failed to upsert document permission") @@ -554,10 +552,10 @@ func (ctrl *Controller) DeleteDocumentUserPermission(ctx *fiber.Ctx, req dtos.De DocumentId: req.DocumentId, TargetUserId: req.UserId, }); err != nil { - switch err.Error() { - case "forbidden": + switch { + case errors.Is(err, apperrors.ErrForbidden): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} - case "not_found": + case errors.Is(err, apperrors.ErrDocumentNotFound) || errors.Is(err, apperrors.ErrNotFound): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Document not found", Type: "DOCUMENT_NOT_FOUND"} default: logger.Error().Err(err).Msg("failed to delete document permission") @@ -619,9 +617,9 @@ func (ctrl *Controller) RestoreDocument(ctx *fiber.Ctx, req dtos.RestoreDocument }) if err != nil { switch { - case err.Error() == "access denied: insufficient permissions to restore document": + case errors.Is(err, apperrors.ErrAccessDenied): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} - case err.Error() == "document is not deleted": + case errors.Is(err, apperrors.ErrDocumentNotDeleted): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusBadRequest, Details: "Document is not in trash", Type: "BAD_REQUEST"} default: logger.Error().Err(err).Msg("failed to restore document") @@ -651,7 +649,7 @@ func (ctrl *Controller) SetPublic(ctx *fiber.Ctx, req dtos.SetPublicRequest) (*d Public: req.Public, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } logger.Error().Err(err).Msg("failed to set document public status") @@ -716,7 +714,7 @@ func (ctrl *Controller) GetComments(ctx *fiber.Ctx, req dtos.GetCommentsRequest) DocumentId: req.DocumentId, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } logger.Error().Err(err).Msg("failed to get comments") @@ -759,7 +757,7 @@ func (ctrl *Controller) CreateComment(ctx *fiber.Ctx, req dtos.CreateCommentRequ BlockId: req.BlockId, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } logger.Error().Err(err).Msg("failed to create comment") @@ -785,10 +783,10 @@ func (ctrl *Controller) UpdateComment(ctx *fiber.Ctx, req dtos.UpdateCommentRequ Content: req.Content, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Only the comment author can update it", Type: "FORBIDDEN"} } - if strings.Contains(err.Error(), "not found") { + if errors.Is(err, apperrors.ErrNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Comment not found", Type: "NOT_FOUND"} } logger.Error().Err(err).Msg("failed to update comment") @@ -813,10 +811,10 @@ func (ctrl *Controller) DeleteComment(ctx *fiber.Ctx, req dtos.DeleteCommentRequ CommentId: req.CommentId, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Only the comment author can delete it", Type: "FORBIDDEN"} } - if strings.Contains(err.Error(), "not found") { + if errors.Is(err, apperrors.ErrNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Comment not found", Type: "NOT_FOUND"} } logger.Error().Err(err).Msg("failed to delete comment") @@ -842,10 +840,10 @@ func (ctrl *Controller) ResolveComment(ctx *fiber.Ctx, req dtos.ResolveCommentRe Resolved: req.Resolved, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } - if strings.Contains(err.Error(), "not found") { + if errors.Is(err, apperrors.ErrNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Comment not found", Type: "NOT_FOUND"} } logger.Error().Err(err).Msg("failed to resolve comment") @@ -926,7 +924,7 @@ func (ctrl *Controller) ListVersions(ctx *fiber.Ctx, req dtos.ListVersionsReques }) if err != nil { logger.Error().Err(err).Str("spaceId", req.SpaceId).Str("documentId", req.DocumentId).Msg("failed to list versions") - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusInternalServerError, Details: "Failed to list versions", Type: "INTERNAL_SERVER_ERROR"} @@ -966,10 +964,10 @@ func (ctrl *Controller) GetVersion(ctx *fiber.Ctx, req dtos.GetVersionRequest) ( VersionId: req.VersionId, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } - if strings.Contains(err.Error(), "not found") { + if errors.Is(err, apperrors.ErrVersionNotFound) || errors.Is(err, apperrors.ErrNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Version not found", Type: "NOT_FOUND"} } logger.Error().Err(err).Msg("failed to get version") @@ -1022,10 +1020,10 @@ func (ctrl *Controller) RestoreVersion(ctx *fiber.Ctx, req dtos.RestoreVersionRe VersionId: req.VersionId, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } - if strings.Contains(err.Error(), "not found") { + if errors.Is(err, apperrors.ErrVersionNotFound) || errors.Is(err, apperrors.ErrNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Version not found", Type: "NOT_FOUND"} } logger.Error().Err(err).Msg("failed to restore version") @@ -1051,7 +1049,7 @@ func (ctrl *Controller) CreateVersion(ctx *fiber.Ctx, req dtos.CreateVersionRequ Description: req.Description, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } logger.Error().Err(err).Msg("failed to create version") @@ -1090,7 +1088,7 @@ func (ctrl *Controller) ReorderDocuments(ctx *fiber.Ctx, req dtos.ReorderDocumen Items: items, }) if err != nil { - if strings.Contains(err.Error(), "insufficient permissions") { + if errors.Is(err, apperrors.ErrAccessDenied) || errors.Is(err, apperrors.ErrForbidden) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } logger.Error().Err(err).Msg("failed to reorder documents") diff --git a/interfaces/http/v1/drawing/dtos/drawing.go b/interfaces/http/v1/drawing/dtos/drawing.go index adb09ad..7a058f0 100644 --- a/interfaces/http/v1/drawing/dtos/drawing.go +++ b/interfaces/http/v1/drawing/dtos/drawing.go @@ -5,14 +5,14 @@ import "time" // Request DTOs type CreateDrawingRequest struct { - SpaceId string `json:"space_id"` - DocumentId *string `json:"document_id,omitempty"` - Name string `json:"name"` - Icon string `json:"icon,omitempty"` - Elements []interface{} `json:"elements,omitempty"` - AppState map[string]interface{} `json:"app_state,omitempty"` - Files map[string]interface{} `json:"files,omitempty"` - Thumbnail string `json:"thumbnail,omitempty"` + SpaceId string `json:"space_id"` + DocumentId *string `json:"document_id,omitempty"` + Name string `json:"name"` + Icon string `json:"icon,omitempty"` + Elements []any `json:"elements,omitempty"` + AppState map[string]any `json:"app_state,omitempty"` + Files map[string]any `json:"files,omitempty"` + Thumbnail string `json:"thumbnail,omitempty"` } type ListDrawingsRequest struct { @@ -24,13 +24,13 @@ type GetDrawingRequest struct { } type UpdateDrawingRequest struct { - DrawingId string `path:"drawing_id"` - Name *string `json:"name,omitempty"` - Icon *string `json:"icon,omitempty"` - Elements []interface{} `json:"elements,omitempty"` - AppState map[string]interface{} `json:"app_state,omitempty"` - Files map[string]interface{} `json:"files,omitempty"` - Thumbnail *string `json:"thumbnail,omitempty"` + DrawingId string `path:"drawing_id"` + Name *string `json:"name,omitempty"` + Icon *string `json:"icon,omitempty"` + Elements []any `json:"elements,omitempty"` + AppState map[string]any `json:"app_state,omitempty"` + Files map[string]any `json:"files,omitempty"` + Thumbnail *string `json:"thumbnail,omitempty"` } type DeleteDrawingRequest struct { @@ -76,16 +76,16 @@ type ListDrawingsResponse struct { } type GetDrawingResponse struct { - Id string `json:"id"` - SpaceId string `json:"space_id"` - DocumentId *string `json:"document_id,omitempty"` - Name string `json:"name"` - Icon string `json:"icon,omitempty"` - Elements []interface{} `json:"elements"` - AppState map[string]interface{} `json:"app_state"` - Files map[string]interface{} `json:"files"` - Thumbnail string `json:"thumbnail,omitempty"` - CreatedBy string `json:"created_by"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` + Id string `json:"id"` + SpaceId string `json:"space_id"` + DocumentId *string `json:"document_id,omitempty"` + Name string `json:"name"` + Icon string `json:"icon,omitempty"` + Elements []any `json:"elements"` + AppState map[string]any `json:"app_state"` + Files map[string]any `json:"files"` + Thumbnail string `json:"thumbnail,omitempty"` + CreatedBy string `json:"created_by"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` } diff --git a/interfaces/http/v1/drawing/handlers.go b/interfaces/http/v1/drawing/handlers.go index 8e4a3d0..32ae33c 100644 --- a/interfaces/http/v1/drawing/handlers.go +++ b/interfaces/http/v1/drawing/handlers.go @@ -1,10 +1,11 @@ package drawing import ( - "strings" + "errors" "github.com/gofiber/fiber/v2" fiberoapi "github.com/labbs/fiber-oapi" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" drawingDto "github.com/labbs/nexo/application/drawing/dto" "github.com/labbs/nexo/interfaces/http/v1/drawing/dtos" ) @@ -39,7 +40,7 @@ func (ctrl *Controller) CreateDrawing(ctx *fiber.Ctx, req dtos.CreateDrawingRequ Thumbnail: req.Thumbnail, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } logger.Error().Err(err).Msg("failed to create drawing") @@ -72,7 +73,7 @@ func (ctrl *Controller) ListDrawings(ctx *fiber.Ctx, req dtos.ListDrawingsReques SpaceId: req.SpaceId, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } logger.Error().Err(err).Msg("failed to list drawings") @@ -111,10 +112,10 @@ func (ctrl *Controller) GetDrawing(ctx *fiber.Ctx, req dtos.GetDrawingRequest) ( DrawingId: req.DrawingId, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } - if strings.Contains(err.Error(), "not found") { + if errors.Is(err, apperrors.ErrNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Drawing not found", Type: "NOT_FOUND"} } logger.Error().Err(err).Msg("failed to get drawing") @@ -158,10 +159,10 @@ func (ctrl *Controller) UpdateDrawing(ctx *fiber.Ctx, req dtos.UpdateDrawingRequ Thumbnail: req.Thumbnail, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } - if strings.Contains(err.Error(), "not found") { + if errors.Is(err, apperrors.ErrNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Drawing not found", Type: "NOT_FOUND"} } logger.Error().Err(err).Msg("failed to update drawing") @@ -186,10 +187,10 @@ func (ctrl *Controller) DeleteDrawing(ctx *fiber.Ctx, req dtos.DeleteDrawingRequ DrawingId: req.DrawingId, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } - if strings.Contains(err.Error(), "not found") { + if errors.Is(err, apperrors.ErrNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Drawing not found", Type: "NOT_FOUND"} } logger.Error().Err(err).Msg("failed to delete drawing") @@ -215,10 +216,10 @@ func (ctrl *Controller) MoveDrawing(ctx *fiber.Ctx, req dtos.MoveDrawingRequest) DocumentId: req.DocumentId, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } - if strings.Contains(err.Error(), "not found") { + if errors.Is(err, apperrors.ErrNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Drawing not found", Type: "NOT_FOUND"} } logger.Error().Err(err).Msg("failed to move drawing") @@ -248,10 +249,10 @@ func (ctrl *Controller) ListDrawingPermissions(ctx *fiber.Ctx, req dtos.ListDraw DrawingId: req.DrawingId, }) if err != nil { - switch err.Error() { - case "forbidden": + switch { + case errors.Is(err, apperrors.ErrForbidden): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} - case "not_found": + case errors.Is(err, apperrors.ErrNotFound): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Drawing not found", Type: "NOT_FOUND"} default: logger.Error().Err(err).Msg("failed to list drawing permissions") @@ -298,10 +299,10 @@ func (ctrl *Controller) UpsertDrawingUserPermission(ctx *fiber.Ctx, req dtos.Ups TargetUserId: req.UserId, Role: role, }); err != nil { - switch err.Error() { - case "forbidden": + switch { + case errors.Is(err, apperrors.ErrForbidden): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} - case "not_found": + case errors.Is(err, apperrors.ErrNotFound): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Drawing not found", Type: "NOT_FOUND"} default: logger.Error().Err(err).Msg("failed to upsert drawing permission") @@ -327,10 +328,10 @@ func (ctrl *Controller) DeleteDrawingUserPermission(ctx *fiber.Ctx, req dtos.Del DrawingId: req.DrawingId, TargetUserId: req.UserId, }); err != nil { - switch err.Error() { - case "forbidden": + switch { + case errors.Is(err, apperrors.ErrForbidden): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} - case "not_found": + case errors.Is(err, apperrors.ErrNotFound): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Drawing not found", Type: "NOT_FOUND"} default: logger.Error().Err(err).Msg("failed to delete drawing permission") diff --git a/interfaces/http/v1/space/dtos/create_space_request.go b/interfaces/http/v1/space/dtos/create_space_request.go index 1c6267b..87ca4ee 100644 --- a/interfaces/http/v1/space/dtos/create_space_request.go +++ b/interfaces/http/v1/space/dtos/create_space_request.go @@ -14,20 +14,20 @@ type CreateSpaceResponse struct { // My spaces list is defined under user DTOs (see interfaces/http/v1/user/dtos) type UpdateSpaceRequest struct { - SpaceId string `path:"space_id" validate:"required,uuid4"` - Name *string `json:"name,omitempty" validate:"omitempty,min=3,max=100"` - Icon *string `json:"icon,omitempty"` - IconColor *string `json:"icon_color,omitempty"` + SpaceId string `path:"space_id" validate:"required,uuid4"` + Name *string `json:"name,omitempty" validate:"omitempty,min=3,max=100"` + Icon *string `json:"icon,omitempty"` + IconColor *string `json:"icon_color,omitempty"` } type UpdateSpaceResponse struct { - Space Space `json:"space"` + Space Space `json:"space"` } type DeleteSpaceRequest struct { - SpaceId string `path:"space_id" validate:"required,uuid4"` + SpaceId string `path:"space_id" validate:"required,uuid4"` } type DeleteSpaceResponse struct { - SpaceId string `json:"space_id"` + SpaceId string `json:"space_id"` } diff --git a/interfaces/http/v1/space/dtos/permissions.go b/interfaces/http/v1/space/dtos/permissions.go index 10d6959..b9d05e5 100644 --- a/interfaces/http/v1/space/dtos/permissions.go +++ b/interfaces/http/v1/space/dtos/permissions.go @@ -1,41 +1,37 @@ package dtos type ListSpacePermissionsRequest struct { - SpaceId string `path:"space_id" validate:"required,uuid4"` + SpaceId string `path:"space_id" validate:"required,uuid4"` } type SpacePermission struct { - Id string `json:"id"` - UserId *string `json:"user_id,omitempty"` - Username string `json:"username,omitempty"` - GroupId *string `json:"group_id,omitempty"` - GroupName string `json:"group_name,omitempty"` - Role string `json:"role"` + Id string `json:"id"` + UserId *string `json:"user_id,omitempty"` + Username string `json:"username,omitempty"` + GroupId *string `json:"group_id,omitempty"` + GroupName string `json:"group_name,omitempty"` + Role string `json:"role"` } type ListSpacePermissionsResponse struct { - Permissions []SpacePermission `json:"permissions"` + Permissions []SpacePermission `json:"permissions"` } type UpsertSpaceUserPermissionRequest struct { - SpaceId string `path:"space_id" validate:"required,uuid4"` - UserId string `json:"user_id" validate:"required,uuid4"` - Role string `json:"role" validate:"required,oneof=owner admin editor viewer"` + SpaceId string `path:"space_id" validate:"required,uuid4"` + UserId string `json:"user_id" validate:"required,uuid4"` + Role string `json:"role" validate:"required,oneof=owner admin editor viewer"` } type UpsertSpaceUserPermissionResponse struct { - Message string `json:"message"` + Message string `json:"message"` } type DeleteSpaceUserPermissionRequest struct { - SpaceId string `path:"space_id" validate:"required,uuid4"` - UserId string `path:"user_id" validate:"required,uuid4"` + SpaceId string `path:"space_id" validate:"required,uuid4"` + UserId string `path:"user_id" validate:"required,uuid4"` } type DeleteSpaceUserPermissionResponse struct { - Message string `json:"message"` + Message string `json:"message"` } - - - - diff --git a/interfaces/http/v1/space/handlers.go b/interfaces/http/v1/space/handlers.go index 172f74f..4c99ef5 100644 --- a/interfaces/http/v1/space/handlers.go +++ b/interfaces/http/v1/space/handlers.go @@ -1,8 +1,11 @@ package space import ( + "errors" + "github.com/gofiber/fiber/v2" fiberoapi "github.com/labbs/fiber-oapi" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" spaceDto "github.com/labbs/nexo/application/space/dto" "github.com/labbs/nexo/domain" "github.com/labbs/nexo/interfaces/http/v1/space/dtos" @@ -71,10 +74,10 @@ func (ctrl *Controller) UpdateSpace(ctx *fiber.Ctx, req dtos.UpdateSpaceRequest) }) if err != nil { // Permission vs not found vs generic - if err.Error() == "forbidden" { + if errors.Is(err, apperrors.ErrForbidden) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } - if err.Error() == "not_found" { + if errors.Is(err, apperrors.ErrSpaceNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Space not found", Type: "SPACE_NOT_FOUND"} } logger.Error().Err(err).Msg("failed to update space") @@ -107,12 +110,12 @@ func (ctrl *Controller) DeleteSpace(ctx *fiber.Ctx, req dtos.DeleteSpaceRequest) UserId: authCtx.UserID, SpaceId: req.SpaceId, }); err != nil { - switch err.Error() { - case "forbidden": + switch { + case errors.Is(err, apperrors.ErrForbidden): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} - case "conflict_children": + case errors.Is(err, apperrors.ErrConflictChildren): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusConflict, Details: "Space has active documents", Type: "CONFLICT"} - case "not_found": + case errors.Is(err, apperrors.ErrSpaceNotFound): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Space not found", Type: "SPACE_NOT_FOUND"} default: logger.Error().Err(err).Msg("failed to delete space") @@ -138,10 +141,10 @@ func (ctrl *Controller) ListPermissions(ctx *fiber.Ctx, req dtos.ListSpacePermis SpaceId: req.SpaceId, }) if err != nil { - switch err.Error() { - case "forbidden": + switch { + case errors.Is(err, apperrors.ErrForbidden): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} - case "not_found": + case errors.Is(err, apperrors.ErrSpaceNotFound): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Space not found", Type: "SPACE_NOT_FOUND"} default: logger.Error().Err(err).Msg("failed to list permissions") @@ -191,10 +194,10 @@ func (ctrl *Controller) UpsertUserPermission(ctx *fiber.Ctx, req dtos.UpsertSpac TargetUserId: req.UserId, Role: role, }); err != nil { - switch err.Error() { - case "forbidden": + switch { + case errors.Is(err, apperrors.ErrForbidden): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} - case "not_found": + case errors.Is(err, apperrors.ErrSpaceNotFound): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Space not found", Type: "SPACE_NOT_FOUND"} default: logger.Error().Err(err).Msg("failed to upsert permission") @@ -220,10 +223,10 @@ func (ctrl *Controller) DeleteUserPermission(ctx *fiber.Ctx, req dtos.DeleteSpac SpaceId: req.SpaceId, TargetUserId: req.UserId, }); err != nil { - switch err.Error() { - case "forbidden": + switch { + case errors.Is(err, apperrors.ErrForbidden): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} - case "not_found": + case errors.Is(err, apperrors.ErrSpaceNotFound): return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Space not found", Type: "SPACE_NOT_FOUND"} default: logger.Error().Err(err).Msg("failed to delete permission") diff --git a/interfaces/http/v1/user/dtos/update_favorite_position_request.go b/interfaces/http/v1/user/dtos/update_favorite_position_request.go index 758c9ad..07c983a 100644 --- a/interfaces/http/v1/user/dtos/update_favorite_position_request.go +++ b/interfaces/http/v1/user/dtos/update_favorite_position_request.go @@ -1,15 +1,11 @@ package dtos type UpdateFavoritePositionRequest struct { - FavoriteId string `path:"favorite_id" validate:"required,uuid4"` - Position int `json:"position" validate:"required,min=0"` + FavoriteId string `path:"favorite_id" validate:"required,uuid4"` + Position int `json:"position" validate:"required,min=0"` } type UpdateFavoritePositionResponse struct { - FavoriteId string `json:"favorite_id"` - Position int `json:"position"` + FavoriteId string `json:"favorite_id"` + Position int `json:"position"` } - - - - diff --git a/interfaces/http/v1/user/dtos/update_profile_request.go b/interfaces/http/v1/user/dtos/update_profile_request.go index ef1dce8..9c8cd5a 100644 --- a/interfaces/http/v1/user/dtos/update_profile_request.go +++ b/interfaces/http/v1/user/dtos/update_profile_request.go @@ -1,9 +1,9 @@ package dtos type UpdateProfileRequest struct { - Username *string `json:"username,omitempty"` - AvatarUrl *string `json:"avatar_url,omitempty"` - Preferences *map[string]any `json:"preferences,omitempty"` + Username *string `json:"username,omitempty"` + AvatarUrl *string `json:"avatar_url,omitempty"` + Preferences *map[string]any `json:"preferences,omitempty"` } type UpdateProfileResponse struct { diff --git a/interfaces/http/v1/user/handlers.go b/interfaces/http/v1/user/handlers.go index a7ebf7c..273ad63 100644 --- a/interfaces/http/v1/user/handlers.go +++ b/interfaces/http/v1/user/handlers.go @@ -1,8 +1,11 @@ package user import ( + "errors" + "github.com/gofiber/fiber/v2" fiberoapi "github.com/labbs/fiber-oapi" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" fdto "github.com/labbs/nexo/application/favorite/dto" spaceDto "github.com/labbs/nexo/application/space/dto" userDto "github.com/labbs/nexo/application/user/dto" @@ -262,7 +265,7 @@ func (ctrl *Controller) UpdateFavoritePosition(ctx *fiber.Ctx, req dtos.UpdateFa Position: req.Position, }) if err != nil { - if err.Error() == "not_found" { + if errors.Is(err, apperrors.ErrFavoriteNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Favorite not found", Type: "FAVORITE_NOT_FOUND"} } logger.Error().Err(err).Msg("failed to update favorite position") @@ -325,7 +328,7 @@ func (ctrl *Controller) ChangePassword(ctx *fiber.Ctx, req dtos.ChangePasswordRe NewPassword: req.NewPassword, }) if err != nil { - if err.Error() == "invalid current password" { + if errors.Is(err, apperrors.ErrInvalidPassword) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusBadRequest, Details: "Invalid current password", Type: "INVALID_PASSWORD"} } logger.Error().Err(err).Msg("failed to change password") diff --git a/interfaces/http/v1/webhook/handlers.go b/interfaces/http/v1/webhook/handlers.go index 88ff575..e303773 100644 --- a/interfaces/http/v1/webhook/handlers.go +++ b/interfaces/http/v1/webhook/handlers.go @@ -1,10 +1,11 @@ package webhook import ( - "strings" + "errors" "github.com/gofiber/fiber/v2" fiberoapi "github.com/labbs/fiber-oapi" + "github.com/labbs/nexo/infrastructure/helpers/apperrors" webhookDto "github.com/labbs/nexo/application/webhook/dto" "github.com/labbs/nexo/interfaces/http/v1/webhook/dtos" ) @@ -107,10 +108,10 @@ func (ctrl *Controller) GetWebhook(ctx *fiber.Ctx, req dtos.GetWebhookRequest) ( WebhookId: req.WebhookId, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } - if strings.Contains(err.Error(), "not found") { + if errors.Is(err, apperrors.ErrNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Webhook not found", Type: "NOT_FOUND"} } logger.Error().Err(err).Msg("failed to get webhook") @@ -154,10 +155,10 @@ func (ctrl *Controller) UpdateWebhook(ctx *fiber.Ctx, req dtos.UpdateWebhookRequ Active: req.Active, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } - if strings.Contains(err.Error(), "not found") { + if errors.Is(err, apperrors.ErrNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Webhook not found", Type: "NOT_FOUND"} } logger.Error().Err(err).Msg("failed to update webhook") @@ -182,10 +183,10 @@ func (ctrl *Controller) DeleteWebhook(ctx *fiber.Ctx, req dtos.DeleteWebhookRequ WebhookId: req.WebhookId, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } - if strings.Contains(err.Error(), "not found") { + if errors.Is(err, apperrors.ErrNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Webhook not found", Type: "NOT_FOUND"} } logger.Error().Err(err).Msg("failed to delete webhook") @@ -216,10 +217,10 @@ func (ctrl *Controller) GetDeliveries(ctx *fiber.Ctx, req dtos.GetDeliveriesRequ Limit: limit, }) if err != nil { - if strings.Contains(err.Error(), "access denied") { + if errors.Is(err, apperrors.ErrAccessDenied) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusForbidden, Details: "Forbidden", Type: "FORBIDDEN"} } - if strings.Contains(err.Error(), "not found") { + if errors.Is(err, apperrors.ErrNotFound) { return nil, &fiberoapi.ErrorResponse{Code: fiber.StatusNotFound, Details: "Webhook not found", Type: "NOT_FOUND"} } logger.Error().Err(err).Msg("failed to get deliveries")