@hatk/hatk 0.0.1-alpha.35 → 0.0.1-alpha.37
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/backfill.js +6 -6
- package/dist/cli.js +56 -13
- package/dist/cloudflare/container.d.ts +73 -0
- package/dist/cloudflare/container.d.ts.map +1 -0
- package/dist/cloudflare/container.js +232 -0
- package/dist/cloudflare/hooks.d.ts +33 -0
- package/dist/cloudflare/hooks.d.ts.map +1 -0
- package/dist/cloudflare/hooks.js +40 -0
- package/dist/cloudflare/init.d.ts +27 -0
- package/dist/cloudflare/init.d.ts.map +1 -0
- package/dist/cloudflare/init.js +103 -0
- package/dist/cloudflare/worker.d.ts +27 -0
- package/dist/cloudflare/worker.d.ts.map +1 -0
- package/dist/cloudflare/worker.js +54 -0
- package/dist/database/adapters/d1.d.ts +56 -0
- package/dist/database/adapters/d1.d.ts.map +1 -0
- package/dist/database/adapters/d1.js +108 -0
- package/dist/database/db.d.ts +8 -0
- package/dist/database/db.d.ts.map +1 -1
- package/dist/database/db.js +54 -5
- package/dist/database/fts.js +1 -1
- package/dist/database/schema.d.ts +1 -0
- package/dist/database/schema.d.ts.map +1 -1
- package/dist/database/schema.js +27 -21
- package/dist/labels.d.ts +1 -1
- package/dist/labels.d.ts.map +1 -1
- package/dist/labels.js +2 -2
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +24 -31
- package/package.json +1 -1
- package/public/admin.html +0 -54
package/dist/database/schema.js
CHANGED
|
@@ -8,36 +8,36 @@ export function toSnakeCase(str) {
|
|
|
8
8
|
function mapType(prop, dialect) {
|
|
9
9
|
if (prop.type === 'string') {
|
|
10
10
|
if (prop.format === 'datetime')
|
|
11
|
-
return { sqlType: dialect.typeMap.timestamp, isRef: false };
|
|
11
|
+
return { sqlType: dialect.typeMap.timestamp, isRef: false, isJson: false };
|
|
12
12
|
if (prop.format === 'at-uri')
|
|
13
|
-
return { sqlType: dialect.typeMap.text, isRef: true };
|
|
14
|
-
return { sqlType: dialect.typeMap.text, isRef: false };
|
|
13
|
+
return { sqlType: dialect.typeMap.text, isRef: true, isJson: false };
|
|
14
|
+
return { sqlType: dialect.typeMap.text, isRef: false, isJson: false };
|
|
15
15
|
}
|
|
16
16
|
if (prop.type === 'integer')
|
|
17
|
-
return { sqlType: dialect.typeMap.integer, isRef: false };
|
|
17
|
+
return { sqlType: dialect.typeMap.integer, isRef: false, isJson: false };
|
|
18
18
|
if (prop.type === 'boolean')
|
|
19
|
-
return { sqlType: dialect.typeMap.boolean, isRef: false };
|
|
19
|
+
return { sqlType: dialect.typeMap.boolean, isRef: false, isJson: false };
|
|
20
20
|
if (prop.type === 'bytes')
|
|
21
|
-
return { sqlType: dialect.typeMap.blob, isRef: false };
|
|
21
|
+
return { sqlType: dialect.typeMap.blob, isRef: false, isJson: false };
|
|
22
22
|
if (prop.type === 'cid-link')
|
|
23
|
-
return { sqlType: dialect.typeMap.text, isRef: false };
|
|
23
|
+
return { sqlType: dialect.typeMap.text, isRef: false, isJson: false };
|
|
24
24
|
if (prop.type === 'array')
|
|
25
|
-
return { sqlType: dialect.jsonType, isRef: false };
|
|
25
|
+
return { sqlType: dialect.jsonType, isRef: false, isJson: true };
|
|
26
26
|
if (prop.type === 'blob')
|
|
27
|
-
return { sqlType: dialect.jsonType, isRef: false };
|
|
27
|
+
return { sqlType: dialect.jsonType, isRef: false, isJson: true };
|
|
28
28
|
if (prop.type === 'union')
|
|
29
|
-
return { sqlType: dialect.jsonType, isRef: false };
|
|
29
|
+
return { sqlType: dialect.jsonType, isRef: false, isJson: true };
|
|
30
30
|
if (prop.type === 'unknown')
|
|
31
|
-
return { sqlType: dialect.jsonType, isRef: false };
|
|
31
|
+
return { sqlType: dialect.jsonType, isRef: false, isJson: true };
|
|
32
32
|
if (prop.type === 'object')
|
|
33
|
-
return { sqlType: dialect.jsonType, isRef: false };
|
|
33
|
+
return { sqlType: dialect.jsonType, isRef: false, isJson: true };
|
|
34
34
|
if (prop.type === 'ref') {
|
|
35
35
|
// strongRef contains { uri, cid } — handled specially in generateTableSchema
|
|
36
36
|
if (prop.ref === 'com.atproto.repo.strongRef')
|
|
37
|
-
return { sqlType: 'STRONG_REF', isRef: true };
|
|
38
|
-
return { sqlType: dialect.jsonType, isRef: false };
|
|
37
|
+
return { sqlType: 'STRONG_REF', isRef: true, isJson: false };
|
|
38
|
+
return { sqlType: dialect.jsonType, isRef: false, isJson: true };
|
|
39
39
|
}
|
|
40
|
-
return { sqlType: dialect.typeMap.text, isRef: false };
|
|
40
|
+
return { sqlType: dialect.typeMap.text, isRef: false, isJson: false };
|
|
41
41
|
}
|
|
42
42
|
// Recursively find all .json files in a directory
|
|
43
43
|
function findJsonFiles(dir) {
|
|
@@ -182,7 +182,7 @@ function resolveUnionBranch(ref, collection, fieldName, defs, lexicons, dialect)
|
|
|
182
182
|
const tableName = `"${collection}__${snakeField}_${branchName}"`;
|
|
183
183
|
const columns = [];
|
|
184
184
|
for (const [propName, prop] of Object.entries(propSource)) {
|
|
185
|
-
const { sqlType, isRef } = mapType(prop, dialect);
|
|
185
|
+
const { sqlType, isRef, isJson } = mapType(prop, dialect);
|
|
186
186
|
// Skip STRONG_REF expansion in branch tables — treat as JSON
|
|
187
187
|
const finalType = sqlType === 'STRONG_REF' ? dialect.jsonType : sqlType;
|
|
188
188
|
columns.push({
|
|
@@ -190,7 +190,8 @@ function resolveUnionBranch(ref, collection, fieldName, defs, lexicons, dialect)
|
|
|
190
190
|
originalName: propName,
|
|
191
191
|
sqlType: finalType,
|
|
192
192
|
notNull: branchRequired.has(propName),
|
|
193
|
-
isRef: finalType !==
|
|
193
|
+
isRef: finalType !== dialect.jsonType && isRef,
|
|
194
|
+
isJson: isJson || sqlType === 'STRONG_REF',
|
|
194
195
|
});
|
|
195
196
|
}
|
|
196
197
|
return { type: fullType, branchName, tableName, columns, isArray, arrayField, wrapperField };
|
|
@@ -229,6 +230,7 @@ export function generateTableSchema(nsid, lexicon, lexicons, dialect = DUCKDB_DI
|
|
|
229
230
|
sqlType: dialect.jsonType,
|
|
230
231
|
notNull: required.has(fieldName),
|
|
231
232
|
isRef: false,
|
|
233
|
+
isJson: true,
|
|
232
234
|
});
|
|
233
235
|
continue;
|
|
234
236
|
}
|
|
@@ -239,13 +241,14 @@ export function generateTableSchema(nsid, lexicon, lexicons, dialect = DUCKDB_DI
|
|
|
239
241
|
const childColumns = [];
|
|
240
242
|
const itemRequired = new Set(p.items?.required || lexicon.defs?.[p.items?.ref?.slice(1)]?.required || []);
|
|
241
243
|
for (const [itemField, itemProp] of Object.entries(itemProps)) {
|
|
242
|
-
const { sqlType, isRef } = mapType(itemProp, dialect);
|
|
244
|
+
const { sqlType, isRef, isJson } = mapType(itemProp, dialect);
|
|
243
245
|
childColumns.push({
|
|
244
246
|
name: toSnakeCase(itemField),
|
|
245
247
|
originalName: itemField,
|
|
246
248
|
sqlType,
|
|
247
249
|
notNull: itemRequired.has(itemField),
|
|
248
250
|
isRef,
|
|
251
|
+
isJson,
|
|
249
252
|
});
|
|
250
253
|
}
|
|
251
254
|
const snakeField = toSnakeCase(fieldName);
|
|
@@ -258,7 +261,7 @@ export function generateTableSchema(nsid, lexicon, lexicons, dialect = DUCKDB_DI
|
|
|
258
261
|
continue;
|
|
259
262
|
}
|
|
260
263
|
}
|
|
261
|
-
const { sqlType, isRef } = mapType(p, dialect);
|
|
264
|
+
const { sqlType, isRef, isJson } = mapType(p, dialect);
|
|
262
265
|
if (sqlType === 'STRONG_REF') {
|
|
263
266
|
// Expand strongRef into two columns: {name}_uri and {name}_cid
|
|
264
267
|
columns.push({
|
|
@@ -267,6 +270,7 @@ export function generateTableSchema(nsid, lexicon, lexicons, dialect = DUCKDB_DI
|
|
|
267
270
|
sqlType: dialect.typeMap.text,
|
|
268
271
|
notNull: required.has(fieldName),
|
|
269
272
|
isRef: true,
|
|
273
|
+
isJson: false,
|
|
270
274
|
});
|
|
271
275
|
columns.push({
|
|
272
276
|
name: toSnakeCase(fieldName) + '_cid',
|
|
@@ -274,6 +278,7 @@ export function generateTableSchema(nsid, lexicon, lexicons, dialect = DUCKDB_DI
|
|
|
274
278
|
sqlType: dialect.typeMap.text,
|
|
275
279
|
notNull: required.has(fieldName),
|
|
276
280
|
isRef: false,
|
|
281
|
+
isJson: false,
|
|
277
282
|
});
|
|
278
283
|
}
|
|
279
284
|
else {
|
|
@@ -283,6 +288,7 @@ export function generateTableSchema(nsid, lexicon, lexicons, dialect = DUCKDB_DI
|
|
|
283
288
|
sqlType,
|
|
284
289
|
notNull: required.has(fieldName),
|
|
285
290
|
isRef,
|
|
291
|
+
isJson,
|
|
286
292
|
});
|
|
287
293
|
}
|
|
288
294
|
}
|
|
@@ -331,7 +337,7 @@ export function generateCreateTableSQL(schema, dialect = DUCKDB_DIALECT) {
|
|
|
331
337
|
childDDL.push(`CREATE INDEX IF NOT EXISTS idx_${childPrefix}_parent ON ${child.tableName}(parent_uri);`);
|
|
332
338
|
childDDL.push(`CREATE INDEX IF NOT EXISTS idx_${childPrefix}_did ON ${child.tableName}(parent_did);`);
|
|
333
339
|
for (const col of child.columns) {
|
|
334
|
-
if (col.
|
|
340
|
+
if (col.isJson || col.sqlType === 'BLOB')
|
|
335
341
|
continue;
|
|
336
342
|
childDDL.push(`CREATE INDEX IF NOT EXISTS idx_${childPrefix}_${col.name} ON ${child.tableName}(${col.name});`);
|
|
337
343
|
}
|
|
@@ -349,7 +355,7 @@ export function generateCreateTableSQL(schema, dialect = DUCKDB_DIALECT) {
|
|
|
349
355
|
childDDL.push(`CREATE INDEX IF NOT EXISTS idx_${branchPrefix}_parent ON ${branch.tableName}(parent_uri);`);
|
|
350
356
|
childDDL.push(`CREATE INDEX IF NOT EXISTS idx_${branchPrefix}_did ON ${branch.tableName}(parent_did);`);
|
|
351
357
|
for (const col of branch.columns) {
|
|
352
|
-
if (col.
|
|
358
|
+
if (col.isJson || col.sqlType === 'BLOB')
|
|
353
359
|
continue;
|
|
354
360
|
childDDL.push(`CREATE INDEX IF NOT EXISTS idx_${branchPrefix}_${col.name} ON ${branch.tableName}(${col.name});`);
|
|
355
361
|
}
|
package/dist/labels.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export interface LabelModule {
|
|
|
17
17
|
definition?: LabelDefinition;
|
|
18
18
|
evaluate?: (ctx: LabelRuleContext) => Promise<string[]>;
|
|
19
19
|
}
|
|
20
|
-
export declare function
|
|
20
|
+
export declare function defineLabel(module: LabelModule): {
|
|
21
21
|
definition?: LabelDefinition;
|
|
22
22
|
evaluate?: (ctx: LabelRuleContext) => Promise<string[]>;
|
|
23
23
|
__type: "labels";
|
package/dist/labels.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"labels.d.ts","sourceRoot":"","sources":["../src/labels.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAIlD,wDAAwD;AACxD,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE;QACF,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;QACtD,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;KACtD,CAAA;IACD,MAAM,EAAE;QACN,GAAG,EAAE,MAAM,CAAA;QACX,GAAG,EAAE,MAAM,CAAA;QACX,GAAG,EAAE,MAAM,CAAA;QACX,UAAU,EAAE,MAAM,CAAA;QAClB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;KAC3B,CAAA;CACF;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,CAAC,EAAE,eAAe,CAAA;IAC5B,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;CACxD;AAED,wBAAgB,
|
|
1
|
+
{"version":3,"file":"labels.d.ts","sourceRoot":"","sources":["../src/labels.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAIlD,wDAAwD;AACxD,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE;QACF,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;QACtD,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;KACtD,CAAA;IACD,MAAM,EAAE;QACN,GAAG,EAAE,MAAM,CAAA;QACX,GAAG,EAAE,MAAM,CAAA;QACX,GAAG,EAAE,MAAM,CAAA;QACX,UAAU,EAAE,MAAM,CAAA;QAClB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;KAC3B,CAAA;CACF;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,CAAC,EAAE,eAAe,CAAA;IAC5B,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;CACxD;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW;iBAJhC,eAAe;eACjB,CAAC,GAAG,EAAE,gBAAgB,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC;;EAKxD;AAYD;;;;;;;;GAQG;AACH,wBAAsB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmCjE;AAED,oEAAoE;AACpE,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE;IAAE,UAAU,CAAC,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;CAAE,GAClG,IAAI,CAON;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE;IAC1C,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAC3B,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBhB;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAuCvG;AAED,yEAAyE;AACzE,wBAAgB,mBAAmB,IAAI,eAAe,EAAE,CAEvD"}
|
package/dist/labels.js
CHANGED
|
@@ -38,7 +38,7 @@ import { resolve } from 'node:path';
|
|
|
38
38
|
import { readdirSync } from 'node:fs';
|
|
39
39
|
import { querySQL, runSQL, insertLabels, getSchema } from "./database/db.js";
|
|
40
40
|
import { log, emit } from "./logger.js";
|
|
41
|
-
export function
|
|
41
|
+
export function defineLabel(module) {
|
|
42
42
|
return { __type: 'labels', ...module };
|
|
43
43
|
}
|
|
44
44
|
const rules = [];
|
|
@@ -144,7 +144,7 @@ export async function rescanLabels(collections) {
|
|
|
144
144
|
let v = row[col.name];
|
|
145
145
|
if (v === null || v === undefined)
|
|
146
146
|
continue;
|
|
147
|
-
if (col.
|
|
147
|
+
if (col.isJson && typeof v === 'string') {
|
|
148
148
|
try {
|
|
149
149
|
v = JSON.parse(v);
|
|
150
150
|
}
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAqDA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AA0B9C;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI,CAwH3F;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,KAAK,EAAE,WAAW,GAAG,IAAI,CAAA;IACzB,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,KAAK,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACxF,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IAC5D,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;CACtB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CA8xB5F;AAGD,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EAAE,EACrB,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,KAAK,EAAE,WAAW,GAAG,IAAI,EACzB,MAAM,GAAE,MAAM,EAAO,EACrB,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,EAC5D,QAAQ,CAAC,EAAE,MAAM,IAAI,GACpB,OAAO,WAAW,EAAE,MAAM,CAG5B"}
|
package/dist/server.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { readFile } from 'node:fs/promises';
|
|
3
3
|
import { join, extname } from 'node:path';
|
|
4
|
-
import { queryRecords, getRecordByUri, searchRecords, getSchema, reshapeRow, setRepoStatus, getRepoStatus, getRepoRetryInfo,
|
|
4
|
+
import { queryRecords, getRecordByUri, searchRecords, getSchema, reshapeRow, setRepoStatus, getRepoStatus, getRepoRetryInfo, queryLabelsForUris, insertLabels, searchAccounts, listReposPaginated, getCollectionCounts, getRepoStatusCounts, getDatabaseSize, deleteLabels, getRecentRecords, listActiveRepoDids, removeRepo, getRepoHandle, getPreferences, putPreference, } from "./database/db.js";
|
|
5
5
|
import { executeFeed, listFeeds } from "./feeds.js";
|
|
6
6
|
import { executeXrpc, InvalidRequestError, NotFoundError, registerCoreXrpcHandler } from "./xrpc.js";
|
|
7
7
|
import { resolveRecords } from "./hydrate.js";
|
|
@@ -377,10 +377,8 @@ export function createHandler(config) {
|
|
|
377
377
|
const { val } = JSON.parse(await request.text());
|
|
378
378
|
if (!val)
|
|
379
379
|
return withCors(jsonError(400, 'Missing val', acceptEncoding));
|
|
380
|
-
const
|
|
381
|
-
|
|
382
|
-
await querySQL(`DELETE FROM _labels WHERE val = $1`, [val]);
|
|
383
|
-
return withCors(json({ deleted: count }, 200, acceptEncoding));
|
|
380
|
+
const deleted = await deleteLabels(val);
|
|
381
|
+
return withCors(json({ deleted }, 200, acceptEncoding));
|
|
384
382
|
}
|
|
385
383
|
// POST /admin/labels/negate — negate a label
|
|
386
384
|
if (url.pathname === '/admin/labels/negate' && request.method === 'POST') {
|
|
@@ -433,10 +431,9 @@ export function createHandler(config) {
|
|
|
433
431
|
const allResults = [];
|
|
434
432
|
for (const col of collections) {
|
|
435
433
|
try {
|
|
436
|
-
const
|
|
437
|
-
if (!
|
|
434
|
+
const rows = await getRecentRecords(col, limit + offset);
|
|
435
|
+
if (!rows.length)
|
|
438
436
|
continue;
|
|
439
|
-
const rows = await querySQL(`SELECT t.* FROM ${schema.tableName} t JOIN _repos r ON t.did = r.did WHERE t.indexed_at > r.backfilled_at ORDER BY t.indexed_at DESC LIMIT $1`, [limit + offset]);
|
|
440
437
|
const uris = rows.map((r) => r.uri);
|
|
441
438
|
const labelsMap = await queryLabelsForUris(uris);
|
|
442
439
|
for (const rec of rows) {
|
|
@@ -515,14 +512,25 @@ export function createHandler(config) {
|
|
|
515
512
|
repoList = dids;
|
|
516
513
|
}
|
|
517
514
|
else {
|
|
518
|
-
|
|
519
|
-
repoList = rows.map((r) => r.did);
|
|
515
|
+
repoList = await listActiveRepoDids();
|
|
520
516
|
}
|
|
517
|
+
const isTargeted = Array.isArray(dids) && dids.length > 0;
|
|
521
518
|
for (const did of repoList) {
|
|
522
519
|
await setRepoStatus(did, 'pending');
|
|
523
520
|
}
|
|
524
|
-
if (
|
|
521
|
+
if (isTargeted) {
|
|
522
|
+
for (const did of repoList) {
|
|
523
|
+
triggerAutoBackfill(did);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
else if (config.onResync) {
|
|
525
527
|
config.onResync();
|
|
528
|
+
}
|
|
529
|
+
else {
|
|
530
|
+
for (const did of repoList) {
|
|
531
|
+
triggerAutoBackfill(did);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
526
534
|
return withCors(json({ resyncing: repoList.length }, 200, acceptEncoding));
|
|
527
535
|
}
|
|
528
536
|
// POST /admin/repos/remove — remove DIDs from tracking
|
|
@@ -534,7 +542,7 @@ export function createHandler(config) {
|
|
|
534
542
|
if (!Array.isArray(dids))
|
|
535
543
|
return withCors(jsonError(400, 'Missing dids array', acceptEncoding));
|
|
536
544
|
for (const did of dids) {
|
|
537
|
-
await
|
|
545
|
+
await removeRepo(did);
|
|
538
546
|
}
|
|
539
547
|
return withCors(json({ removed: dids.length }, 200, acceptEncoding));
|
|
540
548
|
}
|
|
@@ -543,12 +551,8 @@ export function createHandler(config) {
|
|
|
543
551
|
const denied = requireAdmin(viewer, acceptEncoding);
|
|
544
552
|
if (denied)
|
|
545
553
|
return denied;
|
|
546
|
-
const
|
|
547
|
-
const
|
|
548
|
-
for (const row of rows)
|
|
549
|
-
counts[row.status] = Number(row.count);
|
|
550
|
-
const sizeRows = await querySQL(`SELECT database_size, memory_usage, memory_limit FROM pragma_database_size()`);
|
|
551
|
-
const dbInfo = sizeRows[0] ?? {};
|
|
554
|
+
const counts = await getRepoStatusCounts();
|
|
555
|
+
const dbInfo = await getDatabaseSize();
|
|
552
556
|
const collectionCounts = await getCollectionCounts();
|
|
553
557
|
const mem = process.memoryUsage();
|
|
554
558
|
const node = {
|
|
@@ -588,15 +592,6 @@ export function createHandler(config) {
|
|
|
588
592
|
const result = await listReposPaginated({ limit, offset, status, q });
|
|
589
593
|
return withCors(json(result, 200, acceptEncoding));
|
|
590
594
|
}
|
|
591
|
-
// GET /admin/schema — full DuckDB DDL dump + lexicons
|
|
592
|
-
if (url.pathname === '/admin/schema') {
|
|
593
|
-
const denied = requireAdmin(viewer, acceptEncoding);
|
|
594
|
-
if (denied)
|
|
595
|
-
return denied;
|
|
596
|
-
const { getAllLexicons } = await import("./database/schema.js");
|
|
597
|
-
const ddl = await getSchemaDump();
|
|
598
|
-
return withCors(json({ ddl, lexicons: getAllLexicons() }, 200, acceptEncoding));
|
|
599
|
-
}
|
|
600
595
|
// ── Public Repo Endpoints (used by hatk clients for auto-sync) ──
|
|
601
596
|
// POST /repos/add — enqueue DIDs for backfill (public)
|
|
602
597
|
if (url.pathname === '/repos/add' && request.method === 'POST') {
|
|
@@ -642,8 +637,7 @@ export function createHandler(config) {
|
|
|
642
637
|
const did = url.searchParams.get('did');
|
|
643
638
|
if (!did)
|
|
644
639
|
return withCors(jsonError(400, 'did required', acceptEncoding));
|
|
645
|
-
const
|
|
646
|
-
const handle = handleRows[0]?.handle ?? did;
|
|
640
|
+
const handle = await getRepoHandle(did) ?? did;
|
|
647
641
|
const cookieValue = await createSessionCookie({ did, handle });
|
|
648
642
|
const secure = url.protocol === 'https:';
|
|
649
643
|
return new Response(JSON.stringify({ ok: true }), {
|
|
@@ -715,8 +709,7 @@ export function createHandler(config) {
|
|
|
715
709
|
return withCors(jsonError(400, 'Missing code', acceptEncoding));
|
|
716
710
|
const result = await handleCallback(oauth, code, state, iss);
|
|
717
711
|
const isSecure = requestOrigin.startsWith('https');
|
|
718
|
-
const
|
|
719
|
-
const handle = handleRows[0]?.handle ?? result.did;
|
|
712
|
+
const handle = await getRepoHandle(result.did) ?? result.did;
|
|
720
713
|
const cookie = await createSessionCookie({ did: result.did, handle });
|
|
721
714
|
// Server-initiated login stores redirectUri as '/' — redirect cleanly without code/iss params
|
|
722
715
|
const redirectTo = result.clientRedirectUri.startsWith('/?code=') ? '/' : result.clientRedirectUri;
|
package/package.json
CHANGED
package/public/admin.html
CHANGED
|
@@ -783,23 +783,6 @@
|
|
|
783
783
|
font-size: 1rem;
|
|
784
784
|
}
|
|
785
785
|
|
|
786
|
-
/* ── Schema ── */
|
|
787
|
-
.schema-pre {
|
|
788
|
-
font-family: var(--mono);
|
|
789
|
-
font-size: 0.8rem;
|
|
790
|
-
line-height: 1.6;
|
|
791
|
-
padding: 1rem;
|
|
792
|
-
margin: 0;
|
|
793
|
-
background: var(--bg-recessed);
|
|
794
|
-
border-radius: 0 0 6px 6px;
|
|
795
|
-
white-space: pre-wrap;
|
|
796
|
-
word-break: break-word;
|
|
797
|
-
color: var(--text);
|
|
798
|
-
overflow-x: auto;
|
|
799
|
-
}
|
|
800
|
-
.schema-section {
|
|
801
|
-
margin-bottom: 1.5rem;
|
|
802
|
-
}
|
|
803
786
|
.loading {
|
|
804
787
|
color: var(--text-3);
|
|
805
788
|
font-size: 0.9375rem;
|
|
@@ -1220,7 +1203,6 @@
|
|
|
1220
1203
|
<button class="tab active" data-tab="overview">Overview</button>
|
|
1221
1204
|
<button class="tab" data-tab="repos">Repos</button>
|
|
1222
1205
|
<button class="tab" data-tab="content">Content</button>
|
|
1223
|
-
<button class="tab" data-tab="schema">Schema</button>
|
|
1224
1206
|
</nav>
|
|
1225
1207
|
|
|
1226
1208
|
<!-- Overview -->
|
|
@@ -1277,10 +1259,6 @@
|
|
|
1277
1259
|
<div id="repos-results"><div class="loading">Loading</div></div>
|
|
1278
1260
|
</div>
|
|
1279
1261
|
|
|
1280
|
-
<!-- Schema -->
|
|
1281
|
-
<div class="tab-panel" id="panel-schema">
|
|
1282
|
-
<div id="schema-results"><div class="loading">Loading</div></div>
|
|
1283
|
-
</div>
|
|
1284
1262
|
|
|
1285
1263
|
<!-- Content -->
|
|
1286
1264
|
<div class="tab-panel" id="panel-content">
|
|
@@ -1307,7 +1285,6 @@
|
|
|
1307
1285
|
<button class="bnav-btn active" data-tab="overview">Overview</button>
|
|
1308
1286
|
<button class="bnav-btn" data-tab="repos">Repos</button>
|
|
1309
1287
|
<button class="bnav-btn" data-tab="content">Content</button>
|
|
1310
|
-
<button class="bnav-btn" data-tab="schema">Schema</button>
|
|
1311
1288
|
</div>
|
|
1312
1289
|
</div>
|
|
1313
1290
|
</div>
|
|
@@ -1490,7 +1467,6 @@
|
|
|
1490
1467
|
document.getElementById(`panel-${tab}`).classList.add('active')
|
|
1491
1468
|
if (tab === 'overview') loadOverview()
|
|
1492
1469
|
if (tab === 'repos') loadRepos()
|
|
1493
|
-
if (tab === 'schema') loadSchema()
|
|
1494
1470
|
if (tab === 'content') loadContent()
|
|
1495
1471
|
if (push) pushURL({ tab, status: '', q: '', offset: 0, cq: '' })
|
|
1496
1472
|
}
|
|
@@ -1599,36 +1575,6 @@
|
|
|
1599
1575
|
}
|
|
1600
1576
|
})
|
|
1601
1577
|
|
|
1602
|
-
// ── Schema ──
|
|
1603
|
-
|
|
1604
|
-
async function loadSchema() {
|
|
1605
|
-
const container = document.getElementById('schema-results')
|
|
1606
|
-
try {
|
|
1607
|
-
const data = await api('/admin/schema')
|
|
1608
|
-
let html = ''
|
|
1609
|
-
|
|
1610
|
-
// Lexicons section
|
|
1611
|
-
if (data.lexicons && data.lexicons.length) {
|
|
1612
|
-
html += '<div class="schema-section"><div class="section-label">Lexicons</div>'
|
|
1613
|
-
for (const lex of data.lexicons) {
|
|
1614
|
-
html += `<div class="card" style="margin-bottom:0.5rem;"><div style="font-family:var(--mono);font-size:0.8rem;font-weight:600;padding:0.5rem 0.75rem;border-bottom:1px solid var(--border);">${escapeHtml(lex.nsid)}</div><pre class="schema-pre">${escapeHtml(JSON.stringify(lex.lexicon, null, 2))}</pre></div>`
|
|
1615
|
-
}
|
|
1616
|
-
html += '</div>'
|
|
1617
|
-
}
|
|
1618
|
-
|
|
1619
|
-
// DDL section
|
|
1620
|
-
if (data.ddl) {
|
|
1621
|
-
html += '<div class="schema-section"><div class="section-label">Tables (DuckDB DDL)</div>'
|
|
1622
|
-
html += `<div class="card"><pre class="schema-pre">${escapeHtml(data.ddl)}</pre></div>`
|
|
1623
|
-
html += '</div>'
|
|
1624
|
-
}
|
|
1625
|
-
|
|
1626
|
-
container.innerHTML = html || '<div class="empty-state">No schema found</div>'
|
|
1627
|
-
} catch (e) {
|
|
1628
|
-
container.innerHTML = `<div class="empty-state">${escapeHtml(e.message)}</div>`
|
|
1629
|
-
}
|
|
1630
|
-
}
|
|
1631
|
-
|
|
1632
1578
|
// ── Repos ──
|
|
1633
1579
|
|
|
1634
1580
|
let reposLoaded = false
|