| import crypto from 'node:crypto'; |
| import { DEFAULT_USER } from '../constants.js'; |
| import { getConfigValue } from '../util.js'; |
|
|
| |
| |
| |
| class CacheBuster { |
| |
| |
| |
| |
| #keys = new Set(); |
|
|
| |
| |
| |
| |
| #userAgentRegex = null; |
|
|
| |
| |
| |
| |
| #isEnabled = null; |
|
|
| constructor() { |
| this.#isEnabled = !!getConfigValue('cacheBuster.enabled', false, 'boolean'); |
| const userAgentPattern = getConfigValue('cacheBuster.userAgentPattern', ''); |
| if (userAgentPattern) { |
| try { |
| this.#userAgentRegex = new RegExp(userAgentPattern, 'i'); |
| } catch { |
| console.error('[Cache Buster] Invalid user agent pattern:', userAgentPattern); |
| } |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| shouldBust(request, response) { |
| |
| if (!this.#isEnabled) { |
| return false; |
| } |
|
|
| |
| if (response.headersSent || response.writableEnded) { |
| console.warn('[Cache Buster] Response ended or headers already sent'); |
| return false; |
| } |
|
|
| |
| const userAgent = request.headers['user-agent'] || ''; |
|
|
| |
| if (!this.#userAgentRegex) { |
| return true; |
| } |
|
|
| return this.#userAgentRegex.test(userAgent); |
| } |
|
|
| |
| |
| |
| |
| #middleware(request, response, next) { |
| const handle = request.user?.profile?.handle || DEFAULT_USER.handle; |
| const userAgent = request.headers['user-agent'] || ''; |
| const hash = crypto.createHash('sha256').update(userAgent).digest('hex'); |
| const key = `${handle}-${hash}`; |
|
|
| if (this.#keys.has(key)) { |
| return next(); |
| } |
|
|
| this.#keys.add(key); |
| this.bust(request, response); |
| next(); |
| } |
|
|
| |
| |
| |
| |
| get middleware() { |
| return this.#middleware.bind(this); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| bust(request, response) { |
| if (this.shouldBust(request, response)) { |
| response.setHeader('Clear-Site-Data', '"cache"'); |
| } |
| } |
| } |
|
|
| |
| const instance = new CacheBuster(); |
| export default instance; |
|
|