Files
AriWeb-Suite/portal/webhooks/api.mjs

27 lines
2.0 KiB
JavaScript

/**
* portal/webhooks/api.mjs — HAL → 포털 이벤트 수신과 포털 건강 확인.
*
* POST /internal/hal-events HAL 이 작업 상태 변화를 밀어 넣는다(lib/serviceauth, 키 id 'hal'). 포털 쪽 작업은 5초 주기로 HAL 을 다시 묻는 구조이므로
* 여기서는 "그 HAL 작업을 기다리는 포털 작업" 의 next_run_at 을 지금으로 당겨 바로 깨운다(둘 중 빠른 쪽, docs/HAL-JOBS.md §6).
* GET /api/health HAL 모듈 설명자가 부르는 건강 확인(버전·DB·작업 엔진).
*/
import { verifyCtx } from '../../lib/serviceauth.mjs';
import { HttpError } from '../../lib/http.mjs';
import { nowIso } from '../../lib/db.mjs';
export async function register(app, deps) {
const { store } = deps;
app.get('/api/health', (ctx) => { let db = false; try { db = !!store.get('SELECT 1 AS ok').ok; } catch (_) {} return { ok: db, version: deps.version, db, jobs: deps.jobs?.kinds().length || 0, time: nowIso() }; });
app.post('/internal/hal-events', async (ctx) => {
const r = await verifyCtx(ctx, (id) => (id === 'hal' ? deps.vault.get('service:hal') : null));
if (!r.ok) throw new HttpError(401, 'service_auth_failed', `서비스 인증 실패(${r.reason})`);
const ev = await ctx.body(); const halJobId = String(ev.halJobId || ev.id || ''); if (!halJobId) return { ok: true, woken: 0 };
// 기다리는 포털 작업: 단계 결과에 halJobId 를 적어 둔 작업(halJobFor) 또는 deployments.hal_job_id 로 연결된 작업
const now = nowIso(); let woken = 0;
woken += store.run('UPDATE jobs SET next_run_at=?, updated_at=? WHERE status=\'queued\' AND next_run_at>? AND steps_json LIKE ?', now, now, now, `%${halJobId}%`).changes;
const dep = store.get('SELECT job_id FROM deployments WHERE hal_job_id=?', halJobId);
if (dep && dep.job_id) woken += store.run('UPDATE jobs SET next_run_at=?, updated_at=? WHERE id=? AND status=\'queued\' AND next_run_at>?', now, now, dep.job_id, now).changes;
return { ok: true, woken };
});
}