fix: prevent config clobbering
This commit is contained in:
@@ -133,10 +133,12 @@ describe("applyConfigSnapshot", () => {
|
||||
const state = createState();
|
||||
applyConfigSnapshot(state, {
|
||||
config: {
|
||||
telegram: {},
|
||||
discord: {},
|
||||
signal: {},
|
||||
imessage: {},
|
||||
channels: {
|
||||
telegram: {},
|
||||
discord: {},
|
||||
signal: {},
|
||||
imessage: {},
|
||||
},
|
||||
},
|
||||
valid: true,
|
||||
issues: [],
|
||||
@@ -171,7 +173,7 @@ describe("updateConfigFormValue", () => {
|
||||
it("seeds from snapshot when form is null", () => {
|
||||
const state = createState();
|
||||
state.configSnapshot = {
|
||||
config: { telegram: { botToken: "t" }, gateway: { mode: "local" } },
|
||||
config: { channels: { telegram: { botToken: "t" } }, gateway: { mode: "local" } },
|
||||
valid: true,
|
||||
issues: [],
|
||||
raw: "{}",
|
||||
@@ -181,7 +183,7 @@ describe("updateConfigFormValue", () => {
|
||||
|
||||
expect(state.configFormDirty).toBe(true);
|
||||
expect(state.configForm).toEqual({
|
||||
telegram: { botToken: "t" },
|
||||
channels: { telegram: { botToken: "t" } },
|
||||
gateway: { mode: "local", port: 18789 },
|
||||
});
|
||||
});
|
||||
@@ -212,11 +214,15 @@ describe("applyConfig", () => {
|
||||
state.applySessionKey = "agent:main:whatsapp:dm:+15555550123";
|
||||
state.configFormMode = "raw";
|
||||
state.configRaw = "{\n agent: { workspace: \"~/clawd\" }\n}\n";
|
||||
state.configSnapshot = {
|
||||
hash: "hash-123",
|
||||
};
|
||||
|
||||
await applyConfig(state);
|
||||
|
||||
expect(request).toHaveBeenCalledWith("config.apply", {
|
||||
raw: "{\n agent: { workspace: \"~/clawd\" }\n}\n",
|
||||
baseHash: "hash-123",
|
||||
sessionKey: "agent:main:whatsapp:dm:+15555550123",
|
||||
});
|
||||
});
|
||||
|
||||
@@ -115,11 +115,12 @@ export function applyConfigSnapshot(state: ConfigState, snapshot: ConfigSnapshot
|
||||
state.configIssues = Array.isArray(snapshot.issues) ? snapshot.issues : [];
|
||||
|
||||
const config = snapshot.config ?? {};
|
||||
const telegram = (config.telegram ?? {}) as Record<string, unknown>;
|
||||
const discord = (config.discord ?? {}) as Record<string, unknown>;
|
||||
const slack = (config.slack ?? {}) as Record<string, unknown>;
|
||||
const signal = (config.signal ?? {}) as Record<string, unknown>;
|
||||
const imessage = (config.imessage ?? {}) as Record<string, unknown>;
|
||||
const channels = (config.channels ?? {}) as Record<string, unknown>;
|
||||
const telegram = (channels.telegram ?? config.telegram ?? {}) as Record<string, unknown>;
|
||||
const discord = (channels.discord ?? config.discord ?? {}) as Record<string, unknown>;
|
||||
const slack = (channels.slack ?? config.slack ?? {}) as Record<string, unknown>;
|
||||
const signal = (channels.signal ?? config.signal ?? {}) as Record<string, unknown>;
|
||||
const imessage = (channels.imessage ?? config.imessage ?? {}) as Record<string, unknown>;
|
||||
const toList = (value: unknown) =>
|
||||
Array.isArray(value)
|
||||
? value
|
||||
@@ -406,7 +407,12 @@ export async function saveConfig(state: ConfigState) {
|
||||
state.configFormMode === "form" && state.configForm
|
||||
? serializeConfigForm(state.configForm)
|
||||
: state.configRaw;
|
||||
await state.client.request("config.set", { raw });
|
||||
const baseHash = state.configSnapshot?.hash;
|
||||
if (!baseHash) {
|
||||
state.lastError = "Config hash missing; reload and retry.";
|
||||
return;
|
||||
}
|
||||
await state.client.request("config.set", { raw, baseHash });
|
||||
state.configFormDirty = false;
|
||||
await loadConfig(state);
|
||||
} catch (err) {
|
||||
@@ -425,8 +431,14 @@ export async function applyConfig(state: ConfigState) {
|
||||
state.configFormMode === "form" && state.configForm
|
||||
? serializeConfigForm(state.configForm)
|
||||
: state.configRaw;
|
||||
const baseHash = state.configSnapshot?.hash;
|
||||
if (!baseHash) {
|
||||
state.lastError = "Config hash missing; reload and retry.";
|
||||
return;
|
||||
}
|
||||
await state.client.request("config.apply", {
|
||||
raw,
|
||||
baseHash,
|
||||
sessionKey: state.applySessionKey,
|
||||
});
|
||||
state.configFormDirty = false;
|
||||
|
||||
@@ -13,70 +13,68 @@ export async function saveDiscordConfig(state: ConnectionsState) {
|
||||
state.discordSaving = true;
|
||||
state.discordConfigStatus = null;
|
||||
try {
|
||||
const base = state.configSnapshot?.config ?? {};
|
||||
const config = { ...base } as Record<string, unknown>;
|
||||
const discord = { ...(config.discord ?? {}) } as Record<string, unknown>;
|
||||
const baseHash = state.configSnapshot?.hash;
|
||||
if (!baseHash) {
|
||||
state.discordConfigStatus = "Config hash missing; reload and retry.";
|
||||
return;
|
||||
}
|
||||
const discord: Record<string, unknown> = {};
|
||||
const form = state.discordForm;
|
||||
|
||||
if (form.enabled) {
|
||||
delete discord.enabled;
|
||||
discord.enabled = null;
|
||||
} else {
|
||||
discord.enabled = false;
|
||||
}
|
||||
|
||||
if (!state.discordTokenLocked) {
|
||||
const token = form.token.trim();
|
||||
if (token) discord.token = token;
|
||||
else delete discord.token;
|
||||
discord.token = token || null;
|
||||
}
|
||||
|
||||
const allowFrom = parseList(form.allowFrom);
|
||||
const groupChannels = parseList(form.groupChannels);
|
||||
const dm = { ...(discord.dm ?? {}) } as Record<string, unknown>;
|
||||
if (form.dmEnabled) delete dm.enabled;
|
||||
else dm.enabled = false;
|
||||
if (allowFrom.length > 0) dm.allowFrom = allowFrom;
|
||||
else delete dm.allowFrom;
|
||||
if (form.groupEnabled) dm.groupEnabled = true;
|
||||
else delete dm.groupEnabled;
|
||||
if (groupChannels.length > 0) dm.groupChannels = groupChannels;
|
||||
else delete dm.groupChannels;
|
||||
if (Object.keys(dm).length > 0) discord.dm = dm;
|
||||
else delete discord.dm;
|
||||
const dm: Record<string, unknown> = {
|
||||
enabled: form.dmEnabled ? null : false,
|
||||
allowFrom: allowFrom.length > 0 ? allowFrom : null,
|
||||
groupEnabled: form.groupEnabled ? true : null,
|
||||
groupChannels: groupChannels.length > 0 ? groupChannels : null,
|
||||
};
|
||||
discord.dm = dm;
|
||||
|
||||
const mediaMaxMb = Number(form.mediaMaxMb);
|
||||
if (Number.isFinite(mediaMaxMb) && mediaMaxMb > 0) {
|
||||
discord.mediaMaxMb = mediaMaxMb;
|
||||
} else {
|
||||
delete discord.mediaMaxMb;
|
||||
discord.mediaMaxMb = null;
|
||||
}
|
||||
|
||||
const historyLimitRaw = form.historyLimit.trim();
|
||||
if (historyLimitRaw.length === 0) {
|
||||
delete discord.historyLimit;
|
||||
discord.historyLimit = null;
|
||||
} else {
|
||||
const historyLimit = Number(historyLimitRaw);
|
||||
if (Number.isFinite(historyLimit) && historyLimit >= 0) {
|
||||
discord.historyLimit = historyLimit;
|
||||
} else {
|
||||
delete discord.historyLimit;
|
||||
discord.historyLimit = null;
|
||||
}
|
||||
}
|
||||
|
||||
const chunkLimitRaw = form.textChunkLimit.trim();
|
||||
if (chunkLimitRaw.length === 0) {
|
||||
delete discord.textChunkLimit;
|
||||
discord.textChunkLimit = null;
|
||||
} else {
|
||||
const chunkLimit = Number(chunkLimitRaw);
|
||||
if (Number.isFinite(chunkLimit) && chunkLimit > 0) {
|
||||
discord.textChunkLimit = chunkLimit;
|
||||
} else {
|
||||
delete discord.textChunkLimit;
|
||||
discord.textChunkLimit = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (form.replyToMode === "off") {
|
||||
delete discord.replyToMode;
|
||||
discord.replyToMode = null;
|
||||
} else {
|
||||
discord.replyToMode = form.replyToMode;
|
||||
}
|
||||
@@ -114,7 +112,7 @@ export async function saveDiscordConfig(state: ConnectionsState) {
|
||||
guilds[key] = entry;
|
||||
});
|
||||
if (Object.keys(guilds).length > 0) discord.guilds = guilds;
|
||||
else delete discord.guilds;
|
||||
else discord.guilds = null;
|
||||
|
||||
const actions: Partial<DiscordActionForm> = {};
|
||||
const applyAction = (key: keyof DiscordActionForm) => {
|
||||
@@ -139,36 +137,33 @@ export async function saveDiscordConfig(state: ConnectionsState) {
|
||||
if (Object.keys(actions).length > 0) {
|
||||
discord.actions = actions;
|
||||
} else {
|
||||
delete discord.actions;
|
||||
discord.actions = null;
|
||||
}
|
||||
|
||||
const slash = { ...(discord.slashCommand ?? {}) } as Record<string, unknown>;
|
||||
if (form.slashEnabled) {
|
||||
slash.enabled = true;
|
||||
} else {
|
||||
delete slash.enabled;
|
||||
slash.enabled = null;
|
||||
}
|
||||
if (form.slashName.trim()) slash.name = form.slashName.trim();
|
||||
else delete slash.name;
|
||||
else slash.name = null;
|
||||
if (form.slashSessionPrefix.trim())
|
||||
slash.sessionPrefix = form.slashSessionPrefix.trim();
|
||||
else delete slash.sessionPrefix;
|
||||
else slash.sessionPrefix = null;
|
||||
if (form.slashEphemeral) {
|
||||
delete slash.ephemeral;
|
||||
slash.ephemeral = null;
|
||||
} else {
|
||||
slash.ephemeral = false;
|
||||
}
|
||||
if (Object.keys(slash).length > 0) discord.slashCommand = slash;
|
||||
else delete discord.slashCommand;
|
||||
discord.slashCommand = Object.keys(slash).length > 0 ? slash : null;
|
||||
|
||||
if (Object.keys(discord).length > 0) {
|
||||
config.discord = discord;
|
||||
} else {
|
||||
delete config.discord;
|
||||
}
|
||||
|
||||
const raw = `${JSON.stringify(config, null, 2).trimEnd()}\n`;
|
||||
await state.client.request("config.set", { raw });
|
||||
const raw = `${JSON.stringify(
|
||||
{ channels: { discord } },
|
||||
null,
|
||||
2,
|
||||
).trimEnd()}\n`;
|
||||
await state.client.request("config.patch", { raw, baseHash });
|
||||
state.discordConfigStatus = "Saved. Restart gateway if needed.";
|
||||
} catch (err) {
|
||||
state.discordConfigStatus = String(err);
|
||||
@@ -176,4 +171,3 @@ export async function saveDiscordConfig(state: ConnectionsState) {
|
||||
state.discordSaving = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,57 +7,53 @@ export async function saveIMessageConfig(state: ConnectionsState) {
|
||||
state.imessageSaving = true;
|
||||
state.imessageConfigStatus = null;
|
||||
try {
|
||||
const base = state.configSnapshot?.config ?? {};
|
||||
const config = { ...base } as Record<string, unknown>;
|
||||
const imessage = { ...(config.imessage ?? {}) } as Record<string, unknown>;
|
||||
const baseHash = state.configSnapshot?.hash;
|
||||
if (!baseHash) {
|
||||
state.imessageConfigStatus = "Config hash missing; reload and retry.";
|
||||
return;
|
||||
}
|
||||
const imessage: Record<string, unknown> = {};
|
||||
const form = state.imessageForm;
|
||||
|
||||
if (form.enabled) {
|
||||
delete imessage.enabled;
|
||||
imessage.enabled = null;
|
||||
} else {
|
||||
imessage.enabled = false;
|
||||
}
|
||||
|
||||
const cliPath = form.cliPath.trim();
|
||||
if (cliPath) imessage.cliPath = cliPath;
|
||||
else delete imessage.cliPath;
|
||||
imessage.cliPath = cliPath || null;
|
||||
|
||||
const dbPath = form.dbPath.trim();
|
||||
if (dbPath) imessage.dbPath = dbPath;
|
||||
else delete imessage.dbPath;
|
||||
imessage.dbPath = dbPath || null;
|
||||
|
||||
if (form.service === "auto") {
|
||||
delete imessage.service;
|
||||
imessage.service = null;
|
||||
} else {
|
||||
imessage.service = form.service;
|
||||
}
|
||||
|
||||
const region = form.region.trim();
|
||||
if (region) imessage.region = region;
|
||||
else delete imessage.region;
|
||||
imessage.region = region || null;
|
||||
|
||||
const allowFrom = parseList(form.allowFrom);
|
||||
if (allowFrom.length > 0) imessage.allowFrom = allowFrom;
|
||||
else delete imessage.allowFrom;
|
||||
imessage.allowFrom = allowFrom.length > 0 ? allowFrom : null;
|
||||
|
||||
if (form.includeAttachments) imessage.includeAttachments = true;
|
||||
else delete imessage.includeAttachments;
|
||||
imessage.includeAttachments = form.includeAttachments ? true : null;
|
||||
|
||||
const mediaMaxMb = Number(form.mediaMaxMb);
|
||||
if (Number.isFinite(mediaMaxMb) && mediaMaxMb > 0) {
|
||||
imessage.mediaMaxMb = mediaMaxMb;
|
||||
} else {
|
||||
delete imessage.mediaMaxMb;
|
||||
imessage.mediaMaxMb = null;
|
||||
}
|
||||
|
||||
if (Object.keys(imessage).length > 0) {
|
||||
config.imessage = imessage;
|
||||
} else {
|
||||
delete config.imessage;
|
||||
}
|
||||
|
||||
const raw = `${JSON.stringify(config, null, 2).trimEnd()}\n`;
|
||||
await state.client.request("config.set", { raw });
|
||||
const raw = `${JSON.stringify(
|
||||
{ channels: { imessage } },
|
||||
null,
|
||||
2,
|
||||
).trimEnd()}\n`;
|
||||
await state.client.request("config.patch", { raw, baseHash });
|
||||
state.imessageConfigStatus = "Saved. Restart gateway if needed.";
|
||||
} catch (err) {
|
||||
state.imessageConfigStatus = String(err);
|
||||
@@ -65,4 +61,3 @@ export async function saveIMessageConfig(state: ConnectionsState) {
|
||||
state.imessageSaving = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,42 +7,41 @@ export async function saveSignalConfig(state: ConnectionsState) {
|
||||
state.signalSaving = true;
|
||||
state.signalConfigStatus = null;
|
||||
try {
|
||||
const base = state.configSnapshot?.config ?? {};
|
||||
const config = { ...base } as Record<string, unknown>;
|
||||
const signal = { ...(config.signal ?? {}) } as Record<string, unknown>;
|
||||
const baseHash = state.configSnapshot?.hash;
|
||||
if (!baseHash) {
|
||||
state.signalConfigStatus = "Config hash missing; reload and retry.";
|
||||
return;
|
||||
}
|
||||
const signal: Record<string, unknown> = {};
|
||||
const form = state.signalForm;
|
||||
|
||||
if (form.enabled) {
|
||||
delete signal.enabled;
|
||||
signal.enabled = null;
|
||||
} else {
|
||||
signal.enabled = false;
|
||||
}
|
||||
|
||||
const account = form.account.trim();
|
||||
if (account) signal.account = account;
|
||||
else delete signal.account;
|
||||
signal.account = account || null;
|
||||
|
||||
const httpUrl = form.httpUrl.trim();
|
||||
if (httpUrl) signal.httpUrl = httpUrl;
|
||||
else delete signal.httpUrl;
|
||||
signal.httpUrl = httpUrl || null;
|
||||
|
||||
const httpHost = form.httpHost.trim();
|
||||
if (httpHost) signal.httpHost = httpHost;
|
||||
else delete signal.httpHost;
|
||||
signal.httpHost = httpHost || null;
|
||||
|
||||
const httpPort = Number(form.httpPort);
|
||||
if (Number.isFinite(httpPort) && httpPort > 0) {
|
||||
signal.httpPort = httpPort;
|
||||
} else {
|
||||
delete signal.httpPort;
|
||||
signal.httpPort = null;
|
||||
}
|
||||
|
||||
const cliPath = form.cliPath.trim();
|
||||
if (cliPath) signal.cliPath = cliPath;
|
||||
else delete signal.cliPath;
|
||||
signal.cliPath = cliPath || null;
|
||||
|
||||
if (form.autoStart) {
|
||||
delete signal.autoStart;
|
||||
signal.autoStart = null;
|
||||
} else {
|
||||
signal.autoStart = false;
|
||||
}
|
||||
@@ -50,35 +49,29 @@ export async function saveSignalConfig(state: ConnectionsState) {
|
||||
if (form.receiveMode === "on-start" || form.receiveMode === "manual") {
|
||||
signal.receiveMode = form.receiveMode;
|
||||
} else {
|
||||
delete signal.receiveMode;
|
||||
signal.receiveMode = null;
|
||||
}
|
||||
|
||||
if (form.ignoreAttachments) signal.ignoreAttachments = true;
|
||||
else delete signal.ignoreAttachments;
|
||||
if (form.ignoreStories) signal.ignoreStories = true;
|
||||
else delete signal.ignoreStories;
|
||||
if (form.sendReadReceipts) signal.sendReadReceipts = true;
|
||||
else delete signal.sendReadReceipts;
|
||||
signal.ignoreAttachments = form.ignoreAttachments ? true : null;
|
||||
signal.ignoreStories = form.ignoreStories ? true : null;
|
||||
signal.sendReadReceipts = form.sendReadReceipts ? true : null;
|
||||
|
||||
const allowFrom = parseList(form.allowFrom);
|
||||
if (allowFrom.length > 0) signal.allowFrom = allowFrom;
|
||||
else delete signal.allowFrom;
|
||||
signal.allowFrom = allowFrom.length > 0 ? allowFrom : null;
|
||||
|
||||
const mediaMaxMb = Number(form.mediaMaxMb);
|
||||
if (Number.isFinite(mediaMaxMb) && mediaMaxMb > 0) {
|
||||
signal.mediaMaxMb = mediaMaxMb;
|
||||
} else {
|
||||
delete signal.mediaMaxMb;
|
||||
signal.mediaMaxMb = null;
|
||||
}
|
||||
|
||||
if (Object.keys(signal).length > 0) {
|
||||
config.signal = signal;
|
||||
} else {
|
||||
delete config.signal;
|
||||
}
|
||||
|
||||
const raw = `${JSON.stringify(config, null, 2).trimEnd()}\n`;
|
||||
await state.client.request("config.set", { raw });
|
||||
const raw = `${JSON.stringify(
|
||||
{ channels: { signal } },
|
||||
null,
|
||||
2,
|
||||
).trimEnd()}\n`;
|
||||
await state.client.request("config.patch", { raw, baseHash });
|
||||
state.signalConfigStatus = "Saved. Restart gateway if needed.";
|
||||
} catch (err) {
|
||||
state.signalConfigStatus = String(err);
|
||||
@@ -86,4 +79,3 @@ export async function saveSignalConfig(state: ConnectionsState) {
|
||||
state.signalSaving = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,60 +8,58 @@ export async function saveSlackConfig(state: ConnectionsState) {
|
||||
state.slackSaving = true;
|
||||
state.slackConfigStatus = null;
|
||||
try {
|
||||
const base = state.configSnapshot?.config ?? {};
|
||||
const config = { ...base } as Record<string, unknown>;
|
||||
const slack = { ...(config.slack ?? {}) } as Record<string, unknown>;
|
||||
const baseHash = state.configSnapshot?.hash;
|
||||
if (!baseHash) {
|
||||
state.slackConfigStatus = "Config hash missing; reload and retry.";
|
||||
return;
|
||||
}
|
||||
const slack: Record<string, unknown> = {};
|
||||
const form = state.slackForm;
|
||||
|
||||
if (form.enabled) {
|
||||
delete slack.enabled;
|
||||
slack.enabled = null;
|
||||
} else {
|
||||
slack.enabled = false;
|
||||
}
|
||||
|
||||
if (!state.slackTokenLocked) {
|
||||
const token = form.botToken.trim();
|
||||
if (token) slack.botToken = token;
|
||||
else delete slack.botToken;
|
||||
slack.botToken = token || null;
|
||||
}
|
||||
if (!state.slackAppTokenLocked) {
|
||||
const token = form.appToken.trim();
|
||||
if (token) slack.appToken = token;
|
||||
else delete slack.appToken;
|
||||
slack.appToken = token || null;
|
||||
}
|
||||
|
||||
const dm = { ...(slack.dm ?? {}) } as Record<string, unknown>;
|
||||
const dm: Record<string, unknown> = {};
|
||||
dm.enabled = form.dmEnabled;
|
||||
const allowFrom = parseList(form.allowFrom);
|
||||
if (allowFrom.length > 0) dm.allowFrom = allowFrom;
|
||||
else delete dm.allowFrom;
|
||||
dm.allowFrom = allowFrom.length > 0 ? allowFrom : null;
|
||||
if (form.groupEnabled) {
|
||||
dm.groupEnabled = true;
|
||||
} else {
|
||||
delete dm.groupEnabled;
|
||||
dm.groupEnabled = null;
|
||||
}
|
||||
const groupChannels = parseList(form.groupChannels);
|
||||
if (groupChannels.length > 0) dm.groupChannels = groupChannels;
|
||||
else delete dm.groupChannels;
|
||||
if (Object.keys(dm).length > 0) slack.dm = dm;
|
||||
else delete slack.dm;
|
||||
dm.groupChannels = groupChannels.length > 0 ? groupChannels : null;
|
||||
slack.dm = dm;
|
||||
|
||||
const mediaMaxMb = Number.parseFloat(form.mediaMaxMb);
|
||||
if (Number.isFinite(mediaMaxMb) && mediaMaxMb > 0) {
|
||||
slack.mediaMaxMb = mediaMaxMb;
|
||||
} else {
|
||||
delete slack.mediaMaxMb;
|
||||
slack.mediaMaxMb = null;
|
||||
}
|
||||
|
||||
const textChunkLimit = Number.parseInt(form.textChunkLimit, 10);
|
||||
if (Number.isFinite(textChunkLimit) && textChunkLimit > 0) {
|
||||
slack.textChunkLimit = textChunkLimit;
|
||||
} else {
|
||||
delete slack.textChunkLimit;
|
||||
slack.textChunkLimit = null;
|
||||
}
|
||||
|
||||
if (form.reactionNotifications === "own") {
|
||||
delete slack.reactionNotifications;
|
||||
slack.reactionNotifications = null;
|
||||
} else {
|
||||
slack.reactionNotifications = form.reactionNotifications;
|
||||
}
|
||||
@@ -69,27 +67,26 @@ export async function saveSlackConfig(state: ConnectionsState) {
|
||||
if (reactionAllowlist.length > 0) {
|
||||
slack.reactionAllowlist = reactionAllowlist;
|
||||
} else {
|
||||
delete slack.reactionAllowlist;
|
||||
slack.reactionAllowlist = null;
|
||||
}
|
||||
|
||||
const slash = { ...(slack.slashCommand ?? {}) } as Record<string, unknown>;
|
||||
const slash: Record<string, unknown> = {};
|
||||
if (form.slashEnabled) {
|
||||
slash.enabled = true;
|
||||
} else {
|
||||
delete slash.enabled;
|
||||
slash.enabled = null;
|
||||
}
|
||||
if (form.slashName.trim()) slash.name = form.slashName.trim();
|
||||
else delete slash.name;
|
||||
else slash.name = null;
|
||||
if (form.slashSessionPrefix.trim())
|
||||
slash.sessionPrefix = form.slashSessionPrefix.trim();
|
||||
else delete slash.sessionPrefix;
|
||||
else slash.sessionPrefix = null;
|
||||
if (form.slashEphemeral) {
|
||||
delete slash.ephemeral;
|
||||
slash.ephemeral = null;
|
||||
} else {
|
||||
slash.ephemeral = false;
|
||||
}
|
||||
if (Object.keys(slash).length > 0) slack.slashCommand = slash;
|
||||
else delete slack.slashCommand;
|
||||
slack.slashCommand = slash;
|
||||
|
||||
const actions: Partial<SlackActionForm> = {};
|
||||
const applyAction = (key: keyof SlackActionForm) => {
|
||||
@@ -104,7 +101,7 @@ export async function saveSlackConfig(state: ConnectionsState) {
|
||||
if (Object.keys(actions).length > 0) {
|
||||
slack.actions = actions;
|
||||
} else {
|
||||
delete slack.actions;
|
||||
slack.actions = null;
|
||||
}
|
||||
|
||||
const channels = form.channels
|
||||
@@ -123,17 +120,15 @@ export async function saveSlackConfig(state: ConnectionsState) {
|
||||
if (channels.length > 0) {
|
||||
slack.channels = Object.fromEntries(channels);
|
||||
} else {
|
||||
delete slack.channels;
|
||||
slack.channels = null;
|
||||
}
|
||||
|
||||
if (Object.keys(slack).length > 0) {
|
||||
config.slack = slack;
|
||||
} else {
|
||||
delete config.slack;
|
||||
}
|
||||
|
||||
const raw = `${JSON.stringify(config, null, 2).trimEnd()}\n`;
|
||||
await state.client.request("config.set", { raw });
|
||||
const raw = `${JSON.stringify(
|
||||
{ channels: { slack } },
|
||||
null,
|
||||
2,
|
||||
).trimEnd()}\n`;
|
||||
await state.client.request("config.patch", { raw, baseHash });
|
||||
state.slackConfigStatus = "Saved. Restart gateway if needed.";
|
||||
} catch (err) {
|
||||
state.slackConfigStatus = String(err);
|
||||
|
||||
@@ -165,56 +165,53 @@ export async function saveTelegramConfig(state: ConnectionsState) {
|
||||
}
|
||||
}
|
||||
const base = state.configSnapshot?.config ?? {};
|
||||
const config = { ...base } as Record<string, unknown>;
|
||||
const telegram = { ...(config.telegram ?? {}) } as Record<string, unknown>;
|
||||
const channels = (base.channels ?? {}) as Record<string, unknown>;
|
||||
const telegram = {
|
||||
...(channels.telegram ?? base.telegram ?? {}),
|
||||
} as Record<string, unknown>;
|
||||
if (!state.telegramTokenLocked) {
|
||||
const token = state.telegramForm.token.trim();
|
||||
if (token) telegram.botToken = token;
|
||||
else delete telegram.botToken;
|
||||
telegram.botToken = token || null;
|
||||
}
|
||||
const groups =
|
||||
telegram.groups && typeof telegram.groups === "object"
|
||||
? ({ ...(telegram.groups as Record<string, unknown>) } as Record<
|
||||
string,
|
||||
unknown
|
||||
>)
|
||||
: {};
|
||||
const groupsPatch: Record<string, unknown> = {};
|
||||
if (state.telegramForm.groupsWildcardEnabled) {
|
||||
const existingGroups = telegram.groups as Record<string, unknown> | undefined;
|
||||
const defaultGroup =
|
||||
groups["*"] && typeof groups["*"] === "object"
|
||||
? ({ ...(groups["*"] as Record<string, unknown>) } as Record<
|
||||
existingGroups?.["*"] && typeof existingGroups["*"] === "object"
|
||||
? ({ ...(existingGroups["*"] as Record<string, unknown>) } as Record<
|
||||
string,
|
||||
unknown
|
||||
>)
|
||||
: {};
|
||||
defaultGroup.requireMention = state.telegramForm.requireMention;
|
||||
groups["*"] = defaultGroup;
|
||||
telegram.groups = groups;
|
||||
} else if (groups["*"]) {
|
||||
delete groups["*"];
|
||||
if (Object.keys(groups).length > 0) telegram.groups = groups;
|
||||
else delete telegram.groups;
|
||||
groupsPatch["*"] = defaultGroup;
|
||||
} else {
|
||||
groupsPatch["*"] = null;
|
||||
}
|
||||
delete telegram.requireMention;
|
||||
telegram.groups = groupsPatch;
|
||||
telegram.requireMention = null;
|
||||
const allowFrom = parseList(state.telegramForm.allowFrom);
|
||||
if (allowFrom.length > 0) telegram.allowFrom = allowFrom;
|
||||
else delete telegram.allowFrom;
|
||||
telegram.allowFrom = allowFrom.length > 0 ? allowFrom : null;
|
||||
const proxy = state.telegramForm.proxy.trim();
|
||||
if (proxy) telegram.proxy = proxy;
|
||||
else delete telegram.proxy;
|
||||
telegram.proxy = proxy || null;
|
||||
const webhookUrl = state.telegramForm.webhookUrl.trim();
|
||||
if (webhookUrl) telegram.webhookUrl = webhookUrl;
|
||||
else delete telegram.webhookUrl;
|
||||
telegram.webhookUrl = webhookUrl || null;
|
||||
const webhookSecret = state.telegramForm.webhookSecret.trim();
|
||||
if (webhookSecret) telegram.webhookSecret = webhookSecret;
|
||||
else delete telegram.webhookSecret;
|
||||
telegram.webhookSecret = webhookSecret || null;
|
||||
const webhookPath = state.telegramForm.webhookPath.trim();
|
||||
if (webhookPath) telegram.webhookPath = webhookPath;
|
||||
else delete telegram.webhookPath;
|
||||
telegram.webhookPath = webhookPath || null;
|
||||
|
||||
config.telegram = telegram;
|
||||
const raw = `${JSON.stringify(config, null, 2).trimEnd()}\n`;
|
||||
await state.client.request("config.set", { raw });
|
||||
const baseHash = state.configSnapshot?.hash;
|
||||
if (!baseHash) {
|
||||
state.telegramConfigStatus = "Config hash missing; reload and retry.";
|
||||
return;
|
||||
}
|
||||
const raw = `${JSON.stringify(
|
||||
{ channels: { telegram } },
|
||||
null,
|
||||
2,
|
||||
).trimEnd()}\n`;
|
||||
await state.client.request("config.patch", { raw, baseHash });
|
||||
state.telegramConfigStatus = "Saved. Restart gateway if needed.";
|
||||
} catch (err) {
|
||||
state.telegramConfigStatus = String(err);
|
||||
|
||||
@@ -214,6 +214,7 @@ export type ConfigSnapshot = {
|
||||
path?: string | null;
|
||||
exists?: boolean | null;
|
||||
raw?: string | null;
|
||||
hash?: string | null;
|
||||
parsed?: unknown;
|
||||
valid?: boolean | null;
|
||||
config?: Record<string, unknown> | null;
|
||||
|
||||
Reference in New Issue
Block a user