1 line
7.2 KiB
JavaScript
1 line
7.2 KiB
JavaScript
require("dotenv").config();const express=require("express"),mongoose=require("mongoose"),cors=require("cors"),cron=require("node-cron"),rateLimit=require("express-rate-limit"),app=express(),PORT=3e3,HOST="0.0.0.0";app.use(express.json()),app.use(cors());const limiter=rateLimit({windowMs:6e4,max:20});app.use(limiter),mongoose.connect(`mongodb://${HOST}/price-history`);const db=mongoose.connection;db.on("error",console.error.bind(console,"connection error:")),db.once("open",(()=>{console.log("Connected to MongoDB")})),app.get("/",((req,res)=>{res.send('\n <title>RanchiMall Price History API</title>\n <meta name="viewport" content="width=device-width, initial-scale=1.0">\n <style>\n body {\n font-family: sans-serif;\n }\n h1{\n font-size: 2rem;\n margin-bottom: 2rem;\n }\n a{\n color: inherit;\n }\n table {\n border-collapse: collapse;\n }\n table, th, td {\n border: 1px solid black;\n padding: 0.5rem;\n }\n code {\n display: inline-block;\n background-color: #eee;\n padding: 0.3rem;\n border-radius: 0.2rem;\n font: monospace;\n }\n @media (prefers-color-scheme: dark) {\n body {\n background-color: #222;\n color: #eee;\n }\n table, th, td {\n border-color: #eee;\n }\n code {\n background-color: #333;\n color: #eee;\n }\n }\n\n </style>\n <section style="padding:4vw;">\n <h1>\n Welcome to the RanchiMall Price History API!\n </h1>\n <h3>\n Available endpoints:\n </h3>\n <ul>\n <li>\n <a href="/price-history">/price-history</a>\n </li>\n </ul>\n <h3>\n Query parameters:\n </h3>\n <table>\n <thead>\n <tr>\n <th>Parameter</th>\n <th>Required</th>\n <th>Default</th>\n <th>format | values</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td>from</td>\n <td>No</td>\n <td>None</td>\n <td>YYYY-MM-DD</td>\n </tr>\n <tr>\n <td>to</td>\n <td>No</td>\n <td>None</td>\n <td>YYYY-MM-DD</td>\n </tr>\n <tr>\n <td>on</td>\n <td>No</td>\n <td>None</td>\n <td>YYYY-MM-DD</td>\n </tr>\n <tr>\n <td>limit</td>\n <td>No</td>\n <td>100</td>\n <td>all | <number></td>\n </tr>\n <tr>\n <td>asset</td>\n <td>No</td>\n <td>btc</td>\n <td>btc</td>\n </tr>\n <tr>\n <td>currency</td>\n <td>No</td>\n <td>All</td>\n <td>usd | inr</td>\n </tr>\n </tbody>\n </table>\n <h3>\n Example:\n </h3>\n <ul>\n <li>\n <code>\n /price-history?from=2020-01-01&to=2020-01-31\n </code>\n </li>\n </ul>\n </section>\n ')}));const PriceHistory=require("./models/price-history");function loadHistoricToDb(){const now=parseInt(Date.now()/1e3);Promise.all([fetch(`https://query1.finance.yahoo.com/v7/finance/download/BTC-USD?period1=1410912000&period2=${now}&interval=1d&events=history&includeAdjustedClose=true`).then((res=>res.text())),fetch(`https://query1.finance.yahoo.com/v7/finance/download/BTC-INR?period1=1410912000&period2=${now}&interval=1d&events=history&includeAdjustedClose=true`).then((res=>res.text()))]).then((async([usd,inr])=>{const usdData=usd.split("\n").slice(1),inrData=inr.split("\n").slice(1),priceHistoryData=[];for(let i=0;i<usdData.length;i++){const[date,open,high,low,close,adjClose,volume]=usdData[i].split(","),[date2,open2,high2,low2,close2,adjClose2,volume2]=inrData[i].split(",");priceHistoryData.push({date:new Date(date).getTime(),asset:"btc",usd:parseFloat(parseFloat(close).toFixed(2)),inr:parseFloat(parseFloat(close2).toFixed(2))})}await PriceHistory.insertMany(priceHistoryData)})).catch((err=>{console.log(err)}))}PriceHistory.countDocuments().then((count=>{0===count&&loadHistoricToDb()})).catch((err=>{console.log(err)})),app.get("/price-history",(async(req,res)=>{try{const{from:from,to:to,on:on,limit:limit=100,asset:asset="btc",currency:currency}=req.query,searchParams={asset:asset};from&&(searchParams.date={$gte:new Date(from).getTime()}),to&&(searchParams.date={...searchParams.date,$lte:new Date(to).getTime()}),on&&(searchParams.date={$eq:new Date(on).getTime()}),currency&&(searchParams[currency]={$exists:!0});const dataFormat={_id:0,__v:0,asset:0};"inr"===currency&&(dataFormat.usd=0),"usd"===currency&&(dataFormat.inr=0);const priceHistory=await PriceHistory.find(searchParams,dataFormat).sort({date:-1}).limit("all"===limit?0:parseInt(limit));res.json(priceHistory)}catch(err){console.log(err),res.status(500).json({error:err})}})),app.post("/price-history",(async(req,res)=>{try{const{dates:dates}=req.body;if(!dates)return res.status(400).json({error:"dates is required"});if(!Array.isArray(dates))return res.status(400).json({error:"dates must be an array"});const priceHistory=await PriceHistory.find({date:{$in:dates}},{_id:0,__v:0,asset:0});res.json(priceHistory)}catch(err){console.log(err),res.status(500).json({error:err})}})),app.listen(3e3,HOST,(()=>{console.log(`Listening on ${HOST}:3000`)})),cron.schedule("0 12 * * *",(async()=>{try{const[usd,inr]=await Promise.all([fetch("https://query1.finance.yahoo.com/v7/finance/download/BTC-USD").then((res=>res.text())),fetch("https://query1.finance.yahoo.com/v7/finance/download/BTC-INR").then((res=>res.text()))]),usdData=usd.split("\n").slice(1),inrData=inr.split("\n").slice(1);for(let i=0;i<usdData.length;i++){const[date,open,high,low,close,adjClose,volume]=usdData[i].split(","),[date2,open2,high2,low2,close2,adjClose2,volume2]=inrData[i].split(","),priceHistoryData={date:new Date(date).getTime(),asset:"btc",usd:parseFloat(parseFloat(close).toFixed(2)),inr:parseFloat(parseFloat(close2).toFixed(2))};await PriceHistory.findOneAndUpdate({date:priceHistoryData.date,asset:priceHistoryData.asset},priceHistoryData,{upsert:!0})}}catch(err){console.log(err)}})); |