nxtgauge-admin-solid/src/lib/runtime/storage.ts

144 lines
4.8 KiB
TypeScript
Raw Normal View History

// 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;
}