Openstatus www.openstatus.dev
at main 208 lines 6.3 kB view raw
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};