Files
sgse-app/packages/backend/convex/actions/buscarInfoProduto.ts

119 lines
3.1 KiB
TypeScript

import { action } from '../_generated/server';
import { v } from 'convex/values';
interface OpenFoodFactsProduct {
product?: {
product_name?: string;
product_name_pt?: string;
generic_name?: string;
generic_name_pt?: string;
categories?: string;
categories_tags?: string[];
image_url?: string;
image_front_url?: string;
image_front_small_url?: string;
brands?: string;
quantity?: string;
packaging?: string;
};
status?: number;
status_verbose?: string;
}
interface ProductInfo {
nome?: string;
descricao?: string;
categoria?: string;
imagemUrl?: string;
marca?: string;
quantidade?: string;
embalagem?: string;
}
/**
* Busca informações de produto via API externa (Open Food Facts)
* Esta é uma funcionalidade opcional que pode ser usada para preencher
* automaticamente informações de produtos quando disponível.
*/
export const buscarInfoProdutoPorCodigoBarras = action({
args: {
codigoBarras: v.string()
},
handler: async (ctx, args): Promise<ProductInfo | null> => {
const { codigoBarras } = args;
// Validar formato básico de código de barras (EAN-13, UPC, etc.)
if (!codigoBarras || codigoBarras.length < 8 || codigoBarras.length > 14) {
return null;
}
try {
// Tentar buscar na API Open Food Facts (gratuita, sem autenticação)
const response = await fetch(
`https://world.openfoodfacts.org/api/v0/product/${codigoBarras}.json`,
{
method: 'GET',
headers: {
'User-Agent': 'SGSE-App/1.0 (Almoxarifado)'
},
signal: AbortSignal.timeout(5000) // Timeout de 5 segundos
}
);
if (!response.ok) {
return null;
}
const data = (await response.json()) as OpenFoodFactsProduct;
if (data.status !== 1 || !data.product) {
return null;
}
const product = data.product;
// Extrair categoria (primeira categoria disponível)
let categoria: string | undefined;
if (product.categories_tags && product.categories_tags.length > 0) {
// Pegar a primeira categoria e limpar tags
const primeiraCategoria = product.categories_tags[0];
categoria = primeiraCategoria
.replace(/^pt:/, '')
.replace(/^en:/, '')
.replace(/-/g, ' ')
.split(' ')
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
} else if (product.categories) {
categoria = product.categories.split(',')[0].trim();
}
const info: ProductInfo = {
nome: product.product_name_pt || product.product_name || undefined,
descricao: product.generic_name_pt || product.generic_name || undefined,
categoria,
imagemUrl:
product.image_front_url ||
product.image_url ||
product.image_front_small_url ||
undefined,
marca: product.brands || undefined,
quantidade: product.quantity || undefined,
embalagem: product.packaging || undefined
};
// Retornar apenas se tiver pelo menos nome ou descrição
if (info.nome || info.descricao) {
return info;
}
return null;
} catch (error) {
// Log do erro mas não falhar a operação
console.error('Erro ao buscar informações do produto:', error);
return null;
}
}
});