Files
probably-rendering-one-more…/apps/waterfall-wiki/src/proxy.ts
T
codex-bot 5a0554b3b1
creative workspace review checks / verify (push) Failing after 4s
creative workspace review checks / verify (pull_request) Failing after 2s
Introduce creative landing page and workspace with focused editor layouts
2026-10-30 14:10:40 +08:00

30 lines
2.9 KiB
TypeScript

import { NextResponse, type NextRequest } from 'next/server';
import { authConfig, SESSION_COOKIE, validSession } from './lib/auth';
export function proxy(request: NextRequest) {
const path = request.nextUrl.pathname;
const responseHeaders = {'Cache-Control':'private, no-store', 'Referrer-Policy':'same-origin'};
// Public knowledge pages are read-only. Account handlers enforce their own origin and session checks.
const publicRead = ['GET','HEAD'].includes(request.method) && (path === '/' || ['/workspace','/graphic','/infinite_canvas','/studio','/studio/graphic','/studio/infinite_canvas'].includes(path) || path.startsWith('/studio/_next/') || path === '/protocol' || ['/llms.txt','/index.md','/agent','/agent/index.md','/api/agent-index','/api/guides'].includes(path) || path.startsWith('/guide/') || path.startsWith('/api/guides/'));
if (path === '/write' || path === '/documents' || path.startsWith('/documents/') || path === '/api/documents' || path.startsWith('/api/documents/') || publicRead || path === '/account' || path.startsWith('/api/account/')) return NextResponse.next({headers:{...responseHeaders,'Referrer-Policy':'no-referrer'}});
if (path === '/brand-mark.svg' || path === '/login' || path === '/api/auth/login' || path === '/api/auth/logout' || path === '/api/health' || path === '/api/ready' || path.startsWith('/_next/static/')) return NextResponse.next({headers:responseHeaders});
let config;
try { config = authConfig(); } catch { return NextResponse.json({error:'登录服务配置不可用'}, {status:503,headers:responseHeaders}); }
if (!config) {
if (process.env.AUTH_DISABLED_FOR_TESTS === 'true' && process.env.AUTH_CONFIG_FILE === '') return NextResponse.next();
return NextResponse.json({error:'管理员登录服务尚未配置'}, {status:503,headers:responseHeaders});
}
const bearer = request.headers.get('authorization');
const token = bearer?.startsWith('Bearer ') ? bearer.slice(7) : request.cookies.get(SESSION_COOKIE)?.value;
if (validSession(token,config)) {
if (!['GET','HEAD','OPTIONS'].includes(request.method) && !bearer) {
const origin = request.headers.get('origin');
const host = request.headers.get('host');
try { if (!origin || new URL(origin).host !== host || request.headers.get('sec-fetch-site') === 'cross-site') return NextResponse.json({error:'请求来源不受信任'}, {status:403,headers:responseHeaders}); } catch { return NextResponse.json({error:'请求来源不受信任'}, {status:403,headers:responseHeaders}); }
}
return NextResponse.next({headers:responseHeaders});
}
if (path.startsWith('/api/') || path.startsWith('/studio/api/') || !['GET','HEAD'].includes(request.method)) return NextResponse.json({error:'请先使用动态码登录'}, {status:401,headers:responseHeaders});
const login = request.nextUrl.clone(); login.pathname = '/login'; login.search = ''; login.searchParams.set('next',path + request.nextUrl.search);
return NextResponse.redirect(login,{headers:responseHeaders});
}