koishi-plugin-echo-cave 1.29.9 → 1.29.10
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/README.md +3 -0
- package/lib/core/command/admin.d.ts +1 -0
- package/lib/index.cjs +199 -47
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
- 🧭 **转发绑定体验优化**:转发消息关联用户的详细输入指南仅在用户首次成功完成选择前显示,选择完成或超时后会尝试撤回提示消息
|
|
25
25
|
- 🤖 **特殊转发用户处理**:可配置在检测到转发记录中包含特殊用户 `1094950020` 时直接拒绝存储,或要求二次确认后再存储
|
|
26
26
|
- 🛠️ **回声洞 ID 重排**:提供管理员维护命令,可在写入备份后安全重排现有回声洞 ID
|
|
27
|
+
- ♻️ **回声洞备份恢复**:提供管理员维护命令,可从 ID 重排生成的 JSON 备份自动恢复数据库
|
|
27
28
|
- ⚖️ **加权随机抽取**:根据消息被抽取次数动态调整抽取概率,被抽取次数越多,概率越低。使用公式:
|
|
28
29
|
```
|
|
29
30
|
权重 = 1 / (1 + drawCount * α)
|
|
@@ -47,6 +48,7 @@
|
|
|
47
48
|
| `cave.bind <id> <...userIds>` | 将用户绑定到特定 ID 的回声洞 | 所有人 |
|
|
48
49
|
| `cave.rank [period]` | 查看回声洞排行榜,支持多种时间段 | 所有人 |
|
|
49
50
|
| `cave.admin.reindex` | 安全重排所有现有回声洞 ID,并先写入备份 | 管理员(私聊) |
|
|
51
|
+
| `cave.admin.restore-reindex <backupPath>` | 从重排生成的 JSON 备份恢复回声洞数据 | 管理员(私聊) |
|
|
50
52
|
|
|
51
53
|
## 🚀 使用指南
|
|
52
54
|
|
|
@@ -95,6 +97,7 @@ npm install koishi-plugin-echo-cave
|
|
|
95
97
|
- 可通过配置项禁用转发消息用户选择、开启单用户自动绑定,或配置检测到特殊用户 `1094950020` 时的处理模式
|
|
96
98
|
- `cave.admin.reindex` 会先在 `logs/` 下写入备份文件,再执行 ID 重排;建议仅在单实例维护时段执行
|
|
97
99
|
- 如果重排前备份写入失败,命令会直接终止,不会开始改写数据库
|
|
100
|
+
- 如果需要恢复,可执行 `cave.admin.restore-reindex <备份路径>` 读取对应的 JSON 备份并回写数据库
|
|
98
101
|
|
|
99
102
|
## 🤝 贡献指南
|
|
100
103
|
|
|
@@ -6,3 +6,4 @@ export declare function migrateLegacyLocalMedia(ctx: Context, session: Session,
|
|
|
6
6
|
export declare function migrateMediaToS3(ctx: Context, session: Session, cfg: Config, keepLocalOption?: string): Promise<string>;
|
|
7
7
|
export declare function inspectMediaRefsForMigration(ctx: Context, session: Session, cfg: Config, idRangesOption?: string): Promise<string>;
|
|
8
8
|
export declare function reindexCaveIds(ctx: Context, session: Session, cfg: Config): Promise<string>;
|
|
9
|
+
export declare function restoreReindexBackup(ctx: Context, session: Session, cfg: Config, backupPathInput?: string): Promise<string>;
|
package/lib/index.cjs
CHANGED
|
@@ -34852,6 +34852,105 @@ var import_node_fs2 = require("node:fs");
|
|
|
34852
34852
|
var import_node_path2 = __toESM(require("node:path"), 1);
|
|
34853
34853
|
var REINDEX_SPECIAL_OFFSET = 1e6;
|
|
34854
34854
|
var caveMaintenanceLock = false;
|
|
34855
|
+
function cloneCaveRecord(cave, id) {
|
|
34856
|
+
return {
|
|
34857
|
+
...cave,
|
|
34858
|
+
createTime: new Date(cave.createTime),
|
|
34859
|
+
id,
|
|
34860
|
+
relatedUsers: [...cave.relatedUsers]
|
|
34861
|
+
};
|
|
34862
|
+
}
|
|
34863
|
+
function getCaveIdList(caves) {
|
|
34864
|
+
return caves.map((cave) => cave.id);
|
|
34865
|
+
}
|
|
34866
|
+
async function removeCavesByIds(ctx, ids) {
|
|
34867
|
+
for (const id of ids) {
|
|
34868
|
+
await ctx.database.remove("echo_cave_v2", id);
|
|
34869
|
+
}
|
|
34870
|
+
}
|
|
34871
|
+
async function upsertCaves(ctx, caves) {
|
|
34872
|
+
if (caves.length === 0) {
|
|
34873
|
+
return;
|
|
34874
|
+
}
|
|
34875
|
+
await ctx.database.upsert("echo_cave_v2", caves);
|
|
34876
|
+
}
|
|
34877
|
+
function buildTemporarySnapshot(currentCaves, nextCaves) {
|
|
34878
|
+
const currentMaxId = currentCaves.reduce((currentMax, cave) => Math.max(currentMax, cave.id), 0);
|
|
34879
|
+
const nextMaxId = nextCaves.reduce((currentMax, cave) => Math.max(currentMax, cave.id), 0);
|
|
34880
|
+
const offset = Math.max(currentMaxId, nextMaxId) + currentCaves.length + nextCaves.length + REINDEX_SPECIAL_OFFSET;
|
|
34881
|
+
return currentCaves.map((cave) => cloneCaveRecord(cave, cave.id + offset));
|
|
34882
|
+
}
|
|
34883
|
+
async function rollbackCaveReplacement(ctx, currentCaves, nextCaves, tempCaves) {
|
|
34884
|
+
const idsToRemove = [.../* @__PURE__ */ new Set([...getCaveIdList(nextCaves), ...getCaveIdList(tempCaves)])];
|
|
34885
|
+
await removeCavesByIds(ctx, idsToRemove);
|
|
34886
|
+
await upsertCaves(ctx, currentCaves);
|
|
34887
|
+
}
|
|
34888
|
+
async function replaceCaveSnapshot(ctx, currentCaves, nextCaves) {
|
|
34889
|
+
const tempCaves = buildTemporarySnapshot(currentCaves, nextCaves);
|
|
34890
|
+
try {
|
|
34891
|
+
await upsertCaves(ctx, tempCaves);
|
|
34892
|
+
await removeCavesByIds(ctx, getCaveIdList(currentCaves));
|
|
34893
|
+
await upsertCaves(ctx, nextCaves);
|
|
34894
|
+
await removeCavesByIds(ctx, getCaveIdList(tempCaves));
|
|
34895
|
+
} catch (error2) {
|
|
34896
|
+
await rollbackCaveReplacement(ctx, currentCaves, nextCaves, tempCaves);
|
|
34897
|
+
throw error2;
|
|
34898
|
+
}
|
|
34899
|
+
}
|
|
34900
|
+
function buildSequentialCaveSnapshot(caves) {
|
|
34901
|
+
return [...caves].sort((a5, b5) => a5.id - b5.id).map((cave, index) => cloneCaveRecord(cave, index + 1));
|
|
34902
|
+
}
|
|
34903
|
+
function normalizeCaveRecord(cave) {
|
|
34904
|
+
return {
|
|
34905
|
+
channelId: cave.channelId,
|
|
34906
|
+
content: cave.content,
|
|
34907
|
+
createTime: new Date(cave.createTime).toISOString(),
|
|
34908
|
+
drawCount: cave.drawCount,
|
|
34909
|
+
id: cave.id,
|
|
34910
|
+
originUserId: cave.originUserId,
|
|
34911
|
+
relatedUsers: [...cave.relatedUsers],
|
|
34912
|
+
type: cave.type,
|
|
34913
|
+
userId: cave.userId
|
|
34914
|
+
};
|
|
34915
|
+
}
|
|
34916
|
+
function isRecordObject(value) {
|
|
34917
|
+
return typeof value === "object" && value !== null;
|
|
34918
|
+
}
|
|
34919
|
+
function isEchoCaveRecord(value) {
|
|
34920
|
+
if (!isRecordObject(value)) {
|
|
34921
|
+
return false;
|
|
34922
|
+
}
|
|
34923
|
+
return typeof value.id === "number" && typeof value.channelId === "string" && (value.createTime instanceof Date || typeof value.createTime === "string" || typeof value.createTime === "number") && typeof value.userId === "string" && typeof value.originUserId === "string" && (value.type === "forward" || value.type === "msg") && typeof value.content === "string" && Array.isArray(value.relatedUsers) && value.relatedUsers.every((user) => typeof user === "string") && typeof value.drawCount === "number";
|
|
34924
|
+
}
|
|
34925
|
+
function isReindexPlanItem(value) {
|
|
34926
|
+
if (!isRecordObject(value)) {
|
|
34927
|
+
return false;
|
|
34928
|
+
}
|
|
34929
|
+
return typeof value.oldId === "number" && typeof value.newId === "number" && typeof value.tempId === "number";
|
|
34930
|
+
}
|
|
34931
|
+
function parseReindexBackupPayload(content) {
|
|
34932
|
+
const parsed = JSON.parse(content);
|
|
34933
|
+
if (!isRecordObject(parsed)) {
|
|
34934
|
+
throw new Error("invalid_backup_payload");
|
|
34935
|
+
}
|
|
34936
|
+
if (typeof parsed.createdAt !== "string") {
|
|
34937
|
+
throw new Error("invalid_backup_created_at");
|
|
34938
|
+
}
|
|
34939
|
+
if (!Array.isArray(parsed.records) || !parsed.records.every((record) => isEchoCaveRecord(record))) {
|
|
34940
|
+
throw new Error("invalid_backup_records");
|
|
34941
|
+
}
|
|
34942
|
+
if (!Array.isArray(parsed.mapping) || !parsed.mapping.every((item) => isReindexPlanItem(item))) {
|
|
34943
|
+
throw new Error("invalid_backup_mapping");
|
|
34944
|
+
}
|
|
34945
|
+
return {
|
|
34946
|
+
createdAt: parsed.createdAt,
|
|
34947
|
+
mapping: parsed.mapping,
|
|
34948
|
+
records: parsed.records.map((record) => cloneCaveRecord(record, record.id))
|
|
34949
|
+
};
|
|
34950
|
+
}
|
|
34951
|
+
function resolveBackupPath(backupPath) {
|
|
34952
|
+
return import_node_path2.default.isAbsolute(backupPath) ? backupPath : import_node_path2.default.resolve(process.cwd(), backupPath);
|
|
34953
|
+
}
|
|
34855
34954
|
function getCaveMaintenanceMessage(session) {
|
|
34856
34955
|
return caveMaintenanceLock ? session.text("echo-cave.general.maintenanceLocked") : null;
|
|
34857
34956
|
}
|
|
@@ -35166,56 +35265,37 @@ async function writeReindexBackup(backupDir, caves, mapping) {
|
|
|
35166
35265
|
await import_node_fs2.promises.writeFile(backupPath, JSON.stringify(payload2, null, 2), "utf8");
|
|
35167
35266
|
return backupPath;
|
|
35168
35267
|
}
|
|
35169
|
-
async function applyReindexPlan(ctx,
|
|
35170
|
-
const
|
|
35171
|
-
|
|
35172
|
-
|
|
35173
|
-
|
|
35174
|
-
|
|
35175
|
-
|
|
35268
|
+
async function applyReindexPlan(ctx, originalCaves) {
|
|
35269
|
+
const reindexedCaves = buildSequentialCaveSnapshot(originalCaves);
|
|
35270
|
+
await replaceCaveSnapshot(ctx, originalCaves, reindexedCaves);
|
|
35271
|
+
}
|
|
35272
|
+
async function verifyCaveSnapshot(ctx, expectedCaves) {
|
|
35273
|
+
const actualCaves = (await ctx.database.get("echo_cave_v2", {})).sort((a5, b5) => a5.id - b5.id);
|
|
35274
|
+
const normalizedExpected = [...expectedCaves].sort((a5, b5) => a5.id - b5.id).map(normalizeCaveRecord);
|
|
35275
|
+
const normalizedActual = actualCaves.map(normalizeCaveRecord);
|
|
35276
|
+
if (normalizedActual.length !== normalizedExpected.length) {
|
|
35277
|
+
throw new Error("record_count_mismatch");
|
|
35176
35278
|
}
|
|
35177
|
-
|
|
35178
|
-
|
|
35179
|
-
|
|
35180
|
-
|
|
35279
|
+
for (let index = 0; index < normalizedActual.length; index++) {
|
|
35280
|
+
const actual = normalizedActual[index];
|
|
35281
|
+
const expected = normalizedExpected[index];
|
|
35282
|
+
if (actual.id !== index + 1) {
|
|
35283
|
+
throw new Error("id_sequence_mismatch");
|
|
35284
|
+
}
|
|
35285
|
+
if (JSON.stringify(actual) !== JSON.stringify(expected)) {
|
|
35286
|
+
throw new Error("record_content_mismatch");
|
|
35181
35287
|
}
|
|
35182
|
-
await ctx.database.set("echo_cave_v2", { id: item.tempId }, { id: item.newId });
|
|
35183
35288
|
}
|
|
35184
35289
|
}
|
|
35185
|
-
async function
|
|
35186
|
-
const
|
|
35187
|
-
|
|
35290
|
+
async function verifyRestoredSnapshot(ctx, expectedCaves) {
|
|
35291
|
+
const actualCaves = (await ctx.database.get("echo_cave_v2", {})).sort((a5, b5) => a5.id - b5.id);
|
|
35292
|
+
const normalizedExpected = [...expectedCaves].sort((a5, b5) => a5.id - b5.id).map(normalizeCaveRecord);
|
|
35293
|
+
const normalizedActual = actualCaves.map(normalizeCaveRecord);
|
|
35294
|
+
if (normalizedActual.length !== normalizedExpected.length) {
|
|
35188
35295
|
throw new Error("record_count_mismatch");
|
|
35189
35296
|
}
|
|
35190
|
-
|
|
35191
|
-
|
|
35192
|
-
content: cave.content,
|
|
35193
|
-
createTime: new Date(cave.createTime).toISOString(),
|
|
35194
|
-
drawCount: cave.drawCount,
|
|
35195
|
-
originUserId: cave.originUserId,
|
|
35196
|
-
relatedUsers: [...cave.relatedUsers],
|
|
35197
|
-
type: cave.type,
|
|
35198
|
-
userId: cave.userId
|
|
35199
|
-
}));
|
|
35200
|
-
const normalizedReindexed = reindexedCaves.map((cave, index) => ({
|
|
35201
|
-
channelId: cave.channelId,
|
|
35202
|
-
content: cave.content,
|
|
35203
|
-
createTime: new Date(cave.createTime).toISOString(),
|
|
35204
|
-
drawCount: cave.drawCount,
|
|
35205
|
-
id: cave.id,
|
|
35206
|
-
originUserId: cave.originUserId,
|
|
35207
|
-
relatedUsers: [...cave.relatedUsers],
|
|
35208
|
-
type: cave.type,
|
|
35209
|
-
userId: cave.userId,
|
|
35210
|
-
expectedId: index + 1
|
|
35211
|
-
}));
|
|
35212
|
-
for (let index = 0; index < normalizedReindexed.length; index++) {
|
|
35213
|
-
const reindexed = normalizedReindexed[index];
|
|
35214
|
-
const original = normalizedOriginal[index];
|
|
35215
|
-
if (reindexed.id !== reindexed.expectedId) {
|
|
35216
|
-
throw new Error("id_sequence_mismatch");
|
|
35217
|
-
}
|
|
35218
|
-
if (reindexed.channelId !== original.channelId || reindexed.content !== original.content || reindexed.createTime !== original.createTime || reindexed.drawCount !== original.drawCount || reindexed.originUserId !== original.originUserId || reindexed.relatedUsers.join(",") !== original.relatedUsers.join(",") || reindexed.type !== original.type || reindexed.userId !== original.userId) {
|
|
35297
|
+
for (let index = 0; index < normalizedActual.length; index++) {
|
|
35298
|
+
if (JSON.stringify(normalizedActual[index]) !== JSON.stringify(normalizedExpected[index])) {
|
|
35219
35299
|
throw new Error("record_content_mismatch");
|
|
35220
35300
|
}
|
|
35221
35301
|
}
|
|
@@ -35265,8 +35345,9 @@ async function reindexCaveIds(ctx, session, cfg) {
|
|
|
35265
35345
|
}
|
|
35266
35346
|
setCaveMaintenanceLock(true);
|
|
35267
35347
|
try {
|
|
35268
|
-
|
|
35269
|
-
await
|
|
35348
|
+
const reindexedCaves = buildSequentialCaveSnapshot(caves);
|
|
35349
|
+
await applyReindexPlan(ctx, caves);
|
|
35350
|
+
await verifyCaveSnapshot(ctx, reindexedCaves);
|
|
35270
35351
|
return session.text("commands.cave.admin.reindex.messages.reindexDone", {
|
|
35271
35352
|
caveCount: caves.length,
|
|
35272
35353
|
backupPath
|
|
@@ -35281,6 +35362,61 @@ async function reindexCaveIds(ctx, session, cfg) {
|
|
|
35281
35362
|
setCaveMaintenanceLock(false);
|
|
35282
35363
|
}
|
|
35283
35364
|
}
|
|
35365
|
+
async function restoreReindexBackup(ctx, session, cfg, backupPathInput) {
|
|
35366
|
+
const accessError = ensureAdminPrivateAccess(session, cfg);
|
|
35367
|
+
if (accessError) {
|
|
35368
|
+
return accessError;
|
|
35369
|
+
}
|
|
35370
|
+
if (!backupPathInput?.trim()) {
|
|
35371
|
+
return session.text("commands.cave.admin.restore-reindex.messages.missingBackupPath");
|
|
35372
|
+
}
|
|
35373
|
+
const backupPath = resolveBackupPath(backupPathInput.trim());
|
|
35374
|
+
let backup;
|
|
35375
|
+
try {
|
|
35376
|
+
const content = await import_node_fs2.promises.readFile(backupPath, "utf8");
|
|
35377
|
+
backup = parseReindexBackupPayload(content);
|
|
35378
|
+
} catch (error2) {
|
|
35379
|
+
ctx.logger.error(`Failed to read cave reindex backup: ${error2}`);
|
|
35380
|
+
return session.text("commands.cave.admin.restore-reindex.messages.backupReadFailed", {
|
|
35381
|
+
backupPath,
|
|
35382
|
+
error: error2 instanceof Error ? error2.message : String(error2)
|
|
35383
|
+
});
|
|
35384
|
+
}
|
|
35385
|
+
const currentCaves = (await ctx.database.get("echo_cave_v2", {})).sort((a5, b5) => a5.id - b5.id);
|
|
35386
|
+
const confirmed = await requestSecondConfirmation(
|
|
35387
|
+
ctx,
|
|
35388
|
+
session,
|
|
35389
|
+
session.text("commands.cave.admin.restore-reindex.messages.confirmSummary", {
|
|
35390
|
+
backupPath,
|
|
35391
|
+
currentCount: currentCaves.length,
|
|
35392
|
+
backupCount: backup.records.length,
|
|
35393
|
+
backupCreatedAt: backup.createdAt
|
|
35394
|
+
}),
|
|
35395
|
+
session.text("commands.cave.admin.restore-reindex.messages.confirmRetry"),
|
|
35396
|
+
session.text("commands.cave.admin.restore-reindex.messages.confirmTimeout"),
|
|
35397
|
+
session.text("commands.cave.admin.restore-reindex.messages.confirmCancelled")
|
|
35398
|
+
);
|
|
35399
|
+
if (!confirmed) {
|
|
35400
|
+
return;
|
|
35401
|
+
}
|
|
35402
|
+
setCaveMaintenanceLock(true);
|
|
35403
|
+
try {
|
|
35404
|
+
await replaceCaveSnapshot(ctx, currentCaves, backup.records);
|
|
35405
|
+
await verifyRestoredSnapshot(ctx, backup.records);
|
|
35406
|
+
return session.text("commands.cave.admin.restore-reindex.messages.restoreDone", {
|
|
35407
|
+
backupPath,
|
|
35408
|
+
caveCount: backup.records.length
|
|
35409
|
+
});
|
|
35410
|
+
} catch (error2) {
|
|
35411
|
+
ctx.logger.error(`Failed to restore cave reindex backup: ${error2}`);
|
|
35412
|
+
return session.text("commands.cave.admin.restore-reindex.messages.restoreFailed", {
|
|
35413
|
+
backupPath,
|
|
35414
|
+
error: error2 instanceof Error ? error2.message : String(error2)
|
|
35415
|
+
});
|
|
35416
|
+
} finally {
|
|
35417
|
+
setCaveMaintenanceLock(false);
|
|
35418
|
+
}
|
|
35419
|
+
}
|
|
35284
35420
|
|
|
35285
35421
|
// src/core/parser/forward-parser.ts
|
|
35286
35422
|
async function reconstructForwardMsg(ctx, session, message, cfg, processMedia = true) {
|
|
@@ -36704,7 +36840,20 @@ var zh_CN_default = {
|
|
|
36704
36840
|
confirmCancelled: "\u{1F6D1} \u5DF2\u53D6\u6D88\u672C\u6B21\u56DE\u58F0\u6D1E ID \u91CD\u6392\u3002",
|
|
36705
36841
|
confirmTimeout: "\u231B \u4E8C\u6B21\u786E\u8BA4\u8D85\u65F6\uFF0C\u56DE\u58F0\u6D1E ID \u91CD\u6392\u672A\u6267\u884C\u3002",
|
|
36706
36842
|
reindexDone: "\u2705 \u56DE\u58F0\u6D1E ID \u91CD\u6392\u5B8C\u6210\uFF0C\u5171\u5904\u7406 {caveCount} \u6761\u8BB0\u5F55\u3002\u5907\u4EFD\u6587\u4EF6\uFF1A{backupPath}",
|
|
36707
|
-
reindexFailed: "\u274C \u56DE\u58F0\u6D1E ID \u91CD\u6392\u5931\u8D25\uFF0C\u5DF2\u505C\u6B62\u7EE7\u7EED\u5199\u5165\u3002\
|
|
36843
|
+
reindexFailed: "\u274C \u56DE\u58F0\u6D1E ID \u91CD\u6392\u5931\u8D25\uFF0C\u5DF2\u505C\u6B62\u7EE7\u7EED\u5199\u5165\u3002\u53EF\u4F7F\u7528 cave.admin.restore-reindex \u6062\u590D\u5907\u4EFD\uFF1A{backupPath}\n\u9519\u8BEF\uFF1A{error}"
|
|
36844
|
+
}
|
|
36845
|
+
},
|
|
36846
|
+
"cave.admin.restore-reindex": {
|
|
36847
|
+
description: "\u4ECE\u56DE\u58F0\u6D1E ID \u91CD\u6392\u751F\u6210\u7684 JSON \u5907\u4EFD\u6062\u590D\u6570\u636E\u5E93",
|
|
36848
|
+
messages: {
|
|
36849
|
+
missingBackupPath: "\u274C \u8BF7\u63D0\u4F9B\u8981\u6062\u590D\u7684\u5907\u4EFD\u6587\u4EF6\u8DEF\u5F84\u3002",
|
|
36850
|
+
backupReadFailed: "\u274C \u8BFB\u53D6\u56DE\u58F0\u6D1E\u91CD\u6392\u5907\u4EFD\u5931\u8D25\uFF1A{backupPath}\n\u9519\u8BEF\uFF1A{error}",
|
|
36851
|
+
confirmSummary: "\u26A0\uFE0F \u5373\u5C06\u4ECE\u5907\u4EFD\u6062\u590D\u56DE\u58F0\u6D1E\u6570\u636E\n\u5907\u4EFD\u6587\u4EF6\uFF1A{backupPath}\n\u5907\u4EFD\u521B\u5EFA\u65F6\u95F4\uFF1A{backupCreatedAt}\n\u5907\u4EFD\u8BB0\u5F55\u6570\uFF1A{backupCount}\n\u5F53\u524D\u6570\u636E\u5E93\u8BB0\u5F55\u6570\uFF1A{currentCount}\n\n\u672C\u6B21\u64CD\u4F5C\u4F1A\u5148\u4E34\u65F6\u817E\u632A\u5F53\u524D\u8BB0\u5F55\uFF0C\u518D\u6309\u5907\u4EFD\u5185\u5BB9\u5B8C\u6574\u6062\u590D\u3002\u6267\u884C\u671F\u95F4\u4F1A\u4E34\u65F6\u9501\u5B9A\u56DE\u58F0\u6D1E\u76F8\u5173\u547D\u4EE4\u3002\u8BF7\u5728 30 \u79D2\u5185\u8F93\u5165\u201C\u786E\u8BA4\u201D\u7EE7\u7EED\uFF0C\u8F93\u5165\u201C\u53D6\u6D88\u201D\u53EF\u7EC8\u6B62\u3002",
|
|
36852
|
+
confirmRetry: "\u26A0\uFE0F \u672A\u6536\u5230\u201C\u786E\u8BA4\u201D\u3002\u8BF7\u5728\u5269\u4F59\u65F6\u95F4\u5185\u8F93\u5165\u201C\u786E\u8BA4\u201D\u7EE7\u7EED\uFF0C\u6216\u8F93\u5165\u201C\u53D6\u6D88\u201D\u7EC8\u6B62\u3002",
|
|
36853
|
+
confirmCancelled: "\u{1F6D1} \u5DF2\u53D6\u6D88\u672C\u6B21\u56DE\u58F0\u6D1E\u5907\u4EFD\u6062\u590D\u3002",
|
|
36854
|
+
confirmTimeout: "\u231B \u4E8C\u6B21\u786E\u8BA4\u8D85\u65F6\uFF0C\u56DE\u58F0\u6D1E\u5907\u4EFD\u6062\u590D\u672A\u6267\u884C\u3002",
|
|
36855
|
+
restoreDone: "\u2705 \u56DE\u58F0\u6D1E\u5907\u4EFD\u6062\u590D\u5B8C\u6210\uFF0C\u5171\u6062\u590D {caveCount} \u6761\u8BB0\u5F55\u3002\u5907\u4EFD\u6587\u4EF6\uFF1A{backupPath}",
|
|
36856
|
+
restoreFailed: "\u274C \u56DE\u58F0\u6D1E\u5907\u4EFD\u6062\u590D\u5931\u8D25\uFF0C\u5DF2\u5C1D\u8BD5\u56DE\u6EDA\u5230\u6062\u590D\u524D\u72B6\u6001\uFF1A{backupPath}\n\u9519\u8BEF\uFF1A{error}"
|
|
36708
36857
|
}
|
|
36709
36858
|
}
|
|
36710
36859
|
}
|
|
@@ -36920,6 +37069,9 @@ function apply(ctx, cfg) {
|
|
|
36920
37069
|
ctx.command("cave.admin.reindex").action(
|
|
36921
37070
|
async ({ session }) => await reindexCaveIds(ctx, session, cfg)
|
|
36922
37071
|
);
|
|
37072
|
+
ctx.command("cave.admin.restore-reindex <backupPath:text>").action(
|
|
37073
|
+
async ({ session }, backupPath) => await restoreReindexBackup(ctx, session, cfg, backupPath)
|
|
37074
|
+
);
|
|
36923
37075
|
registerSendFailureSummaryScheduler(ctx, cfg);
|
|
36924
37076
|
}
|
|
36925
37077
|
// Annotate the CommonJS export names for ESM import in node:
|