diff --git a/server/index.js b/server/index.js index f17e36d..99353af 100644 --- a/server/index.js +++ b/server/index.js @@ -18,6 +18,7 @@ import settingsRouter from './routes/settings.js' import syncRouter from './routes/sync.js' import projectsRouter from './routes/projects.js' import backupRouter from './routes/backup.js' +import ratesProxyRouter from './routes/rates-proxy.js' const app = express() const PORT = process.env.PORT || 3001 @@ -39,6 +40,7 @@ app.use(express.json({ limit: '50mb' })) app.use('/api/sync', syncRouter) app.use('/api/projects', projectsRouter) app.use('/api/backup', backupRouter) + app.use('/api/rates-proxy', ratesProxyRouter) if (existsSync(distPath)) { app.use(express.static(distPath)) diff --git a/server/routes/rates-proxy.js b/server/routes/rates-proxy.js new file mode 100644 index 0000000..ae800ee --- /dev/null +++ b/server/routes/rates-proxy.js @@ -0,0 +1,22 @@ +import { Router } from 'express' + +const router = Router() + +router.get('/', async (req, res) => { + const url = req.query.url + if (!url) { + return res.status(400).json({ error: 'Missing url parameter' }) + } + try { + const response = await fetch(url, { signal: AbortSignal.timeout(10_000) }) + if (!response.ok) { + return res.status(502).json({ error: `Upstream returned ${response.status}` }) + } + const data = await response.json() + res.json(data) + } catch (err) { + res.status(502).json({ error: err.message || 'Failed to fetch rates' }) + } +}) + +export default router diff --git a/src/App.jsx b/src/App.jsx index 7113135..bfb9c2a 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -56,7 +56,8 @@ function App() { if (!settings?.ratesUrl) { return } - fetch(settings.ratesUrl) + const proxyUrl = `/api/rates-proxy?url=${encodeURIComponent(settings.ratesUrl)}` + fetch(proxyUrl) .then((response) => { if (!response.ok) { throw new Error('Не удалось получить курсы валют') diff --git a/src/components/ConvertedAmount.jsx b/src/components/ConvertedAmount.jsx index f60c3d5..176ae20 100644 --- a/src/components/ConvertedAmount.jsx +++ b/src/components/ConvertedAmount.jsx @@ -7,12 +7,16 @@ function sourceMeta(source) { if (source === 'global') { return { label: 'Глобальный курс', className: 'bg-blue-lt text-blue' } } + if (source === 'no-rates') { + return { label: 'Курсы не загружены', className: 'bg-yellow-lt text-yellow' } + } return { label: 'Без конвертации', className: 'bg-secondary-lt text-secondary' } } export function ConvertedAmount({ amount, currency, provider, settings, ratesData }) { const result = convertWithProviderRate(amount, currency, provider, settings, ratesData) const meta = sourceMeta(result.source) + const showOriginal = result.source !== 'native' && result.source !== 'no-rates' return (