diff --git a/.env.example b/.env.example
index b04656e..1ec8745 100644
--- a/.env.example
+++ b/.env.example
@@ -18,7 +18,11 @@ AI_GATEWAY_API_KEY=""
# ─── Auth.js (required — generate with: npx auth secret) ─────
AUTH_SECRET=""
-AUTH_URL="https://echoocr.yourdomain.com"
+# AUTH_URL must match the canonical origin this deployment serves.
+# Local dev: http://localhost:3000
+# Production: https:// AI-powered response card scanning for churches AI-powered response card scanning for churches
@@ -78,8 +77,13 @@ export async function POST(req: NextRequest) {
},
});
- const resetUrl = `${SITE_URL}/reset-password?token=${token}`;
- await sendEmail(user.email, "Reset your password — Echo", resetEmailHtml(resetUrl));
+ const siteUrl = getSiteUrlFromRequest(req);
+ const resetUrl = `${siteUrl}/reset-password?token=${token}`;
+ await sendEmail(
+ user.email,
+ "Reset your password — Echo",
+ resetEmailHtml(resetUrl, siteUrl)
+ );
}
return NextResponse.json({ ok: true });
diff --git a/src/app/api/dev/email-preview/route.ts b/src/app/api/dev/email-preview/route.ts
index d7bd95d..2607451 100644
--- a/src/app/api/dev/email-preview/route.ts
+++ b/src/app/api/dev/email-preview/route.ts
@@ -1,4 +1,5 @@
import { NextRequest, NextResponse } from "next/server";
+import { getSiteHostname, getSiteUrlFromRequest } from "@/lib/site-url";
export async function GET(req: NextRequest) {
if (process.env.NODE_ENV === "production") {
@@ -10,7 +11,8 @@ export async function GET(req: NextRequest) {
const { searchParams } = req.nextUrl;
const template = searchParams.get("template") || "verification";
- const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL || "https://echoocr.com";
+ const SITE_URL = getSiteUrlFromRequest(req);
+ const SITE_HOSTNAME = getSiteHostname(SITE_URL);
const BRAND = {
dark: "#2d2b28",
@@ -105,7 +107,7 @@ export async function GET(req: NextRequest) {
diff --git a/src/app/api/form-templates/[id]/qr/route.ts b/src/app/api/form-templates/[id]/qr/route.ts
index 2c2be0e..2257dec 100644
--- a/src/app/api/form-templates/[id]/qr/route.ts
+++ b/src/app/api/form-templates/[id]/qr/route.ts
@@ -1,11 +1,12 @@
import { NextRequest } from "next/server";
import { prisma } from "@/lib/db";
import { requireApiAuthWithOrg, handleApiError } from "@/lib/api-auth";
+import { getSiteUrlFromRequest } from "@/lib/site-url";
import QRCode from "qrcode";
type RouteContext = { params: Promise<{ id: string }> };
-export async function GET(_request: NextRequest, ctx: RouteContext) {
+export async function GET(request: NextRequest, ctx: RouteContext) {
try {
const session = await requireApiAuthWithOrg();
const { id } = await ctx.params;
@@ -21,10 +22,7 @@ export async function GET(_request: NextRequest, ctx: RouteContext) {
return new Response("Not found", { status: 404 });
}
- const baseUrl =
- process.env.NEXTAUTH_URL ||
- process.env.AUTH_URL ||
- "https://echoocr.app";
+ const baseUrl = getSiteUrlFromRequest(request);
const surveyUrl = `${baseUrl}/s/${template.organization.slug}/${template.slug}`;
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index b78d054..cc8b8fe 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -2,6 +2,7 @@ import type { Metadata } from "next";
import { Quicksand } from "next/font/google";
import "./globals.css";
import { Providers } from "@/components/providers";
+import { getSiteUrl } from "@/lib/site-url";
const quicksand = Quicksand({
variable: "--font-sans",
@@ -9,7 +10,7 @@ const quicksand = Quicksand({
weight: ["300", "400", "500", "600", "700"],
});
-const siteUrl = process.env.NEXT_PUBLIC_SITE_URL || "https://echoocr.com";
+const siteUrl = getSiteUrl();
export const metadata: Metadata = {
title: {
diff --git a/src/lib/email-sender.ts b/src/lib/email-sender.ts
index b3069ab..0c1b7e3 100644
--- a/src/lib/email-sender.ts
+++ b/src/lib/email-sender.ts
@@ -1,6 +1,7 @@
import { BrevoClient } from "@getbrevo/brevo";
import { prisma } from "@/lib/db";
import crypto from "crypto";
+import { getSiteHostname, getSiteUrl } from "@/lib/site-url";
const brevo = new BrevoClient({ apiKey: process.env.BREVO_API_KEY! });
@@ -9,8 +10,8 @@ const DEFAULT_SENDER = {
email: process.env.EMAIL_FROM_ADDRESS || "mars@noreply.stillwell.cloud",
};
-const SITE_URL =
- process.env.NEXT_PUBLIC_SITE_URL || "https://echoocr.com";
+const SITE_URL = getSiteUrl();
+const SITE_HOSTNAME = getSiteHostname(SITE_URL);
/* ------------------------------------------------------------------ */
/* Shared template shell */
@@ -133,7 +134,7 @@ function emailShell({
AI-powered response card scanning for churches