Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions db-server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ function buildModuleList() {
type: String((raw as Record<string, unknown>).type || "feature"),
description: String((raw as Record<string, unknown>).description || ""),
default_enabled: (raw as Record<string, unknown>).enabledByDefault !== false,
can_disable: (raw as Record<string, unknown>).canDisable !== false,
can_disable: moduleCanDisable(raw as Record<string, unknown>),
// ConfigManager 认 installed!==false;已装包默认 true
installed: true,
requires: Array.isArray((raw as Record<string, unknown>).requires)
Expand All @@ -193,14 +193,25 @@ function buildModuleList() {
.filter(Boolean);
}

/** catalog 省略 canDisable 时默认允许禁用(与 buildModuleList.can_disable 同源)。 */
function moduleCanDisable(raw: Record<string, unknown>): boolean {
return raw.canDisable !== false;
}

function resolveModuleByKey(key: string) {
const k = String(key || "").trim();
const catalog = loadModuleCatalog();
return catalog.find(
const raw = catalog.find(
(m) =>
String((m as Record<string, unknown>).id || "") === k ||
String((m as Record<string, unknown>).configKey || (m as Record<string, unknown>).config_key || "") === k
) as { id: string; configKey: string; canDisable: boolean } | null;
) as Record<string, unknown> | undefined;
if (!raw) return null;
const id = String(raw.id || "").trim();
const configKey = String(raw.configKey || raw.config_key || "").trim();
if (!id || !configKey) return null;
/* LSP:与 buildModuleList.can_disable 同源 — 省略字段视为可禁用 */
return { id, configKey, canDisable: moduleCanDisable(raw) };
}

function setModuleEnabled(mod: { id: string; canDisable: boolean }, enabled: boolean) {
Expand Down
62 changes: 18 additions & 44 deletions db-server/src/routes/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,50 +71,24 @@ function createConfigRoutes({ json, projectRoot, listModules, getModuleTokens }:
function getAllConfigs(): Record<string, unknown> {
const modules = typeof listModules === "function" ? listModules() : [];
const module_tokens = typeof getModuleTokens === "function" ? getModuleTokens() : {};
/**
* SAPI ConfigManager 契约:banned_items 为 string[](与 GET /banned_items 同源)。
* 勿再映射成 {item_id} — 否则 filter(typeof s === "string") 会得到空缓存(LSP)。
* 其余资源复用单资源 helpers,避免 getAllConfigs 与专用路由双写(DRY)。
*/
return {
// 与 /api/sfmc/modules 同源;ConfigManager.init 一次拉齐启停态(DRY)
modules,
// loopback-only 下发;SAPI 无 fs,靠此注入模块身份(DIP)
module_tokens,
settings: stripMeta(readJson(configPath(projectRoot, "settings.json")) as Record<string, unknown> | null),
areas: (arrayOrEmpty(readJson(configPath(projectRoot, "areas.json"))) as Array<Record<string, unknown>>)
.filter((r) => r && r.module && r.dimension != null)
.map((r) => stripMetaDeep(r)),
permissions: (
arrayOrEmpty(readJson(configPath(projectRoot, "permissions.json"))) as Array<Record<string, unknown>>
)
.filter((r) => r && r.player_name)
.map((r) => stripMetaDeep(r)),
banned_items: (arrayOrEmpty(readJson(configPath(projectRoot, "banned_items.json"))) as Array<string>)
.filter((i) => typeof i === "string" && i && !i.startsWith("_"))
.map((id) => ({ item_id: id })),
clean: stripMetaDeep(readJson(configPath(projectRoot, "clean.json")) ?? {}),
grids: (arrayOrEmpty(readJson(configPath(projectRoot, "grids.json"))) as Array<Record<string, unknown>>)
.filter((r) => r && r.name)
.map((r) => stripMetaDeep(r)),
peace_filters: (
arrayOrEmpty(readJson(configPath(projectRoot, "peace_filters.json"))) as Array<Record<string, unknown>>
)
.filter((r) => r && r.family)
.map((r) => stripMetaDeep(r)),
questions: (arrayOrEmpty(readJson(configPath(projectRoot, "questions.json"))) as Array<Record<string, unknown>>)
.filter((r) => r && r.question)
.map((r, idx: number) => {
const clean = stripMetaDeep(r) as Record<string, unknown>;
return {
id: idx + 1,
weight: clean.weight ?? 1,
question: clean.question,
answers: clean.answers ?? [],
msg_right: clean.msg_right ?? "",
msg_wrong: clean.msg_wrong ?? "",
explanation: clean.explanation ?? "",
min_rank: clean.min_rank ?? null,
max_rank: clean.max_rank ?? null,
rewards: clean.rewards ?? [],
punishments: clean.punishments ?? [],
};
}),
settings: stripMeta(readCfg("settings.json") as Record<string, unknown> | null),
areas: getAreas(),
permissions: getPermissions(),
banned_items: getBannedItems(),
clean: getClean(),
grids: getGrids(),
peace_filters: getPeaceFilters(),
questions: getQA(),
};
}

Expand Down Expand Up @@ -153,10 +127,10 @@ function createConfigRoutes({ json, projectRoot, listModules, getModuleTokens }:
.map((r) => stripMetaDeep(r));
}

function getBannedItems(): Array<{ item_id: string }> {
return (arrayOrEmpty(readCfg("banned_items.json")) as Array<string>)
.filter((i) => typeof i === "string" && i && !i.startsWith("_"))
.map((id) => ({ item_id: id }));
function getBannedItems(): string[] {
return (arrayOrEmpty(readCfg("banned_items.json")) as Array<string>).filter(
(i) => typeof i === "string" && i && !i.startsWith("_")
);
}

function getClean(): { item_max: number; poll_interval: number } {
Expand Down Expand Up @@ -248,7 +222,7 @@ function createConfigRoutes({ json, projectRoot, listModules, getModuleTokens }:
}
if (requestPath === "/api/sfmc/banned_items") {
if (method === "GET") {
json(res, { items: getBannedItems().map((x) => x.item_id) });
json(res, { items: getBannedItems() });
return true;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ type AllConfigs = {
end_z: number;
}>;
permissions: Array<{ player_name: string; level: number }>;
banned_items: string[];
/** 权威为 string[];兼容历史 {item_id} */
banned_items: Array<string | { item_id?: string }>;
clean: { item_max?: number; poll_interval?: number };
grids: Array<{
name: string;
Expand Down Expand Up @@ -275,7 +276,10 @@ export class ConfigManager {
ConfigManager.cache.permissions[p.player_name] = p.level;
}

ConfigManager.cache.bannedItems = (all.banned_items || []).filter((s) => typeof s === "string");
// 权威契约为 string[];兼容历史 {item_id} 以免旧服务端把缓存清空(LSP 防御)
ConfigManager.cache.bannedItems = (all.banned_items || [])
.map((s) => (typeof s === "string" ? s : s && typeof s === "object" ? String((s as { item_id?: unknown }).item_id || "") : ""))
.filter((id) => !!id);

if (all.clean) {
ConfigManager.cache.clean = {
Expand Down
4 changes: 3 additions & 1 deletion modules/sdk/@sfmc-sdk/src/sapi/service/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ export const service = {
authOpts()
);
if (!res.ok) {
throw new ServiceError(res.error ?? "service_error", "internal", res.status);
// LSP:与 db 客户端一致,保留服务端 code,勿一律打成 internal
const data = res.data as { error?: string; code?: string } | undefined;
throw new ServiceError(data?.error ?? res.error ?? "service_error", data?.code || "internal", res.status);
}
return (res.data as { ok: true; result: T }).result;
},
Expand Down
3 changes: 0 additions & 3 deletions sfmc/src/module-commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,6 @@ export const MODULE_CMD_NAMES = ["module", "mod"] as const;

export type ModuleCmdName = (typeof MODULE_CMD_NAMES)[number];

/** HELP 中主名+别名展示串(权威来源 MODULE_CMD_NAMES),如 "module/mod"。 */
export const MODULE_CMD_ALIAS_LABEL = MODULE_CMD_NAMES.join("/");

/** 判断是否为 module 顶层命令(含别名);避免 main/repl 再硬编码 case。 */
export function isModuleCommand(cmd: string | undefined): cmd is ModuleCmdName {
return !!cmd && (MODULE_CMD_NAMES as readonly string[]).includes(cmd);
Expand Down
18 changes: 9 additions & 9 deletions sfmc/src/repl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ function setRaw(v: boolean): void {
} catch {}
}

/** HELP 行首: MODULE_CMD_NAMES 着色后用 / 拼接(如 module/mod)。 */
const MODULE_HELP_LABEL = MODULE_CMD_NAMES.map((n) => c.green(n)).join("/");
/** HELP 行首:一次染色 MODULE_CMD_NAMES(与 paintModuleCmdAlias 同源,DRY)。 */
const MODULE_HELP_LABEL = paintModuleCmdAlias(c.green);

const welcome = `\n
${c.text(`⠪⡁⡯⠁`)}
Expand Down Expand Up @@ -53,19 +53,19 @@ ${c.bold("Commands")}
${c.green("remote enroll")} <url> <token> [name]
Enroll this supervisor with a controller
${c.green("remote disable")} Disable + disconnect remote agent
${paintModuleCmdAlias(c.green)} list
${MODULE_HELP_LABEL} list
List installed modules
${paintModuleCmdAlias(c.green)} search [id]
${MODULE_HELP_LABEL} search [id]
Fetch registry list / show one module's registry info
${paintModuleCmdAlias(c.green)} install <id> [--from <source>]
${MODULE_HELP_LABEL} install <id> [--from <source>]
Fetch + install a module
${paintModuleCmdAlias(c.green)} uninstall <id>
${MODULE_HELP_LABEL} uninstall <id>
Remove an installed module
${paintModuleCmdAlias(c.green)} verify [id]
${MODULE_HELP_LABEL} verify [id]
Verify installed modules (SHA-256)
${paintModuleCmdAlias(c.green)} info <id>
${MODULE_HELP_LABEL} info <id>
Show one installed module's details
${paintModuleCmdAlias(c.green)} enable|disable <id>
${MODULE_HELP_LABEL} enable|disable <id>
Toggle module (needs db-server)
${c.green("version")} Show version
${c.green("help")} Show this
Expand Down
7 changes: 7 additions & 0 deletions tools/check-ootb.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,13 @@ async function main() {
if (!all.body.module_tokens || typeof all.body.module_tokens !== "object") {
throw new Error("configs/all 缺少 module_tokens");
}
// LSP:banned_items 须为 string[](与 ConfigManager / GET /banned_items 同源),勿 {item_id}
if (!Array.isArray(all.body.banned_items)) {
throw new Error("configs/all.banned_items 须为数组");
}
if (all.body.banned_items.some((x) => typeof x !== "string")) {
throw new Error("configs/all.banned_items 须为 string[]");
}
pass(`db-server 启动 + 平台 API (modules=${mods.body.modules.length})`);
} catch (e) {
fail("db-server 启动 + 平台 API", e.message);
Expand Down
Loading