mirror of
https://github.com/shuaiplus/nodewarden.git
synced 2026-06-20 21:00:41 +00:00
feat: simplify asset serving and enhance bootstrap response handling
This commit is contained in:
+1
-30
@@ -4,7 +4,6 @@ import { handleRequest } from './router';
|
|||||||
import { StorageService } from './services/storage';
|
import { StorageService } from './services/storage';
|
||||||
import { applyCors, jsonResponse } from './utils/response';
|
import { applyCors, jsonResponse } from './utils/response';
|
||||||
import { runScheduledBackupIfDue } from './handlers/backup';
|
import { runScheduledBackupIfDue } from './handlers/backup';
|
||||||
import { buildWebBootstrapResponse } from './router-public';
|
|
||||||
|
|
||||||
let dbInitialized = false;
|
let dbInitialized = false;
|
||||||
let dbInitError: string | null = null;
|
let dbInitError: string | null = null;
|
||||||
@@ -23,41 +22,13 @@ function isWorkerHandledPath(path: string): boolean {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function injectBootstrapIntoHtml(html: string, env: Env): string {
|
|
||||||
const payload = JSON.stringify(buildWebBootstrapResponse(env)).replace(/</g, '\\u003c');
|
|
||||||
const script = `<script>window.__NW_BOOT__=${payload};</script>`;
|
|
||||||
if (html.includes('</head>')) {
|
|
||||||
return html.replace('</head>', `${script}</head>`);
|
|
||||||
}
|
|
||||||
return `${script}${html}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function responseStatusCannotHaveBody(status: number): boolean {
|
|
||||||
return status === 101 || status === 204 || status === 205 || status === 304;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function maybeServeAsset(request: Request, env: Env): Promise<Response | null> {
|
async function maybeServeAsset(request: Request, env: Env): Promise<Response | null> {
|
||||||
if (!env.ASSETS) return null;
|
if (!env.ASSETS) return null;
|
||||||
if (request.method !== 'GET' && request.method !== 'HEAD') return null;
|
if (request.method !== 'GET' && request.method !== 'HEAD') return null;
|
||||||
const url = new URL(request.url);
|
const url = new URL(request.url);
|
||||||
if (isWorkerHandledPath(url.pathname)) return null;
|
if (isWorkerHandledPath(url.pathname)) return null;
|
||||||
|
|
||||||
const assetResponse = await env.ASSETS.fetch(request);
|
return env.ASSETS.fetch(request);
|
||||||
const contentType = String(assetResponse.headers.get('Content-Type') || '').toLowerCase();
|
|
||||||
if (
|
|
||||||
request.method === 'GET' &&
|
|
||||||
contentType.includes('text/html') &&
|
|
||||||
!responseStatusCannotHaveBody(assetResponse.status)
|
|
||||||
) {
|
|
||||||
const html = await assetResponse.text();
|
|
||||||
const injected = injectBootstrapIntoHtml(html, env);
|
|
||||||
return new Response(injected, {
|
|
||||||
status: assetResponse.status,
|
|
||||||
statusText: assetResponse.statusText,
|
|
||||||
headers: assetResponse.headers,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return assetResponse;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ensureDatabaseInitialized(env: Env): Promise<void> {
|
async function ensureDatabaseInitialized(env: Env): Promise<void> {
|
||||||
|
|||||||
@@ -175,6 +175,12 @@ export async function handlePublicRoute(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((path === '/api/web-bootstrap' || path === '/web-bootstrap') && method === 'GET') {
|
||||||
|
const blocked = await enforcePublicRateLimit('public-read', LIMITS.rateLimit.publicReadRequestsPerMinute);
|
||||||
|
if (blocked) return blocked;
|
||||||
|
return jsonResponse(buildWebBootstrapResponse(env));
|
||||||
|
}
|
||||||
|
|
||||||
const iconMatch = path.match(/^\/icons\/([^/]+)\/icon\.png$/i);
|
const iconMatch = path.match(/^\/icons\/([^/]+)\/icon\.png$/i);
|
||||||
if (iconMatch && method === 'GET') {
|
if (iconMatch && method === 'GET') {
|
||||||
return handleWebsiteIcon(iconMatch[1]);
|
return handleWebsiteIcon(iconMatch[1]);
|
||||||
|
|||||||
+34
-11
@@ -92,6 +92,35 @@ function readWindowBootstrap(): WebBootstrapResponse {
|
|||||||
return raw && typeof raw === 'object' ? raw : {};
|
return raw && typeof raw === 'object' ? raw : {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeBootstrapResponse(boot: WebBootstrapResponse): Pick<InitialAppBootstrapState, 'defaultKdfIterations' | 'jwtWarning'> {
|
||||||
|
const defaultKdfIterations = Number(boot.defaultKdfIterations || 600000);
|
||||||
|
const jwtUnsafeReason = boot.jwtUnsafeReason || null;
|
||||||
|
const jwtWarning = jwtUnsafeReason
|
||||||
|
? {
|
||||||
|
reason: jwtUnsafeReason,
|
||||||
|
minLength: Number(boot.jwtSecretMinLength || 32),
|
||||||
|
}
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
defaultKdfIterations,
|
||||||
|
jwtWarning,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchBootstrapConfig(): Promise<WebBootstrapResponse> {
|
||||||
|
try {
|
||||||
|
const resp = await fetch('/api/web-bootstrap', {
|
||||||
|
method: 'GET',
|
||||||
|
headers: { Accept: 'application/json' },
|
||||||
|
});
|
||||||
|
if (!resp.ok) return {};
|
||||||
|
return ((await resp.json()) as WebBootstrapResponse) || {};
|
||||||
|
} catch {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
interface AccessTokenClaims {
|
interface AccessTokenClaims {
|
||||||
sub?: string;
|
sub?: string;
|
||||||
email?: string;
|
email?: string;
|
||||||
@@ -129,15 +158,7 @@ function buildTransientProfile(token: TokenSuccess, email: string): Profile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function readInitialAppBootstrapState(): InitialAppBootstrapState {
|
export function readInitialAppBootstrapState(): InitialAppBootstrapState {
|
||||||
const boot = readWindowBootstrap();
|
const { defaultKdfIterations, jwtWarning } = normalizeBootstrapResponse(readWindowBootstrap());
|
||||||
const defaultKdfIterations = Number(boot.defaultKdfIterations || 600000);
|
|
||||||
const jwtUnsafeReason = boot.jwtUnsafeReason || null;
|
|
||||||
const jwtWarning = jwtUnsafeReason
|
|
||||||
? {
|
|
||||||
reason: jwtUnsafeReason,
|
|
||||||
minLength: Number(boot.jwtSecretMinLength || 32),
|
|
||||||
}
|
|
||||||
: null;
|
|
||||||
const session = loadSession();
|
const session = loadSession();
|
||||||
const hasInviteCode = !!readInviteCodeFromUrl();
|
const hasInviteCode = !!readInviteCodeFromUrl();
|
||||||
|
|
||||||
@@ -150,8 +171,10 @@ export function readInitialAppBootstrapState(): InitialAppBootstrapState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function bootstrapAppSession(initial: InitialAppBootstrapState = readInitialAppBootstrapState()): Promise<BootstrapAppResult> {
|
export async function bootstrapAppSession(initial: InitialAppBootstrapState = readInitialAppBootstrapState()): Promise<BootstrapAppResult> {
|
||||||
const defaultKdfIterations = initial.defaultKdfIterations;
|
const remoteBoot = await fetchBootstrapConfig();
|
||||||
const jwtWarning = initial.jwtWarning;
|
const normalizedBoot = normalizeBootstrapResponse(remoteBoot);
|
||||||
|
const defaultKdfIterations = normalizedBoot.defaultKdfIterations || initial.defaultKdfIterations;
|
||||||
|
const jwtWarning = normalizedBoot.jwtWarning ?? initial.jwtWarning;
|
||||||
|
|
||||||
if (jwtWarning) {
|
if (jwtWarning) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ enabled = false
|
|||||||
binding = "ASSETS"
|
binding = "ASSETS"
|
||||||
directory = "./dist"
|
directory = "./dist"
|
||||||
not_found_handling = "single-page-application"
|
not_found_handling = "single-page-application"
|
||||||
run_worker_first = true
|
run_worker_first = false
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
command = "npm run build"
|
command = "npm run build"
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ enabled = false
|
|||||||
binding = "ASSETS"
|
binding = "ASSETS"
|
||||||
directory = "./dist"
|
directory = "./dist"
|
||||||
not_found_handling = "single-page-application"
|
not_found_handling = "single-page-application"
|
||||||
run_worker_first = true
|
run_worker_first = false
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
command = "npm run build"
|
command = "npm run build"
|
||||||
|
|||||||
Reference in New Issue
Block a user