first commit

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
michilis
2026-08-20 22:41:25 +02:00
co-authored by Cursor
commit aa1771ea20
136 changed files with 27069 additions and 0 deletions
+65
View File
@@ -0,0 +1,65 @@
import fs from 'node:fs/promises';
import path from 'node:path';
import { config } from './config.ts';
import { log } from './log.ts';
import type { MintRow } from './mints.ts';
const EXT_BY_TYPE: Record<string, string> = {
'image/png': '.png',
'image/jpeg': '.jpg',
'image/webp': '.webp',
'image/svg+xml': '.svg',
'image/gif': '.gif',
'image/x-icon': '.ico',
'image/vnd.microsoft.icon': '.ico',
};
const MAX_ICON_BYTES = 512 * 1024;
/**
* Cache a mint's icon to disk so offline mints keep theirs.
* Re-fetches only when the icon_url changed, per BACKEND.md.
* Returns the stored filename, or the existing one if nothing needed doing.
*/
export async function cacheIcon(row: MintRow, iconUrl: string | null): Promise<string | null> {
if (!iconUrl) return row.icon_file;
if (iconUrl === row.icon_url && row.icon_file) return row.icon_file;
// Some mints inline the icon as a data URI. Nothing to fetch, and nothing worth storing.
if (iconUrl.startsWith('data:')) return row.icon_file;
try {
const absolute = new URL(iconUrl, `${row.url}/`).toString();
if (!absolute.startsWith('https://') && !absolute.startsWith('http://')) return row.icon_file;
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), config.probeTimeoutMs);
let res: Response;
try {
res = await fetch(absolute, {
signal: controller.signal,
headers: { 'User-Agent': config.userAgent },
});
} finally {
clearTimeout(timer);
}
if (!res.ok) return row.icon_file;
const type = (res.headers.get('content-type') ?? '').split(';')[0]?.trim() ?? '';
const ext = EXT_BY_TYPE[type];
if (!ext) return row.icon_file;
const buf = Buffer.from(await res.arrayBuffer());
if (buf.byteLength === 0 || buf.byteLength > MAX_ICON_BYTES) return row.icon_file;
const filename = `${row.host}${ext}`;
await fs.writeFile(path.join(config.iconDir, filename), buf);
return filename;
} catch (err) {
log.warn('icon fetch failed', {
url: row.url,
reason: err instanceof Error ? err.message : String(err),
});
return row.icon_file;
}
}