optimize the way we consume env variables
This commit is contained in:
parent
f1428397f2
commit
2ee4f3197d
|
|
@ -9,9 +9,6 @@ GOOGLE_CLIENT_SECRET=
|
|||
|
||||
AUTH_COOKIE_NAME=app.session-token
|
||||
|
||||
# openssl rand -base64 32
|
||||
AUTH_SECRET=xxx
|
||||
|
||||
OUR_DOMAIN=localhost:3000
|
||||
|
||||
S3_BUCKET_NAME=xxx
|
||||
|
|
@ -19,6 +16,7 @@ S3_REGION=xxx
|
|||
S3_ACCESS_KEY_ID=xxx
|
||||
S3_SECRET_ACCESS_KEY=xxx
|
||||
S3_ENDPOINT=s3.us-west-004.backblazeb2.com
|
||||
S3_CDN_PREFIX=xxx
|
||||
|
||||
MAILGUN_APIKEY=xxx
|
||||
MAILGUN_DOMAIN=xxx
|
||||
|
|
|
|||
|
|
@ -1,14 +1,13 @@
|
|||
import { Link } from "@remix-run/react"
|
||||
import { logout } from "~/lib/auth.client"
|
||||
import { APP_NAME } from "~/lib/env"
|
||||
import { useStore } from "~/lib/store"
|
||||
|
||||
export function MainLayout({
|
||||
isLoggedIn,
|
||||
appName,
|
||||
children,
|
||||
}: {
|
||||
isLoggedIn: boolean
|
||||
appName: string
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
const setLoginModalOpened = useStore((store) => store.setLoginModalOpened)
|
||||
|
|
@ -42,7 +41,7 @@ export function MainLayout({
|
|||
<div className="max-w-screen-md mx-auto px-5">
|
||||
<div>
|
||||
<span className="inline-block text-3xl lg:text-7xl px-3 rounded-lg py-2 font-bold bg-indigo-600 text-white">
|
||||
{appName}
|
||||
{APP_NAME}
|
||||
</span>
|
||||
</div>
|
||||
<div className="italic text-zinc-500 text-sm mt-1">
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ import { SubscribeModal } from "../common/SubscribeModal"
|
|||
import { Avatar } from "../ui/Avatar"
|
||||
import { Button } from "../ui/Button"
|
||||
import { UniLink } from "../ui/UniLink"
|
||||
import { IS_PROD, OUR_DOMAIN } from "~/lib/config.shared"
|
||||
import { IS_PROD } from "~/lib/constants"
|
||||
import { OUR_DOMAIN } from "~/lib/env"
|
||||
|
||||
type MenuLink = {
|
||||
text: string
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import type { User, Membership } from "@prisma/client"
|
||||
import { AUTH_COOKIE_NAME } from "./config.server"
|
||||
import { prismaRead } from "./db.server"
|
||||
import dayjs from "dayjs"
|
||||
import { type CookieSerializeOptions, createCookie } from "@remix-run/node"
|
||||
import { IS_PROD, OUR_DOMAIN } from "./config.shared"
|
||||
import { AUTH_COOKIE_NAME, OUR_DOMAIN } from "./env"
|
||||
import { IS_PROD } from "./constants"
|
||||
|
||||
export type AuthUser = User & {
|
||||
memberships: Membership[]
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
export const AUTH_COOKIE_NAME = process.env.AUTH_COOKIE_NAME
|
||||
export const S3_REGION = process.env.S3_REGION
|
||||
export const S3_ACCESS_KEY_ID = process.env.S3_ACCESS_KEY_ID
|
||||
export const S3_SECRET_ACCESS_KEY = process.env.S3_SECRET_ACCESS_KEY
|
||||
|
||||
export const S3_BUCKET_NAME = process.env.S3_BUCKET_NAME
|
||||
export const S3_ENDPOINT = process.env.S3_ENDPOINT
|
||||
export const MAILGUN_APIKEY = process.env.MAILGUN_APIKEY
|
||||
export const MAILGUN_DOMAIN = process.env.MAILGUN_DOMAIN
|
||||
export const MAILGUN_EU = process.env.MAILGUN_EU
|
||||
// Primary Fly region
|
||||
export const PRIMARY_REGION = process.env.PRIMARY_REGION
|
||||
// Curreny Fly region
|
||||
export const FLY_REGION = process.env.FLY_REGION
|
||||
export const IS_PRIMARY_REGION = Boolean(
|
||||
FLY_REGION && PRIMARY_REGION === FLY_REGION
|
||||
)
|
||||
|
|
@ -1,12 +1,9 @@
|
|||
import { description } from "../../package.json"
|
||||
|
||||
export const IS_BROWSER = typeof window !== "undefined"
|
||||
export const IS_BROWSER = typeof document !== "undefined"
|
||||
|
||||
export const IS_PROD = IS_BROWSER
|
||||
? !location.hostname.endsWith("localhost")
|
||||
: process.env.NODE_ENV === "production"
|
||||
|
||||
export const APP_NAME = IS_BROWSER ? ENV.APP_NAME : process.env.APP_NAME
|
||||
export const OUR_DOMAIN = IS_BROWSER ? ENV.OUR_DOMAIN : process.env.OUR_DOMAIN
|
||||
|
||||
export const APP_DESCRIPTION = description
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { PrismaClient } from "@prisma/client"
|
||||
import { IS_PRIMARY_REGION } from "./config.server"
|
||||
import { IS_PROD } from "./config.shared"
|
||||
import { IS_PROD } from "./constants"
|
||||
import { isPrimaryRegion } from "./env"
|
||||
import { singleton } from "./singleton.server"
|
||||
|
||||
export const prismaWrite = /* @__PURE__ */ singleton(
|
||||
|
|
@ -12,7 +12,7 @@ export const prismaWrite = /* @__PURE__ */ singleton(
|
|||
export const prismaRead = /* @__PURE__ */ singleton("prisma-read", () => {
|
||||
// 5433 is the read-replica port
|
||||
let url = process.env.DATABASE_URL
|
||||
if (!IS_PRIMARY_REGION && IS_PROD) {
|
||||
if (!isPrimaryRegion() && IS_PROD) {
|
||||
url = url.replace(":5432", ":5433")
|
||||
}
|
||||
console.log("read replica url", url)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
import { IS_BROWSER } from "./constants"
|
||||
|
||||
/**
|
||||
* Can be called in browser and server
|
||||
* only exposing env variables that're available in browser
|
||||
*/
|
||||
export const getCommonEnv = <T extends keyof BrowserEnv>(
|
||||
key: T
|
||||
): BrowserEnv[T] => {
|
||||
return IS_BROWSER ? ENV[key] : process.env[key]
|
||||
}
|
||||
|
||||
export const getServerEnv = <T extends keyof ServerEnv>(
|
||||
key: T
|
||||
): ServerEnv[T] => {
|
||||
if (IS_BROWSER) throw new Error(`getServerEnv() called in browser`)
|
||||
return process.env[key]
|
||||
}
|
||||
|
||||
export const isPrimaryRegion = () => {
|
||||
const FLY_REGION = getServerEnv("FLY_REGION")
|
||||
const PRIMARY_REGION = getServerEnv("PRIMARY_REGION")
|
||||
return Boolean(FLY_REGION && PRIMARY_REGION === FLY_REGION)
|
||||
}
|
||||
|
||||
// Use /* @__PURE__ */ annotation to make tree-shaking work
|
||||
export const AUTH_COOKIE_NAME = /* @__PURE__ */ getServerEnv("AUTH_COOKIE_NAME")
|
||||
export const APP_NAME = /* @__PURE__ */ getCommonEnv("APP_NAME")
|
||||
export const OUR_DOMAIN = /* @__PURE__ */ getCommonEnv("OUR_DOMAIN")
|
||||
export const S3_CDN_PREFIX = /* @__PURE__ */ getCommonEnv("S3_CDN_PREFIX")
|
||||
export const S3_REGION = /* @__PURE__ */ getServerEnv("S3_REGION")
|
||||
export const S3_ACCESS_KEY_ID = /* @__PURE__ */ getServerEnv("S3_ACCESS_KEY_ID")
|
||||
export const S3_SECRET_ACCESS_KEY = /* @__PURE__ */ getServerEnv(
|
||||
"S3_SECRET_ACCESS_KEY"
|
||||
)
|
||||
export const S3_BUCKET_NAME = /* @__PURE__ */ getServerEnv("S3_BUCKET_NAME")
|
||||
export const S3_ENDPOINT = /* @__PURE__ */ getServerEnv("S3_ENDPOINT")
|
||||
export const MAILGUN_APIKEY = /* @__PURE__ */ getServerEnv("MAILGUN_APIKEY")
|
||||
export const MAILGUN_DOMAIN = /* @__PURE__ */ getServerEnv("MAILGUN_DOMAIN")
|
||||
export const MAILGUN_EU = /* @__PURE__ */ getServerEnv("MAILGUN_EU")
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import { IS_PROD, OUR_DOMAIN } from "./config.shared"
|
||||
import { IS_PROD } from "./constants"
|
||||
import { OUR_DOMAIN } from "./env"
|
||||
|
||||
export const getSiteLink = ({ subdomain }: { subdomain: string }) => {
|
||||
return `${IS_PROD ? "https" : "http"}://${subdomain}.${OUR_DOMAIN}`
|
||||
|
|
|
|||
|
|
@ -1,9 +1,15 @@
|
|||
import Mailgun from "mailgun.js"
|
||||
import FormData from "form-data"
|
||||
import { singleton } from "./singleton.server"
|
||||
import { MAILGUN_APIKEY, MAILGUN_DOMAIN, MAILGUN_EU } from "~/lib/config.server"
|
||||
import {
|
||||
APP_NAME,
|
||||
MAILGUN_APIKEY,
|
||||
MAILGUN_DOMAIN,
|
||||
MAILGUN_EU,
|
||||
OUR_DOMAIN,
|
||||
} from "~/lib/env"
|
||||
import type { MailgunMessageData } from "mailgun.js/interfaces/Messages"
|
||||
import { APP_NAME, IS_PROD, OUR_DOMAIN } from "./config.shared"
|
||||
import { IS_PROD } from "./constants"
|
||||
|
||||
const getClient = () =>
|
||||
singleton("mailgun", () => {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import {
|
|||
S3_SECRET_ACCESS_KEY,
|
||||
S3_BUCKET_NAME,
|
||||
S3_ENDPOINT,
|
||||
} from "~/lib/config.server"
|
||||
} from "~/lib/env"
|
||||
import {
|
||||
S3Client,
|
||||
PutObjectCommand,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { OUR_DOMAIN } from "./config.shared"
|
||||
import { OUR_DOMAIN } from "./env"
|
||||
|
||||
export const getTenant = (request: Request) => {
|
||||
const host = request.headers.get("host")
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { IS_PROD, OUR_DOMAIN } from "./config.shared"
|
||||
import { IS_PROD } from "./constants"
|
||||
import { S3_CDN_PREFIX } from "./env"
|
||||
|
||||
export function getUserContentsUrl(filename: string): string
|
||||
export function getUserContentsUrl(filename: undefined | null): undefined
|
||||
|
|
@ -8,7 +9,7 @@ export function getUserContentsUrl<T extends string | undefined | null>(
|
|||
export function getUserContentsUrl(filename: string | undefined | null) {
|
||||
if (!filename) return undefined
|
||||
if (IS_PROD) {
|
||||
return `https://usercontents.${OUR_DOMAIN}/${filename}`
|
||||
return `${S3_CDN_PREFIX}/${filename}`
|
||||
}
|
||||
return `/dev-s3-proxy?${new URLSearchParams({
|
||||
filename: filename,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import {
|
|||
} from "@remix-run/node"
|
||||
import {
|
||||
Links,
|
||||
LiveReload,
|
||||
Meta,
|
||||
Outlet,
|
||||
Scripts,
|
||||
|
|
@ -17,7 +16,7 @@ import LoginModal from "~/components/common/LoginModal"
|
|||
import { createStore, StoreProvider } from "./lib/store"
|
||||
import { Toaster } from "react-hot-toast"
|
||||
import css from "./generated.css"
|
||||
import { APP_NAME } from "./lib/config.shared"
|
||||
import { APP_NAME } from "./lib/env"
|
||||
|
||||
export const meta: MetaFunction = () => {
|
||||
return {
|
||||
|
|
@ -31,13 +30,14 @@ export const links: LinksFunction = () => {
|
|||
return [{ href: css, rel: "stylesheet", type: "text/css" }]
|
||||
}
|
||||
|
||||
type LoaderData = { ENV: Record<string, string> }
|
||||
type LoaderData = { ENV: BrowserEnv }
|
||||
|
||||
export const loader: LoaderFunction = async () => {
|
||||
return json<LoaderData>({
|
||||
ENV: {
|
||||
APP_NAME: process.env.APP_NAME,
|
||||
OUR_DOMAIN: process.env.OUR_DOMAIN,
|
||||
S3_CDN_PREFIX: process.env.S3_CDN_PREFIX,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import { type LoaderFunction, redirect } from "@remix-run/node"
|
||||
import { z } from "zod"
|
||||
import { generateCookie } from "~/lib/auth.server"
|
||||
import { IS_PROD, OUR_DOMAIN } from "~/lib/config.shared"
|
||||
import { IS_PROD } from "~/lib/constants"
|
||||
import { prismaRead, prismaWrite } from "~/lib/db.server"
|
||||
import { OUR_DOMAIN } from "~/lib/env"
|
||||
|
||||
export const loader: LoaderFunction = async ({ request }) => {
|
||||
const url = new URL(request.url)
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@ import { prismaRead, prismaWrite } from "~/lib/db.server"
|
|||
import { nanoid } from "nanoid"
|
||||
import UAParser from "ua-parser-js"
|
||||
import dayjs from "dayjs"
|
||||
import { IS_PROD, OUR_DOMAIN } from "~/lib/config.shared"
|
||||
import { generateCookie } from "~/lib/auth.server"
|
||||
import { OUR_DOMAIN } from "~/lib/env"
|
||||
import { IS_PROD } from "~/lib/constants"
|
||||
|
||||
export const loader: LoaderFunction = async ({ request }) => {
|
||||
const url = new URL(request.url)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import { ActionFunction, redirect } from "@remix-run/node"
|
||||
import { ActionFunction } from "@remix-run/node"
|
||||
import dayjs from "dayjs"
|
||||
import { z } from "zod"
|
||||
import { IS_PROD, OUR_DOMAIN } from "~/lib/config.shared"
|
||||
import { IS_PROD } from "~/lib/constants"
|
||||
import { prismaWrite } from "~/lib/db.server"
|
||||
import { OUR_DOMAIN } from "~/lib/env"
|
||||
import { sendLoginEmail } from "~/lib/mailgun.server"
|
||||
|
||||
export const action: ActionFunction = async ({ request }) => {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { Button } from "~/components/ui/Button"
|
|||
import { Input } from "~/components/ui/Input"
|
||||
import { siteController } from "~/controllers/site.controller"
|
||||
import { getAuthUser } from "~/lib/auth.server"
|
||||
import { OUR_DOMAIN } from "~/lib/config.shared"
|
||||
import { OUR_DOMAIN } from "~/lib/env"
|
||||
|
||||
export const loader: LoaderFunction = async ({ request, params }) => {
|
||||
await getAuthUser(request, true)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { Button } from "~/components/ui/Button"
|
|||
import { Input } from "~/components/ui/Input"
|
||||
import { siteController } from "~/controllers/site.controller"
|
||||
import { getAuthUser } from "~/lib/auth.server"
|
||||
import { OUR_DOMAIN } from "~/lib/config.shared"
|
||||
import { OUR_DOMAIN } from "~/lib/env"
|
||||
|
||||
export const loader: LoaderFunction = async ({ request }) => {
|
||||
const user = await getAuthUser(request, true)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { LoaderFunction } from "@remix-run/node"
|
||||
import { S3_BUCKET_NAME, S3_ENDPOINT } from "~/lib/config.server"
|
||||
import { IS_PROD } from "~/lib/config.shared"
|
||||
import { type LoaderFunction } from "@remix-run/node"
|
||||
import { IS_PROD } from "~/lib/constants"
|
||||
import { S3_BUCKET_NAME, S3_ENDPOINT } from "~/lib/env"
|
||||
|
||||
export const loader: LoaderFunction = ({ request }) => {
|
||||
if (IS_PROD) {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ import { SiteHome } from "~/components/site/SiteHome"
|
|||
import { SiteLayout, type SiteLayoutProps } from "~/components/site/SiteLayout"
|
||||
import { siteController } from "~/controllers/site.controller"
|
||||
import { getAuthUser } from "~/lib/auth.server"
|
||||
import { APP_DESCRIPTION, APP_NAME } from "~/lib/config.shared"
|
||||
import { APP_DESCRIPTION } from "~/lib/constants"
|
||||
import { APP_NAME } from "~/lib/env"
|
||||
import { getTenant } from "~/lib/tenant.server"
|
||||
import { PageVisibilityEnum, type PostOnSiteHome } from "~/lib/types"
|
||||
import { getSubscription } from "~/models/site.model"
|
||||
|
|
@ -13,13 +14,11 @@ import { getSubscription } from "~/models/site.model"
|
|||
type LoaderData =
|
||||
| {
|
||||
type: "main"
|
||||
appName: string
|
||||
isLoggedIn: boolean
|
||||
}
|
||||
| {
|
||||
type: "tenant"
|
||||
isLoggedIn: boolean
|
||||
appName: string
|
||||
tenant?: string
|
||||
posts: PostOnSiteHome[]
|
||||
site: SiteLayoutProps["site"]
|
||||
|
|
@ -62,7 +61,6 @@ export const loader: LoaderFunction = async (ctx) => {
|
|||
return json<LoaderData>({
|
||||
type: "tenant",
|
||||
isLoggedIn,
|
||||
appName: APP_NAME,
|
||||
tenant,
|
||||
posts: pages,
|
||||
site: {
|
||||
|
|
@ -75,7 +73,10 @@ export const loader: LoaderFunction = async (ctx) => {
|
|||
})
|
||||
}
|
||||
|
||||
return json<LoaderData>({ type: "main", isLoggedIn, appName: APP_NAME })
|
||||
return json<LoaderData>({
|
||||
type: "main",
|
||||
isLoggedIn,
|
||||
})
|
||||
}
|
||||
|
||||
export default function Home() {
|
||||
|
|
@ -93,9 +94,5 @@ export default function Home() {
|
|||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<MainLayout isLoggedIn={data.isLoggedIn} appName={data.appName}>
|
||||
{""}
|
||||
</MainLayout>
|
||||
)
|
||||
return <MainLayout isLoggedIn={data.isLoggedIn}>{""}</MainLayout>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
import { type RequestHandler } from "express"
|
||||
import {
|
||||
FLY_REGION,
|
||||
IS_PRIMARY_REGION,
|
||||
PRIMARY_REGION,
|
||||
} from "~/lib/config.server"
|
||||
import { IS_PROD } from "~/lib/config.shared"
|
||||
import { IS_PROD } from "~/lib/constants"
|
||||
import { getServerEnv, isPrimaryRegion } from "~/lib/env"
|
||||
|
||||
const PRIMARY_REGION = getServerEnv("PRIMARY_REGION")
|
||||
const FLY_REGION = getServerEnv("FLY_REGION")
|
||||
|
||||
export const setFlyRegionHeader: RequestHandler = (req, res, next) => {
|
||||
res.setHeader("x-fly-region", FLY_REGION || "unknown")
|
||||
res.setHeader("x-fly-region", PRIMARY_REGION || "unknown")
|
||||
next()
|
||||
}
|
||||
|
||||
|
|
@ -17,7 +16,7 @@ export const getReplayResponse: RequestHandler = (req, res, next) => {
|
|||
if (
|
||||
!IS_PROD ||
|
||||
["GET", "OPTIONS", "HEAD"].includes(method) ||
|
||||
IS_PRIMARY_REGION
|
||||
isPrimaryRegion()
|
||||
) {
|
||||
return next()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,23 +1,30 @@
|
|||
declare namespace NodeJS {
|
||||
interface ProcessEnv {
|
||||
// Additional environment variables
|
||||
APP_NAME: string
|
||||
DATABASE_URL: string
|
||||
AUTH_COOKIE_NAME: string
|
||||
AUTH_SECRET: string
|
||||
OUR_DOMAIN: string
|
||||
S3_REGION: string
|
||||
S3_ACCESS_KEY_ID: string
|
||||
S3_SECRET_ACCESS_KEY: string
|
||||
S3_BUCKET_NAME: string
|
||||
S3_ENDPOINT?: string
|
||||
MAILGUN_APIKEY: string
|
||||
MAILGUN_DOMAIN: string
|
||||
MAILGUN_EU?: string
|
||||
}
|
||||
declare interface ServerEnv {
|
||||
// Additional environment variables
|
||||
APP_NAME: string
|
||||
DATABASE_URL: string
|
||||
AUTH_COOKIE_NAME: string
|
||||
OUR_DOMAIN: string
|
||||
S3_REGION: string
|
||||
S3_ACCESS_KEY_ID: string
|
||||
S3_SECRET_ACCESS_KEY: string
|
||||
S3_BUCKET_NAME: string
|
||||
S3_ENDPOINT?: string
|
||||
MAILGUN_APIKEY: string
|
||||
MAILGUN_DOMAIN: string
|
||||
MAILGUN_EU?: string
|
||||
S3_CDN_PREFIX: string
|
||||
FLY_REGION?: string
|
||||
PRIMARY_REGION?: string
|
||||
}
|
||||
|
||||
declare const ENV: {
|
||||
declare interface BrowserEnv {
|
||||
APP_NAME: string
|
||||
OUR_DOMAIN: string
|
||||
S3_CDN_PREFIX: string
|
||||
}
|
||||
|
||||
declare namespace NodeJS {
|
||||
interface ProcessEnv extends ServerEnv {}
|
||||
}
|
||||
|
||||
declare const ENV: BrowserEnv
|
||||
|
|
|
|||
Loading…
Reference in New Issue