feat/parapharmacy-baby-products #31

Open
Ichitux wants to merge 24 commits from feat/parapharmacy-baby-products into main
2 changed files with 23 additions and 8 deletions
Showing only changes of commit 20debdb023 - Show all commits
+3 -1
View File
@@ -20,7 +20,9 @@ VAPID_SUBJECT=mailto:admin@example.com
# https://expo.dev/accounts/[username]/settings/access-tokens
EXPO_ACCESS_TOKEN=
# Open Food Facts (optional, improves rate limits)
# Open Food Facts
# Register at: https://world.openfoodfacts.org/
# User-Agent is REQUIRED per OFF docs (format: AppName/Version (ContactEmail))
OFF_USER_AGENT=FarmaFinder/1.0 (https://github.com/farmafinder)
OFF_USERNAME=
OFF_PASSWORD=
+20 -7
View File
@@ -1,15 +1,25 @@
import axios from 'axios';
import redisClient from './redis-client.js';
// OFF API v3 (recommended) with fallback to v2 for search
const OFF_API_BASE = 'https://world.openfoodfacts.org';
const CACHE_TTL = 3600;
const MAX_RETRIES = 2;
const RETRY_DELAY_MS = 1000;
// Open Food Facts authentication (required for reliable access)
// User-Agent is REQUIRED per OFF docs: AppName/Version (ContactEmail)
const OFF_USER_AGENT = process.env.OFF_USER_AGENT || 'FarmaFinder/1.0 (https://github.com/farmafinder)';
// Open Food Facts authentication (optional, for write operations)
const OFF_USERNAME = process.env.OFF_USERNAME;
const OFF_PASSWORD = process.env.OFF_PASSWORD;
function getOffHeaders() {
return {
'User-Agent': OFF_USER_AGENT,
};
}
function getOffAuth() {
if (OFF_USERNAME && OFF_PASSWORD) {
return {
@@ -74,18 +84,18 @@ export async function searchBabyProducts(query) {
console.log(`Fetching from OFF API: ${searchTerm}`);
const auth = getOffAuth();
const headers = getOffHeaders();
let lastError;
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
try {
const response = await axios.get(`${OFF_API_BASE}/cgi/search.pl`, {
// Use v2 structured search with brand filter (more reliable than /cgi/search.pl)
const response = await axios.get(`${OFF_API_BASE}/api/v2/search`, {
params: {
search_terms: searchTerm,
json: true,
fields: 'product_name,brands,image_url,nutriscore_grade,ingredients_text,nova_group,ecoscore_grade,categories_tags',
brands_tags: searchTerm,
page_size: 20,
action: 'process',
},
headers,
timeout: 10000,
validateStatus: (status) => status === 200,
...(auth && { auth }),
@@ -150,7 +160,10 @@ export async function getBabyProductDetails(barcode) {
console.log(`Fetching OFF product details: ${barcode}`);
const auth = getOffAuth();
const response = await axios.get(`${OFF_API_BASE}/api/v2/product/${barcode}.json`, {
const headers = getOffHeaders();
// Use v3 API (recommended) for product details
const response = await axios.get(`${OFF_API_BASE}/api/v3/product/${barcode}.json`, {
headers,
timeout: 10000,
validateStatus: (status) => status === 200,
...(auth && { auth }),