mcp-scraper 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +56 -0
  2. package/dist/bin/api-server.cjs +9256 -0
  3. package/dist/bin/api-server.cjs.map +1 -0
  4. package/dist/bin/api-server.d.cts +1 -0
  5. package/dist/bin/api-server.d.ts +1 -0
  6. package/dist/bin/api-server.js +38 -0
  7. package/dist/bin/api-server.js.map +1 -0
  8. package/dist/bin/mcp-stdio-server.cjs +840 -0
  9. package/dist/bin/mcp-stdio-server.cjs.map +1 -0
  10. package/dist/bin/mcp-stdio-server.d.cts +1 -0
  11. package/dist/bin/mcp-stdio-server.d.ts +1 -0
  12. package/dist/bin/mcp-stdio-server.js +41 -0
  13. package/dist/bin/mcp-stdio-server.js.map +1 -0
  14. package/dist/bin/paa-harvest.cjs +1438 -0
  15. package/dist/bin/paa-harvest.cjs.map +1 -0
  16. package/dist/bin/paa-harvest.d.cts +1 -0
  17. package/dist/bin/paa-harvest.d.ts +1 -0
  18. package/dist/bin/paa-harvest.js +37 -0
  19. package/dist/bin/paa-harvest.js.map +1 -0
  20. package/dist/chunk-4API3ZCT.js +1387 -0
  21. package/dist/chunk-4API3ZCT.js.map +1 -0
  22. package/dist/chunk-LXZDJJXR.js +476 -0
  23. package/dist/chunk-LXZDJJXR.js.map +1 -0
  24. package/dist/chunk-ZBP4RHNW.js +805 -0
  25. package/dist/chunk-ZBP4RHNW.js.map +1 -0
  26. package/dist/db-IOYMX64U.js +87 -0
  27. package/dist/db-IOYMX64U.js.map +1 -0
  28. package/dist/index.cjs +1689 -0
  29. package/dist/index.cjs.map +1 -0
  30. package/dist/index.d.cts +210 -0
  31. package/dist/index.d.ts +210 -0
  32. package/dist/index.js +275 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/server-63DR2HE5.js +6062 -0
  35. package/dist/server-63DR2HE5.js.map +1 -0
  36. package/dist/worker-3ECJHPRE.js +88 -0
  37. package/dist/worker-3ECJHPRE.js.map +1 -0
  38. package/package.json +76 -0
