@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
|
@@ -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,92 +298,82 @@ 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
|
+
type TenantComputeAddon = Selectable<TenantComputeAddonsTable>;
|
|
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;
|
|
406
342
|
updated_at: Generated<Date>;
|
|
407
343
|
}
|
|
408
|
-
|
|
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 {
|
|
409
347
|
id: Generated<string>;
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
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;
|
|
414
356
|
created_at: Generated<Date>;
|
|
415
|
-
updated_at: Generated<Date>;
|
|
416
357
|
}
|
|
417
|
-
type Subgraph = Selectable<SubgraphsTable>;
|
|
418
358
|
/**
|
|
419
|
-
*
|
|
359
|
+
* Compute add-ons — extras on top of a plan's base spec.
|
|
360
|
+
*
|
|
361
|
+
* Effective compute is NEVER derived from just the `tenants.plan`
|
|
362
|
+
* column — always run `computeEffectiveCompute(tenantId, planBase)`
|
|
363
|
+
* to fold in active add-ons. Provisioning, resize, and Stripe metering
|
|
364
|
+
* all share this source of truth.
|
|
420
365
|
*/
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
}
|
|
428
|
-
data: any[]
|
|
429
|
-
meta: {
|
|
430
|
-
total: number
|
|
431
|
-
limit: number
|
|
432
|
-
offset: number
|
|
433
|
-
}
|
|
434
|
-
}>;
|
|
435
|
-
/**
|
|
436
|
-
* Get a single public subgraph by name with creator info.
|
|
437
|
-
*/
|
|
438
|
-
declare function getPublicSubgraph(db: Kysely<Database>, name: string): Promise<any | undefined>;
|
|
439
|
-
/**
|
|
440
|
-
* Get a creator profile by slug with their public subgraphs.
|
|
441
|
-
*/
|
|
442
|
-
declare function getCreatorProfile(db: Kysely<Database>, slug: string): Promise<{
|
|
443
|
-
account: any
|
|
444
|
-
subgraphs: any[]
|
|
445
|
-
} | null>;
|
|
446
|
-
/**
|
|
447
|
-
* Publish a subgraph (set is_public = true).
|
|
448
|
-
*/
|
|
449
|
-
declare function publishSubgraph(db: Kysely<Database>, subgraphId: string, opts?: {
|
|
450
|
-
tags?: string[]
|
|
451
|
-
description?: string
|
|
452
|
-
}): Promise<Subgraph>;
|
|
453
|
-
/**
|
|
454
|
-
* Unpublish a subgraph (set is_public = false).
|
|
455
|
-
*/
|
|
456
|
-
declare function unpublishSubgraph(db: Kysely<Database>, subgraphId: string): Promise<Subgraph>;
|
|
457
|
-
/**
|
|
458
|
-
* Increment per-subgraph query count for today. Fire-and-forget safe.
|
|
459
|
-
*/
|
|
460
|
-
declare function incrementSubgraphQueryCount(db: Kysely<Database>, subgraphId: string): Promise<void>;
|
|
461
|
-
/**
|
|
462
|
-
* Get daily usage history for a subgraph.
|
|
463
|
-
*/
|
|
464
|
-
declare function getSubgraphUsageHistory(db: Kysely<Database>, subgraphId: string, days: number): Promise<Array<{
|
|
465
|
-
date: string
|
|
466
|
-
query_count: number
|
|
467
|
-
}>>;
|
|
366
|
+
/** Active = open-ended (effective_until IS NULL) OR not yet expired. */
|
|
367
|
+
declare function listActiveAddonsForTenant(db: Kysely<Database>, tenantId: string, now?: Date): Promise<TenantComputeAddon[]>;
|
|
368
|
+
interface ComputeSpec {
|
|
369
|
+
cpus: number;
|
|
370
|
+
memoryMb: number;
|
|
371
|
+
storageLimitMb: number;
|
|
372
|
+
}
|
|
468
373
|
/**
|
|
469
|
-
*
|
|
374
|
+
* Apply active add-ons on top of a base spec. `storageLimitMb` of -1
|
|
375
|
+
* (enterprise unlimited) passes through unchanged — add-ons don't
|
|
376
|
+
* further modify unlimited storage.
|
|
470
377
|
*/
|
|
471
|
-
declare function
|
|
472
|
-
export {
|
|
378
|
+
declare function computeEffectiveCompute(db: Kysely<Database>, tenantId: string, base: ComputeSpec, now?: Date): Promise<ComputeSpec>;
|
|
379
|
+
export { listActiveAddonsForTenant, computeEffectiveCompute, ComputeSpec };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __returnValue = (v) => v;
|
|
4
|
+
function __exportSetter(name, newValue) {
|
|
5
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
+
}
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, {
|
|
10
|
+
get: all[name],
|
|
11
|
+
enumerable: true,
|
|
12
|
+
configurable: true,
|
|
13
|
+
set: __exportSetter.bind(all, name)
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// src/db/queries/tenant-compute-addons.ts
|
|
18
|
+
import { sql } from "kysely";
|
|
19
|
+
async function listActiveAddonsForTenant(db, tenantId, now = new Date) {
|
|
20
|
+
return db.selectFrom("tenant_compute_addons").selectAll().where("tenant_id", "=", tenantId).where("effective_from", "<=", now).where((eb) => eb.or([
|
|
21
|
+
eb("effective_until", "is", null),
|
|
22
|
+
eb("effective_until", ">", now)
|
|
23
|
+
])).execute();
|
|
24
|
+
}
|
|
25
|
+
async function computeEffectiveCompute(db, tenantId, base, now = new Date) {
|
|
26
|
+
const row = await db.selectFrom("tenant_compute_addons").select([
|
|
27
|
+
sql`coalesce(sum(memory_mb_delta), 0)`.as("mem_delta"),
|
|
28
|
+
sql`coalesce(sum(cpu_delta), 0)`.as("cpu_delta"),
|
|
29
|
+
sql`coalesce(sum(storage_mb_delta), 0)`.as("stor_delta")
|
|
30
|
+
]).where("tenant_id", "=", tenantId).where("effective_from", "<=", now).where((eb) => eb.or([
|
|
31
|
+
eb("effective_until", "is", null),
|
|
32
|
+
eb("effective_until", ">", now)
|
|
33
|
+
])).executeTakeFirst();
|
|
34
|
+
if (!row)
|
|
35
|
+
return base;
|
|
36
|
+
const cpus = base.cpus + Number(row.cpu_delta ?? 0);
|
|
37
|
+
const memoryMb = base.memoryMb + Number(row.mem_delta ?? 0);
|
|
38
|
+
const storageLimitMb = base.storageLimitMb === -1 ? -1 : base.storageLimitMb + Number(row.stor_delta ?? 0);
|
|
39
|
+
return { cpus, memoryMb, storageLimitMb };
|
|
40
|
+
}
|
|
41
|
+
export {
|
|
42
|
+
listActiveAddonsForTenant,
|
|
43
|
+
computeEffectiveCompute
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
//# debugId=70D8508BE398EADC64756E2164756E21
|
|
47
|
+
//# sourceMappingURL=tenant-compute-addons.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/db/queries/tenant-compute-addons.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import { type Kysely, sql } from \"kysely\";\nimport type { Database, TenantComputeAddon } from \"../types.ts\";\n\n/**\n * Compute add-ons — extras on top of a plan's base spec.\n *\n * Effective compute is NEVER derived from just the `tenants.plan`\n * column — always run `computeEffectiveCompute(tenantId, planBase)`\n * to fold in active add-ons. Provisioning, resize, and Stripe metering\n * all share this source of truth.\n */\n\n/** Active = open-ended (effective_until IS NULL) OR not yet expired. */\nexport async function listActiveAddonsForTenant(\n\tdb: Kysely<Database>,\n\ttenantId: string,\n\tnow: Date = new Date(),\n): Promise<TenantComputeAddon[]> {\n\treturn db\n\t\t.selectFrom(\"tenant_compute_addons\")\n\t\t.selectAll()\n\t\t.where(\"tenant_id\", \"=\", tenantId)\n\t\t.where(\"effective_from\", \"<=\", now)\n\t\t.where((eb) =>\n\t\t\teb.or([\n\t\t\t\teb(\"effective_until\", \"is\", null),\n\t\t\t\teb(\"effective_until\", \">\", now),\n\t\t\t]),\n\t\t)\n\t\t.execute();\n}\n\nexport interface ComputeSpec {\n\tcpus: number;\n\tmemoryMb: number;\n\tstorageLimitMb: number;\n}\n\n/**\n * Apply active add-ons on top of a base spec. `storageLimitMb` of -1\n * (enterprise unlimited) passes through unchanged — add-ons don't\n * further modify unlimited storage.\n */\nexport async function computeEffectiveCompute(\n\tdb: Kysely<Database>,\n\ttenantId: string,\n\tbase: ComputeSpec,\n\tnow: Date = new Date(),\n): Promise<ComputeSpec> {\n\tconst row = await db\n\t\t.selectFrom(\"tenant_compute_addons\")\n\t\t.select([\n\t\t\tsql<number>`coalesce(sum(memory_mb_delta), 0)`.as(\"mem_delta\"),\n\t\t\tsql<string>`coalesce(sum(cpu_delta), 0)`.as(\"cpu_delta\"),\n\t\t\tsql<number>`coalesce(sum(storage_mb_delta), 0)`.as(\"stor_delta\"),\n\t\t])\n\t\t.where(\"tenant_id\", \"=\", tenantId)\n\t\t.where(\"effective_from\", \"<=\", now)\n\t\t.where((eb) =>\n\t\t\teb.or([\n\t\t\t\teb(\"effective_until\", \"is\", null),\n\t\t\t\teb(\"effective_until\", \">\", now),\n\t\t\t]),\n\t\t)\n\t\t.executeTakeFirst();\n\n\tif (!row) return base;\n\n\tconst cpus = base.cpus + Number(row.cpu_delta ?? 0);\n\tconst memoryMb = base.memoryMb + Number(row.mem_delta ?? 0);\n\tconst storageLimitMb =\n\t\tbase.storageLimitMb === -1\n\t\t\t? -1\n\t\t\t: base.storageLimitMb + Number(row.stor_delta ?? 0);\n\n\treturn { cpus, memoryMb, storageLimitMb };\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";;;;;;;;;;;;;;;;;AAAA;AAaA,eAAsB,yBAAyB,CAC9C,IACA,UACA,MAAY,IAAI,MACgB;AAAA,EAChC,OAAO,GACL,WAAW,uBAAuB,EAClC,UAAU,EACV,MAAM,aAAa,KAAK,QAAQ,EAChC,MAAM,kBAAkB,MAAM,GAAG,EACjC,MAAM,CAAC,OACP,GAAG,GAAG;AAAA,IACL,GAAG,mBAAmB,MAAM,IAAI;AAAA,IAChC,GAAG,mBAAmB,KAAK,GAAG;AAAA,EAC/B,CAAC,CACF,EACC,QAAQ;AAAA;AAcX,eAAsB,uBAAuB,CAC5C,IACA,UACA,MACA,MAAY,IAAI,MACO;AAAA,EACvB,MAAM,MAAM,MAAM,GAChB,WAAW,uBAAuB,EAClC,OAAO;AAAA,IACP,uCAA+C,GAAG,WAAW;AAAA,IAC7D,iCAAyC,GAAG,WAAW;AAAA,IACvD,wCAAgD,GAAG,YAAY;AAAA,EAChE,CAAC,EACA,MAAM,aAAa,KAAK,QAAQ,EAChC,MAAM,kBAAkB,MAAM,GAAG,EACjC,MAAM,CAAC,OACP,GAAG,GAAG;AAAA,IACL,GAAG,mBAAmB,MAAM,IAAI;AAAA,IAChC,GAAG,mBAAmB,KAAK,GAAG;AAAA,EAC/B,CAAC,CACF,EACC,iBAAiB;AAAA,EAEnB,IAAI,CAAC;AAAA,IAAK,OAAO;AAAA,EAEjB,MAAM,OAAO,KAAK,OAAO,OAAO,IAAI,aAAa,CAAC;AAAA,EAClD,MAAM,WAAW,KAAK,WAAW,OAAO,IAAI,aAAa,CAAC;AAAA,EAC1D,MAAM,iBACL,KAAK,mBAAmB,KACrB,KACA,KAAK,iBAAiB,OAAO,IAAI,cAAc,CAAC;AAAA,EAEpD,OAAO,EAAE,MAAM,UAAU,eAAe;AAAA;",
|
|
8
|
+
"debugId": "70D8508BE398EADC64756E2164756E21",
|
|
9
|
+
"names": []
|
|
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,9 +298,9 @@ 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;
|
|
@@ -391,29 +308,52 @@ interface TenantsTable {
|
|
|
391
308
|
updated_at: Generated<Date>;
|
|
392
309
|
}
|
|
393
310
|
type Tenant = Selectable<TenantsTable>;
|
|
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
|
/**
|
|
419
359
|
* Tenant registry queries. Encrypted columns are stored as `bytea` and
|
|
@@ -439,17 +379,34 @@ interface NewTenantInput {
|
|
|
439
379
|
serviceKey: string;
|
|
440
380
|
apiUrlInternal: string;
|
|
441
381
|
apiUrlPublic: string;
|
|
442
|
-
trialEndsAt: Date;
|
|
443
382
|
projectId?: string;
|
|
444
383
|
}
|
|
445
384
|
declare function insertTenant(db: Kysely<Database>, input: NewTenantInput): Promise<Tenant>;
|
|
446
385
|
declare function getTenantByAccount(db: Kysely<Database>, accountId: string): Promise<Tenant | null>;
|
|
447
386
|
declare function getTenantBySlug(db: Kysely<Database>, slug: string): Promise<Tenant | null>;
|
|
448
387
|
declare function listTenantsByStatus(db: Kysely<Database>, status: TenantStatus): Promise<Tenant[]>;
|
|
449
|
-
|
|
388
|
+
/**
|
|
389
|
+
* Tenants considered "idle" for auto-pause on the Hobby tier. Active = any
|
|
390
|
+
* successful tenant-API query OR workflow run wrote `last_active_at` within
|
|
391
|
+
* the threshold.
|
|
392
|
+
*/
|
|
393
|
+
declare function listIdleHobbyTenants(db: Kysely<Database>, idleSince: Date): Promise<Tenant[]>;
|
|
394
|
+
/**
|
|
395
|
+
* Bump `last_active_at` for a tenant. Callers are expected to throttle
|
|
396
|
+
* (don't hammer on every request) — the activity middleware + workflow-
|
|
397
|
+
* runner enforce a 60s per-tenant min between writes.
|
|
398
|
+
*/
|
|
399
|
+
declare function bumpTenantActivity(db: Kysely<Database>, slug: string): Promise<void>;
|
|
450
400
|
declare function listSuspendedOlderThan(db: Kysely<Database>, olderThan: Date): Promise<Tenant[]>;
|
|
451
401
|
declare function setTenantStatus(db: Kysely<Database>, slug: string, status: TenantStatus): Promise<void>;
|
|
452
402
|
declare function recordHealthCheck(db: Kysely<Database>, slug: string, storageUsedMb: number | null): Promise<void>;
|
|
403
|
+
/**
|
|
404
|
+
* Record a storage measurement into the current calendar month's bucket.
|
|
405
|
+
* Maintains peak, running average, and the most recent value in a single
|
|
406
|
+
* upsert. Billing will consume this later; for now the table just gives
|
|
407
|
+
* us evidence of usage over time.
|
|
408
|
+
*/
|
|
409
|
+
declare function recordMonthlyUsage(db: Kysely<Database>, tenantId: string, storageMb: number): Promise<void>;
|
|
453
410
|
declare function updateTenantPlan(db: Kysely<Database>, slug: string, plan: string, cpus: number, memoryMb: number, storageLimitMb: number): Promise<void>;
|
|
454
411
|
type RotateType = "service" | "anon" | "both";
|
|
455
412
|
/**
|
|
@@ -490,4 +447,4 @@ interface TenantCredentials {
|
|
|
490
447
|
* CLI). Never log the returned object.
|
|
491
448
|
*/
|
|
492
449
|
declare function getTenantCredentials(db: Kysely<Database>, slug: string): Promise<TenantCredentials | null>;
|
|
493
|
-
export { updateTenantPlan, updateTenantKeys, setTenantStatus, recordHealthCheck, listTenantsByStatus, listSuspendedOlderThan,
|
|
450
|
+
export { updateTenantPlan, updateTenantKeys, setTenantStatus, recordMonthlyUsage, recordHealthCheck, listTenantsByStatus, listSuspendedOlderThan, listIdleHobbyTenants, insertTenant, getTenantCredentials, getTenantBySlug, getTenantByAccount, deleteTenant, bumpTenantKeyGen, bumpTenantActivity, TenantCredentials, RotateType, NewTenantInput };
|
|
@@ -61,6 +61,7 @@ function generateSecretsKey() {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
// src/db/queries/tenants.ts
|
|
64
|
+
import { sql } from "kysely";
|
|
64
65
|
async function insertTenant(db, input) {
|
|
65
66
|
const row = {
|
|
66
67
|
account_id: input.accountId,
|
|
@@ -79,7 +80,6 @@ async function insertTenant(db, input) {
|
|
|
79
80
|
service_key_enc: encryptSecret(input.serviceKey),
|
|
80
81
|
api_url_internal: input.apiUrlInternal,
|
|
81
82
|
api_url_public: input.apiUrlPublic,
|
|
82
|
-
trial_ends_at: input.trialEndsAt,
|
|
83
83
|
project_id: input.projectId ?? null
|
|
84
84
|
};
|
|
85
85
|
return db.insertInto("tenants").values(row).returningAll().executeTakeFirstOrThrow();
|
|
@@ -95,8 +95,11 @@ async function getTenantBySlug(db, slug) {
|
|
|
95
95
|
async function listTenantsByStatus(db, status) {
|
|
96
96
|
return db.selectFrom("tenants").selectAll().where("status", "=", status).execute();
|
|
97
97
|
}
|
|
98
|
-
async function
|
|
99
|
-
return db.selectFrom("tenants").selectAll().where("status", "
|
|
98
|
+
async function listIdleHobbyTenants(db, idleSince) {
|
|
99
|
+
return db.selectFrom("tenants").selectAll().where("status", "=", "active").where("plan", "=", "hobby").where("last_active_at", "<", idleSince).execute();
|
|
100
|
+
}
|
|
101
|
+
async function bumpTenantActivity(db, slug) {
|
|
102
|
+
await db.updateTable("tenants").set({ last_active_at: new Date }).where("slug", "=", slug).execute();
|
|
100
103
|
}
|
|
101
104
|
async function listSuspendedOlderThan(db, olderThan) {
|
|
102
105
|
return db.selectFrom("tenants").selectAll().where("status", "=", "suspended").where("suspended_at", "<", olderThan).execute();
|
|
@@ -119,6 +122,30 @@ async function recordHealthCheck(db, slug, storageUsedMb) {
|
|
|
119
122
|
updated_at: new Date
|
|
120
123
|
}).where("slug", "=", slug).execute();
|
|
121
124
|
}
|
|
125
|
+
async function recordMonthlyUsage(db, tenantId, storageMb) {
|
|
126
|
+
const now = new Date;
|
|
127
|
+
const periodMonth = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1));
|
|
128
|
+
await sql`
|
|
129
|
+
INSERT INTO tenant_usage_monthly (
|
|
130
|
+
tenant_id, period_month,
|
|
131
|
+
storage_peak_mb, storage_avg_mb, storage_last_mb,
|
|
132
|
+
measurements, first_at, last_at
|
|
133
|
+
) VALUES (
|
|
134
|
+
${tenantId}, ${periodMonth},
|
|
135
|
+
${storageMb}, ${storageMb}, ${storageMb},
|
|
136
|
+
1, now(), now()
|
|
137
|
+
)
|
|
138
|
+
ON CONFLICT (tenant_id, period_month) DO UPDATE SET
|
|
139
|
+
storage_peak_mb = GREATEST(tenant_usage_monthly.storage_peak_mb, EXCLUDED.storage_last_mb),
|
|
140
|
+
storage_avg_mb = (
|
|
141
|
+
(tenant_usage_monthly.storage_avg_mb * tenant_usage_monthly.measurements + EXCLUDED.storage_last_mb)
|
|
142
|
+
/ (tenant_usage_monthly.measurements + 1)
|
|
143
|
+
),
|
|
144
|
+
storage_last_mb = EXCLUDED.storage_last_mb,
|
|
145
|
+
measurements = tenant_usage_monthly.measurements + 1,
|
|
146
|
+
last_at = now()
|
|
147
|
+
`.execute(db);
|
|
148
|
+
}
|
|
122
149
|
async function updateTenantPlan(db, slug, plan, cpus, memoryMb, storageLimitMb) {
|
|
123
150
|
await db.updateTable("tenants").set({
|
|
124
151
|
plan,
|
|
@@ -178,17 +205,19 @@ export {
|
|
|
178
205
|
updateTenantPlan,
|
|
179
206
|
updateTenantKeys,
|
|
180
207
|
setTenantStatus,
|
|
208
|
+
recordMonthlyUsage,
|
|
181
209
|
recordHealthCheck,
|
|
182
210
|
listTenantsByStatus,
|
|
183
211
|
listSuspendedOlderThan,
|
|
184
|
-
|
|
212
|
+
listIdleHobbyTenants,
|
|
185
213
|
insertTenant,
|
|
186
214
|
getTenantCredentials,
|
|
187
215
|
getTenantBySlug,
|
|
188
216
|
getTenantByAccount,
|
|
189
217
|
deleteTenant,
|
|
190
|
-
bumpTenantKeyGen
|
|
218
|
+
bumpTenantKeyGen,
|
|
219
|
+
bumpTenantActivity
|
|
191
220
|
};
|
|
192
221
|
|
|
193
|
-
//# debugId=
|
|
222
|
+
//# debugId=D0CC1ED1445094FE64756E2164756E21
|
|
194
223
|
//# sourceMappingURL=tenants.js.map
|