add adminer and guestbook delete
This commit is contained in:
parent
7f674379d2
commit
9eb34fd46a
|
@ -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:
|
||||
|
|
|
@ -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'})
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
module.exports = {
|
||||
ViewLoader: require('./view_loader')
|
||||
ViewLoader: require('./view_loader'),
|
||||
TimeSince: require('./timesince')
|
||||
}
|
|
@ -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
|
10
index.js
10
index.js
|
@ -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))
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -46,7 +46,8 @@ module.exports = (sequelize, DataTypes) => {
|
|||
}
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'Guestbook'
|
||||
modelName: 'Guestbook',
|
||||
tableName: 'guestbook'
|
||||
});
|
||||
return Guestbook;
|
||||
};
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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}
|
Loading…
Reference in New Issue