diff --git a/docker-compose.local.yml b/docker-compose.local.yml index 5f4cc2d..3ccd699 100644 --- a/docker-compose.local.yml +++ b/docker-compose.local.yml @@ -28,7 +28,7 @@ services: build: docker/node command: sh ./docker/web.sh volumes: - - ".:/src:ro" + - ".:/src" - "web_npm_cache:/var/cache/npm" - "/src/node_modules" ports: diff --git a/index.js b/index.js index f70272a..18a628e 100644 --- a/index.js +++ b/index.js @@ -1,8 +1,32 @@ const path = require('path'); +const mysql = require('mysql'); const express = require('express'); +const moment = require('moment-timezone'); const fs = require('fs'); const bodyParser = require('body-parser'); +/* Database setup */ + +const dbPool = mysql.createPool({ + host: process.env.MYSQL_HOST || 'localhost', + user: process.env.MYSQL_USER || 'root', + password: process.env.MYSQL_PASSWORD || undefined, + database: process.env.MYSQL_DATABASE || 'vizon', + socketPath: process.env.MYSQL_SOCKET_PATH || undefined, + timezone: process.env.TZ || 'local', + + debug: process.env.NODE_ENV === 'development', + +}); + +const tableNames = { + drawings: process.env.MYSQL_TABLE_DRAWINGS || 'vizon_drawings', + users: process.env.MYSQL_TABLE_USERS || 'vizon_users', + rankings: 'vizon_web_rankings', +}; + +/* Frontend server setup */ + const frontendDir = path.resolve(__dirname, 'dist'); const app = express(); @@ -29,6 +53,76 @@ fs.stat(frontendDir, (err) => { app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); +// Additional middleware which will set headers that we need on each request. +app.use((req, res, next) => { + // Disable caching so we'll always get the latest comments. + res.setHeader('Cache-Control', 'no-cache'); + next(); +}); + +function calculateRankings(id: Number) { + db.query('SELECT * FROM `vizon_web_rankings` WHERE `vizon_drawings_id`=?', [id], (err, results, fields) => { + if (err) { + console.error('Failed to query rankings from database:', err); + res.status(500).json({ + error: 'Database query failed', + }); + } + }); +} + +app.get('/api/status', (req, res) => { + dbPool.getConnection((err, db) => { + if (err) { + console.error('Failed to connect to database:', err); + res.status(500).json({ + error: 'Database connection failed', + }); + } + + db.query('SELECT * FROM ?? ORDER BY ?? DESC LIMIT 0, 1', [ + tableNames.drawings, + 'id', + ], (qerr, results, fields) => { + if (qerr) { + console.error('Failed to request drawings:', err); + res.status(500).json({ + error: 'Database query failed', + }); + return; + } + + const row = results[0]; + + const id = row.id; + + const date = moment(row.drawing_date); + + const numbers = [ + row.first, + row.second, + row.third, + row.fourth, + row.fifth, + row.sixth, + ].map(i => parseInt(i, 10)); + + const ranking = [ + // @TODO - calculate ranking from mysql tables {drawings} and {bets} + ]; + + res.json({ + lastDrawing: { + id, + date, + numbers, + ranking, + }, + }); + }); + }); +}); + app.listen(app.get('port'), () => { console.log(`Server started: http://localhost:${app.get('port')}/`); }); diff --git a/package-lock.json b/package-lock.json index 930b122..01bd5b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1643,6 +1643,11 @@ "integrity": "sha1-TK2iGTZS6zyp7I5VyQFWacmAaXg=", "dev": true }, + "bignumber.js": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-4.0.2.tgz", + "integrity": "sha1-LR3DfuWWiGfs6pC22k0W5oYI0h0=" + }, "binary-extensions": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.10.0.tgz", @@ -2474,8 +2479,7 @@ "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "cosmiconfig": { "version": "2.2.2", @@ -5518,8 +5522,7 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "isbinaryfile": { "version": "3.0.2", @@ -6691,14 +6694,12 @@ "moment": { "version": "2.18.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz", - "integrity": "sha1-w2GT3Tzhwu7SrbfIAtu8d6gbHA8=", - "dev": true + "integrity": "sha1-w2GT3Tzhwu7SrbfIAtu8d6gbHA8=" }, "moment-timezone": { "version": "0.5.13", "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.13.tgz", "integrity": "sha1-mc5cfYJyYusPH3AgRBd/YHRde5A=", - "dev": true, "requires": { "moment": "2.18.1" } @@ -6730,6 +6731,17 @@ "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", "dev": true }, + "mysql": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.14.1.tgz", + "integrity": "sha512-ZPXqQeYH7L1QPDyC77Rcp32cNCQnNjz8Y4BbF17tOjm5yhSfjFa3xS4PvuxWJtEEmwVc4ccI7sSntj4eyYRq0A==", + "requires": { + "bignumber.js": "4.0.2", + "readable-stream": "2.3.3", + "safe-buffer": "5.1.1", + "sqlstring": "2.2.0" + } + }, "nan": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz", @@ -8940,8 +8952,7 @@ "process-nextick-args": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", - "dev": true + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" }, "progress": { "version": "2.0.0", @@ -9291,7 +9302,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", - "dev": true, "requires": { "core-util-is": "1.0.2", "inherits": "2.0.3", @@ -9774,8 +9784,7 @@ "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", - "dev": true + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" }, "sass-graph": { "version": "2.2.4", @@ -10471,6 +10480,11 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "sqlstring": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.2.0.tgz", + "integrity": "sha1-wxNcTqirzX5+50GklmqJHYak8ZE=" + }, "sshpk": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", @@ -10554,7 +10568,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true, "requires": { "safe-buffer": "5.1.1" } @@ -11128,8 +11141,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "utila": { "version": "0.4.0", diff --git a/package.json b/package.json index 51fda19..de727e3 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,9 @@ "docker-compose": "docker-compose -f docker-compose.local.yml", "docker:down:clean": "npm run -s docker:down -- --rmi all -v", "docker:down": "npm run -s docker-compose -- down", + "docker:logs": "npm run -s docker-compose -- logs", + "docker:logs:follow": "npm run -s docker:logs -- -f", + "docker:restart": "npm run -s docker-compose -- restart", "docker:up:daemon": "npm run -s docker:up -- -d", "docker:up": "npm run -s docker-compose -- up --build", "docker": "npm run -s docker:up", @@ -51,7 +54,6 @@ "eslint-plugin-react": "^7.2.1", "eslint": "^4.5.0", "file-loader": "^0.11.2", - "moment-timezone": "^0.5.13", "normalize-scss": "^7.0.0", "nwb-sass": "^0.8.1", "nwb": "^0.18.10", @@ -68,6 +70,8 @@ }, "dependencies": { "body-parser": "^1.17.2", - "express": "^4.15.4" + "express": "^4.15.4", + "moment-timezone": "^0.5.13", + "mysql": "^2.14.1" } } diff --git a/sql/vizon_web_rankings.sql b/sql/vizon_web_rankings.sql new file mode 100644 index 0000000..e4588af --- /dev/null +++ b/sql/vizon_web_rankings.sql @@ -0,0 +1,91 @@ +CREATE OR REPLACE VIEW `vizon_web_rankings` AS +SELECT + bets.id, + vizon_drawings_id, + COUNT(*) AS richtige +FROM + ( + SELECT + vizon_users_id AS id, + vizon_drawings_id, + FIRST AS NUMBER + FROM + vizon_bets + UNION ALL +SELECT + vizon_users_id, + vizon_drawings_id, + SECOND +FROM + vizon_bets +UNION ALL +SELECT + vizon_users_id, + vizon_drawings_id, + third +FROM + vizon_bets +UNION ALL +SELECT + vizon_users_id, + vizon_drawings_id, + fourth +FROM + vizon_bets +UNION ALL +SELECT + vizon_users_id, + vizon_drawings_id, + fifth +FROM + vizon_bets +UNION ALL +SELECT + vizon_users_id, + vizon_drawings_id, + sixth +FROM + vizon_bets +) bets, +( + SELECT + id, + FIRST AS NUMBER + FROM + vizon_drawings + UNION ALL +SELECT + id, + SECOND +FROM + vizon_drawings +UNION ALL +SELECT + id, + third +FROM + vizon_drawings +UNION ALL +SELECT + id, + fourth +FROM + vizon_drawings +UNION ALL +SELECT + id, + fifth +FROM + vizon_drawings +UNION ALL +SELECT + id, + sixth +FROM + vizon_drawings +) drawings +WHERE + bets.vizon_drawings_id = drawings.id AND bets.number = drawings.number +GROUP BY + bets.id, + vizon_drawings_id \ No newline at end of file diff --git a/src/App.jsx b/src/App.jsx index 8de7a2d..58d7de4 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -4,6 +4,7 @@ import moment from 'moment-timezone'; import 'react-fontawesome'; import WebFont from 'webfontloader'; import Countdown from './Countdown'; +import Drawing from './Drawing'; import Header from './Header'; import Footer from './Footer'; import getUpcomingDate from './getUpcomingDate'; @@ -79,6 +80,19 @@ class App extends React.Component { The next VIzon draw is on {nextUpcomingDate.format('dddd')}, {nextUpcomingDate.format('L LT z')}.

+ +

+ Last Drawing: +

+