@bluefields/db 0.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/LICENSE +202 -0
- package/dist/client.d.ts +6 -0
- package/dist/client.js +13 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/migrate.d.ts +11 -0
- package/dist/migrate.js +48 -0
- package/dist/migrate.js.map +1 -0
- package/dist/schema/api-keys.d.ts +145 -0
- package/dist/schema/api-keys.js +18 -0
- package/dist/schema/api-keys.js.map +1 -0
- package/dist/schema/audit-log.d.ts +228 -0
- package/dist/schema/audit-log.js +49 -0
- package/dist/schema/audit-log.js.map +1 -0
- package/dist/schema/cost-records.d.ts +190 -0
- package/dist/schema/cost-records.js +55 -0
- package/dist/schema/cost-records.js.map +1 -0
- package/dist/schema/crawl-job-pages.d.ts +186 -0
- package/dist/schema/crawl-job-pages.js +31 -0
- package/dist/schema/crawl-job-pages.js.map +1 -0
- package/dist/schema/crawl-jobs.d.ts +271 -0
- package/dist/schema/crawl-jobs.js +40 -0
- package/dist/schema/crawl-jobs.js.map +1 -0
- package/dist/schema/customers.d.ts +145 -0
- package/dist/schema/customers.js +19 -0
- package/dist/schema/customers.js.map +1 -0
- package/dist/schema/event-feedback.d.ts +170 -0
- package/dist/schema/event-feedback.js +35 -0
- package/dist/schema/event-feedback.js.map +1 -0
- package/dist/schema/events.d.ts +291 -0
- package/dist/schema/events.js +58 -0
- package/dist/schema/events.js.map +1 -0
- package/dist/schema/extractions.d.ts +327 -0
- package/dist/schema/extractions.js +56 -0
- package/dist/schema/extractions.js.map +1 -0
- package/dist/schema/host-extractors.d.ts +131 -0
- package/dist/schema/host-extractors.js +18 -0
- package/dist/schema/host-extractors.js.map +1 -0
- package/dist/schema/host-records.d.ts +237 -0
- package/dist/schema/host-records.js +42 -0
- package/dist/schema/host-records.js.map +1 -0
- package/dist/schema/notification-preferences.d.ts +195 -0
- package/dist/schema/notification-preferences.js +49 -0
- package/dist/schema/notification-preferences.js.map +1 -0
- package/dist/schema/scrape-cache.d.ts +136 -0
- package/dist/schema/scrape-cache.js +23 -0
- package/dist/schema/scrape-cache.js.map +1 -0
- package/dist/schema/sessions.d.ts +153 -0
- package/dist/schema/sessions.js +27 -0
- package/dist/schema/sessions.js.map +1 -0
- package/dist/schema/snapshots.d.ts +382 -0
- package/dist/schema/snapshots.js +60 -0
- package/dist/schema/snapshots.js.map +1 -0
- package/dist/schema/subscriptions.d.ts +604 -0
- package/dist/schema/subscriptions.js +79 -0
- package/dist/schema/subscriptions.js.map +1 -0
- package/dist/schema/usage-periods.d.ts +446 -0
- package/dist/schema/usage-periods.js +71 -0
- package/dist/schema/usage-periods.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +54 -0
- package/src/migrations/0001_initial.sql +398 -0
- package/src/migrations/0002_pg_cron.sql +109 -0
- package/src/migrations/0003_webhook_secret_cleanup.sql +55 -0
- package/src/migrations/0004_events_pg_notify.sql +39 -0
- package/src/migrations/0005_subscription_consecutive_failures.sql +19 -0
- package/src/migrations/0006_notification_preferences.sql +27 -0
- package/src/migrations/0007_crawl_jobs.sql +65 -0
- package/src/migrations/0008_sessions.sql +38 -0
- package/src/migrations/0009_scrape_cache.sql +34 -0
- package/src/migrations/0010_neon_defaults.sql +99 -0
- package/src/migrations/0011_credits_consumed_numeric.sql +12 -0
- package/src/migrations/0012_crawl_page_attestation.sql +10 -0
- package/src/migrations/0013_customer_deleted_at.sql +5 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit log (Review #11): unified record of security-relevant +
|
|
3
|
+
* customer-facing actions. Consolidates what was previously scattered across
|
|
4
|
+
* last_response_body_excerpt (Review #7), event_feedback (Review #5),
|
|
5
|
+
* prompt-injection alerts (Review #10), and routine API request logs.
|
|
6
|
+
*
|
|
7
|
+
* Retention varies by category (set via partition pruning rules):
|
|
8
|
+
* - 'request' / 'tool_call' : 90 days
|
|
9
|
+
* - 'auth' / 'security' / 'admin' : 7 years (compliance)
|
|
10
|
+
* - 'attestation' : indefinite (provenance chain)
|
|
11
|
+
*
|
|
12
|
+
* Customers access their own audit log via `GET /audit-log` with the
|
|
13
|
+
* standard customer_id filter; security/admin entries are internal-only.
|
|
14
|
+
*/
|
|
15
|
+
export declare const auditLog: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
16
|
+
name: "audit_log";
|
|
17
|
+
schema: undefined;
|
|
18
|
+
columns: {
|
|
19
|
+
id: import("drizzle-orm/pg-core").PgColumn<{
|
|
20
|
+
name: "id";
|
|
21
|
+
tableName: "audit_log";
|
|
22
|
+
dataType: "number";
|
|
23
|
+
columnType: "PgBigSerial53";
|
|
24
|
+
data: number;
|
|
25
|
+
driverParam: number;
|
|
26
|
+
notNull: true;
|
|
27
|
+
hasDefault: true;
|
|
28
|
+
isPrimaryKey: false;
|
|
29
|
+
isAutoincrement: false;
|
|
30
|
+
hasRuntimeDefault: false;
|
|
31
|
+
enumValues: undefined;
|
|
32
|
+
baseColumn: never;
|
|
33
|
+
identity: undefined;
|
|
34
|
+
generated: undefined;
|
|
35
|
+
}, {}, {}>;
|
|
36
|
+
customerId: import("drizzle-orm/pg-core").PgColumn<{
|
|
37
|
+
name: "customer_id";
|
|
38
|
+
tableName: "audit_log";
|
|
39
|
+
dataType: "string";
|
|
40
|
+
columnType: "PgUUID";
|
|
41
|
+
data: string;
|
|
42
|
+
driverParam: string;
|
|
43
|
+
notNull: false;
|
|
44
|
+
hasDefault: false;
|
|
45
|
+
isPrimaryKey: false;
|
|
46
|
+
isAutoincrement: false;
|
|
47
|
+
hasRuntimeDefault: false;
|
|
48
|
+
enumValues: undefined;
|
|
49
|
+
baseColumn: never;
|
|
50
|
+
identity: undefined;
|
|
51
|
+
generated: undefined;
|
|
52
|
+
}, {}, {}>;
|
|
53
|
+
actorType: import("drizzle-orm/pg-core").PgColumn<{
|
|
54
|
+
name: "actor_type";
|
|
55
|
+
tableName: "audit_log";
|
|
56
|
+
dataType: "string";
|
|
57
|
+
columnType: "PgText";
|
|
58
|
+
data: string;
|
|
59
|
+
driverParam: string;
|
|
60
|
+
notNull: true;
|
|
61
|
+
hasDefault: false;
|
|
62
|
+
isPrimaryKey: false;
|
|
63
|
+
isAutoincrement: false;
|
|
64
|
+
hasRuntimeDefault: false;
|
|
65
|
+
enumValues: [string, ...string[]];
|
|
66
|
+
baseColumn: never;
|
|
67
|
+
identity: undefined;
|
|
68
|
+
generated: undefined;
|
|
69
|
+
}, {}, {}>;
|
|
70
|
+
actorId: import("drizzle-orm/pg-core").PgColumn<{
|
|
71
|
+
name: "actor_id";
|
|
72
|
+
tableName: "audit_log";
|
|
73
|
+
dataType: "string";
|
|
74
|
+
columnType: "PgText";
|
|
75
|
+
data: string;
|
|
76
|
+
driverParam: string;
|
|
77
|
+
notNull: false;
|
|
78
|
+
hasDefault: false;
|
|
79
|
+
isPrimaryKey: false;
|
|
80
|
+
isAutoincrement: false;
|
|
81
|
+
hasRuntimeDefault: false;
|
|
82
|
+
enumValues: [string, ...string[]];
|
|
83
|
+
baseColumn: never;
|
|
84
|
+
identity: undefined;
|
|
85
|
+
generated: undefined;
|
|
86
|
+
}, {}, {}>;
|
|
87
|
+
category: import("drizzle-orm/pg-core").PgColumn<{
|
|
88
|
+
name: "category";
|
|
89
|
+
tableName: "audit_log";
|
|
90
|
+
dataType: "string";
|
|
91
|
+
columnType: "PgText";
|
|
92
|
+
data: string;
|
|
93
|
+
driverParam: string;
|
|
94
|
+
notNull: true;
|
|
95
|
+
hasDefault: false;
|
|
96
|
+
isPrimaryKey: false;
|
|
97
|
+
isAutoincrement: false;
|
|
98
|
+
hasRuntimeDefault: false;
|
|
99
|
+
enumValues: [string, ...string[]];
|
|
100
|
+
baseColumn: never;
|
|
101
|
+
identity: undefined;
|
|
102
|
+
generated: undefined;
|
|
103
|
+
}, {}, {}>;
|
|
104
|
+
action: import("drizzle-orm/pg-core").PgColumn<{
|
|
105
|
+
name: "action";
|
|
106
|
+
tableName: "audit_log";
|
|
107
|
+
dataType: "string";
|
|
108
|
+
columnType: "PgText";
|
|
109
|
+
data: string;
|
|
110
|
+
driverParam: string;
|
|
111
|
+
notNull: true;
|
|
112
|
+
hasDefault: false;
|
|
113
|
+
isPrimaryKey: false;
|
|
114
|
+
isAutoincrement: false;
|
|
115
|
+
hasRuntimeDefault: false;
|
|
116
|
+
enumValues: [string, ...string[]];
|
|
117
|
+
baseColumn: never;
|
|
118
|
+
identity: undefined;
|
|
119
|
+
generated: undefined;
|
|
120
|
+
}, {}, {}>;
|
|
121
|
+
resourceType: import("drizzle-orm/pg-core").PgColumn<{
|
|
122
|
+
name: "resource_type";
|
|
123
|
+
tableName: "audit_log";
|
|
124
|
+
dataType: "string";
|
|
125
|
+
columnType: "PgText";
|
|
126
|
+
data: string;
|
|
127
|
+
driverParam: string;
|
|
128
|
+
notNull: false;
|
|
129
|
+
hasDefault: false;
|
|
130
|
+
isPrimaryKey: false;
|
|
131
|
+
isAutoincrement: false;
|
|
132
|
+
hasRuntimeDefault: false;
|
|
133
|
+
enumValues: [string, ...string[]];
|
|
134
|
+
baseColumn: never;
|
|
135
|
+
identity: undefined;
|
|
136
|
+
generated: undefined;
|
|
137
|
+
}, {}, {}>;
|
|
138
|
+
resourceId: import("drizzle-orm/pg-core").PgColumn<{
|
|
139
|
+
name: "resource_id";
|
|
140
|
+
tableName: "audit_log";
|
|
141
|
+
dataType: "string";
|
|
142
|
+
columnType: "PgText";
|
|
143
|
+
data: string;
|
|
144
|
+
driverParam: string;
|
|
145
|
+
notNull: false;
|
|
146
|
+
hasDefault: false;
|
|
147
|
+
isPrimaryKey: false;
|
|
148
|
+
isAutoincrement: false;
|
|
149
|
+
hasRuntimeDefault: false;
|
|
150
|
+
enumValues: [string, ...string[]];
|
|
151
|
+
baseColumn: never;
|
|
152
|
+
identity: undefined;
|
|
153
|
+
generated: undefined;
|
|
154
|
+
}, {}, {}>;
|
|
155
|
+
metadata: import("drizzle-orm/pg-core").PgColumn<{
|
|
156
|
+
name: "metadata";
|
|
157
|
+
tableName: "audit_log";
|
|
158
|
+
dataType: "json";
|
|
159
|
+
columnType: "PgJsonb";
|
|
160
|
+
data: unknown;
|
|
161
|
+
driverParam: unknown;
|
|
162
|
+
notNull: false;
|
|
163
|
+
hasDefault: false;
|
|
164
|
+
isPrimaryKey: false;
|
|
165
|
+
isAutoincrement: false;
|
|
166
|
+
hasRuntimeDefault: false;
|
|
167
|
+
enumValues: undefined;
|
|
168
|
+
baseColumn: never;
|
|
169
|
+
identity: undefined;
|
|
170
|
+
generated: undefined;
|
|
171
|
+
}, {}, {}>;
|
|
172
|
+
ipAddress: import("drizzle-orm/pg-core").PgColumn<{
|
|
173
|
+
name: "ip_address";
|
|
174
|
+
tableName: "audit_log";
|
|
175
|
+
dataType: "string";
|
|
176
|
+
columnType: "PgInet";
|
|
177
|
+
data: string;
|
|
178
|
+
driverParam: string;
|
|
179
|
+
notNull: false;
|
|
180
|
+
hasDefault: false;
|
|
181
|
+
isPrimaryKey: false;
|
|
182
|
+
isAutoincrement: false;
|
|
183
|
+
hasRuntimeDefault: false;
|
|
184
|
+
enumValues: undefined;
|
|
185
|
+
baseColumn: never;
|
|
186
|
+
identity: undefined;
|
|
187
|
+
generated: undefined;
|
|
188
|
+
}, {}, {}>;
|
|
189
|
+
userAgent: import("drizzle-orm/pg-core").PgColumn<{
|
|
190
|
+
name: "user_agent";
|
|
191
|
+
tableName: "audit_log";
|
|
192
|
+
dataType: "string";
|
|
193
|
+
columnType: "PgText";
|
|
194
|
+
data: string;
|
|
195
|
+
driverParam: string;
|
|
196
|
+
notNull: false;
|
|
197
|
+
hasDefault: false;
|
|
198
|
+
isPrimaryKey: false;
|
|
199
|
+
isAutoincrement: false;
|
|
200
|
+
hasRuntimeDefault: false;
|
|
201
|
+
enumValues: [string, ...string[]];
|
|
202
|
+
baseColumn: never;
|
|
203
|
+
identity: undefined;
|
|
204
|
+
generated: undefined;
|
|
205
|
+
}, {}, {}>;
|
|
206
|
+
recordedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
207
|
+
name: "recorded_at";
|
|
208
|
+
tableName: "audit_log";
|
|
209
|
+
dataType: "date";
|
|
210
|
+
columnType: "PgTimestamp";
|
|
211
|
+
data: Date;
|
|
212
|
+
driverParam: string;
|
|
213
|
+
notNull: true;
|
|
214
|
+
hasDefault: true;
|
|
215
|
+
isPrimaryKey: false;
|
|
216
|
+
isAutoincrement: false;
|
|
217
|
+
hasRuntimeDefault: false;
|
|
218
|
+
enumValues: undefined;
|
|
219
|
+
baseColumn: never;
|
|
220
|
+
identity: undefined;
|
|
221
|
+
generated: undefined;
|
|
222
|
+
}, {}, {}>;
|
|
223
|
+
};
|
|
224
|
+
dialect: "pg";
|
|
225
|
+
}>;
|
|
226
|
+
export type AuditLog = typeof auditLog.$inferSelect;
|
|
227
|
+
export type NewAuditLog = typeof auditLog.$inferInsert;
|
|
228
|
+
export declare const AUDIT_LOG_PARTITION_COLUMN = "recorded_at";
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import { sql } from 'drizzle-orm';
|
|
3
|
+
import { bigserial, index, inet, jsonb, pgTable, primaryKey, text, timestamp, uuid, } from 'drizzle-orm/pg-core';
|
|
4
|
+
import { customers } from './customers.js';
|
|
5
|
+
/**
|
|
6
|
+
* Audit log (Review #11): unified record of security-relevant +
|
|
7
|
+
* customer-facing actions. Consolidates what was previously scattered across
|
|
8
|
+
* last_response_body_excerpt (Review #7), event_feedback (Review #5),
|
|
9
|
+
* prompt-injection alerts (Review #10), and routine API request logs.
|
|
10
|
+
*
|
|
11
|
+
* Retention varies by category (set via partition pruning rules):
|
|
12
|
+
* - 'request' / 'tool_call' : 90 days
|
|
13
|
+
* - 'auth' / 'security' / 'admin' : 7 years (compliance)
|
|
14
|
+
* - 'attestation' : indefinite (provenance chain)
|
|
15
|
+
*
|
|
16
|
+
* Customers access their own audit log via `GET /audit-log` with the
|
|
17
|
+
* standard customer_id filter; security/admin entries are internal-only.
|
|
18
|
+
*/
|
|
19
|
+
export const auditLog = pgTable('audit_log', {
|
|
20
|
+
id: bigserial('id', { mode: 'number' }).notNull(),
|
|
21
|
+
// nullable for system-level entries
|
|
22
|
+
customerId: uuid('customer_id').references(() => customers.id),
|
|
23
|
+
// 'customer' | 'system' | 'admin' | 'agent_oauth'
|
|
24
|
+
actorType: text('actor_type').notNull(),
|
|
25
|
+
// api_key_id, admin email, OAuth client_id, etc.
|
|
26
|
+
actorId: text('actor_id'),
|
|
27
|
+
// 'request' | 'tool_call' | 'auth' | 'security' | 'admin' | 'attestation'
|
|
28
|
+
category: text('category').notNull(),
|
|
29
|
+
// 'subscription.created' | 'webhook.rotated' | 'oauth.granted' | 'prompt_injection_detected' | ...
|
|
30
|
+
action: text('action').notNull(),
|
|
31
|
+
// 'subscription' | 'snapshot' | 'customer' | 'webhook' | ...
|
|
32
|
+
resourceType: text('resource_type'),
|
|
33
|
+
// the affected resource's ID
|
|
34
|
+
resourceId: text('resource_id'),
|
|
35
|
+
// action-specific details; PII-scrubbed before write
|
|
36
|
+
metadata: jsonb('metadata'),
|
|
37
|
+
ipAddress: inet('ip_address'),
|
|
38
|
+
userAgent: text('user_agent'),
|
|
39
|
+
recordedAt: timestamp('recorded_at', { withTimezone: true }).notNull().defaultNow(),
|
|
40
|
+
}, (table) => [
|
|
41
|
+
primaryKey({ columns: [table.id, table.recordedAt] }),
|
|
42
|
+
index('idx_audit_customer').on(table.customerId, table.recordedAt.desc()),
|
|
43
|
+
index('idx_audit_security')
|
|
44
|
+
.on(table.category, table.recordedAt.desc())
|
|
45
|
+
.where(sql `${table.category} IN ('security', 'auth')`),
|
|
46
|
+
index('idx_audit_actor').on(table.actorType, table.actorId, table.recordedAt.desc()),
|
|
47
|
+
]);
|
|
48
|
+
export const AUDIT_LOG_PARTITION_COLUMN = 'recorded_at';
|
|
49
|
+
//# sourceMappingURL=audit-log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-log.js","sourceRoot":"","sources":["../../src/schema/audit-log.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,EACL,SAAS,EACT,KAAK,EACL,IAAI,EACJ,KAAK,EACL,OAAO,EACP,UAAU,EACV,IAAI,EACJ,SAAS,EACT,IAAI,GACL,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,OAAO,CAC7B,WAAW,EACX;IACE,EAAE,EAAE,SAAS,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE;IACjD,oCAAoC;IACpC,UAAU,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC;IAC9D,kDAAkD;IAClD,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE;IACvC,iDAAiD;IACjD,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC;IACzB,0EAA0E;IAC1E,QAAQ,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE;IACpC,mGAAmG;IACnG,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE;IAChC,6DAA6D;IAC7D,YAAY,EAAE,IAAI,CAAC,eAAe,CAAC;IACnC,6BAA6B;IAC7B,UAAU,EAAE,IAAI,CAAC,aAAa,CAAC;IAC/B,qDAAqD;IACrD,QAAQ,EAAE,KAAK,CAAC,UAAU,CAAC;IAC3B,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC;IAC7B,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC;IAC7B,UAAU,EAAE,SAAS,CAAC,aAAa,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,UAAU,EAAE;CACpF,EACD,CAAC,KAAK,EAAE,EAAE,CAAC;IACT,UAAU,CAAC,EAAE,OAAO,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;IACrD,KAAK,CAAC,oBAAoB,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;IACzE,KAAK,CAAC,oBAAoB,CAAC;SACxB,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;SAC3C,KAAK,CAAC,GAAG,CAAA,GAAG,KAAK,CAAC,QAAQ,0BAA0B,CAAC;IACxD,KAAK,CAAC,iBAAiB,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;CACrF,CACF,CAAC;AAKF,MAAM,CAAC,MAAM,0BAA0B,GAAG,aAAa,CAAC"}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cost tracking — instrumented from day one, partitioned by month on
|
|
3
|
+
* recorded_at. The cost_type enum was expanded in Review #11 to ~16
|
|
4
|
+
* categories covering all cost sources surfaced by Reviews #4-10.
|
|
5
|
+
*
|
|
6
|
+
* cost_type values (Review #11):
|
|
7
|
+
* Fetcher (Review #4):
|
|
8
|
+
* 'fetch_tier0' — HTTP-only with TLS impersonation (cheapest)
|
|
9
|
+
* 'fetch_tier1' — Patchright Chromium
|
|
10
|
+
* 'fetch_tier2' — Camoufox Firefox
|
|
11
|
+
* 'fetch_tier3' — external anti-bot service (Bright Data / ZenRows / Scrapfly)
|
|
12
|
+
* 'fetch_304' — conditional GET 304 Not Modified (Review #9; near-zero cost)
|
|
13
|
+
* 'proxy_bandwidth' — proxy provider bandwidth charge (separate from fetch compute)
|
|
14
|
+
* LLM (Reviews #5, #6):
|
|
15
|
+
* 'llm_extract_haiku' — extraction via Haiku 4.5 (simple-schema path)
|
|
16
|
+
* 'llm_extract_sonnet' — extraction via Sonnet 4.6 (complex-schema path)
|
|
17
|
+
* 'llm_judge' — diff engine LLM judgment (Review #5)
|
|
18
|
+
* Attestation (Review #3):
|
|
19
|
+
* 'kms_sign' — AWS KMS Ed25519 signing operation
|
|
20
|
+
* Delivery (Review #7):
|
|
21
|
+
* 'webhook_deliver' — outbound HTTP POST attempt (per attempt, including retries)
|
|
22
|
+
* Infrastructure:
|
|
23
|
+
* 'inngest_step' — Inngest step execution (Review #1)
|
|
24
|
+
* 'compute' — VPS / worker compute amortized
|
|
25
|
+
* 'storage' — R2 storage + DB storage
|
|
26
|
+
* 'stripe_fee' — Stripe payment processing
|
|
27
|
+
*/
|
|
28
|
+
export declare const costRecords: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
29
|
+
name: "cost_records";
|
|
30
|
+
schema: undefined;
|
|
31
|
+
columns: {
|
|
32
|
+
id: import("drizzle-orm/pg-core").PgColumn<{
|
|
33
|
+
name: "id";
|
|
34
|
+
tableName: "cost_records";
|
|
35
|
+
dataType: "number";
|
|
36
|
+
columnType: "PgBigSerial53";
|
|
37
|
+
data: number;
|
|
38
|
+
driverParam: number;
|
|
39
|
+
notNull: true;
|
|
40
|
+
hasDefault: true;
|
|
41
|
+
isPrimaryKey: false;
|
|
42
|
+
isAutoincrement: false;
|
|
43
|
+
hasRuntimeDefault: false;
|
|
44
|
+
enumValues: undefined;
|
|
45
|
+
baseColumn: never;
|
|
46
|
+
identity: undefined;
|
|
47
|
+
generated: undefined;
|
|
48
|
+
}, {}, {}>;
|
|
49
|
+
customerId: import("drizzle-orm/pg-core").PgColumn<{
|
|
50
|
+
name: "customer_id";
|
|
51
|
+
tableName: "cost_records";
|
|
52
|
+
dataType: "string";
|
|
53
|
+
columnType: "PgUUID";
|
|
54
|
+
data: string;
|
|
55
|
+
driverParam: string;
|
|
56
|
+
notNull: true;
|
|
57
|
+
hasDefault: false;
|
|
58
|
+
isPrimaryKey: false;
|
|
59
|
+
isAutoincrement: false;
|
|
60
|
+
hasRuntimeDefault: false;
|
|
61
|
+
enumValues: undefined;
|
|
62
|
+
baseColumn: never;
|
|
63
|
+
identity: undefined;
|
|
64
|
+
generated: undefined;
|
|
65
|
+
}, {}, {}>;
|
|
66
|
+
subscriptionId: import("drizzle-orm/pg-core").PgColumn<{
|
|
67
|
+
name: "subscription_id";
|
|
68
|
+
tableName: "cost_records";
|
|
69
|
+
dataType: "string";
|
|
70
|
+
columnType: "PgUUID";
|
|
71
|
+
data: string;
|
|
72
|
+
driverParam: string;
|
|
73
|
+
notNull: false;
|
|
74
|
+
hasDefault: false;
|
|
75
|
+
isPrimaryKey: false;
|
|
76
|
+
isAutoincrement: false;
|
|
77
|
+
hasRuntimeDefault: false;
|
|
78
|
+
enumValues: undefined;
|
|
79
|
+
baseColumn: never;
|
|
80
|
+
identity: undefined;
|
|
81
|
+
generated: undefined;
|
|
82
|
+
}, {}, {}>;
|
|
83
|
+
urlCanonical: import("drizzle-orm/pg-core").PgColumn<{
|
|
84
|
+
name: "url_canonical";
|
|
85
|
+
tableName: "cost_records";
|
|
86
|
+
dataType: "string";
|
|
87
|
+
columnType: "PgText";
|
|
88
|
+
data: string;
|
|
89
|
+
driverParam: string;
|
|
90
|
+
notNull: false;
|
|
91
|
+
hasDefault: false;
|
|
92
|
+
isPrimaryKey: false;
|
|
93
|
+
isAutoincrement: false;
|
|
94
|
+
hasRuntimeDefault: false;
|
|
95
|
+
enumValues: [string, ...string[]];
|
|
96
|
+
baseColumn: never;
|
|
97
|
+
identity: undefined;
|
|
98
|
+
generated: undefined;
|
|
99
|
+
}, {}, {}>;
|
|
100
|
+
costType: import("drizzle-orm/pg-core").PgColumn<{
|
|
101
|
+
name: "cost_type";
|
|
102
|
+
tableName: "cost_records";
|
|
103
|
+
dataType: "string";
|
|
104
|
+
columnType: "PgText";
|
|
105
|
+
data: string;
|
|
106
|
+
driverParam: string;
|
|
107
|
+
notNull: true;
|
|
108
|
+
hasDefault: false;
|
|
109
|
+
isPrimaryKey: false;
|
|
110
|
+
isAutoincrement: false;
|
|
111
|
+
hasRuntimeDefault: false;
|
|
112
|
+
enumValues: [string, ...string[]];
|
|
113
|
+
baseColumn: never;
|
|
114
|
+
identity: undefined;
|
|
115
|
+
generated: undefined;
|
|
116
|
+
}, {}, {}>;
|
|
117
|
+
costMicroUsd: import("drizzle-orm/pg-core").PgColumn<{
|
|
118
|
+
name: "cost_micro_usd";
|
|
119
|
+
tableName: "cost_records";
|
|
120
|
+
dataType: "number";
|
|
121
|
+
columnType: "PgInteger";
|
|
122
|
+
data: number;
|
|
123
|
+
driverParam: string | number;
|
|
124
|
+
notNull: true;
|
|
125
|
+
hasDefault: false;
|
|
126
|
+
isPrimaryKey: false;
|
|
127
|
+
isAutoincrement: false;
|
|
128
|
+
hasRuntimeDefault: false;
|
|
129
|
+
enumValues: undefined;
|
|
130
|
+
baseColumn: never;
|
|
131
|
+
identity: undefined;
|
|
132
|
+
generated: undefined;
|
|
133
|
+
}, {}, {}>;
|
|
134
|
+
units: import("drizzle-orm/pg-core").PgColumn<{
|
|
135
|
+
name: "units";
|
|
136
|
+
tableName: "cost_records";
|
|
137
|
+
dataType: "string";
|
|
138
|
+
columnType: "PgNumeric";
|
|
139
|
+
data: string;
|
|
140
|
+
driverParam: string;
|
|
141
|
+
notNull: false;
|
|
142
|
+
hasDefault: false;
|
|
143
|
+
isPrimaryKey: false;
|
|
144
|
+
isAutoincrement: false;
|
|
145
|
+
hasRuntimeDefault: false;
|
|
146
|
+
enumValues: undefined;
|
|
147
|
+
baseColumn: never;
|
|
148
|
+
identity: undefined;
|
|
149
|
+
generated: undefined;
|
|
150
|
+
}, {}, {}>;
|
|
151
|
+
costSubtype: import("drizzle-orm/pg-core").PgColumn<{
|
|
152
|
+
name: "cost_subtype";
|
|
153
|
+
tableName: "cost_records";
|
|
154
|
+
dataType: "string";
|
|
155
|
+
columnType: "PgText";
|
|
156
|
+
data: string;
|
|
157
|
+
driverParam: string;
|
|
158
|
+
notNull: false;
|
|
159
|
+
hasDefault: false;
|
|
160
|
+
isPrimaryKey: false;
|
|
161
|
+
isAutoincrement: false;
|
|
162
|
+
hasRuntimeDefault: false;
|
|
163
|
+
enumValues: [string, ...string[]];
|
|
164
|
+
baseColumn: never;
|
|
165
|
+
identity: undefined;
|
|
166
|
+
generated: undefined;
|
|
167
|
+
}, {}, {}>;
|
|
168
|
+
recordedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
169
|
+
name: "recorded_at";
|
|
170
|
+
tableName: "cost_records";
|
|
171
|
+
dataType: "date";
|
|
172
|
+
columnType: "PgTimestamp";
|
|
173
|
+
data: Date;
|
|
174
|
+
driverParam: string;
|
|
175
|
+
notNull: true;
|
|
176
|
+
hasDefault: true;
|
|
177
|
+
isPrimaryKey: false;
|
|
178
|
+
isAutoincrement: false;
|
|
179
|
+
hasRuntimeDefault: false;
|
|
180
|
+
enumValues: undefined;
|
|
181
|
+
baseColumn: never;
|
|
182
|
+
identity: undefined;
|
|
183
|
+
generated: undefined;
|
|
184
|
+
}, {}, {}>;
|
|
185
|
+
};
|
|
186
|
+
dialect: "pg";
|
|
187
|
+
}>;
|
|
188
|
+
export type CostRecord = typeof costRecords.$inferSelect;
|
|
189
|
+
export type NewCostRecord = typeof costRecords.$inferInsert;
|
|
190
|
+
export declare const COST_RECORDS_PARTITION_COLUMN = "recorded_at";
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import { bigserial, index, integer, numeric, pgTable, primaryKey, text, timestamp, uuid, } from 'drizzle-orm/pg-core';
|
|
3
|
+
import { customers } from './customers.js';
|
|
4
|
+
import { subscriptions } from './subscriptions.js';
|
|
5
|
+
/**
|
|
6
|
+
* Cost tracking — instrumented from day one, partitioned by month on
|
|
7
|
+
* recorded_at. The cost_type enum was expanded in Review #11 to ~16
|
|
8
|
+
* categories covering all cost sources surfaced by Reviews #4-10.
|
|
9
|
+
*
|
|
10
|
+
* cost_type values (Review #11):
|
|
11
|
+
* Fetcher (Review #4):
|
|
12
|
+
* 'fetch_tier0' — HTTP-only with TLS impersonation (cheapest)
|
|
13
|
+
* 'fetch_tier1' — Patchright Chromium
|
|
14
|
+
* 'fetch_tier2' — Camoufox Firefox
|
|
15
|
+
* 'fetch_tier3' — external anti-bot service (Bright Data / ZenRows / Scrapfly)
|
|
16
|
+
* 'fetch_304' — conditional GET 304 Not Modified (Review #9; near-zero cost)
|
|
17
|
+
* 'proxy_bandwidth' — proxy provider bandwidth charge (separate from fetch compute)
|
|
18
|
+
* LLM (Reviews #5, #6):
|
|
19
|
+
* 'llm_extract_haiku' — extraction via Haiku 4.5 (simple-schema path)
|
|
20
|
+
* 'llm_extract_sonnet' — extraction via Sonnet 4.6 (complex-schema path)
|
|
21
|
+
* 'llm_judge' — diff engine LLM judgment (Review #5)
|
|
22
|
+
* Attestation (Review #3):
|
|
23
|
+
* 'kms_sign' — AWS KMS Ed25519 signing operation
|
|
24
|
+
* Delivery (Review #7):
|
|
25
|
+
* 'webhook_deliver' — outbound HTTP POST attempt (per attempt, including retries)
|
|
26
|
+
* Infrastructure:
|
|
27
|
+
* 'inngest_step' — Inngest step execution (Review #1)
|
|
28
|
+
* 'compute' — VPS / worker compute amortized
|
|
29
|
+
* 'storage' — R2 storage + DB storage
|
|
30
|
+
* 'stripe_fee' — Stripe payment processing
|
|
31
|
+
*/
|
|
32
|
+
export const costRecords = pgTable('cost_records', {
|
|
33
|
+
id: bigserial('id', { mode: 'number' }).notNull(),
|
|
34
|
+
customerId: uuid('customer_id')
|
|
35
|
+
.notNull()
|
|
36
|
+
.references(() => customers.id),
|
|
37
|
+
subscriptionId: uuid('subscription_id').references(() => subscriptions.id),
|
|
38
|
+
urlCanonical: text('url_canonical'),
|
|
39
|
+
costType: text('cost_type').notNull(),
|
|
40
|
+
costMicroUsd: integer('cost_micro_usd').notNull(),
|
|
41
|
+
// bytes, tokens, requests, depending on type
|
|
42
|
+
units: numeric('units'),
|
|
43
|
+
// optional refinement: model name, proxy provider, fetcher tier sub-detail (Review #11)
|
|
44
|
+
costSubtype: text('cost_subtype'),
|
|
45
|
+
recordedAt: timestamp('recorded_at', { withTimezone: true }).notNull().defaultNow(),
|
|
46
|
+
}, (table) => [
|
|
47
|
+
// Partition key must be in PK (Postgres requirement for partitioned tables).
|
|
48
|
+
primaryKey({ columns: [table.id, table.recordedAt] }),
|
|
49
|
+
index('idx_cost_customer_time').on(table.customerId, table.recordedAt),
|
|
50
|
+
index('idx_cost_url').on(table.urlCanonical, table.recordedAt),
|
|
51
|
+
// Hot-path index for per-cost-type analytics (Review #11)
|
|
52
|
+
index('idx_cost_type_time').on(table.costType, table.recordedAt),
|
|
53
|
+
]);
|
|
54
|
+
export const COST_RECORDS_PARTITION_COLUMN = 'recorded_at';
|
|
55
|
+
//# sourceMappingURL=cost-records.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cost-records.js","sourceRoot":"","sources":["../../src/schema/cost-records.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,OAAO,EACL,SAAS,EACT,KAAK,EACL,OAAO,EACP,OAAO,EACP,OAAO,EACP,UAAU,EACV,IAAI,EACJ,SAAS,EACT,IAAI,GACL,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,OAAO,CAChC,cAAc,EACd;IACE,EAAE,EAAE,SAAS,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE;IACjD,UAAU,EAAE,IAAI,CAAC,aAAa,CAAC;SAC5B,OAAO,EAAE;SACT,UAAU,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC;IACjC,cAAc,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC;IAC1E,YAAY,EAAE,IAAI,CAAC,eAAe,CAAC;IACnC,QAAQ,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC,OAAO,EAAE;IACrC,YAAY,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC,OAAO,EAAE;IACjD,6CAA6C;IAC7C,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC;IACvB,wFAAwF;IACxF,WAAW,EAAE,IAAI,CAAC,cAAc,CAAC;IACjC,UAAU,EAAE,SAAS,CAAC,aAAa,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,UAAU,EAAE;CACpF,EACD,CAAC,KAAK,EAAE,EAAE,CAAC;IACT,6EAA6E;IAC7E,UAAU,CAAC,EAAE,OAAO,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;IACrD,KAAK,CAAC,wBAAwB,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC;IACtE,KAAK,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,YAAY,EAAE,KAAK,CAAC,UAAU,CAAC;IAC9D,0DAA0D;IAC1D,KAAK,CAAC,oBAAoB,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,UAAU,CAAC;CACjE,CACF,CAAC;AAKF,MAAM,CAAC,MAAM,6BAA6B,GAAG,aAAa,CAAC"}
|