@remit/backend 0.0.122 → 0.0.123
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/package.json
CHANGED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
import { logger } from "@remit/logger-lambda";
|
|
5
|
+
import {
|
|
6
|
+
backfillClassifications,
|
|
7
|
+
type ClassificationBackfillCheckpoint,
|
|
8
|
+
type ClassificationBackfillCheckpointStore,
|
|
9
|
+
} from "@remit/mailbox-service";
|
|
10
|
+
import { getClient } from "../src/service/data-client.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* One-time, full-corpus classification backfill over the stored-body cohort
|
|
14
|
+
* the classifier never examined (issue #1197): rows whose bodies were synced
|
|
15
|
+
* before the classifier reached them, which carry `NotExamined` on
|
|
16
|
+
* `Message.classificationState` and so stay `uncategorized` in the list. The
|
|
17
|
+
* same alternate-entrypoint shape `backfill-list-id.mjs` uses: baked into the
|
|
18
|
+
* backend image, never wired into a compose one-shot, run by hand once per
|
|
19
|
+
* install:
|
|
20
|
+
*
|
|
21
|
+
* docker compose -f docker-compose.sqlite.yml run --rm backend \
|
|
22
|
+
* node backfill-classification.mjs
|
|
23
|
+
*
|
|
24
|
+
* Safe to interrupt: progress is checkpointed to disk after every page and
|
|
25
|
+
* picked back up on the next run, every already-examined row reads as
|
|
26
|
+
* `alreadyExamined` on a rerun, and a row whose category was already decided
|
|
27
|
+
* is recorded without re-deriving it, so a partial pass resumes rather than
|
|
28
|
+
* repeats.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
const CHECKPOINT_PATH =
|
|
32
|
+
process.env.CLASSIFICATION_BACKFILL_CHECKPOINT_PATH ??
|
|
33
|
+
"/data/sqlite/classification-backfill-checkpoint.json";
|
|
34
|
+
|
|
35
|
+
const isCheckpoint = (
|
|
36
|
+
value: unknown,
|
|
37
|
+
): value is ClassificationBackfillCheckpoint => {
|
|
38
|
+
if (typeof value !== "object" || value === null) return false;
|
|
39
|
+
if (!("accountConfigId" in value)) return false;
|
|
40
|
+
const candidate = value as Record<string, unknown>;
|
|
41
|
+
return (
|
|
42
|
+
typeof candidate.accountConfigId === "string" &&
|
|
43
|
+
(candidate.continuationToken === undefined ||
|
|
44
|
+
typeof candidate.continuationToken === "string")
|
|
45
|
+
);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const fileCheckpointStore: ClassificationBackfillCheckpointStore = {
|
|
49
|
+
load: async () => {
|
|
50
|
+
if (!existsSync(CHECKPOINT_PATH)) return undefined;
|
|
51
|
+
const raw = await readFile(CHECKPOINT_PATH, "utf8");
|
|
52
|
+
const parsed: unknown = JSON.parse(raw);
|
|
53
|
+
return isCheckpoint(parsed) ? parsed : undefined;
|
|
54
|
+
},
|
|
55
|
+
save: async (checkpoint) => {
|
|
56
|
+
await mkdir(dirname(CHECKPOINT_PATH), { recursive: true });
|
|
57
|
+
const staging = `${CHECKPOINT_PATH}.writing`;
|
|
58
|
+
await writeFile(staging, JSON.stringify(checkpoint));
|
|
59
|
+
await rename(staging, CHECKPOINT_PATH);
|
|
60
|
+
},
|
|
61
|
+
clear: async () => {
|
|
62
|
+
await rm(CHECKPOINT_PATH, { force: true });
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const run = async (): Promise<void> => {
|
|
67
|
+
const client = await getClient();
|
|
68
|
+
|
|
69
|
+
const result = await backfillClassifications(
|
|
70
|
+
{
|
|
71
|
+
accountConfigService: client.accountConfig,
|
|
72
|
+
addressService: client.address,
|
|
73
|
+
threadMessageService: client.threadMessage,
|
|
74
|
+
messageService: client.message,
|
|
75
|
+
storageService: client.storage,
|
|
76
|
+
},
|
|
77
|
+
{ checkpointStore: fileCheckpointStore, logger },
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
// biome-ignore lint/plugin/no-logger-info: a completed full-corpus backfill is an audit-grade signal
|
|
81
|
+
logger.info({ result }, "backfill done");
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
run()
|
|
85
|
+
.then(() => process.exit(0))
|
|
86
|
+
.catch((error: unknown) => {
|
|
87
|
+
logger.error({ error }, "backfill failed");
|
|
88
|
+
process.exit(1);
|
|
89
|
+
});
|