feat(api): /api/lead с SQLite и миграциями

db/: better-sqlite3 по DB_PATH (WAL, foreign_keys), миграции через
PRAGMA user_version, схема leads и lead_clarifications по плану.
createApp(deps) с инъекцией базы, часов и notifier (пока no-op, зовётся
после ответа). POST /api/lead: только JSON (415), тело до 16 КБ (413),
validateLead → 400, запись → 201. index.ts: env и graceful shutdown.
This commit is contained in:
2026-10-17 15:38:42 +03:00
parent a2885d180c
commit fa0964b24d
9 changed files with 412 additions and 3 deletions
+26
View File
@@ -0,0 +1,26 @@
import type { ValidLead } from '@kvarta/shared/lead';
import type { Db } from './db/index.js';
import type { LeadEvent } from './notifier.js';
export type SaveMeta = { now: Date; userAgent: string | null };
export type SaveResult = { kind: 'created'; event: LeadEvent };
export function saveLead(db: Db, lead: ValidLead, meta: SaveMeta): SaveResult {
const { lastInsertRowid } = db
.prepare(
`INSERT INTO leads (created_at, phone, name, interest, comment, first_touch, last_touch, user_agent)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
)
.run(
meta.now.toISOString(),
lead.phone,
lead.name,
lead.interest,
lead.comment,
lead.firstTouch ? JSON.stringify(lead.firstTouch) : null,
JSON.stringify(lead.lastTouch),
meta.userAgent,
);
return { kind: 'created', event: { kind: 'lead', leadId: Number(lastInsertRowid) } };
}