76 lines
2.2 KiB
TypeScript
76 lines
2.2 KiB
TypeScript
import fs from "fs"
|
|
import path from "path"
|
|
import { get } from "./settings"
|
|
|
|
const COVERS_DIR = process.env.COVERS_DIR || path.join(process.cwd(), "data", "covers")
|
|
|
|
function ensureDir() {
|
|
fs.mkdirSync(COVERS_DIR, { recursive: true })
|
|
}
|
|
|
|
function extFromContentType(ct: string | null): string {
|
|
if (!ct) return ".jpg"
|
|
const m = ct.match(/image\/(\w+)/)
|
|
if (!m) return ".jpg"
|
|
const exts: Record<string, string> = {
|
|
jpeg: ".jpg",
|
|
png: ".png",
|
|
webp: ".webp",
|
|
gif: ".gif",
|
|
}
|
|
return exts[m[1]] || ".jpg"
|
|
}
|
|
|
|
// Descarga la portada DIRECTAMENTE del upstream (API_BASE_URL), con la misma
|
|
// API key que usa el resto de la app. NO pasa por el proxy /api/proxy/* (que el
|
|
// middleware protege), evitando el redirect a /login que guardaba HTML como portada.
|
|
export async function cacheCover(gid: string): Promise<string | null> {
|
|
ensureDir()
|
|
try {
|
|
const apiBase = get("API_BASE_URL", "http://127.0.0.1:8080/api/v1")
|
|
const apiKey = get("API_KEY")
|
|
const headers: Record<string, string> = {}
|
|
if (apiKey) headers["X-API-Key"] = apiKey
|
|
|
|
const res = await fetch(`${apiBase}/galleries/${gid}/cover`, {
|
|
headers,
|
|
signal: AbortSignal.timeout(15000),
|
|
})
|
|
if (!res.ok) return null
|
|
|
|
const buffer = Buffer.from(await res.arrayBuffer())
|
|
const ext = extFromContentType(res.headers.get("content-type"))
|
|
const filePath = path.join(COVERS_DIR, `${gid}${ext}`)
|
|
|
|
fs.writeFileSync(filePath, buffer)
|
|
return filePath
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
export function getCoverPath(gid: string): string | null {
|
|
ensureDir()
|
|
const files = fs.readdirSync(COVERS_DIR).filter((f) => f.startsWith(`${gid}.`))
|
|
if (files.length === 0) return null
|
|
return path.join(COVERS_DIR, files[0])
|
|
}
|
|
|
|
export function getCoverContentType(gid: string): string {
|
|
const fp = getCoverPath(gid)
|
|
if (!fp) return "image/jpeg"
|
|
const ext = path.extname(fp).toLowerCase()
|
|
const m: Record<string, string> = {
|
|
".jpg": "image/jpeg",
|
|
".jpeg": "image/jpeg",
|
|
".png": "image/png",
|
|
".webp": "image/webp",
|
|
".gif": "image/gif",
|
|
}
|
|
return m[ext] || "image/jpeg"
|
|
}
|
|
|
|
export function deleteCover(gid: string): void {
|
|
const fp = getCoverPath(gid)
|
|
if (fp) fs.unlinkSync(fp)
|
|
} |