diff --git a/contact-api/.env b/contact-api/.env index 1bb8750..d7ed8f1 100644 --- a/contact-api/.env +++ b/contact-api/.env @@ -1,8 +1,8 @@ NODE_ENV=production PORT=8787 -CONTACT_ALLOWED_ORIGIN=http://localhost:5173,https://jodyholt.com,https://www.jodyholt.com +CONTACT_ALLOWED_ORIGIN=https://jodyholt.com,https://www.jodyholt.com +TURNSTILE_EXPECTED_HOSTNAME=jodyholt.com,www.jodyholt.com TURNSTILE_SECRET_KEY=0x4AAAAAACfQyRwRzwsEMIfVtCSkjz7__Yc -TURNSTILE_EXPECTED_HOSTNAME=jodyholt.com TURNSTILE_EXPECTED_ACTION=contact_form SMTP_HOST=mail.jodyholt.com SMTP_PORT=587 diff --git a/contact-api/src/config.ts b/contact-api/src/config.ts index 5470243..88a37b1 100644 --- a/contact-api/src/config.ts +++ b/contact-api/src/config.ts @@ -10,7 +10,9 @@ const boolFromEnv = z const envSchema = z.object({ NODE_ENV: z.enum(["development", "test", "production"]).default("development"), PORT: z.coerce.number().int().positive().default(8787), - CONTACT_ALLOWED_ORIGIN: z.string().url(), + // Comma-separated list of allowed browser origins, e.g. + // https://jodyholt.com,https://www.jodyholt.com + CONTACT_ALLOWED_ORIGIN: z.string().min(1), TURNSTILE_SECRET_KEY: z.string().min(1), TURNSTILE_EXPECTED_HOSTNAME: z.string().min(1), TURNSTILE_EXPECTED_ACTION: z.string().min(1).default("contact_form"), diff --git a/contact-api/src/index.ts b/contact-api/src/index.ts index 907f287..0243e37 100644 --- a/contact-api/src/index.ts +++ b/contact-api/src/index.ts @@ -16,12 +16,19 @@ type ApiErrorResponse = { const app = express(); app.set("trust proxy", 1); -const normalizeOrigin = (value: string): string => - value +const normalizeOrigin = (value: string): string => { + const cleaned = value .trim() .replace(/^['"]|['"]$/g, "") - .replace(/\/+$/g, "") - .toLowerCase(); + .replace(/\/+$/g, ""); + + // Some clients can emit explicit default ports. URL.origin normalizes them. + try { + return new URL(cleaned).origin.toLowerCase(); + } catch { + return cleaned.toLowerCase(); + } +}; const allowedOrigins = config.CONTACT_ALLOWED_ORIGIN .split(",")