export type Role = "user" | "moderator" | "admin"; export const ROLES: readonly Role[] = ["user", "moderator", "admin"]; export function isRole(value: unknown): value is Role { return typeof value === "string" && (ROLES as readonly string[]).includes(value); } /** A user as anyone may see them: no password hash, no email. */ export interface PublicUser { id: string; username: string; role: Role; createdAt: Date; updatedAt?: Date; avatarMime?: string; description?: string; invitedByUsername?: string; } /** A user as they see themselves: everything but the password hash. */ export interface SelfUser extends PublicUser { email: string; } /** A match for the @mention autocomplete. */ export interface UserSuggestion { id: string; username: string; avatarMime: string | null; } /** Someone a user invited, directly or down the line. */ export interface Invitee { id: string; username: string; avatarMime?: string; invitedById: string; createdAt: Date; } export interface LoginUserRequest { username: string; password: string; } export interface RegisterUserRequest { username: string; password: string; inviteToken: string; email: string; } export interface UpdateUserRequest { username?: string; password?: string; role?: Role; description?: string | null; email?: string; } /** The session token, plus the media-only one for file and thumbnail URLs. */ export interface AuthTokens { token: string; mediaToken: string; } export interface AuthResponse extends AuthTokens { user: SelfUser; } export type MediaTokenResponse = Pick; export interface InviteResponse { token: string; }