@secondlayer/shared 2.0.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 +64 -130
- 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 +61 -108
- 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 +47 -107
- package/dist/src/db/queries/projects.d.ts +47 -107
- package/dist/src/db/queries/{workflows.d.ts → provisioning-audit.d.ts} +70 -142
- package/dist/src/db/queries/provisioning-audit.js +40 -0
- package/dist/src/db/queries/provisioning-audit.js.map +10 -0
- package/dist/src/db/queries/subgraph-gaps.d.ts +47 -107
- package/dist/src/db/queries/subgraphs.d.ts +47 -108
- package/dist/src/db/queries/subgraphs.js +2 -3
- package/dist/src/db/queries/subgraphs.js.map +4 -4
- package/dist/src/db/queries/{marketplace.d.ts → tenant-compute-addons.d.ts} +66 -159
- 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 +67 -110
- package/dist/src/db/queries/tenants.js +35 -6
- package/dist/src/db/queries/tenants.js.map +3 -3
- package/dist/src/db/queries/usage.d.ts +48 -132
- 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 +59 -129
- 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 +98 -212
- package/dist/src/index.js +69 -80
- package/dist/src/index.js.map +6 -6
- package/dist/src/mode.d.ts +4 -5
- package/dist/src/mode.js.map +2 -2
- package/dist/src/node/local-client.d.ts +47 -107
- package/dist/src/pricing.d.ts +20 -1
- package/dist/src/pricing.js +58 -1
- package/dist/src/pricing.js.map +3 -3
- package/dist/src/schemas/accounts.d.ts +14 -0
- package/dist/src/schemas/{marketplace.js → accounts.js} +4 -14
- package/dist/src/schemas/accounts.js.map +10 -0
- package/dist/src/schemas/index.d.ts +28 -77
- package/dist/src/schemas/index.js +59 -69
- package/dist/src/schemas/index.js.map +4 -4
- package/migrations/0043_tenant_usage_monthly.ts +36 -0
- package/migrations/0044_provisioning_audit_log.ts +40 -0
- 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 +33 -21
- package/dist/src/db/queries/marketplace.js +0 -139
- package/dist/src/db/queries/marketplace.js.map +0 -10
- 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/marketplace.d.ts +0 -63
- package/dist/src/schemas/marketplace.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,15 +273,11 @@ 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;
|
|
277
|
+
tenant_usage_monthly: TenantUsageMonthlyTable;
|
|
278
|
+
tenant_compute_addons: TenantComputeAddonsTable;
|
|
279
|
+
account_spend_caps: AccountSpendCapsTable;
|
|
280
|
+
provisioning_audit_log: ProvisioningAuditLogTable;
|
|
364
281
|
}
|
|
365
282
|
type TenantStatus = "provisioning" | "active" | "suspended" | "error" | "deleted";
|
|
366
283
|
interface TenantsTable {
|
|
@@ -382,38 +299,61 @@ interface TenantsTable {
|
|
|
382
299
|
service_key_enc: Buffer;
|
|
383
300
|
api_url_internal: string;
|
|
384
301
|
api_url_public: string;
|
|
385
|
-
trial_ends_at: Date;
|
|
386
302
|
suspended_at: Date | null;
|
|
387
303
|
last_health_check_at: Date | null;
|
|
304
|
+
last_active_at: Generated<Date>;
|
|
388
305
|
service_gen: Generated<number>;
|
|
389
306
|
anon_gen: Generated<number>;
|
|
390
307
|
project_id: string | null;
|
|
391
308
|
created_at: Generated<Date>;
|
|
392
309
|
updated_at: Generated<Date>;
|
|
393
310
|
}
|
|
394
|
-
interface
|
|
311
|
+
interface TenantUsageMonthlyTable {
|
|
312
|
+
id: Generated<string>;
|
|
313
|
+
tenant_id: string;
|
|
314
|
+
period_month: Date;
|
|
315
|
+
storage_peak_mb: Generated<number>;
|
|
316
|
+
storage_avg_mb: Generated<number>;
|
|
317
|
+
storage_last_mb: Generated<number>;
|
|
318
|
+
measurements: Generated<number>;
|
|
319
|
+
first_at: Generated<Date>;
|
|
320
|
+
last_at: Generated<Date>;
|
|
321
|
+
}
|
|
322
|
+
interface TenantComputeAddonsTable {
|
|
395
323
|
id: Generated<string>;
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
run_count: Generated<number>;
|
|
404
|
-
step_count: Generated<number>;
|
|
405
|
-
reset_at: Date;
|
|
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;
|
|
406
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;
|
|
407
342
|
updated_at: Generated<Date>;
|
|
408
343
|
}
|
|
409
|
-
|
|
344
|
+
type ProvisioningAuditEvent = "provision.start" | "provision.success" | "provision.failure" | "suspend" | "resume" | "resize" | "keys.rotate" | "bastion.key.upload" | "bastion.key.revoke" | "teardown";
|
|
345
|
+
type ProvisioningAuditStatus = "ok" | "error";
|
|
346
|
+
interface ProvisioningAuditLogTable {
|
|
410
347
|
id: Generated<string>;
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
348
|
+
tenant_id: string | null;
|
|
349
|
+
tenant_slug: string | null;
|
|
350
|
+
account_id: string | null;
|
|
351
|
+
actor: string;
|
|
352
|
+
event: ProvisioningAuditEvent;
|
|
353
|
+
status: ProvisioningAuditStatus;
|
|
354
|
+
detail: unknown | null;
|
|
355
|
+
error: string | null;
|
|
415
356
|
created_at: Generated<Date>;
|
|
416
|
-
updated_at: Generated<Date>;
|
|
417
357
|
}
|
|
418
358
|
type Account = Selectable<AccountsTable>;
|
|
419
359
|
declare function upsertAccount(db: Kysely<Database>, email: string): Promise<Account>;
|
|
@@ -423,6 +363,19 @@ declare function updateAccountProfile(db: Kysely<Database>, id: string, data: {
|
|
|
423
363
|
bio?: string
|
|
424
364
|
slug?: string
|
|
425
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>;
|
|
426
379
|
declare function isSlugTaken(db: Kysely<Database>, slug: string, excludeAccountId: string): Promise<boolean>;
|
|
427
380
|
declare function isEmailAllowed(db: Kysely<Database>, email: string): Promise<boolean>;
|
|
428
381
|
declare function createMagicLink(db: Kysely<Database>, email: string, token: string, code: string, expiresInMs?: number): Promise<void>;
|
|
@@ -445,4 +398,4 @@ declare function approveWaitlistEntry(db: Kysely<Database>, email: string): Prom
|
|
|
445
398
|
code: string
|
|
446
399
|
status: "approved" | "already_approved" | "not_found"
|
|
447
400
|
}>;
|
|
448
|
-
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,15 +272,11 @@ 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;
|
|
276
|
+
tenant_usage_monthly: TenantUsageMonthlyTable;
|
|
277
|
+
tenant_compute_addons: TenantComputeAddonsTable;
|
|
278
|
+
account_spend_caps: AccountSpendCapsTable;
|
|
279
|
+
provisioning_audit_log: ProvisioningAuditLogTable;
|
|
363
280
|
}
|
|
364
281
|
type TenantStatus = "provisioning" | "active" | "suspended" | "error" | "deleted";
|
|
365
282
|
interface TenantsTable {
|
|
@@ -381,38 +298,61 @@ interface TenantsTable {
|
|
|
381
298
|
service_key_enc: Buffer;
|
|
382
299
|
api_url_internal: string;
|
|
383
300
|
api_url_public: string;
|
|
384
|
-
trial_ends_at: Date;
|
|
385
301
|
suspended_at: Date | null;
|
|
386
302
|
last_health_check_at: Date | null;
|
|
303
|
+
last_active_at: Generated<Date>;
|
|
387
304
|
service_gen: Generated<number>;
|
|
388
305
|
anon_gen: Generated<number>;
|
|
389
306
|
project_id: string | null;
|
|
390
307
|
created_at: Generated<Date>;
|
|
391
308
|
updated_at: Generated<Date>;
|
|
392
309
|
}
|
|
393
|
-
interface
|
|
310
|
+
interface TenantUsageMonthlyTable {
|
|
394
311
|
id: Generated<string>;
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
312
|
+
tenant_id: string;
|
|
313
|
+
period_month: Date;
|
|
314
|
+
storage_peak_mb: Generated<number>;
|
|
315
|
+
storage_avg_mb: Generated<number>;
|
|
316
|
+
storage_last_mb: Generated<number>;
|
|
317
|
+
measurements: Generated<number>;
|
|
318
|
+
first_at: Generated<Date>;
|
|
319
|
+
last_at: Generated<Date>;
|
|
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;
|
|
405
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;
|
|
406
341
|
updated_at: Generated<Date>;
|
|
407
342
|
}
|
|
408
|
-
|
|
343
|
+
type ProvisioningAuditEvent = "provision.start" | "provision.success" | "provision.failure" | "suspend" | "resume" | "resize" | "keys.rotate" | "bastion.key.upload" | "bastion.key.revoke" | "teardown";
|
|
344
|
+
type ProvisioningAuditStatus = "ok" | "error";
|
|
345
|
+
interface ProvisioningAuditLogTable {
|
|
409
346
|
id: Generated<string>;
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
347
|
+
tenant_id: string | null;
|
|
348
|
+
tenant_slug: string | null;
|
|
349
|
+
account_id: string | null;
|
|
350
|
+
actor: string;
|
|
351
|
+
event: ProvisioningAuditEvent;
|
|
352
|
+
status: ProvisioningAuditStatus;
|
|
353
|
+
detail: unknown | null;
|
|
354
|
+
error: string | null;
|
|
414
355
|
created_at: Generated<Date>;
|
|
415
|
-
updated_at: Generated<Date>;
|
|
416
356
|
}
|
|
417
357
|
interface Gap {
|
|
418
358
|
gapStart: number;
|