diff options
-rw-r--r-- | src/css/styles.css | 4 | ||||
-rw-r--r-- | src/html/admin.html | 41 | ||||
-rw-r--r--[-rwxr-xr-x] | src/html/snake.html | 0 | ||||
-rw-r--r-- | src/index.js | 9 | ||||
-rw-r--r-- | src/server.js | 76 |
5 files changed, 95 insertions, 35 deletions
diff --git a/src/css/styles.css b/src/css/styles.css index cb61c99..9a7d048 100644 --- a/src/css/styles.css +++ b/src/css/styles.css @@ -88,4 +88,8 @@ img { .date { font-style: italic; margin: 0em; +} + +.url-table, .ip-table { + width: 30%; }
\ No newline at end of file diff --git a/src/html/admin.html b/src/html/admin.html index d0d2e81..1ec7e15 100644 --- a/src/html/admin.html +++ b/src/html/admin.html @@ -7,6 +7,24 @@ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <link rel="stylesheet" type="text/css" href="/css/bootstrap.css"> <link rel="stylesheet" type="text/css" href="/css/styles.css"> + <script src="https://cdn.jsdelivr.net/npm/vue"></script> + <!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> --> + <script> + window.onload = function () { + var feed = new Vue({ + el: '#stats', + data: { + stats: {ip: [], url: []}, + showIpTable: true, + showUrlTable: true, + }, + created() { + fetch(new Request('/admin/stats')).then(response => response.json()) + .then(response => this.stats = response); + } + }); + } + </script> </head> <body> @@ -31,6 +49,29 @@ </form> </div> </div> + <div id="stats"> + <h1>Stats</h1> + <div> + <input type="checkbox" id="showIp" v-model="showIpTable"> + <label for="showIp">Show IP Stats</label> + </div> + <table class="table ip-table" v-if="showIpTable"> + <tr><th>Host</th><th>Total Requests</th></tr> + <tr v-for="item in stats.ip"> + <td>{{item.ip}}</td><td>{{item.c}}</td> + </tr> + </table> + <div> + <input type="checkbox" id="showUrl" v-model="showUrlTable"> + <label for="showUrl">Show URL Stats</label> + </div> + <table class="table url-table" v-if="showUrlTable"> + <tr><th>Method</th><th>Path</th><th>Total Requests</th></tr> + <tr v-for="item in stats.url"> + <td>{{item.method}}</td><td>{{item.url}}</td><td>{{item.c}}</td> + </tr> + </table> + </div> </body> </html>
\ No newline at end of file diff --git a/src/html/snake.html b/src/html/snake.html index e2158e5..e2158e5 100755..100644 --- a/src/html/snake.html +++ b/src/html/snake.html diff --git a/src/index.js b/src/index.js index f678224..c689779 100644 --- a/src/index.js +++ b/src/index.js @@ -65,7 +65,12 @@ function setUpModels(){ password: { type: Sequelize.STRING, allowNull: false, - },}) + },}), + "requests": database.define('requests', { + ip: Sequelize.STRING, + method: Sequelize.STRING, + url: Sequelize.STRING, + }) } models.pictures.belongsTo(models.posts); return models; @@ -74,6 +79,6 @@ function setUpModels(){ const models = setUpModels(); sync(); -server.setUpRoutes(models, jwtFunctions); +server.setUpRoutes(models, jwtFunctions, database); server.listen(config.port); diff --git a/src/server.js b/src/server.js index 1de966c..5bbb980 100644 --- a/src/server.js +++ b/src/server.js @@ -7,45 +7,39 @@ const crypto = require('crypto'); const multer = require('multer'); var storage = multer.diskStorage({ destination: function (req, file, cb) { - cb(null, 'src/uploads/') + cb(null, 'src/uploads/') }, filename: function (req, file, cb) { var ext = ""; - if(file.originalname.includes(".")){ + if (file.originalname.includes(".")) { ext = "." + file.originalname.split(".")[1]; } - return cb(null, 'img-' + Date.now()+ext) + return cb(null, 'img-' + Date.now() + ext) } - }) +}) var upload = multer({ storage: storage }) const server = express(); -// server.use(bodyParser.json()); server.use(cookieParser()) server.use(bodyParser.urlencoded({ extended: true })); -// Route logging -server.use(function (req, res, next) { - console.debug("express:", req.method, req.originalUrl); - next() -}) -function listen(port){ +function listen(port) { server.listen(port, () => console.info(`Listening on port ${port}!`)); } -function setUpRoutes(models, jwtFunctions){ +function setUpRoutes(models, jwtFunctions, database) { // Authentication routine - server.use(function(req, res, next) { - if(req.path.startsWith("/admin")){ + server.use(function (req, res, next) { + if (req.path.startsWith("/admin")) { let cookie = req.cookies.authorization if (!cookie) { console.debug("Redirecting to login - no cookie") res.redirect('/login'); return; - } + } try { const decryptedUserId = jwtFunctions.verify(cookie); - models.users.findOne({where: {username: decryptedUserId}}).then((user, error) => { + models.users.findOne({ where: { username: decryptedUserId } }).then((user, error) => { if (user) { res.locals.user = user.get({ plain: true }); } else { @@ -54,13 +48,19 @@ function setUpRoutes(models, jwtFunctions){ return; } }); - } catch (e){ + } catch (e) { res.status(400).send(e.message); } } next(); }) + // Route logging + server.use(function (req, res, next) { + var request = models.requests.create({ createdAt: new Date(), ip: req.ip, method: req.method, url: req.originalUrl }); + next() + }) + server.get('/', (req, res) => res.sendFile(__dirname + "/html/index.html")) server.get('/index', (req, res) => res.sendFile(__dirname + "/html/index.html")) server.get('/admin', (req, res) => res.sendFile(__dirname + "/html/admin.html")); @@ -69,17 +69,27 @@ function setUpRoutes(models, jwtFunctions){ server.get('/essay', (req, res) => res.sendFile(__dirname + "/html/essay.html")); server.get('/snake', (req, res) => res.sendFile(__dirname + "/html/snake.html")); server.get('/setScore', (req, res) => { - request(`http://localhost:8000?${req.url.split("?")[1]}`, function(error, response, body) { + request(`http://localhost:8000?${req.url.split("?")[1]}`, function (error, response, body) { }); }) - server.get('/posts/:type', async (req, res, next) => { + server.get('/admin/stats', async (req, res, next) => { + try { + var ipResult = await database.query("SELECT ip, count(id) as c FROM requests GROUP BY ip", { type: database.QueryTypes.SELECT }) + var urlResult = await database.query("SELECT method, url, count(id) as c FROM requests GROUP BY method, url", { type: database.QueryTypes.SELECT }) + res.status(200).send({ ip: ipResult, url: urlResult }); + next(); + } catch (e) { + res.status(400).send(e.message); + } + }) + server.get('/posts/:type', async (req, res, next) => { try { const { type } = req.params; - var posts = await models.posts.findAll({where: { type: type }, order: [['createdAt', 'DESC']]}); - posts = posts.map(x => x.get({ plain: true })); + var posts = await models.posts.findAll({ where: { type: type }, order: [['createdAt', 'DESC']] }); + posts = posts.map(x => x.get({ plain: true })); for (const post of posts) { - const images = await models.pictures.findAll({ attributes: ["source"], where: { postId: post.id }}).map(x => x.source); - post.images = images; + const images = await models.pictures.findAll({ attributes: ["source"], where: { postId: post.id } }).map(x => x.source); + post.images = images; } res.status(200).send(posts); next(); @@ -87,12 +97,12 @@ function setUpRoutes(models, jwtFunctions){ res.status(400).send(e.message); } }) - server.post('/posts', upload.array('images'), async (req, res, next) => { + server.post('/posts', upload.array('images'), async (req, res, next) => { try { const type = req.body.type const newPost = await models.posts.create(req.body); req.files.forEach(async (file) => { - await models.pictures.create({"source": "uploads/"+file.filename, "postId": newPost.id}); + await models.pictures.create({ "source": "uploads/" + file.filename, "postId": newPost.id }); console.log("uploaded ", file.path); }) console.log(newPost); @@ -104,10 +114,10 @@ function setUpRoutes(models, jwtFunctions){ }) server.post('/login', async (req, res, next) => { const hash = crypto.createHash("sha512").update(req.body.password, "binary").digest("base64"); - const user = await models.users.findOne({where: { username: req.body.username, password: hash }}) - if(user){ + const user = await models.users.findOne({ where: { username: req.body.username, password: hash } }) + if (user) { const token = jwtFunctions.sign(user.username); - res.cookie('authorization',token); + res.cookie('authorization', token); console.debug("Redirecting to admin - logged in") res.redirect('/admin'); } else { @@ -119,13 +129,13 @@ function setUpRoutes(models, jwtFunctions){ server.get('/favicon.ico', (req, res) => res.sendFile(__dirname + "/icon/favicon.ico")) server.get('/css/:id', (req, res) => { - res.sendFile(__dirname + "/css/"+req.params.id); + res.sendFile(__dirname + "/css/" + req.params.id); }); - server.get('/uploads/:id', (req, res) => { - res.sendFile(__dirname + "/uploads/"+req.params.id); + server.get('/uploads/:id', (req, res) => { + res.sendFile(__dirname + "/uploads/" + req.params.id); }); - server.get('/essay/:id', (req, res) => { - res.sendFile(__dirname + "/html/essay/"+req.params.id); + server.get('/essay/:id', (req, res) => { + res.sendFile(__dirname + "/html/essay/" + req.params.id); }); } |