Account
The account module owns the signed-in user's profile, avatar, notification preferences, security form, and deletion flow.
On this page
Where to look
| Area | Maintainer reference |
|---|---|
| Route | Implementation referencesrc/app/(dashboard)/account/page.tsx |
| Actions | Implementation referencesrc/features/account/server/actions/profile.ts, security.ts, notifications.ts, delete.ts |
| Queries | Implementation referencesrc/features/account/server/queries/account.ts |
| Persistence | Implementation referencesrc/features/account/server/persistence/profile.ts, notification-preferences.ts |
| UI | Implementation referencesrc/features/account/ui/profile-settings-form.tsx, avatar-uploader.tsx, notification-preferences-form.tsx, security-settings-form.tsx, delete-account-form.tsx |
| Shared | Implementation referencesrc/features/account/shared/form.ts |
| Related infrastructure | Implementation referencesrc/lib/auth/session, src/lib/auth/guards, src/lib/auth/rbac |
Implementation reference3 areas
These Product code locations explain how the documented behavior is implemented. Expand them when you are ready to customize or maintain this area.
Account page and forms
The route renders account settings while feature UI owns profile, avatar, notifications, security, and deletion forms.
src/app/(dashboard)/account/page.tsxsrc/features/account/ui/profile-settings-form.tsxsrc/features/account/ui/security-settings-form.tsx
Server actions
Profile, notification preference, password, session, and deletion mutations stay behind authenticated server actions.
src/features/account/server/actions/profile.tssrc/features/account/server/actions/notifications.tssrc/features/account/server/actions/security.tssrc/features/account/server/actions/delete.ts
Reads and persistence
Account reads and writes go through query and persistence helpers instead of trusting client-supplied identity.
src/features/account/server/queries/account.tssrc/features/account/server/persistence/profile.tssrc/features/account/server/persistence/notification-preferences.ts
Reference paths are relative to the Shipflash-Product checkout.
Account update flow
- 1
Resolve the signed-in profile
The page and actions use server-side identity helpers, not browser-provided profile ids.
Relevant Product code
src/app/(dashboard)/account/page.tsxsrc/lib/auth/session/profile.ts
- 2
Validate form input
Shared form helpers and action utilities normalize fields before profile, security, or notification mutations run.
Relevant Product code
src/features/account/shared/form.tssrc/features/account/server/actions/utils.ts
- 3
Write through persistence helpers
Profile and preference writes stay behind account-owned persistence modules.
Relevant Product code
src/features/account/server/persistence/profile.tssrc/features/account/server/persistence/notification-preferences.ts
- 4
Treat deletion as high risk
Account deletion is isolated in its own action and should be tested directly before customization.
Relevant Product code
src/features/account/server/actions/delete.tssrc/features/account/server/actions/__tests__/delete.test.ts
Profile update boundary
Use this example as a starting point, then adapt it to your product's rules and configuration.
The signed-in server profile owns the update target; the browser never supplies the profile id.
export async function updateMyProfile(prevState: ActionResult, formData: FormData): Promise<ActionResult> {
const profile = await getActiveProfile();
if (!profile) {
return { success: false, message: "Not authenticated" };
}
if (hasReadOnlyAccess(profile)) {
return { success: false, message: "This account has read-only access and cannot make changes." };
}
const raw = {
name: formData.get("name")?.toString() ?? "",
phone: formData.get("phone")?.toString() ?? "",
locale: formData.get("locale")?.toString() ?? "",
timezone: formData.get("timezone")?.toString() ?? "",
avatar_url: formData.get("avatar_url")?.toString() ?? "",
job_title: formData.get("job_title")?.toString() ?? "",
bio: formData.get("bio")?.toString() ?? "",
github_url: formData.get("github_url")?.toString() ?? "",
linkedin_url: formData.get("linkedin_url")?.toString() ?? "",
x_url: formData.get("x_url")?.toString() ?? "",
};
const parsed = ProfileUpdateSchema.safeParse(raw);
if (!parsed.success) {
return { success: false, message: "Validation failed", errors: zodErrors(parsed.error) };
}Source reference
src/features/account/server/actions/profile.ts
Change rules
- Use the server session as the source of identity.
- Keep profile and preference writes behind server actions.
- Treat account deletion as a high-risk path and test it directly.
- Do not trust browser-supplied profile ids when the server already knows the signed-in profile.