push
This commit is contained in:
@@ -1,24 +1,97 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import type { NextRequest } from 'next/server'
|
||||
|
||||
const PUBLIC_PATHS = ['/login', '/api/auth', '/api/trpc/stellen.listPublic', '/api/setup']
|
||||
const PUBLIC_PREFIXES = [
|
||||
'/login',
|
||||
'/api/auth',
|
||||
'/api/trpc/stellen.listPublic',
|
||||
'/api/setup',
|
||||
'/registrierung',
|
||||
'/impressum',
|
||||
'/datenschutz',
|
||||
]
|
||||
const PUBLIC_EXACT_PATHS = ['/']
|
||||
|
||||
// Reserved subdomains that shouldn't be treated as tenant slugs
|
||||
const RESERVED_SUBDOMAINS = ['www', 'app', 'admin', 'localhost', 'superadmin', 'api']
|
||||
|
||||
export function middleware(request: NextRequest) {
|
||||
const pathname = request.nextUrl.pathname
|
||||
const isPublic = PUBLIC_PATHS.some((p) => pathname.startsWith(p))
|
||||
const url = request.nextUrl
|
||||
const pathname = url.pathname
|
||||
|
||||
if (isPublic) return NextResponse.next()
|
||||
// 1. Subdomain Extraction
|
||||
const hostname = request.headers.get('host') || ''
|
||||
const domainParts = hostname.split(':')[0].split('.')
|
||||
let slug = null
|
||||
|
||||
// For localhost: tischler.localhost -> parts: ['tischler', 'localhost']
|
||||
// For production: tischler.innungsapp.de -> parts: ['tischler', 'innungsapp', 'de']
|
||||
if (
|
||||
domainParts.length > 2 ||
|
||||
(domainParts.length === 2 && domainParts[1] === 'localhost')
|
||||
) {
|
||||
const potentialSlug = domainParts[0]
|
||||
if (!RESERVED_SUBDOMAINS.includes(potentialSlug)) {
|
||||
slug = potentialSlug
|
||||
}
|
||||
}
|
||||
|
||||
// Allow static files from /public
|
||||
const isStaticFile = pathname.includes('.') && !pathname.startsWith('/api')
|
||||
const isPublic =
|
||||
isStaticFile ||
|
||||
PUBLIC_EXACT_PATHS.includes(pathname) ||
|
||||
PUBLIC_PREFIXES.some((p) => pathname.startsWith(p))
|
||||
|
||||
// 2. Auth Check
|
||||
const sessionToken =
|
||||
request.cookies.get('better-auth.session_token') ??
|
||||
request.cookies.get('__Secure-better-auth.session_token')
|
||||
|
||||
if (!sessionToken) {
|
||||
if (!isPublic && !sessionToken) {
|
||||
const loginUrl = new URL('/login', request.url)
|
||||
loginUrl.searchParams.set('callbackUrl', pathname)
|
||||
return NextResponse.redirect(loginUrl)
|
||||
}
|
||||
|
||||
// 3. Subdomain Redirection / Rewrite
|
||||
if (slug) {
|
||||
// Paths that should not be rewritten into the slug folder
|
||||
// because they are shared across the entire app
|
||||
const SHARED_PATHS = ['/login', '/api', '/superadmin', '/registrierung', '/impressum', '/datenschutz', '/passwort-aendern']
|
||||
const isSharedPath = SHARED_PATHS.some((p) => pathname.startsWith(p)) || pathname.startsWith('/_next')
|
||||
|
||||
if (!isSharedPath && !pathname.startsWith(`/${slug}`)) {
|
||||
const rewriteUrl = request.nextUrl.clone()
|
||||
rewriteUrl.pathname = `/${slug}${pathname === '/' ? '' : pathname}`
|
||||
return NextResponse.rewrite(rewriteUrl)
|
||||
}
|
||||
} else {
|
||||
// Check if the user is trying to access a path that starts with a potential slug
|
||||
// but they are on the root domain.
|
||||
// Example: localhost/tischler/... should redirect to tischler.localhost/...
|
||||
const pathParts = pathname.split('/')
|
||||
if (pathParts.length > 1) {
|
||||
const potentialSlug = pathParts[1]
|
||||
// Check if it's a known non-reserved path but could be an organization slug
|
||||
// We don't want to redirect /login, /api, etc.
|
||||
const SHARED_PATHS = ['login', 'api', 'superadmin', 'dashboard', 'registrierung', 'impressum', 'datenschutz', '_next', 'uploads', 'favicon.ico', 'passwort-aendern']
|
||||
if (potentialSlug && !SHARED_PATHS.includes(potentialSlug)) {
|
||||
// This looks like a tenant path being accessed from the root domain.
|
||||
// Redirect to subdomain.
|
||||
const baseHost = hostname.split('.').slice(-2).join('.') // Simplistic, assumes domain.tld or localhost
|
||||
// For localhost it's special
|
||||
const isLocalhost = hostname.includes('localhost')
|
||||
const newHost = isLocalhost
|
||||
? `${potentialSlug}.localhost${hostname.includes(':') ? `:${hostname.split(':')[1]}` : ''}`
|
||||
: `${potentialSlug}.${baseHost}`
|
||||
|
||||
const remainingPath = '/' + pathParts.slice(2).join('/')
|
||||
return NextResponse.redirect(new URL(remainingPath, `${url.protocol}//${newHost}`))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NextResponse.next()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user