diff --git a/dashboard/src/lib/api.ts b/dashboard/src/lib/api.ts index a8b111a..78a50a1 100644 --- a/dashboard/src/lib/api.ts +++ b/dashboard/src/lib/api.ts @@ -50,6 +50,47 @@ export interface App { export type AppRole = 'app_admin' | 'editor' | 'viewer'; +export interface Group { + id: string; + parent_id: string | null; + slug: string; + name: string; + description: string | null; + structure_version: number; + created_at: string; + updated_at: string; +} + +export interface GroupDetail extends Group { + /** Root → … → this node breadcrumb. */ + path: Group[]; + subgroups: Group[]; + apps: App[]; +} + +export interface GroupMember { + user_id: string; + username: string; + email: string | null; + instance_role: InstanceRole; + is_active: boolean; + role: AppRole; + created_at: string; +} + +export interface CreateGroupInput { + slug: string; + name: string; + description?: string | null; + /** Parent group slug or id; omit (or null) for a root group. */ + parent?: string | null; +} + +export interface PatchGroupInput { + name?: string; + description?: string | null; +} + export type DomainShape = 'exact' | 'wildcard' | 'parameterized'; export interface AppDomain { @@ -89,6 +130,8 @@ export interface CreateAppInput { name: string; description?: string | null; force_takeover?: boolean; + /** Parent group slug or id; omit for the root group. */ + group?: string | null; } export interface PatchAppInput { @@ -778,6 +821,52 @@ export const api = { ) }, + groups: { + list: () => adminRequest('/api/v1/admin/groups'), + get: (idOrSlug: string) => + adminRequest(`/api/v1/admin/groups/${encodeURIComponent(idOrSlug)}`), + create: (input: CreateGroupInput) => + adminRequest('/api/v1/admin/groups', { + method: 'POST', + body: JSON.stringify(input) + }), + update: (idOrSlug: string, input: PatchGroupInput) => + adminRequest(`/api/v1/admin/groups/${encodeURIComponent(idOrSlug)}`, { + method: 'PATCH', + body: JSON.stringify(input) + }), + reparent: (idOrSlug: string, parent: string | null) => + adminRequest( + `/api/v1/admin/groups/${encodeURIComponent(idOrSlug)}/reparent`, + { method: 'POST', body: JSON.stringify({ parent }) } + ), + delete: (idOrSlug: string) => + adminRequest(`/api/v1/admin/groups/${encodeURIComponent(idOrSlug)}`, { + method: 'DELETE' + }), + members: { + list: (idOrSlug: string) => + adminRequest( + `/api/v1/admin/groups/${encodeURIComponent(idOrSlug)}/members` + ), + grant: (idOrSlug: string, input: GrantAppMemberInput) => + adminRequest( + `/api/v1/admin/groups/${encodeURIComponent(idOrSlug)}/members`, + { method: 'POST', body: JSON.stringify(input) } + ), + update: (idOrSlug: string, userId: string, role: AppRole) => + adminRequest( + `/api/v1/admin/groups/${encodeURIComponent(idOrSlug)}/members/${userId}`, + { method: 'PATCH', body: JSON.stringify({ role }) } + ), + remove: (idOrSlug: string, userId: string) => + adminRequest( + `/api/v1/admin/groups/${encodeURIComponent(idOrSlug)}/members/${userId}`, + { method: 'DELETE' } + ) + } + }, + domains: { listForApp: (idOrSlug: string) => adminRequest( diff --git a/dashboard/src/routes/+layout.svelte b/dashboard/src/routes/+layout.svelte index 7f1905f..1a1b2e3 100644 --- a/dashboard/src/routes/+layout.svelte +++ b/dashboard/src/routes/+layout.svelte @@ -55,6 +55,7 @@ PiCloud