aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Powers <markppowers0@gmail.com>2019-02-04 17:27:00 -0500
committerMark Powers <markppowers0@gmail.com>2019-02-04 17:27:00 -0500
commitf4b2eabe2da89abc33e4cbe21ca1560995b32eca (patch)
treed66bf7178aadf040aff19f2e57f5300780157458
parentcf0a680fd9dc07ac7b2d95d1cd2bbca5431d4ab6 (diff)
Add log viewer in admin panel
-rw-r--r--src/css/styles.css4
-rw-r--r--src/html/admin.html41
-rw-r--r--[-rwxr-xr-x]src/html/snake.html0
-rw-r--r--src/index.js9
-rw-r--r--src/server.js76
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);
});
}