- wrap root layout with QueryProvider for React Query - replace market UnderConstruction placeholder with MarketClient and real data fetching - implement search, pagination, and sort via search params using getActiveListings - add necessary client components and queries - update dependencies (e.g., prisma, pkg-types)
129 lines
4.7 KiB
TypeScript
129 lines
4.7 KiB
TypeScript
import { NextRequest } from "next/server";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { getItemById } from "@/lib/items-db";
|
|
import { getItemByIdUnified } from "@/lib/db/queries/items";
|
|
import { buildItemImagePath } from "@/lib/item-images";
|
|
|
|
export async function GET(req: NextRequest) {
|
|
const { searchParams } = new URL(req.url);
|
|
const relPath = searchParams.get("path") || "";
|
|
const itemId = searchParams.get("item");
|
|
const itemName = searchParams.get("name"); // Nome do item para validação (opcional)
|
|
|
|
let imagePath: string | null = null;
|
|
|
|
// Se foi fornecido um item ID, buscar o item no banco
|
|
if (itemId) {
|
|
try {
|
|
const itemIdNum = parseInt(itemId, 10);
|
|
if (!isNaN(itemIdNum)) {
|
|
// Tentar primeiro buscar na tabela unificada PostgreSQL
|
|
// Se a tabela não existir, retornará null silenciosamente
|
|
let item: any = null;
|
|
item = await getItemByIdUnified(itemIdNum);
|
|
|
|
// Se encontrou na tabela unificada, validar o nome se fornecido
|
|
if (item && itemName && item.item_name) {
|
|
if (item.item_name.toLowerCase() !== itemName.toLowerCase()) {
|
|
// Nome não bate, ignorar este resultado
|
|
item = null;
|
|
}
|
|
}
|
|
|
|
// Se não encontrou na tabela unificada, tentar SQLite legado
|
|
// Passar itemName para buscar por ID E NOME (evita ambiguidade)
|
|
if (!item) {
|
|
item = getItemById(itemIdNum, itemName || undefined);
|
|
}
|
|
|
|
if (item) {
|
|
// PRIORIDADE: 1. image_local_path (local), 2. image_url (externa), 3. image (fallback)
|
|
const imageLocalPath = item.image_local_path || "";
|
|
const imageUrl = item.image_url || "";
|
|
const image = item.image || "";
|
|
|
|
// Tentar primeiro a imagem local
|
|
if (imageLocalPath) {
|
|
const normalizedPath = imageLocalPath.replace(/\\/g, "/");
|
|
const category = (item.categoria || item.category || "other").toLowerCase();
|
|
|
|
// Se já for um caminho completo (começa com data/), usar diretamente
|
|
if (normalizedPath.startsWith("data/")) {
|
|
imagePath = normalizedPath;
|
|
} else {
|
|
// Construir o caminho usando a função helper
|
|
imagePath = buildItemImagePath(category, normalizedPath);
|
|
}
|
|
|
|
// Verificar se o arquivo existe antes de usar
|
|
if (imagePath) {
|
|
const baseDir = path.join(process.cwd(), "data", "images");
|
|
const resolved = path.join(process.cwd(), imagePath);
|
|
|
|
if (resolved.startsWith(baseDir)) {
|
|
try {
|
|
await fs.promises.access(resolved);
|
|
// Arquivo existe, usar este caminho
|
|
} catch {
|
|
// Arquivo não existe, tentar fallback externo
|
|
imagePath = null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Se não encontrou imagem local ou não existe, usar URL externa como fallback
|
|
if (!imagePath && (imageUrl || image)) {
|
|
const externalUrl = imageUrl || image;
|
|
if (externalUrl.startsWith("http://") || externalUrl.startsWith("https://")) {
|
|
return Response.redirect(externalUrl, 302);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error("[api/images] Erro ao buscar item:", error);
|
|
}
|
|
} else if (relPath) {
|
|
// Se foi fornecido um path, usar diretamente
|
|
imagePath = relPath;
|
|
}
|
|
|
|
if (!imagePath) {
|
|
// Se não foi possível construir o caminho, retornar 404 ao invés de 400
|
|
// para evitar erros visíveis no console do navegador
|
|
return new Response("Not Found", { status: 404 });
|
|
}
|
|
|
|
// Permitir apenas caminhos sob data/images para segurança
|
|
const baseDir = path.join(process.cwd(), "data", "images");
|
|
const resolved = path.join(process.cwd(), imagePath);
|
|
|
|
if (!resolved.startsWith(baseDir)) {
|
|
return new Response("Forbidden", { status: 403 });
|
|
}
|
|
|
|
try {
|
|
const file = await fs.promises.readFile(resolved);
|
|
const ext = path.extname(resolved).toLowerCase();
|
|
const contentType =
|
|
ext === ".gif"
|
|
? "image/gif"
|
|
: ext === ".png"
|
|
? "image/png"
|
|
: ext === ".webp"
|
|
? "image/webp"
|
|
: ext === ".jpg" || ext === ".jpeg"
|
|
? "image/jpeg"
|
|
: "application/octet-stream";
|
|
return new Response(file, { status: 200, headers: { "Content-Type": contentType } });
|
|
} catch (error) {
|
|
console.error("[api/images] Erro ao ler arquivo:", error);
|
|
// Retornar 404 silenciosamente para não gerar erros no console
|
|
return new Response("Not Found", { status: 404 });
|
|
}
|
|
}
|
|
|
|
|