Skip to content

Commit 9f968f2

Browse files
committed
feat(all): add self-service account deletion
Users can now delete their own accounts from the profile page. The feature includes: - New DELETE /api/users/me endpoint with validation for team ownership - Complete cleanup of user data: teams, MCP installations, memberships, sessions, and preferences - Confirmation modal requiring typed verification before deletion - Email confirmation sent after successful deletion - Automatic logout after account deletion - Safety check preventing deletion when user owns non-default teams Fixed missing is_default field in team response that was preventing proper team type detection.
1 parent 0ca6146 commit 9f968f2

File tree

9 files changed

+1064
-21
lines changed

9 files changed

+1064
-21
lines changed

services/backend/api-spec.json

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3391,6 +3391,166 @@
33913391
}
33923392
}
33933393
}
3394+
},
3395+
"delete": {
3396+
"summary": "Delete my account",
3397+
"tags": [
3398+
"Users"
3399+
],
3400+
"description": "Permanently delete your own account. This action is irreversible and will remove all your data including your default team, MCP installations, preferences, and sessions. You cannot delete your account if you own non-default teams - you must delete those teams first. You will be removed from all teams where you are a member (but not owner).",
3401+
"security": [
3402+
{
3403+
"cookieAuth": []
3404+
}
3405+
],
3406+
"responses": {
3407+
"200": {
3408+
"description": "Account deleted successfully",
3409+
"content": {
3410+
"application/json": {
3411+
"schema": {
3412+
"type": "object",
3413+
"properties": {
3414+
"success": {
3415+
"type": "boolean",
3416+
"description": "Indicates if the operation was successful"
3417+
},
3418+
"message": {
3419+
"type": "string",
3420+
"description": "Success message"
3421+
}
3422+
},
3423+
"required": [
3424+
"success",
3425+
"message"
3426+
],
3427+
"description": "Account deleted successfully"
3428+
}
3429+
}
3430+
}
3431+
},
3432+
"400": {
3433+
"description": "Bad Request - Cannot delete account while owning non-default teams",
3434+
"content": {
3435+
"application/json": {
3436+
"schema": {
3437+
"type": "object",
3438+
"properties": {
3439+
"success": {
3440+
"type": "boolean",
3441+
"default": false,
3442+
"description": "Indicates the operation failed"
3443+
},
3444+
"error": {
3445+
"type": "string",
3446+
"description": "Error message describing what went wrong"
3447+
},
3448+
"owned_teams": {
3449+
"type": "array",
3450+
"items": {
3451+
"type": "object",
3452+
"properties": {
3453+
"id": {
3454+
"type": "string",
3455+
"description": "Team ID"
3456+
},
3457+
"name": {
3458+
"type": "string",
3459+
"description": "Team name"
3460+
}
3461+
}
3462+
},
3463+
"description": "List of non-default teams owned by the user"
3464+
}
3465+
},
3466+
"required": [
3467+
"success",
3468+
"error"
3469+
],
3470+
"description": "Bad Request - Cannot delete account while owning non-default teams"
3471+
}
3472+
}
3473+
}
3474+
},
3475+
"401": {
3476+
"description": "Unauthorized - Authentication required",
3477+
"content": {
3478+
"application/json": {
3479+
"schema": {
3480+
"type": "object",
3481+
"properties": {
3482+
"success": {
3483+
"type": "boolean",
3484+
"default": false,
3485+
"description": "Indicates the operation failed"
3486+
},
3487+
"error": {
3488+
"type": "string",
3489+
"description": "Error message describing what went wrong"
3490+
}
3491+
},
3492+
"required": [
3493+
"success",
3494+
"error"
3495+
],
3496+
"description": "Unauthorized - Authentication required"
3497+
}
3498+
}
3499+
}
3500+
},
3501+
"404": {
3502+
"description": "Not Found - User or default team not found",
3503+
"content": {
3504+
"application/json": {
3505+
"schema": {
3506+
"type": "object",
3507+
"properties": {
3508+
"success": {
3509+
"type": "boolean",
3510+
"default": false,
3511+
"description": "Indicates the operation failed"
3512+
},
3513+
"error": {
3514+
"type": "string",
3515+
"description": "Error message describing what went wrong"
3516+
}
3517+
},
3518+
"required": [
3519+
"success",
3520+
"error"
3521+
],
3522+
"description": "Not Found - User or default team not found"
3523+
}
3524+
}
3525+
}
3526+
},
3527+
"500": {
3528+
"description": "Internal Server Error",
3529+
"content": {
3530+
"application/json": {
3531+
"schema": {
3532+
"type": "object",
3533+
"properties": {
3534+
"success": {
3535+
"type": "boolean",
3536+
"default": false,
3537+
"description": "Indicates the operation failed"
3538+
},
3539+
"error": {
3540+
"type": "string",
3541+
"description": "Error message describing what went wrong"
3542+
}
3543+
},
3544+
"required": [
3545+
"success",
3546+
"error"
3547+
],
3548+
"description": "Internal Server Error"
3549+
}
3550+
}
3551+
}
3552+
}
3553+
}
33943554
}
33953555
},
33963556
"/api/users/me/teams": {
@@ -3445,6 +3605,10 @@
34453605
"type": "string",
34463606
"description": "User ID of the team owner"
34473607
},
3608+
"is_default": {
3609+
"type": "boolean",
3610+
"description": "Whether this is the default team"
3611+
},
34483612
"created_at": {
34493613
"type": [
34503614
"null",
@@ -3473,6 +3637,7 @@
34733637
"name",
34743638
"slug",
34753639
"owner_id",
3640+
"is_default",
34763641
"role",
34773642
"is_owner"
34783643
],

services/backend/api-spec.yaml

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2364,6 +2364,119 @@ paths:
23642364
- success
23652365
- error
23662366
description: Internal Server Error
2367+
delete:
2368+
summary: Delete my account
2369+
tags:
2370+
- Users
2371+
description: Permanently delete your own account. This action is irreversible
2372+
and will remove all your data including your default team, MCP
2373+
installations, preferences, and sessions. You cannot delete your account
2374+
if you own non-default teams - you must delete those teams first. You
2375+
will be removed from all teams where you are a member (but not owner).
2376+
security:
2377+
- cookieAuth: []
2378+
responses:
2379+
"200":
2380+
description: Account deleted successfully
2381+
content:
2382+
application/json:
2383+
schema:
2384+
type: object
2385+
properties:
2386+
success:
2387+
type: boolean
2388+
description: Indicates if the operation was successful
2389+
message:
2390+
type: string
2391+
description: Success message
2392+
required:
2393+
- success
2394+
- message
2395+
description: Account deleted successfully
2396+
"400":
2397+
description: Bad Request - Cannot delete account while owning non-default teams
2398+
content:
2399+
application/json:
2400+
schema:
2401+
type: object
2402+
properties:
2403+
success:
2404+
type: boolean
2405+
default: false
2406+
description: Indicates the operation failed
2407+
error:
2408+
type: string
2409+
description: Error message describing what went wrong
2410+
owned_teams:
2411+
type: array
2412+
items:
2413+
type: object
2414+
properties:
2415+
id:
2416+
type: string
2417+
description: Team ID
2418+
name:
2419+
type: string
2420+
description: Team name
2421+
description: List of non-default teams owned by the user
2422+
required:
2423+
- success
2424+
- error
2425+
description: Bad Request - Cannot delete account while owning non-default teams
2426+
"401":
2427+
description: Unauthorized - Authentication required
2428+
content:
2429+
application/json:
2430+
schema:
2431+
type: object
2432+
properties:
2433+
success:
2434+
type: boolean
2435+
default: false
2436+
description: Indicates the operation failed
2437+
error:
2438+
type: string
2439+
description: Error message describing what went wrong
2440+
required:
2441+
- success
2442+
- error
2443+
description: Unauthorized - Authentication required
2444+
"404":
2445+
description: Not Found - User or default team not found
2446+
content:
2447+
application/json:
2448+
schema:
2449+
type: object
2450+
properties:
2451+
success:
2452+
type: boolean
2453+
default: false
2454+
description: Indicates the operation failed
2455+
error:
2456+
type: string
2457+
description: Error message describing what went wrong
2458+
required:
2459+
- success
2460+
- error
2461+
description: Not Found - User or default team not found
2462+
"500":
2463+
description: Internal Server Error
2464+
content:
2465+
application/json:
2466+
schema:
2467+
type: object
2468+
properties:
2469+
success:
2470+
type: boolean
2471+
default: false
2472+
description: Indicates the operation failed
2473+
error:
2474+
type: string
2475+
description: Error message describing what went wrong
2476+
required:
2477+
- success
2478+
- error
2479+
description: Internal Server Error
23672480
/api/users/me/teams:
23682481
get:
23692482
summary: Get current user teams
@@ -2405,6 +2518,9 @@ paths:
24052518
owner_id:
24062519
type: string
24072520
description: User ID of the team owner
2521+
is_default:
2522+
type: boolean
2523+
description: Whether this is the default team
24082524
created_at:
24092525
type:
24102526
- "null"
@@ -2426,6 +2542,7 @@ paths:
24262542
- name
24272543
- slug
24282544
- owner_id
2545+
- is_default
24292546
- role
24302547
- is_owner
24312548
additionalProperties: false
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//- @description Email notification when a user account is deleted
2+
//- @variables userName, userEmail, deletionDate, supportEmail
3+
extends layouts/base.pug
4+
5+
block content
6+
h1 Account Deleted
7+
8+
p Hi #{userName},
9+
10+
p This email confirms that your DeployStack account has been permanently deleted on #{new Date(deletionDate).toLocaleDateString()}.
11+
12+
h2 What was deleted?
13+
14+
p The following data has been permanently removed from our systems:
15+
16+
ul
17+
li
18+
strong Your user account 
19+
| - All personal information associated with #{userEmail}
20+
li
21+
strong Your default team 
22+
| - Your personal team and all associated data
23+
li
24+
strong All MCP server installations 
25+
| - All MCP servers configured in your default team
26+
li
27+
strong All team memberships 
28+
| - You have been removed from all teams where you were a member
29+
li
30+
strong All user preferences 
31+
| - All configuration settings and preferences
32+
li
33+
strong All active sessions 
34+
| - You have been logged out from all devices
35+
36+
h2 Important Information
37+
38+
p
39+
strong This action is permanent and cannot be undone.
40+
41+
p You will no longer be able to:
42+
43+
ul
44+
li Log in with your email address: #{userEmail}
45+
li Access any data from your deleted account
46+
li Recover any MCP server configurations or settings
47+
48+
p
49+
| If you did not request this deletion or believe this was done in error, please contact our support team immediately at 
50+
strong #{supportEmail || '[email protected]'}
51+
| .
52+
53+
p.text-muted
54+
| Thank you for using DeployStack.
55+
br
56+
| The DeployStack Team

0 commit comments

Comments
 (0)