@hatk/hatk 0.0.1-alpha.96 → 0.0.1-alpha.98
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.d.ts +4 -1
- package/dist/backfill.d.ts.map +1 -1
- package/dist/backfill.js +17 -5
- package/dist/cli.js +0 -1
- package/dist/config.d.ts +1 -14
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +0 -11
- package/dist/database/db.d.ts +13 -64
- package/dist/database/db.d.ts.map +1 -1
- package/dist/database/db.js +27 -235
- package/dist/indexer.d.ts +18 -0
- package/dist/indexer.d.ts.map +1 -1
- package/dist/indexer.js +65 -1
- package/dist/jetstream.d.ts.map +1 -1
- package/dist/jetstream.js +13 -3
- package/dist/main.js +0 -4
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +1 -89
- package/package.json +2 -3
- package/public/admin.html +0 -467
- package/dist/classifiers.d.ts +0 -168
- package/dist/classifiers.d.ts.map +0 -1
- package/dist/classifiers.js +0 -370
package/dist/indexer.js
CHANGED
|
@@ -4,7 +4,7 @@ import { rkeyOf } from "./spaces/uri.js";
|
|
|
4
4
|
import { cborDecode } from "./cbor.js";
|
|
5
5
|
import { parseCarFrame } from "./car.js";
|
|
6
6
|
import { isPrivateCollection } from "./private-collections.js";
|
|
7
|
-
import { insertRecord, deleteRecord, setCursor, setRepoStatus, getRepoRetryInfo, listAllRepoStatuses, getDatabasePort, updateRepoHandle, } from "./database/db.js";
|
|
7
|
+
import { insertRecord, deleteRecord, setCursor, setRepoStatus, getRepoStatus, purgeRepoRecords, getRepoRetryInfo, listAllRepoStatuses, getDatabasePort, updateRepoHandle, } from "./database/db.js";
|
|
8
8
|
import { backfillRepo } from "./backfill.js";
|
|
9
9
|
import { rebuildAllIndexes } from "./database/fts.js";
|
|
10
10
|
import { log, emit, timer } from "./logger.js";
|
|
@@ -649,6 +649,61 @@ export async function handleIdentityEvent(did, payloadHandle) {
|
|
|
649
649
|
});
|
|
650
650
|
}
|
|
651
651
|
}
|
|
652
|
+
/**
|
|
653
|
+
* Handle an `#account` event: the network saying an account is active again,
|
|
654
|
+
* or is not — deactivated, suspended, taken down by its host, or deleted.
|
|
655
|
+
*
|
|
656
|
+
* - deleted: every record it holds here is removed, and it is marked
|
|
657
|
+
* `deleted`.
|
|
658
|
+
* - deactivated, suspended, taken down by its host: marked `deactivated`. Its
|
|
659
|
+
* records stay, and no read serves them (see HIDDEN_REPO_STATUSES), so an
|
|
660
|
+
* account that comes back is whole again at once.
|
|
661
|
+
* - active again, after either: marked `pending` and backfilled, since it may
|
|
662
|
+
* have changed while it was gone.
|
|
663
|
+
*
|
|
664
|
+
* An admin's own takedown here wins over all of it: only reverse-takedown
|
|
665
|
+
* lifts that. Statuses that say nothing about the account itself (a relay's
|
|
666
|
+
* `desynchronized` or `throttled`) change nothing. Like identity events, only
|
|
667
|
+
* DIDs already tracked are touched.
|
|
668
|
+
*/
|
|
669
|
+
export async function handleAccountEvent(did, active, status) {
|
|
670
|
+
if (!repoStatusCache.has(did))
|
|
671
|
+
return;
|
|
672
|
+
try {
|
|
673
|
+
const current = await getRepoStatus(did);
|
|
674
|
+
if (current === null || current === 'takendown')
|
|
675
|
+
return;
|
|
676
|
+
if (!active && status === 'deleted') {
|
|
677
|
+
const removed = await purgeRepoRecords(did);
|
|
678
|
+
await setRepoStatus(did, 'deleted');
|
|
679
|
+
repoStatusCache.set(did, 'deleted');
|
|
680
|
+
emit('indexer', 'account_deleted', { did, removed });
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
if (!active && status && INACTIVE_ACCOUNT_STATUSES.has(status)) {
|
|
684
|
+
if (current === 'deactivated')
|
|
685
|
+
return;
|
|
686
|
+
await setRepoStatus(did, 'deactivated');
|
|
687
|
+
repoStatusCache.set(did, 'deactivated');
|
|
688
|
+
emit('indexer', 'account_deactivated', { did, status });
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
if (active && (current === 'deactivated' || current === 'deleted')) {
|
|
692
|
+
await setRepoStatus(did, 'pending');
|
|
693
|
+
repoStatusCache.set(did, 'pending');
|
|
694
|
+
emit('indexer', 'account_reactivated', { did, was: current });
|
|
695
|
+
void triggerAutoBackfill(did);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
catch (err) {
|
|
699
|
+
emit('indexer', 'account_event_error', {
|
|
700
|
+
did,
|
|
701
|
+
error: err instanceof Error ? err.message : String(err),
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
/** `#account` statuses that mean the account itself is not there. */
|
|
706
|
+
const INACTIVE_ACCOUNT_STATUSES = new Set(['deactivated', 'suspended', 'takendown']);
|
|
652
707
|
/**
|
|
653
708
|
* Whether a collection's records may be written from network data.
|
|
654
709
|
*
|
|
@@ -751,6 +806,15 @@ export function processMessage(bytes, collections, onSeq = noteSeq) {
|
|
|
751
806
|
handleIdentityEvent(did, handle);
|
|
752
807
|
return;
|
|
753
808
|
}
|
|
809
|
+
// Account status changes (deactivated, deleted, back again). Fire-and-forget,
|
|
810
|
+
// like identity.
|
|
811
|
+
if (header.value.t === '#account') {
|
|
812
|
+
const did = typeof body.value.did === 'string' ? body.value.did : undefined;
|
|
813
|
+
const status = typeof body.value.status === 'string' ? body.value.status : undefined;
|
|
814
|
+
if (did)
|
|
815
|
+
void handleAccountEvent(did, body.value.active === true, status);
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
754
818
|
if (header.value.op !== 1 || header.value.t !== '#commit')
|
|
755
819
|
return;
|
|
756
820
|
if (!body.value.blocks || !body.value.ops)
|
package/dist/jetstream.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jetstream.d.ts","sourceRoot":"","sources":["../src/jetstream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"jetstream.d.ts","sourceRoot":"","sources":["../src/jetstream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAWL,KAAK,QAAQ,EACb,KAAK,eAAe,EACrB,MAAM,cAAc,CAAA;AAGrB,sEAAsE;AACtE,eAAO,MAAM,eAAe,MAAM,CAAA;AAClC,eAAO,MAAM,QAAQ,QAAS,CAAA;AAU9B;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,kBAAkB,IAAI,CAAA;AAEnC,MAAM,WAAW,aAAc,SAAQ,eAAe;IACpD,sEAAsE;IACtE,YAAY,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACvB;AAED,wDAAwD;AACxD,UAAU,eAAe;IACvB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAC7B;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,EACxB,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,EAC/B,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,GACrB,MAAM,CASR;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,GAAG,IAAI,CAalG;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,eAAe,EAAE,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,QAAQ,GAAG,IAAI,CAY7F;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CA8BzE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GAAG,IAAI,EACtB,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACpC,MAAM,GAAG,IAAI,GAAG,SAAS,CAK3B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,aAAa,EAAE,QAAQ,SAAI,GAAG,OAAO,CAAC,SAAS,CAAC,CA0DjG"}
|
package/dist/jetstream.js
CHANGED
|
@@ -14,13 +14,16 @@
|
|
|
14
14
|
* @see https://bsky.network/docs/jetstream
|
|
15
15
|
* @module
|
|
16
16
|
*/
|
|
17
|
-
import { applyCommit, configureIndexer, getLastSeq, handleIdentityEvent, isIndexableCollection, jetstreamCursorKey, noteSeq, resumeCursor, setCursorKey, } from "./indexer.js";
|
|
17
|
+
import { applyCommit, configureIndexer, getLastSeq, handleIdentityEvent, handleAccountEvent, isIndexableCollection, jetstreamCursorKey, noteSeq, resumeCursor, setCursorKey, } from "./indexer.js";
|
|
18
18
|
import { log, emit } from "./logger.js";
|
|
19
19
|
/** Server-side filter caps, rejected before the WebSocket upgrade. */
|
|
20
20
|
export const MAX_COLLECTIONS = 100;
|
|
21
21
|
export const MAX_DIDS = 10_000;
|
|
22
|
-
/**
|
|
23
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Event kinds hatk consumes. `commit` carries records; `identity` drives handle
|
|
24
|
+
* renames; `account` says an account was deactivated, deleted, or came back.
|
|
25
|
+
*/
|
|
26
|
+
const KINDS = ['commit', 'identity', 'account'];
|
|
24
27
|
const RECONNECT_DELAY_MS = 3000;
|
|
25
28
|
/**
|
|
26
29
|
* Refused handshakes before the next attempt drops its cursor and probes live.
|
|
@@ -118,6 +121,13 @@ export function processEvent(payload, collections) {
|
|
|
118
121
|
handleIdentityEvent(did, handle);
|
|
119
122
|
return;
|
|
120
123
|
}
|
|
124
|
+
if (kind === 'account') {
|
|
125
|
+
const did = typeof payload.did === 'string' ? payload.did : undefined;
|
|
126
|
+
const status = typeof payload.status === 'string' ? payload.status : undefined;
|
|
127
|
+
if (did)
|
|
128
|
+
void handleAccountEvent(did, payload.active === true, status);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
121
131
|
if (kind !== 'commit')
|
|
122
132
|
return;
|
|
123
133
|
// Track sequence for the cursor before filtering: a stream of events for
|
package/dist/main.js
CHANGED
|
@@ -22,7 +22,6 @@ import { initFeeds, listFeeds } from "./feeds.js";
|
|
|
22
22
|
import { initXrpc, listXrpc, configureRelay, configureCdn, configureOAuth, callXrpc } from "./xrpc.js";
|
|
23
23
|
import { initOpengraph } from "./opengraph.js";
|
|
24
24
|
import { initLabels, getLabelDefinitions } from "./labels.js";
|
|
25
|
-
import { initClassifiers, listClassifiers, configureSystemOne } from "./classifiers.js";
|
|
26
25
|
import { auxCursorKey, jetstreamCursorKey, startAuxIndexer, startIndexer, sweepReferences } from "./indexer.js";
|
|
27
26
|
import { startJetstreamIndexer } from "./jetstream.js";
|
|
28
27
|
import { rebuildAllIndexes } from "./database/fts.js";
|
|
@@ -109,11 +108,8 @@ else {
|
|
|
109
108
|
log(`[main] OpenGraph initialized`);
|
|
110
109
|
await initLabels(resolve(configDir, 'labels'));
|
|
111
110
|
log(`[main] Labels initialized: ${getLabelDefinitions().length} definitions`);
|
|
112
|
-
await initClassifiers(resolve(configDir, 'classifiers'));
|
|
113
|
-
log(`[main] Classifiers initialized: ${listClassifiers().join(', ') || 'none'}`);
|
|
114
111
|
}
|
|
115
112
|
// Register built-in dev.hatk.* handlers so callXrpc() can find them
|
|
116
|
-
configureSystemOne(config.typesafe);
|
|
117
113
|
setPrivateCollections(config.privateCollections);
|
|
118
114
|
registerCoreHandlers(collections, config.oauth);
|
|
119
115
|
configureOAuth(config.oauth);
|
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":"AAsEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAuE9C;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI,CA2M3F;AAgDD,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,mFAAmF;IACnF,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;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;IACrB,mFAAmF;IACnF,MAAM,CAAC,EAAE,kBAAkB,CAAA;CAC5B;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CA0qC5F;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
|
@@ -3,8 +3,7 @@ import { existsSync } from 'node:fs';
|
|
|
3
3
|
import { readFile } from 'node:fs/promises';
|
|
4
4
|
import { join, extname } from 'node:path';
|
|
5
5
|
import { gzipSync } from 'node:zlib';
|
|
6
|
-
import { queryRecords, getRecordByUri, searchRecords, getSchema, reshapeRow, setRepoStatus, getRepoStatus, getRepoRetryInfo, queryLabelsForUris, insertLabels, searchAccounts, listReposPaginated, getCollectionCounts, getRepoStatusCounts, getDatabaseSize, deleteLabels, getRecentRecords, listActiveRepoDids, removeRepo, getRepoHandle, getPreferences, putPreference, insertReport, queryReports, resolveReport, getOpenReportCount,
|
|
7
|
-
import { runScan, getScanProgress, listClassifiers, isSystemOneConfigured } from "./classifiers.js";
|
|
6
|
+
import { queryRecords, getRecordByUri, searchRecords, getSchema, reshapeRow, setRepoStatus, getRepoStatus, getRepoRetryInfo, queryLabelsForUris, insertLabels, searchAccounts, listReposPaginated, getCollectionCounts, getRepoStatusCounts, getDatabaseSize, deleteLabels, getRecentRecords, listActiveRepoDids, removeRepo, getRepoHandle, getPreferences, putPreference, insertReport, queryReports, resolveReport, getOpenReportCount, } from "./database/db.js";
|
|
8
7
|
import { executeFeed, listFeeds } from "./feeds.js";
|
|
9
8
|
import { executeXrpc, InvalidRequestError, NotFoundError, registerCoreXrpcHandler, isLocalRelay, paramsFromSearch } from "./xrpc.js";
|
|
10
9
|
import { pdsFor } from "./backfill.js";
|
|
@@ -826,93 +825,6 @@ export function createHandler(config) {
|
|
|
826
825
|
invalidateAdminStats();
|
|
827
826
|
return withCors(json({ ok: true }, 200, acceptEncoding));
|
|
828
827
|
}
|
|
829
|
-
// GET /admin/review — the classifier review queue
|
|
830
|
-
if (url.pathname === '/admin/review' && request.method === 'GET') {
|
|
831
|
-
const denied = requireAdmin(viewer, acceptEncoding);
|
|
832
|
-
if (denied)
|
|
833
|
-
return denied;
|
|
834
|
-
const result = await queryClassifications({
|
|
835
|
-
classifier: url.searchParams.get('classifier') || undefined,
|
|
836
|
-
signal: url.searchParams.get('signal') || undefined,
|
|
837
|
-
minScore: url.searchParams.has('minScore') ? Number(url.searchParams.get('minScore')) : undefined,
|
|
838
|
-
state: url.searchParams.get('state') || 'pending',
|
|
839
|
-
limit: parseInt(url.searchParams.get('limit') || '25'),
|
|
840
|
-
offset: parseInt(url.searchParams.get('offset') || '0'),
|
|
841
|
-
});
|
|
842
|
-
return withCors(json(result, 200, acceptEncoding));
|
|
843
|
-
}
|
|
844
|
-
// GET /admin/review/stats — rollups, classifier names, scan progress
|
|
845
|
-
if (url.pathname === '/admin/review/stats' && request.method === 'GET') {
|
|
846
|
-
const denied = requireAdmin(viewer, acceptEncoding);
|
|
847
|
-
if (denied)
|
|
848
|
-
return denied;
|
|
849
|
-
const stats = await getClassificationStats();
|
|
850
|
-
return withCors(json({ ...stats, classifiers: listClassifiers(), configured: isSystemOneConfigured(), scan: getScanProgress() }, 200, acceptEncoding));
|
|
851
|
-
}
|
|
852
|
-
// POST /admin/review/scan — start a scan. Returns as soon as it is under
|
|
853
|
-
// way; a scan of a whole network outlives any sensible request timeout, so
|
|
854
|
-
// progress is polled from /admin/review/stats rather than awaited here.
|
|
855
|
-
if (url.pathname === '/admin/review/scan' && request.method === 'POST') {
|
|
856
|
-
const denied = requireAdmin(viewer, acceptEncoding);
|
|
857
|
-
if (denied)
|
|
858
|
-
return denied;
|
|
859
|
-
if (!isSystemOneConfigured())
|
|
860
|
-
return withCors(jsonError(400, 'System One is not configured — set `typesafe` in hatk.config.ts', acceptEncoding));
|
|
861
|
-
const body = JSON.parse((await request.text()) || '{}');
|
|
862
|
-
const running = getScanProgress();
|
|
863
|
-
if (running?.running)
|
|
864
|
-
return withCors(jsonError(409, 'A scan is already running', acceptEncoding));
|
|
865
|
-
runScan({ classifier: body.classifier, force: body.force === true, limit: body.limit })
|
|
866
|
-
.then(() => invalidateAdminStats())
|
|
867
|
-
.catch(() => { });
|
|
868
|
-
return withCors(json({ ok: true, started: true }, 202, acceptEncoding));
|
|
869
|
-
}
|
|
870
|
-
// GET /admin/review/subject/:did — everything needed to judge one subject
|
|
871
|
-
if (url.pathname.startsWith('/admin/review/subject/')) {
|
|
872
|
-
const denied = requireAdmin(viewer, acceptEncoding);
|
|
873
|
-
if (denied)
|
|
874
|
-
return denied;
|
|
875
|
-
const did = decodeURIComponent(url.pathname.slice('/admin/review/subject/'.length));
|
|
876
|
-
if (!did.startsWith('did:'))
|
|
877
|
-
return withCors(jsonError(400, 'Expected a DID', acceptEncoding));
|
|
878
|
-
const [records, counts, handle, status, labels] = await Promise.all([
|
|
879
|
-
getRecordsByDid(did, collections, 6),
|
|
880
|
-
getRecordCountsByDid(did, collections),
|
|
881
|
-
getRepoHandle(did),
|
|
882
|
-
getRepoStatus(did),
|
|
883
|
-
queryLabelsForUris([did]),
|
|
884
|
-
]);
|
|
885
|
-
return withCors(json({ did, handle, status, records, counts, labels }, 200, acceptEncoding));
|
|
886
|
-
}
|
|
887
|
-
// POST /admin/review/act — resolve the report and act, in one step
|
|
888
|
-
if (url.pathname === '/admin/review/act' && request.method === 'POST') {
|
|
889
|
-
const denied = requireAdmin(viewer, acceptEncoding);
|
|
890
|
-
if (denied)
|
|
891
|
-
return denied;
|
|
892
|
-
const { did, uri, action, label } = JSON.parse(await request.text());
|
|
893
|
-
const subjectUri = uri || did;
|
|
894
|
-
if (!subjectUri || !action)
|
|
895
|
-
return withCors(jsonError(400, 'Missing subject or action', acceptEncoding));
|
|
896
|
-
if (!['takedown', 'label', 'dismiss'].includes(action))
|
|
897
|
-
return withCors(jsonError(400, 'Action must be takedown, label or dismiss', acceptEncoding));
|
|
898
|
-
if (action === 'takedown') {
|
|
899
|
-
if (!did)
|
|
900
|
-
return withCors(jsonError(400, 'Takedown needs a did', acceptEncoding));
|
|
901
|
-
await setRepoStatus(did, 'takendown');
|
|
902
|
-
}
|
|
903
|
-
else if (action === 'label') {
|
|
904
|
-
if (!label)
|
|
905
|
-
return withCors(jsonError(400, 'Label action needs a label', acceptEncoding));
|
|
906
|
-
await insertLabels([{ src: 'admin', uri: subjectUri, val: label }]);
|
|
907
|
-
}
|
|
908
|
-
// Close whatever the classifier filed, so the subject leaves the queue
|
|
909
|
-
// whichever way it was decided.
|
|
910
|
-
for (const id of await findOpenReportsForSubject(subjectUri)) {
|
|
911
|
-
await resolveReport(id, action === 'dismiss' ? 'dismissed' : 'resolved', viewer.did);
|
|
912
|
-
}
|
|
913
|
-
invalidateAdminStats();
|
|
914
|
-
return withCors(json({ ok: true }, 200, acceptEncoding));
|
|
915
|
-
}
|
|
916
828
|
// GET /admin/info/:did — repo status info
|
|
917
829
|
if (url.pathname.startsWith('/admin/info/did:')) {
|
|
918
830
|
const denied = requireAdmin(viewer, acceptEncoding);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hatk/hatk",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.98",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"bin": {
|
|
6
6
|
"hatk": "dist/cli.js"
|
|
@@ -26,8 +26,7 @@
|
|
|
26
26
|
"./config": "./dist/config.js",
|
|
27
27
|
"./vite-plugin": "./dist/vite-plugin.js",
|
|
28
28
|
"./renderer": "./dist/renderer.js",
|
|
29
|
-
"./spaces": "./dist/spaces/index.js"
|
|
30
|
-
"./classifiers": "./dist/classifiers.js"
|
|
29
|
+
"./spaces": "./dist/spaces/index.js"
|
|
31
30
|
},
|
|
32
31
|
"scripts": {
|
|
33
32
|
"clean": "rm -rf dist",
|