mirror of
https://github.com/Patiun/com.gregkilmer.www.git
synced 2026-11-07 19:58:24 +00:00
Reply time setting, and server settings from process.env only
Inquiries and the sent page promise a reply within the site setting replyTime (2 days, Greg 2026-10-11). The site reads its settings through one helper that uses process.env only, so a .env file can never be copied into the built bundle through import.meta.env. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012aSbo2D1qP8cMFQCykf7kx
This commit is contained in:
@@ -475,6 +475,7 @@ export const siteSettings = def({
|
|||||||
lockup: line({ title: "Lockup line" }),
|
lockup: line({ title: "Lockup line" }),
|
||||||
location: line({ title: "Location" }).optional(),
|
location: line({ title: "Location" }).optional(),
|
||||||
email: line({ title: "Email", description: "Printed on the resume PDF." }).optional(),
|
email: line({ title: "Email", description: "Printed on the resume PDF." }).optional(),
|
||||||
|
replyTime: line({ title: "Reply time", description: "How soon you answer inquiries, like 2 days." }).optional(),
|
||||||
availability: line({ title: "Availability", description: "Shown on the resume and Inquiries, like Open to freelance web work." }).optional(),
|
availability: line({ title: "Availability", description: "Shown on the resume and Inquiries, like Open to freelance web work." }).optional(),
|
||||||
socials: z
|
socials: z
|
||||||
.array(z.object({ label: line({ title: "Label" }), handle: line({ title: "Handle" }), url: z.url().meta({ title: "Link" }) }))
|
.array(z.object({ label: line({ title: "Label" }), handle: line({ title: "Handle" }), url: z.url().meta({ title: "Link" }) }))
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
* background, so a saved note shows within a minute and a slow or restarting
|
* background, so a saved note shows within a minute and a slow or restarting
|
||||||
* content service never takes a page down: the last good copy keeps serving.
|
* content service never takes a page down: the last good copy keeps serving.
|
||||||
*/
|
*/
|
||||||
|
import { env } from "./env.ts";
|
||||||
import type { NoteData, NoteType } from "@gk/schema";
|
import type { NoteData, NoteType } from "@gk/schema";
|
||||||
import { SiteIndex } from "./site.ts";
|
import { SiteIndex } from "./site.ts";
|
||||||
|
|
||||||
@@ -27,7 +28,6 @@ export interface Redirect {
|
|||||||
toPath: string;
|
toPath: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const env = (k: string, fallback?: string) => process.env[k] ?? import.meta.env[k] ?? fallback;
|
|
||||||
const baseUrl = () => (env("CONTENT_URL", "http://localhost:4310") as string).replace(/\/$/, "");
|
const baseUrl = () => (env("CONTENT_URL", "http://localhost:4310") as string).replace(/\/$/, "");
|
||||||
const token = () => env("CONTENT_TOKEN", "") as string;
|
const token = () => env("CONTENT_TOKEN", "") as string;
|
||||||
/** Preview only: include unpublished notes. Needs an editor or admin token; never set in production. */
|
/** Preview only: include unpublished notes. Needs an editor or admin token; never set in production. */
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* Server settings, read from process.env at run time only. Never read secrets
|
||||||
|
* through import.meta.env: Vite replaces it at build time and can copy every
|
||||||
|
* value, secrets included, into the built bundle. Set variables in the
|
||||||
|
* environment (Docker env_file, or the shell when running locally).
|
||||||
|
*/
|
||||||
|
export function env(key: string): string | undefined;
|
||||||
|
export function env(key: string, fallback: string): string;
|
||||||
|
export function env(key: string, fallback?: string): string | undefined {
|
||||||
|
const v = process.env[key];
|
||||||
|
return v === undefined || v === "" ? fallback : v;
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import Seal from "@gk/ui/components/Seal.astro";
|
|||||||
import Star from "@gk/ui/components/Star.astro";
|
import Star from "@gk/ui/components/Star.astro";
|
||||||
import StatusPill from "@gk/ui/components/StatusPill.astro";
|
import StatusPill from "@gk/ui/components/StatusPill.astro";
|
||||||
import ChoicePills from "../../components/work/ChoicePills.astro";
|
import ChoicePills from "../../components/work/ChoicePills.astro";
|
||||||
|
import { env } from "../../lib/env.ts";
|
||||||
import { site } from "../../lib/page.ts";
|
import { site } from "../../lib/page.ts";
|
||||||
import { inquiryTypes, parseInquiryType, type InquiryType } from "../../lib/work.ts";
|
import { inquiryTypes, parseInquiryType, type InquiryType } from "../../lib/work.ts";
|
||||||
|
|
||||||
@@ -15,7 +16,7 @@ const settings = s.settings();
|
|||||||
const text = s.pageText("/inquiries");
|
const text = s.pageText("/inquiries");
|
||||||
const selected = parseInquiryType(Astro.url.searchParams.get("type"));
|
const selected = parseInquiryType(Astro.url.searchParams.get("type"));
|
||||||
const lede = text?.data.lede ?? "Roles, freelance web work or a custom Patiun order. Pick one and the form asks the right questions.";
|
const lede = text?.data.lede ?? "Roles, freelance web work or a custom Patiun order. Pick one and the form asks the right questions.";
|
||||||
const quickFacts = text?.data.quickFacts?.map((f) => f.text) ?? ["Replies within [PLACEHOLDER] days", "Read by me, not a bot"];
|
const quickFacts = text?.data.quickFacts?.map((f) => f.text) ?? [`Replies within ${s.settings()?.replyTime ?? "a few days"}`, "Read by me, not a bot"];
|
||||||
const socials = settings?.socials ?? [];
|
const socials = settings?.socials ?? [];
|
||||||
|
|
||||||
const placeholders: Record<InquiryType | "none", string> = {
|
const placeholders: Record<InquiryType | "none", string> = {
|
||||||
@@ -29,7 +30,6 @@ const timing = [
|
|||||||
{ value: "within-a-month", label: "Within a month" },
|
{ value: "within-a-month", label: "Within a month" },
|
||||||
{ value: "date", label: "A specific date" },
|
{ value: "date", label: "A specific date" },
|
||||||
];
|
];
|
||||||
const env = (k: string) => (process.env[k] ?? import.meta.env[k]) as string | undefined;
|
|
||||||
const turnstileKey = env("PUBLIC_TURNSTILE_SITE_KEY") ?? env("TURNSTILE_SITE_KEY");
|
const turnstileKey = env("PUBLIC_TURNSTILE_SITE_KEY") ?? env("TURNSTILE_SITE_KEY");
|
||||||
---
|
---
|
||||||
<PageShell title="Inquiries" family="inquiries" current="inquiries" description="Ask about a role, freelance web work or a custom Patiun order.">
|
<PageShell title="Inquiries" family="inquiries" current="inquiries" description="Ask about a role, freelance web work or a custom Patiun order.">
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ import Star from "@gk/ui/components/Star.astro";
|
|||||||
import TickRing from "@gk/ui/components/TickRing.astro";
|
import TickRing from "@gk/ui/components/TickRing.astro";
|
||||||
import TickScale from "@gk/ui/components/TickScale.astro";
|
import TickScale from "@gk/ui/components/TickScale.astro";
|
||||||
import { inquiryTypes, parseInquiryType, parseSentSummary } from "../../lib/work.ts";
|
import { inquiryTypes, parseInquiryType, parseSentSummary } from "../../lib/work.ts";
|
||||||
|
import { site } from "../../lib/page.ts";
|
||||||
|
|
||||||
|
const replyTime = (await site().catch(() => null))?.settings()?.replyTime ?? "a few days";
|
||||||
const sent = parseSentSummary(Astro.cookies.get("gk-inquiry")?.value);
|
const sent = parseSentSummary(Astro.cookies.get("gk-inquiry")?.value);
|
||||||
if (sent) Astro.cookies.delete("gk-inquiry", { path: "/" });
|
if (sent) Astro.cookies.delete("gk-inquiry", { path: "/" });
|
||||||
Astro.response.headers.set("Cache-Control", "private, no-store");
|
Astro.response.headers.set("Cache-Control", "private, no-store");
|
||||||
@@ -38,7 +40,7 @@ const steps = [
|
|||||||
<Seal name="transmutation" size={132} class="seal" />
|
<Seal name="transmutation" size={132} class="seal" />
|
||||||
<span class="eyebrow">Inquiry sent</span>
|
<span class="eyebrow">Inquiry sent</span>
|
||||||
<h1>Your note is on its way</h1>
|
<h1>Your note is on its way</h1>
|
||||||
<p class="lede">Thanks{firstName ? `, ${firstName}` : ""}. I read every inquiry myself and reply within [PLACEHOLDER] days.</p>
|
<p class="lede">Thanks{firstName ? `, ${firstName}` : ""}. I read every inquiry myself and reply within {replyTime}.</p>
|
||||||
<ol class="steps" aria-label="What happens next">
|
<ol class="steps" aria-label="What happens next">
|
||||||
{
|
{
|
||||||
steps.map((st, i) => (
|
steps.map((st, i) => (
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ export const site = {
|
|||||||
email: "greg@gregkilmer.com",
|
email: "greg@gregkilmer.com",
|
||||||
/** Resume Interview: open to offers but not looking, so the page says this. */
|
/** Resume Interview: open to offers but not looking, so the page says this. */
|
||||||
availability: "Open to freelance web work",
|
availability: "Open to freelance web work",
|
||||||
|
/** Greg, 2026-10-11. */
|
||||||
|
replyTime: "2 days",
|
||||||
socials: [
|
socials: [
|
||||||
{ label: "GitHub", handle: "Patiun", url: "https://github.com/patiun" },
|
{ label: "GitHub", handle: "Patiun", url: "https://github.com/patiun" },
|
||||||
{ label: "LinkedIn", handle: "Gregory Kilmer", url: "https://www.linkedin.com/in/gregory-kilmer-36646bb5/" },
|
{ label: "LinkedIn", handle: "Gregory Kilmer", url: "https://www.linkedin.com/in/gregory-kilmer-36646bb5/" },
|
||||||
|
|||||||
Reference in New Issue
Block a user