feat(drive): policies management from UI
This commit is contained in:
@@ -13,6 +13,8 @@ import type {
|
||||
Drive,
|
||||
DriveMember,
|
||||
DriveMemberSubject,
|
||||
DrivePolicies,
|
||||
DrivePoliciesPartial,
|
||||
DriveRole
|
||||
} from '$lib/api/types';
|
||||
|
||||
@@ -130,6 +132,41 @@ export async function deleteDrive(driveId: string): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* `PATCH /api/drives/{id}/policies` — update drive policies (D5).
|
||||
*
|
||||
* **OxiCloud-admin only.** Owners cannot mutate policies — the carve-out
|
||||
* exists because policies are a compliance surface (an owner who could
|
||||
* flip them would defeat the gates by disabling, sharing, re-enabling).
|
||||
* Non-admin callers receive 404 (anti-enum). The frontend only surfaces
|
||||
* this from the admin panel.
|
||||
*
|
||||
* Body is a partial — keys not present are left untouched at the JSONB
|
||||
* merge layer. Returns the post-merge typed view.
|
||||
*/
|
||||
export async function updateDrivePolicies(
|
||||
driveId: string,
|
||||
partial: DrivePoliciesPartial
|
||||
): Promise<DrivePolicies> {
|
||||
const res = await apiFetch(`/api/drives/${encodeURIComponent(driveId)}/policies`, {
|
||||
method: 'PATCH',
|
||||
headers: { ...JSON_HEADERS, ...getCsrfHeaders() },
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify(partial)
|
||||
});
|
||||
if (!res.ok) {
|
||||
let detail = '';
|
||||
try {
|
||||
const parsed = (await res.json()) as { error?: string; message?: string };
|
||||
detail = parsed.error ?? parsed.message ?? '';
|
||||
} catch {
|
||||
/* response body wasn't JSON */
|
||||
}
|
||||
throw new Error(detail || `update policies failed: ${res.status}`);
|
||||
}
|
||||
return (await res.json()) as DrivePolicies;
|
||||
}
|
||||
|
||||
/**
|
||||
* `DELETE /api/drives/{id}/members/{kind}/{sid}` — remove a member.
|
||||
* Idempotent (removing a non-member returns 204). Refused with 400 if it
|
||||
|
||||
@@ -237,12 +237,39 @@ export interface Drive {
|
||||
root_folder_id: string;
|
||||
quota_bytes?: number | null;
|
||||
used_bytes: number;
|
||||
/**
|
||||
* Drive policies — raw JSONB bag from the backend. Unknown keys are
|
||||
* preserved verbatim. For the typed view used by the admin policy
|
||||
* editor, see [`DrivePolicies`].
|
||||
*/
|
||||
policies: Record<string, unknown>;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
caller_role?: DriveRole | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Typed mirror of the five known D5 policy keys. Every field defaults to
|
||||
* `false` (= allowed). The wire shape returned by
|
||||
* `PATCH /api/drives/{id}/policies` carries all five keys; the request
|
||||
* body uses [`DrivePoliciesPartial`] so unsupplied keys aren't disturbed.
|
||||
*
|
||||
* See `docs/plan/drive.md` §8 for what each key gates.
|
||||
*/
|
||||
export interface DrivePolicies {
|
||||
forbid_sharing: boolean;
|
||||
forbid_external_sharing: boolean;
|
||||
forbid_public_links: boolean;
|
||||
forbid_cross_drive_move: boolean;
|
||||
forbid_owner_role_change: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Body shape for the admin policy editor — every key optional so omitting
|
||||
* a field leaves that policy untouched (the backend uses a JSONB merge).
|
||||
*/
|
||||
export type DrivePoliciesPartial = Partial<DrivePolicies>;
|
||||
|
||||
/**
|
||||
* Request body for `POST /api/drives` (D3a). Mirrors `CreateDriveDto` in
|
||||
* `src/interfaces/api/handlers/drive_handler.rs`. `kind: 'personal'` is a
|
||||
|
||||
Reference in New Issue
Block a user