ucs/backend/src/db.mjs
2025-03-15 18:50:10 +02:00

182 lines
4.1 KiB
JavaScript

/**
* Database interactions.
*/
import Database from "better-sqlite3";
import { migrate } from "@blackglory/better-sqlite3-migrations";
import utils from "./utils.mjs";
import config from "./config.mjs";
// Interface.
export class DB {
/**
* Connect to a database and perform migrations.
*/
constructor() {
this.my_db = createDB();
}
/**
* Fetch information about a site.
* @param {!string} site url.
* @return {!ObjType} site information.
*/
siteInfo(site_url) {
return getSiteInfo(this.my_db, site_url);
}
/**
* Fetch all comments from a specific site.
* @param {!string} site url.
* @return {!Array<!ObjType>} comment objects.
*/
siteComments(site_url) {
return getSiteComments(this.my_db, site_url);
}
/**
* Fetch comments from a specific page in a specific site.
* @param {!string} site url.
* @param {!string} page path.
* @return {!Array<!ObjType>} comment objects.
*/
pageComments(site_url, page) {
return getPageComments(this.my_db, site_url, page);
}
/**
* Insert a comment into the database.
* @param {!string} site url.
* @param {!string} page path.
* @param {!ObjType} comment.
* @return {!Array<!ObjType>} page comments.
*/
insertPageComment(site_url, path, comment) {
return insertPageComment(this.my_db, site_url, path, comment);
}
}
const db = new DB();
export default db;
// Migrations
function migrations() {
return [
{
version: 1,
up: `
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,
PRIMARY KEY (site, path, id)
);
`,
down: `
DROP TABLE comment;
`,
},
];
}
// setup
function createDB() {
const db = new Database(utils.db_path);
db.pragma("journal_mode = WAL");
migrate(db, migrations(), 1);
return db;
}
// queries
function getSiteInfo(db, site_url) {
const site = config.getSite(site_url);
if (!site || !site.info) {
throw "Unknown site";
}
return site;
}
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 comment
WHERE site = @site_id AND path = @path
),
@site_id,
@path,
@user,
@user_website,
@message,
@reply_to
RETURNING
id,
user,
user_website,
message,
published,
reply_to
;
`);
stmt.all(object);
// return all comments because a new comment might have been added
// since the user loaded the page.
return getPageComments(db, site_url, path);
}
function getPageComments(db, site_url, path) {
const site = config.getSite(site_url);
if (!site || !site.info.id) {
return [];
}
const stmt = db.prepare(`
SELECT
id,
user,
user_website,
message,
published,
reply_to
FROM comment
WHERE site = @site_id AND path = @path
ORDER BY id
;`);
return stmt.all({ site_id: site.info.id, path });
}
function getSiteComments(db, site_url) {
const site = config.getSite(site_url);
if (!site || !site.info.id) {
return [];
}
const stmt = db.prepare(`
SELECT
id,
@site_url as site,
path,
user,
user_website,
message,
published
FROM
comment
WHERE site = @site_id
ORDER BY published DESC
;
`);
return stmt.all({ site_id: site.info.id, site_url });
}