This commit is contained in:
Anaïs Saraiva
2026-03-29 19:53:02 +02:00
parent ddebcd6437
commit 75f48591e9
2 changed files with 22 additions and 3 deletions
+17 -1
View File
@@ -4,6 +4,18 @@ import { hostedMailboxSchema } from "../lib/validation";
import { runDocker } from "./runner"; import { runDocker } from "./runner";
const MAILDIR_FOLDER = "INBOX"; const MAILDIR_FOLDER = "INBOX";
const infrastructureErrorPatterns = [
/cannot connect to the docker daemon/i,
/permission denied/i,
/no such container/i,
/is not running/i,
/executable file not found/i,
/no such file or directory/i,
/command not found/i
];
const looksLikeInfrastructureFailure = (output: string): boolean =>
infrastructureErrorPatterns.some((pattern) => pattern.test(output));
const localPartRegex = /^[A-Z0-9.!#$%&'*+/=?^_`{|}~-]{1,64}$/i; const localPartRegex = /^[A-Z0-9.!#$%&'*+/=?^_`{|}~-]{1,64}$/i;
@@ -29,6 +41,11 @@ export const authenticateMailbox = async (email: string, password: string): Prom
{ allowNonZero: true } { allowNonZero: true }
); );
const combinedOutput = `${result.stdout}\n${result.stderr}`.trim();
if (result.exitCode !== 0 && looksLikeInfrastructureFailure(combinedOutput)) {
throw new HttpError(502, "Mail authentication backend is unavailable.");
}
return result.exitCode === 0; return result.exitCode === 0;
}; };
@@ -85,4 +102,3 @@ export const removeForwardingAlias = async (sourceEmail: string, targetEmail: st
}; };
export const getSupportedFolder = (): string => MAILDIR_FOLDER; export const getSupportedFolder = (): string => MAILDIR_FOLDER;
+5 -2
View File
@@ -1,6 +1,10 @@
import { resolve } from "node:path"; import { resolve } from "node:path";
import { z } from "zod"; import { z } from "zod";
const normalizedEnv = Object.fromEntries(
Object.entries(Bun.env).map(([key, value]) => [key, value === "" ? undefined : value])
);
const splitList = (value: string | undefined, fallback: string): string[] => const splitList = (value: string | undefined, fallback: string): string[] =>
(value ?? fallback) (value ?? fallback)
.split(",") .split(",")
@@ -35,7 +39,7 @@ const envSchema = z.object({
TOTP_ENCRYPTION_KEY: z.string().optional() TOTP_ENCRYPTION_KEY: z.string().optional()
}); });
const raw = envSchema.parse(Bun.env); const raw = envSchema.parse(normalizedEnv);
if ((raw.APP_ADMIN_EMAIL && !raw.APP_ADMIN_PASSWORD) || (!raw.APP_ADMIN_EMAIL && raw.APP_ADMIN_PASSWORD)) { if ((raw.APP_ADMIN_EMAIL && !raw.APP_ADMIN_PASSWORD) || (!raw.APP_ADMIN_EMAIL && raw.APP_ADMIN_PASSWORD)) {
throw new Error("APP_ADMIN_EMAIL and APP_ADMIN_PASSWORD must be provided together."); throw new Error("APP_ADMIN_EMAIL and APP_ADMIN_PASSWORD must be provided together.");
@@ -69,4 +73,3 @@ export const config = {
adminPassword: raw.APP_ADMIN_PASSWORD ?? null, adminPassword: raw.APP_ADMIN_PASSWORD ?? null,
totpEncryptionKey: raw.TOTP_ENCRYPTION_KEY ?? null totpEncryptionKey: raw.TOTP_ENCRYPTION_KEY ?? null
} as const; } as const;