Openstatus
www.openstatus.dev
1import { NextResponse } from "next/server";
2
3import { auth } from "@/lib/auth";
4
5import { db, sql } from "@openstatus/db";
6import { page, selectPageSchema } from "@openstatus/db/src/schema";
7import { getValidSubdomain } from "./lib/domain";
8import { createProtectedCookieKey } from "./lib/protected";
9
10export default auth(async (req) => {
11 const url = req.nextUrl.clone();
12 const response = NextResponse.next();
13 const cookies = req.cookies;
14 const headers = req.headers;
15 const host = headers.get("x-forwarded-host");
16
17 let prefix = "";
18 let type: "hostname" | "pathname";
19
20 const hostnames = host?.split(/[.:]/) ?? url.host.split(/[.:]/);
21 const pathnames = url.pathname.split("/");
22
23 const subdomain = getValidSubdomain(url.host);
24 console.log({
25 hostnames,
26 pathnames,
27 host,
28 urlHost: url.host,
29 subdomain,
30 });
31
32 if (
33 hostnames.length > 2 &&
34 hostnames[0] !== "www" &&
35 !url.host.endsWith(".vercel.app")
36 ) {
37 prefix = hostnames[0].toLowerCase();
38 type = "hostname";
39 } else {
40 prefix = pathnames[1].toLowerCase();
41 type = "pathname";
42 }
43
44 if (subdomain !== null) {
45 prefix = subdomain.toLowerCase();
46 }
47
48 console.log({ pathname: url.pathname, type, prefix, subdomain });
49
50 if (url.pathname === "/" && type !== "hostname" && subdomain === null) {
51 return response;
52 }
53
54 const query = await db
55 .select()
56 .from(page)
57 .where(
58 sql`lower(${page.slug}) = ${prefix} OR lower(${page.customDomain}) = ${prefix}`,
59 )
60 .get();
61
62 const validation = selectPageSchema.safeParse(query);
63
64 if (!validation.success) {
65 return response;
66 }
67
68 const _page = validation.data;
69
70 console.log({ slug: _page?.slug, customDomain: _page?.customDomain });
71
72 if (_page?.accessType === "password") {
73 const protectedCookie = cookies.get(createProtectedCookieKey(_page.slug));
74 const cookiePassword = protectedCookie ? protectedCookie.value : undefined;
75 const queryPassword = url.searchParams.get("pw");
76 const password = queryPassword || cookiePassword;
77
78 if (password !== _page.password && !url.pathname.endsWith("/login")) {
79 const { pathname, origin } = req.nextUrl;
80
81 // custom domain redirect
82 if (_page.customDomain && host !== `${_page.slug}.stpg.dev`) {
83 const redirect = pathname.replace(`/${_page.customDomain}`, "");
84 const url = new URL(
85 `https://${_page.customDomain}/login?redirect=${encodeURIComponent(
86 redirect,
87 )}`,
88 );
89 console.log("redirect to /login", url.toString());
90 return NextResponse.redirect(url);
91 }
92
93 const url = new URL(
94 `${origin}${
95 type === "pathname" ? `/${prefix}` : ""
96 }/login?redirect=${encodeURIComponent(pathname)}`,
97 );
98 return NextResponse.redirect(url);
99 }
100 if (password === _page.password && url.pathname.endsWith("/login")) {
101 const redirect = url.searchParams.get("redirect");
102
103 // custom domain redirect
104 if (_page.customDomain && host !== `${_page.slug}.stpg.dev`) {
105 const url = new URL(`https://${_page.customDomain}${redirect ?? "/"}`);
106 console.log("redirect to /", url.toString());
107 return NextResponse.redirect(url);
108 }
109
110 return NextResponse.redirect(
111 new URL(
112 `${req.nextUrl.origin}${
113 redirect ?? type === "pathname" ? `/${prefix}` : "/"
114 }`,
115 ),
116 );
117 }
118 }
119
120 if (_page.accessType === "email-domain") {
121 const { origin, pathname } = req.nextUrl;
122 const email = req.auth?.user?.email;
123 const emailDomain = email?.split("@")[1];
124 if (
125 !pathname.endsWith("/login") &&
126 (!emailDomain || !_page.authEmailDomains.includes(emailDomain))
127 ) {
128 const url = new URL(
129 `${origin}${type === "pathname" ? `/${prefix}` : ""}/login`,
130 );
131 return NextResponse.redirect(url);
132 }
133 if (
134 pathname.endsWith("/login") &&
135 emailDomain &&
136 _page.authEmailDomains.includes(emailDomain)
137 ) {
138 const url = new URL(
139 `${origin}${type === "pathname" ? `/${prefix}` : ""}`,
140 );
141 return NextResponse.redirect(url);
142 }
143 }
144
145 const proxy = req.headers.get("x-proxy");
146 console.log({ proxy });
147
148 if (proxy) {
149 const rewriteUrl = new URL(`/${prefix}${url.pathname}`, req.url);
150 // Preserve search params from original request
151 rewriteUrl.search = url.search;
152 return NextResponse.rewrite(rewriteUrl);
153 }
154
155 console.log({
156 customDomain: _page.customDomain,
157 host,
158 expectedHost: `${_page.slug}.stpg.dev`,
159 });
160 if (_page.customDomain && host !== `${_page.slug}.stpg.dev`) {
161 if (pathnames.length > 2 && !subdomain) {
162 const pathname = pathnames.slice(2).join("/");
163 const rewriteUrl = new URL(`/${_page.slug}/${pathname}`, req.url);
164 rewriteUrl.search = url.search;
165 return NextResponse.rewrite(rewriteUrl);
166 }
167 if (_page.customDomain && subdomain) {
168 console.log({ url: req.url });
169 // const vercelURL = process.env.VERCEL_URL || "www.stpg.dev";
170 // console.log({newUrl: vercelURL})
171 if (pathnames.length > 2) {
172 const pathname = pathnames.slice(1).join("/");
173
174 const rewriteUrl = new URL(
175 `${pathname}`,
176 `https://${_page.slug}.stpg.dev`,
177 );
178 console.log({ rewriteUrl });
179 rewriteUrl.search = url.search;
180 return NextResponse.rewrite(rewriteUrl);
181 }
182 const rewriteUrl = new URL(
183 `${url.pathname}`,
184 `https://${_page.slug}.stpg.dev`,
185 );
186 console.log({ rewriteUrl });
187 rewriteUrl.search = url.search;
188 return NextResponse.rewrite(rewriteUrl);
189 }
190 const rewriteUrl = new URL(`/${_page.slug}`, req.url);
191 console.log({ rewriteUrl });
192 rewriteUrl.search = url.search;
193 return NextResponse.rewrite(rewriteUrl);
194 }
195 if (host?.includes("openstatus.dev")) {
196 const rewriteUrl = new URL(`/${prefix}${url.pathname}`, req.url);
197 // Preserve search params from original request
198 rewriteUrl.search = url.search;
199 return NextResponse.rewrite(rewriteUrl);
200 }
201 return response;
202});
203
204export const config = {
205 matcher: [
206 "/((?!api|assets|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)",
207 ],
208};