mirror of
https://github.com/Patiun/com.gregkilmer.www.git
synced 2026-11-08 04:08:24 +00:00
Contact form update
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* env.ts
|
||||
* ---------------------------------------------------------------------------
|
||||
* Runtime env-var reader that works in BOTH local dev (`astro dev`, where the
|
||||
* `.env` file is NOT auto-loaded into `process.env`) AND the Docker production
|
||||
* runtime (where docker-compose `env_file` sets real `process.env`).
|
||||
*
|
||||
* Background: Astro/Vite loads `.env` into `import.meta.env`, NOT `process.env`
|
||||
* (https://github.com/withastro/roadmap/issues/853). We deliberately AVOID
|
||||
* `import.meta.env` for these reads because Vite statically replaces it at
|
||||
* build time — and for SECRET values (SMTP password, Turnstile secret key)
|
||||
* that means the secret gets baked into the built JS bundle in plaintext,
|
||||
* which is a security leak and defeats the point of env vars.
|
||||
*
|
||||
* Solution: at module load (once), pull values from the `.env` file via Vite's
|
||||
* `loadEnv` and merge any MISSING keys into `process.env`. Existing real env
|
||||
* vars (e.g. Docker's) always take precedence. All reads then go through
|
||||
* `process.env`, which is runtime-only and never baked into the bundle.
|
||||
*
|
||||
* `loadEnv` is imported from `vite`, which is already a transitive dependency
|
||||
* via Astro — no new dependency required.
|
||||
*/
|
||||
import { loadEnv } from 'vite';
|
||||
|
||||
// One-time: populate process.env from the .env file for keys not already set
|
||||
// by the real environment (Docker env_file / shell exports). This makes local
|
||||
// dev work without any extra setup, and is a no-op in Docker where process.env
|
||||
// is already fully populated.
|
||||
//
|
||||
// NOTE: do NOT reference `import.meta.env` anywhere in this module. Vite
|
||||
// statically replaces any `import.meta.env` usage at build time, and for a
|
||||
// bare reference it inlines the ENTIRE env object (including secrets like
|
||||
// SMTP_PASS / TURNSTILE_SECRET_KEY) as a literal in the bundle. Use
|
||||
// process.env.NODE_ENV to determine mode instead.
|
||||
try {
|
||||
const mode = process.env.NODE_ENV === 'production' ? 'production' : 'development';
|
||||
const fromFile = loadEnv(
|
||||
mode,
|
||||
process.cwd(),
|
||||
// Empty prefix = load ALL vars (not just PUBLIC_*). They land only in the
|
||||
// returned object, not the client bundle, because we copy them into
|
||||
// process.env here rather than referencing import.meta.env elsewhere.
|
||||
'',
|
||||
);
|
||||
for (const [key, value] of Object.entries(fromFile)) {
|
||||
if (process.env[key] === undefined && value !== '') {
|
||||
process.env[key] = value;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// loadEnv throws if it can't find a project root / .env; safe to ignore in
|
||||
// environments where there's no .env (e.g. some serverless runtimes). Real
|
||||
// env vars set via the platform still work via process.env directly.
|
||||
}
|
||||
|
||||
/** Read a single env var from process.env (runtime only; never baked into bundle). */
|
||||
export function env(key: string): string | undefined {
|
||||
return process.env[key] || undefined;
|
||||
}
|
||||
|
||||
/** Public Cloudflare Turnstile site key (safe to ship to the client). */
|
||||
export const turnstileSiteKey = (): string | undefined => env('TURNSTILE_SITE_KEY');
|
||||
|
||||
/** Cloudflare Turnstile secret key (server-only). */
|
||||
export const turnstileSecretKey = (): string | undefined => env('TURNSTILE_SECRET_KEY');
|
||||
|
||||
/** SMTP relay host. When unset, the contact form returns 503. */
|
||||
export const smtpHost = (): string | undefined => env('SMTP_HOST');
|
||||
|
||||
/** SMTP relay port (defaults to 587 at the call site). */
|
||||
export const smtpPort = (): string | undefined => env('SMTP_PORT');
|
||||
|
||||
/** SMTP auth username, if the relay requires auth. */
|
||||
export const smtpUser = (): string | undefined => env('SMTP_USER');
|
||||
|
||||
/** SMTP auth password, if the relay requires auth. */
|
||||
export const smtpPass = (): string | undefined => env('SMTP_PASS');
|
||||
|
||||
/** Inbox that receives contact form submissions. */
|
||||
export const contactTo = (): string | undefined => env('CONTACT_TO');
|
||||
|
||||
/** From address for outgoing mail. */
|
||||
export const smtpFrom = (): string | undefined => env('SMTP_FROM');
|
||||
|
||||
/** Optional webhook URL for delivery fallback (Discord/Slack/n8n). */
|
||||
export const contactWebhookUrl = (): string | undefined => env('CONTACT_WEBHOOK_URL');
|
||||
Reference in New Issue
Block a user