const crypto = require("crypto") const axios = require("axios") const { getRedisClient } = require("./client") const IMAGE_KEY_PREFIX = "cache:deal_create:image:" const DEFAULT_TTL_SECONDS = 5 * 60 const DEFAULT_MAX_IMAGE_BYTES = 8 * 1024 * 1024 function getMaxImageBytes() { const raw = Number(process.env.LINK_PREVIEW_MAX_IMAGE_BYTES) if (!Number.isFinite(raw) || raw < 256 * 1024) return DEFAULT_MAX_IMAGE_BYTES return Math.floor(raw) } function createRedisClient() { return getRedisClient() } function normalizeUrl(rawUrl) { if (!rawUrl || typeof rawUrl !== "string") return null try { const url = new URL(rawUrl) if (!["http:", "https:"].includes(url.protocol)) return null const hostname = url.hostname.toLowerCase() const pathname = url.pathname || "/" const search = url.search || "" return `${url.protocol}//${hostname}${pathname}${search}` } catch { return null } } function buildKey(normalizedUrl) { const hash = crypto.createHash("sha1").update(normalizedUrl).digest("hex") return `${IMAGE_KEY_PREFIX}${hash}` } async function cacheImageFromUrl(rawUrl, { ttlSeconds = DEFAULT_TTL_SECONDS } = {}) { const normalized = normalizeUrl(rawUrl) if (!normalized) return null const maxImageBytes = getMaxImageBytes() const key = buildKey(normalized) const redis = createRedisClient() try { const cached = await redis.get(key) if (cached) return { key: key.replace(IMAGE_KEY_PREFIX, ""), cached: true } const response = await axios.get(normalized, { responseType: "arraybuffer", timeout: 15000, maxContentLength: maxImageBytes, maxBodyLength: maxImageBytes, validateStatus: (status) => status >= 200 && status < 300, }) const contentType = String(response.headers?.["content-type"] || "") if (!contentType.startsWith("image/")) return null const buffer = Buffer.from(response.data || []) if (!buffer.length || buffer.length > maxImageBytes) return null const payload = JSON.stringify({ ct: contentType, b64: buffer.toString("base64"), }) await redis.set(key, payload, "EX", Number(ttlSeconds) || DEFAULT_TTL_SECONDS) return { key: key.replace(IMAGE_KEY_PREFIX, ""), cached: false } } catch { return null } finally {} } async function getCachedImageByKey(hashKey) { if (!hashKey || typeof hashKey !== "string") return null const redis = createRedisClient() const key = `${IMAGE_KEY_PREFIX}${hashKey}` try { const raw = await redis.get(key) if (!raw) return null const parsed = JSON.parse(raw) if (!parsed?.ct || !parsed?.b64) return null return { contentType: parsed.ct, buffer: Buffer.from(parsed.b64, "base64") } } catch { return null } finally {} } module.exports = { cacheImageFromUrl, getCachedImageByKey, }