sites moved to configuration instead of db
This commit is contained in:
parent
f2ded06594
commit
bbe4dc87ab
11 changed files with 142 additions and 81 deletions
24
backend/config/default.json
Normal file
24
backend/config/default.json
Normal file
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"domain": "localhost",
|
||||
"port": 8080,
|
||||
"db_path": "./ucs.db",
|
||||
"sites": {
|
||||
"localhost": {
|
||||
"info": {
|
||||
"id": 2,
|
||||
"url": "localhost",
|
||||
"comment_token": "שלום"
|
||||
},
|
||||
"cors": ["localhost"],
|
||||
"limits_per_second": {
|
||||
"get": 500,
|
||||
"post": 20
|
||||
},
|
||||
"max_lengths": {
|
||||
"user": 20,
|
||||
"website": 100,
|
||||
"message": 200
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
25
backend/package-lock.json
generated
25
backend/package-lock.json
generated
|
@ -12,6 +12,7 @@
|
|||
"@blackglory/better-sqlite3-migrations": "^0.1.19",
|
||||
"better-sqlite3": "^11.8.1",
|
||||
"compression": "^1.8.0",
|
||||
"config": "^3.3.12",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.21.2",
|
||||
"express-rate-limit": "^7.5.0",
|
||||
|
@ -378,6 +379,18 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/config": {
|
||||
"version": "3.3.12",
|
||||
"resolved": "https://registry.npmjs.org/config/-/config-3.3.12.tgz",
|
||||
"integrity": "sha512-Vmx389R/QVM3foxqBzXO8t2tUikYZP64Q6vQxGrsMpREeJc/aWRnPRERXWsYzOHAumx/AOoILWe6nU3ZJL+6Sw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"json5": "^2.2.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/content-disposition": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||
|
@ -977,6 +990,18 @@
|
|||
"node": ">=0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/json5": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"json5": "lib/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/justypes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/justypes/-/justypes-3.1.2.tgz",
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
"@blackglory/better-sqlite3-migrations": "^0.1.19",
|
||||
"better-sqlite3": "^11.8.1",
|
||||
"compression": "^1.8.0",
|
||||
"config": "^3.3.12",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.21.2",
|
||||
"express-rate-limit": "^7.5.0",
|
||||
|
|
|
@ -14,7 +14,7 @@ router.use(express.json());
|
|||
// POST
|
||||
|
||||
router.post('/:site/*', utils.limiter(20), (req, res) => {
|
||||
const site = req.params.site;
|
||||
const site_url = req.params.site;
|
||||
const path = req.params[0];
|
||||
|
||||
if (!req.body.token || !req.body.message) {
|
||||
|
@ -22,30 +22,27 @@ router.post('/:site/*', utils.limiter(20), (req, res) => {
|
|||
return;
|
||||
}
|
||||
|
||||
let object = {
|
||||
let comment = {
|
||||
user: utils.escapeHtml(req.body.name) || "Anonymous",
|
||||
user_website: utils.escapeHtml(req.body.website) || null,
|
||||
message: utils.escapeHtml(req.body.message),
|
||||
reply_to: req.body.reply_to || null,
|
||||
site,
|
||||
path,
|
||||
};
|
||||
|
||||
// validation
|
||||
const user_token = req.body.token;
|
||||
const site_info = db.siteInfo(site);
|
||||
console.log(site_info.comment_token, site_info.message_length_limit);
|
||||
const site = db.siteInfo(site_url);
|
||||
|
||||
if (user_token !== site_info.comment_token) {
|
||||
if (user_token !== site.info.comment_token) {
|
||||
res.status(403).json("תשובת סינון שגויה.");
|
||||
} else if (object.user.length > utils.MAX_LENGTHS.username) {
|
||||
} else if (object.user.length > site.max_lengths.user) {
|
||||
res.status(400).json("שם משתמש ארוך מדי.");
|
||||
} else if (object.user_website > utils.MAX_LENGTHS.user_website) {
|
||||
} else if (object.user_website > site.max_lengths.website) {
|
||||
res.status(400).json("כתובת אתר ארוכה מדי.");
|
||||
} else if (object.message > site_info.message_length_limit) {
|
||||
} else if (object.message > site.max_lengths.message) {
|
||||
res.status(400).json("הודעה ארוכה מדי.");
|
||||
} else {
|
||||
const comment = db.insertPageComment(object);
|
||||
const comment = db.insertPageComment(site_url, path, comment);
|
||||
|
||||
res.json(comment);
|
||||
}
|
||||
|
|
12
backend/src/config.mjs
Normal file
12
backend/src/config.mjs
Normal file
|
@ -0,0 +1,12 @@
|
|||
import config from 'config';
|
||||
|
||||
const configuration = config.util.toObject();
|
||||
|
||||
// console.log(JSON.stringify(configuration));
|
||||
|
||||
export default {
|
||||
config: configuration,
|
||||
getSite: (site_url) => {
|
||||
return config.sites[site_url];
|
||||
}
|
||||
};
|
|
@ -2,6 +2,7 @@ import Database from 'better-sqlite3';
|
|||
import { migrate } from '@blackglory/better-sqlite3-migrations';
|
||||
|
||||
import utils from './utils.mjs';
|
||||
import config from './config.mjs';
|
||||
|
||||
// class
|
||||
|
||||
|
@ -12,14 +13,14 @@ export class DB {
|
|||
siteInfo(site) {
|
||||
return getSiteInfo(this.my_db, site);
|
||||
}
|
||||
siteComments(site) {
|
||||
return getSiteComments(this.my_db, site);
|
||||
siteComments(site_url) {
|
||||
return getSiteComments(this.my_db, site_url);
|
||||
}
|
||||
pageComments(site, page) {
|
||||
return getPageComments(this.my_db, site, page);
|
||||
pageComments(site_url, page) {
|
||||
return getPageComments(this.my_db, site_url, page);
|
||||
}
|
||||
insertPageComment(comment) {
|
||||
insertPageComment(this.my_db, comment);
|
||||
insertPageComment(site_url, path, comment) {
|
||||
insertPageComment(this.my_db, site_url, path, comment);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,13 +33,6 @@ function migrations() {
|
|||
return [
|
||||
{ version: 1,
|
||||
up: `
|
||||
CREATE TABLE site (
|
||||
id integer primary key autoincrement,
|
||||
url text not null,
|
||||
comment_token text not null,
|
||||
length_limit integer
|
||||
);
|
||||
|
||||
CREATE TABLE comment (
|
||||
id integer not null,
|
||||
site integer not null,
|
||||
|
@ -48,13 +42,11 @@ CREATE TABLE comment (
|
|||
message text not null,
|
||||
published text default (datetime('now')),
|
||||
reply_to integer,
|
||||
FOREIGN KEY(site) REFERENCES site(id),
|
||||
PRIMARY KEY (site, path, id)
|
||||
);
|
||||
`,
|
||||
down: `
|
||||
DROP TABLE comment;
|
||||
DROP TABLE site;
|
||||
`
|
||||
}
|
||||
];
|
||||
|
@ -63,7 +55,7 @@ DROP TABLE site;
|
|||
// setup
|
||||
|
||||
function createDB() {
|
||||
const db = new Database(process.env.DB || utils.DEFAULT_DB_PATH);
|
||||
const db = new Database(utils.db_path);
|
||||
db.pragma('journal_mode = WAL');
|
||||
migrate(db, migrations(), 1);
|
||||
return db;
|
||||
|
@ -71,25 +63,31 @@ function createDB() {
|
|||
|
||||
// queries
|
||||
|
||||
function getSiteInfo(db, site) {
|
||||
return db.prepare(`SELECT comment_token, length_limit as message_length_limit FROM site WHERE url = ?`).get(site);
|
||||
function getSiteInfo(db, site_url) {
|
||||
const site = config.getSite(site_url);
|
||||
if (!site || !site.info) { throw "Unknown site" }
|
||||
return site;
|
||||
}
|
||||
|
||||
function insertPageComment(db, object) {
|
||||
function insertPageComment(db, site_url, path, comment) {
|
||||
const site = config.getSite(site_url);
|
||||
if (!site || !site.info.id) { throw "Unknown site" }
|
||||
|
||||
let object = {...comment, site_id: site.info.id, path: path };
|
||||
|
||||
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
|
||||
FROM comment
|
||||
WHERE site = @site_id AND path = @path
|
||||
),
|
||||
( SELECT id FROM site WHERE url = @site ),
|
||||
@path,
|
||||
@user,
|
||||
@user_website,
|
||||
@message,
|
||||
@reply_to
|
||||
@site_id,
|
||||
@path,
|
||||
@user,
|
||||
@user_website,
|
||||
@message,
|
||||
@reply_to
|
||||
RETURNING
|
||||
id as id,
|
||||
user,
|
||||
|
@ -102,40 +100,41 @@ function insertPageComment(db, object) {
|
|||
return stmt.all(object);
|
||||
}
|
||||
|
||||
function getPageComments(db, site, path) {
|
||||
function getPageComments(db, site_url, path) {
|
||||
const site = config.getSite(site_url);
|
||||
if (!site || !site.info.id) { return []; }
|
||||
const stmt = db.prepare(`
|
||||
SELECT
|
||||
c.id as id,
|
||||
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
|
||||
FROM comment
|
||||
WHERE site = @site_id AND path = @path
|
||||
ORDER BY id
|
||||
;`);
|
||||
return stmt.all({ site, path });
|
||||
return stmt.all({ site_id: site.info.id, path });
|
||||
}
|
||||
|
||||
function getSiteComments(db, site) {
|
||||
function getSiteComments(db, site_url) {
|
||||
const site = config.getSite(site_url);
|
||||
if (!site || !site.info.id) { return []; }
|
||||
const stmt = db.prepare(`
|
||||
SELECT
|
||||
c.id,
|
||||
s.url as site,
|
||||
c.path,
|
||||
c.user,
|
||||
c.user_website,
|
||||
c.message,
|
||||
c.published
|
||||
id,
|
||||
@site_url as site,
|
||||
path,
|
||||
user,
|
||||
user_website,
|
||||
message,
|
||||
published
|
||||
FROM
|
||||
(SELECT id, url from site where url = @site) s
|
||||
JOIN comment c
|
||||
ON c.site = s.id
|
||||
ORDER BY c.published DESC
|
||||
comment
|
||||
WHERE site = @site_id
|
||||
ORDER BY published DESC
|
||||
;
|
||||
`);
|
||||
return stmt.all({ site });
|
||||
return stmt.all({ site_id: site.info.id, site_url });
|
||||
}
|
||||
|
|
|
@ -12,13 +12,14 @@ export default router;
|
|||
|
||||
router.use(express.json());
|
||||
|
||||
const domain = process.env.DOMAIN || utils.DEFAULT_DOMAIN;
|
||||
const domain = utils.domain;
|
||||
|
||||
router.get('/:site', utils.limiter(500), (req, res) => {
|
||||
const site = req.params.site;
|
||||
|
||||
var feed = new Feed({
|
||||
title: 'UCS',
|
||||
generator: 'UCS',
|
||||
description: 'תגובות עבור האתר ' + site,
|
||||
id: domain + '/feed/' + site,
|
||||
link: domain,
|
||||
|
@ -49,6 +50,7 @@ router.get('/:site/*', utils.limiter(500), (req, res) => {
|
|||
|
||||
var feed = new Feed({
|
||||
title: 'UCS',
|
||||
generator: 'UCS',
|
||||
description: 'תגובות עבור הדף ' + site + '/' + path,
|
||||
id: domain + '/feed/' + site,
|
||||
link: domain,
|
||||
|
|
|
@ -19,7 +19,7 @@ app.use('/atom', feed);
|
|||
|
||||
// Listen
|
||||
|
||||
const port = process.env.PORT || utils.DEFAULT_PORT;
|
||||
const port = utils.port;
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Listening on port ${port}`);
|
||||
|
|
|
@ -22,7 +22,7 @@ app.use(
|
|||
app.use(morgan('combined'));
|
||||
|
||||
const corsOptions = {
|
||||
origin: utils.DEFAULT_CORS,
|
||||
origin: utils.cors,
|
||||
optionsSuccessStatus: 200
|
||||
};
|
||||
|
||||
|
|
|
@ -1,24 +1,26 @@
|
|||
import RateLimit from 'express-rate-limit';
|
||||
|
||||
import config from './config.mjs';
|
||||
|
||||
// Constants
|
||||
|
||||
export const MAX_LENGTHS = {
|
||||
username: 100,
|
||||
user_website: 200,
|
||||
message_body: 1000
|
||||
};
|
||||
const domain = process.env.DOMAIN || config.domain || "localhost";
|
||||
|
||||
export const DEFAULT_PORT = 8080;
|
||||
const port = process.env.PORT || config.port || 8080;
|
||||
|
||||
export const DEFAULT_CORS = ["alloca.space", "www.alloca.space"];
|
||||
const db_path = process.env.DB || config.db_path || "ucs.db";
|
||||
|
||||
export const DEFAULT_DOMAIN = "comments.alloca.space";
|
||||
|
||||
export const DEFAULT_DB_PATH = "ucs.db";
|
||||
const cors = (function() {
|
||||
let origins = new Set();
|
||||
for (const site in config.sites) {
|
||||
config.sites[site].cors.forEach(origin => {origins.add(origin) });
|
||||
}
|
||||
return Array.from(origins);
|
||||
})();
|
||||
|
||||
// functions
|
||||
|
||||
export function escapeHtml(unsafe) {
|
||||
function escapeHtml(unsafe) {
|
||||
return unsafe
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
|
@ -29,18 +31,17 @@ export function escapeHtml(unsafe) {
|
|||
|
||||
// limiters
|
||||
|
||||
export const limiter = limit => RateLimit({
|
||||
const limiter = limit => RateLimit({
|
||||
windowMs: 1 * 60 * 1000,
|
||||
max: limit,
|
||||
});
|
||||
|
||||
|
||||
export default {
|
||||
MAX_LENGTHS,
|
||||
DEFAULT_PORT,
|
||||
DEFAULT_CORS,
|
||||
DEFAULT_DOMAIN,
|
||||
DEFAULT_DB_PATH,
|
||||
domain,
|
||||
port,
|
||||
db_path,
|
||||
cors,
|
||||
escapeHtml,
|
||||
limiter
|
||||
};
|
||||
|
|
|
@ -20,7 +20,7 @@ cp ../frontend/dist/assets/* public/
|
|||
|
||||
PACKAGE="ucs-$(cat package.json | jq -r .version).tar.gz"
|
||||
|
||||
tar czvf "$PACKAGE" package.json package-lock.json src/ node_modules/ public/
|
||||
tar czvf "$PACKAGE" package.json package-lock.json node_modules/ src/ config/ public/
|
||||
cd ..
|
||||
mv backend/"$PACKAGE" .
|
||||
echo "$PACKAGE created."
|
||||
|
|
Loading…
Add table
Reference in a new issue