- Service API: list (filtered by type/owner/lifecycle/tier), detail, upsert, delete, ownership summary - Discovery API: trigger AWS/GitHub scans, scan history, staged update review (apply/reject) - Search: Meilisearch full-text with PG ILIKE fallback, reindex endpoint - Data layer: withTenant() RLS wrapper, Zod config with MEILI_URL/MEILI_KEY - Fastify server entry point
64 lines
2.2 KiB
TypeScript
64 lines
2.2 KiB
TypeScript
import type { FastifyInstance } from 'fastify';
|
|
import pino from 'pino';
|
|
import { withTenant } from '../data/db.js';
|
|
|
|
const logger = pino({ name: 'api-discovery' });
|
|
|
|
export function registerDiscoveryRoutes(app: FastifyInstance) {
|
|
// Trigger AWS discovery scan
|
|
app.post('/api/v1/discovery/aws', async (req, reply) => {
|
|
const tenantId = (req as any).tenantId;
|
|
// TODO: Enqueue scan job (Redis queue or direct execution)
|
|
logger.info({ tenantId }, 'AWS discovery scan triggered');
|
|
return reply.status(202).send({ status: 'scan_queued', scanner: 'aws' });
|
|
});
|
|
|
|
// Trigger GitHub discovery scan
|
|
app.post('/api/v1/discovery/github', async (req, reply) => {
|
|
const tenantId = (req as any).tenantId;
|
|
logger.info({ tenantId }, 'GitHub discovery scan triggered');
|
|
return reply.status(202).send({ status: 'scan_queued', scanner: 'github' });
|
|
});
|
|
|
|
// Get scan history
|
|
app.get('/api/v1/discovery/history', async (req, reply) => {
|
|
const tenantId = (req as any).tenantId;
|
|
|
|
const result = await withTenant(tenantId, async (client) => {
|
|
return client.query('SELECT * FROM scan_history ORDER BY started_at DESC LIMIT 20');
|
|
});
|
|
|
|
return { scans: result.rows };
|
|
});
|
|
|
|
// List staged updates (from partial scans)
|
|
app.get('/api/v1/discovery/staged', async (req, reply) => {
|
|
const tenantId = (req as any).tenantId;
|
|
|
|
const result = await withTenant(tenantId, async (client) => {
|
|
return client.query("SELECT * FROM staged_updates WHERE status = 'pending' ORDER BY created_at DESC");
|
|
});
|
|
|
|
return { staged: result.rows };
|
|
});
|
|
|
|
// Apply or reject staged update
|
|
app.post('/api/v1/discovery/staged/:id/:action', async (req, reply) => {
|
|
const { id, action } = req.params as { id: string; action: string };
|
|
const tenantId = (req as any).tenantId;
|
|
|
|
if (action !== 'apply' && action !== 'reject') {
|
|
return reply.status(400).send({ error: 'Action must be apply or reject' });
|
|
}
|
|
|
|
const newStatus = action === 'apply' ? 'applied' : 'rejected';
|
|
|
|
await withTenant(tenantId, async (client) => {
|
|
await client.query('UPDATE staged_updates SET status = $1 WHERE id = $2', [newStatus, id]);
|
|
// TODO: If applied, merge changes into services table
|
|
});
|
|
|
|
return { status: newStatus };
|
|
});
|
|
}
|