diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..75e90ca --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +FROM node:14-alpine + +WORKDIR /app + +COPY package*.json . + +RUN npm install --only=production + +COPY . . + +RUN mkdir -p ./public ./data +RUN mv ./client/build/* ./public +RUN rm -rf ./client + +EXPOSE 5005 + +CMD ["node", "server.js"] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..2a4cc81 --- /dev/null +++ b/README.md @@ -0,0 +1,42 @@ +# Flame + +## Description +Flame is self-hosted startpage for your server. It's inspired (heavily) by [SUI](https://github.com/jeroenpardon/sui) + +## Technology +- Backend + - Node.js + Express + - Sequelize ORM + SQLite +- Frontend + - React + - Redux + - TypeScript +- Deployment + - Docker + +## Development +```sh +git clone https://github.com/pawelmalak/flame +cd flame + +# run only once + + +# start backend and frontend development servers +npm run dev +``` + +## Deployment with Docker +```sh +# build image +docker build -t flame . + +# run container +docker run \ + -p 5005:5005 \ + -v :/app/data \ + flame +``` + +## Functionality +todo \ No newline at end of file diff --git a/api.js b/api.js index 6068aaf..6ea1ab1 100644 --- a/api.js +++ b/api.js @@ -1,10 +1,13 @@ +const path = require('path'); const express = require('express'); const errorHandler = require('./middleware/errorHandler'); const api = express(); -api.get('/', (req, res) => { - res.send('Server is working'); +// Static files +api.use(express.static(path.join(__dirname, 'public'))); +api.get(/^\/(?!api)/, (req, res) => { + res.sendFile(path.join(__dirname, 'public/index.html')); }) // Body parser diff --git a/db.js b/db.js index 9d1420e..b357dca 100644 --- a/db.js +++ b/db.js @@ -9,13 +9,13 @@ const sequelize = new Sequelize({ const connectDB = async () => { try { await sequelize.authenticate({ logging: false }); - console.log('Connected to database'.cyan.underline); + console.log('Connected to database'); await sequelize.sync({ // alter: true, logging: false }); - console.log('All models were synced'.cyan.underline); + console.log('All models were synced'); } catch (error) { console.error('Unable to connect to the database:', error); } diff --git a/middleware/errorHandler.js b/middleware/errorHandler.js index 582dace..2de45a7 100644 --- a/middleware/errorHandler.js +++ b/middleware/errorHandler.js @@ -11,7 +11,7 @@ const errorHandler = (err, req, res, next) => { // } console.log(error); - console.log(`${err}`.bgRed); + console.log(`${err}`); res.status(err.statusCode || 500).json({ success: false, diff --git a/package.json b/package.json index 85d6a43..0cf2a92 100644 --- a/package.json +++ b/package.json @@ -5,10 +5,13 @@ "main": "index.js", "scripts": { "start": "node server.js", - "server": "nodemon server.js", - "client": "npm start --prefix client", - "dev": "concurrently \"npm run server\" \"npm run client\"", - "dev-lines": "git ls-files | grep -v '.json' | xargs wc -l" + "init-server": "echo Instaling server dependencies && npm install", + "init-client": "cd client && echo Instaling client dependencies && npm install", + "dev-init": "npm run init-server && npm run init-client", + "dev-server": "nodemon server.js", + "dev-client": "npm start --prefix client", + "dev": "concurrently \"npm run dev-server\" \"npm run dev-client\"", + "build": "docker build -t flame ." }, "author": "", "license": "ISC", diff --git a/server.js b/server.js index 6cd01d6..569b7ac 100644 --- a/server.js +++ b/server.js @@ -1,3 +1,4 @@ +require('dotenv').config(); const http = require('http'); const { connectDB } = require('./db'); const api = require('./api'); @@ -5,12 +6,15 @@ const jobs = require('./utils/jobs'); const Socket = require('./Socket'); const Sockets = require('./Sockets'); const associateModels = require('./models/associateModels'); +const initConfig = require('./utils/initConfig'); -require('dotenv').config(); const PORT = process.env.PORT || 5005; -connectDB(); -associateModels(); +connectDB() + .then(() => { + associateModels(); + initConfig(); + }); // Create server for Express API and WebSockets const server = http.createServer(); @@ -21,5 +25,5 @@ const weatherSocket = new Socket(server); Sockets.registerSocket('weather', weatherSocket); server.listen(PORT, () => { - console.log(`Server is running on port ${PORT} in ${process.env.NODE_ENV} mode`.yellow.bold); + console.log(`Server is running on port ${PORT} in ${process.env.NODE_ENV} mode`); }) \ No newline at end of file diff --git a/utils/initConfig.js b/utils/initConfig.js new file mode 100644 index 0000000..02f20ec --- /dev/null +++ b/utils/initConfig.js @@ -0,0 +1,36 @@ +const { Op } = require('sequelize'); +const Config = require('../models/Config'); + +const initConfig = async () => { + // Config keys + const keys = ['WEATHER_API_KEY', 'lat', 'long', 'isCelsius']; + const values = ['', 0, 0, true]; + + // Get config values + const configPairs = await Config.findAll({ + where: { + key: { + [Op.or]: keys + } + } + }) + + // Get key from each pair + const configKeys = configPairs.map((pair) => pair.key); + + // Create missing pairs + keys.forEach(async (key, idx) => { + if (!configKeys.includes(key)) { + await Config.create({ + key, + value: values[idx], + valueType: typeof values[idx] + }) + } + }) + + console.log('Initial config created'); + return; +} + +module.exports = initConfig; \ No newline at end of file diff --git a/utils/jobs.js b/utils/jobs.js index bb2670c..aa8d41c 100644 --- a/utils/jobs.js +++ b/utils/jobs.js @@ -2,6 +2,7 @@ const schedule = require('node-schedule'); const getExternalWeather = require('./getExternalWeather'); const Sockets = require('../Sockets'); +// Update weather data every 15 minutes const weatherJob = schedule.scheduleJob('updateWeather', '0 */15 * * * *', async () => { try { const weatherData = await getExternalWeather(); @@ -10,4 +11,9 @@ const weatherJob = schedule.scheduleJob('updateWeather', '0 */15 * * * *', async } catch (err) { console.log(err.message); } +}) + +// Clear old weather data every 4 hours +const weatherCleanerJob = schedule.scheduleJob('clearWeather', '0 0 */4 * * *', async () => { + console.log('clean') }) \ No newline at end of file