feat(backend): add Open Food Facts authentication support
- Add OFF_USERNAME and OFF_PASSWORD env vars - Use Basic Auth when credentials are provided - Improves rate limits for authenticated requests - Auth is optional (works without credentials)
This commit is contained in:
@@ -19,3 +19,8 @@ VAPID_SUBJECT=mailto:admin@example.com
|
|||||||
# Expo Push Notifications (mobile). Get token from:
|
# Expo Push Notifications (mobile). Get token from:
|
||||||
# https://expo.dev/accounts/[username]/settings/access-tokens
|
# https://expo.dev/accounts/[username]/settings/access-tokens
|
||||||
EXPO_ACCESS_TOKEN=
|
EXPO_ACCESS_TOKEN=
|
||||||
|
|
||||||
|
# Open Food Facts (optional, improves rate limits)
|
||||||
|
# Register at: https://world.openfoodfacts.org/
|
||||||
|
OFF_USERNAME=
|
||||||
|
OFF_PASSWORD=
|
||||||
|
|||||||
@@ -4,6 +4,20 @@ import redisClient from './redis-client.js';
|
|||||||
const OFF_API_BASE = 'https://world.openfoodfacts.org';
|
const OFF_API_BASE = 'https://world.openfoodfacts.org';
|
||||||
const CACHE_TTL = 3600;
|
const CACHE_TTL = 3600;
|
||||||
|
|
||||||
|
// Open Food Facts authentication (optional, improves rate limits)
|
||||||
|
const OFF_USERNAME = process.env.OFF_USERNAME;
|
||||||
|
const OFF_PASSWORD = process.env.OFF_PASSWORD;
|
||||||
|
|
||||||
|
function getOffAuth() {
|
||||||
|
if (OFF_USERNAME && OFF_PASSWORD) {
|
||||||
|
return {
|
||||||
|
username: OFF_USERNAME,
|
||||||
|
password: OFF_PASSWORD,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
export function transformOFFProduct(offProduct) {
|
export function transformOFFProduct(offProduct) {
|
||||||
if (!offProduct || !offProduct._id || !offProduct.product_name) {
|
if (!offProduct || !offProduct._id || !offProduct.product_name) {
|
||||||
return null;
|
return null;
|
||||||
@@ -53,6 +67,7 @@ export async function searchBabyProducts(query) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Fetching from OFF API: ${searchTerm}`);
|
console.log(`Fetching from OFF API: ${searchTerm}`);
|
||||||
|
const auth = getOffAuth();
|
||||||
const response = await axios.get(`${OFF_API_BASE}/cgi/search.pl`, {
|
const response = await axios.get(`${OFF_API_BASE}/cgi/search.pl`, {
|
||||||
params: {
|
params: {
|
||||||
search_terms: searchTerm,
|
search_terms: searchTerm,
|
||||||
@@ -63,6 +78,7 @@ export async function searchBabyProducts(query) {
|
|||||||
},
|
},
|
||||||
timeout: 10000,
|
timeout: 10000,
|
||||||
validateStatus: (status) => status === 200,
|
validateStatus: (status) => status === 200,
|
||||||
|
...(auth && { auth }),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check if response is actually JSON (OFF sometimes returns HTML on error)
|
// Check if response is actually JSON (OFF sometimes returns HTML on error)
|
||||||
@@ -108,8 +124,11 @@ export async function getBabyProductDetails(barcode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Fetching OFF product details: ${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 response = await axios.get(`${OFF_API_BASE}/api/v2/product/${barcode}.json`, {
|
||||||
timeout: 10000,
|
timeout: 10000,
|
||||||
|
validateStatus: (status) => status === 200,
|
||||||
|
...(auth && { auth }),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.data && response.data.product) {
|
if (response.data && response.data.product) {
|
||||||
|
|||||||
Reference in New Issue
Block a user