Define account deletion cascade for user and admin deletes #355

Closed
opened 2026-06-03 15:56:06 -05:00 by Codex · 1 comment
Member

Account deletion currently needs a consistent backend workflow so self-service deletion and admin soft-deletion clean up the same user-owned data without leaving active records behind.

Self-service deletion should mark the account as user-deleted, invalidate the user's session, soft-delete the user row, and soft-delete the related user-owned rows listed below. Admin deletion should use the same cascade and session invalidation behavior, but should not set users.user_deleted = 'Y'.

Scope

  • Add a shared account deletion service/action that can be called from the settings delete-account flow and the admin user flow.
  • For self-service deletion, set users.user_deleted = 'Y'.
  • For admin deletion, do not set users.user_deleted = 'Y'; only soft-delete the user and related records.
  • Soft-delete all related records with both deleted_at = now() and updated_at = now():
    • blog_comments where user_id matches the deleted user.
    • blog_entries where user_id matches the deleted user.
    • console_lists for the deleted user.
    • favorites for the deleted user.
    • user_friends where either user_id or friend_id matches the deleted user.
    • game_comments where user_id matches the deleted user.
    • game_lists where user_id matches the deleted user.
    • user_gamer_tags for the deleted user.
    • user_infos for the deleted user.
    • user_notification_settings for the deleted user.
    • user_site_settings for the deleted user.
    • users row for the deleted user.
  • Invalidate the deleted user's active session after self-service deletion and when an admin deletes the account.
  • Keep the workflow idempotent so rerunning it for an already soft-deleted user does not fail or resurrect data.
  • Keep issue #46 separate; this issue covers app database/session cleanup, while #46 covers the Fider-side account state.

Acceptance Criteria

  • When a user deletes their own account, their related rows listed in scope are soft-deleted and have updated_at refreshed to the deletion time.
  • Self-service deletion sets users.user_deleted = 'Y', soft-deletes the user row, and logs the user out.
  • When an admin soft-deletes a user account, the same related rows are soft-deleted and have updated_at refreshed to the deletion time.
  • Admin deletion soft-deletes the user row but does not change users.user_deleted to Y.
  • Friend rows are removed from active friendship views regardless of whether the deleted user is stored in user_id or friend_id.
  • Deleted user content no longer appears in active blog, comment, console-list, favorite, game-list, profile, or notification/settings queries that already filter soft-deleted rows.
  • The workflow is wrapped so partial deletion does not leave the account in an inconsistent state.
  • Existing admin/profile delete UI continues to follow the current Laravel/Tailwind patterns.

Test Coverage Required

  • Feature test for self-service account deletion covering users.user_deleted = 'Y', user soft-delete, session invalidation, and every related table in scope.
  • Feature test for admin account deletion covering user soft-delete and related-table cascade without setting users.user_deleted = 'Y'.
  • Regression test confirming user_friends rows are soft-deleted when the deleted user is either user_id or friend_id.
  • Regression test confirming all touched rows receive a refreshed updated_at.
  • Authorization test confirming non-admin users cannot trigger admin deletion.
  • Idempotency test confirming rerunning deletion for an already soft-deleted user does not fail.
  • Run the focused affected tests, then run vendor/bin/pint --dirty before closing the issue.

Progress Checklist

  • Add shared account deletion service/action
  • Wire self-service delete-account flow to the shared deletion workflow
  • Wire admin user soft-delete flow to the shared deletion workflow
  • Cascade soft-delete blog comments and blog entries
  • Cascade soft-delete console lists, favorites, game comments, and game-list rows
  • Cascade soft-delete friend rows from both friendship columns
  • Cascade soft-delete gamer tags, user info, notification settings, and site settings
  • Soft-delete the user row and refresh updated_at
  • Set users.user_deleted = 'Y' only for self-service deletion
  • Invalidate the deleted user's session
  • Add focused Pest coverage for both deletion paths and the cascade
