@secondlayer/shared 0.7.1 → 0.8.1
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/crypto/hmac.js +2 -2
- package/dist/src/crypto/hmac.js.map +3 -3
- package/dist/src/db/index.d.ts +15 -1
- package/dist/src/db/index.js +5 -3
- package/dist/src/db/index.js.map +4 -4
- package/dist/src/db/jsonb.js.map +2 -2
- package/dist/src/db/queries/accounts.d.ts +12 -0
- package/dist/src/db/queries/accounts.js.map +2 -2
- package/dist/src/db/queries/integrity.d.ts +12 -0
- package/dist/src/db/queries/integrity.js.map +2 -2
- package/dist/src/db/queries/metrics.d.ts +12 -0
- package/dist/src/db/queries/metrics.js.map +2 -2
- package/dist/src/db/queries/subgraph-gaps.d.ts +305 -0
- package/dist/src/db/queries/subgraph-gaps.js +103 -0
- package/dist/src/db/queries/subgraph-gaps.js.map +10 -0
- package/dist/src/db/queries/subgraphs.d.ts +13 -0
- package/dist/src/db/queries/subgraphs.js +4 -2
- package/dist/src/db/queries/subgraphs.js.map +4 -4
- package/dist/src/db/queries/usage.d.ts +12 -0
- package/dist/src/db/queries/usage.js +13 -3
- package/dist/src/db/queries/usage.js.map +4 -4
- package/dist/src/db/schema.d.ts +15 -1
- package/dist/src/env.js.map +2 -2
- package/dist/src/errors.js.map +2 -2
- package/dist/src/index.d.ts +59 -1
- package/dist/src/index.js +12 -8
- package/dist/src/index.js.map +12 -12
- package/dist/src/lib/plans.js.map +2 -2
- package/dist/src/logger.js.map +3 -3
- package/dist/src/node/archive-client.js +22 -6
- package/dist/src/node/archive-client.js.map +5 -5
- package/dist/src/node/client.js.map +2 -2
- package/dist/src/node/hiro-client.js +47 -11
- package/dist/src/node/hiro-client.js.map +5 -5
- package/dist/src/node/hiro-pg-client.js +131 -26
- package/dist/src/node/hiro-pg-client.js.map +3 -3
- package/dist/src/node/local-client.d.ts +12 -0
- package/dist/src/node/local-client.js.map +2 -2
- package/dist/src/queue/index.js +7 -5
- package/dist/src/queue/index.js.map +5 -5
- package/dist/src/queue/listener.js.map +2 -2
- package/dist/src/queue/recovery.js +5 -3
- package/dist/src/queue/recovery.js.map +5 -5
- package/dist/src/schemas/filters.js +2 -2
- package/dist/src/schemas/filters.js.map +3 -3
- package/dist/src/schemas/index.d.ts +45 -1
- package/dist/src/schemas/index.js +5 -3
- package/dist/src/schemas/index.js.map +5 -5
- package/dist/src/schemas/subgraphs.d.ts +45 -1
- package/dist/src/schemas/subgraphs.js.map +2 -2
- package/migrations/0001_initial.ts +295 -159
- package/migrations/0002_api_keys.ts +44 -28
- package/migrations/0003_tenant_isolation.ts +116 -107
- package/migrations/0004_accounts_and_usage.ts +81 -75
- package/migrations/0005_sessions.ts +33 -33
- package/migrations/0006_tx_index.ts +6 -2
- package/migrations/0007_contracts.ts +38 -24
- package/migrations/0008_drop_contracts.ts +33 -19
- package/migrations/0009_waitlist.ts +12 -12
- package/migrations/0010_waitlist_status.ts +5 -5
- package/migrations/0011_account_insights.ts +52 -52
- package/migrations/0012_view_health_snapshots.ts +21 -21
- package/migrations/0013_view_processing_stats.ts +32 -32
- package/migrations/0014_view_table_snapshots.ts +24 -24
- package/migrations/0015_rename_views_to_subgraphs.ts +137 -75
- package/migrations/0016_rename_webhook_to_endpoint.ts +12 -4
- package/migrations/0017_security_hardening.ts +23 -11
- package/migrations/0018_subgraph_gaps.ts +39 -0
- package/package.json +147 -143
|
@@ -1,114 +1,123 @@
|
|
|
1
1
|
import { type Kysely, sql } from "kysely";
|
|
2
2
|
|
|
3
3
|
export async function up(db: Kysely<any>): Promise<void> {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
4
|
+
// 1. Drop PG schemas for views with null api_key_id (orphaned pre-product data)
|
|
5
|
+
const orphanedViews = await db
|
|
6
|
+
.selectFrom("views")
|
|
7
|
+
.select("name")
|
|
8
|
+
.where("api_key_id", "is", null)
|
|
9
|
+
.execute();
|
|
10
|
+
|
|
11
|
+
for (const view of orphanedViews) {
|
|
12
|
+
const schemaName = `view_${view.name.replace(/-/g, "_")}`;
|
|
13
|
+
await sql.raw(`DROP SCHEMA IF EXISTS "${schemaName}" CASCADE`).execute(db);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// 2. Delete orphaned rows (null api_key_id)
|
|
17
|
+
await db.deleteFrom("views").where("api_key_id", "is", null).execute();
|
|
18
|
+
|
|
19
|
+
// Delete stream_metrics for orphaned streams first (FK)
|
|
20
|
+
const orphanedStreamIds = await db
|
|
21
|
+
.selectFrom("streams")
|
|
22
|
+
.select("id")
|
|
23
|
+
.where("api_key_id", "is", null)
|
|
24
|
+
.execute();
|
|
25
|
+
|
|
26
|
+
if (orphanedStreamIds.length > 0) {
|
|
27
|
+
await db
|
|
28
|
+
.deleteFrom("stream_metrics")
|
|
29
|
+
.where(
|
|
30
|
+
"stream_id",
|
|
31
|
+
"in",
|
|
32
|
+
orphanedStreamIds.map((s) => s.id),
|
|
33
|
+
)
|
|
34
|
+
.execute();
|
|
35
|
+
|
|
36
|
+
// Delete jobs + deliveries for orphaned streams
|
|
37
|
+
await db
|
|
38
|
+
.deleteFrom("deliveries")
|
|
39
|
+
.where(
|
|
40
|
+
"stream_id",
|
|
41
|
+
"in",
|
|
42
|
+
orphanedStreamIds.map((s) => s.id),
|
|
43
|
+
)
|
|
44
|
+
.execute();
|
|
45
|
+
|
|
46
|
+
await db
|
|
47
|
+
.deleteFrom("jobs")
|
|
48
|
+
.where(
|
|
49
|
+
"stream_id",
|
|
50
|
+
"in",
|
|
51
|
+
orphanedStreamIds.map((s) => s.id),
|
|
52
|
+
)
|
|
53
|
+
.execute();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
await db.deleteFrom("streams").where("api_key_id", "is", null).execute();
|
|
57
|
+
|
|
58
|
+
// 3. Add schema_name column to views
|
|
59
|
+
await db.schema
|
|
60
|
+
.alterTable("views")
|
|
61
|
+
.addColumn("schema_name", "text")
|
|
62
|
+
.execute();
|
|
63
|
+
|
|
64
|
+
// Backfill schema_name for existing views
|
|
65
|
+
const existingViews = await db
|
|
66
|
+
.selectFrom("views")
|
|
67
|
+
.select(["id", "name"])
|
|
68
|
+
.execute();
|
|
69
|
+
|
|
70
|
+
for (const view of existingViews) {
|
|
71
|
+
const schemaName = `view_${view.name.replace(/-/g, "_")}`;
|
|
72
|
+
await db
|
|
73
|
+
.updateTable("views")
|
|
74
|
+
.set({ schema_name: schemaName })
|
|
75
|
+
.where("id", "=", view.id)
|
|
76
|
+
.execute();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// 4. Drop unique constraint on views(name), replace with views(name, api_key_id)
|
|
80
|
+
// The original unique constraint is from the initial migration on "name"
|
|
81
|
+
await sql
|
|
82
|
+
.raw(`ALTER TABLE views DROP CONSTRAINT IF EXISTS views_name_key`)
|
|
83
|
+
.execute(db);
|
|
84
|
+
await sql
|
|
85
|
+
.raw(`ALTER TABLE views DROP CONSTRAINT IF EXISTS views_name_unique`)
|
|
86
|
+
.execute(db);
|
|
87
|
+
|
|
88
|
+
await db.schema
|
|
89
|
+
.createIndex("views_name_api_key_id_unique")
|
|
90
|
+
.on("views")
|
|
91
|
+
.columns(["name", "api_key_id"])
|
|
92
|
+
.unique()
|
|
93
|
+
.execute();
|
|
94
|
+
|
|
95
|
+
// 5. Add indexes for tenant scoping
|
|
96
|
+
await db.schema
|
|
97
|
+
.createIndex("streams_api_key_id_idx")
|
|
98
|
+
.on("streams")
|
|
99
|
+
.column("api_key_id")
|
|
100
|
+
.execute();
|
|
101
|
+
|
|
102
|
+
await db.schema
|
|
103
|
+
.createIndex("views_api_key_id_idx")
|
|
104
|
+
.on("views")
|
|
105
|
+
.column("api_key_id")
|
|
106
|
+
.execute();
|
|
103
107
|
}
|
|
104
108
|
|
|
105
109
|
export async function down(db: Kysely<any>): Promise<void> {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
110
|
+
await db.schema.dropIndex("views_api_key_id_idx").ifExists().execute();
|
|
111
|
+
await db.schema.dropIndex("streams_api_key_id_idx").ifExists().execute();
|
|
112
|
+
await db.schema
|
|
113
|
+
.dropIndex("views_name_api_key_id_unique")
|
|
114
|
+
.ifExists()
|
|
115
|
+
.execute();
|
|
116
|
+
|
|
117
|
+
// Restore original unique constraint on name
|
|
118
|
+
await sql
|
|
119
|
+
.raw(`ALTER TABLE views ADD CONSTRAINT views_name_key UNIQUE (name)`)
|
|
120
|
+
.execute(db);
|
|
121
|
+
|
|
122
|
+
await db.schema.alterTable("views").dropColumn("schema_name").execute();
|
|
114
123
|
}
|
|
@@ -1,90 +1,96 @@
|
|
|
1
1
|
import { type Kysely, sql } from "kysely";
|
|
2
2
|
|
|
3
3
|
export async function up(db: Kysely<any>): Promise<void> {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
4
|
+
// 1. Create accounts table
|
|
5
|
+
await db.schema
|
|
6
|
+
.createTable("accounts")
|
|
7
|
+
.addColumn("id", "uuid", (col) =>
|
|
8
|
+
col.primaryKey().defaultTo(sql`gen_random_uuid()`),
|
|
9
|
+
)
|
|
10
|
+
.addColumn("email", "text", (col) => col.unique().notNull())
|
|
11
|
+
.addColumn("plan", "text", (col) => col.defaultTo("free").notNull())
|
|
12
|
+
.addColumn("created_at", "timestamptz", (col) =>
|
|
13
|
+
col.defaultTo(sql`NOW()`).notNull(),
|
|
14
|
+
)
|
|
15
|
+
.execute();
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
17
|
+
// 2. Create magic_links table
|
|
18
|
+
await db.schema
|
|
19
|
+
.createTable("magic_links")
|
|
20
|
+
.addColumn("id", "uuid", (col) =>
|
|
21
|
+
col.primaryKey().defaultTo(sql`gen_random_uuid()`),
|
|
22
|
+
)
|
|
23
|
+
.addColumn("email", "text", (col) => col.notNull())
|
|
24
|
+
.addColumn("token", "text", (col) => col.unique().notNull())
|
|
25
|
+
.addColumn("expires_at", "timestamptz", (col) => col.notNull())
|
|
26
|
+
.addColumn("used_at", "timestamptz")
|
|
27
|
+
.addColumn("created_at", "timestamptz", (col) =>
|
|
28
|
+
col.defaultTo(sql`NOW()`).notNull(),
|
|
29
|
+
)
|
|
30
|
+
.execute();
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
32
|
+
// 3. Create usage_daily table
|
|
33
|
+
await db.schema
|
|
34
|
+
.createTable("usage_daily")
|
|
35
|
+
.addColumn("account_id", "uuid", (col) =>
|
|
36
|
+
col.references("accounts.id").onDelete("cascade").notNull(),
|
|
37
|
+
)
|
|
38
|
+
.addColumn("date", "date", (col) => col.notNull())
|
|
39
|
+
.addColumn("api_requests", "integer", (col) => col.defaultTo(0).notNull())
|
|
40
|
+
.addColumn("deliveries", "integer", (col) => col.defaultTo(0).notNull())
|
|
41
|
+
.execute();
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
await sql`ALTER TABLE usage_daily ADD PRIMARY KEY (account_id, date)`.execute(
|
|
44
|
+
db,
|
|
45
|
+
);
|
|
44
46
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
47
|
+
// 4. Create usage_snapshots table
|
|
48
|
+
await db.schema
|
|
49
|
+
.createTable("usage_snapshots")
|
|
50
|
+
.addColumn("id", "uuid", (col) =>
|
|
51
|
+
col.primaryKey().defaultTo(sql`gen_random_uuid()`),
|
|
52
|
+
)
|
|
53
|
+
.addColumn("account_id", "uuid", (col) =>
|
|
54
|
+
col.references("accounts.id").onDelete("cascade").notNull(),
|
|
55
|
+
)
|
|
56
|
+
.addColumn("measured_at", "timestamptz", (col) =>
|
|
57
|
+
col.defaultTo(sql`NOW()`).notNull(),
|
|
58
|
+
)
|
|
59
|
+
.addColumn("storage_bytes", "bigint", (col) => col.defaultTo(0).notNull())
|
|
60
|
+
.execute();
|
|
59
61
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
// 5. Add account_id FK to api_keys
|
|
63
|
+
await db.schema
|
|
64
|
+
.alterTable("api_keys")
|
|
65
|
+
.addColumn("account_id", "uuid", (col) =>
|
|
66
|
+
col.references("accounts.id").onDelete("cascade"),
|
|
67
|
+
)
|
|
68
|
+
.execute();
|
|
67
69
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
+
// 6. Delete orphan api_keys (no account_id)
|
|
71
|
+
await db.deleteFrom("api_keys").where("account_id", "is", null).execute();
|
|
70
72
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
+
// 7. Set NOT NULL on account_id
|
|
74
|
+
await sql`ALTER TABLE api_keys ALTER COLUMN account_id SET NOT NULL`.execute(
|
|
75
|
+
db,
|
|
76
|
+
);
|
|
73
77
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
// 8. Index for account_id lookups
|
|
79
|
+
await db.schema
|
|
80
|
+
.createIndex("api_keys_account_id_idx")
|
|
81
|
+
.on("api_keys")
|
|
82
|
+
.column("account_id")
|
|
83
|
+
.execute();
|
|
80
84
|
}
|
|
81
85
|
|
|
82
86
|
export async function down(db: Kysely<any>): Promise<void> {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
await db.schema.dropIndex("api_keys_account_id_idx").ifExists().execute();
|
|
88
|
+
await sql`ALTER TABLE api_keys ALTER COLUMN account_id DROP NOT NULL`.execute(
|
|
89
|
+
db,
|
|
90
|
+
);
|
|
91
|
+
await db.schema.alterTable("api_keys").dropColumn("account_id").execute();
|
|
92
|
+
await db.schema.dropTable("usage_snapshots").ifExists().execute();
|
|
93
|
+
await db.schema.dropTable("usage_daily").ifExists().execute();
|
|
94
|
+
await db.schema.dropTable("magic_links").ifExists().execute();
|
|
95
|
+
await db.schema.dropTable("accounts").ifExists().execute();
|
|
90
96
|
}
|
|
@@ -1,42 +1,42 @@
|
|
|
1
1
|
import { type Kysely, sql } from "kysely";
|
|
2
2
|
|
|
3
3
|
export async function up(db: Kysely<any>): Promise<void> {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
4
|
+
await db.schema
|
|
5
|
+
.createTable("sessions")
|
|
6
|
+
.addColumn("id", "uuid", (col) =>
|
|
7
|
+
col.primaryKey().defaultTo(sql`gen_random_uuid()`),
|
|
8
|
+
)
|
|
9
|
+
.addColumn("token_hash", "text", (col) => col.unique().notNull())
|
|
10
|
+
.addColumn("token_prefix", "text", (col) => col.notNull())
|
|
11
|
+
.addColumn("account_id", "uuid", (col) =>
|
|
12
|
+
col.references("accounts.id").onDelete("cascade").notNull(),
|
|
13
|
+
)
|
|
14
|
+
.addColumn("ip_address", "text", (col) => col.notNull())
|
|
15
|
+
.addColumn("expires_at", "timestamptz", (col) =>
|
|
16
|
+
col.defaultTo(sql`NOW() + INTERVAL '90 days'`).notNull(),
|
|
17
|
+
)
|
|
18
|
+
.addColumn("revoked_at", "timestamptz")
|
|
19
|
+
.addColumn("last_used_at", "timestamptz")
|
|
20
|
+
.addColumn("created_at", "timestamptz", (col) =>
|
|
21
|
+
col.defaultTo(sql`NOW()`).notNull(),
|
|
22
|
+
)
|
|
23
|
+
.execute();
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
await db.schema
|
|
26
|
+
.createIndex("sessions_token_hash_idx")
|
|
27
|
+
.on("sessions")
|
|
28
|
+
.column("token_hash")
|
|
29
|
+
.execute();
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
await db.schema
|
|
32
|
+
.createIndex("sessions_account_id_idx")
|
|
33
|
+
.on("sessions")
|
|
34
|
+
.column("account_id")
|
|
35
|
+
.execute();
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
export async function down(db: Kysely<any>): Promise<void> {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
await db.schema.dropIndex("sessions_account_id_idx").ifExists().execute();
|
|
40
|
+
await db.schema.dropIndex("sessions_token_hash_idx").ifExists().execute();
|
|
41
|
+
await db.schema.dropTable("sessions").ifExists().execute();
|
|
42
42
|
}
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { type Kysely, sql } from "kysely";
|
|
2
2
|
|
|
3
3
|
export async function up(db: Kysely<any>): Promise<void> {
|
|
4
|
-
|
|
4
|
+
await sql`ALTER TABLE transactions ADD COLUMN IF NOT EXISTS tx_index INTEGER NOT NULL DEFAULT 0`.execute(
|
|
5
|
+
db,
|
|
6
|
+
);
|
|
5
7
|
}
|
|
6
8
|
|
|
7
9
|
export async function down(db: Kysely<any>): Promise<void> {
|
|
8
|
-
|
|
10
|
+
await sql`ALTER TABLE transactions DROP COLUMN IF EXISTS tx_index`.execute(
|
|
11
|
+
db,
|
|
12
|
+
);
|
|
9
13
|
}
|
|
@@ -1,30 +1,44 @@
|
|
|
1
1
|
import { type Kysely, sql } from "kysely";
|
|
2
2
|
|
|
3
3
|
export async function up(db: Kysely<any>): Promise<void> {
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
// Enable pg_trgm for fast ILIKE search
|
|
5
|
+
await sql`CREATE EXTENSION IF NOT EXISTS pg_trgm`.execute(db);
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
7
|
+
await db.schema
|
|
8
|
+
.createTable("contracts")
|
|
9
|
+
.addColumn("contract_id", "text", (c) => c.primaryKey())
|
|
10
|
+
.addColumn("name", "text", (c) => c.notNull())
|
|
11
|
+
.addColumn("deployer", "text", (c) => c.notNull())
|
|
12
|
+
.addColumn("deploy_block", "integer", (c) => c.notNull())
|
|
13
|
+
.addColumn("deploy_tx_id", "text", (c) => c.notNull())
|
|
14
|
+
.addColumn("call_count", "integer", (c) => c.notNull().defaultTo(0))
|
|
15
|
+
.addColumn("last_called_at", "timestamptz")
|
|
16
|
+
.addColumn("abi", "jsonb")
|
|
17
|
+
.addColumn("abi_fetched_at", "timestamptz")
|
|
18
|
+
.addColumn("created_at", "timestamptz", (c) =>
|
|
19
|
+
c.notNull().defaultTo(sql`NOW()`),
|
|
20
|
+
)
|
|
21
|
+
.addColumn("updated_at", "timestamptz", (c) =>
|
|
22
|
+
c.notNull().defaultTo(sql`NOW()`),
|
|
23
|
+
)
|
|
24
|
+
.execute();
|
|
21
25
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
await db.schema
|
|
27
|
+
.createIndex("contracts_name_idx")
|
|
28
|
+
.on("contracts")
|
|
29
|
+
.column("name")
|
|
30
|
+
.execute();
|
|
31
|
+
await db.schema
|
|
32
|
+
.createIndex("contracts_deployer_idx")
|
|
33
|
+
.on("contracts")
|
|
34
|
+
.column("deployer")
|
|
35
|
+
.execute();
|
|
36
|
+
await sql`CREATE INDEX contracts_name_trgm_idx ON contracts USING gin(name gin_trgm_ops)`.execute(
|
|
37
|
+
db,
|
|
38
|
+
);
|
|
25
39
|
|
|
26
|
-
|
|
27
|
-
|
|
40
|
+
// Backfill step 1: insert deployed contracts from transactions
|
|
41
|
+
await sql`
|
|
28
42
|
INSERT INTO contracts (contract_id, name, deployer, deploy_block, deploy_tx_id, created_at)
|
|
29
43
|
SELECT DISTINCT ON (contract_id)
|
|
30
44
|
contract_id,
|
|
@@ -39,8 +53,8 @@ export async function up(db: Kysely<any>): Promise<void> {
|
|
|
39
53
|
ON CONFLICT (contract_id) DO NOTHING
|
|
40
54
|
`.execute(db);
|
|
41
55
|
|
|
42
|
-
|
|
43
|
-
|
|
56
|
+
// Backfill step 2: update call counts from contract_call transactions
|
|
57
|
+
await sql`
|
|
44
58
|
UPDATE contracts c
|
|
45
59
|
SET call_count = sub.cnt, last_called_at = sub.last_call
|
|
46
60
|
FROM (
|
|
@@ -54,5 +68,5 @@ export async function up(db: Kysely<any>): Promise<void> {
|
|
|
54
68
|
}
|
|
55
69
|
|
|
56
70
|
export async function down(db: Kysely<any>): Promise<void> {
|
|
57
|
-
|
|
71
|
+
await db.schema.dropTable("contracts").ifExists().cascade().execute();
|
|
58
72
|
}
|
|
@@ -1,28 +1,42 @@
|
|
|
1
1
|
import { type Kysely, sql } from "kysely";
|
|
2
2
|
|
|
3
3
|
export async function up(db: Kysely<any>): Promise<void> {
|
|
4
|
-
|
|
4
|
+
await db.schema.dropTable("contracts").ifExists().cascade().execute();
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
export async function down(db: Kysely<any>): Promise<void> {
|
|
8
|
-
|
|
8
|
+
await sql`CREATE EXTENSION IF NOT EXISTS pg_trgm`.execute(db);
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
10
|
+
await db.schema
|
|
11
|
+
.createTable("contracts")
|
|
12
|
+
.addColumn("contract_id", "text", (c) => c.primaryKey())
|
|
13
|
+
.addColumn("name", "text", (c) => c.notNull())
|
|
14
|
+
.addColumn("deployer", "text", (c) => c.notNull())
|
|
15
|
+
.addColumn("deploy_block", "integer", (c) => c.notNull())
|
|
16
|
+
.addColumn("deploy_tx_id", "text", (c) => c.notNull())
|
|
17
|
+
.addColumn("call_count", "integer", (c) => c.notNull().defaultTo(0))
|
|
18
|
+
.addColumn("last_called_at", "timestamptz")
|
|
19
|
+
.addColumn("abi", "jsonb")
|
|
20
|
+
.addColumn("abi_fetched_at", "timestamptz")
|
|
21
|
+
.addColumn("created_at", "timestamptz", (c) =>
|
|
22
|
+
c.notNull().defaultTo(sql`NOW()`),
|
|
23
|
+
)
|
|
24
|
+
.addColumn("updated_at", "timestamptz", (c) =>
|
|
25
|
+
c.notNull().defaultTo(sql`NOW()`),
|
|
26
|
+
)
|
|
27
|
+
.execute();
|
|
24
28
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
29
|
+
await db.schema
|
|
30
|
+
.createIndex("contracts_name_idx")
|
|
31
|
+
.on("contracts")
|
|
32
|
+
.column("name")
|
|
33
|
+
.execute();
|
|
34
|
+
await db.schema
|
|
35
|
+
.createIndex("contracts_deployer_idx")
|
|
36
|
+
.on("contracts")
|
|
37
|
+
.column("deployer")
|
|
38
|
+
.execute();
|
|
39
|
+
await sql`CREATE INDEX contracts_name_trgm_idx ON contracts USING gin(name gin_trgm_ops)`.execute(
|
|
40
|
+
db,
|
|
41
|
+
);
|
|
28
42
|
}
|