GoldScrape_Api / src /proxy.js
Aurum
Initialize clean codebase without binary files
dcc4f27
Raw
History Blame Contribute Delete
4.1 kB
require('dotenv').config();
const express = require('express');
const axios = require('axios');
const cors = require('cors');
const { connect: connectDB } = require('./db');
const { authMiddleware } = require('./auth');
const { initTelegramBot } = require('./telegramBot');
// Connect to database to validate keys
connectDB();
const app = express();
app.use(cors());
app.use(express.json());
// Configuration (Set these env variables on Render/Vercel)
const PORT = process.env.PORT || 8080;
const HUGGINGFACE_API_URL = process.env.HUGGINGFACE_API_URL || 'https://yogeshjio5770-goldscrape-api.hf.space';
// Use a single API_KEY for both validation and HF authorization
const API_KEY = process.env.API_KEY || 'aurum_live_key_2026_xK9mP3vL7qR2';
if (!process.env.API_KEY) {
process.env.API_KEY = API_KEY;
}
const CACHE_TTL_MS = (parseInt(process.env.PROXY_CACHE_SECONDS) || 5) * 1000; // default 5 seconds cache
// Partitioned memory caches based on rate source
const proxyCaches = {
SLN: { data: null, timestamp: 0, promise: null },
IBJA: { data: null, timestamp: 0, promise: null },
BOTH: { data: null, timestamp: 0, promise: null }
};
// Route: Get cached live rates (requires valid API key from Supabase)
app.get('/api/rates', authMiddleware, async (req, res) => {
const source = req.priceSource || 'SLN';
const cacheKey = (req.isMasterKey || source === 'BOTH') ? 'BOTH' : source;
const cache = proxyCaches[cacheKey] || proxyCaches.SLN;
const now = Date.now();
// If cache is fresh, serve it immediately
if (cache.data && (now - cache.timestamp) < CACHE_TTL_MS) {
return res.json({
...cache.data,
from_proxy_cache: true,
proxy_cache_age_seconds: Math.round((now - cache.timestamp) / 1000)
});
}
// If already fetching from Hugging Face, reuse the active promise to avoid duplicate calls
if (cache.promise) {
try {
const data = await cache.promise;
return res.json({
...data,
from_proxy_cache: true,
proxy_cache_age_seconds: Math.round((Date.now() - cache.timestamp) / 1000)
});
} catch (err) {
// Fallback below if promise fails
}
}
// Fetch fresh data from Hugging Face Space passing original client api key
const clientApiKey = req.headers['x-api-key'] || req.query.apikey || API_KEY;
cache.promise = (async () => {
const response = await axios.get(`${HUGGINGFACE_API_URL}/api/rates`, {
headers: {
'x-api-key': clientApiKey
},
timeout: 8000
});
// Update cache
cache.data = response.data;
cache.timestamp = Date.now();
cache.promise = null;
return response.data;
})();
try {
const data = await cache.promise;
res.json({
...data,
from_proxy_cache: false
});
} catch (error) {
cache.promise = null;
// If Hugging Face is slow/down, serve stale cache as fallback
if (cache.data) {
console.warn('⚠️ Hugging Face API call failed. Serving stale proxy cache.');
return res.json({
...cache.data,
from_proxy_cache: true,
proxy_stale_fallback: true
});
}
res.status(502).json({
success: false,
error: 'Failed to fetch rates from Hugging Face Space backend',
details: error.message
});
}
});
// Health check endpoint
app.get('/health', (req, res) => {
res.json({
status: 'UP',
caches: {
sln: proxyCaches.SLN.data ? 'ACTIVE' : 'EMPTY',
ibja: proxyCaches.IBJA.data ? 'ACTIVE' : 'EMPTY',
both: proxyCaches.BOTH.data ? 'ACTIVE' : 'EMPTY'
}
});
});
// Root endpoint for browser visits
app.get('/', (req, res) => {
res.send('<h1>GoldScrape API Cache Proxy</h1><p>Status: Running</p><p>Use <code>/api/rates?apikey=YOUR_KEY</code> to fetch prices.</p>');
});
app.listen(PORT, () => {
console.log(`🚀 Cache Proxy Server running on port ${PORT}`);
console.log(`🔗 Target Hugging Face URL: ${HUGGINGFACE_API_URL}`);
console.log(`⏱️ Proxy Cache TTL: ${CACHE_TTL_MS / 1000} seconds`);
// Start Telegram bot on Render proxy
initTelegramBot();
});