162 lines
3.8 KiB
JavaScript
162 lines
3.8 KiB
JavaScript
import express from 'express';
|
|
import Database from 'better-sqlite3';
|
|
import { migrate } from '@blackglory/better-sqlite3-migrations';
|
|
|
|
// Constants
|
|
|
|
const MAX_LENGTHS = {
|
|
username: 100,
|
|
user_website: 200,
|
|
message_body: 1000
|
|
};
|
|
|
|
// functions
|
|
|
|
function escapeHtml(unsafe) {
|
|
return unsafe
|
|
.replace(/&/g, "&")
|
|
.replace(/</g, "<")
|
|
.replace(/>/g, ">")
|
|
.replace(/"/g, """)
|
|
.replace(/'/g, "'");
|
|
}
|
|
|
|
// db
|
|
|
|
const db = new Database('ucs.db'); // , { verbose: console.log });
|
|
db.pragma('journal_mode = WAL');
|
|
|
|
const migrations = [
|
|
{ version: 1,
|
|
up: `
|
|
CREATE TABLE site (
|
|
id integer primary key autoincrement,
|
|
url text not null,
|
|
comment_token text not null,
|
|
length_limit integer
|
|
) STRICT;
|
|
|
|
CREATE TABLE comment (
|
|
id integer not null,
|
|
site integer not null,
|
|
path text not null,
|
|
user text not null,
|
|
user_website text,
|
|
message text not null,
|
|
published text default (datetime('now')),
|
|
reply_to integer,
|
|
FOREIGN KEY(site) REFERENCES site(id),
|
|
PRIMARY KEY (site, path, id)
|
|
) STRICT;
|
|
`,
|
|
down: `
|
|
DROP TABLE comment;
|
|
DROP TABLE site;
|
|
`
|
|
}
|
|
];
|
|
|
|
migrate(db, migrations, 1);
|
|
|
|
// server
|
|
|
|
const app = express();
|
|
const port = 8080;
|
|
|
|
app.use(express.static("public"));
|
|
app.use(express.urlencoded({ extended: true }));
|
|
|
|
app.get('/', (req, res) => {
|
|
res.send(`<p>Hello, <span style="color: blue;">${req.get('User-Agent')}</span>!</p>`);
|
|
});
|
|
|
|
// GET
|
|
app.get('/url/:site/*', (req, res) => {
|
|
const site = req.params.site;
|
|
const path = req.params[0];
|
|
const stmt = db.prepare(`
|
|
SELECT
|
|
c.id as id,
|
|
user,
|
|
user_website,
|
|
message,
|
|
published,
|
|
reply_to
|
|
FROM
|
|
(SELECT id from site where url = @site) s
|
|
JOIN (SELECT * FROM comment WHERE path = @path) c
|
|
ON c.site = s.id
|
|
ORDER BY c.id
|
|
;`);
|
|
const comments = stmt.all({ site, path });
|
|
res.json(comments);
|
|
});
|
|
|
|
app.use(express.json());
|
|
|
|
// POST
|
|
app.post('/url/:site/*', (req, res) => {
|
|
const site = req.params.site;
|
|
const path = req.params[0];
|
|
|
|
if (!req.body.token || !req.body.message) {
|
|
res.status(400).json("הודעה ריקה.");
|
|
return;
|
|
}
|
|
|
|
let object = {
|
|
user: escapeHtml(req.body.name) || "Anonymous",
|
|
user_website: escapeHtml(req.body.website) || null,
|
|
message: escapeHtml(req.body.message),
|
|
reply_to: req.body.reply_to || null,
|
|
site,
|
|
path,
|
|
};
|
|
|
|
// validation
|
|
const user_token = req.body.token;
|
|
const site_info = db.prepare(`SELECT comment_token, length_limit as message_length_limit FROM site WHERE url = ?`).get(site);
|
|
console.log(site_info.comment_token, site_info.message_length_limit);
|
|
|
|
if (user_token !== site_info.comment_token) {
|
|
res.status(403).json("תשובת סינון שגויה.");
|
|
} else if (object.user.length > MAX_LENGTHS.username) {
|
|
res.status(400).json("שם משתמש ארוך מדי.");
|
|
} else if (object.user_website > MAX_LENGTHS.user_website) {
|
|
res.status(400).json("כתובת אתר ארוכה מדי.");
|
|
} else if (object.message > site_info.message_length_limit) {
|
|
res.status(400).json("הודעה ארוכה מדי.");
|
|
} else {
|
|
const stmt = db.prepare(`
|
|
INSERT INTO comment(id, site, path, user, user_website, message, reply_to)
|
|
SELECT
|
|
( SELECT count(*)
|
|
FROM (SELECT * FROM comment WHERE path = @path) c
|
|
JOIN (SELECT id FROM site WHERE url = @site) s
|
|
ON s.id = c.site
|
|
),
|
|
( SELECT id FROM site WHERE url = @site ),
|
|
@path,
|
|
@user,
|
|
@user_website,
|
|
@message,
|
|
@reply_to
|
|
RETURNING
|
|
id as id,
|
|
user,
|
|
user_website,
|
|
message,
|
|
published,
|
|
reply_to
|
|
;
|
|
`);
|
|
const comment = stmt.all(object);
|
|
|
|
res.json(comment);
|
|
}
|
|
});
|
|
|
|
app.listen(port, () => {
|
|
console.log(`Listening on port ${port}`);
|
|
});
|