fix(kimi-web): cache content-hashed assets (#2865)

This commit is contained in:
weiwei Wang 2026-08-14 13:08:30 +08:00 committed by GitHub
parent cb30a7799d
commit 53909d91e3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 69 additions and 1 deletions

View file

@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": patch
---
Cache content-hashed Kimi Web assets across reloads while keeping the app entry point revalidated.

View file

@ -1,6 +1,6 @@
import { createReadStream } from 'node:fs';
import { stat } from 'node:fs/promises';
import { extname, join, normalize, resolve, sep } from 'node:path';
import { extname, join, normalize, relative, resolve, sep } from 'node:path';
import type { FastifyReply, FastifyRequest } from 'fastify';
@ -56,10 +56,20 @@ async function serveWebAsset(
return reply
.type(mimeType(filePath))
.header('Cache-Control', cacheControl(assetsDir, filePath))
.header('Content-Length', String(fileInfo.size))
.send(createReadStream(filePath));
}
function cacheControl(assetsDir: string, filePath: string): string {
const assetPath = relative(assetsDir, filePath);
const fileName = filePath.slice(filePath.lastIndexOf(sep) + 1);
if (assetPath.startsWith(`assets${sep}`) && /[-.][A-Za-z0-9_-]{8}\.[^.]+$/.test(fileName)) {
return 'public, max-age=31536000, immutable';
}
return 'no-cache';
}
async function resolveStaticFile(
assetsDir: string,
pathname: string,

View file

@ -0,0 +1,53 @@
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import Fastify, { type FastifyInstance } from 'fastify';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { registerWebAssetRoutes } from '../src/routes/webAssets';
describe('web asset cache policy', () => {
let app: FastifyInstance;
let assetsDir: string;
beforeEach(async () => {
assetsDir = await mkdtemp(join(tmpdir(), 'kimi-web-assets-'));
await mkdir(join(assetsDir, 'assets'));
await Promise.all([
writeFile(join(assetsDir, 'index.html'), '<main>Kimi</main>'),
writeFile(join(assetsDir, 'assets', 'index-Dy7xs5tu.js'), 'export {};'),
writeFile(join(assetsDir, 'assets', 'application-configuration.json'), '{}'),
writeFile(join(assetsDir, 'favicon.svg'), '<svg></svg>'),
]);
app = Fastify();
await registerWebAssetRoutes(app, assetsDir);
});
afterEach(async () => {
await app.close();
await rm(assetsDir, { recursive: true, force: true });
});
it('caches content-hashed assets as immutable', async () => {
const response = await app.inject({ method: 'GET', url: '/assets/index-Dy7xs5tu.js' });
expect(response.statusCode).toBe(200);
expect(response.headers['cache-control']).toBe('public, max-age=31536000, immutable');
});
it.each([
'/index.html',
'/sessions/active',
'/favicon.svg',
'/assets/application-configuration.json',
])(
'requires revalidation for %s',
async (url) => {
const response = await app.inject({ method: 'GET', url });
expect(response.statusCode).toBe(200);
expect(response.headers['cache-control']).toBe('no-cache');
},
);
});