diff --git a/routes/api.js b/routes/api.js new file mode 100644 index 0000000..4974c02 --- /dev/null +++ b/routes/api.js @@ -0,0 +1,63 @@ +const express = require("express"); +const axios = require("axios"); +const packageJson = require("../package.json"); +const httpStatusCodes = require("../utils/httpStatusCodes"); + +const router = express.Router(); + +const priceCache = {}; +const CACHE_DURATION = 60 * 1000; + +async function fetchPrices() { + try { + const response = await axios.get( + "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin,ethereum&vs_currencies=usd,eur", + ); + return response.data; + } catch (error) { + console.error("Error fetching prices:", error.message); + throw error; + } +} + +// Prices endpoint +router.get("/prices", async (req, res) => { + const now = Date.now(); + + if (priceCache.data && now - priceCache.timestamp < CACHE_DURATION) { + return res.json({ + ...priceCache.data, + cached: true, + timestamp: new Date(priceCache.timestamp).toISOString(), + }); + } + + try { + const prices = await fetchPrices(); + priceCache.data = prices; + priceCache.timestamp = now; + + res.json({ + ...prices, + cached: false, + timestamp: new Date(now).toISOString(), + }); + } catch (error) { + res.status(httpStatusCodes.INTERNAL_SERVER_ERROR).json({ + error: "Failed to fetch prices", + message: error.message, + }); + } +}); + +// Test endpoint +router.get("/test", (req, res) => { + res.json({ message: "Test endpoint working" }); +}); + +// Version endpoint +router.get("/version", (req, res) => { + res.json({ version: packageJson.version }); +}); + +module.exports = router; diff --git a/server.js b/server.js index 2595754..e19eb29 100644 --- a/server.js +++ b/server.js @@ -1,9 +1,8 @@ -const express = require('express'); -const rateLimit = require('express-rate-limit'); -const axios = require('axios'); -const packageJson = require('./package.json'); +const express = require("express"); +const rateLimit = require("express-rate-limit"); +require("dotenv").config(); -require('dotenv').config(); +const apiRoutes = require("./routes/api"); function logWithTime(message) { console.log(`[${new Date().toISOString()}] ${message}`); @@ -16,71 +15,24 @@ const limiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 100, message: { - error: 'Too many requests from this IP, please try again after 15 minutes.' + error: "Too many requests from this IP, please try again after 15 minutes.", }, standardHeaders: true, legacyHeaders: false, }); -const priceCache = {}; -const CACHE_DURATION = 60 * 1000; app.use(express.json()); -app.get('/health', (req, res) => { +// Health route stays in server (bootstrap concern) +app.get("/health", (req, res) => { res.json({ - status: 'OK', - timestamp: new Date().toISOString() + status: "OK", + timestamp: new Date().toISOString(), }); }); -app.use('/api/v1/', limiter); - -async function fetchPrices() { - try { - const response = await axios.get('https://api.coingecko.com/api/v3/simple/price?ids=bitcoin,ethereum&vs_currencies=usd,eur'); - return response.data; - } catch (error) { - console.error('Error fetching prices:', error.message); - throw error; - } -} - -app.get('/api/v1/prices', async (req, res) => { - const now = Date.now(); - - if (priceCache.data && (now - priceCache.timestamp) < CACHE_DURATION) { - return res.json({ - ...priceCache.data, - cached: true, - timestamp: new Date(priceCache.timestamp).toISOString() - }); - } - - try { - const prices = await fetchPrices(); - priceCache.data = prices; - priceCache.timestamp = now; - - res.json({ - ...prices, - cached: false, - timestamp: new Date(now).toISOString() - }); - } catch (error) { - res.status(httpStatusCodes.INTERNAL_SERVER_ERROR).json({ - error: 'Failed to fetch prices', - message: error.message - }); - } -}); - -app.get('/api/v1/test', (req, res) => { - res.json({ message: 'Test endpoint working' }); -}); - -app.get('/api/v1/version', (req, res) => { - res.json({ version: packageJson.version }); -}); +// Apply rate limit to API +app.use("/api/v1", limiter, apiRoutes); app.listen(PORT, () => { logWithTime(`Server running on port ${PORT}`);