Some checks are pending
CI / Lint bridge (Biome) (push) Waiting to run
CI / Type-check bridge (push) Blocked by required conditions
CI / Tests unit bridge (push) Blocked by required conditions
CI / Tests integration bridge (push) Blocked by required conditions
CI / Security scan (push) Waiting to run
CI / Docker build + healthcheck (push) Blocked by required conditions
Decouverts via smoke test local contre Baserow + Docmost reels.
1. **BaseRepo.list robuste** : try/catch toDomain par row, skip + log warn
si throw (ex Personne avec splits null != 100). Avant : 500 sur la liste
entiere. Apres : 200 avec items valides + meta.skipped count pour visibilite.
`get()` continue de propager (un get sur row corrompue = legitimement 500
pour investigation manuelle).
2. **BASEROW_TABLE_IDS env override** : BaserowClient.resolveTableIds appelle
/api/database/tables/database/:id/ qui requiert un JWT user (Baserow API
distingue DB tokens reservees aux endpoints rows, et JWT pour les endpoints
admin). En dev/prod simple on passe le mapping directement par env var :
BASEROW_TABLE_IDS={"personne":609,"formation":610,...}. Le code resolveTableIds
reste en place pour Phase 3+ (bridge avec JWT user).
Smoke test post-fix :
- GET /api/health, /api/ready : 200
- Auth : 401 absent / 401 invalide / 200 valide
- GET /personnes (rows test invalides) : 200 data:[] meta.skipped:2
- GET /formations, /projets : 200 avec rows
- GET /personnes/9999 : 404
Tests Vitest : 163/163 verts. tsc + biome ci verts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
88 lines
3.4 KiB
TypeScript
88 lines
3.4 KiB
TypeScript
/**
|
|
* Bridge service entrypoint — Hono HTTP server.
|
|
*
|
|
* Boot sequence : loadConfig -> initContainer (Baserow + Redis + repos + token map)
|
|
* -> wire middleware globaux + routes /api/v1/* avec auth + serve.
|
|
*/
|
|
|
|
import { serve } from '@hono/node-server';
|
|
import { Hono } from 'hono';
|
|
import { logger as honoLogger } from 'hono/logger';
|
|
import { loadConfig } from './lib/config.js';
|
|
import { getContainer, initContainer } from './lib/container.js';
|
|
import { logger } from './lib/logger.js';
|
|
import { type AuthVariables, authMiddleware } from './middleware/auth.js';
|
|
import { errorHandler } from './middleware/error-handler.js';
|
|
import { attributionsRoutes } from './routes/attributions.js';
|
|
import { formationsRoutes } from './routes/formations.js';
|
|
import { interventionsRoutes } from './routes/interventions.js';
|
|
import { modulesRoutes } from './routes/modules.js';
|
|
import { personnesRoutes } from './routes/personnes.js';
|
|
import { projetsRoutes } from './routes/projets.js';
|
|
|
|
export async function buildApp(): Promise<Hono<{ Variables: AuthVariables }>> {
|
|
const app = new Hono<{ Variables: AuthVariables }>();
|
|
app.use('*', honoLogger());
|
|
app.onError(errorHandler);
|
|
|
|
app.get('/api/health', (c) => c.json({ status: 'ok', service: 'bridge', version: '0.1.0' }));
|
|
|
|
app.get('/api/ready', async (c) => {
|
|
const { baserow, redis } = getContainer();
|
|
const [baserowOk, redisOk] = await Promise.all([baserow.healthCheck(), redis.healthCheck()]);
|
|
const status = baserowOk && redisOk ? 'ok' : 'degraded';
|
|
const code = baserowOk && redisOk ? 200 : 503;
|
|
return c.json(
|
|
{ status, dependencies: { baserow: baserowOk, redis: redisOk } },
|
|
code as 200 | 503,
|
|
);
|
|
});
|
|
|
|
// Auth middleware applique sur tout /api/v1/*
|
|
const { tokens } = getContainer();
|
|
const v1 = new Hono<{ Variables: AuthVariables }>();
|
|
v1.use('*', authMiddleware(tokens));
|
|
v1.route('/personnes', personnesRoutes);
|
|
v1.route('/formations', formationsRoutes);
|
|
v1.route('/projets', projetsRoutes);
|
|
v1.route('/modules', modulesRoutes);
|
|
v1.route('/interventions', interventionsRoutes);
|
|
v1.route('/attributions', attributionsRoutes);
|
|
app.route('/api/v1', v1);
|
|
|
|
app.notFound((c) => c.json({ error: { code: 'NOT_FOUND', message: 'Route not found' } }, 404));
|
|
|
|
return app;
|
|
}
|
|
|
|
async function main() {
|
|
const config = loadConfig();
|
|
// Soit BASEROW_TABLE_IDS={"personne":609,...} (preferred — DB tokens n'ont pas
|
|
// acces a /api/database/tables/database/:id/), soit BASEROW_DATABASE_ID + un JWT
|
|
// user (Phase 3+). Cf doc 19 §5.
|
|
const tableIdsRaw = process.env.BASEROW_TABLE_IDS;
|
|
const databaseIdRaw = process.env.BASEROW_DATABASE_ID;
|
|
let initOpts: Parameters<typeof initContainer>[0];
|
|
if (tableIdsRaw) {
|
|
initOpts = { config, tableIds: JSON.parse(tableIdsRaw) };
|
|
} else {
|
|
const databaseId = databaseIdRaw ? Number.parseInt(databaseIdRaw, 10) : undefined;
|
|
if (!databaseId || Number.isNaN(databaseId)) {
|
|
throw new Error('BASEROW_TABLE_IDS ou BASEROW_DATABASE_ID requis');
|
|
}
|
|
initOpts = { config, databaseId };
|
|
}
|
|
await initContainer(initOpts);
|
|
const app = await buildApp();
|
|
|
|
serve({ fetch: app.fetch, port: config.port }, (info) => {
|
|
logger.info({ port: info.port, env: config.nodeEnv }, 'Bridge service started');
|
|
});
|
|
}
|
|
|
|
if (process.env.NODE_ENV !== 'test' && import.meta.url === `file://${process.argv[1]}`) {
|
|
main().catch((err) => {
|
|
logger.fatal({ err }, 'Bridge service failed to start');
|
|
process.exit(1);
|
|
});
|
|
}
|