143 lines
4.8 KiB
TypeScript
143 lines
4.8 KiB
TypeScript
// All API calls go through the server route gateway proxy
|
|
const API_GATEWAY = '/api/gateway';
|
|
|
|
export type RuntimeRecordType = 'role' | 'dashboard' | 'onboarding';
|
|
export type RuntimeRecordStatus = 'draft' | 'published';
|
|
|
|
export type RuntimeStoredRecord<T> = {
|
|
status: RuntimeRecordStatus;
|
|
updatedAt: string;
|
|
payload: T;
|
|
};
|
|
|
|
export type RuntimeListItem<T> = RuntimeStoredRecord<T> & {
|
|
key: string;
|
|
};
|
|
|
|
// Types corresponding to Rust backend models
|
|
type RoleRecord = {
|
|
id: string;
|
|
key: string;
|
|
name: string;
|
|
audience: string;
|
|
};
|
|
|
|
export async function saveRuntimeConfig(type: RuntimeRecordType, key: string, payload: unknown, status: RuntimeRecordStatus) {
|
|
const normalizedKey = key.trim().toUpperCase();
|
|
if (!normalizedKey) return;
|
|
|
|
try {
|
|
// 1. Ensure the Role exists first. We lookup by key.
|
|
// If it doesn't exist, we create it generically.
|
|
let roleRes = await fetch(`${API_GATEWAY}/api/admin/roles/${normalizedKey}`);
|
|
let role: RoleRecord;
|
|
|
|
if (!roleRes.ok && roleRes.status === 404) {
|
|
// Create it
|
|
const createRes = await fetch(`${API_GATEWAY}/api/admin/roles`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
key: normalizedKey,
|
|
name: normalizedKey, // Using key as name for now
|
|
audience: 'EXTERNAL',
|
|
})
|
|
});
|
|
if (!createRes.ok) throw new Error("Failed to create role");
|
|
role = await createRes.json();
|
|
} else {
|
|
role = await roleRes.json();
|
|
}
|
|
|
|
// 2. Attach the specific config payload to the role
|
|
const endpointMap: Record<RuntimeRecordType, string> = {
|
|
role: '/api/runtime-config', // Storing the "role config wrapper" as runtime_config
|
|
dashboard: '/api/admin/dashboard-config',
|
|
onboarding: '/api/admin/onboarding-config',
|
|
};
|
|
|
|
let bodyPayload: any = {
|
|
role_id: role.id,
|
|
};
|
|
|
|
if (type === 'dashboard') {
|
|
bodyPayload.audience = 'EXTERNAL';
|
|
bodyPayload.config_json = payload;
|
|
} else if (type === 'onboarding') {
|
|
bodyPayload.schema_json = payload;
|
|
} else if (type === 'role') {
|
|
bodyPayload.config_json = payload;
|
|
}
|
|
|
|
const saveRes = await fetch(`${API_GATEWAY}${endpointMap[type]}`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(bodyPayload)
|
|
});
|
|
|
|
if (!saveRes.ok) {
|
|
throw new Error(`Failed to save ${type} config`);
|
|
}
|
|
|
|
console.log(`Saved ${type} mapping for role ${normalizedKey} successfully to Rust backend`);
|
|
|
|
} catch (err) {
|
|
console.error(`Error saving runtime config [${type}]:`, err);
|
|
}
|
|
}
|
|
|
|
export async function listRuntimeConfigs<T>(type: RuntimeRecordType): Promise<Array<RuntimeListItem<T>>> {
|
|
const endpointMap: Record<RuntimeRecordType, string> = {
|
|
role: '/api/admin/roles',
|
|
dashboard: '/api/admin/dashboard-config',
|
|
onboarding: '/api/admin/onboarding-config',
|
|
};
|
|
|
|
try {
|
|
const res = await fetch(`${API_GATEWAY}${endpointMap[type]}`);
|
|
if (!res.ok) throw new Error(`Failed to list ${type} configs`);
|
|
const data = await res.json();
|
|
|
|
// Map Rust backend models to UI expected format
|
|
return data.map((item: any) => ({
|
|
key: item.role_key || item.key,
|
|
status: item.is_active ? 'published' : 'draft',
|
|
updatedAt: item.updated_at || item.created_at,
|
|
payload: item.config_json || item.schema_json || item,
|
|
// include raw id for routing convenience
|
|
id: item.id,
|
|
role_id: item.role_id
|
|
}));
|
|
} catch (err) {
|
|
console.error(`Error listing runtime config [${type}]:`, err);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
export async function getRuntimeConfig<T>(type: RuntimeRecordType, roleId: string): Promise<RuntimeStoredRecord<T> | null> {
|
|
const endpointMap: Record<RuntimeRecordType, string> = {
|
|
role: `/api/admin/roles/${roleId}`, // Assume roleId is key if testing
|
|
dashboard: `/api/admin/dashboard-config/${roleId}?audience=EXTERNAL`,
|
|
onboarding: `/api/admin/onboarding-config/${roleId}`,
|
|
};
|
|
|
|
try {
|
|
const res = await fetch(`${API_GATEWAY}${endpointMap[type]}`);
|
|
if (!res.ok) return null;
|
|
const data = await res.json();
|
|
|
|
return {
|
|
status: data.is_active ? 'published' : 'draft',
|
|
updatedAt: data.updated_at || data.created_at,
|
|
payload: data.config_json || data.schema_json || data,
|
|
};
|
|
} catch (err) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export async function deleteRuntimeConfig(type: RuntimeRecordType, key: string): Promise<boolean> {
|
|
// Mock delete since hard deletes usually aren't ideal for configs (we just soft-disable them on new save)
|
|
console.log(`Delete requested for ${type} config with key ${key}. In Rust, we just save a new one with is_active=true overriding the old one.`);
|
|
return true;
|
|
}
|