Files
Fabric.Backend.Center/src/entities/user.entity.ts
hzhang f1ca33f2a2 feat(auth): center-scoped single admin + GET /admin-email + cli
Triage channels in Guilds need to deliver every message to a single
"observer" admin user across the whole Center deployment (regardless of
on-duty / mention). This adds the data + auth surface for that.

## Schema

`users.isAdmin TINYINT DEFAULT 0` (synchronize:true auto-applies; cli
`user set-admin` enforces at-most-one in a transaction).

## CLI (subject `user`)

- `set-admin --email <e>` — clears every other admin row first, then
  marks the target. Returns `{admin: {email, userId}}`
- `clear-admin` — unsets all (returns `{cleared: N}`)
- `show-admin` — prints `{admin: {email, userId}}` or `{admin: null}`

## HTTP

`GET /auth/admin-email` (NOT @Public — requires a guild-node api key
via the existing CenterApiKeyGuard). Returns:
  - `{email: "...", userId: "..."}` if an admin exists
  - `null` (literal JSON) if no admin

Guild backends cache the result (1 day TTL per spec; with cli refresh
override on guild side).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 22:09:58 +01:00

36 lines
1.0 KiB
TypeScript

import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity('users')
export class User {
@PrimaryGeneratedColumn('uuid')
id!: string;
@Column({ unique: true })
email!: string;
@Column({ type: 'varchar', length: 120, nullable: true })
name!: string | null;
@Column()
passwordHash!: string;
@Column({ type: 'varchar', length: 255, nullable: true })
refreshTokenHash!: string | null;
/**
* Center-scoped admin flag. At most one user should have this set at a
* time; the cli `user set-admin` enforces single-admin by clearing all
* other admins before promoting the target user. Guild backends learn
* the current admin via `GET /admin-email`.
*
* Use: triage channels deliver every message to the admin as a silent
* observer (wake=false) regardless of on-duty / mention status, so a
* single human can audit all triage traffic.
*/
@Column({ type: 'tinyint', default: 0, name: 'isAdmin' })
isAdmin!: boolean;
@CreateDateColumn()
createdAt!: Date;
}