Add runtime storage and API/server wiring updates

This commit is contained in:
Ashwin Kumar 2026-03-19 02:36:33 +01:00
parent 56803bd7b2
commit a2ce927d66
3 changed files with 117 additions and 6 deletions

View file

@ -1,4 +1,5 @@
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8080';
// 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';
@ -28,12 +29,12 @@ export async function saveRuntimeConfig(type: RuntimeRecordType, key: string, pa
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_URL}/api/admin/roles/${normalizedKey}`);
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_URL}/api/admin/roles`, {
const createRes = await fetch(`${API_GATEWAY}/api/admin/roles`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@ -68,7 +69,7 @@ export async function saveRuntimeConfig(type: RuntimeRecordType, key: string, pa
bodyPayload.config_json = payload;
}
const saveRes = await fetch(`${API_URL}${endpointMap[type]}`, {
const saveRes = await fetch(`${API_GATEWAY}${endpointMap[type]}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(bodyPayload)
@ -93,7 +94,7 @@ export async function listRuntimeConfigs<T>(type: RuntimeRecordType): Promise<Ar
};
try {
const res = await fetch(`${API_URL}${endpointMap[type]}`);
const res = await fetch(`${API_GATEWAY}${endpointMap[type]}`);
if (!res.ok) throw new Error(`Failed to list ${type} configs`);
const data = await res.json();
@ -121,7 +122,7 @@ export async function getRuntimeConfig<T>(type: RuntimeRecordType, roleId: strin
};
try {
const res = await fetch(`${API_URL}${endpointMap[type]}`);
const res = await fetch(`${API_GATEWAY}${endpointMap[type]}`);
if (!res.ok) return null;
const data = await res.json();

34
src/lib/server/gateway.ts Normal file
View file

@ -0,0 +1,34 @@
// Server-side helper: all backend calls go through the Rust gateway
const GATEWAY_URL = (process.env.GATEWAY_URL || 'http://localhost:8000').replace(/\/+$/, '');
export function gatewayUrl(path: string): string {
const normalized = path.startsWith('/') ? path : `/${path}`;
return `${GATEWAY_URL}${normalized}`;
}
/** Forward the Authorization header from an incoming browser request to the gateway */
export function forwardAuth(request: Request): Record<string, string> {
const auth = request.headers.get('Authorization');
return auth ? { Authorization: auth } : {};
}
/** Forward the Cookie header from an incoming browser request to the gateway */
export function forwardCookies(request: Request): Record<string, string> {
const cookie = request.headers.get('cookie');
return cookie ? { cookie } : {};
}
/**
* Merge auth + cookie headers from the incoming request with any extra headers provided.
*/
export function withAuthHeaders(
request: Request,
extra: Record<string, string> = {},
): Record<string, string> {
return {
'Content-Type': 'application/json',
...forwardAuth(request),
...forwardCookies(request),
...extra,
};
}

View file

@ -0,0 +1,76 @@
import { gatewayUrl, withAuthHeaders } from '~/lib/server/gateway';
/**
* Generic gateway proxy endpoint for admin panel
* Forwards all requests to the Rust backend gateway with proper auth headers
*/
export async function GET({ request, params }: { request: Request; params: any }) {
return proxyRequest('GET', request, params);
}
export async function POST({ request, params }: { request: Request; params: any }) {
return proxyRequest('POST', request, params);
}
export async function PUT({ request, params }: { request: Request; params: any }) {
return proxyRequest('PUT', request, params);
}
export async function DELETE({ request, params }: { request: Request; params: any }) {
return proxyRequest('DELETE', request, params);
}
export async function PATCH({ request, params }: { request: Request; params: any }) {
return proxyRequest('PATCH', request, params);
}
async function proxyRequest(method: string, request: Request, params: any) {
try {
// Handle different param structures
let pathArray = params.path;
if (!Array.isArray(pathArray)) {
pathArray = [pathArray];
}
const path = `/${pathArray.join('/')}`;
const url = new URL(request.url);
const queryString = url.search ? url.search : '';
let body: string | undefined;
if (['POST', 'PUT', 'PATCH'].includes(method)) {
body = await request.text();
}
const upstreamUrl = gatewayUrl(path + queryString);
const upstreamRequest = new Request(upstreamUrl, {
method,
headers: withAuthHeaders(request, {
'Content-Type': request.headers.get('Content-Type') || 'application/json',
}),
body,
cache: 'no-store',
});
const response = await fetch(upstreamRequest);
const responseHeaders = new Headers();
response.headers.forEach((value, key) => {
if (!['server', 'transfer-encoding', 'connection'].includes(key.toLowerCase())) {
responseHeaders.set(key, value);
}
});
responseHeaders.set('Content-Type', 'application/json');
const responseBody = await response.text();
return new Response(responseBody, {
status: response.status,
statusText: response.statusText,
headers: responseHeaders,
});
} catch (error: any) {
return new Response(
JSON.stringify({ success: false, error: error?.message || 'Internal Server Error' }),
{ status: 500, headers: { 'Content-Type': 'application/json' } },
);
}
}