feat(web): finish RE 5000PBR lesson video and fix non-ASCII GLB import
- Add 11:07 Chinese-narrated lesson video, VTT, poster and 13 chapter screenshots. - Regenerate 20-render WebGL2 matrix and 15 operation captures on RTX 4060. - Fix WASM staging for non-ASCII import filenames (猴头.glb); rebuild web_engine. - Harden lesson video recording pipeline; fix stale lesson-link E2E selector. - Add RE PBR derived libraries, lesson page, evidence and status report. Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -42,9 +42,15 @@ function resolvedFile(root, pathname) {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
function strongEtag(filePath) {
|
||||
const etagCache = new Map();
|
||||
function strongEtag(filePath, stat) {
|
||||
const key = path.resolve(filePath);
|
||||
const cached = etagCache.get(key);
|
||||
if (cached && cached.size === stat.size && cached.mtimeMs === stat.mtimeMs) return cached.etag;
|
||||
const hash = crypto.createHash("sha256").update(fs.readFileSync(filePath)).digest("hex");
|
||||
return `"sha256-${hash}"`;
|
||||
const etag = `"sha256-${hash}"`;
|
||||
etagCache.set(key, { size: stat.size, mtimeMs: stat.mtimeMs, etag });
|
||||
return etag;
|
||||
}
|
||||
|
||||
function matchesIfNoneMatch(value, etag) {
|
||||
@@ -121,7 +127,7 @@ export function createDeploymentHttpServer({ root, contract = loadDeploymentCont
|
||||
const extension = path.extname(filePath).toLowerCase();
|
||||
if (expectedAuthorization) { headers["Cache-Control"] = "private, no-store"; headers.Vary = "Authorization"; }
|
||||
const rangeEnabled = contract.rangeRequests.extensions.includes(extension);
|
||||
const etag = strongEtag(filePath);
|
||||
const etag = strongEtag(filePath, stat);
|
||||
headers.ETag = etag;
|
||||
if (rangeEnabled) headers["Accept-Ranges"] = contract.rangeRequests.unit;
|
||||
if (matchesIfNoneMatch(request.headers["if-none-match"], etag)) {
|
||||
|
||||
Reference in New Issue
Block a user