feat: Implement comments, notifications, and custom emojis with new API routes, UI components, and database migrations.

This commit is contained in:
x
2026-01-25 03:48:24 +01:00
parent 595118c2c8
commit d903ce8b98
18 changed files with 1900 additions and 44 deletions

22
debug/init_comments.mjs Normal file
View File

@@ -0,0 +1,22 @@
import db from "../src/inc/sql.mjs";
import { promises as fs } from "fs";
(async () => {
try {
const migration = await fs.readFile("./migration_comments.sql", "utf-8");
console.log("Applying migration...");
// Split by semicolon to handle multiple statements if the driver requires it,
// but postgres.js usually handles simple files well or we can execute as one block
// if it's just DDL. However, postgres.js template literal usually prefers single statements
// or we can use `db.file` if available, or just execute the string.
// Simple approach: execute the whole string
await db.unsafe(migration);
console.log("Migration applied successfully.");
process.exit(0);
} catch (e) {
console.error("Migration failed:", e);
process.exit(1);
}
})();

34
debug/init_emojis.mjs Normal file
View File

@@ -0,0 +1,34 @@
import db from "../src/inc/sql.mjs";
async function run() {
try {
console.log("Creating custom_emojis table...");
await db`
CREATE TABLE IF NOT EXISTS custom_emojis (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL UNIQUE,
url TEXT NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
)
`;
// Seed with existing default emojis if table is empty
const count = await db`SELECT count(*) FROM custom_emojis`;
if (count[0].count == 0) {
console.log("Seeding default emojis...");
await db`
INSERT INTO custom_emojis (name, url) VALUES
('f0ck', '/s/img/f0ck.png')
`;
}
console.log("Done.");
process.exit(0);
} catch (e) {
console.error(e);
process.exit(1);
}
}
run();

106
debug/verify_comments.mjs Normal file
View File

@@ -0,0 +1,106 @@
import db from "../src/inc/sql.mjs";
import http from "http";
import crypto from "crypto";
const HOST = "localhost";
const PORT = 3000;
import { readFile } from "fs/promises";
const cfg = JSON.parse(await readFile("../config.json", "utf8"));
const serverPort = cfg.websrv.port;
const runTest = async () => {
// 1. Setup Data
console.log("Setting up test data...");
const user = await db`SELECT id FROM "user" LIMIT 1`;
const item = await db`SELECT id FROM "items" LIMIT 1`;
if (!user.length || !item.length) {
console.error("No user or item found.");
process.exit(1);
}
const userId = user[0].id;
const itemId = item[0].id;
// Create session
const sessionKey = "testsession_" + Date.now();
const sessionHash = crypto.createHash('md5').update(sessionKey).digest("hex");
await db`DELETE FROM user_sessions WHERE user_id = ${userId}`; // Clear old sessions for clean test
await db`INSERT INTO user_sessions (user_id, session, browser, created_at, last_used, last_action)
VALUES (${userId}, ${sessionHash}, 'test-bot', ${Math.floor(Date.now() / 1000)}, ${Math.floor(Date.now() / 1000)}, 'test')`;
console.log(`User: ${userId}, Item: ${itemId}, Session: ${sessionKey}`);
// Helper for requests
const request = (method, path, body = null, cookie = null) => {
return new Promise((resolve, reject) => {
const options = {
hostname: HOST,
port: serverPort,
path: path,
method: method,
headers: {
'Content-Type': 'application/json',
'Cookie': cookie ? `session=${cookie}` : ''
}
};
const req = http.request(options, (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => resolve({ statusCode: res.statusCode, body: data }));
});
req.on('error', reject);
if (body) req.write(JSON.stringify(body));
req.end();
});
};
// 2. Test GET (Empty)
console.log("Testing GET /api/comments/" + itemId);
let res = await request('GET', `/api/comments/${itemId}`);
console.log("GET Response:", res.body);
let json = JSON.parse(res.body);
if (!json.success) throw new Error("GET failed");
// 3. Test POST
console.log("Testing POST /api/comments");
res = await request('POST', '/api/comments', {
item_id: itemId,
content: "Hello World from Test Bot"
}, sessionKey);
console.log("POST Response:", res.body);
json = JSON.parse(res.body);
if (!json.success) throw new Error("POST failed");
const commentId = json.comment.id;
// 4. Test GET (With comment)
console.log("Testing GET /api/comments/" + itemId);
res = await request('GET', `/api/comments/${itemId}`);
json = JSON.parse(res.body);
if (json.comments.length === 0) throw new Error("Comment not found");
console.log("Found comments:", json.comments.length);
// 5. Test Subscribe
console.log("Testing POST /api/subscribe/" + itemId);
res = await request('POST', `/api/subscribe/${itemId}`, {}, sessionKey);
console.log("Subscribe Response:", res.body);
json = JSON.parse(res.body);
if (!json.success) throw new Error("Subscribe failed");
if (!json.subscribed) throw new Error("Expected subscribed=true");
console.log("Testing Unsubscribe...");
res = await request('POST', `/api/subscribe/${itemId}`, {}, sessionKey);
json = JSON.parse(res.body);
if (json.subscribed) throw new Error("Expected subscribed=false");
console.log("ALL TESTS PASSED");
process.exit(0);
};
runTest().catch(e => {
console.error(e);
process.exit(1);
});

62
debug/verify_db.mjs Normal file
View File

@@ -0,0 +1,62 @@
import db from "../src/inc/sql.mjs";
const runTest = async () => {
console.log("Verifying Database Schema...");
// 1. Check Tables
try {
await db`SELECT 1 FROM comments LIMIT 1`;
console.log("✔ Table 'comments' exists.");
} catch (e) {
console.error("✘ Table 'comments' missing.");
process.exit(1);
}
try {
await db`SELECT 1 FROM comment_subscriptions LIMIT 1`;
console.log("✔ Table 'comment_subscriptions' exists.");
} catch (e) {
console.error("✘ Table 'comment_subscriptions' missing.");
process.exit(1);
}
// 2. Insert Test Data
console.log("Testing Insert...");
const user = await db`SELECT id FROM "user" LIMIT 1`;
const item = await db`SELECT id FROM "items" LIMIT 1`;
if (!user.length || !item.length) {
console.log("⚠ No user/item to test insert. Skipping.");
} else {
const userId = user[0].id;
const itemId = item[0].id;
const comment = await db`
INSERT INTO comments (item_id, user_id, content)
VALUES (${itemId}, ${userId}, 'Test Comment DB Verify')
RETURNING id
`;
console.log("✔ Inserted comment ID:", comment[0].id);
const fetch = await db`SELECT content FROM comments WHERE id = ${comment[0].id}`;
if (fetch[0].content === 'Test Comment DB Verify') {
console.log("✔ Verified content.");
} else {
console.error("✘ Content mismatch.");
process.exit(1);
}
// Cleanup
await db`DELETE FROM comments WHERE id = ${comment[0].id}`;
}
console.log("DB Schema Verification Passed.");
// 3. Optional: subscription test
await db`INSERT INTO comment_subscriptions (user_id, item_id) VALUES (${user[0].id}, ${item[0].id}) ON CONFLICT DO NOTHING`;
console.log("✔ Subscription table write access confirmed.");
process.exit(0);
};
runTest().catch(console.error);