# 12. Backend Router — User & Family Group Management

meta:
  id: shieldai-unified-restructure-12
  feature: shieldai-unified-restructure
  priority: P1
  depends_on: [shieldai-unified-restructure-11]
  tags: [backend, trpc, users, family, api]

objective:
- Build the tRPC router for user management and family group functionality. Port all user-related logic from the legacy `packages/api/src/routes/` and `packages/shared-auth/` into a unified `user` router.

deliverables:
- `web/src/server/api/routers/user.ts` — User router with procedures:
  - `user.me` — `protectedProcedure` returning current user profile
  - `user.update` — `protectedProcedure` updating user name, email, image
  - `user.delete` — `protectedProcedure` deleting account and all related data
  - `user.listFamilyMembers` — `protectedProcedure` returning family group members
  - `user.inviteFamilyMember` — `protectedProcedure` sending invite by email
  - `user.removeFamilyMember` — `protectedProcedure` removing member from group
  - `user.updateFamilyMemberRole` — `protectedProcedure` changing member role
- `web/src/server/services/user.service.ts` — Business logic layer:
  - `getUserById(id)` — fetch user with relations
  - `updateUser(id, data)` — update and return user
  - `deleteUser(id)` — cascade delete all user data
  - `getFamilyGroup(userId)` — get family group with members
  - `inviteMember(groupId, email)` — create invite record, send email
  - `removeMember(groupId, userId)` — remove membership
  - `updateMemberRole(groupId, userId, role)` — update role enum
- `web/src/server/services/family.service.ts` — Family group logic:
  - `createFamilyGroup(ownerId, name)`
  - `getFamilyGroupWithMembers(groupId)`
  - `transferOwnership(groupId, newOwnerId)`
- Zod schemas for all inputs in `web/src/server/api/schemas/user.ts`

steps:
1. Create `web/src/server/api/routers/user.ts`.
2. Define Zod schemas:
   - `updateUserSchema`: `name: z.string().min(1).optional()`, `email: z.string().email().optional()`
   - `inviteMemberSchema`: `email: z.string().email()`, `role: z.enum(['admin', 'member']).default('member')`
   - `removeMemberSchema`: `userId: z.string().uuid()`
   - `updateRoleSchema`: `userId: z.string().uuid()`, `role: z.enum(['owner', 'admin', 'member'])`
3. Implement `user.me`:
   - `protectedProcedure.query(({ ctx }) => ctx.db.select().from(users).where(eq(users.id, ctx.user.id)))`
   - Include relations: accounts, sessions, familyGroups, subscriptions
4. Implement `user.update`:
   - Validate input with Zod
   - Update user record, return updated user
5. Implement `user.delete`:
   - Verify user exists
   - Delete all related records (cascade where defined, manual where not)
   - Return success
6. Implement family procedures:
   - `listFamilyMembers`: query family group where user is owner or member, return members with roles
   - `inviteFamilyMember`: create `FamilyGroupMember` record with pending status, trigger email notification (task 14)
   - `removeFamilyMember`: verify caller is owner/admin, delete membership record
   - `updateFamilyMemberRole`: verify caller is owner, update role enum
7. Create service layer `web/src/server/services/user.service.ts`:
   - Extract reusable DB queries
   - Handle error cases (user not found, unauthorized, duplicate email)
8. Create `web/src/server/services/family.service.ts`.
9. Wire router into `web/src/server/api/root.ts`.
10. Write unit tests for service functions.

steps:
- Unit: `getUserById` returns user with correct relations
- Unit: `updateUser` applies changes and returns updated record
- Unit: `inviteMember` creates record and rejects duplicate invites
- Unit: `removeMember` rejects non-admin callers
- Unit: `updateMemberRole` rejects non-owner callers
- Integration: tRPC `user.me` returns authenticated user
- Integration: tRPC `user.inviteFamilyMember` creates member record

acceptance_criteria:
- [ ] `user.me` returns the authenticated user's profile with all relations
- [ ] `user.update` modifies allowed fields and rejects invalid data
- [ ] `user.delete` removes the user and all related data
- [ ] Family member listing returns correct members with roles
- [ ] Family invites create pending members and validate permissions
- [ ] Family member removal and role updates enforce authorization rules
- [ ] All procedures use Zod input validation
- [ ] Service layer is pure and testable (no request/response logic)

validation:
- Create a temporary test page or use tRPC playground to call each procedure
- Verify `user.me` returns seeded user data from task 10
- Attempt to remove a family member as a regular member → expect TRPCError FORBIDDEN
- Run `cd web && pnpm test` for unit tests

notes:
- Reference legacy code: `packages/api/src/routes/` for route patterns, `packages/shared-auth/src/` for auth logic.
- Family group ownership transfer is a sensitive operation — ensure only the current owner can initiate it.
- When a user is deleted, consider soft-delete (set `deletedAt`) instead of hard delete to preserve audit trails. Update schema if needed.
- The `user.delete` operation must handle cascade deletes carefully to avoid foreign key constraint errors. Either rely on Drizzle's cascade definitions or delete in correct order.
- For invites, consider adding an `Invitation` table with expiration if not already in schema. If not, add it as part of this task.
