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