Created Cron job to get data from external api every 15 minutes and save it to local database. Created Weather model and controller to get latest weather status
This commit is contained in:
parent
3fc3d07598
commit
adc4aaed0f
8 changed files with 195 additions and 0 deletions
18
controllers/weather.js
Normal file
18
controllers/weather.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
const asyncWrapper = require('../middleware/asyncWrapper');
|
||||
const ErrorResponse = require('../utils/ErrorResponse');
|
||||
const Weather = require('../models/Weather');
|
||||
|
||||
// @desc Get latest weather status
|
||||
// @route POST /api/weather
|
||||
// @access Public
|
||||
exports.getWeather = asyncWrapper(async (req, res, next) => {
|
||||
const weather = await Weather.findAll({
|
||||
order: [['createdAt', 'DESC']],
|
||||
limit: 1
|
||||
});
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: weather
|
||||
})
|
||||
})
|
15
models/Weather.js
Normal file
15
models/Weather.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
const { DataTypes } = require('sequelize');
|
||||
const { sequelize } = require('../db');
|
||||
|
||||
const Weather = sequelize.define('Weather', {
|
||||
externalLastUpdate: DataTypes.STRING,
|
||||
tempC: DataTypes.FLOAT,
|
||||
tempF: DataTypes.FLOAT,
|
||||
isDay: DataTypes.INTEGER,
|
||||
conditionText: DataTypes.TEXT,
|
||||
conditionCode: DataTypes.INTEGER
|
||||
}, {
|
||||
freezeTableName: true
|
||||
});
|
||||
|
||||
module.exports = Weather;
|
93
package-lock.json
generated
93
package-lock.json
generated
|
@ -276,6 +276,14 @@
|
|||
"integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
|
||||
"optional": true
|
||||
},
|
||||
"axios": {
|
||||
"version": "0.21.1",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
|
||||
"integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.10.0"
|
||||
}
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
|
@ -393,6 +401,15 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"call-bind": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
|
||||
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
|
||||
"requires": {
|
||||
"function-bind": "^1.1.1",
|
||||
"get-intrinsic": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"camelcase": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
|
||||
|
@ -633,6 +650,15 @@
|
|||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
|
||||
},
|
||||
"cron-parser": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-3.5.0.tgz",
|
||||
"integrity": "sha512-wyVZtbRs6qDfFd8ap457w3XVntdvqcwBGxBoTvJQH9KGVKL/fB+h2k3C8AqiVxvUQKN1Ps/Ns46CNViOpVDhfQ==",
|
||||
"requires": {
|
||||
"is-nan": "^1.3.2",
|
||||
"luxon": "^1.26.0"
|
||||
}
|
||||
},
|
||||
"crypto-random-string": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
|
||||
|
@ -681,6 +707,14 @@
|
|||
"integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==",
|
||||
"dev": true
|
||||
},
|
||||
"define-properties": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
|
||||
"integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
|
||||
"requires": {
|
||||
"object-keys": "^1.0.12"
|
||||
}
|
||||
},
|
||||
"delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
|
@ -885,6 +919,11 @@
|
|||
"unpipe": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz",
|
||||
"integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg=="
|
||||
},
|
||||
"forever-agent": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
|
||||
|
@ -1002,6 +1041,16 @@
|
|||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
|
||||
},
|
||||
"get-intrinsic": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
|
||||
"integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
|
||||
"requires": {
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
"has-symbols": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"get-stream": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
|
||||
|
@ -1104,6 +1153,11 @@
|
|||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
|
||||
},
|
||||
"has-symbols": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
|
||||
"integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw=="
|
||||
},
|
||||
"has-unicode": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
|
||||
|
@ -1273,6 +1327,15 @@
|
|||
"is-path-inside": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"is-nan": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz",
|
||||
"integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==",
|
||||
"requires": {
|
||||
"call-bind": "^1.0.0",
|
||||
"define-properties": "^1.1.3"
|
||||
}
|
||||
},
|
||||
"is-npm": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz",
|
||||
|
@ -1405,6 +1468,11 @@
|
|||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
},
|
||||
"long-timeout": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/long-timeout/-/long-timeout-0.1.1.tgz",
|
||||
"integrity": "sha1-lyHXiLR+C8taJMLivuGg2lXatRQ="
|
||||
},
|
||||
"lowercase-keys": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
|
||||
|
@ -1419,6 +1487,11 @@
|
|||
"yallist": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"luxon": {
|
||||
"version": "1.27.0",
|
||||
"resolved": "https://registry.npmjs.org/luxon/-/luxon-1.27.0.tgz",
|
||||
"integrity": "sha512-VKsFsPggTA0DvnxtJdiExAucKdAnwbCCNlMM5ENvHlxubqWd0xhZcdb4XgZ7QFNhaRhilXCFxHuoObP5BNA4PA=="
|
||||
},
|
||||
"make-dir": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
|
||||
|
@ -1657,6 +1730,16 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node-schedule": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/node-schedule/-/node-schedule-2.0.0.tgz",
|
||||
"integrity": "sha512-cHc9KEcfiuXxYDU+HjsBVo2FkWL1jRAUoczFoMIzRBpOA4p/NRHuuLs85AWOLgKsHtSPjN8csvwIxc2SqMv+CQ==",
|
||||
"requires": {
|
||||
"cron-parser": "^3.1.0",
|
||||
"long-timeout": "0.1.1",
|
||||
"sorted-array-functions": "^1.3.0"
|
||||
}
|
||||
},
|
||||
"nodemon": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.7.tgz",
|
||||
|
@ -1774,6 +1857,11 @@
|
|||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
|
||||
},
|
||||
"object-keys": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
|
||||
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
|
||||
},
|
||||
"on-finished": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
|
||||
|
@ -2253,6 +2341,11 @@
|
|||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
|
||||
"integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA=="
|
||||
},
|
||||
"sorted-array-functions": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz",
|
||||
"integrity": "sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA=="
|
||||
},
|
||||
"spawn-command": {
|
||||
"version": "0.0.2-1",
|
||||
"resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz",
|
||||
|
|
|
@ -13,10 +13,12 @@
|
|||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@types/express": "^4.17.11",
|
||||
"axios": "^0.21.1",
|
||||
"colors": "^1.4.0",
|
||||
"concurrently": "^6.0.2",
|
||||
"dotenv": "^9.0.0",
|
||||
"express": "^4.17.1",
|
||||
"node-schedule": "^2.0.0",
|
||||
"sequelize": "^6.6.2",
|
||||
"sqlite3": "^5.0.2"
|
||||
},
|
||||
|
|
12
routes/weather.js
Normal file
12
routes/weather.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
const express = require('express');
|
||||
const router = express.Router();
|
||||
|
||||
const {
|
||||
getWeather
|
||||
} = require('../controllers/weather');
|
||||
|
||||
router
|
||||
.route('')
|
||||
.get(getWeather);
|
||||
|
||||
module.exports = router;
|
|
@ -1,6 +1,7 @@
|
|||
const express = require('express');
|
||||
const { connectDB } = require('./db');
|
||||
const errorHandler = require('./middleware/errorHandler');
|
||||
const jobs = require('./utils/jobs');
|
||||
const colors = require('colors');
|
||||
require('dotenv').config();
|
||||
|
||||
|
@ -19,6 +20,7 @@ app.use(express.json());
|
|||
// Link controllers with routes
|
||||
app.use('/api/apps', require('./routes/apps'));
|
||||
app.use('/api/config', require('./routes/config'));
|
||||
app.use('/api/weather', require('./routes/weather'));
|
||||
|
||||
// Custom error handler
|
||||
app.use(errorHandler);
|
||||
|
|
42
utils/getExternalWeather.js
Normal file
42
utils/getExternalWeather.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
const Config = require('../models/Config');
|
||||
const Weather = require('../models/Weather');
|
||||
const axios = require('axios');
|
||||
|
||||
const getExternalWeather = async () => {
|
||||
// Get API key from database
|
||||
let secret = await Config.findOne({
|
||||
where: { key: 'WEATHER_API_KEY' }
|
||||
});
|
||||
|
||||
if (!secret) {
|
||||
console.log('API key was not found');
|
||||
return;
|
||||
}
|
||||
|
||||
secret = secret.value;
|
||||
|
||||
// Fetch data from external API
|
||||
try {
|
||||
const res = await axios.get(`http://api.weatherapi.com/v1/current.json?key=${secret}&q=52.229676,21.012229`);
|
||||
|
||||
// For dev
|
||||
// console.log(res.data);
|
||||
|
||||
// Save weather data
|
||||
const cursor = res.data.current;
|
||||
await Weather.create({
|
||||
externalLastUpdate: cursor.last_updated,
|
||||
tempC: cursor.temp_c,
|
||||
tempF: cursor.temp_f,
|
||||
isDay: cursor.is_day,
|
||||
conditionText: cursor.condition.text,
|
||||
conditionCode: cursor.condition.code
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
console.log('External API request failed');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = getExternalWeather;
|
11
utils/jobs.js
Normal file
11
utils/jobs.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
const schedule = require('node-schedule');
|
||||
const getExternalWeather = require('./getExternalWeather');
|
||||
|
||||
const weatherJob = schedule.scheduleJob('updateWeather', '0 */15 * * * *', async () => {
|
||||
try {
|
||||
await getExternalWeather();
|
||||
console.log('weather updated');
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
})
|
Loading…
Reference in a new issue