Files

59 lines
2.6 KiB
JavaScript

// Ad reports: members flag a broken or inappropriate ad (ads are auto-approved,
// so this is the safety valve). Stored dual-mode (MySQL when DATABASE_URL, else
// a JSON file in the volume) and surfaced to the admin.
const fs = require('fs');
const path = require('path');
const db = require('./db');
let DATA_DIR = null;
const J = {
db: null,
FILE: () => path.join(DATA_DIR, 'ad-reports.json'),
load() { try { this.db = JSON.parse(fs.readFileSync(this.FILE(), 'utf8')); } catch (e) { this.db = { nextId: 1, items: [] }; } },
save() { try { fs.writeFileSync(this.FILE(), JSON.stringify(this.db)); } catch (e) {} }
};
function init(opts) { DATA_DIR = opts.dataDir; }
const REASONS = ['broken', 'inappropriate', 'spam', 'scam', 'other'];
async function add(campaignId, reporterEmail, reason, note) {
const now = Date.now();
const r = REASONS.includes(String(reason)) ? String(reason) : 'other';
const n = String(note || '').slice(0, 500);
const cid = Number(campaignId) || 0;
const who = String(reporterEmail || '').toLowerCase();
if (db.enabled()) {
const ins = await db.q('INSERT INTO ad_reports (campaign_id,reporter,reason,note,ts) VALUES (?,?,?,?,?)',
[cid, who, r, n, now]);
return { id: ins.insertId, campaignId: cid, reason: r };
}
if (!J.db) J.load();
const id = J.db.nextId++;
J.db.items.push({ id, campaignId: cid, reporter: who, reason: r, note: n, ts: now, resolved: false });
J.save();
return { id, campaignId: cid, reason: r };
}
// recent reports for the admin view (newest first)
async function list(limit = 200) {
if (db.enabled()) {
const rows = await db.q('SELECT id,campaign_id,reporter,reason,note,ts,resolved FROM ad_reports ORDER BY ts DESC LIMIT ?', [limit]);
return rows.map(x => ({ id: x.id, campaignId: x.campaign_id, reporter: x.reporter, reason: x.reason, note: x.note, ts: Number(x.ts), resolved: !!x.resolved }));
}
if (!J.db) J.load();
return J.db.items.slice().sort((a, b) => b.ts - a.ts).slice(0, limit);
}
async function resolve(id) {
if (db.enabled()) { await db.q('UPDATE ad_reports SET resolved=1 WHERE id=?', [Number(id)]); return { ok: true }; }
if (!J.db) J.load();
const it = J.db.items.find(x => x.id === Number(id)); if (it) { it.resolved = true; J.save(); }
return { ok: true };
}
// how many unresolved (for an admin badge)
async function openCount() {
if (db.enabled()) { const r = await db.q('SELECT COUNT(*) n FROM ad_reports WHERE resolved=0'); return r[0].n; }
if (!J.db) J.load();
return J.db.items.filter(x => !x.resolved).length;
}
module.exports = { init, add, list, resolve, openCount, REASONS };