feat(dashboard): add MeDto, AdminDto, apiKeys + role/password helpers
Extends api.ts with the Phase 3.5 wire types (InstanceRole, Scope, MeDto, AdminDto, ApiKeyDto, MintApiKey*) and the matching apiKeys namespace. AdminUser in auth.ts now carries instance_role and email, so layout/store consumers see the role without a separate fetch. Adds two tiny lib helpers used by the upcoming profile/users pages: RoleChip.svelte for the colored owner/admin/member pill, and password-gen.ts for crypto.getRandomValues-backed temporary passwords used in user-invite + reset-password reveals. AdminUserRecord stays as a deprecated alias until /admins is retired in a follow-up commit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,9 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import { base } from '$app/paths';
|
||||
import { browser } from '$app/environment';
|
||||
import { clearSession, getToken, setSession, type AdminUser } from './auth';
|
||||
import { clearSession, getToken, setSession, type InstanceRole } from './auth';
|
||||
|
||||
export type { InstanceRole };
|
||||
|
||||
export interface ScriptSandbox {
|
||||
max_operations?: number;
|
||||
@@ -232,27 +234,88 @@ function safeJson(text: string): unknown {
|
||||
}
|
||||
}
|
||||
|
||||
export interface AdminUserRecord {
|
||||
export type Scope =
|
||||
| 'script:read'
|
||||
| 'script:write'
|
||||
| 'route:write'
|
||||
| 'domain:manage'
|
||||
| 'log:read'
|
||||
| 'app:admin'
|
||||
| 'instance:admin';
|
||||
|
||||
export const ALL_SCOPES: readonly Scope[] = [
|
||||
'script:read',
|
||||
'script:write',
|
||||
'route:write',
|
||||
'domain:manage',
|
||||
'log:read',
|
||||
'app:admin',
|
||||
'instance:admin'
|
||||
] as const;
|
||||
|
||||
export function isInstanceScope(s: Scope): boolean {
|
||||
return s.startsWith('instance:');
|
||||
}
|
||||
|
||||
export interface MeDto {
|
||||
id: string;
|
||||
username: string;
|
||||
instance_role: InstanceRole;
|
||||
email: string | null;
|
||||
}
|
||||
|
||||
export interface AdminDto {
|
||||
id: string;
|
||||
username: string;
|
||||
is_active: boolean;
|
||||
instance_role: InstanceRole;
|
||||
email: string | null;
|
||||
created_at: string;
|
||||
last_login_at: string | null;
|
||||
}
|
||||
|
||||
/** @deprecated use AdminDto. Kept until the /admins route is retired. */
|
||||
export type AdminUserRecord = AdminDto;
|
||||
|
||||
export interface CreateAdminInput {
|
||||
username: string;
|
||||
password: string;
|
||||
instance_role?: InstanceRole;
|
||||
email?: string | null;
|
||||
}
|
||||
|
||||
export interface PatchAdminInput {
|
||||
username?: string;
|
||||
password?: string;
|
||||
is_active?: boolean;
|
||||
instance_role?: InstanceRole;
|
||||
email?: string | null;
|
||||
}
|
||||
|
||||
export interface ApiKeyDto {
|
||||
id: string;
|
||||
prefix: string;
|
||||
name: string;
|
||||
scopes: Scope[];
|
||||
app_id: string | null;
|
||||
expires_at: string | null;
|
||||
last_used_at: string | null;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface MintApiKeyInput {
|
||||
name: string;
|
||||
scopes: Scope[];
|
||||
app_id?: string | null;
|
||||
expires_at?: string | null;
|
||||
}
|
||||
|
||||
export interface MintApiKeyResponse extends ApiKeyDto {
|
||||
raw_token: string;
|
||||
}
|
||||
|
||||
interface LoginResponse {
|
||||
user: AdminUser;
|
||||
user: MeDto;
|
||||
token: string;
|
||||
expires_at: string;
|
||||
}
|
||||
@@ -263,7 +326,7 @@ export const api = {
|
||||
version: () => adminRequest<VersionInfo>('/version'),
|
||||
|
||||
auth: {
|
||||
login: async (username: string, password: string): Promise<AdminUser> => {
|
||||
login: async (username: string, password: string): Promise<MeDto> => {
|
||||
const r = await adminRequest<LoginResponse>('/api/v1/admin/auth/login', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ username, password })
|
||||
@@ -282,19 +345,19 @@ export const api = {
|
||||
clearSession();
|
||||
}
|
||||
},
|
||||
me: () => adminRequest<AdminUser>('/api/v1/admin/auth/me')
|
||||
me: () => adminRequest<MeDto>('/api/v1/admin/auth/me')
|
||||
},
|
||||
|
||||
admins: {
|
||||
list: () => adminRequest<AdminUserRecord[]>('/api/v1/admin/admins'),
|
||||
get: (id: string) => adminRequest<AdminUserRecord>(`/api/v1/admin/admins/${id}`),
|
||||
list: () => adminRequest<AdminDto[]>('/api/v1/admin/admins'),
|
||||
get: (id: string) => adminRequest<AdminDto>(`/api/v1/admin/admins/${id}`),
|
||||
create: (input: CreateAdminInput) =>
|
||||
adminRequest<AdminUserRecord>('/api/v1/admin/admins', {
|
||||
adminRequest<AdminDto>('/api/v1/admin/admins', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(input)
|
||||
}),
|
||||
update: (id: string, input: PatchAdminInput) =>
|
||||
adminRequest<AdminUserRecord>(`/api/v1/admin/admins/${id}`, {
|
||||
adminRequest<AdminDto>(`/api/v1/admin/admins/${id}`, {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify(input)
|
||||
}),
|
||||
@@ -302,6 +365,17 @@ export const api = {
|
||||
adminRequest<null>(`/api/v1/admin/admins/${id}`, { method: 'DELETE' })
|
||||
},
|
||||
|
||||
apiKeys: {
|
||||
list: () => adminRequest<ApiKeyDto[]>('/api/v1/admin/api-keys'),
|
||||
mint: (input: MintApiKeyInput) =>
|
||||
adminRequest<MintApiKeyResponse>('/api/v1/admin/api-keys', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(input)
|
||||
}),
|
||||
revoke: (id: string) =>
|
||||
adminRequest<null>(`/api/v1/admin/api-keys/${id}`, { method: 'DELETE' })
|
||||
},
|
||||
|
||||
routes: {
|
||||
listForScript: (scriptId: string) =>
|
||||
adminRequest<Route[]>(`/api/v1/admin/scripts/${scriptId}/routes`),
|
||||
|
||||
Reference in New Issue
Block a user