add adminer and guestbook delete

This commit is contained in:
b1ek 2023-02-20 12:43:53 +10:00
parent 7f674379d2
commit 9eb34fd46a
11 changed files with 242 additions and 18 deletions

View File

@ -32,6 +32,15 @@ services:
- './data/db:/var/lib/postgresql'
networks:
- homepage
adminer:
image: adminer:standalone
ports:
- '8001:8080'
networks:
- homepage
environment:
ADMINER_DEFAULT_SERVER: postgres
ADMINER_DESIGN: rmsoft
networks:
homepage:

View File

@ -15,6 +15,7 @@ function css(cb) {
gulp.task("serve_dev", (cb) => {
console.log('Running in dev mode');
console.log('Launching node...');
let node = spawn('node', ['--inspect=0.0.0.0', 'index.js'], {stdio: 'inherit'})

View File

@ -1,3 +1,4 @@
module.exports = {
ViewLoader: require('./view_loader')
ViewLoader: require('./view_loader'),
TimeSince: require('./timesince')
}

29
helpers/timesince.js Normal file
View File

@ -0,0 +1,29 @@
function TimeSince(date) {
var seconds = Math.floor((new Date() - date) / 1000);
var interval = seconds / 31536000;
if (interval > 1) {
return Math.floor(interval) + " years";
}
interval = seconds / 2592000;
if (interval > 1) {
return Math.floor(interval) + " months";
}
interval = seconds / 86400;
if (interval > 1) {
return Math.floor(interval) + " days";
}
interval = seconds / 3600;
if (interval > 1) {
return Math.floor(interval) + " hours";
}
interval = seconds / 60;
if (interval > 1) {
return Math.floor(interval) + " minutes";
}
return Math.floor(seconds) + " seconds";
}
module.exports = TimeSince

View File

@ -21,7 +21,15 @@ let RedisStore = require("connect-redis")(session)
const { APP_PORT, APP_KEY } = process.env;
app.use((req, res, next) => {
req.start = Date.now();
res.on('header', (res) => {
let time = Date.now() - req.start;
console.log(time)
res.setHeader('X-Reponse-Time', time);
})
next();
});
app.use(bodyparser.json());
app.use(bodyparser.urlencoded({ extended: true }));
app.use(cookie_parse(APP_KEY))

View File

@ -2,7 +2,7 @@
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, DataTypes) {
await queryInterface.createTable('Guestbooks', {
await queryInterface.createTable('guestbook', {
id: {
type: DataTypes.BIGINT(11),
primaryKey: true,

View File

@ -46,7 +46,8 @@ module.exports = (sequelize, DataTypes) => {
}
}, {
sequelize,
modelName: 'Guestbook'
modelName: 'Guestbook',
tableName: 'guestbook'
});
return Guestbook;
};

View File

@ -22,6 +22,7 @@
"express-session": "^1.17.3",
"glob": "^8.1.0",
"gulp": "^4.0.2",
"html-escaper": "^3.0.3",
"ioredis": "^5.3.1",
"js-base64": "^3.7.5",
"mocha": "^10.2.0",

View File

@ -1,6 +1,26 @@
/* entries */
.gb_entries {
border: 0px solid #00000000;
border-collapse: collapse;
margin: 16px 10px;
width: 99%
}
.gb_entries tr {
border: 0 !important;
border-bottom: 1px solid #e2e4e2 !important;
transition: 250ms ease;
}
.gb_entries tr:hover {
background-color: #c2c4c220;
}
.gb_entries tr:target {
background-color: #91c29120;
}
.gb_entry_text {
padding: 0 8px;
vertical-align: middle;
border-left: 1px solid #c2c4c2;
border-collapse: collapse;
}
.gb_entry_text .gb_entry_text_title {
font-size:9pt;
@ -8,4 +28,63 @@
margin:0;
padding:0;
padding-top:1em
}
.gb_sender_data {
padding:0 8px;
}
.gb_hidden_mail {
display:inline-block;
height:9.5pt;
border-radius:4px;
background:#666d65;
transform: translateY(1px);
border: 1px solid #4f534e;
box-sizing: border-box;
transition: 150ms ease;
font-size:7pt;
color: #eaeaea;
text-align: center;
user-select: none;
box-shadow: inset 0 1px 2px #fefefe20 1px 2px #30303040;
content: '';
min-width: 100px;
}
.gb_hidden_mail:hover {
background: #7e8b7b;
}
.gb_hidden_mail:hover::before {
content: 'Email is hidden';
}
/* ui elements */
/* delete record button */
.gb_record_del_btn {
user-select: none;
box-shadow:inset 0px 1px 0px 0px #cf866c;
background:linear-gradient(to bottom, #d0451b 5%, #bc3315 100%);
background-color:#d0451b;
border-radius:6px;
border:1px solid #942911;
display:inline-block;
cursor:pointer;
color:#ffffff;
font-family:Arial;
font-size:12px;
font-weight:bold;
padding:2px 10px;
text-decoration:none;
text-shadow:0px 1px 0px #854629;
}
.gb_record_del_btn:hover {
background:linear-gradient(to bottom, #bc3315 5%, #d0451b 100%);
background-color:#bc3315;
}
.gb_record_del_btn:active {
position:relative;
top:1px;
}

View File

@ -1,9 +1,21 @@
const Helpers = require('../helpers');
const Sequelize = require('../models');
const html_escape = require('html-escaper');
const send_error = async (req, res, error, data) => {
res.send(await Helpers.ViewLoader.load('guestbook.pug', {
current_route: req.originalUrl,
ip: req.ip,
errors: error,
data
}));
};
async function handler(req, res, next) {
try {
const errors = req.query.error;
let data = {};
let sqldata = await Sequelize.Guestbook.findAll({
where: {
@ -19,7 +31,8 @@ async function handler(req, res, next) {
res.send(await Helpers.ViewLoader.load('guestbook.pug', {
current_route: req.originalUrl,
ip: req.ip,
data
data,
errors
}));
return;
} catch (err) {
@ -40,14 +53,45 @@ async function submit(req, res, next) {
hidden: false,
time: Math.floor(Date.now() / 1000)
});
if (!data) next(new Error('Failed to create a new record.'));
if (!data) {
res.send(await Helpers.ViewLoader.load('guestbook.pug', {
current_route: req.originalUrl,
ip: req.ip,
errors: 'Could not create a new record'
}));
}
res.redirect('/guestbook#gb_entry_' + data.id);
return;
}
async function del(req, res, next) {
try
{
let record = await Sequelize.Guestbook.findAndCountAll({
where: {id: req.params.id}
});
if (record.count == 0) {
res.redirect('/guestbook');
}
const data = record.rows[0];
if (
data.ip == req.ip &&
Math.floor(Date.now() / 1000) - data.time <= (60 * 60 * 24)
) {
await Sequelize.Guestbook.update({hidden: true}, {where: {id: req.params.id}})
res.redirect('/guestbook');
} else {
res.redirect('/guestbook?error=' + encodeURIComponent('You don\'t have permission to delete this record.'))
return
}
}
catch (err) { next(err); }
}
module.exports = (router) => {
router.get('/guestbook', handler);
router.post('/guestbook/submit', submit);
router.get('/guestbook/del/:id', del);
}

View File

@ -1,6 +1,34 @@
extends layout/main.pug
block root
- var title = 'Guestbook'
-
function TimeSince(date) {
var seconds = Math.floor((new Date() - date) / 1000);
var interval = seconds / 31536000;
if (interval > 1) {
return Math.floor(interval) + " years";
}
interval = seconds / 2592000;
if (interval > 1) {
return Math.floor(interval) + " months";
}
interval = seconds / 86400;
if (interval > 1) {
return Math.floor(interval) + " days";
}
interval = seconds / 3600;
if (interval > 1) {
return Math.floor(interval) + " hours";
}
interval = seconds / 60;
if (interval > 1) {
return Math.floor(interval) + " minutes";
}
return Math.floor(seconds) + " seconds";
}
block head
<link rel='stylesheet' href='/static/ui/send_button.css'>
<link rel='stylesheet' href='/static/ui/gb_ui.css'>
@ -18,19 +46,26 @@ block content
tr
td Your name:
td
input(type='text' name='name' value='John Doe')
input(type='text' name='name' value='' style='width:50%')
span(style='font-size:9pt;color:red;user-select:none' title='required') *
tr
td Your email:
td
input(type='email' name='email' value='john.doe@example.com')
input(type='email' name='email' value='')
tr
td Hide your email?
td
input(type='checkbox' name='hidemail')
p(style='margin:6px 0') Your message (512 chars max):
// span(style='font-size:9pt;color:red;user-select:none' title='required') *
p(style='margin:6px 0')
| Your message (512 chars max):
span(style='font-size:9pt;color:red;user-select:none' title='required') *
textarea(name='message' style='width:100%;height:150px;max-width:600px;max-height:300px')
p
input(type='submit' class='send_button_1')
if (errors)
br
span(style='font-weight:bold;color:darkred;font-size:9pt') !{errors}
td(style='padding:0 16px;margin:0')
h5 Guidelines
ul
@ -42,22 +77,38 @@ block content
span(style='font-size:10pt;color:darkred;font-weight:bold').
Warning: Your ip (#{ip}) will be logged and displayed for everyone.<br/>
You can delete your own message if it was sent from the same ip for 24 hours after it was sent.
p
span(style='font-size:9pt;color:red;user-select:none' title='required') *
| - required
hr
if (!data)
p No records available.
else
table
table(class='gb_entries')
each entry, id in data
tr
tr(id='gb_entry_' + id)
td(width='20%' class='gb_sender_data')
p(style='font-size:9pt').
ID: <a id='gb_entry_#{id}' href='#gb_entry_#{id}'>##{id}</a><br/>
Sender: #{entry.name}<br/>
Email: #{entry.email}<br/>
IP: #{entry.ip}<br/>
// Date: #{new Date(entry.time).toISOString()}<br/>
p(style='font-size:9pt')
| ID:
a(href='gb_entry_' + entry.id) ##{entry.id}
br
| Sender: #{entry.name}
br
if (!entry.hidemail)
| Email: #{entry.email}
else
| Email:
span(class='gb_hidden_mail' style='width:' + (10 * entry.email.length) + 'px')
br
| IP: #{entry.ip}
br
| Date: #{TimeSince(new Date(entry.time * 1000))} ago
if (ip == entry.ip && Math.floor(Date.now() / 1000) - entry.time <= (60 * 60 * 24))
p(style='margin:0;padding:0;padding-bottom:12px')
a(href='/guestbook/del/' + id class='gb_record_del_btn' title='you can delete your own messages') delete
td(width='80%' class='gb_entry_text')
p(class='gb_entry_text_title') Message:
p(style='margin:0;padding:0;font-size:10pt').
hiii
#{entry.text}