76 lines
2.4 KiB
TypeScript
76 lines
2.4 KiB
TypeScript
import { NextResponse } from 'next/server'
|
|
import type { NextRequest } from 'next/server'
|
|
import { isDemoRequest } from '@encryptid/sdk/server/nextjs'
|
|
|
|
/**
|
|
* Middleware to handle subdomain-based space routing and protect /space routes.
|
|
*
|
|
* Subdomain routing:
|
|
* - rfunds.online -> home/landing page
|
|
* - www.rfunds.online -> home/landing page
|
|
* - <space>.rfunds.online -> rewrite to /s/<space>
|
|
*
|
|
* Auth protection:
|
|
* - /space routes require auth (cookie or Bearer token)
|
|
* - Demo spaces (ENCRYPTID_DEMO_SPACES env var) bypass auth
|
|
*
|
|
* Also handles localhost for development.
|
|
*/
|
|
export function middleware(request: NextRequest) {
|
|
const url = request.nextUrl.clone()
|
|
const hostname = request.headers.get('host') || ''
|
|
const { pathname } = request.nextUrl
|
|
|
|
// --- Subdomain routing ---
|
|
let subdomain: string | null = null
|
|
|
|
// Match production: <space>.rfunds.online
|
|
const match = hostname.match(/^([a-z0-9][a-z0-9-]*[a-z0-9]|[a-z0-9])\.\w+\.online/)
|
|
if (match && match[1] !== 'www') {
|
|
subdomain = match[1]
|
|
} else if (hostname.includes('localhost')) {
|
|
// Development: <space>.localhost:port
|
|
const parts = hostname.split('.localhost')[0].split('.')
|
|
if (parts.length > 0 && parts[0] !== 'localhost') {
|
|
subdomain = parts[parts.length - 1]
|
|
}
|
|
}
|
|
|
|
// If we have a subdomain, rewrite root path to space page
|
|
if (subdomain && subdomain.length > 0 && pathname === '/') {
|
|
url.pathname = `/s/${subdomain}`
|
|
return NextResponse.rewrite(url)
|
|
}
|
|
|
|
// --- Auth protection for /space routes ---
|
|
// Only protect /space routes (not /tbff which is a public demo)
|
|
if (pathname.startsWith('/space')) {
|
|
// Demo spaces get anonymous access — SDK provides synthetic claims
|
|
if (isDemoRequest(request)) {
|
|
return NextResponse.next()
|
|
}
|
|
|
|
// Check for auth token in cookie (set by client after login)
|
|
const token = request.cookies.get('encryptid_token')?.value
|
|
|
|
// Also check Authorization header for API-style access
|
|
const authHeader = request.headers.get('authorization')
|
|
const bearerToken = authHeader?.startsWith('Bearer ') ? authHeader.slice(7) : null
|
|
|
|
if (!token && !bearerToken) {
|
|
url.pathname = '/'
|
|
url.searchParams.set('login', 'required')
|
|
url.searchParams.set('return', pathname)
|
|
return NextResponse.redirect(url)
|
|
}
|
|
}
|
|
|
|
return NextResponse.next()
|
|
}
|
|
|
|
export const config = {
|
|
matcher: [
|
|
'/((?!_next/static|_next/image|favicon.ico|.*\\..*|api).*)',
|
|
],
|
|
}
|