- JsonCacheManager: a leitura inicial concorrente carregava o arquivo duas vezes e a segunda descartava as alterações da primeira; gravações simultâneas escreviam no mesmo arquivo. Agora a leitura é compartilhada e a gravação é enfileirada, via arquivo temporário + rename. - POST /api/cache/sync: aceitava qualquer objeto e usava code como chave do cache (inclusive ausente ou não numérico); agora só grava produtos com code numérico e conta os demais em failed. JSON malformado responde 400 em vez de 500. - GET /api/products/[barcode]: resposta da API sem product virava 200 com corpo vazio; agora responde 404. - Chamadas ao Open Food Facts (busca e produto) passam a ter timeout de 15 s e o User-Agent vem de um único lugar (lib/offSearch).
47 lines
1.5 KiB
TypeScript
47 lines
1.5 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server";
|
|
import { jsonCacheManager } from "@/lib/jsonCacheManager";
|
|
import type { Product } from "@/lib/openFoodFactsApi";
|
|
|
|
const INVALID_PRODUCTS = { error: "Produtos inválidos" };
|
|
|
|
/** Aceita só objetos com `code` numérico (texto de dígitos): a chave do cache não pode ser arbitrária. */
|
|
function isSyncableProduct(value: unknown): value is Product {
|
|
if (typeof value !== "object" || value === null) return false;
|
|
const code = (value as { code?: unknown }).code;
|
|
return typeof code === "string" && /^\d{1,32}$/.test(code);
|
|
}
|
|
|
|
export async function POST(request: NextRequest) {
|
|
let body: { products?: unknown };
|
|
try {
|
|
body = await request.json();
|
|
} catch {
|
|
return NextResponse.json(INVALID_PRODUCTS, { status: 400 });
|
|
}
|
|
|
|
try {
|
|
const products = body?.products;
|
|
if (!Array.isArray(products)) {
|
|
return NextResponse.json(INVALID_PRODUCTS, { status: 400 });
|
|
}
|
|
|
|
// Salvar produtos válidos no cache; os demais contam como falha
|
|
const valid = products.filter(isSyncableProduct);
|
|
const result = await jsonCacheManager.saveProducts(valid);
|
|
|
|
console.log(`[Cache Sync] ${result.saved} novos produtos salvos`);
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
saved: result.saved,
|
|
failed: result.failed + (products.length - valid.length),
|
|
});
|
|
} catch (error) {
|
|
console.error("Erro ao sincronizar cache:", error);
|
|
return NextResponse.json(
|
|
{ error: "Erro ao sincronizar cache" },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|