|
1 | 1 | /** |
2 | 2 | * Centralized env variable reader. |
3 | 3 | * |
4 | | - * All keys are prefixed with NEXT_PUBLIC_ so they are available |
5 | | - * on both the server (RSC, metadata, API routes) and the client |
6 | | - * (Client Components, hooks). |
| 4 | + * IMPORTANT: Next.js statically replaces process.env.NEXT_PUBLIC_* at bundle |
| 5 | + * time using literal key strings. Using process.env[variable] with a dynamic |
| 6 | + * key does NOT work in client bundles — the bundler cannot resolve it and |
| 7 | + * returns undefined, causing all client-side reads to fall back to defaults. |
7 | 8 | * |
8 | | - * Every value has a typed string fallback so the build never |
9 | | - * crashes when a variable is missing (e.g. in CI or local dev |
10 | | - * without a .env.local file). |
11 | | - * |
12 | | - * To override any value: add it to .env.local (never commit that |
13 | | - * file). See .env.example for the full key list. |
| 9 | + * Every key must be accessed as a literal: process.env.NEXT_PUBLIC_SITE_NAME |
| 10 | + * Never use bracket notation with a variable for NEXT_PUBLIC_ keys. |
14 | 11 | */ |
15 | 12 |
|
16 | | -function read(key: string, fallback: string): string { |
17 | | - // process.env is statically replaced by the Next.js bundler for |
18 | | - // NEXT_PUBLIC_ prefixed keys — do NOT destructure or alias the |
19 | | - // object; always use the full literal key string. |
20 | | - const value = process.env[key]; |
21 | | - return value !== undefined && value.trim() !== "" ? value.trim() : fallback; |
| 13 | +function fallback(value: string | undefined, def: string): string { |
| 14 | + return value !== undefined && value.trim() !== "" ? value.trim() : def; |
22 | 15 | } |
23 | 16 |
|
24 | 17 | export const env = { |
25 | 18 | // ── Brand identity ──────────────────────────────────────────────────────── |
26 | | - SITE_NAME: read( |
27 | | - "NEXT_PUBLIC_SITE_NAME", |
28 | | - "CloudForgeOps", |
29 | | - ), |
30 | | - SITE_TAGLINE: read( |
31 | | - "NEXT_PUBLIC_SITE_TAGLINE", |
32 | | - "Freelance DevOps & Cloud Engineering", |
33 | | - ), |
34 | | - SITE_DESCRIPTION: read( |
35 | | - "NEXT_PUBLIC_SITE_DESCRIPTION", |
36 | | - "I design, build, and operate cloud-native infrastructure. From Kubernetes to CI/CD pipelines, I help startups ship faster and stay reliable.", |
37 | | - ), |
38 | | - SITE_URL: read( |
39 | | - "NEXT_PUBLIC_SITE_URL", |
40 | | - "https://cloudforgeops.com", |
41 | | - ), |
42 | | - SITE_OG_IMAGE: read( |
43 | | - "NEXT_PUBLIC_SITE_OG_IMAGE", |
44 | | - "/images/og-default.png", |
45 | | - ), |
| 19 | + SITE_NAME: fallback(process.env.NEXT_PUBLIC_SITE_NAME, "CloudForgeOps"), |
| 20 | + SITE_TAGLINE: fallback(process.env.NEXT_PUBLIC_SITE_TAGLINE, "Freelance DevOps & Cloud Engineering"), |
| 21 | + SITE_DESCRIPTION: fallback(process.env.NEXT_PUBLIC_SITE_DESCRIPTION, "I design, build, and operate cloud-native infrastructure. From Kubernetes to CI/CD pipelines, I help startups ship faster and stay reliable."), |
| 22 | + SITE_URL: fallback(process.env.NEXT_PUBLIC_SITE_URL, "https://cloudforgeops.com"), |
| 23 | + SITE_OG_IMAGE: fallback(process.env.NEXT_PUBLIC_SITE_OG_IMAGE, "/images/og-default.png"), |
46 | 24 |
|
47 | 25 | // ── Contact ─────────────────────────────────────────────────────────────── |
48 | | - CONTACT_EMAIL: read( |
49 | | - "NEXT_PUBLIC_CONTACT_EMAIL", |
50 | | - "sagardeepak2002@gmail.com", |
51 | | - ), |
| 26 | + CONTACT_EMAIL: fallback(process.env.NEXT_PUBLIC_CONTACT_EMAIL, "sagardeepak2002@gmail.com"), |
52 | 27 |
|
53 | 28 | // ── Social links ───────────────────────────────────────────────────────── |
54 | | - SOCIAL_GITHUB: read( |
55 | | - "NEXT_PUBLIC_SOCIAL_GITHUB", |
56 | | - "https://github.com/cloudforgeops", |
57 | | - ), |
58 | | - SOCIAL_LINKEDIN: read( |
59 | | - "NEXT_PUBLIC_SOCIAL_LINKEDIN", |
60 | | - "https://linkedin.com/company/cloudforgeops", |
61 | | - ), |
| 29 | + SOCIAL_GITHUB: fallback(process.env.NEXT_PUBLIC_SOCIAL_GITHUB, "https://github.com/sagarDeepakDevOps"), |
| 30 | + SOCIAL_LINKEDIN: fallback(process.env.NEXT_PUBLIC_SOCIAL_LINKEDIN, "https://linkedin.com/in/sagardeepak2002"), |
62 | 31 |
|
63 | 32 | // ── Owner / personal identity ───────────────────────────────────────────── |
64 | | - OWNER_NAME: read( |
65 | | - "NEXT_PUBLIC_OWNER_NAME", |
66 | | - "Deepak Sagar", |
67 | | - ), |
68 | | - OWNER_TITLE: read( |
69 | | - "NEXT_PUBLIC_OWNER_TITLE", |
70 | | - "Freelance DevOps & Cloud Engineer", |
71 | | - ), |
72 | | - OWNER_EXPERIENCE: read( |
73 | | - "NEXT_PUBLIC_OWNER_EXPERIENCE", |
74 | | - "3+", |
75 | | - ), |
76 | | - OWNER_EXPERIENCE_LABEL: read( |
77 | | - "NEXT_PUBLIC_OWNER_EXPERIENCE_LABEL", |
78 | | - "Hands-On Cloud & Automation Experience", |
79 | | - ), |
80 | | - OWNER_BIO: read( |
81 | | - "NEXT_PUBLIC_OWNER_BIO", |
82 | | - "I work directly with founders and engineering teams to design, automate, and stabilize cloud environments across AWS and Azure.", |
83 | | - ), |
84 | | - OWNER_LINKEDIN: read( |
85 | | - "NEXT_PUBLIC_OWNER_LINKEDIN", |
86 | | - "https://linkedin.com/in/sagardeepak2002", |
87 | | - ), |
| 33 | + OWNER_NAME: fallback(process.env.NEXT_PUBLIC_OWNER_NAME, "Deepak Sagar"), |
| 34 | + OWNER_TITLE: fallback(process.env.NEXT_PUBLIC_OWNER_TITLE, "Freelance DevOps & Cloud Engineer"), |
| 35 | + OWNER_EXPERIENCE: fallback(process.env.NEXT_PUBLIC_OWNER_EXPERIENCE, "3+"), |
| 36 | + OWNER_EXPERIENCE_LABEL: fallback(process.env.NEXT_PUBLIC_OWNER_EXPERIENCE_LABEL, "Hands-On Cloud & Automation Experience"), |
| 37 | + OWNER_BIO: fallback(process.env.NEXT_PUBLIC_OWNER_BIO, "I work directly with founders and engineering teams to design, automate, and stabilize cloud environments across AWS and Azure."), |
| 38 | + OWNER_LINKEDIN: fallback(process.env.NEXT_PUBLIC_OWNER_LINKEDIN, "https://linkedin.com/in/sagardeepak2002"), |
88 | 39 |
|
89 | 40 | // ── Free Review / Calendly ──────────────────────────────────────────────── |
90 | | - FREE_REVIEW_HREF: read( |
91 | | - "NEXT_PUBLIC_FREE_REVIEW_HREF", |
92 | | - "/free-review", |
93 | | - ), |
94 | | - FREE_REVIEW_DURATION: read( |
95 | | - "NEXT_PUBLIC_FREE_REVIEW_DURATION", |
96 | | - "30 min", |
97 | | - ), |
98 | | - CALENDLY_URL: read( |
99 | | - "NEXT_PUBLIC_CALENDLY_URL", |
100 | | - "https://calendly.com/sagardeepak2002/30min", |
101 | | - ), |
| 41 | + FREE_REVIEW_HREF: fallback(process.env.NEXT_PUBLIC_FREE_REVIEW_HREF, "/free-review"), |
| 42 | + FREE_REVIEW_DURATION: fallback(process.env.NEXT_PUBLIC_FREE_REVIEW_DURATION, "30 min"), |
| 43 | + CALENDLY_URL: fallback(process.env.NEXT_PUBLIC_CALENDLY_URL, "https://calendly.com/sagardeepak2002/30min"), |
102 | 44 | } as const; |
103 | 45 |
|
104 | 46 | export type Env = typeof env; |
0 commit comments