mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-26 23:04:57 +00:00
code: nextjs,frontends,tsx (2 files)
This commit is contained in:
48
frontends/nextjs/src/app/[tenant]/[package]/layout.tsx
Normal file
48
frontends/nextjs/src/app/[tenant]/[package]/layout.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Tenant-Scoped Layout
|
||||
*
|
||||
* This layout wraps all pages under /{tenant}/{package}/...
|
||||
* It provides tenant context to all child components.
|
||||
*/
|
||||
|
||||
import { headers } from 'next/headers'
|
||||
import { notFound } from 'next/navigation'
|
||||
import { TenantProvider } from './tenant-context'
|
||||
|
||||
interface TenantLayoutProps {
|
||||
children: React.ReactNode
|
||||
params: Promise<{
|
||||
tenant: string
|
||||
package: string
|
||||
}>
|
||||
}
|
||||
|
||||
export default async function TenantLayout({
|
||||
children,
|
||||
params,
|
||||
}: TenantLayoutProps) {
|
||||
const resolvedParams = await params
|
||||
const { tenant, package: pkg } = resolvedParams
|
||||
|
||||
// Validate tenant exists
|
||||
// TODO: Check against database
|
||||
// const tenantData = await getTenant(tenant)
|
||||
// if (!tenantData) {
|
||||
// notFound()
|
||||
// }
|
||||
|
||||
// Validate package exists and is installed for tenant
|
||||
// TODO: Check against database
|
||||
// const packageInstalled = await isPackageInstalled(tenant, pkg)
|
||||
// if (!packageInstalled) {
|
||||
// notFound()
|
||||
// }
|
||||
|
||||
return (
|
||||
<TenantProvider tenant={tenant} packageId={pkg}>
|
||||
<div className="tenant-layout" data-tenant={tenant} data-package={pkg}>
|
||||
{children}
|
||||
</div>
|
||||
</TenantProvider>
|
||||
)
|
||||
}
|
||||
51
frontends/nextjs/src/middleware.ts
Normal file
51
frontends/nextjs/src/middleware.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Next.js Middleware
|
||||
*
|
||||
* Handles multi-tenant routing at the edge.
|
||||
* Routes: /{tenant}/{package}/... are treated as tenant-scoped requests.
|
||||
*/
|
||||
|
||||
import { NextResponse } from 'next/server'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { isReservedPath, RESERVED_PATHS } from '@/lib/routing/route-parser'
|
||||
|
||||
export function middleware(request: NextRequest) {
|
||||
const { pathname } = request.nextUrl
|
||||
|
||||
// Skip reserved paths
|
||||
const firstSegment = pathname.split('/')[1]
|
||||
if (!firstSegment || isReservedPath(firstSegment)) {
|
||||
return NextResponse.next()
|
||||
}
|
||||
|
||||
// Check if this looks like a tenant route: /{tenant}/{package}/...
|
||||
const segments = pathname.split('/').filter(Boolean)
|
||||
|
||||
if (segments.length >= 2) {
|
||||
// Looks like a tenant route
|
||||
const tenant = segments[0]
|
||||
const pkg = segments[1]
|
||||
|
||||
// Add tenant info to headers for downstream use
|
||||
const response = NextResponse.next()
|
||||
response.headers.set('x-tenant-id', tenant)
|
||||
response.headers.set('x-package-id', pkg)
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
return NextResponse.next()
|
||||
}
|
||||
|
||||
export const config = {
|
||||
matcher: [
|
||||
/*
|
||||
* Match all request paths except:
|
||||
* - _next/static (static files)
|
||||
* - _next/image (image optimization files)
|
||||
* - favicon.ico (favicon file)
|
||||
* - public folder
|
||||
*/
|
||||
'/((?!_next/static|_next/image|favicon.ico|public/).*)',
|
||||
],
|
||||
}
|
||||
Reference in New Issue
Block a user