@secondlayer/shared 6.1.0 → 6.2.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/dist/src/db/index.d.ts +2 -0
- package/dist/src/db/queries/account-spend-caps.d.ts +2 -0
- package/dist/src/db/queries/account-usage.d.ts +2 -0
- package/dist/src/db/queries/accounts.d.ts +2 -0
- package/dist/src/db/queries/chain-reorgs.d.ts +2 -0
- package/dist/src/db/queries/integrity.d.ts +2 -0
- package/dist/src/db/queries/projects.d.ts +2 -0
- package/dist/src/db/queries/provisioning-audit.d.ts +2 -0
- package/dist/src/db/queries/subgraph-gaps.d.ts +2 -0
- package/dist/src/db/queries/subgraph-operations.d.ts +2 -0
- package/dist/src/db/queries/subgraphs.d.ts +2 -0
- package/dist/src/db/queries/subscriptions.d.ts +2 -0
- package/dist/src/db/queries/tenant-compute-addons.d.ts +2 -0
- package/dist/src/db/queries/tenants.d.ts +2 -0
- package/dist/src/db/queries/usage.d.ts +11 -1
- package/dist/src/db/queries/usage.js +18 -2
- package/dist/src/db/queries/usage.js.map +3 -3
- package/dist/src/db/schema.d.ts +2 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/node/local-client.d.ts +2 -0
- package/migrations/0069_api_keys_product_tier.ts +43 -0
- package/package.json +1 -1
package/dist/src/db/index.d.ts
CHANGED
|
@@ -122,6 +122,8 @@ interface ApiKeysTable {
|
|
|
122
122
|
rate_limit: Generated<number>;
|
|
123
123
|
ip_address: string;
|
|
124
124
|
account_id: string;
|
|
125
|
+
product: Generated<"account" | "streams" | "index">;
|
|
126
|
+
tier: "free" | "build" | "scale" | "enterprise" | null;
|
|
125
127
|
last_used_at: Date | null;
|
|
126
128
|
revoked_at: Date | null;
|
|
127
129
|
created_at: Generated<Date>;
|
|
@@ -104,6 +104,8 @@ interface ApiKeysTable {
|
|
|
104
104
|
rate_limit: Generated<number>;
|
|
105
105
|
ip_address: string;
|
|
106
106
|
account_id: string;
|
|
107
|
+
product: Generated<"account" | "streams" | "index">;
|
|
108
|
+
tier: "free" | "build" | "scale" | "enterprise" | null;
|
|
107
109
|
last_used_at: Date | null;
|
|
108
110
|
revoked_at: Date | null;
|
|
109
111
|
created_at: Generated<Date>;
|
|
@@ -104,6 +104,8 @@ interface ApiKeysTable {
|
|
|
104
104
|
rate_limit: Generated<number>;
|
|
105
105
|
ip_address: string;
|
|
106
106
|
account_id: string;
|
|
107
|
+
product: Generated<"account" | "streams" | "index">;
|
|
108
|
+
tier: "free" | "build" | "scale" | "enterprise" | null;
|
|
107
109
|
last_used_at: Date | null;
|
|
108
110
|
revoked_at: Date | null;
|
|
109
111
|
created_at: Generated<Date>;
|
|
@@ -105,6 +105,8 @@ interface ApiKeysTable {
|
|
|
105
105
|
rate_limit: Generated<number>;
|
|
106
106
|
ip_address: string;
|
|
107
107
|
account_id: string;
|
|
108
|
+
product: Generated<"account" | "streams" | "index">;
|
|
109
|
+
tier: "free" | "build" | "scale" | "enterprise" | null;
|
|
108
110
|
last_used_at: Date | null;
|
|
109
111
|
revoked_at: Date | null;
|
|
110
112
|
created_at: Generated<Date>;
|
|
@@ -103,6 +103,8 @@ interface ApiKeysTable {
|
|
|
103
103
|
rate_limit: Generated<number>;
|
|
104
104
|
ip_address: string;
|
|
105
105
|
account_id: string;
|
|
106
|
+
product: Generated<"account" | "streams" | "index">;
|
|
107
|
+
tier: "free" | "build" | "scale" | "enterprise" | null;
|
|
106
108
|
last_used_at: Date | null;
|
|
107
109
|
revoked_at: Date | null;
|
|
108
110
|
created_at: Generated<Date>;
|
|
@@ -104,6 +104,8 @@ interface ApiKeysTable {
|
|
|
104
104
|
rate_limit: Generated<number>;
|
|
105
105
|
ip_address: string;
|
|
106
106
|
account_id: string;
|
|
107
|
+
product: Generated<"account" | "streams" | "index">;
|
|
108
|
+
tier: "free" | "build" | "scale" | "enterprise" | null;
|
|
107
109
|
last_used_at: Date | null;
|
|
108
110
|
revoked_at: Date | null;
|
|
109
111
|
created_at: Generated<Date>;
|
|
@@ -104,6 +104,8 @@ interface ApiKeysTable {
|
|
|
104
104
|
rate_limit: Generated<number>;
|
|
105
105
|
ip_address: string;
|
|
106
106
|
account_id: string;
|
|
107
|
+
product: Generated<"account" | "streams" | "index">;
|
|
108
|
+
tier: "free" | "build" | "scale" | "enterprise" | null;
|
|
107
109
|
last_used_at: Date | null;
|
|
108
110
|
revoked_at: Date | null;
|
|
109
111
|
created_at: Generated<Date>;
|
|
@@ -104,6 +104,8 @@ interface ApiKeysTable {
|
|
|
104
104
|
rate_limit: Generated<number>;
|
|
105
105
|
ip_address: string;
|
|
106
106
|
account_id: string;
|
|
107
|
+
product: Generated<"account" | "streams" | "index">;
|
|
108
|
+
tier: "free" | "build" | "scale" | "enterprise" | null;
|
|
107
109
|
last_used_at: Date | null;
|
|
108
110
|
revoked_at: Date | null;
|
|
109
111
|
created_at: Generated<Date>;
|
|
@@ -104,6 +104,8 @@ interface ApiKeysTable {
|
|
|
104
104
|
rate_limit: Generated<number>;
|
|
105
105
|
ip_address: string;
|
|
106
106
|
account_id: string;
|
|
107
|
+
product: Generated<"account" | "streams" | "index">;
|
|
108
|
+
tier: "free" | "build" | "scale" | "enterprise" | null;
|
|
107
109
|
last_used_at: Date | null;
|
|
108
110
|
revoked_at: Date | null;
|
|
109
111
|
created_at: Generated<Date>;
|
|
@@ -104,6 +104,8 @@ interface ApiKeysTable {
|
|
|
104
104
|
rate_limit: Generated<number>;
|
|
105
105
|
ip_address: string;
|
|
106
106
|
account_id: string;
|
|
107
|
+
product: Generated<"account" | "streams" | "index">;
|
|
108
|
+
tier: "free" | "build" | "scale" | "enterprise" | null;
|
|
107
109
|
last_used_at: Date | null;
|
|
108
110
|
revoked_at: Date | null;
|
|
109
111
|
created_at: Generated<Date>;
|
|
@@ -104,6 +104,8 @@ interface ApiKeysTable {
|
|
|
104
104
|
rate_limit: Generated<number>;
|
|
105
105
|
ip_address: string;
|
|
106
106
|
account_id: string;
|
|
107
|
+
product: Generated<"account" | "streams" | "index">;
|
|
108
|
+
tier: "free" | "build" | "scale" | "enterprise" | null;
|
|
107
109
|
last_used_at: Date | null;
|
|
108
110
|
revoked_at: Date | null;
|
|
109
111
|
created_at: Generated<Date>;
|
|
@@ -104,6 +104,8 @@ interface ApiKeysTable {
|
|
|
104
104
|
rate_limit: Generated<number>;
|
|
105
105
|
ip_address: string;
|
|
106
106
|
account_id: string;
|
|
107
|
+
product: Generated<"account" | "streams" | "index">;
|
|
108
|
+
tier: "free" | "build" | "scale" | "enterprise" | null;
|
|
107
109
|
last_used_at: Date | null;
|
|
108
110
|
revoked_at: Date | null;
|
|
109
111
|
created_at: Generated<Date>;
|
|
@@ -104,6 +104,8 @@ interface ApiKeysTable {
|
|
|
104
104
|
rate_limit: Generated<number>;
|
|
105
105
|
ip_address: string;
|
|
106
106
|
account_id: string;
|
|
107
|
+
product: Generated<"account" | "streams" | "index">;
|
|
108
|
+
tier: "free" | "build" | "scale" | "enterprise" | null;
|
|
107
109
|
last_used_at: Date | null;
|
|
108
110
|
revoked_at: Date | null;
|
|
109
111
|
created_at: Generated<Date>;
|
|
@@ -104,6 +104,8 @@ interface ApiKeysTable {
|
|
|
104
104
|
rate_limit: Generated<number>;
|
|
105
105
|
ip_address: string;
|
|
106
106
|
account_id: string;
|
|
107
|
+
product: Generated<"account" | "streams" | "index">;
|
|
108
|
+
tier: "free" | "build" | "scale" | "enterprise" | null;
|
|
107
109
|
last_used_at: Date | null;
|
|
108
110
|
revoked_at: Date | null;
|
|
109
111
|
created_at: Generated<Date>;
|
|
@@ -104,6 +104,8 @@ interface ApiKeysTable {
|
|
|
104
104
|
rate_limit: Generated<number>;
|
|
105
105
|
ip_address: string;
|
|
106
106
|
account_id: string;
|
|
107
|
+
product: Generated<"account" | "streams" | "index">;
|
|
108
|
+
tier: "free" | "build" | "scale" | "enterprise" | null;
|
|
107
109
|
last_used_at: Date | null;
|
|
108
110
|
revoked_at: Date | null;
|
|
109
111
|
created_at: Generated<Date>;
|
|
@@ -501,9 +503,17 @@ interface UsageSummary {
|
|
|
501
503
|
}
|
|
502
504
|
/** Get current usage for an account. */
|
|
503
505
|
declare function getUsage(db: Kysely<Database>, accountId: string): Promise<UsageSummary>;
|
|
506
|
+
interface ProductUsageBreakdown {
|
|
507
|
+
streamsEventsToday: number;
|
|
508
|
+
streamsEventsThisMonth: number;
|
|
509
|
+
indexDecodedEventsToday: number;
|
|
510
|
+
indexDecodedEventsThisMonth: number;
|
|
511
|
+
}
|
|
512
|
+
/** Get per-product event counts (today + this month) for an account. */
|
|
513
|
+
declare function getProductUsage(db: Kysely<Database>, accountId: string): Promise<ProductUsageBreakdown>;
|
|
504
514
|
/**
|
|
505
515
|
* Measure storage for all accounts by querying pg_total_relation_size
|
|
506
516
|
* for each tenant's subgraph schemas.
|
|
507
517
|
*/
|
|
508
518
|
declare function measureStorage(db: Kysely<Database>): Promise<void>;
|
|
509
|
-
export { measureStorage, incrementStreamsEventsReturned, incrementIndexDecodedEventsReturned, incrementApiRequests, getUsage, UsageSummary };
|
|
519
|
+
export { measureStorage, incrementStreamsEventsReturned, incrementIndexDecodedEventsReturned, incrementApiRequests, getUsage, getProductUsage, UsageSummary, ProductUsageBreakdown };
|
|
@@ -54,6 +54,21 @@ async function getUsage(db, accountId) {
|
|
|
54
54
|
storageBytes: Number(storageRow?.storage_bytes ?? 0)
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
|
+
async function getProductUsage(db, accountId) {
|
|
58
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
59
|
+
const monthStart = `${today.slice(0, 7)}-01`;
|
|
60
|
+
const dailyRow = await db.selectFrom("usage_daily").select(["streams_events_returned", "index_decoded_events_returned"]).where("account_id", "=", accountId).where("date", "=", today).executeTakeFirst();
|
|
61
|
+
const monthlyRow = await db.selectFrom("usage_daily").select([
|
|
62
|
+
sql`COALESCE(SUM(streams_events_returned), 0)`.as("streams_total"),
|
|
63
|
+
sql`COALESCE(SUM(index_decoded_events_returned), 0)`.as("index_total")
|
|
64
|
+
]).where("account_id", "=", accountId).where("date", ">=", monthStart).executeTakeFirst();
|
|
65
|
+
return {
|
|
66
|
+
streamsEventsToday: Number(dailyRow?.streams_events_returned ?? 0),
|
|
67
|
+
streamsEventsThisMonth: Number(monthlyRow?.streams_total ?? 0),
|
|
68
|
+
indexDecodedEventsToday: Number(dailyRow?.index_decoded_events_returned ?? 0),
|
|
69
|
+
indexDecodedEventsThisMonth: Number(monthlyRow?.index_total ?? 0)
|
|
70
|
+
};
|
|
71
|
+
}
|
|
57
72
|
async function measureStorage(db) {
|
|
58
73
|
const accountSubgraphs = await db.selectFrom("subgraphs").select(["account_id", "schema_name"]).where("schema_name", "is not", null).execute();
|
|
59
74
|
const byAccount = new Map;
|
|
@@ -86,8 +101,9 @@ export {
|
|
|
86
101
|
incrementStreamsEventsReturned,
|
|
87
102
|
incrementIndexDecodedEventsReturned,
|
|
88
103
|
incrementApiRequests,
|
|
89
|
-
getUsage
|
|
104
|
+
getUsage,
|
|
105
|
+
getProductUsage
|
|
90
106
|
};
|
|
91
107
|
|
|
92
|
-
//# debugId=
|
|
108
|
+
//# debugId=4876492CE8C84D6164756E2164756E21
|
|
93
109
|
//# sourceMappingURL=usage.js.map
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/db/queries/usage.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import { type Kysely, sql } from \"kysely\";\nimport type { Database } from \"../types.ts\";\n\n/** Increment API request counter for today. Fire-and-forget safe. */\nexport async function incrementApiRequests(\n\tdb: Kysely<Database>,\n\taccountId: string,\n): Promise<void> {\n\tconst today = new Date().toISOString().slice(0, 10);\n\tawait sql`\n\t\tINSERT INTO usage_daily (account_id, tenant_id, date, api_requests, deliveries)\n\t\tVALUES (${accountId}, NULL, ${today}, 1, 0)\n\t\tON CONFLICT (account_id, date) WHERE tenant_id IS NULL\n\t\tDO UPDATE SET api_requests = usage_daily.api_requests + 1\n\t`.execute(db);\n}\n\nasync function incrementAccountDailyCounter(\n\tdb: Kysely<Database>,\n\taccountId: string,\n\tcolumn: \"streams_events_returned\" | \"index_decoded_events_returned\",\n\tquantity: number,\n): Promise<void> {\n\tif (quantity <= 0) return;\n\tconst today = new Date().toISOString().slice(0, 10);\n\tawait sql`\n\t\tINSERT INTO usage_daily (account_id, tenant_id, date, api_requests, deliveries, ${sql.raw(column)})\n\t\tVALUES (${accountId}, NULL, ${today}, 0, 0, ${quantity})\n\t\tON CONFLICT (account_id, date) WHERE tenant_id IS NULL\n\t\tDO UPDATE SET ${sql.raw(column)} = usage_daily.${sql.raw(column)} + ${quantity}\n\t`.execute(db);\n}\n\nexport async function incrementStreamsEventsReturned(\n\tdb: Kysely<Database>,\n\taccountId: string,\n\tquantity: number,\n): Promise<void> {\n\tawait incrementAccountDailyCounter(\n\t\tdb,\n\t\taccountId,\n\t\t\"streams_events_returned\",\n\t\tquantity,\n\t);\n}\n\nexport async function incrementIndexDecodedEventsReturned(\n\tdb: Kysely<Database>,\n\taccountId: string,\n\tquantity: number,\n): Promise<void> {\n\tawait incrementAccountDailyCounter(\n\t\tdb,\n\t\taccountId,\n\t\t\"index_decoded_events_returned\",\n\t\tquantity,\n\t);\n}\n\nexport interface UsageSummary {\n\tapiRequestsToday: number;\n\tdeliveriesThisMonth: number;\n\tstorageBytes: number;\n}\n\n/** Get current usage for an account. */\nexport async function getUsage(\n\tdb: Kysely<Database>,\n\taccountId: string,\n): Promise<UsageSummary> {\n\tconst today = new Date().toISOString().slice(0, 10);\n\tconst monthStart = `${today.slice(0, 7)}-01`; // YYYY-MM-01\n\n\t// Today's API requests\n\tconst dailyRow = await db\n\t\t.selectFrom(\"usage_daily\")\n\t\t.select(\"api_requests\")\n\t\t.where(\"account_id\", \"=\", accountId)\n\t\t.where(\"date\", \"=\", today)\n\t\t.executeTakeFirst();\n\n\t// This month's deliveries\n\tconst monthlyRow = await db\n\t\t.selectFrom(\"usage_daily\")\n\t\t.select(sql<number>`COALESCE(SUM(deliveries), 0)`.as(\"total\"))\n\t\t.where(\"account_id\", \"=\", accountId)\n\t\t.where(\"date\", \">=\", monthStart)\n\t\t.executeTakeFirst();\n\n\t// Latest storage snapshot\n\tconst storageRow = await db\n\t\t.selectFrom(\"usage_snapshots\")\n\t\t.select(\"storage_bytes\")\n\t\t.where(\"account_id\", \"=\", accountId)\n\t\t.orderBy(\"measured_at\", \"desc\")\n\t\t.limit(1)\n\t\t.executeTakeFirst();\n\n\treturn {\n\t\tapiRequestsToday: dailyRow?.api_requests ?? 0,\n\t\tdeliveriesThisMonth: Number(monthlyRow?.total ?? 0),\n\t\tstorageBytes: Number(storageRow?.storage_bytes ?? 0),\n\t};\n}\n\n/**\n * Measure storage for all accounts by querying pg_total_relation_size\n * for each tenant's subgraph schemas.\n */\nexport async function measureStorage(db: Kysely<Database>): Promise<void> {\n\t// Get all accounts with subgraphs\n\tconst accountSubgraphs = await db\n\t\t.selectFrom(\"subgraphs\")\n\t\t.select([\"account_id\", \"schema_name\"])\n\t\t.where(\"schema_name\", \"is not\", null)\n\t\t.execute();\n\n\t// Group schemas by account\n\tconst byAccount = new Map<string, string[]>();\n\tfor (const row of accountSubgraphs) {\n\t\tconst schemas = byAccount.get(row.account_id) ?? [];\n\t\tif (row.schema_name) schemas.push(row.schema_name);\n\t\tbyAccount.set(row.account_id, schemas);\n\t}\n\n\tfor (const [accountId, schemas] of byAccount) {\n\t\tlet totalBytes = 0;\n\t\tfor (const schema of schemas) {\n\t\t\ttry {\n\t\t\t\tconst result = await sql<{ size: string }>`\n SELECT COALESCE(SUM(pg_total_relation_size(quote_ident(schemaname) || '.' || quote_ident(tablename))), 0)::text as size\n FROM pg_tables WHERE schemaname = ${schema}\n `.execute(db);\n\t\t\t\tconst row = result.rows[0] as { size?: string } | undefined;\n\t\t\t\ttotalBytes += Number(row?.size ?? 0);\n\t\t\t} catch {\n\t\t\t\t// Schema may not exist\n\t\t\t}\n\t\t}\n\n\t\tawait db\n\t\t\t.insertInto(\"usage_snapshots\")\n\t\t\t.values({\n\t\t\t\taccount_id: accountId,\n\t\t\t\tstorage_bytes: totalBytes,\n\t\t\t})\n\t\t\t.execute();\n\t}\n}\n"
|
|
5
|
+
"import { type Kysely, sql } from \"kysely\";\nimport type { Database } from \"../types.ts\";\n\n/** Increment API request counter for today. Fire-and-forget safe. */\nexport async function incrementApiRequests(\n\tdb: Kysely<Database>,\n\taccountId: string,\n): Promise<void> {\n\tconst today = new Date().toISOString().slice(0, 10);\n\tawait sql`\n\t\tINSERT INTO usage_daily (account_id, tenant_id, date, api_requests, deliveries)\n\t\tVALUES (${accountId}, NULL, ${today}, 1, 0)\n\t\tON CONFLICT (account_id, date) WHERE tenant_id IS NULL\n\t\tDO UPDATE SET api_requests = usage_daily.api_requests + 1\n\t`.execute(db);\n}\n\nasync function incrementAccountDailyCounter(\n\tdb: Kysely<Database>,\n\taccountId: string,\n\tcolumn: \"streams_events_returned\" | \"index_decoded_events_returned\",\n\tquantity: number,\n): Promise<void> {\n\tif (quantity <= 0) return;\n\tconst today = new Date().toISOString().slice(0, 10);\n\tawait sql`\n\t\tINSERT INTO usage_daily (account_id, tenant_id, date, api_requests, deliveries, ${sql.raw(column)})\n\t\tVALUES (${accountId}, NULL, ${today}, 0, 0, ${quantity})\n\t\tON CONFLICT (account_id, date) WHERE tenant_id IS NULL\n\t\tDO UPDATE SET ${sql.raw(column)} = usage_daily.${sql.raw(column)} + ${quantity}\n\t`.execute(db);\n}\n\nexport async function incrementStreamsEventsReturned(\n\tdb: Kysely<Database>,\n\taccountId: string,\n\tquantity: number,\n): Promise<void> {\n\tawait incrementAccountDailyCounter(\n\t\tdb,\n\t\taccountId,\n\t\t\"streams_events_returned\",\n\t\tquantity,\n\t);\n}\n\nexport async function incrementIndexDecodedEventsReturned(\n\tdb: Kysely<Database>,\n\taccountId: string,\n\tquantity: number,\n): Promise<void> {\n\tawait incrementAccountDailyCounter(\n\t\tdb,\n\t\taccountId,\n\t\t\"index_decoded_events_returned\",\n\t\tquantity,\n\t);\n}\n\nexport interface UsageSummary {\n\tapiRequestsToday: number;\n\tdeliveriesThisMonth: number;\n\tstorageBytes: number;\n}\n\n/** Get current usage for an account. */\nexport async function getUsage(\n\tdb: Kysely<Database>,\n\taccountId: string,\n): Promise<UsageSummary> {\n\tconst today = new Date().toISOString().slice(0, 10);\n\tconst monthStart = `${today.slice(0, 7)}-01`; // YYYY-MM-01\n\n\t// Today's API requests\n\tconst dailyRow = await db\n\t\t.selectFrom(\"usage_daily\")\n\t\t.select(\"api_requests\")\n\t\t.where(\"account_id\", \"=\", accountId)\n\t\t.where(\"date\", \"=\", today)\n\t\t.executeTakeFirst();\n\n\t// This month's deliveries\n\tconst monthlyRow = await db\n\t\t.selectFrom(\"usage_daily\")\n\t\t.select(sql<number>`COALESCE(SUM(deliveries), 0)`.as(\"total\"))\n\t\t.where(\"account_id\", \"=\", accountId)\n\t\t.where(\"date\", \">=\", monthStart)\n\t\t.executeTakeFirst();\n\n\t// Latest storage snapshot\n\tconst storageRow = await db\n\t\t.selectFrom(\"usage_snapshots\")\n\t\t.select(\"storage_bytes\")\n\t\t.where(\"account_id\", \"=\", accountId)\n\t\t.orderBy(\"measured_at\", \"desc\")\n\t\t.limit(1)\n\t\t.executeTakeFirst();\n\n\treturn {\n\t\tapiRequestsToday: dailyRow?.api_requests ?? 0,\n\t\tdeliveriesThisMonth: Number(monthlyRow?.total ?? 0),\n\t\tstorageBytes: Number(storageRow?.storage_bytes ?? 0),\n\t};\n}\n\nexport interface ProductUsageBreakdown {\n\tstreamsEventsToday: number;\n\tstreamsEventsThisMonth: number;\n\tindexDecodedEventsToday: number;\n\tindexDecodedEventsThisMonth: number;\n}\n\n/** Get per-product event counts (today + this month) for an account. */\nexport async function getProductUsage(\n\tdb: Kysely<Database>,\n\taccountId: string,\n): Promise<ProductUsageBreakdown> {\n\tconst today = new Date().toISOString().slice(0, 10);\n\tconst monthStart = `${today.slice(0, 7)}-01`;\n\n\tconst dailyRow = await db\n\t\t.selectFrom(\"usage_daily\")\n\t\t.select([\"streams_events_returned\", \"index_decoded_events_returned\"])\n\t\t.where(\"account_id\", \"=\", accountId)\n\t\t.where(\"date\", \"=\", today)\n\t\t.executeTakeFirst();\n\n\tconst monthlyRow = await db\n\t\t.selectFrom(\"usage_daily\")\n\t\t.select([\n\t\t\tsql<number>`COALESCE(SUM(streams_events_returned), 0)`.as(\n\t\t\t\t\"streams_total\",\n\t\t\t),\n\t\t\tsql<number>`COALESCE(SUM(index_decoded_events_returned), 0)`.as(\n\t\t\t\t\"index_total\",\n\t\t\t),\n\t\t])\n\t\t.where(\"account_id\", \"=\", accountId)\n\t\t.where(\"date\", \">=\", monthStart)\n\t\t.executeTakeFirst();\n\n\treturn {\n\t\tstreamsEventsToday: Number(dailyRow?.streams_events_returned ?? 0),\n\t\tstreamsEventsThisMonth: Number(monthlyRow?.streams_total ?? 0),\n\t\tindexDecodedEventsToday: Number(\n\t\t\tdailyRow?.index_decoded_events_returned ?? 0,\n\t\t),\n\t\tindexDecodedEventsThisMonth: Number(monthlyRow?.index_total ?? 0),\n\t};\n}\n\n/**\n * Measure storage for all accounts by querying pg_total_relation_size\n * for each tenant's subgraph schemas.\n */\nexport async function measureStorage(db: Kysely<Database>): Promise<void> {\n\t// Get all accounts with subgraphs\n\tconst accountSubgraphs = await db\n\t\t.selectFrom(\"subgraphs\")\n\t\t.select([\"account_id\", \"schema_name\"])\n\t\t.where(\"schema_name\", \"is not\", null)\n\t\t.execute();\n\n\t// Group schemas by account\n\tconst byAccount = new Map<string, string[]>();\n\tfor (const row of accountSubgraphs) {\n\t\tconst schemas = byAccount.get(row.account_id) ?? [];\n\t\tif (row.schema_name) schemas.push(row.schema_name);\n\t\tbyAccount.set(row.account_id, schemas);\n\t}\n\n\tfor (const [accountId, schemas] of byAccount) {\n\t\tlet totalBytes = 0;\n\t\tfor (const schema of schemas) {\n\t\t\ttry {\n\t\t\t\tconst result = await sql<{ size: string }>`\n SELECT COALESCE(SUM(pg_total_relation_size(quote_ident(schemaname) || '.' || quote_ident(tablename))), 0)::text as size\n FROM pg_tables WHERE schemaname = ${schema}\n `.execute(db);\n\t\t\t\tconst row = result.rows[0] as { size?: string } | undefined;\n\t\t\t\ttotalBytes += Number(row?.size ?? 0);\n\t\t\t} catch {\n\t\t\t\t// Schema may not exist\n\t\t\t}\n\t\t}\n\n\t\tawait db\n\t\t\t.insertInto(\"usage_snapshots\")\n\t\t\t.values({\n\t\t\t\taccount_id: accountId,\n\t\t\t\tstorage_bytes: totalBytes,\n\t\t\t})\n\t\t\t.execute();\n\t}\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": "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",
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "4876492CE8C84D6164756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/src/db/schema.d.ts
CHANGED
|
@@ -103,6 +103,8 @@ interface ApiKeysTable {
|
|
|
103
103
|
rate_limit: Generated<number>;
|
|
104
104
|
ip_address: string;
|
|
105
105
|
account_id: string;
|
|
106
|
+
product: Generated<"account" | "streams" | "index">;
|
|
107
|
+
tier: "free" | "build" | "scale" | "enterprise" | null;
|
|
106
108
|
last_used_at: Date | null;
|
|
107
109
|
revoked_at: Date | null;
|
|
108
110
|
created_at: Generated<Date>;
|
package/dist/src/index.d.ts
CHANGED
|
@@ -103,6 +103,8 @@ interface ApiKeysTable {
|
|
|
103
103
|
rate_limit: Generated<number>;
|
|
104
104
|
ip_address: string;
|
|
105
105
|
account_id: string;
|
|
106
|
+
product: Generated<"account" | "streams" | "index">;
|
|
107
|
+
tier: "free" | "build" | "scale" | "enterprise" | null;
|
|
106
108
|
last_used_at: Date | null;
|
|
107
109
|
revoked_at: Date | null;
|
|
108
110
|
created_at: Generated<Date>;
|
|
@@ -104,6 +104,8 @@ interface ApiKeysTable {
|
|
|
104
104
|
rate_limit: Generated<number>;
|
|
105
105
|
ip_address: string;
|
|
106
106
|
account_id: string;
|
|
107
|
+
product: Generated<"account" | "streams" | "index">;
|
|
108
|
+
tier: "free" | "build" | "scale" | "enterprise" | null;
|
|
107
109
|
last_used_at: Date | null;
|
|
108
110
|
revoked_at: Date | null;
|
|
109
111
|
created_at: Generated<Date>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type Kysely, sql } from "kysely";
|
|
2
|
+
|
|
3
|
+
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
4
|
+
await sql`SET lock_timeout = '30s'`.execute(db);
|
|
5
|
+
|
|
6
|
+
await sql`
|
|
7
|
+
ALTER TABLE api_keys
|
|
8
|
+
ADD COLUMN IF NOT EXISTS product text NOT NULL DEFAULT 'account',
|
|
9
|
+
ADD COLUMN IF NOT EXISTS tier text
|
|
10
|
+
`.execute(db);
|
|
11
|
+
|
|
12
|
+
await sql`
|
|
13
|
+
ALTER TABLE api_keys
|
|
14
|
+
ADD CONSTRAINT api_keys_product_check
|
|
15
|
+
CHECK (product IN ('account', 'streams', 'index'))
|
|
16
|
+
`.execute(db);
|
|
17
|
+
|
|
18
|
+
await sql`
|
|
19
|
+
ALTER TABLE api_keys
|
|
20
|
+
ADD CONSTRAINT api_keys_tier_check
|
|
21
|
+
CHECK (tier IS NULL OR tier IN ('free', 'build', 'scale', 'enterprise'))
|
|
22
|
+
`.execute(db);
|
|
23
|
+
|
|
24
|
+
await sql`
|
|
25
|
+
CREATE INDEX IF NOT EXISTS api_keys_product_status_idx
|
|
26
|
+
ON api_keys (product, status)
|
|
27
|
+
WHERE status = 'active'
|
|
28
|
+
`.execute(db);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
32
|
+
await sql`DROP INDEX IF EXISTS api_keys_product_status_idx`.execute(db);
|
|
33
|
+
await sql`
|
|
34
|
+
ALTER TABLE api_keys
|
|
35
|
+
DROP CONSTRAINT IF EXISTS api_keys_product_check,
|
|
36
|
+
DROP CONSTRAINT IF EXISTS api_keys_tier_check
|
|
37
|
+
`.execute(db);
|
|
38
|
+
await sql`
|
|
39
|
+
ALTER TABLE api_keys
|
|
40
|
+
DROP COLUMN IF EXISTS product,
|
|
41
|
+
DROP COLUMN IF EXISTS tier
|
|
42
|
+
`.execute(db);
|
|
43
|
+
}
|