diff --git a/packages/ui/certd-client/src/views/certd/mine/user-profile.vue b/packages/ui/certd-client/src/views/certd/mine/user-profile.vue
index 11009f97e..08f624945 100644
--- a/packages/ui/certd-client/src/views/certd/mine/user-profile.vue
+++ b/packages/ui/certd-client/src/views/certd/mine/user-profile.vue
@@ -396,7 +396,7 @@ const userAvatar = computed(() => {
return userInfo.value.avatar;
}
- return `api/basic/file/download?token=${userStore.getToken}&key=${userInfo.value.avatar}`;
+ return `api/basic/file/download?key=${userInfo.value.avatar}`;
});
onMounted(async () => {
diff --git a/packages/ui/certd-server/src/configuration-cache.ts b/packages/ui/certd-server/src/configuration-cache.ts
new file mode 100644
index 000000000..7b1091800
--- /dev/null
+++ b/packages/ui/certd-server/src/configuration-cache.ts
@@ -0,0 +1,6 @@
+export function shouldSetDefaultNoCache(path: string, cacheControl?: string) {
+ if (cacheControl) {
+ return false;
+ }
+ return path === '/' || path === '/index.html' || path.startsWith('/api');
+}
diff --git a/packages/ui/certd-server/src/configuration.test.ts b/packages/ui/certd-server/src/configuration.test.ts
new file mode 100644
index 000000000..9b1266f81
--- /dev/null
+++ b/packages/ui/certd-server/src/configuration.test.ts
@@ -0,0 +1,22 @@
+///
+///
+
+import assert from "node:assert/strict";
+
+import { shouldSetDefaultNoCache } from "./configuration-cache.js";
+
+describe("shouldSetDefaultNoCache", () => {
+ it("sets default no-cache for html and api responses without cache headers", () => {
+ assert.equal(shouldSetDefaultNoCache("/"), true);
+ assert.equal(shouldSetDefaultNoCache("/index.html"), true);
+ assert.equal(shouldSetDefaultNoCache("/api/basic/file/download"), true);
+ });
+
+ it("keeps explicit cache headers from file responses", () => {
+ assert.equal(shouldSetDefaultNoCache("/api/basic/file/download", "public,max-age=259200"), false);
+ });
+
+ it("ignores non-html and non-api paths", () => {
+ assert.equal(shouldSetDefaultNoCache("/static/images/logo.svg"), false);
+ });
+});
diff --git a/packages/ui/certd-server/src/configuration.ts b/packages/ui/certd-server/src/configuration.ts
index a3450eaa3..13ee49993 100644
--- a/packages/ui/certd-server/src/configuration.ts
+++ b/packages/ui/certd-server/src/configuration.ts
@@ -20,6 +20,7 @@ import * as commercial from '@certd/commercial-core';
import * as upload from '@midwayjs/upload';
import { setLogger } from '@certd/acme-client';
import {HiddenMiddleware} from "./middleware/hidden.js";
+import { shouldSetDefaultNoCache } from './configuration-cache.js';
// import * as swagger from '@midwayjs/swagger';
//@ts-ignore
// process.env.UV_THREADPOOL_SIZE = 2
@@ -123,7 +124,7 @@ export class MainConfiguration {
this.app.getMiddleware().insertFirst(async (ctx: IMidwayKoaContext, next: NextFunction) => {
await next();
- if (ctx.path === '/' || ctx.path === '/index.html' || ctx.path.startsWith("/api")) {
+ if (shouldSetDefaultNoCache(ctx.path, ctx.response.get('Cache-Control'))) {
ctx.response.set('Cache-Control', 'public,max-age=0');
}
});
diff --git a/packages/ui/certd-server/src/controller/basic/file-controller.test.ts b/packages/ui/certd-server/src/controller/basic/file-controller.test.ts
index a09837292..f968d1c15 100644
--- a/packages/ui/certd-server/src/controller/basic/file-controller.test.ts
+++ b/packages/ui/certd-server/src/controller/basic/file-controller.test.ts
@@ -3,7 +3,7 @@
import assert from "node:assert/strict";
-import { isImageFile } from "./file-controller.js";
+import { getImageDownloadOptions, isImageFile } from "./file-controller.js";
describe("FileController.isImageFile", () => {
it("detects uploaded logo image files", () => {
@@ -17,4 +17,23 @@ describe("FileController.isImageFile", () => {
assert.equal(isImageFile("data/upload/public/user/cert.pem"), false);
assert.equal(isImageFile("data/upload/public/user/logo"), false);
});
+
+ it("builds koa-send options that keep image cache headers at 3 days", () => {
+ const options = getImageDownloadOptions("data/upload/public/user/logo.png");
+
+ assert.equal(options?.maxage, 259200000);
+
+ const headers: Record = {};
+ options?.setHeaders({
+ setHeader(key: string, value: string) {
+ headers[key] = value;
+ },
+ });
+
+ assert.equal(headers["Cache-Control"], "public,max-age=259200");
+ });
+
+ it("does not build cache options for non-image files", () => {
+ assert.equal(getImageDownloadOptions("data/upload/private/user/cert.pem"), undefined);
+ });
});
diff --git a/packages/ui/certd-server/src/controller/basic/file-controller.ts b/packages/ui/certd-server/src/controller/basic/file-controller.ts
index e0340fd79..1fb6bd203 100644
--- a/packages/ui/certd-server/src/controller/basic/file-controller.ts
+++ b/packages/ui/certd-server/src/controller/basic/file-controller.ts
@@ -12,6 +12,18 @@ export function isImageFile(filePath: string) {
return imageExtSet.has(filePath.substring(filePath.lastIndexOf('.')).toLowerCase());
}
+export function getImageDownloadOptions(filePath: string) {
+ if (!isImageFile(filePath)) {
+ return undefined;
+ }
+ return {
+ maxage: imageCacheSeconds * 1000,
+ setHeaders(res: any) {
+ res.setHeader('Cache-Control', `public,max-age=${imageCacheSeconds}`);
+ },
+ };
+}
+
/**
*/
@Provide()
@@ -47,11 +59,10 @@ export class FileController extends BaseController {
userId = this.getUserId();
}
const filePath = this.fileService.getFile(key, userId);
- if (isImageFile(filePath)) {
- this.ctx.response.set('Cache-Control', `public,max-age=${imageCacheSeconds}`);
- } else {
+ const sendOptions = getImageDownloadOptions(filePath);
+ if (!sendOptions) {
this.ctx.response.attachment(filePath);
}
- await send(this.ctx, filePath);
+ await send(this.ctx, filePath, sendOptions);
}
}