diff --git a/internal/services/rbac/seeds.go b/internal/services/rbac/seeds.go index 60e3eca..c1bfc08 100644 --- a/internal/services/rbac/seeds.go +++ b/internal/services/rbac/seeds.go @@ -105,6 +105,7 @@ var AllPermissions = []domain.PermissionSeed{ {Key: "users.update_self", Name: "Update Own Profile", Description: "Update own user profile", GroupName: "Users"}, {Key: "users.update_status", Name: "Update User Status", Description: "Activate/deactivate users", GroupName: "Users"}, {Key: "users.delete", Name: "Delete User", Description: "Delete a user", GroupName: "Users"}, + {Key: "users.delete_self", Name: "Delete Own Account", Description: "Delete own user account", GroupName: "Users"}, {Key: "users.search", Name: "Search Users", Description: "Search users by name or phone", GroupName: "Users"}, {Key: "users.profile_completed", Name: "Check Profile Completed", Description: "Check if user profile is completed", GroupName: "Users"}, {Key: "users.upload_profile_picture", Name: "Upload Profile Picture", Description: "Upload user profile picture", GroupName: "Users"}, @@ -250,7 +251,7 @@ var DefaultRolePermissions = map[string][]string{ "payments.direct_initiate", "payments.direct_verify_otp", // Users (full access) - "users.list", "users.get", "users.update_self", "users.update_status", "users.delete", "users.search", + "users.list", "users.get", "users.update_self", "users.update_status", "users.delete", "users.delete_self", "users.search", "users.profile_completed", "users.upload_profile_picture", "users.admin_profile", "users.user_profile", // Admin management @@ -325,7 +326,7 @@ var DefaultRolePermissions = map[string][]string{ "payments.direct_initiate", "payments.direct_verify_otp", // User (self-service) - "users.update_self", "users.profile_completed", "users.upload_profile_picture", "users.user_profile", + "users.update_self", "users.delete_self", "users.profile_completed", "users.upload_profile_picture", "users.user_profile", // Notifications (own) "notifications.ws_connect", "notifications.list_mine", "notifications.list_all", @@ -373,7 +374,7 @@ var DefaultRolePermissions = map[string][]string{ "payments.direct_initiate", "payments.direct_verify_otp", // User (self-service) - "users.update_self", "users.profile_completed", "users.upload_profile_picture", "users.user_profile", + "users.update_self", "users.delete_self", "users.profile_completed", "users.upload_profile_picture", "users.user_profile", // Notifications (own) "notifications.ws_connect", "notifications.list_mine", "notifications.list_all", @@ -415,7 +416,7 @@ var DefaultRolePermissions = map[string][]string{ "question_set_personas.list", // Users (view + search for support) - "users.list", "users.get", "users.search", "users.update_self", "users.profile_completed", + "users.list", "users.get", "users.search", "users.update_self", "users.delete_self", "users.profile_completed", "users.upload_profile_picture", "users.user_profile", // Notifications (own) diff --git a/internal/web_server/handlers/user.go b/internal/web_server/handlers/user.go index 9ae4df4..6ca9206 100644 --- a/internal/web_server/handlers/user.go +++ b/internal/web_server/handlers/user.go @@ -1714,6 +1714,50 @@ func (h *Handler) DeleteUser(c *fiber.Ctx) error { return response.WriteJSON(c, fiber.StatusOK, "User deleted successfully", nil, nil) } +// DeleteMyUserAccount godoc +// @Summary Delete my user account +// @Description Deletes the authenticated learner's own account +// @Tags user +// @Produce json +// @Success 200 {object} response.APIResponse +// @Failure 401 {object} response.APIResponse +// @Failure 403 {object} response.APIResponse +// @Failure 500 {object} response.APIResponse +// @Security Bearer +// @Router /api/v1/user/me [delete] +func (h *Handler) DeleteMyUserAccount(c *fiber.Ctx) error { + userID, ok := c.Locals("user_id").(int64) + if !ok || userID <= 0 { + return fiber.NewError(fiber.StatusUnauthorized, "Invalid authenticated user") + } + + role, ok := c.Locals("role").(domain.Role) + if !ok { + return fiber.NewError(fiber.StatusUnauthorized, "Invalid authenticated role") + } + if role != domain.RoleStudent { + return fiber.NewError(fiber.StatusForbidden, "Only learners can delete their own account using this endpoint") + } + + if err := h.userSvc.DeleteUser(c.Context(), userID); err != nil { + h.mongoLoggerSvc.Error("Failed to self-delete user account", + zap.Int64("userID", userID), + zap.Int("status_code", fiber.StatusInternalServerError), + zap.Error(err), + zap.Time("timestamp", time.Now()), + ) + return fiber.NewError(fiber.StatusInternalServerError, "Failed to delete account:"+err.Error()) + } + + actorRole := string(role) + ip := c.IP() + ua := c.Get("User-Agent") + meta, _ := json.Marshal(map[string]interface{}{"deleted_user_id": userID, "self_delete": true}) + go h.activityLogSvc.RecordAction(context.Background(), &userID, &actorRole, domain.ActionUserDeleted, domain.ResourceUser, &userID, fmt.Sprintf("Self-deleted user account ID: %d", userID), meta, &ip, &ua) + + return response.WriteJSON(c, fiber.StatusOK, "Account deleted successfully", nil, nil) +} + type UpdateUserSuspendReq struct { UserID int64 `json:"user_id" validate:"required" example:"123"` Suspended bool `json:"suspended" example:"true"` diff --git a/internal/web_server/routes.go b/internal/web_server/routes.go index de52745..40fdb4b 100644 --- a/internal/web_server/routes.go +++ b/internal/web_server/routes.go @@ -233,6 +233,7 @@ func (a *App) initAppRoutes() { groupV1.Post("/user/checkPhoneEmailExist", h.CheckPhoneEmailExist) groupV1.Get("/user/admin-profile", a.authMiddleware, a.RequirePermission("users.admin_profile"), h.AdminProfile) groupV1.Get("/user/user-profile", a.authMiddleware, a.RequirePermission("users.user_profile"), h.GetUserProfile) + groupV1.Delete("/user/me", a.authMiddleware, a.RequirePermission("users.delete_self"), h.DeleteMyUserAccount) groupV1.Get("/user/single/:id", a.authMiddleware, a.RequirePermission("users.get"), h.GetUserByID) groupV1.Delete("/user/delete/:id", a.authMiddleware, a.RequirePermission("users.delete"), h.DeleteUser) groupV1.Post("/user/search", a.authMiddleware, a.RequirePermission("users.search"), h.SearchUserByNameOrPhone)