diff --git a/apps/stock-price-checker-proxy/api/v1.js b/apps/stock-price-checker-proxy/api/v1.js index 6ff3ade30..461e033ce 100644 --- a/apps/stock-price-checker-proxy/api/v1.js +++ b/apps/stock-price-checker-proxy/api/v1.js @@ -12,7 +12,7 @@ const db = new Datastore({ // cleaning cache data on app restart db.remove( - { $or: [{ data: {} }, { data: "Unknown symbol" }] }, + { $or: [{ stockData: {} }, { stockData: "Unknown symbol" }] }, { multi: true }, (err, count) => { console.log("\nremoving garbage from cache..."); @@ -31,10 +31,12 @@ const getUID = (n = 8, symbols = _symbols) => .map(() => symbols[Math.floor(Math.random() * symbols.length)]) .join(""); -const { IEX_API_KEY = "", CACHE_TTL_MINUTES = 10 } = process.env; +const { ALPHA_VANTAGE_API_KEY = "", CACHE_TTL_MINUTES = 10 } = process.env; const validTickerRegExp = /^[a-z]{1,6}$/; -const isValidStock = stock => validTickerRegExp.test(stock); +const isValidStock = (stock) => validTickerRegExp.test(stock); +const parseFloatAndRound = (value, digits) => + Number(parseFloat(value).toFixed(digits)); router.use(cors()); @@ -60,30 +62,78 @@ router.get("/stock/:stock/quote", (req, res, next) => { if (err) return next(err); if (cached) { console.log(`rid: ${req_id} ** ${stock} from cache **`); - return res.json(cached.data); + return res.json(cached.stockData); } try { const { data } = await axios.get( - `https://cloud.iexapis.com/stable/stock/${stock}/quote?token=${IEX_API_KEY}` + `https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=${stock}&apikey=${ALPHA_VANTAGE_API_KEY}` ); console.log(`rid: ${req_id} !! ${stock} from api !!`); - res.json(data); + const temp = { ...data?.["Global Quote"] }; + let stockData; + if (Object.keys(temp).length === 0 && temp.constructor === Object) { + stockData = "Unknown symbol"; // Mimic IEX API response for this case + } else { + const symbol = temp["01. symbol"]; + const open = parseFloatAndRound(temp["02. open"], 2); + const high = parseFloatAndRound(temp["03. high"], 2); + const low = parseFloatAndRound(temp["04. low"], 2); + const close = parseFloatAndRound(temp["05. price"], 2); + const volume = Number(parseFloatAndRound(temp["06. volume"]), 2); + const latestTime = new Date( + temp["07. latest trading day"] + ).toLocaleString("en-US", { + month: "long", + day: "numeric", + year: "numeric" + }); + const previousClose = parseFloatAndRound( + temp["08. previous close"], + 2 + ); + const change = parseFloatAndRound(temp["09. change"], 2); + + // Transform the response to match the IEX's as closely as possible + // with the available data + stockData = { + change, + changePercent: parseFloatAndRound( + (close - previousClose) / previousClose, + 5 + ), + close, + high, + latestPrice: close, + latestTime, + latestVolume: volume, + low, + open, + previousClose, + symbol, + volume + }; + } + res.json(stockData); db.update( { _id: stock }, - { _id: stock, data, updatedAt: Date.now() }, + { _id: stock, stockData, updatedAt: Date.now() }, { upsert: true }, () => console.log(`rid: ${req_id} ++ ${stock} stored ++`) ); } catch (e) { if (e.response) { - res.status(e.response.status).json(e.response.data); + res.status(e.response.status).json(e.response.stockData); db.update( { _id: stock }, - { _id: stock, data: e.response.data, updatedAt: Date.now() }, + { + _id: stock, + stockData: e.response.stockData, + updatedAt: Date.now() + }, { upsert: true } ); } else { diff --git a/apps/stock-price-checker-proxy/sample.env b/apps/stock-price-checker-proxy/sample.env index 7249a1175..3bb5a1cc5 100644 --- a/apps/stock-price-checker-proxy/sample.env +++ b/apps/stock-price-checker-proxy/sample.env @@ -1,3 +1,3 @@ PORT=3000 CACHE_TTL_MINUTES= -IEX_API_KEY= +ALPHA_VANTAGE_API_KEY= diff --git a/apps/stock-price-checker-proxy/server.js b/apps/stock-price-checker-proxy/server.js index 28ace7bfa..52af8b557 100644 --- a/apps/stock-price-checker-proxy/server.js +++ b/apps/stock-price-checker-proxy/server.js @@ -1,4 +1,4 @@ -require('dotenv').config(); +require("dotenv").config(); const express = require("express"); const app = express(); @@ -12,7 +12,8 @@ app.get("/", (req, res) => { res.sendFile(__dirname + "/views/index.html"); }); -app.use((err, req, res) => { +// eslint-disable-next-line no-unused-vars +app.use((err, req, res, next) => { if (err) { console.log(err.message, err.stack); res.status(500).json({ status: "internal server error" }); diff --git a/docker-compose.yml b/docker-compose.yml index 26856ef5b..e32b46888 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,7 @@ networks: proxy: + services: caddy: @@ -12,8 +13,8 @@ services: volumes: - ./Caddyfile:/etc/caddy/Caddyfile ports: - - 80:80 - - 443:443 + - 80:80 + - 443:443 mongo: image: mongo @@ -373,7 +374,7 @@ services: dockerfile: ./Dockerfile environment: - CACHE_TTL_MINUTES=${STOCK_PRICE_CHECKER_PROXY_CACHE_TTL_MINUTES} - - IEX_API_KEY=${STOCK_PRICE_CHECKER_PROXY_IEX_API_KEY} + - ALPHA_VANTAGE_API_KEY=${STOCK_PRICE_CHECKER_PROXY_ALPHA_VANTAGE_API_KEY} ports: - 50130:3000 diff --git a/sample.env b/sample.env index c1090e902..fd32b1466 100644 --- a/sample.env +++ b/sample.env @@ -42,7 +42,7 @@ STOCK_PRICE_CHECKER_DB_URI=mongodb://mongo:27017/stock-price-checker # Stock Price Checker Proxy STOCK_PRICE_CHECKER_PROXY_CACHE_TTL_MINUTES=30 -STOCK_PRICE_CHECKER_PROXY_IEX_API_KEY=api_key_from_iex_dashboard +STOCK_PRICE_CHECKER_PROXY_ALPHA_VANTAGE_API_KEY=api_key_from_alpha_vantage # Twitch Proxy TWITCH_PROXY_TWITCH_CLIENT_ID=client_id_from_twitch_dashboard