@@ -0,0 +1,476 @@
1
+ // src/api/db.ts
2
+ import { createClient } from "@libsql/client/http";
3
+ import { randomBytes, randomUUID, scryptSync, timingSafeEqual } from "crypto";
4
+ import { z } from "zod";
5
+ var DB_URL = process.env.TURSO_DATABASE_URL ?? "file:./paa-api.db";
6
+ var DB_TOKEN = process.env.TURSO_AUTH_TOKEN;
7
+ var _db = null;
8
+ function getDb() {
9
+ if (!_db) _db = createClient({ url: DB_URL, authToken: DB_TOKEN });
10
+ return _db;
11
+ }
12
+ async function migrate() {
13
+ const db = getDb();
14
+ await db.execute(`
15
+ CREATE TABLE IF NOT EXISTS users (
16
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
17
+ email TEXT UNIQUE NOT NULL,
18
+ name TEXT,
19
+ api_key TEXT UNIQUE NOT NULL,
20
+ password_hash TEXT,
21
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
22
+ active INTEGER NOT NULL DEFAULT 1
23
+ )
24
+ `);
25
+ await db.execute(`
26
+ CREATE TABLE IF NOT EXISTS jobs (
27
+ id TEXT PRIMARY KEY,
28
+ user_id INTEGER NOT NULL REFERENCES users(id),
29
+ status TEXT NOT NULL DEFAULT 'pending',
30
+ query TEXT NOT NULL,
31
+ options TEXT NOT NULL,
32
+ callback_url TEXT,
33
+ result TEXT,
34
+ error TEXT,
35
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
36
+ started_at TEXT,
37
+ completed_at TEXT
38
+ )
39
+ `);
40
+ await db.execute(`CREATE INDEX IF NOT EXISTS jobs_user_id ON jobs(user_id)`);
41
+ await db.execute(`CREATE INDEX IF NOT EXISTS jobs_status ON jobs(status)`);
42
+ try {
43
+ await db.execute(`ALTER TABLE users ADD COLUMN password_hash TEXT`);
44
+ } catch {
45
+ }
46
+ try {
47
+ await db.execute(`ALTER TABLE users ADD COLUMN key_active INTEGER NOT NULL DEFAULT 1`);
48
+ } catch {
49
+ }
50
+ await db.execute(`
51
+ CREATE TABLE IF NOT EXISTS kpo_jobs (
52
+ id TEXT PRIMARY KEY,
53
+ user_id INTEGER NOT NULL REFERENCES users(id),
54
+ status TEXT NOT NULL,
55
+ entity_name TEXT NOT NULL,
56
+ entity_type TEXT NOT NULL,
57
+ url TEXT,
58
+ request TEXT NOT NULL,
59
+ result TEXT,
60
+ error TEXT,
61
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
62
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
63
+ )
64
+ `);
65
+ await db.execute(`
66
+ CREATE TABLE IF NOT EXISTS kpo_phase_log (
67
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
68
+ job_id TEXT NOT NULL REFERENCES kpo_jobs(id),
69
+ phase TEXT NOT NULL,
70
+ output TEXT NOT NULL,
71
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
72
+ )
73
+ `);
74
+ await db.execute(`CREATE INDEX IF NOT EXISTS kpo_jobs_user_id ON kpo_jobs(user_id)`);
75
+ await db.execute(`CREATE INDEX IF NOT EXISTS kpo_jobs_status ON kpo_jobs(status)`);
76
+ await db.execute(`CREATE INDEX IF NOT EXISTS kpo_phase_log_job_id ON kpo_phase_log(job_id)`);
77
+ try {
78
+ await db.execute(`ALTER TABLE users ADD COLUMN stripe_customer_id TEXT`);
79
+ } catch {
80
+ }
81
+ try {
82
+ await db.execute(`ALTER TABLE users ADD COLUMN balance_mc INTEGER NOT NULL DEFAULT 0`);
83
+ } catch {
84
+ }
85
+ await db.execute(`
86
+ CREATE TABLE IF NOT EXISTS ledger (
87
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
88
+ user_id INTEGER NOT NULL REFERENCES users(id),
89
+ amount_mc INTEGER NOT NULL,
90
+ operation TEXT NOT NULL,
91
+ description TEXT,
92
+ stripe_pi TEXT,
93
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
94
+ )
95
+ `);
96
+ await db.execute(`CREATE INDEX IF NOT EXISTS ledger_user_id ON ledger(user_id)`);
97
+ await db.execute(`
98
+ CREATE TABLE IF NOT EXISTS site_audit_jobs (
99
+ id TEXT PRIMARY KEY,
100
+ user_id INTEGER,
101
+ status TEXT DEFAULT 'pending',
102
+ client_domain TEXT,
103
+ session_path TEXT,
104
+ request TEXT,
105
+ result TEXT,
106
+ error TEXT,
107
+ created_at TEXT DEFAULT (datetime('now')),
108
+ updated_at TEXT DEFAULT (datetime('now'))
109
+ )
110
+ `);
111
+ await db.execute(`
112
+ CREATE TABLE IF NOT EXISTS site_audit_phase_log (
113
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
114
+ job_id TEXT REFERENCES site_audit_jobs(id),
115
+ phase TEXT,
116
+ completed_at TEXT DEFAULT (datetime('now')),
117
+ output_summary TEXT
118
+ )
119
+ `);
120
+ await db.execute(`CREATE INDEX IF NOT EXISTS site_audit_jobs_user_id ON site_audit_jobs(user_id)`);
121
+ await db.execute(`CREATE INDEX IF NOT EXISTS site_audit_jobs_status ON site_audit_jobs(status)`);
122
+ await db.execute(`CREATE INDEX IF NOT EXISTS site_audit_phase_log_job_id ON site_audit_phase_log(job_id)`);
123
+ }
124
+ function generateApiKey() {
125
+ return "sk_" + randomBytes(24).toString("hex");
126
+ }
127
+ function hashPassword(password) {
128
+ const salt = randomBytes(16).toString("hex");
129
+ const hash = scryptSync(password, salt, 64);
130
+ return `${salt}:${hash.toString("hex")}`;
131
+ }
132
+ function verifyPassword(password, stored) {
133
+ const [salt, hash] = stored.split(":");
134
+ const hashBuf = Buffer.from(hash, "hex");
135
+ const derived = scryptSync(password, salt, 64);
136
+ return timingSafeEqual(hashBuf, derived);
137
+ }
138
+ function rowToUser(row) {
139
+ return {
140
+ id: Number(row.id),
141
+ email: String(row.email),
142
+ name: row.name != null ? String(row.name) : null,
143
+ api_key: String(row.api_key),
144
+ key_active: Number(row.key_active ?? 1),
145
+ password_hash: row.password_hash != null ? String(row.password_hash) : null,
146
+ created_at: String(row.created_at),
147
+ active: Number(row.active),
148
+ stripe_customer_id: row.stripe_customer_id != null ? String(row.stripe_customer_id) : null,
149
+ balance_mc: Number(row.balance_mc ?? 0)
150
+ };
151
+ }
152
+ async function getUserByEmail(email) {
153
+ const res = await getDb().execute({ sql: "SELECT * FROM users WHERE email = ? AND active = 1", args: [email] });
154
+ return res.rows[0] ? rowToUser(res.rows[0]) : void 0;
155
+ }
156
+ async function getUserByApiKey(api_key) {
157
+ const res = await getDb().execute({ sql: "SELECT * FROM users WHERE api_key = ? AND active = 1 AND key_active = 1", args: [api_key] });
158
+ return res.rows[0] ? rowToUser(res.rows[0]) : void 0;
159
+ }
160
+ async function getUserById(id) {
161
+ const res = await getDb().execute({ sql: "SELECT * FROM users WHERE id = ? AND active = 1", args: [id] });
162
+ return res.rows[0] ? rowToUser(res.rows[0]) : void 0;
163
+ }
164
+ async function setPassword(id, password) {
165
+ await getDb().execute({ sql: "UPDATE users SET password_hash = ? WHERE id = ?", args: [hashPassword(password), id] });
166
+ }
167
+ async function getUserStats(userId) {
168
+ const db = getDb();
169
+ const [totR, doneR, failR, tqR, luR] = await Promise.all([
170
+ db.execute({ sql: "SELECT COUNT(*) as n FROM jobs WHERE user_id = ?", args: [userId] }),
171
+ db.execute({ sql: "SELECT COUNT(*) as n FROM jobs WHERE user_id = ? AND status = 'done'", args: [userId] }),
172
+ db.execute({ sql: "SELECT COUNT(*) as n FROM jobs WHERE user_id = ? AND status = 'failed'", args: [userId] }),
173
+ db.execute({ sql: "SELECT COALESCE(SUM(json_extract(result, '$.totalQuestions')), 0) as n FROM jobs WHERE user_id = ? AND status = 'done'", args: [userId] }),
174
+ db.execute({ sql: "SELECT MAX(created_at) as t FROM jobs WHERE user_id = ?", args: [userId] })
175
+ ]);
176
+ return {
177
+ total: Number(totR.rows[0]?.n ?? 0),
178
+ done: Number(doneR.rows[0]?.n ?? 0),
179
+ failed: Number(failR.rows[0]?.n ?? 0),
180
+ totalQuestions: Number(tqR.rows[0]?.n ?? 0),
181
+ lastUsed: luR.rows[0]?.t != null ? String(luR.rows[0].t) : null
182
+ };
183
+ }
184
+ async function createUser(email, name, password) {
185
+ const db = getDb();
186
+ const api_key = generateApiKey();
187
+ const plainPassword = password ?? randomBytes(6).toString("hex");
188
+ const password_hash = hashPassword(plainPassword);
189
+ const result = await db.execute({
190
+ sql: "INSERT INTO users (email, name, api_key, password_hash) VALUES (?, ?, ?, ?)",
191
+ args: [email, name ?? null, api_key, password_hash]
192
+ });
193
+ return { id: result.lastInsertRowid, email, name, api_key, password: plainPassword };
194
+ }
195
+ async function rotateApiKey(userId) {
196
+ const newKey = generateApiKey();
197
+ await getDb().execute({ sql: "UPDATE users SET api_key = ?, key_active = 1 WHERE id = ?", args: [newKey, userId] });
198
+ return newKey;
199
+ }
200
+ async function revokeApiKey(userId) {
201
+ await getDb().execute({ sql: "UPDATE users SET key_active = 0 WHERE id = ?", args: [userId] });
202
+ }
203
+ async function listUsers() {
204
+ const res = await getDb().execute("SELECT id, email, name, created_at, active FROM users ORDER BY created_at DESC");
205
+ return res.rows.map((r) => rowToUser({ ...r, api_key: "", key_active: 1 }));
206
+ }
207
+ async function deactivateUser(id) {
208
+ await getDb().execute({ sql: "UPDATE users SET active = 0 WHERE id = ?", args: [id] });
209
+ }
210
+ function rowToRawJob(row) {
211
+ return {
212
+ id: String(row.id),
213
+ user_id: Number(row.user_id),
214
+ status: String(row.status),
215
+ query: String(row.query),
216
+ options: String(row.options),
217
+ callback_url: row.callback_url != null ? String(row.callback_url) : null,
218
+ result: row.result != null ? String(row.result) : null,
219
+ error: row.error != null ? String(row.error) : null,
220
+ created_at: String(row.created_at),
221
+ started_at: row.started_at != null ? String(row.started_at) : null,
222
+ completed_at: row.completed_at != null ? String(row.completed_at) : null
223
+ };
224
+ }
225
+ function deserialize(raw) {
226
+ return {
227
+ ...raw,
228
+ options: JSON.parse(raw.options),
229
+ result: raw.result ? JSON.parse(raw.result) : null
230
+ };
231
+ }
232
+ async function createJob(userId, query, options, callbackUrl) {
233
+ const id = randomUUID();
234
+ await getDb().execute({
235
+ sql: "INSERT INTO jobs (id, user_id, query, options, callback_url) VALUES (?, ?, ?, ?, ?)",
236
+ args: [id, userId, query, JSON.stringify(options), callbackUrl ?? null]
237
+ });
238
+ return id;
239
+ }
240
+ async function createRunningJob(userId, query, options) {
241
+ const id = randomUUID();
242
+ await getDb().execute({
243
+ sql: `INSERT INTO jobs (id, user_id, query, options, status, started_at) VALUES (?, ?, ?, ?, 'running', datetime('now'))`,
244
+ args: [id, userId, query, JSON.stringify(options)]
245
+ });
246
+ return id;
247
+ }
248
+ async function getJob(id, userId) {
249
+ const db = getDb();
250
+ const res = userId ? await db.execute({ sql: "SELECT * FROM jobs WHERE id = ? AND user_id = ?", args: [id, userId] }) : await db.execute({ sql: "SELECT * FROM jobs WHERE id = ?", args: [id] });
251
+ return res.rows[0] ? deserialize(rowToRawJob(res.rows[0])) : void 0;
252
+ }
253
+ async function listJobs(userId) {
254
+ const res = await getDb().execute({ sql: "SELECT * FROM jobs WHERE user_id = ? ORDER BY created_at DESC LIMIT 50", args: [userId] });
255
+ return res.rows.map((r) => deserialize(rowToRawJob(r)));
256
+ }
257
+ async function countActiveUsers() {
258
+ const res = await getDb().execute(`SELECT COUNT(*) as n FROM users WHERE active = 1`);
259
+ return Number(res.rows[0]?.n ?? 0);
260
+ }
261
+ async function countActiveJobsForUser(userId) {
262
+ const res = await getDb().execute({
263
+ sql: `SELECT COUNT(*) as n FROM jobs WHERE user_id = ? AND (status = 'pending' OR (status = 'running' AND started_at > datetime('now', '-10 minutes')))`,
264
+ args: [userId]
265
+ });
266
+ return Number(res.rows[0]?.n ?? 0);
267
+ }
268
+ async function countJobsLast7Days(userId) {
269
+ const res = await getDb().execute({
270
+ sql: `SELECT COUNT(*) as n FROM jobs WHERE user_id = ? AND created_at > datetime('now', '-7 days')`,
271
+ args: [userId]
272
+ });
273
+ return Number(res.rows[0]?.n ?? 0);
274
+ }
275
+ async function claimPendingJob() {
276
+ const db = getDb();
277
+ const res = await db.execute(`SELECT * FROM jobs WHERE status = 'pending' ORDER BY created_at LIMIT 1`);
278
+ const job = res.rows[0] ? rowToRawJob(res.rows[0]) : void 0;
279
+ if (!job) return void 0;
280
+ const upd = await db.execute({
281
+ sql: `UPDATE jobs SET status = 'running', started_at = datetime('now') WHERE id = ? AND status = 'pending'`,
282
+ args: [job.id]
283
+ });
284
+ return upd.rowsAffected === 0 ? void 0 : { ...job, status: "running" };
285
+ }
286
+ async function completeJob(id, result) {
287
+ await getDb().execute({ sql: `UPDATE jobs SET status = 'done', result = ?, completed_at = datetime('now') WHERE id = ?`, args: [JSON.stringify(result), id] });
288
+ }
289
+ async function failJob(id, error) {
290
+ await getDb().execute({ sql: `UPDATE jobs SET status = 'failed', error = ?, completed_at = datetime('now') WHERE id = ?`, args: [error, id] });
291
+ }
292
+ function rowToKpoJob(row) {
293
+ return {
294
+ id: String(row.id),
295
+ user_id: Number(row.user_id),
296
+ status: String(row.status),
297
+ entity_name: String(row.entity_name),
298
+ entity_type: String(row.entity_type),
299
+ url: row.url != null ? String(row.url) : null,
300
+ request: String(row.request),
301
+ result: row.result != null ? String(row.result) : null,
302
+ error: row.error != null ? String(row.error) : null,
303
+ created_at: String(row.created_at),
304
+ updated_at: String(row.updated_at)
305
+ };
306
+ }
307
+ function rowToKpoPhaseLog(row) {
308
+ return {
309
+ id: Number(row.id),
310
+ job_id: String(row.job_id),
311
+ phase: String(row.phase),
312
+ output: String(row.output),
313
+ created_at: String(row.created_at)
314
+ };
315
+ }
316
+ async function createKpoJob(jobId, userId, entityName, entityType, request, url) {
317
+ await getDb().execute({
318
+ sql: `INSERT INTO kpo_jobs (id, user_id, status, entity_name, entity_type, url, request) VALUES (?, ?, 'pending', ?, ?, ?, ?)`,
319
+ args: [jobId, userId, entityName, entityType, url ?? null, JSON.stringify(request)]
320
+ });
321
+ }
322
+ async function claimPendingKpoJob() {
323
+ const db = getDb();
324
+ const upd = await db.execute(
325
+ `UPDATE kpo_jobs SET status='running', updated_at=datetime('now') WHERE id = (SELECT id FROM kpo_jobs WHERE status='pending' ORDER BY created_at ASC LIMIT 1) RETURNING *`
326
+ );
327
+ if (upd.rowsAffected === 0) return null;
328
+ return upd.rows[0] ? rowToKpoJob(upd.rows[0]) : null;
329
+ }
330
+ async function getKpoJob(jobId) {
331
+ const res = await getDb().execute({ sql: `SELECT * FROM kpo_jobs WHERE id = ?`, args: [jobId] });
332
+ return res.rows[0] ? rowToKpoJob(res.rows[0]) : null;
333
+ }
334
+ async function updateKpoJobState(jobId, status) {
335
+ await getDb().execute({ sql: `UPDATE kpo_jobs SET status = ?, updated_at = datetime('now') WHERE id = ?`, args: [status, jobId] });
336
+ }
337
+ async function completeKpoJob(jobId, result) {
338
+ await getDb().execute({ sql: `UPDATE kpo_jobs SET status = 'done', result = ?, updated_at = datetime('now') WHERE id = ?`, args: [JSON.stringify(result), jobId] });
339
+ }
340
+ async function failKpoJob(jobId, error) {
341
+ await getDb().execute({ sql: `UPDATE kpo_jobs SET status = 'failed', error = ?, updated_at = datetime('now') WHERE id = ?`, args: [error, jobId] });
342
+ }
343
+ async function logKpoPhaseComplete(jobId, phase, output) {
344
+ await getDb().execute({
345
+ sql: `INSERT INTO kpo_phase_log (job_id, phase, output) VALUES (?, ?, ?)`,
346
+ args: [jobId, phase, JSON.stringify(output)]
347
+ });
348
+ }
349
+ async function getKpoPhaseLog(jobId) {
350
+ const res = await getDb().execute({ sql: `SELECT * FROM kpo_phase_log WHERE job_id = ? ORDER BY created_at ASC`, args: [jobId] });
351
+ return res.rows.map((r) => rowToKpoPhaseLog(r));
352
+ }
353
+ async function listKpoJobs(userId) {
354
+ const res = userId != null ? await getDb().execute({ sql: `SELECT * FROM kpo_jobs WHERE user_id = ? ORDER BY created_at DESC LIMIT 50`, args: [userId] }) : await getDb().execute(`SELECT * FROM kpo_jobs ORDER BY created_at DESC LIMIT 50`);
355
+ return res.rows.map((r) => rowToKpoJob(r));
356
+ }
357
+ async function getUserByStripeCustomerId(customerId) {
358
+ const res = await getDb().execute({
359
+ sql: "SELECT * FROM users WHERE stripe_customer_id = ? AND active = 1",
360
+ args: [customerId]
361
+ });
362
+ return res.rows[0] ? rowToUser(res.rows[0]) : void 0;
363
+ }
364
+ async function setStripeCustomerId(userId, customerId) {
365
+ await getDb().execute({
366
+ sql: "UPDATE users SET stripe_customer_id = ? WHERE id = ?",
367
+ args: [customerId, userId]
368
+ });
369
+ }
370
+ async function creditMc(userId, mc, operation, description, stripePaymentIntent) {
371
+ const db = getDb();
372
+ await db.execute({
373
+ sql: "UPDATE users SET balance_mc = balance_mc + ? WHERE id = ?",
374
+ args: [mc, userId]
375
+ });
376
+ await db.execute({
377
+ sql: "INSERT INTO ledger (user_id, amount_mc, operation, description, stripe_pi) VALUES (?, ?, ?, ?, ?)",
378
+ args: [userId, mc, operation, description ?? null, stripePaymentIntent ?? null]
379
+ });
380
+ const res = await db.execute({ sql: "SELECT balance_mc FROM users WHERE id = ?", args: [userId] });
381
+ return Number(res.rows[0]?.balance_mc ?? 0);
382
+ }
383
+ async function debitMc(userId, mc, operation, description) {
384
+ const db = getDb();
385
+ const res = await db.execute({ sql: "SELECT balance_mc FROM users WHERE id = ?", args: [userId] });
386
+ const current = Number(res.rows[0]?.balance_mc ?? 0);
387
+ if (current < mc) return { ok: false, balance_mc: current };
388
+ await db.execute({
389
+ sql: "UPDATE users SET balance_mc = balance_mc - ? WHERE id = ?",
390
+ args: [mc, userId]
391
+ });
392
+ await db.execute({
393
+ sql: "INSERT INTO ledger (user_id, amount_mc, operation, description) VALUES (?, ?, ?, ?)",
394
+ args: [userId, -mc, operation, description ?? null]
395
+ });
396
+ return { ok: true, balance_mc: current - mc };
397
+ }
398
+ async function getLedger(userId, limit = 50) {
399
+ const res = await getDb().execute({
400
+ sql: "SELECT * FROM ledger WHERE user_id = ? ORDER BY created_at DESC LIMIT ?",
401
+ args: [userId, limit]
402
+ });
403
+ return res.rows.map((r) => ({
404
+ id: Number(r.id),
405
+ user_id: Number(r.user_id),
406
+ amount_mc: Number(r.amount_mc),
407
+ operation: String(r.operation),
408
+ description: r.description != null ? String(r.description) : null,
409
+ stripe_pi: r.stripe_pi != null ? String(r.stripe_pi) : null,
410
+ created_at: String(r.created_at)
411
+ }));
412
+ }
413
+ var SiteAuditJobRowSchema = z.object({
414
+ id: z.string(),
415
+ user_id: z.number(),
416
+ status: z.string().default("pending"),
417
+ client_domain: z.string(),
418
+ session_path: z.string(),
419
+ request: z.string(),
420
+ result: z.string().nullable(),
421
+ error: z.string().nullable(),
422
+ created_at: z.string(),
423
+ updated_at: z.string()
424
+ });
425
+ var SiteAuditPhaseLogRowSchema = z.object({
426
+ id: z.number(),
427
+ job_id: z.string(),
428
+ phase: z.string(),
429
+ completed_at: z.string(),
430
+ output_summary: z.string()
431
+ });
432
+
433
+ export {
434
+ getDb,
435
+ migrate,
436
+ generateApiKey,
437
+ hashPassword,
438
+ verifyPassword,
439
+ getUserByEmail,
440
+ getUserByApiKey,
441
+ getUserById,
442
+ setPassword,
443
+ getUserStats,
444
+ createUser,
445
+ rotateApiKey,
446
+ revokeApiKey,
447
+ listUsers,
448
+ deactivateUser,
449
+ createJob,
450
+ createRunningJob,
451
+ getJob,
452
+ listJobs,
453
+ countActiveUsers,
454
+ countActiveJobsForUser,
455
+ countJobsLast7Days,
456
+ claimPendingJob,
457
+ completeJob,
458
+ failJob,
459
+ createKpoJob,
460
+ claimPendingKpoJob,
461
+ getKpoJob,
462
+ updateKpoJobState,
463
+ completeKpoJob,
464
+ failKpoJob,
465
+ logKpoPhaseComplete,
466
+ getKpoPhaseLog,
467
+ listKpoJobs,
468
+ getUserByStripeCustomerId,
469
+ setStripeCustomerId,
470
+ creditMc,
471
+ debitMc,
472
+ getLedger,
473
+ SiteAuditJobRowSchema,
474
+ SiteAuditPhaseLogRowSchema
475
+ };
476
+ //# sourceMappingURL=chunk-LXZDJJXR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/api/db.ts"],"sourcesContent":["import { createClient, type Client } from '@libsql/client/http'\nimport { randomBytes, randomUUID, scryptSync, timingSafeEqual } from 'node:crypto'\nimport { z } from 'zod'\n\nconst DB_URL = process.env.TURSO_DATABASE_URL ?? 'file:./paa-api.db'\nconst DB_TOKEN = process.env.TURSO_AUTH_TOKEN\n\nlet _db: Client | null = null\n\nexport function getDb(): Client {\n if (!_db) _db = createClient({ url: DB_URL, authToken: DB_TOKEN })\n return _db\n}\n\nexport async function migrate(): Promise<void> {\n const db = getDb()\n await db.execute(`\n CREATE TABLE IF NOT EXISTS users (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n email TEXT UNIQUE NOT NULL,\n name TEXT,\n api_key TEXT UNIQUE NOT NULL,\n password_hash TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n active INTEGER NOT NULL DEFAULT 1\n )\n `)\n await db.execute(`\n CREATE TABLE IF NOT EXISTS jobs (\n id TEXT PRIMARY KEY,\n user_id INTEGER NOT NULL REFERENCES users(id),\n status TEXT NOT NULL DEFAULT 'pending',\n query TEXT NOT NULL,\n options TEXT NOT NULL,\n callback_url TEXT,\n result TEXT,\n error TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n started_at TEXT,\n completed_at TEXT\n )\n `)\n await db.execute(`CREATE INDEX IF NOT EXISTS jobs_user_id ON jobs(user_id)`)\n await db.execute(`CREATE INDEX IF NOT EXISTS jobs_status ON jobs(status)`)\n try { await db.execute(`ALTER TABLE users ADD COLUMN password_hash TEXT`) } catch {}\n try { await db.execute(`ALTER TABLE users ADD COLUMN key_active INTEGER NOT NULL DEFAULT 1`) } catch {}\n\n await db.execute(`\n CREATE TABLE IF NOT EXISTS kpo_jobs (\n id TEXT PRIMARY KEY,\n user_id INTEGER NOT NULL REFERENCES users(id),\n status TEXT NOT NULL,\n entity_name TEXT NOT NULL,\n entity_type TEXT NOT NULL,\n url TEXT,\n request TEXT NOT NULL,\n result TEXT,\n error TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n updated_at TEXT NOT NULL DEFAULT (datetime('now'))\n )\n `)\n await db.execute(`\n CREATE TABLE IF NOT EXISTS kpo_phase_log (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n job_id TEXT NOT NULL REFERENCES kpo_jobs(id),\n phase TEXT NOT NULL,\n output TEXT NOT NULL,\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n )\n `)\n await db.execute(`CREATE INDEX IF NOT EXISTS kpo_jobs_user_id ON kpo_jobs(user_id)`)\n await db.execute(`CREATE INDEX IF NOT EXISTS kpo_jobs_status ON kpo_jobs(status)`)\n await db.execute(`CREATE INDEX IF NOT EXISTS kpo_phase_log_job_id ON kpo_phase_log(job_id)`)\n try { await db.execute(`ALTER TABLE users ADD COLUMN stripe_customer_id TEXT`) } catch {}\n try { await db.execute(`ALTER TABLE users ADD COLUMN balance_mc INTEGER NOT NULL DEFAULT 0`) } catch {}\n await db.execute(`\n CREATE TABLE IF NOT EXISTS ledger (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL REFERENCES users(id),\n amount_mc INTEGER NOT NULL,\n operation TEXT NOT NULL,\n description TEXT,\n stripe_pi TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n )\n `)\n await db.execute(`CREATE INDEX IF NOT EXISTS ledger_user_id ON ledger(user_id)`)\n await db.execute(`\n CREATE TABLE IF NOT EXISTS site_audit_jobs (\n id TEXT PRIMARY KEY,\n user_id INTEGER,\n status TEXT DEFAULT 'pending',\n client_domain TEXT,\n session_path TEXT,\n request TEXT,\n result TEXT,\n error TEXT,\n created_at TEXT DEFAULT (datetime('now')),\n updated_at TEXT DEFAULT (datetime('now'))\n )\n `)\n await db.execute(`\n CREATE TABLE IF NOT EXISTS site_audit_phase_log (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n job_id TEXT REFERENCES site_audit_jobs(id),\n phase TEXT,\n completed_at TEXT DEFAULT (datetime('now')),\n output_summary TEXT\n )\n `)\n await db.execute(`CREATE INDEX IF NOT EXISTS site_audit_jobs_user_id ON site_audit_jobs(user_id)`)\n await db.execute(`CREATE INDEX IF NOT EXISTS site_audit_jobs_status ON site_audit_jobs(status)`)\n await db.execute(`CREATE INDEX IF NOT EXISTS site_audit_phase_log_job_id ON site_audit_phase_log(job_id)`)\n}\n\nexport function generateApiKey(): string {\n return 'sk_' + randomBytes(24).toString('hex')\n}\n\nexport interface User {\n id: number\n email: string\n name: string | null\n api_key: string\n key_active: number\n password_hash: string | null\n created_at: string\n active: number\n stripe_customer_id: string | null\n balance_mc: number\n}\n\nexport function hashPassword(password: string): string {\n const salt = randomBytes(16).toString('hex')\n const hash = scryptSync(password, salt, 64)\n return `${salt}:${hash.toString('hex')}`\n}\n\nexport function verifyPassword(password: string, stored: string): boolean {\n const [salt, hash] = stored.split(':')\n const hashBuf = Buffer.from(hash, 'hex')\n const derived = scryptSync(password, salt, 64)\n return timingSafeEqual(hashBuf, derived)\n}\n\nfunction rowToUser(row: Record<string, unknown>): User {\n return {\n id: Number(row.id),\n email: String(row.email),\n name: row.name != null ? String(row.name) : null,\n api_key: String(row.api_key),\n key_active: Number(row.key_active ?? 1),\n password_hash: row.password_hash != null ? String(row.password_hash) : null,\n created_at: String(row.created_at),\n active: Number(row.active),\n stripe_customer_id: row.stripe_customer_id != null ? String(row.stripe_customer_id) : null,\n balance_mc: Number(row.balance_mc ?? 0),\n }\n}\n\nexport async function getUserByEmail(email: string): Promise<User | undefined> {\n const res = await getDb().execute({ sql: 'SELECT * FROM users WHERE email = ? AND active = 1', args: [email] })\n return res.rows[0] ? rowToUser(res.rows[0] as unknown as Record<string, unknown>) : undefined\n}\n\nexport async function getUserByApiKey(api_key: string): Promise<User | undefined> {\n const res = await getDb().execute({ sql: 'SELECT * FROM users WHERE api_key = ? AND active = 1 AND key_active = 1', args: [api_key] })\n return res.rows[0] ? rowToUser(res.rows[0] as unknown as Record<string, unknown>) : undefined\n}\n\nexport async function getUserById(id: number | bigint): Promise<User | undefined> {\n const res = await getDb().execute({ sql: 'SELECT * FROM users WHERE id = ? AND active = 1', args: [id] })\n return res.rows[0] ? rowToUser(res.rows[0] as unknown as Record<string, unknown>) : undefined\n}\n\nexport async function setPassword(id: number | bigint, password: string): Promise<void> {\n await getDb().execute({ sql: 'UPDATE users SET password_hash = ? WHERE id = ?', args: [hashPassword(password), id] })\n}\n\nexport async function getUserStats(userId: number | bigint): Promise<{ total: number; done: number; failed: number; totalQuestions: number; lastUsed: string | null }> {\n const db = getDb()\n const [totR, doneR, failR, tqR, luR] = await Promise.all([\n db.execute({ sql: 'SELECT COUNT(*) as n FROM jobs WHERE user_id = ?', args: [userId] }),\n db.execute({ sql: \"SELECT COUNT(*) as n FROM jobs WHERE user_id = ? AND status = 'done'\", args: [userId] }),\n db.execute({ sql: \"SELECT COUNT(*) as n FROM jobs WHERE user_id = ? AND status = 'failed'\", args: [userId] }),\n db.execute({ sql: \"SELECT COALESCE(SUM(json_extract(result, '$.totalQuestions')), 0) as n FROM jobs WHERE user_id = ? AND status = 'done'\", args: [userId] }),\n db.execute({ sql: 'SELECT MAX(created_at) as t FROM jobs WHERE user_id = ?', args: [userId] }),\n ])\n return {\n total: Number(totR.rows[0]?.n ?? 0),\n done: Number(doneR.rows[0]?.n ?? 0),\n failed: Number(failR.rows[0]?.n ?? 0),\n totalQuestions: Number(tqR.rows[0]?.n ?? 0),\n lastUsed: luR.rows[0]?.t != null ? String(luR.rows[0].t) : null,\n }\n}\n\nexport async function createUser(email: string, name?: string, password?: string): Promise<{ id: number | bigint; email: string; name?: string; api_key: string; password?: string }> {\n const db = getDb()\n const api_key = generateApiKey()\n const plainPassword = password ?? randomBytes(6).toString('hex')\n const password_hash = hashPassword(plainPassword)\n const result = await db.execute({\n sql: 'INSERT INTO users (email, name, api_key, password_hash) VALUES (?, ?, ?, ?)',\n args: [email, name ?? null, api_key, password_hash],\n })\n return { id: result.lastInsertRowid!, email, name, api_key, password: plainPassword }\n}\n\nexport async function rotateApiKey(userId: number | bigint): Promise<string> {\n const newKey = generateApiKey()\n await getDb().execute({ sql: 'UPDATE users SET api_key = ?, key_active = 1 WHERE id = ?', args: [newKey, userId] })\n return newKey\n}\n\nexport async function revokeApiKey(userId: number | bigint): Promise<void> {\n await getDb().execute({ sql: 'UPDATE users SET key_active = 0 WHERE id = ?', args: [userId] })\n}\n\nexport async function listUsers(): Promise<Omit<User, 'api_key'>[]> {\n const res = await getDb().execute('SELECT id, email, name, created_at, active FROM users ORDER BY created_at DESC')\n return res.rows.map(r => rowToUser({ ...r as unknown as Record<string, unknown>, api_key: '', key_active: 1 }))\n}\n\nexport async function deactivateUser(id: number): Promise<void> {\n await getDb().execute({ sql: 'UPDATE users SET active = 0 WHERE id = ?', args: [id] })\n}\n\ninterface RawJob {\n id: string\n user_id: number\n status: string\n query: string\n options: string\n callback_url: string | null\n result: string | null\n error: string | null\n created_at: string\n started_at: string | null\n completed_at: string | null\n}\n\nexport interface Job {\n id: string\n user_id: number\n status: string\n query: string\n options: Record<string, unknown>\n callback_url: string | null\n result: unknown | null\n error: string | null\n created_at: string\n started_at: string | null\n completed_at: string | null\n}\n\nfunction rowToRawJob(row: Record<string, unknown>): RawJob {\n return {\n id: String(row.id),\n user_id: Number(row.user_id),\n status: String(row.status),\n query: String(row.query),\n options: String(row.options),\n callback_url: row.callback_url != null ? String(row.callback_url) : null,\n result: row.result != null ? String(row.result) : null,\n error: row.error != null ? String(row.error) : null,\n created_at: String(row.created_at),\n started_at: row.started_at != null ? String(row.started_at) : null,\n completed_at: row.completed_at != null ? String(row.completed_at) : null,\n }\n}\n\nfunction deserialize(raw: RawJob): Job {\n return {\n ...raw,\n options: JSON.parse(raw.options) as Record<string, unknown>,\n result: raw.result ? JSON.parse(raw.result) : null,\n }\n}\n\nexport async function createJob(userId: number | bigint, query: string, options: object, callbackUrl?: string): Promise<string> {\n const id = randomUUID()\n await getDb().execute({\n sql: 'INSERT INTO jobs (id, user_id, query, options, callback_url) VALUES (?, ?, ?, ?, ?)',\n args: [id, userId, query, JSON.stringify(options), callbackUrl ?? null],\n })\n return id\n}\n\nexport async function createRunningJob(userId: number | bigint, query: string, options: object): Promise<string> {\n const id = randomUUID()\n await getDb().execute({\n sql: `INSERT INTO jobs (id, user_id, query, options, status, started_at) VALUES (?, ?, ?, ?, 'running', datetime('now'))`,\n args: [id, userId, query, JSON.stringify(options)],\n })\n return id\n}\n\nexport async function getJob(id: string, userId?: number | bigint): Promise<Job | undefined> {\n const db = getDb()\n const res = userId\n ? await db.execute({ sql: 'SELECT * FROM jobs WHERE id = ? AND user_id = ?', args: [id, userId] })\n : await db.execute({ sql: 'SELECT * FROM jobs WHERE id = ?', args: [id] })\n return res.rows[0] ? deserialize(rowToRawJob(res.rows[0] as unknown as Record<string, unknown>)) : undefined\n}\n\nexport async function listJobs(userId: number | bigint): Promise<Job[]> {\n const res = await getDb().execute({ sql: 'SELECT * FROM jobs WHERE user_id = ? ORDER BY created_at DESC LIMIT 50', args: [userId] })\n return res.rows.map(r => deserialize(rowToRawJob(r as unknown as Record<string, unknown>)))\n}\n\nexport async function countActiveUsers(): Promise<number> {\n const res = await getDb().execute(`SELECT COUNT(*) as n FROM users WHERE active = 1`)\n return Number(res.rows[0]?.n ?? 0)\n}\n\nexport async function countActiveJobsForUser(userId: number | bigint): Promise<number> {\n const res = await getDb().execute({\n sql: `SELECT COUNT(*) as n FROM jobs WHERE user_id = ? AND (status = 'pending' OR (status = 'running' AND started_at > datetime('now', '-10 minutes')))`,\n args: [userId],\n })\n return Number(res.rows[0]?.n ?? 0)\n}\n\nexport async function countJobsLast7Days(userId: number | bigint): Promise<number> {\n const res = await getDb().execute({\n sql: `SELECT COUNT(*) as n FROM jobs WHERE user_id = ? AND created_at > datetime('now', '-7 days')`,\n args: [userId],\n })\n return Number(res.rows[0]?.n ?? 0)\n}\n\nexport async function claimPendingJob(): Promise<RawJob | undefined> {\n const db = getDb()\n const res = await db.execute(`SELECT * FROM jobs WHERE status = 'pending' ORDER BY created_at LIMIT 1`)\n const job = res.rows[0] ? rowToRawJob(res.rows[0] as unknown as Record<string, unknown>) : undefined\n if (!job) return undefined\n const upd = await db.execute({\n sql: `UPDATE jobs SET status = 'running', started_at = datetime('now') WHERE id = ? AND status = 'pending'`,\n args: [job.id],\n })\n return upd.rowsAffected === 0 ? undefined : { ...job, status: 'running' }\n}\n\nexport async function completeJob(id: string, result: object): Promise<void> {\n await getDb().execute({ sql: `UPDATE jobs SET status = 'done', result = ?, completed_at = datetime('now') WHERE id = ?`, args: [JSON.stringify(result), id] })\n}\n\nexport async function failJob(id: string, error: string): Promise<void> {\n await getDb().execute({ sql: `UPDATE jobs SET status = 'failed', error = ?, completed_at = datetime('now') WHERE id = ?`, args: [error, id] })\n}\n\nexport interface KpoJobRow {\n id: string\n user_id: number\n status: string\n entity_name: string\n entity_type: string\n url: string | null\n request: string\n result: string | null\n error: string | null\n created_at: string\n updated_at: string\n}\n\nexport interface KpoPhaseLogRow {\n id: number\n job_id: string\n phase: string\n output: string\n created_at: string\n}\n\nfunction rowToKpoJob(row: Record<string, unknown>): KpoJobRow {\n return {\n id: String(row.id),\n user_id: Number(row.user_id),\n status: String(row.status),\n entity_name: String(row.entity_name),\n entity_type: String(row.entity_type),\n url: row.url != null ? String(row.url) : null,\n request: String(row.request),\n result: row.result != null ? String(row.result) : null,\n error: row.error != null ? String(row.error) : null,\n created_at: String(row.created_at),\n updated_at: String(row.updated_at),\n }\n}\n\nfunction rowToKpoPhaseLog(row: Record<string, unknown>): KpoPhaseLogRow {\n return {\n id: Number(row.id),\n job_id: String(row.job_id),\n phase: String(row.phase),\n output: String(row.output),\n created_at: String(row.created_at),\n }\n}\n\nexport async function createKpoJob(jobId: string, userId: number, entityName: string, entityType: string, request: object, url?: string): Promise<void> {\n await getDb().execute({\n sql: `INSERT INTO kpo_jobs (id, user_id, status, entity_name, entity_type, url, request) VALUES (?, ?, 'pending', ?, ?, ?, ?)`,\n args: [jobId, userId, entityName, entityType, url ?? null, JSON.stringify(request)],\n })\n}\n\nexport async function claimPendingKpoJob(): Promise<KpoJobRow | null> {\n const db = getDb()\n const upd = await db.execute(\n `UPDATE kpo_jobs SET status='running', updated_at=datetime('now') WHERE id = (SELECT id FROM kpo_jobs WHERE status='pending' ORDER BY created_at ASC LIMIT 1) RETURNING *`\n )\n if (upd.rowsAffected === 0) return null\n return upd.rows[0] ? rowToKpoJob(upd.rows[0] as unknown as Record<string, unknown>) : null\n}\n\nexport async function getKpoJob(jobId: string): Promise<KpoJobRow | null> {\n const res = await getDb().execute({ sql: `SELECT * FROM kpo_jobs WHERE id = ?`, args: [jobId] })\n return res.rows[0] ? rowToKpoJob(res.rows[0] as unknown as Record<string, unknown>) : null\n}\n\nexport async function updateKpoJobState(jobId: string, status: string): Promise<void> {\n await getDb().execute({ sql: `UPDATE kpo_jobs SET status = ?, updated_at = datetime('now') WHERE id = ?`, args: [status, jobId] })\n}\n\nexport async function completeKpoJob(jobId: string, result: object): Promise<void> {\n await getDb().execute({ sql: `UPDATE kpo_jobs SET status = 'done', result = ?, updated_at = datetime('now') WHERE id = ?`, args: [JSON.stringify(result), jobId] })\n}\n\nexport async function failKpoJob(jobId: string, error: string): Promise<void> {\n await getDb().execute({ sql: `UPDATE kpo_jobs SET status = 'failed', error = ?, updated_at = datetime('now') WHERE id = ?`, args: [error, jobId] })\n}\n\nexport async function logKpoPhaseComplete(jobId: string, phase: string, output: unknown): Promise<void> {\n await getDb().execute({\n sql: `INSERT INTO kpo_phase_log (job_id, phase, output) VALUES (?, ?, ?)`,\n args: [jobId, phase, JSON.stringify(output)],\n })\n}\n\nexport async function getKpoPhaseLog(jobId: string): Promise<KpoPhaseLogRow[]> {\n const res = await getDb().execute({ sql: `SELECT * FROM kpo_phase_log WHERE job_id = ? ORDER BY created_at ASC`, args: [jobId] })\n return res.rows.map(r => rowToKpoPhaseLog(r as unknown as Record<string, unknown>))\n}\n\nexport async function listKpoJobs(userId?: number): Promise<KpoJobRow[]> {\n const res = userId != null\n ? await getDb().execute({ sql: `SELECT * FROM kpo_jobs WHERE user_id = ? ORDER BY created_at DESC LIMIT 50`, args: [userId] })\n : await getDb().execute(`SELECT * FROM kpo_jobs ORDER BY created_at DESC LIMIT 50`)\n return res.rows.map(r => rowToKpoJob(r as unknown as Record<string, unknown>))\n}\n\nexport async function getUserByStripeCustomerId(customerId: string): Promise<User | undefined> {\n const res = await getDb().execute({\n sql: 'SELECT * FROM users WHERE stripe_customer_id = ? AND active = 1',\n args: [customerId],\n })\n return res.rows[0] ? rowToUser(res.rows[0] as unknown as Record<string, unknown>) : undefined\n}\n\nexport async function setStripeCustomerId(userId: number, customerId: string): Promise<void> {\n await getDb().execute({\n sql: 'UPDATE users SET stripe_customer_id = ? WHERE id = ?',\n args: [customerId, userId],\n })\n}\n\nexport async function creditMc(\n userId: number,\n mc: number,\n operation: string,\n description?: string,\n stripePaymentIntent?: string,\n): Promise<number> {\n const db = getDb()\n await db.execute({\n sql: 'UPDATE users SET balance_mc = balance_mc + ? WHERE id = ?',\n args: [mc, userId],\n })\n await db.execute({\n sql: 'INSERT INTO ledger (user_id, amount_mc, operation, description, stripe_pi) VALUES (?, ?, ?, ?, ?)',\n args: [userId, mc, operation, description ?? null, stripePaymentIntent ?? null],\n })\n const res = await db.execute({ sql: 'SELECT balance_mc FROM users WHERE id = ?', args: [userId] })\n return Number(res.rows[0]?.balance_mc ?? 0)\n}\n\nexport async function debitMc(\n userId: number,\n mc: number,\n operation: string,\n description?: string,\n): Promise<{ ok: boolean; balance_mc: number }> {\n const db = getDb()\n const res = await db.execute({ sql: 'SELECT balance_mc FROM users WHERE id = ?', args: [userId] })\n const current = Number(res.rows[0]?.balance_mc ?? 0)\n if (current < mc) return { ok: false, balance_mc: current }\n await db.execute({\n sql: 'UPDATE users SET balance_mc = balance_mc - ? WHERE id = ?',\n args: [mc, userId],\n })\n await db.execute({\n sql: 'INSERT INTO ledger (user_id, amount_mc, operation, description) VALUES (?, ?, ?, ?)',\n args: [userId, -mc, operation, description ?? null],\n })\n return { ok: true, balance_mc: current - mc }\n}\n\nexport async function getLedger(userId: number, limit = 50): Promise<LedgerRow[]> {\n const res = await getDb().execute({\n sql: 'SELECT * FROM ledger WHERE user_id = ? ORDER BY created_at DESC LIMIT ?',\n args: [userId, limit],\n })\n return res.rows.map(r => ({\n id: Number(r.id),\n user_id: Number(r.user_id),\n amount_mc: Number(r.amount_mc),\n operation: String(r.operation),\n description: r.description != null ? String(r.description) : null,\n stripe_pi: r.stripe_pi != null ? String(r.stripe_pi) : null,\n created_at: String(r.created_at),\n }))\n}\n\nexport interface LedgerRow {\n id: number\n user_id: number\n amount_mc: number\n operation: string\n description: string | null\n stripe_pi: string | null\n created_at: string\n}\n\nexport const SiteAuditJobRowSchema = z.object({\n id: z.string(),\n user_id: z.number(),\n status: z.string().default('pending'),\n client_domain: z.string(),\n session_path: z.string(),\n request: z.string(),\n result: z.string().nullable(),\n error: z.string().nullable(),\n created_at: z.string(),\n updated_at: z.string(),\n});\nexport type SiteAuditJobRow = z.infer<typeof SiteAuditJobRowSchema>;\n\nexport const SiteAuditPhaseLogRowSchema = z.object({\n id: z.number(),\n job_id: z.string(),\n phase: z.string(),\n completed_at: z.string(),\n output_summary: z.string(),\n});\nexport type SiteAuditPhaseLogRow = z.infer<typeof SiteAuditPhaseLogRowSchema>;\n"],"mappings":"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","names":[]}