Compare commits

...

3 commits

Author SHA1 Message Date
Nicolas Meienberger
8e57031cfd fix: close read line interfaces on spawn errors 2026-03-21 19:11:39 +01:00
Nicolas Meienberger
0082382c57 fix(rclone): correctly propagate RCLONE_NO_CHECK_CERTIFICATE env variable
Closes #688
2026-03-21 18:43:50 +01:00
Nicolas Meienberger
df0d9da4a3 fix(notifications): fix telegram shoutrrr URL encoding
Closes #690
2026-03-21 18:42:20 +01:00
5 changed files with 35 additions and 9 deletions

View file

@ -182,19 +182,19 @@ describe("notification shoutrrr URL builders", () => {
expect( expect(
buildTelegramShoutrrrUrl({ buildTelegramShoutrrrUrl({
type: "telegram", type: "telegram",
botToken: "bot-token", botToken: "123456:ABCdef",
chatId: "chat-id", chatId: "chat-id",
}), }),
).toBe("telegram://bot-token@telegram?channels=chat-id"); ).toBe("telegram://123456:ABCdef@telegram?channels=chat-id");
expect( expect(
buildTelegramShoutrrrUrl({ buildTelegramShoutrrrUrl({
type: "telegram", type: "telegram",
botToken: "bot-token", botToken: "123456:ABCdef",
chatId: "chat-id", chatId: "chat-id",
threadId: "thread-id", threadId: "thread-id",
}), }),
).toBe("telegram://bot-token@telegram?channels=chat-id%3Athread-id"); ).toBe("telegram://123456:ABCdef@telegram?channels=chat-id:thread-id");
}); });
test("builds generic URLs with reserved params, transport flags, and headers", () => { test("builds generic URLs with reserved params, transport flags, and headers", () => {

View file

@ -1,8 +1,9 @@
import type { NotificationConfig } from "~/schemas/notifications"; import type { NotificationConfig } from "~/schemas/notifications";
export const buildTelegramShoutrrrUrl = (config: Extract<NotificationConfig, { type: "telegram" }>) => { export const buildTelegramShoutrrrUrl = (config: Extract<NotificationConfig, { type: "telegram" }>) => {
const shoutrrrUrl = new URL("telegram://telegram"); let shoutrrrUrl = `telegram://${config.botToken}@telegram?channels=${config.chatId}`;
shoutrrrUrl.username = config.botToken; if (config.threadId) {
shoutrrrUrl.searchParams.set("channels", `${config.chatId}${config.threadId ? `:${config.threadId}` : ""}`); shoutrrrUrl += `:${config.threadId}`;
return shoutrrrUrl.toString().replace("/?", "?"); }
return shoutrrrUrl;
}; };

View file

@ -261,6 +261,23 @@ describe("buildEnv", () => {
}); });
}); });
describe("rclone backend", () => {
test("sets RCLONE_NO_CHECK_CERTIFICATE when insecureTls is enabled", async () => {
const env = await buildEnvForTest(
withCustomPassword({
backend: "rclone" as const,
remote: "my-remote",
path: "/backups",
insecureTls: true,
}),
"org-1",
);
expect(env._INSECURE_TLS).toBe("true");
expect(env.RCLONE_NO_CHECK_CERTIFICATE).toBe("true");
});
});
describe("sftp backend", () => { describe("sftp backend", () => {
const baseSftpConfig = withCustomPassword({ const baseSftpConfig = withCustomPassword({
backend: "sftp" as const, backend: "sftp" as const,

View file

@ -142,6 +142,10 @@ export const buildEnv = async (
if (config.insecureTls) { if (config.insecureTls) {
env._INSECURE_TLS = "true"; env._INSECURE_TLS = "true";
if (config.backend === "rclone") {
env.RCLONE_NO_CHECK_CERTIFICATE = "true";
}
} }
return env; return env;

View file

@ -83,11 +83,12 @@ export function safeSpawn(params: SafeSpawnParams): Promise<SpawnResult> {
child.stderr.setEncoding("utf8"); child.stderr.setEncoding("utf8");
const rlErr = createInterface({ input: child.stderr }); const rlErr = createInterface({ input: child.stderr });
let rl: ReturnType<typeof createInterface> | undefined;
if (stdoutMode === "lines") { if (stdoutMode === "lines") {
child.stdout.setEncoding("utf8"); child.stdout.setEncoding("utf8");
const rl = createInterface({ input: child.stdout }); rl = createInterface({ input: child.stdout });
rl.on("line", (line) => { rl.on("line", (line) => {
if (onStdout) onStdout(line); if (onStdout) onStdout(line);
@ -107,6 +108,9 @@ export function safeSpawn(params: SafeSpawnParams): Promise<SpawnResult> {
}); });
child.on("error", (err) => { child.on("error", (err) => {
rlErr.close();
rl?.close();
resolve({ resolve({
exitCode: -1, exitCode: -1,
summary: lastStdout, summary: lastStdout,