@secondlayer/shared 2.1.0 → 3.0.0-alpha.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.
- package/README.md +2 -2
- package/dist/src/db/index.d.ts +39 -137
- package/dist/src/db/index.js.map +2 -2
- package/dist/src/db/jsonb.d.ts +5 -1
- package/dist/src/db/jsonb.js.map +2 -2
- package/dist/src/db/queries/account-spend-caps.d.ts +379 -0
- package/dist/src/db/queries/account-spend-caps.js +60 -0
- package/dist/src/db/queries/account-spend-caps.js.map +10 -0
- package/dist/src/db/queries/account-usage.d.ts +403 -0
- package/dist/src/db/queries/account-usage.js +222 -0
- package/dist/src/db/queries/account-usage.js.map +11 -0
- package/dist/src/db/queries/accounts.d.ts +41 -115
- package/dist/src/db/queries/accounts.js +15 -1
- package/dist/src/db/queries/accounts.js.map +3 -3
- package/dist/src/db/queries/integrity.d.ts +27 -114
- package/dist/src/db/queries/projects.d.ts +27 -114
- package/dist/src/db/queries/provisioning-audit.d.ts +27 -114
- package/dist/src/db/queries/subgraph-gaps.d.ts +27 -114
- package/dist/src/db/queries/subgraphs.d.ts +27 -115
- package/dist/src/db/queries/subgraphs.js +2 -3
- package/dist/src/db/queries/subgraphs.js.map +4 -4
- package/dist/src/db/queries/{workflows.d.ts → tenant-compute-addons.d.ts} +50 -149
- package/dist/src/db/queries/tenant-compute-addons.js +47 -0
- package/dist/src/db/queries/tenant-compute-addons.js.map +10 -0
- package/dist/src/db/queries/tenants.d.ts +40 -117
- package/dist/src/db/queries/tenants.js +9 -6
- package/dist/src/db/queries/tenants.js.map +3 -3
- package/dist/src/db/queries/usage.d.ts +28 -139
- package/dist/src/db/queries/usage.js +5 -64
- package/dist/src/db/queries/usage.js.map +4 -5
- package/dist/src/db/schema.d.ts +34 -136
- package/dist/src/errors.d.ts +8 -7
- package/dist/src/errors.js +11 -12
- package/dist/src/errors.js.map +3 -3
- package/dist/src/index.d.ts +46 -143
- package/dist/src/index.js +11 -12
- package/dist/src/index.js.map +4 -4
- package/dist/src/node/local-client.d.ts +27 -114
- package/dist/src/pricing.d.ts +20 -1
- package/dist/src/pricing.js +58 -1
- package/dist/src/pricing.js.map +3 -3
- package/migrations/0045_drop_marketplace_columns.ts +47 -0
- package/migrations/0046_tenant_activity_signal.ts +47 -0
- package/migrations/0047_usage_daily_tenant_id.ts +73 -0
- package/migrations/0048_tenant_compute_addons.ts +49 -0
- package/migrations/0049_accounts_stripe_customer_id.ts +30 -0
- package/migrations/0050_account_spend_caps.ts +45 -0
- package/migrations/0051_workflow_ai_usage_daily.ts +40 -0
- package/migrations/0052_sentries.ts +61 -0
- package/migrations/0053_workflow_runtime.ts +88 -0
- package/migrations/0054_accounts_plan_hobby.ts +32 -0
- package/migrations/0055_ai_usage_account_scope.ts +108 -0
- package/migrations/0056_drop_workflow_sentry_residuals.ts +23 -0
- package/package.json +26 -14
- package/dist/src/db/queries/workflows.js +0 -260
- package/dist/src/db/queries/workflows.js.map +0 -12
- package/dist/src/lib/plans.d.ts +0 -9
- package/dist/src/lib/plans.js +0 -37
- package/dist/src/lib/plans.js.map +0 -10
- package/dist/src/schemas/workflows.d.ts +0 -70
- package/dist/src/schemas/workflows.js +0 -43
- package/dist/src/schemas/workflows.js.map +0 -10
|
@@ -61,10 +61,6 @@ interface SubgraphsTable {
|
|
|
61
61
|
handler_code: string | null;
|
|
62
62
|
source_code: string | null;
|
|
63
63
|
project_id: string | null;
|
|
64
|
-
is_public: Generated<boolean>;
|
|
65
|
-
tags: Generated<string[]>;
|
|
66
|
-
description: string | null;
|
|
67
|
-
forked_from_id: string | null;
|
|
68
64
|
created_at: Generated<Date>;
|
|
69
65
|
updated_at: Generated<Date>;
|
|
70
66
|
}
|
|
@@ -99,6 +95,7 @@ interface AccountsTable {
|
|
|
99
95
|
bio: string | null;
|
|
100
96
|
avatar_url: string | null;
|
|
101
97
|
slug: string | null;
|
|
98
|
+
stripe_customer_id: string | null;
|
|
102
99
|
created_at: Generated<Date>;
|
|
103
100
|
}
|
|
104
101
|
interface SessionsTable {
|
|
@@ -124,6 +121,7 @@ interface MagicLinksTable {
|
|
|
124
121
|
}
|
|
125
122
|
interface UsageDailyTable {
|
|
126
123
|
account_id: string;
|
|
124
|
+
tenant_id: string | null;
|
|
127
125
|
date: string;
|
|
128
126
|
api_requests: Generated<number>;
|
|
129
127
|
deliveries: Generated<number>;
|
|
@@ -250,83 +248,6 @@ interface ChatMessagesTable {
|
|
|
250
248
|
metadata: unknown | null;
|
|
251
249
|
created_at: Generated<Date>;
|
|
252
250
|
}
|
|
253
|
-
interface WorkflowDefinitionsTable {
|
|
254
|
-
id: Generated<string>;
|
|
255
|
-
name: string;
|
|
256
|
-
version: Generated<string>;
|
|
257
|
-
status: Generated<string>;
|
|
258
|
-
trigger_type: string;
|
|
259
|
-
trigger_config: unknown;
|
|
260
|
-
handler_path: string;
|
|
261
|
-
source_code: string | null;
|
|
262
|
-
retries_config: unknown | null;
|
|
263
|
-
timeout_ms: number | null;
|
|
264
|
-
api_key_id: string;
|
|
265
|
-
project_id: string | null;
|
|
266
|
-
created_at: Generated<Date>;
|
|
267
|
-
updated_at: Generated<Date>;
|
|
268
|
-
}
|
|
269
|
-
interface WorkflowRunsTable {
|
|
270
|
-
id: Generated<string>;
|
|
271
|
-
definition_id: string;
|
|
272
|
-
status: Generated<string>;
|
|
273
|
-
trigger_type: string;
|
|
274
|
-
trigger_data: unknown | null;
|
|
275
|
-
dedup_key: string | null;
|
|
276
|
-
error: string | null;
|
|
277
|
-
started_at: Date | null;
|
|
278
|
-
completed_at: Date | null;
|
|
279
|
-
duration_ms: number | null;
|
|
280
|
-
total_ai_tokens: Generated<number>;
|
|
281
|
-
created_at: Generated<Date>;
|
|
282
|
-
}
|
|
283
|
-
interface WorkflowStepsTable {
|
|
284
|
-
id: Generated<string>;
|
|
285
|
-
run_id: string;
|
|
286
|
-
step_index: number;
|
|
287
|
-
step_id: string;
|
|
288
|
-
step_type: string;
|
|
289
|
-
status: Generated<string>;
|
|
290
|
-
input: unknown | null;
|
|
291
|
-
output: unknown | null;
|
|
292
|
-
error: string | null;
|
|
293
|
-
retry_count: Generated<number>;
|
|
294
|
-
ai_tokens_used: Generated<number>;
|
|
295
|
-
started_at: Date | null;
|
|
296
|
-
completed_at: Date | null;
|
|
297
|
-
duration_ms: number | null;
|
|
298
|
-
memo_key: string | null;
|
|
299
|
-
parent_step_id: string | null;
|
|
300
|
-
created_at: Generated<Date>;
|
|
301
|
-
}
|
|
302
|
-
interface WorkflowQueueTable {
|
|
303
|
-
id: Generated<string>;
|
|
304
|
-
run_id: string;
|
|
305
|
-
status: Generated<string>;
|
|
306
|
-
attempts: Generated<number>;
|
|
307
|
-
max_attempts: Generated<number>;
|
|
308
|
-
scheduled_for: Generated<Date>;
|
|
309
|
-
locked_at: Date | null;
|
|
310
|
-
locked_by: string | null;
|
|
311
|
-
error: string | null;
|
|
312
|
-
created_at: Generated<Date>;
|
|
313
|
-
completed_at: Date | null;
|
|
314
|
-
}
|
|
315
|
-
interface WorkflowSchedulesTable {
|
|
316
|
-
id: Generated<string>;
|
|
317
|
-
definition_id: string;
|
|
318
|
-
cron_expr: string;
|
|
319
|
-
timezone: Generated<string>;
|
|
320
|
-
next_run_at: Date;
|
|
321
|
-
last_run_at: Date | null;
|
|
322
|
-
enabled: Generated<boolean>;
|
|
323
|
-
created_at: Generated<Date>;
|
|
324
|
-
}
|
|
325
|
-
interface WorkflowCursorsTable {
|
|
326
|
-
name: string;
|
|
327
|
-
block_height: Generated<number>;
|
|
328
|
-
updated_at: Generated<Date>;
|
|
329
|
-
}
|
|
330
251
|
interface Database {
|
|
331
252
|
blocks: BlocksTable;
|
|
332
253
|
transactions: TransactionsTable;
|
|
@@ -352,16 +273,10 @@ interface Database {
|
|
|
352
273
|
team_invitations: TeamInvitationsTable;
|
|
353
274
|
chat_sessions: ChatSessionsTable;
|
|
354
275
|
chat_messages: ChatMessagesTable;
|
|
355
|
-
workflow_definitions: WorkflowDefinitionsTable;
|
|
356
|
-
workflow_runs: WorkflowRunsTable;
|
|
357
|
-
workflow_steps: WorkflowStepsTable;
|
|
358
|
-
workflow_queue: WorkflowQueueTable;
|
|
359
|
-
workflow_schedules: WorkflowSchedulesTable;
|
|
360
|
-
workflow_cursors: WorkflowCursorsTable;
|
|
361
|
-
workflow_signer_secrets: WorkflowSignerSecretsTable;
|
|
362
|
-
workflow_budgets: WorkflowBudgetsTable;
|
|
363
276
|
tenants: TenantsTable;
|
|
364
277
|
tenant_usage_monthly: TenantUsageMonthlyTable;
|
|
278
|
+
tenant_compute_addons: TenantComputeAddonsTable;
|
|
279
|
+
account_spend_caps: AccountSpendCapsTable;
|
|
365
280
|
provisioning_audit_log: ProvisioningAuditLogTable;
|
|
366
281
|
}
|
|
367
282
|
type TenantStatus = "provisioning" | "active" | "suspended" | "error" | "deleted";
|
|
@@ -384,9 +299,9 @@ interface TenantsTable {
|
|
|
384
299
|
service_key_enc: Buffer;
|
|
385
300
|
api_url_internal: string;
|
|
386
301
|
api_url_public: string;
|
|
387
|
-
trial_ends_at: Date;
|
|
388
302
|
suspended_at: Date | null;
|
|
389
303
|
last_health_check_at: Date | null;
|
|
304
|
+
last_active_at: Generated<Date>;
|
|
390
305
|
service_gen: Generated<number>;
|
|
391
306
|
anon_gen: Generated<number>;
|
|
392
307
|
project_id: string | null;
|
|
@@ -404,6 +319,28 @@ interface TenantUsageMonthlyTable {
|
|
|
404
319
|
first_at: Generated<Date>;
|
|
405
320
|
last_at: Generated<Date>;
|
|
406
321
|
}
|
|
322
|
+
interface TenantComputeAddonsTable {
|
|
323
|
+
id: Generated<string>;
|
|
324
|
+
tenant_id: string;
|
|
325
|
+
memory_mb_delta: Generated<number>;
|
|
326
|
+
cpu_delta: Generated<number | string>;
|
|
327
|
+
storage_mb_delta: Generated<number>;
|
|
328
|
+
effective_from: Generated<Date>;
|
|
329
|
+
effective_until: Date | null;
|
|
330
|
+
stripe_subscription_item_id: string | null;
|
|
331
|
+
created_at: Generated<Date>;
|
|
332
|
+
}
|
|
333
|
+
interface AccountSpendCapsTable {
|
|
334
|
+
account_id: string;
|
|
335
|
+
monthly_cap_cents: number | null;
|
|
336
|
+
compute_cap_cents: number | null;
|
|
337
|
+
storage_cap_cents: number | null;
|
|
338
|
+
ai_cap_cents: number | null;
|
|
339
|
+
alert_threshold_pct: Generated<number>;
|
|
340
|
+
alert_sent_at: Date | null;
|
|
341
|
+
frozen_at: Date | null;
|
|
342
|
+
updated_at: Generated<Date>;
|
|
343
|
+
}
|
|
407
344
|
type ProvisioningAuditEvent = "provision.start" | "provision.success" | "provision.failure" | "suspend" | "resume" | "resize" | "keys.rotate" | "bastion.key.upload" | "bastion.key.revoke" | "teardown";
|
|
408
345
|
type ProvisioningAuditStatus = "ok" | "error";
|
|
409
346
|
interface ProvisioningAuditLogTable {
|
|
@@ -418,30 +355,6 @@ interface ProvisioningAuditLogTable {
|
|
|
418
355
|
error: string | null;
|
|
419
356
|
created_at: Generated<Date>;
|
|
420
357
|
}
|
|
421
|
-
interface WorkflowBudgetsTable {
|
|
422
|
-
id: Generated<string>;
|
|
423
|
-
workflow_definition_id: string;
|
|
424
|
-
/** Period key: "daily:YYYY-MM-DD" | "weekly:YYYY-Www" | "per-run:<uuid>". */
|
|
425
|
-
period: string;
|
|
426
|
-
ai_usd_used: Generated<string>;
|
|
427
|
-
ai_tokens_used: Generated<string>;
|
|
428
|
-
chain_microstx_used: Generated<string>;
|
|
429
|
-
chain_tx_count: Generated<number>;
|
|
430
|
-
run_count: Generated<number>;
|
|
431
|
-
step_count: Generated<number>;
|
|
432
|
-
reset_at: Date;
|
|
433
|
-
created_at: Generated<Date>;
|
|
434
|
-
updated_at: Generated<Date>;
|
|
435
|
-
}
|
|
436
|
-
interface WorkflowSignerSecretsTable {
|
|
437
|
-
id: Generated<string>;
|
|
438
|
-
account_id: string;
|
|
439
|
-
name: string;
|
|
440
|
-
/** AES-GCM ciphertext bytes produced by the runner's KMS on write. */
|
|
441
|
-
encrypted_value: Buffer;
|
|
442
|
-
created_at: Generated<Date>;
|
|
443
|
-
updated_at: Generated<Date>;
|
|
444
|
-
}
|
|
445
358
|
type Account = Selectable<AccountsTable>;
|
|
446
359
|
declare function upsertAccount(db: Kysely<Database>, email: string): Promise<Account>;
|
|
447
360
|
declare function getAccountById(db: Kysely<Database>, id: string): Promise<Account | null>;
|
|
@@ -450,6 +363,19 @@ declare function updateAccountProfile(db: Kysely<Database>, id: string, data: {
|
|
|
450
363
|
bio?: string
|
|
451
364
|
slug?: string
|
|
452
365
|
}): Promise<Account>;
|
|
366
|
+
/** Persist the Stripe customer id on first upgrade (lazy customer model). */
|
|
367
|
+
declare function setStripeCustomerId(db: Kysely<Database>, accountId: string, stripeCustomerId: string): Promise<void>;
|
|
368
|
+
/**
|
|
369
|
+
* Set the plan tier on an account. Called by the Stripe webhook on
|
|
370
|
+
* subscription lifecycle events + by the billing page's fast-resolve
|
|
371
|
+
* after a successful Checkout redirect. Returns true if a row was
|
|
372
|
+
* updated (account exists).
|
|
373
|
+
*/
|
|
374
|
+
declare function setAccountPlan(db: Kysely<Database>, accountId: string, plan: string): Promise<boolean>;
|
|
375
|
+
/** Resolve an account by its Stripe customer id. Null if no match. */
|
|
376
|
+
declare function getAccountByStripeCustomerId(db: Kysely<Database>, stripeCustomerId: string): Promise<{
|
|
377
|
+
id: string
|
|
378
|
+
} | null>;
|
|
453
379
|
declare function isSlugTaken(db: Kysely<Database>, slug: string, excludeAccountId: string): Promise<boolean>;
|
|
454
380
|
declare function isEmailAllowed(db: Kysely<Database>, email: string): Promise<boolean>;
|
|
455
381
|
declare function createMagicLink(db: Kysely<Database>, email: string, token: string, code: string, expiresInMs?: number): Promise<void>;
|
|
@@ -472,4 +398,4 @@ declare function approveWaitlistEntry(db: Kysely<Database>, email: string): Prom
|
|
|
472
398
|
code: string
|
|
473
399
|
status: "approved" | "already_approved" | "not_found"
|
|
474
400
|
}>;
|
|
475
|
-
export { verifyMagicLinkByCode, verifyMagicLink, upsertAccount, updateAccountProfile, listWaitlist, isSlugTaken, isEmailAllowed, getWaitlistById, getAccountById, createMagicLink, approveWaitlistEntry, WaitlistEntry };
|
|
401
|
+
export { verifyMagicLinkByCode, verifyMagicLink, upsertAccount, updateAccountProfile, setStripeCustomerId, setAccountPlan, listWaitlist, isSlugTaken, isEmailAllowed, getWaitlistById, getAccountByStripeCustomerId, getAccountById, createMagicLink, approveWaitlistEntry, WaitlistEntry };
|
|
@@ -32,6 +32,17 @@ async function updateAccountProfile(db, id, data) {
|
|
|
32
32
|
set.slug = data.slug;
|
|
33
33
|
return db.updateTable("accounts").set(set).where("id", "=", id).returningAll().executeTakeFirstOrThrow();
|
|
34
34
|
}
|
|
35
|
+
async function setStripeCustomerId(db, accountId, stripeCustomerId) {
|
|
36
|
+
await db.updateTable("accounts").set({ stripe_customer_id: stripeCustomerId }).where("id", "=", accountId).execute();
|
|
37
|
+
}
|
|
38
|
+
async function setAccountPlan(db, accountId, plan) {
|
|
39
|
+
const result = await db.updateTable("accounts").set({ plan }).where("id", "=", accountId).executeTakeFirst();
|
|
40
|
+
return (result.numUpdatedRows ?? 0n) > 0n;
|
|
41
|
+
}
|
|
42
|
+
async function getAccountByStripeCustomerId(db, stripeCustomerId) {
|
|
43
|
+
const row = await db.selectFrom("accounts").select("id").where("stripe_customer_id", "=", stripeCustomerId).executeTakeFirst();
|
|
44
|
+
return row ?? null;
|
|
45
|
+
}
|
|
35
46
|
async function isSlugTaken(db, slug, excludeAccountId) {
|
|
36
47
|
const row = await db.selectFrom("accounts").select("id").where("slug", "=", slug).where("id", "!=", excludeAccountId).executeTakeFirst();
|
|
37
48
|
return !!row;
|
|
@@ -94,14 +105,17 @@ export {
|
|
|
94
105
|
verifyMagicLink,
|
|
95
106
|
upsertAccount,
|
|
96
107
|
updateAccountProfile,
|
|
108
|
+
setStripeCustomerId,
|
|
109
|
+
setAccountPlan,
|
|
97
110
|
listWaitlist,
|
|
98
111
|
isSlugTaken,
|
|
99
112
|
isEmailAllowed,
|
|
100
113
|
getWaitlistById,
|
|
114
|
+
getAccountByStripeCustomerId,
|
|
101
115
|
getAccountById,
|
|
102
116
|
createMagicLink,
|
|
103
117
|
approveWaitlistEntry
|
|
104
118
|
};
|
|
105
119
|
|
|
106
|
-
//# debugId=
|
|
120
|
+
//# debugId=E1E89B16EA4EC82664756E2164756E21
|
|
107
121
|
//# sourceMappingURL=accounts.js.map
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/db/queries/accounts.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import { type Kysely, sql } from \"kysely\";\nimport type { Selectable } from \"kysely\";\nimport type { Account, Database, WaitlistTable } from \"../types.ts\";\n\nexport async function upsertAccount(\n\tdb: Kysely<Database>,\n\temail: string,\n): Promise<Account> {\n\treturn await db\n\t\t.insertInto(\"accounts\")\n\t\t.values({ email })\n\t\t.onConflict(\n\t\t\t(oc) => oc.column(\"email\").doUpdateSet({ email }), // no-op update to return existing\n\t\t)\n\t\t.returningAll()\n\t\t.executeTakeFirstOrThrow();\n}\n\nexport async function getAccountById(\n\tdb: Kysely<Database>,\n\tid: string,\n): Promise<Account | null> {\n\treturn (\n\t\t(await db\n\t\t\t.selectFrom(\"accounts\")\n\t\t\t.selectAll()\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.executeTakeFirst()) ?? null\n\t);\n}\n\nexport async function updateAccountProfile(\n\tdb: Kysely<Database>,\n\tid: string,\n\tdata: {\n\t\tdisplay_name?: string;\n\t\tbio?: string;\n\t\tslug?: string;\n\t},\n): Promise<Account> {\n\tconst set: Record<string, unknown> = {};\n\tif (data.display_name !== undefined) set.display_name = data.display_name;\n\tif (data.bio !== undefined) set.bio = data.bio;\n\tif (data.slug !== undefined) set.slug = data.slug;\n\n\treturn db\n\t\t.updateTable(\"accounts\")\n\t\t.set(set)\n\t\t.where(\"id\", \"=\", id)\n\t\t.returningAll()\n\t\t.executeTakeFirstOrThrow();\n}\n\nexport async function isSlugTaken(\n\tdb: Kysely<Database>,\n\tslug: string,\n\texcludeAccountId: string,\n): Promise<boolean> {\n\tconst row = await db\n\t\t.selectFrom(\"accounts\")\n\t\t.select(\"id\")\n\t\t.where(\"slug\", \"=\", slug)\n\t\t.where(\"id\", \"!=\", excludeAccountId)\n\t\t.executeTakeFirst();\n\treturn !!row;\n}\n\nexport async function isEmailAllowed(\n\tdb: Kysely<Database>,\n\temail: string,\n): Promise<boolean> {\n\tconst result = await sql<{ found: number }>`\n SELECT 1 AS found FROM accounts WHERE email = ${email}\n UNION ALL\n SELECT 1 AS found FROM waitlist WHERE email = ${email} AND status = 'approved'\n LIMIT 1\n `.execute(db);\n\n\treturn result.rows.length > 0;\n}\n\nexport async function createMagicLink(\n\tdb: Kysely<Database>,\n\temail: string,\n\ttoken: string,\n\tcode: string,\n\texpiresInMs: number = 15 * 60 * 1000,\n): Promise<void> {\n\tawait db\n\t\t.insertInto(\"magic_links\")\n\t\t.values({\n\t\t\temail,\n\t\t\ttoken,\n\t\t\tcode,\n\t\t\texpires_at: new Date(Date.now() + expiresInMs),\n\t\t})\n\t\t.execute();\n}\n\n/**\n * Verify a magic link token. Returns the email if valid, null otherwise.\n * Marks the token as used atomically. Rejects after 3 failed attempts.\n */\nexport async function verifyMagicLink(\n\tdb: Kysely<Database>,\n\ttoken: string,\n): Promise<string | null> {\n\tconst result = await db\n\t\t.updateTable(\"magic_links\")\n\t\t.set({ used_at: new Date() })\n\t\t.where(\"token\", \"=\", token)\n\t\t.where(\"used_at\", \"is\", null)\n\t\t.where(\"expires_at\", \">\", new Date())\n\t\t.where(\"failed_attempts\", \"<\", 3)\n\t\t.returning(\"email\")\n\t\t.executeTakeFirst();\n\n\tif (result?.email) return result.email;\n\n\t// Increment failed attempts if token exists but didn't verify\n\tawait db\n\t\t.updateTable(\"magic_links\")\n\t\t.set({ failed_attempts: sql`failed_attempts + 1` })\n\t\t.where(\"token\", \"=\", token)\n\t\t.where(\"used_at\", \"is\", null)\n\t\t.where(\"expires_at\", \">\", new Date())\n\t\t.execute();\n\n\treturn null;\n}\n\n/**\n * Verify by 6-digit code + email. Same atomic pattern as verifyMagicLink.\n * Rejects after 3 failed attempts. Increments failed_attempts on all\n * active codes for this email on failure (prevents parallel brute-force).\n */\nexport async function verifyMagicLinkByCode(\n\tdb: Kysely<Database>,\n\temail: string,\n\tcode: string,\n): Promise<string | null> {\n\tconst result = await db\n\t\t.updateTable(\"magic_links\")\n\t\t.set({ used_at: new Date() })\n\t\t.where(\"email\", \"=\", email)\n\t\t.where(\"code\", \"=\", code)\n\t\t.where(\"used_at\", \"is\", null)\n\t\t.where(\"expires_at\", \">\", new Date())\n\t\t.where(\"failed_attempts\", \"<\", 3)\n\t\t.returning(\"email\")\n\t\t.executeTakeFirst();\n\n\tif (result?.email) return result.email;\n\n\t// Increment failed attempts on all active codes for this email\n\tawait db\n\t\t.updateTable(\"magic_links\")\n\t\t.set({ failed_attempts: sql`failed_attempts + 1` })\n\t\t.where(\"email\", \"=\", email)\n\t\t.where(\"used_at\", \"is\", null)\n\t\t.where(\"expires_at\", \">\", new Date())\n\t\t.execute();\n\n\treturn null;\n}\n\n// ── Waitlist ──\n\nexport type WaitlistEntry = Selectable<WaitlistTable>;\n\nexport async function listWaitlist(\n\tdb: Kysely<Database>,\n\tstatus?: string,\n): Promise<WaitlistEntry[]> {\n\tlet query = db\n\t\t.selectFrom(\"waitlist\")\n\t\t.selectAll()\n\t\t.orderBy(\"created_at\", \"desc\");\n\tif (status) {\n\t\tquery = query.where(\"status\", \"=\", status);\n\t}\n\treturn query.execute();\n}\n\nexport async function getWaitlistById(\n\tdb: Kysely<Database>,\n\tid: string,\n): Promise<WaitlistEntry | null> {\n\treturn (\n\t\t(await db\n\t\t\t.selectFrom(\"waitlist\")\n\t\t\t.selectAll()\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.executeTakeFirst()) ?? null\n\t);\n}\n\nexport async function approveWaitlistEntry(\n\tdb: Kysely<Database>,\n\temail: string,\n): Promise<{\n\ttoken: string;\n\tcode: string;\n\tstatus: \"approved\" | \"already_approved\" | \"not_found\";\n}> {\n\tconst row = await db\n\t\t.selectFrom(\"waitlist\")\n\t\t.select(\"status\")\n\t\t.where(\"email\", \"=\", email)\n\t\t.executeTakeFirst();\n\n\tif (!row) return { token: \"\", code: \"\", status: \"not_found\" };\n\tif (row.status !== \"pending\")\n\t\treturn { token: \"\", code: \"\", status: \"already_approved\" };\n\n\tawait db\n\t\t.updateTable(\"waitlist\")\n\t\t.set({ status: \"approved\" })\n\t\t.where(\"email\", \"=\", email)\n\t\t.execute();\n\n\tconst token = Math.floor(100000 + Math.random() * 900000).toString();\n\tconst code = String(Math.floor(Math.random() * 1_000_000)).padStart(6, \"0\");\n\tawait createMagicLink(db, email, token, code, 7 * 24 * 60 * 60 * 1000);\n\n\treturn { token, code, status: \"approved\" };\n}\n"
|
|
5
|
+
"import { type Kysely, sql } from \"kysely\";\nimport type { Selectable } from \"kysely\";\nimport type { Account, Database, WaitlistTable } from \"../types.ts\";\n\nexport async function upsertAccount(\n\tdb: Kysely<Database>,\n\temail: string,\n): Promise<Account> {\n\treturn await db\n\t\t.insertInto(\"accounts\")\n\t\t.values({ email })\n\t\t.onConflict(\n\t\t\t(oc) => oc.column(\"email\").doUpdateSet({ email }), // no-op update to return existing\n\t\t)\n\t\t.returningAll()\n\t\t.executeTakeFirstOrThrow();\n}\n\nexport async function getAccountById(\n\tdb: Kysely<Database>,\n\tid: string,\n): Promise<Account | null> {\n\treturn (\n\t\t(await db\n\t\t\t.selectFrom(\"accounts\")\n\t\t\t.selectAll()\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.executeTakeFirst()) ?? null\n\t);\n}\n\nexport async function updateAccountProfile(\n\tdb: Kysely<Database>,\n\tid: string,\n\tdata: {\n\t\tdisplay_name?: string;\n\t\tbio?: string;\n\t\tslug?: string;\n\t},\n): Promise<Account> {\n\tconst set: Record<string, unknown> = {};\n\tif (data.display_name !== undefined) set.display_name = data.display_name;\n\tif (data.bio !== undefined) set.bio = data.bio;\n\tif (data.slug !== undefined) set.slug = data.slug;\n\n\treturn db\n\t\t.updateTable(\"accounts\")\n\t\t.set(set)\n\t\t.where(\"id\", \"=\", id)\n\t\t.returningAll()\n\t\t.executeTakeFirstOrThrow();\n}\n\n/** Persist the Stripe customer id on first upgrade (lazy customer model). */\nexport async function setStripeCustomerId(\n\tdb: Kysely<Database>,\n\taccountId: string,\n\tstripeCustomerId: string,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"accounts\")\n\t\t.set({ stripe_customer_id: stripeCustomerId })\n\t\t.where(\"id\", \"=\", accountId)\n\t\t.execute();\n}\n\n/**\n * Set the plan tier on an account. Called by the Stripe webhook on\n * subscription lifecycle events + by the billing page's fast-resolve\n * after a successful Checkout redirect. Returns true if a row was\n * updated (account exists).\n */\nexport async function setAccountPlan(\n\tdb: Kysely<Database>,\n\taccountId: string,\n\tplan: string,\n): Promise<boolean> {\n\tconst result = await db\n\t\t.updateTable(\"accounts\")\n\t\t.set({ plan })\n\t\t.where(\"id\", \"=\", accountId)\n\t\t.executeTakeFirst();\n\treturn (result.numUpdatedRows ?? 0n) > 0n;\n}\n\n/** Resolve an account by its Stripe customer id. Null if no match. */\nexport async function getAccountByStripeCustomerId(\n\tdb: Kysely<Database>,\n\tstripeCustomerId: string,\n): Promise<{ id: string } | null> {\n\tconst row = await db\n\t\t.selectFrom(\"accounts\")\n\t\t.select(\"id\")\n\t\t.where(\"stripe_customer_id\", \"=\", stripeCustomerId)\n\t\t.executeTakeFirst();\n\treturn row ?? null;\n}\n\nexport async function isSlugTaken(\n\tdb: Kysely<Database>,\n\tslug: string,\n\texcludeAccountId: string,\n): Promise<boolean> {\n\tconst row = await db\n\t\t.selectFrom(\"accounts\")\n\t\t.select(\"id\")\n\t\t.where(\"slug\", \"=\", slug)\n\t\t.where(\"id\", \"!=\", excludeAccountId)\n\t\t.executeTakeFirst();\n\treturn !!row;\n}\n\nexport async function isEmailAllowed(\n\tdb: Kysely<Database>,\n\temail: string,\n): Promise<boolean> {\n\tconst result = await sql<{ found: number }>`\n SELECT 1 AS found FROM accounts WHERE email = ${email}\n UNION ALL\n SELECT 1 AS found FROM waitlist WHERE email = ${email} AND status = 'approved'\n LIMIT 1\n `.execute(db);\n\n\treturn result.rows.length > 0;\n}\n\nexport async function createMagicLink(\n\tdb: Kysely<Database>,\n\temail: string,\n\ttoken: string,\n\tcode: string,\n\texpiresInMs: number = 15 * 60 * 1000,\n): Promise<void> {\n\tawait db\n\t\t.insertInto(\"magic_links\")\n\t\t.values({\n\t\t\temail,\n\t\t\ttoken,\n\t\t\tcode,\n\t\t\texpires_at: new Date(Date.now() + expiresInMs),\n\t\t})\n\t\t.execute();\n}\n\n/**\n * Verify a magic link token. Returns the email if valid, null otherwise.\n * Marks the token as used atomically. Rejects after 3 failed attempts.\n */\nexport async function verifyMagicLink(\n\tdb: Kysely<Database>,\n\ttoken: string,\n): Promise<string | null> {\n\tconst result = await db\n\t\t.updateTable(\"magic_links\")\n\t\t.set({ used_at: new Date() })\n\t\t.where(\"token\", \"=\", token)\n\t\t.where(\"used_at\", \"is\", null)\n\t\t.where(\"expires_at\", \">\", new Date())\n\t\t.where(\"failed_attempts\", \"<\", 3)\n\t\t.returning(\"email\")\n\t\t.executeTakeFirst();\n\n\tif (result?.email) return result.email;\n\n\t// Increment failed attempts if token exists but didn't verify\n\tawait db\n\t\t.updateTable(\"magic_links\")\n\t\t.set({ failed_attempts: sql`failed_attempts + 1` })\n\t\t.where(\"token\", \"=\", token)\n\t\t.where(\"used_at\", \"is\", null)\n\t\t.where(\"expires_at\", \">\", new Date())\n\t\t.execute();\n\n\treturn null;\n}\n\n/**\n * Verify by 6-digit code + email. Same atomic pattern as verifyMagicLink.\n * Rejects after 3 failed attempts. Increments failed_attempts on all\n * active codes for this email on failure (prevents parallel brute-force).\n */\nexport async function verifyMagicLinkByCode(\n\tdb: Kysely<Database>,\n\temail: string,\n\tcode: string,\n): Promise<string | null> {\n\tconst result = await db\n\t\t.updateTable(\"magic_links\")\n\t\t.set({ used_at: new Date() })\n\t\t.where(\"email\", \"=\", email)\n\t\t.where(\"code\", \"=\", code)\n\t\t.where(\"used_at\", \"is\", null)\n\t\t.where(\"expires_at\", \">\", new Date())\n\t\t.where(\"failed_attempts\", \"<\", 3)\n\t\t.returning(\"email\")\n\t\t.executeTakeFirst();\n\n\tif (result?.email) return result.email;\n\n\t// Increment failed attempts on all active codes for this email\n\tawait db\n\t\t.updateTable(\"magic_links\")\n\t\t.set({ failed_attempts: sql`failed_attempts + 1` })\n\t\t.where(\"email\", \"=\", email)\n\t\t.where(\"used_at\", \"is\", null)\n\t\t.where(\"expires_at\", \">\", new Date())\n\t\t.execute();\n\n\treturn null;\n}\n\n// ── Waitlist ──\n\nexport type WaitlistEntry = Selectable<WaitlistTable>;\n\nexport async function listWaitlist(\n\tdb: Kysely<Database>,\n\tstatus?: string,\n): Promise<WaitlistEntry[]> {\n\tlet query = db\n\t\t.selectFrom(\"waitlist\")\n\t\t.selectAll()\n\t\t.orderBy(\"created_at\", \"desc\");\n\tif (status) {\n\t\tquery = query.where(\"status\", \"=\", status);\n\t}\n\treturn query.execute();\n}\n\nexport async function getWaitlistById(\n\tdb: Kysely<Database>,\n\tid: string,\n): Promise<WaitlistEntry | null> {\n\treturn (\n\t\t(await db\n\t\t\t.selectFrom(\"waitlist\")\n\t\t\t.selectAll()\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.executeTakeFirst()) ?? null\n\t);\n}\n\nexport async function approveWaitlistEntry(\n\tdb: Kysely<Database>,\n\temail: string,\n): Promise<{\n\ttoken: string;\n\tcode: string;\n\tstatus: \"approved\" | \"already_approved\" | \"not_found\";\n}> {\n\tconst row = await db\n\t\t.selectFrom(\"waitlist\")\n\t\t.select(\"status\")\n\t\t.where(\"email\", \"=\", email)\n\t\t.executeTakeFirst();\n\n\tif (!row) return { token: \"\", code: \"\", status: \"not_found\" };\n\tif (row.status !== \"pending\")\n\t\treturn { token: \"\", code: \"\", status: \"already_approved\" };\n\n\tawait db\n\t\t.updateTable(\"waitlist\")\n\t\t.set({ status: \"approved\" })\n\t\t.where(\"email\", \"=\", email)\n\t\t.execute();\n\n\tconst token = Math.floor(100000 + Math.random() * 900000).toString();\n\tconst code = String(Math.floor(Math.random() * 1_000_000)).padStart(6, \"0\");\n\tawait createMagicLink(db, email, token, code, 7 * 24 * 60 * 60 * 1000);\n\n\treturn { token, code, status: \"approved\" };\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";;;;;;;;;;;;;;;;;AAAA;AAIA,eAAsB,aAAa,CAClC,IACA,OACmB;AAAA,EACnB,OAAO,MAAM,GACX,WAAW,UAAU,EACrB,OAAO,EAAE,MAAM,CAAC,EAChB,WACA,CAAC,OAAO,GAAG,OAAO,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,CACjD,EACC,aAAa,EACb,wBAAwB;AAAA;AAG3B,eAAsB,cAAc,CACnC,IACA,IAC0B;AAAA,EAC1B,OACE,MAAM,GACL,WAAW,UAAU,EACrB,UAAU,EACV,MAAM,MAAM,KAAK,EAAE,EACnB,iBAAiB,KAAM;AAAA;AAI3B,eAAsB,oBAAoB,CACzC,IACA,IACA,MAKmB;AAAA,EACnB,MAAM,MAA+B,CAAC;AAAA,EACtC,IAAI,KAAK,iBAAiB;AAAA,IAAW,IAAI,eAAe,KAAK;AAAA,EAC7D,IAAI,KAAK,QAAQ;AAAA,IAAW,IAAI,MAAM,KAAK;AAAA,EAC3C,IAAI,KAAK,SAAS;AAAA,IAAW,IAAI,OAAO,KAAK;AAAA,EAE7C,OAAO,GACL,YAAY,UAAU,EACtB,IAAI,GAAG,EACP,MAAM,MAAM,KAAK,EAAE,EACnB,aAAa,EACb,wBAAwB;AAAA;
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": ";;;;;;;;;;;;;;;;;AAAA;AAIA,eAAsB,aAAa,CAClC,IACA,OACmB;AAAA,EACnB,OAAO,MAAM,GACX,WAAW,UAAU,EACrB,OAAO,EAAE,MAAM,CAAC,EAChB,WACA,CAAC,OAAO,GAAG,OAAO,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,CACjD,EACC,aAAa,EACb,wBAAwB;AAAA;AAG3B,eAAsB,cAAc,CACnC,IACA,IAC0B;AAAA,EAC1B,OACE,MAAM,GACL,WAAW,UAAU,EACrB,UAAU,EACV,MAAM,MAAM,KAAK,EAAE,EACnB,iBAAiB,KAAM;AAAA;AAI3B,eAAsB,oBAAoB,CACzC,IACA,IACA,MAKmB;AAAA,EACnB,MAAM,MAA+B,CAAC;AAAA,EACtC,IAAI,KAAK,iBAAiB;AAAA,IAAW,IAAI,eAAe,KAAK;AAAA,EAC7D,IAAI,KAAK,QAAQ;AAAA,IAAW,IAAI,MAAM,KAAK;AAAA,EAC3C,IAAI,KAAK,SAAS;AAAA,IAAW,IAAI,OAAO,KAAK;AAAA,EAE7C,OAAO,GACL,YAAY,UAAU,EACtB,IAAI,GAAG,EACP,MAAM,MAAM,KAAK,EAAE,EACnB,aAAa,EACb,wBAAwB;AAAA;AAI3B,eAAsB,mBAAmB,CACxC,IACA,WACA,kBACgB;AAAA,EAChB,MAAM,GACJ,YAAY,UAAU,EACtB,IAAI,EAAE,oBAAoB,iBAAiB,CAAC,EAC5C,MAAM,MAAM,KAAK,SAAS,EAC1B,QAAQ;AAAA;AASX,eAAsB,cAAc,CACnC,IACA,WACA,MACmB;AAAA,EACnB,MAAM,SAAS,MAAM,GACnB,YAAY,UAAU,EACtB,IAAI,EAAE,KAAK,CAAC,EACZ,MAAM,MAAM,KAAK,SAAS,EAC1B,iBAAiB;AAAA,EACnB,QAAQ,OAAO,kBAAkB,MAAM;AAAA;AAIxC,eAAsB,4BAA4B,CACjD,IACA,kBACiC;AAAA,EACjC,MAAM,MAAM,MAAM,GAChB,WAAW,UAAU,EACrB,OAAO,IAAI,EACX,MAAM,sBAAsB,KAAK,gBAAgB,EACjD,iBAAiB;AAAA,EACnB,OAAO,OAAO;AAAA;AAGf,eAAsB,WAAW,CAChC,IACA,MACA,kBACmB;AAAA,EACnB,MAAM,MAAM,MAAM,GAChB,WAAW,UAAU,EACrB,OAAO,IAAI,EACX,MAAM,QAAQ,KAAK,IAAI,EACvB,MAAM,MAAM,MAAM,gBAAgB,EAClC,iBAAiB;AAAA,EACnB,OAAO,CAAC,CAAC;AAAA;AAGV,eAAsB,cAAc,CACnC,IACA,OACmB;AAAA,EACnB,MAAM,SAAS,MAAM;AAAA,oDAC8B;AAAA;AAAA,oDAEA;AAAA;AAAA,IAEhD,QAAQ,EAAE;AAAA,EAEb,OAAO,OAAO,KAAK,SAAS;AAAA;AAG7B,eAAsB,eAAe,CACpC,IACA,OACA,OACA,MACA,cAAsB,KAAK,KAAK,MAChB;AAAA,EAChB,MAAM,GACJ,WAAW,aAAa,EACxB,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY,IAAI,KAAK,KAAK,IAAI,IAAI,WAAW;AAAA,EAC9C,CAAC,EACA,QAAQ;AAAA;AAOX,eAAsB,eAAe,CACpC,IACA,OACyB;AAAA,EACzB,MAAM,SAAS,MAAM,GACnB,YAAY,aAAa,EACzB,IAAI,EAAE,SAAS,IAAI,KAAO,CAAC,EAC3B,MAAM,SAAS,KAAK,KAAK,EACzB,MAAM,WAAW,MAAM,IAAI,EAC3B,MAAM,cAAc,KAAK,IAAI,IAAM,EACnC,MAAM,mBAAmB,KAAK,CAAC,EAC/B,UAAU,OAAO,EACjB,iBAAiB;AAAA,EAEnB,IAAI,QAAQ;AAAA,IAAO,OAAO,OAAO;AAAA,EAGjC,MAAM,GACJ,YAAY,aAAa,EACzB,IAAI,EAAE,iBAAiB,yBAAyB,CAAC,EACjD,MAAM,SAAS,KAAK,KAAK,EACzB,MAAM,WAAW,MAAM,IAAI,EAC3B,MAAM,cAAc,KAAK,IAAI,IAAM,EACnC,QAAQ;AAAA,EAEV,OAAO;AAAA;AAQR,eAAsB,qBAAqB,CAC1C,IACA,OACA,MACyB;AAAA,EACzB,MAAM,SAAS,MAAM,GACnB,YAAY,aAAa,EACzB,IAAI,EAAE,SAAS,IAAI,KAAO,CAAC,EAC3B,MAAM,SAAS,KAAK,KAAK,EACzB,MAAM,QAAQ,KAAK,IAAI,EACvB,MAAM,WAAW,MAAM,IAAI,EAC3B,MAAM,cAAc,KAAK,IAAI,IAAM,EACnC,MAAM,mBAAmB,KAAK,CAAC,EAC/B,UAAU,OAAO,EACjB,iBAAiB;AAAA,EAEnB,IAAI,QAAQ;AAAA,IAAO,OAAO,OAAO;AAAA,EAGjC,MAAM,GACJ,YAAY,aAAa,EACzB,IAAI,EAAE,iBAAiB,yBAAyB,CAAC,EACjD,MAAM,SAAS,KAAK,KAAK,EACzB,MAAM,WAAW,MAAM,IAAI,EAC3B,MAAM,cAAc,KAAK,IAAI,IAAM,EACnC,QAAQ;AAAA,EAEV,OAAO;AAAA;AAOR,eAAsB,YAAY,CACjC,IACA,QAC2B;AAAA,EAC3B,IAAI,QAAQ,GACV,WAAW,UAAU,EACrB,UAAU,EACV,QAAQ,cAAc,MAAM;AAAA,EAC9B,IAAI,QAAQ;AAAA,IACX,QAAQ,MAAM,MAAM,UAAU,KAAK,MAAM;AAAA,EAC1C;AAAA,EACA,OAAO,MAAM,QAAQ;AAAA;AAGtB,eAAsB,eAAe,CACpC,IACA,IACgC;AAAA,EAChC,OACE,MAAM,GACL,WAAW,UAAU,EACrB,UAAU,EACV,MAAM,MAAM,KAAK,EAAE,EACnB,iBAAiB,KAAM;AAAA;AAI3B,eAAsB,oBAAoB,CACzC,IACA,OAKE;AAAA,EACF,MAAM,MAAM,MAAM,GAChB,WAAW,UAAU,EACrB,OAAO,QAAQ,EACf,MAAM,SAAS,KAAK,KAAK,EACzB,iBAAiB;AAAA,EAEnB,IAAI,CAAC;AAAA,IAAK,OAAO,EAAE,OAAO,IAAI,MAAM,IAAI,QAAQ,YAAY;AAAA,EAC5D,IAAI,IAAI,WAAW;AAAA,IAClB,OAAO,EAAE,OAAO,IAAI,MAAM,IAAI,QAAQ,mBAAmB;AAAA,EAE1D,MAAM,GACJ,YAAY,UAAU,EACtB,IAAI,EAAE,QAAQ,WAAW,CAAC,EAC1B,MAAM,SAAS,KAAK,KAAK,EACzB,QAAQ;AAAA,EAEV,MAAM,QAAQ,KAAK,MAAM,MAAS,KAAK,OAAO,IAAI,MAAM,EAAE,SAAS;AAAA,EACnE,MAAM,OAAO,OAAO,KAAK,MAAM,KAAK,OAAO,IAAI,GAAS,CAAC,EAAE,SAAS,GAAG,GAAG;AAAA,EAC1E,MAAM,gBAAgB,IAAI,OAAO,OAAO,MAAM,IAAI,KAAK,KAAK,KAAK,IAAI;AAAA,EAErE,OAAO,EAAE,OAAO,MAAM,QAAQ,WAAW;AAAA;",
|
|
8
|
+
"debugId": "E1E89B16EA4EC82664756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -60,10 +60,6 @@ interface SubgraphsTable {
|
|
|
60
60
|
handler_code: string | null;
|
|
61
61
|
source_code: string | null;
|
|
62
62
|
project_id: string | null;
|
|
63
|
-
is_public: Generated<boolean>;
|
|
64
|
-
tags: Generated<string[]>;
|
|
65
|
-
description: string | null;
|
|
66
|
-
forked_from_id: string | null;
|
|
67
63
|
created_at: Generated<Date>;
|
|
68
64
|
updated_at: Generated<Date>;
|
|
69
65
|
}
|
|
@@ -98,6 +94,7 @@ interface AccountsTable {
|
|
|
98
94
|
bio: string | null;
|
|
99
95
|
avatar_url: string | null;
|
|
100
96
|
slug: string | null;
|
|
97
|
+
stripe_customer_id: string | null;
|
|
101
98
|
created_at: Generated<Date>;
|
|
102
99
|
}
|
|
103
100
|
interface SessionsTable {
|
|
@@ -123,6 +120,7 @@ interface MagicLinksTable {
|
|
|
123
120
|
}
|
|
124
121
|
interface UsageDailyTable {
|
|
125
122
|
account_id: string;
|
|
123
|
+
tenant_id: string | null;
|
|
126
124
|
date: string;
|
|
127
125
|
api_requests: Generated<number>;
|
|
128
126
|
deliveries: Generated<number>;
|
|
@@ -249,83 +247,6 @@ interface ChatMessagesTable {
|
|
|
249
247
|
metadata: unknown | null;
|
|
250
248
|
created_at: Generated<Date>;
|
|
251
249
|
}
|
|
252
|
-
interface WorkflowDefinitionsTable {
|
|
253
|
-
id: Generated<string>;
|
|
254
|
-
name: string;
|
|
255
|
-
version: Generated<string>;
|
|
256
|
-
status: Generated<string>;
|
|
257
|
-
trigger_type: string;
|
|
258
|
-
trigger_config: unknown;
|
|
259
|
-
handler_path: string;
|
|
260
|
-
source_code: string | null;
|
|
261
|
-
retries_config: unknown | null;
|
|
262
|
-
timeout_ms: number | null;
|
|
263
|
-
api_key_id: string;
|
|
264
|
-
project_id: string | null;
|
|
265
|
-
created_at: Generated<Date>;
|
|
266
|
-
updated_at: Generated<Date>;
|
|
267
|
-
}
|
|
268
|
-
interface WorkflowRunsTable {
|
|
269
|
-
id: Generated<string>;
|
|
270
|
-
definition_id: string;
|
|
271
|
-
status: Generated<string>;
|
|
272
|
-
trigger_type: string;
|
|
273
|
-
trigger_data: unknown | null;
|
|
274
|
-
dedup_key: string | null;
|
|
275
|
-
error: string | null;
|
|
276
|
-
started_at: Date | null;
|
|
277
|
-
completed_at: Date | null;
|
|
278
|
-
duration_ms: number | null;
|
|
279
|
-
total_ai_tokens: Generated<number>;
|
|
280
|
-
created_at: Generated<Date>;
|
|
281
|
-
}
|
|
282
|
-
interface WorkflowStepsTable {
|
|
283
|
-
id: Generated<string>;
|
|
284
|
-
run_id: string;
|
|
285
|
-
step_index: number;
|
|
286
|
-
step_id: string;
|
|
287
|
-
step_type: string;
|
|
288
|
-
status: Generated<string>;
|
|
289
|
-
input: unknown | null;
|
|
290
|
-
output: unknown | null;
|
|
291
|
-
error: string | null;
|
|
292
|
-
retry_count: Generated<number>;
|
|
293
|
-
ai_tokens_used: Generated<number>;
|
|
294
|
-
started_at: Date | null;
|
|
295
|
-
completed_at: Date | null;
|
|
296
|
-
duration_ms: number | null;
|
|
297
|
-
memo_key: string | null;
|
|
298
|
-
parent_step_id: string | null;
|
|
299
|
-
created_at: Generated<Date>;
|
|
300
|
-
}
|
|
301
|
-
interface WorkflowQueueTable {
|
|
302
|
-
id: Generated<string>;
|
|
303
|
-
run_id: string;
|
|
304
|
-
status: Generated<string>;
|
|
305
|
-
attempts: Generated<number>;
|
|
306
|
-
max_attempts: Generated<number>;
|
|
307
|
-
scheduled_for: Generated<Date>;
|
|
308
|
-
locked_at: Date | null;
|
|
309
|
-
locked_by: string | null;
|
|
310
|
-
error: string | null;
|
|
311
|
-
created_at: Generated<Date>;
|
|
312
|
-
completed_at: Date | null;
|
|
313
|
-
}
|
|
314
|
-
interface WorkflowSchedulesTable {
|
|
315
|
-
id: Generated<string>;
|
|
316
|
-
definition_id: string;
|
|
317
|
-
cron_expr: string;
|
|
318
|
-
timezone: Generated<string>;
|
|
319
|
-
next_run_at: Date;
|
|
320
|
-
last_run_at: Date | null;
|
|
321
|
-
enabled: Generated<boolean>;
|
|
322
|
-
created_at: Generated<Date>;
|
|
323
|
-
}
|
|
324
|
-
interface WorkflowCursorsTable {
|
|
325
|
-
name: string;
|
|
326
|
-
block_height: Generated<number>;
|
|
327
|
-
updated_at: Generated<Date>;
|
|
328
|
-
}
|
|
329
250
|
interface Database {
|
|
330
251
|
blocks: BlocksTable;
|
|
331
252
|
transactions: TransactionsTable;
|
|
@@ -351,16 +272,10 @@ interface Database {
|
|
|
351
272
|
team_invitations: TeamInvitationsTable;
|
|
352
273
|
chat_sessions: ChatSessionsTable;
|
|
353
274
|
chat_messages: ChatMessagesTable;
|
|
354
|
-
workflow_definitions: WorkflowDefinitionsTable;
|
|
355
|
-
workflow_runs: WorkflowRunsTable;
|
|
356
|
-
workflow_steps: WorkflowStepsTable;
|
|
357
|
-
workflow_queue: WorkflowQueueTable;
|
|
358
|
-
workflow_schedules: WorkflowSchedulesTable;
|
|
359
|
-
workflow_cursors: WorkflowCursorsTable;
|
|
360
|
-
workflow_signer_secrets: WorkflowSignerSecretsTable;
|
|
361
|
-
workflow_budgets: WorkflowBudgetsTable;
|
|
362
275
|
tenants: TenantsTable;
|
|
363
276
|
tenant_usage_monthly: TenantUsageMonthlyTable;
|
|
277
|
+
tenant_compute_addons: TenantComputeAddonsTable;
|
|
278
|
+
account_spend_caps: AccountSpendCapsTable;
|
|
364
279
|
provisioning_audit_log: ProvisioningAuditLogTable;
|
|
365
280
|
}
|
|
366
281
|
type TenantStatus = "provisioning" | "active" | "suspended" | "error" | "deleted";
|
|
@@ -383,9 +298,9 @@ interface TenantsTable {
|
|
|
383
298
|
service_key_enc: Buffer;
|
|
384
299
|
api_url_internal: string;
|
|
385
300
|
api_url_public: string;
|
|
386
|
-
trial_ends_at: Date;
|
|
387
301
|
suspended_at: Date | null;
|
|
388
302
|
last_health_check_at: Date | null;
|
|
303
|
+
last_active_at: Generated<Date>;
|
|
389
304
|
service_gen: Generated<number>;
|
|
390
305
|
anon_gen: Generated<number>;
|
|
391
306
|
project_id: string | null;
|
|
@@ -403,6 +318,28 @@ interface TenantUsageMonthlyTable {
|
|
|
403
318
|
first_at: Generated<Date>;
|
|
404
319
|
last_at: Generated<Date>;
|
|
405
320
|
}
|
|
321
|
+
interface TenantComputeAddonsTable {
|
|
322
|
+
id: Generated<string>;
|
|
323
|
+
tenant_id: string;
|
|
324
|
+
memory_mb_delta: Generated<number>;
|
|
325
|
+
cpu_delta: Generated<number | string>;
|
|
326
|
+
storage_mb_delta: Generated<number>;
|
|
327
|
+
effective_from: Generated<Date>;
|
|
328
|
+
effective_until: Date | null;
|
|
329
|
+
stripe_subscription_item_id: string | null;
|
|
330
|
+
created_at: Generated<Date>;
|
|
331
|
+
}
|
|
332
|
+
interface AccountSpendCapsTable {
|
|
333
|
+
account_id: string;
|
|
334
|
+
monthly_cap_cents: number | null;
|
|
335
|
+
compute_cap_cents: number | null;
|
|
336
|
+
storage_cap_cents: number | null;
|
|
337
|
+
ai_cap_cents: number | null;
|
|
338
|
+
alert_threshold_pct: Generated<number>;
|
|
339
|
+
alert_sent_at: Date | null;
|
|
340
|
+
frozen_at: Date | null;
|
|
341
|
+
updated_at: Generated<Date>;
|
|
342
|
+
}
|
|
406
343
|
type ProvisioningAuditEvent = "provision.start" | "provision.success" | "provision.failure" | "suspend" | "resume" | "resize" | "keys.rotate" | "bastion.key.upload" | "bastion.key.revoke" | "teardown";
|
|
407
344
|
type ProvisioningAuditStatus = "ok" | "error";
|
|
408
345
|
interface ProvisioningAuditLogTable {
|
|
@@ -417,30 +354,6 @@ interface ProvisioningAuditLogTable {
|
|
|
417
354
|
error: string | null;
|
|
418
355
|
created_at: Generated<Date>;
|
|
419
356
|
}
|
|
420
|
-
interface WorkflowBudgetsTable {
|
|
421
|
-
id: Generated<string>;
|
|
422
|
-
workflow_definition_id: string;
|
|
423
|
-
/** Period key: "daily:YYYY-MM-DD" | "weekly:YYYY-Www" | "per-run:<uuid>". */
|
|
424
|
-
period: string;
|
|
425
|
-
ai_usd_used: Generated<string>;
|
|
426
|
-
ai_tokens_used: Generated<string>;
|
|
427
|
-
chain_microstx_used: Generated<string>;
|
|
428
|
-
chain_tx_count: Generated<number>;
|
|
429
|
-
run_count: Generated<number>;
|
|
430
|
-
step_count: Generated<number>;
|
|
431
|
-
reset_at: Date;
|
|
432
|
-
created_at: Generated<Date>;
|
|
433
|
-
updated_at: Generated<Date>;
|
|
434
|
-
}
|
|
435
|
-
interface WorkflowSignerSecretsTable {
|
|
436
|
-
id: Generated<string>;
|
|
437
|
-
account_id: string;
|
|
438
|
-
name: string;
|
|
439
|
-
/** AES-GCM ciphertext bytes produced by the runner's KMS on write. */
|
|
440
|
-
encrypted_value: Buffer;
|
|
441
|
-
created_at: Generated<Date>;
|
|
442
|
-
updated_at: Generated<Date>;
|
|
443
|
-
}
|
|
444
357
|
interface Gap {
|
|
445
358
|
gapStart: number;
|
|
446
359
|
gapEnd: number;
|