Shipflash logoDocs

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

AreaPaths
Routesrc/app/(dashboard)/account/page.tsx
Actionssrc/features/account/server/actions/profile.ts, security.ts, notifications.ts, delete.ts
Queriessrc/features/account/server/queries/account.ts
Persistencesrc/features/account/server/persistence/profile.ts, notification-preferences.ts
UIsrc/features/account/ui/profile-settings-form.tsx, avatar-uploader.tsx, notification-preferences-form.tsx, security-settings-form.tsx, delete-account-form.tsx
Sharedsrc/features/account/shared/form.ts
Related infrastructuresrc/lib/auth/session, src/lib/auth/guards, src/lib/auth/rbac

Code-backed account map

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.tsx
  • src/features/account/ui/profile-settings-form.tsx
  • src/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.ts
  • src/features/account/server/actions/notifications.ts
  • src/features/account/server/actions/security.ts
  • src/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.ts
  • src/features/account/server/persistence/profile.ts
  • src/features/account/server/persistence/notification-preferences.ts

Account update flow

  1. 1

    Resolve the signed-in profile

    The page and actions use server-side identity helpers, not browser-provided profile ids.

    • src/app/(dashboard)/account/page.tsx
    • src/lib/auth/session/profile.ts
  2. 2

    Validate form input

    Shared form helpers and action utilities normalize fields before profile, security, or notification mutations run.

    • src/features/account/shared/form.ts
    • src/features/account/server/actions/utils.ts
  3. 3

    Write through persistence helpers

    Profile and preference writes stay behind account-owned persistence modules.

    • src/features/account/server/persistence/profile.ts
    • src/features/account/server/persistence/notification-preferences.ts
  4. 4

    Treat deletion as high risk

    Account deletion is isolated in its own action and should be tested directly before customization.

    • src/features/account/server/actions/delete.ts
    • src/features/account/server/actions/__tests__/delete.test.ts

Profile update boundary

Source: src/features/account/server/actions/profile.ts

The signed-in server profile owns the update target; the browser never supplies the profile id.

ts
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) };
  }

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.