feat(service-integrations): phase 2 — integration widget & app form UI
- Add 6 renderer components: StatCard, Gauge, List, Progress, AlertBanner, Chart - Add IntegrationWidget container with auto-refresh, loading, error states - Add IntegrationAlertOverlay for layout-level critical alerts - Add IntegrationConfigFields for dynamic form generation from Zod schemas - Register integration type in WidgetRenderer - Extend WidgetCreationForm with integration app/endpoint pickers - Extend AppForm with integration config section and test connection button - Add /api/integrations/alerts endpoint
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
import { json } from '@sveltejs/kit';
|
||||
import type { RequestHandler } from './$types.js';
|
||||
import { success } from '$lib/server/utils/response.js';
|
||||
import { prisma } from '$lib/server/prisma.js';
|
||||
import * as registry from '$lib/server/integrations/registry.js';
|
||||
import * as cache from '$lib/server/integrations/cache.js';
|
||||
import { tryDecrypt } from '$lib/server/integrations/encryption.js';
|
||||
import type { AlertBannerData } from '$lib/server/integrations/types.js';
|
||||
|
||||
export const GET: RequestHandler = async () => {
|
||||
try {
|
||||
const apps = await prisma.app.findMany({
|
||||
where: { integrationEnabled: true, integrationType: { not: null } }
|
||||
});
|
||||
|
||||
const alerts: AlertBannerData[] = [];
|
||||
|
||||
for (const app of apps) {
|
||||
const integration = registry.get(app.integrationType!);
|
||||
if (!integration) continue;
|
||||
|
||||
const alertEndpoints = integration.endpoints.filter((ep) => ep.renderer === 'alert-banner');
|
||||
if (alertEndpoints.length === 0) continue;
|
||||
|
||||
const configJson = tryDecrypt(app.integrationConfig);
|
||||
const config = configJson ? JSON.parse(configJson) : {};
|
||||
|
||||
for (const endpoint of alertEndpoints) {
|
||||
const cacheKey = `${app.id}:${endpoint.id}`;
|
||||
const cached = cache.get<AlertBannerData>(cacheKey);
|
||||
if (cached) {
|
||||
alerts.push(cached);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await integration.fetchData(app.url, config, endpoint.id);
|
||||
if (data.renderer === 'alert-banner') {
|
||||
const alertData = data.data as AlertBannerData;
|
||||
cache.set(cacheKey, alertData, endpoint.refreshInterval);
|
||||
if (alertData.severity === 'warning' || alertData.severity === 'critical') {
|
||||
alerts.push(alertData);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Skip failed alert fetches
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return json(success(alerts));
|
||||
} catch {
|
||||
return json(success([]));
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user