chore: consolidate platform split checks
This commit is contained in:
@@ -150,11 +150,20 @@ export type PrivacyRetentionPolicyScope = "system" | "tenant" | "user" | "group"
|
||||
export type PolicySourceStep = {
|
||||
scope_type: string;
|
||||
scope_id?: string | null;
|
||||
path: string;
|
||||
label: string;
|
||||
applied_fields?: string[];
|
||||
policy?: PrivacyRetentionPolicyPatch | PrivacyRetentionPolicy | null;
|
||||
};
|
||||
|
||||
export type PolicyDecision = {
|
||||
allowed: boolean;
|
||||
reason?: string | null;
|
||||
source_path: PolicySourceStep[];
|
||||
requirements: string[];
|
||||
details?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type PrivacyRetentionPolicyScopeResponse = {
|
||||
scope_type: PrivacyRetentionPolicyScope;
|
||||
scope_id?: string | null;
|
||||
@@ -165,20 +174,43 @@ export type PrivacyRetentionPolicyScopeResponse = {
|
||||
parent_policy_sources?: PolicySourceStep[];
|
||||
};
|
||||
|
||||
export type PrivacyRetentionPolicyExplainResponse = {
|
||||
scope_type: PrivacyRetentionPolicyScope;
|
||||
scope_id?: string | null;
|
||||
decision: PolicyDecision;
|
||||
effective_policy: PrivacyRetentionPolicy;
|
||||
parent_policy?: PrivacyRetentionPolicy | null;
|
||||
effective_policy_sources?: PolicySourceStep[];
|
||||
parent_policy_sources?: PolicySourceStep[];
|
||||
blocked_fields: PrivacyRetentionPolicyFieldKey[];
|
||||
};
|
||||
|
||||
export type SystemSettingsItem = {
|
||||
default_locale: string;
|
||||
allow_tenant_custom_groups: boolean;
|
||||
allow_tenant_custom_roles: boolean;
|
||||
allow_tenant_api_keys: boolean;
|
||||
privacy_retention_policy: PrivacyRetentionPolicy;
|
||||
maintenance_mode?: { enabled: boolean; message?: string | null };
|
||||
available_languages?: LanguagePackage[];
|
||||
enabled_language_codes?: string[];
|
||||
settings: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type LanguagePackage = {
|
||||
code: string;
|
||||
label: string;
|
||||
native_label?: string | null;
|
||||
};
|
||||
|
||||
export type TenantSettingsItem = {
|
||||
id: string;
|
||||
slug: string;
|
||||
name: string;
|
||||
default_locale: string;
|
||||
available_languages: LanguagePackage[];
|
||||
system_enabled_language_codes: string[];
|
||||
enabled_language_codes: string[];
|
||||
settings: Record<string, unknown>;
|
||||
};
|
||||
|
||||
@@ -192,6 +224,116 @@ export type RetentionRunResponse = {
|
||||
};
|
||||
};
|
||||
|
||||
export type ConfigurationSafetyField = {
|
||||
key: string;
|
||||
label: string;
|
||||
owner_module: string;
|
||||
scope: "system" | "tenant" | "user" | "group" | "campaign";
|
||||
storage: string;
|
||||
ui_managed: boolean;
|
||||
risk: "low" | "medium" | "high" | "destructive";
|
||||
secret_handling: "none" | "reference_only" | "env_only";
|
||||
required_scopes: string[];
|
||||
dry_run_required: boolean;
|
||||
validation_required: boolean;
|
||||
policy_explanation_required: boolean;
|
||||
audit_event?: string | null;
|
||||
maintenance_required: boolean;
|
||||
two_person_approval_required: boolean;
|
||||
rollback_history_required: boolean;
|
||||
notes?: string | null;
|
||||
};
|
||||
|
||||
export type ConfigurationChangeSafetyPlan = {
|
||||
key: string;
|
||||
allowed: boolean;
|
||||
field?: ConfigurationSafetyField | null;
|
||||
risk?: ConfigurationSafetyField["risk"] | null;
|
||||
missing_scopes: string[];
|
||||
dry_run_required: boolean;
|
||||
dry_run_satisfied: boolean;
|
||||
approval_required: boolean;
|
||||
approval_satisfied: boolean;
|
||||
maintenance_required: boolean;
|
||||
maintenance_satisfied: boolean;
|
||||
rollback_history_required: boolean;
|
||||
secret_handling: ConfigurationSafetyField["secret_handling"];
|
||||
audit_event?: string | null;
|
||||
policy_explanation?: string | null;
|
||||
blockers: string[];
|
||||
warnings: string[];
|
||||
};
|
||||
|
||||
export type ConfigurationChangeRequest = {
|
||||
id: string;
|
||||
key: string;
|
||||
label?: string;
|
||||
target?: Record<string, unknown>;
|
||||
dry_run: boolean;
|
||||
requested_by: string;
|
||||
requested_at: string;
|
||||
updated_at: string;
|
||||
status: "pending_approval" | "approved" | "applied" | "rejected" | string;
|
||||
approvals: Array<Record<string, unknown>>;
|
||||
plan: ConfigurationChangeSafetyPlan;
|
||||
value_preview?: unknown;
|
||||
};
|
||||
|
||||
export type ConfigurationChangeRecord = {
|
||||
id: string;
|
||||
version: number;
|
||||
key: string;
|
||||
target?: Record<string, unknown>;
|
||||
actor_user_id: string;
|
||||
approval_request_id?: string | null;
|
||||
approval_user_ids: string[];
|
||||
before?: unknown;
|
||||
after?: unknown;
|
||||
rollback_value?: unknown;
|
||||
status: string;
|
||||
created_at: string;
|
||||
};
|
||||
|
||||
export type ConfigurationPackageDiagnostic = {
|
||||
severity: "blocker" | "warning" | "info";
|
||||
code: string;
|
||||
message: string;
|
||||
module_id?: string | null;
|
||||
object_ref?: string | null;
|
||||
resolution?: string | null;
|
||||
};
|
||||
|
||||
export type ConfigurationPackageRequiredData = {
|
||||
key: string;
|
||||
label: string;
|
||||
data_type: string;
|
||||
required: boolean;
|
||||
secret: boolean;
|
||||
description?: string | null;
|
||||
};
|
||||
|
||||
export type ConfigurationPackagePlanItem = {
|
||||
action: "create" | "update" | "bind" | "skip" | "blocked" | "noop";
|
||||
module_id: string;
|
||||
fragment_type: string;
|
||||
fragment_id?: string | null;
|
||||
summary?: string | null;
|
||||
};
|
||||
|
||||
export type ConfigurationPackageFragment = {
|
||||
module_id: string;
|
||||
fragment_type: string;
|
||||
fragment_id?: string | null;
|
||||
payload: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type ConfigurationPackageRunPayload = {
|
||||
package: Record<string, unknown>;
|
||||
tenant_id?: string | null;
|
||||
supplied_data?: Record<string, unknown>;
|
||||
change_request_id?: string | null;
|
||||
};
|
||||
|
||||
export type GovernanceAssignment = {
|
||||
tenant_id: string;
|
||||
mode: "available" | "required";
|
||||
@@ -289,7 +431,7 @@ export function fetchTenantSettings(settings: ApiSettings): Promise<TenantSettin
|
||||
return apiFetch(settings, "/api/v1/admin/tenant/settings");
|
||||
}
|
||||
|
||||
export function updateTenantSettings(settings: ApiSettings, payload: { default_locale: string }): Promise<TenantSettingsItem> {
|
||||
export function updateTenantSettings(settings: ApiSettings, payload: { default_locale: string; enabled_language_codes?: string[] | null }): Promise<TenantSettingsItem> {
|
||||
return apiFetch(settings, "/api/v1/admin/tenant/settings", { method: "PATCH", body: JSON.stringify(payload) });
|
||||
}
|
||||
|
||||
@@ -502,6 +644,10 @@ export type SystemSettingsUpdatePayload = {
|
||||
allow_tenant_custom_roles: boolean;
|
||||
allow_tenant_api_keys: boolean;
|
||||
privacy_retention_policy?: PrivacyRetentionPolicy | null;
|
||||
maintenance_mode?: { enabled: boolean; message?: string | null } | null;
|
||||
available_languages?: LanguagePackage[] | null;
|
||||
enabled_language_codes?: string[] | null;
|
||||
change_request_id?: string | null;
|
||||
};
|
||||
|
||||
export function updateSystemSettings(settings: ApiSettings, payload: SystemSettingsUpdatePayload): Promise<SystemSettingsItem> {
|
||||
@@ -515,31 +661,127 @@ export function getPrivacyRetentionPolicy(settings: ApiSettings, scope: PrivacyR
|
||||
return apiFetch(settings, `/api/v1/admin/privacy-retention/policies/${encodeURIComponent(scope)}${suffix}`);
|
||||
}
|
||||
|
||||
export function updatePrivacyRetentionPolicy(settings: ApiSettings, scope: PrivacyRetentionPolicyScope, policy: PrivacyRetentionPolicyPatch, scopeId?: string | null): Promise<PrivacyRetentionPolicyScopeResponse> {
|
||||
export function explainPrivacyRetentionPolicy(settings: ApiSettings, scope: PrivacyRetentionPolicyScope, scopeId?: string | null): Promise<PrivacyRetentionPolicyExplainResponse> {
|
||||
const params = new URLSearchParams();
|
||||
if (scopeId) params.set("scope_id", scopeId);
|
||||
const suffix = params.toString() ? `?${params.toString()}` : "";
|
||||
return apiFetch(settings, `/api/v1/admin/privacy-retention/policies/${encodeURIComponent(scope)}${suffix}`, { method: "PUT", body: JSON.stringify({ policy }) });
|
||||
return apiFetch(settings, `/api/v1/admin/privacy-retention/policies/${encodeURIComponent(scope)}/explain${suffix}`);
|
||||
}
|
||||
|
||||
export function updatePrivacyRetentionPolicy(settings: ApiSettings, scope: PrivacyRetentionPolicyScope, policy: PrivacyRetentionPolicyPatch, scopeId?: string | null, changeRequestId?: string | null): Promise<PrivacyRetentionPolicyScopeResponse> {
|
||||
const params = new URLSearchParams();
|
||||
if (scopeId) params.set("scope_id", scopeId);
|
||||
const suffix = params.toString() ? `?${params.toString()}` : "";
|
||||
return apiFetch(settings, `/api/v1/admin/privacy-retention/policies/${encodeURIComponent(scope)}${suffix}`, { method: "PUT", body: JSON.stringify({ policy, change_request_id: changeRequestId ?? null }) });
|
||||
}
|
||||
|
||||
export function runRetentionPolicy(settings: ApiSettings, dryRun = true): Promise<RetentionRunResponse> {
|
||||
return apiFetch(settings, "/api/v1/admin/system/retention/run", { method: "POST", body: JSON.stringify({ dry_run: dryRun }) });
|
||||
}
|
||||
|
||||
export async function fetchConfigurationSafetyCatalog(settings: ApiSettings, includeEnvOnly = false): Promise<ConfigurationSafetyField[]> {
|
||||
const suffix = includeEnvOnly ? "?include_env_only=true" : "";
|
||||
const response = await apiFetch<{ fields: ConfigurationSafetyField[] }>(settings, `/api/v1/admin/configuration-safety${suffix}`);
|
||||
return response.fields;
|
||||
}
|
||||
|
||||
export async function planConfigurationChange(settings: ApiSettings, payload: {
|
||||
key: string;
|
||||
value?: unknown;
|
||||
dry_run?: boolean;
|
||||
maintenance_mode?: boolean;
|
||||
approval_count?: number;
|
||||
}): Promise<ConfigurationChangeSafetyPlan> {
|
||||
const response = await apiFetch<{ plan: ConfigurationChangeSafetyPlan }>(settings, "/api/v1/admin/configuration-safety/plan", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
return response.plan;
|
||||
}
|
||||
|
||||
export function fetchConfigurationChanges(settings: ApiSettings): Promise<{ requests: ConfigurationChangeRequest[]; history: ConfigurationChangeRecord[] }> {
|
||||
return apiFetch(settings, "/api/v1/admin/configuration-changes");
|
||||
}
|
||||
|
||||
export async function createConfigurationChangeRequest(settings: ApiSettings, payload: {
|
||||
key: string;
|
||||
value?: unknown;
|
||||
dry_run?: boolean;
|
||||
target?: Record<string, unknown>;
|
||||
reason?: string | null;
|
||||
}): Promise<ConfigurationChangeRequest> {
|
||||
const response = await apiFetch<{ request: ConfigurationChangeRequest }>(settings, "/api/v1/admin/configuration-change-requests", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
return response.request;
|
||||
}
|
||||
|
||||
export async function approveConfigurationChangeRequest(settings: ApiSettings, requestId: string, reason?: string | null): Promise<ConfigurationChangeRequest> {
|
||||
const response = await apiFetch<{ request: ConfigurationChangeRequest }>(settings, `/api/v1/admin/configuration-change-requests/${encodeURIComponent(requestId)}/approve`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ reason: reason ?? null })
|
||||
});
|
||||
return response.request;
|
||||
}
|
||||
|
||||
export function fetchConfigurationPackageCatalogValidation(settings: ApiSettings): Promise<{ validation: Record<string, unknown> }> {
|
||||
return apiFetch(settings, "/api/v1/admin/configuration-packages/catalog");
|
||||
}
|
||||
|
||||
export function dryRunConfigurationPackage(settings: ApiSettings, payload: ConfigurationPackageRunPayload): Promise<{
|
||||
diagnostics: ConfigurationPackageDiagnostic[];
|
||||
required_data: ConfigurationPackageRequiredData[];
|
||||
plan: ConfigurationPackagePlanItem[];
|
||||
}> {
|
||||
return apiFetch(settings, "/api/v1/admin/configuration-packages/dry-run", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export function applyConfigurationPackage(settings: ApiSettings, payload: ConfigurationPackageRunPayload): Promise<{
|
||||
diagnostics: ConfigurationPackageDiagnostic[];
|
||||
created_refs: Record<string, string>;
|
||||
updated_refs: Record<string, string>;
|
||||
}> {
|
||||
return apiFetch(settings, "/api/v1/admin/configuration-packages/apply", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export function exportConfigurationPackage(settings: ApiSettings, payload: {
|
||||
tenant_id?: string | null;
|
||||
scopes?: string[];
|
||||
module_ids?: string[];
|
||||
object_refs?: string[];
|
||||
}): Promise<{
|
||||
fragments: ConfigurationPackageFragment[];
|
||||
data_requirements: ConfigurationPackageRequiredData[];
|
||||
diagnostics: ConfigurationPackageDiagnostic[];
|
||||
}> {
|
||||
return apiFetch(settings, "/api/v1/admin/configuration-packages/export", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export async function fetchGovernanceTemplates(settings: ApiSettings, kind?: "group" | "role"): Promise<GovernanceTemplateItem[]> {
|
||||
const suffix = kind ? `?kind=${encodeURIComponent(kind)}` : "";
|
||||
const response = await apiFetch<{ templates: GovernanceTemplateItem[] }>(settings, `/api/v1/admin/system/governance-templates${suffix}`);
|
||||
return response.templates;
|
||||
}
|
||||
|
||||
export function createGovernanceTemplate(settings: ApiSettings, payload: Omit<GovernanceTemplateItem, "id" | "created_at" | "updated_at" | "effective_permission_count">): Promise<GovernanceTemplateItem> {
|
||||
export function createGovernanceTemplate(settings: ApiSettings, payload: Omit<GovernanceTemplateItem, "id" | "created_at" | "updated_at" | "effective_permission_count"> & { change_request_id?: string | null }): Promise<GovernanceTemplateItem> {
|
||||
return apiFetch(settings, "/api/v1/admin/system/governance-templates", { method: "POST", body: JSON.stringify(payload) });
|
||||
}
|
||||
|
||||
export function updateGovernanceTemplate(settings: ApiSettings, templateId: string, payload: Omit<GovernanceTemplateItem, "id" | "kind" | "slug" | "created_at" | "updated_at" | "effective_permission_count">): Promise<GovernanceTemplateItem> {
|
||||
export function updateGovernanceTemplate(settings: ApiSettings, templateId: string, payload: Omit<GovernanceTemplateItem, "id" | "kind" | "slug" | "created_at" | "updated_at" | "effective_permission_count"> & { change_request_id?: string | null }): Promise<GovernanceTemplateItem> {
|
||||
return apiFetch(settings, `/api/v1/admin/system/governance-templates/${templateId}`, { method: "PATCH", body: JSON.stringify(payload) });
|
||||
}
|
||||
|
||||
export function deleteGovernanceTemplate(settings: ApiSettings, templateId: string): Promise<void> {
|
||||
return apiFetch(settings, `/api/v1/admin/system/governance-templates/${templateId}`, { method: "DELETE" });
|
||||
export function deleteGovernanceTemplate(settings: ApiSettings, templateId: string, changeRequestId?: string | null): Promise<void> {
|
||||
const suffix = changeRequestId ? `?change_request_id=${encodeURIComponent(changeRequestId)}` : "";
|
||||
return apiFetch(settings, `/api/v1/admin/system/governance-templates/${templateId}${suffix}`, { method: "DELETE" });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user