Initial commit: Craft2Prints with Stages 1-3

This commit is contained in:
Andymick
2026-07-15 18:09:59 +01:00
commit 41937ffc15
158 changed files with 16054 additions and 0 deletions
+83
View File
@@ -0,0 +1,83 @@
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { jwtVerify } from 'jose';
const CUSTOMER_SESSION_COOKIE = 'customer_session';
const PUBLIC_ACCOUNT_PATHS = ['/account/login', '/account/register', '/account/forgot-password', '/account/reset-password'];
const ADMIN_SESSION_COOKIE = 'admin_session';
const PUBLIC_ADMIN_PATHS = ['/admin/login'];
// Protects everything under /admin with the shop owner's own login session —
// entirely separate cookie and secret from the customer auth below, so
// logging into one never grants access to the other.
async function checkAdminAuth(request: NextRequest, requestHeaders: Headers) {
if (PUBLIC_ADMIN_PATHS.includes(request.nextUrl.pathname)) {
return NextResponse.next({ request: { headers: requestHeaders } });
}
const token = request.cookies.get(ADMIN_SESSION_COOKIE)?.value;
const secret = process.env.ADMIN_SESSION_SECRET;
if (token && secret) {
try {
await jwtVerify(token, new TextEncoder().encode(secret));
return NextResponse.next({ request: { headers: requestHeaders } });
} catch {
// fall through to redirect
}
}
const loginUrl = new URL('/admin/login', request.url);
loginUrl.searchParams.set('next', request.nextUrl.pathname);
return NextResponse.redirect(loginUrl);
}
// Protects everything under /account (except the login/register/reset pages
// themselves) with a customer's own login session — entirely separate cookie
// and secret from the admin auth above, so one never grants access to the other.
async function checkCustomerAuth(request: NextRequest, requestHeaders: Headers) {
if (PUBLIC_ACCOUNT_PATHS.includes(request.nextUrl.pathname)) {
return NextResponse.next({ request: { headers: requestHeaders } });
}
const token = request.cookies.get(CUSTOMER_SESSION_COOKIE)?.value;
const secret = process.env.SESSION_SECRET;
if (token && secret) {
try {
await jwtVerify(token, new TextEncoder().encode(secret));
return NextResponse.next({ request: { headers: requestHeaders } });
} catch {
// fall through to redirect
}
}
const loginUrl = new URL('/account/login', request.url);
loginUrl.searchParams.set('next', request.nextUrl.pathname);
return NextResponse.redirect(loginUrl);
}
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
// Expose the path to server components (the root layout reads x-pathname to
// decide whether to show the maintenance page). Edge middleware can't read the
// DB, so the maintenance flag itself is checked in the layout, not here.
const requestHeaders = new Headers(request.headers);
requestHeaders.set('x-pathname', pathname);
if (pathname.startsWith('/admin')) {
return checkAdminAuth(request, requestHeaders);
}
if (pathname.startsWith('/account')) {
return checkCustomerAuth(request, requestHeaders);
}
return NextResponse.next({ request: { headers: requestHeaders } });
}
export const config = {
// Run on everything except static assets, so the x-pathname header is set on
// every page. The auth branches above still only gate /admin and /account.
matcher: ['/((?!_next/static|_next/image|favicon.ico|logo.png).*)'],
};