Account deletion currently needs a consistent backend workflow so self-service deletion and admin soft-deletion clean up the same user-owned data without leaving active records behind. Self-service deletion should mark the account as user-deleted, invalidate the user's session, soft-delete the user row, and soft-delete the related user-owned rows listed below. Admin deletion should use the same cascade and session invalidation behavior, but should not set `users.user_deleted = 'Y'`. ## Scope - Add a shared account deletion service/action that can be called from the settings delete-account flow and the admin user flow. - For self-service deletion, set `users.user_deleted = 'Y'`. - For admin deletion, do not set `users.user_deleted = 'Y'`; only soft-delete the user and related records. - Soft-delete all related records with both `deleted_at = now()` and `updated_at = now()`: - `blog_comments` where `user_id` matches the deleted user. - `blog_entries` where `user_id` matches the deleted user. - `console_lists` for the deleted user. - `favorites` for the deleted user. - `user_friends` where either `user_id` or `friend_id` matches the deleted user. - `game_comments` where `user_id` matches the deleted user. - `game_lists` where `user_id` matches the deleted user. - `user_gamer_tags` for the deleted user. - `user_infos` for the deleted user. - `user_notification_settings` for the deleted user. - `user_site_settings` for the deleted user. - `users` row for the deleted user. - Invalidate the deleted user's active session after self-service deletion and when an admin deletes the account. - Keep the workflow idempotent so rerunning it for an already soft-deleted user does not fail or resurrect data. - Keep issue #46 separate; this issue covers app database/session cleanup, while #46 covers the Fider-side account state. ## Acceptance Criteria - When a user deletes their own account, their related rows listed in scope are soft-deleted and have `updated_at` refreshed to the deletion time. - Self-service deletion sets `users.user_deleted = 'Y'`, soft-deletes the user row, and logs the user out. - When an admin soft-deletes a user account, the same related rows are soft-deleted and have `updated_at` refreshed to the deletion time. - Admin deletion soft-deletes the user row but does not change `users.user_deleted` to `Y`. - Friend rows are removed from active friendship views regardless of whether the deleted user is stored in `user_id` or `friend_id`. - Deleted user content no longer appears in active blog, comment, console-list, favorite, game-list, profile, or notification/settings queries that already filter soft-deleted rows. - The workflow is wrapped so partial deletion does not leave the account in an inconsistent state. - Existing admin/profile delete UI continues to follow the current Laravel/Tailwind patterns. ## Test Coverage Required - Feature test for self-service account deletion covering `users.user_deleted = 'Y'`, user soft-delete, session invalidation, and every related table in scope. - Feature test for admin account deletion covering user soft-delete and related-table cascade without setting `users.user_deleted = 'Y'`. - Regression test confirming `user_friends` rows are soft-deleted when the deleted user is either `user_id` or `friend_id`. - Regression test confirming all touched rows receive a refreshed `updated_at`. - Authorization test confirming non-admin users cannot trigger admin deletion. - Idempotency test confirming rerunning deletion for an already soft-deleted user does not fail. - Run the focused affected tests, then run `vendor/bin/pint --dirty` before closing the issue. ## Progress Checklist - [x] Add shared account deletion service/action - [x] Wire self-service delete-account flow to the shared deletion workflow - [x] Wire admin user soft-delete flow to the shared deletion workflow - [x] Cascade soft-delete blog comments and blog entries - [x] Cascade soft-delete console lists, favorites, game comments, and game-list rows - [x] Cascade soft-delete friend rows from both friendship columns - [x] Cascade soft-delete gamer tags, user info, notification settings, and site settings - [x] Soft-delete the user row and refresh `updated_at` - [x] Set `users.user_deleted = 'Y'` only for self-service deletion - [x] Invalidate the deleted user's session - [x] Add focused Pest coverage for both deletion paths and the cascade
jimmyb self-assigned this 2026-06-03 21:40:04 -05:00
Author
Member

Implemented and pushed to dev in commit df1eaff (Implement account deletion cascade).

Notes:

  • Added App\Actions\DeleteUserAccount as the shared transactional cascade used by both self-service and admin deletion.
  • Self-service deletion sets users.user_deleted = 'Y'; admin deletion soft-deletes the account and scoped related rows without changing user_deleted.
  • The cascade refreshes deleted_at and updated_at for every scoped table, handles user_friends from both user_id and friend_id, removes database-backed sessions for the deleted user, and supports idempotent reruns via withTrashed().
  • Added an admin delete route/control on the existing admin user edit screen.

Verification:

  • php artisan test --compact tests/Feature/AccountDeletionCascadeTest.php - 4 passed, 51 assertions
  • php artisan test --compact tests/Feature/Settings/ProfileUpdateTest.php --filter='delete account' - 4 passed, 27 assertions
  • php artisan test --compact tests/Feature/AdminUserProfileInfoTest.php - 1 passed, 11 assertions
  • vendor/bin/pint --dirty --format agent - passed
Implemented and pushed to `dev` in commit `df1eaff` (`Implement account deletion cascade`). Notes: - Added `App\Actions\DeleteUserAccount` as the shared transactional cascade used by both self-service and admin deletion. - Self-service deletion sets `users.user_deleted = 'Y'`; admin deletion soft-deletes the account and scoped related rows without changing `user_deleted`. - The cascade refreshes `deleted_at` and `updated_at` for every scoped table, handles `user_friends` from both `user_id` and `friend_id`, removes database-backed sessions for the deleted user, and supports idempotent reruns via `withTrashed()`. - Added an admin delete route/control on the existing admin user edit screen. Verification: - `php artisan test --compact tests/Feature/AccountDeletionCascadeTest.php` - 4 passed, 51 assertions - `php artisan test --compact tests/Feature/Settings/ProfileUpdateTest.php --filter='delete account'` - 4 passed, 27 assertions - `php artisan test --compact tests/Feature/AdminUserProfileInfoTest.php` - 1 passed, 11 assertions - `vendor/bin/pint --dirty --format agent` - passed
Codex 2026-06-03 22:07:34 -05:00
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
MyVideoGameList/myvideogamelist.com#355
No description provided.