perfect-postcode/og-screenshot/src/cache.ts

88 lines
2.2 KiB
TypeScript

import { createHash } from 'crypto';
import { existsSync, mkdirSync, statSync, createReadStream, writeFileSync } from 'fs';
import { join } from 'path';
import type { Readable } from 'stream';
const MAX_AGE_MS = 24 * 60 * 60 * 1000; // 24 hours
export class ScreenshotCache {
private dir: string;
constructor(dir: string) {
this.dir = dir;
if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true });
}
}
/**
* Build a cache key by quantizing view params and hashing.
* - lat/lng quantized to 2 decimal places
* - zoom quantized to integer
* - filters sorted alphabetically
*/
buildKey(params: Record<string, string>): string {
const normalized: Record<string, string> = {};
// Parse and quantize the view param (lat,lng,zoom)
if (params.v) {
const parts = params.v.split(',');
if (parts.length === 3) {
const lat = parseFloat(parts[0]).toFixed(2);
const lng = parseFloat(parts[1]).toFixed(2);
const zoom = Math.round(parseFloat(parts[2])).toString();
normalized.v = `${lat},${lng},${zoom}`;
} else {
normalized.v = params.v;
}
}
// Sort filters
if (params.f) {
const segments = params.f.split(',').sort();
normalized.f = segments.join(',');
}
if (params.poi) {
const cats = params.poi.split(',').sort();
normalized.poi = cats.join(',');
}
if (params.tab) {
normalized.tab = params.tab;
}
const input = JSON.stringify(normalized);
const hash = createHash('sha256').update(input).digest('hex').substring(0, 16);
return hash;
}
getPath(key: string): string {
return join(this.dir, `${key}.png`);
}
/**
* Returns a readable stream if a fresh cached file exists, null otherwise.
*/
get(key: string): Readable | null {
const path = this.getPath(key);
if (!existsSync(path)) return null;
try {
const stat = statSync(path);
const age = Date.now() - stat.mtimeMs;
if (age > MAX_AGE_MS) return null;
return createReadStream(path);
} catch {
return null;
}
}
/**
* Write screenshot bytes to cache.
*/
set(key: string, data: Buffer): void {
const path = this.getPath(key);
writeFileSync(path, data);
}
}