Ad reporting + featured rotates one link + clear campaign form on submit
- Ad reporting (auto-approved ads need a safety valve): new reports.js store (dual-mode) + ad_reports table + POST /api/report-ad; a "⚠ report" control on served ad slots opens a reason prompt and notifies the admin by email (siteConfig.adminEmail / ADMIN_EMAIL). - Featured links now show ONE link at a time and cycle (true rotation), instead of listing all links at once. - Campaign form now clears EVERY field on submit (target/creative/title/etc.). A leftover target URL was carrying into the next campaign — the same reason a video short was saved with the wrong (massifly) CTA target. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,7 @@ const accounts = require('./accounts');
|
||||
const ads = require('./ads');
|
||||
const mailer = require('./mailer');
|
||||
const messages = require('./messages');
|
||||
const reports = require('./reports');
|
||||
const spaces = require('./spaces'); // DO Spaces video storage (inert unless DO_SPACES_* set)
|
||||
let QR = null; try { QR = require('qrcode'); } catch (e) { /* optional */ }
|
||||
const chatbot = require('./chatbot');
|
||||
@@ -126,6 +127,7 @@ async function boot() {
|
||||
ads.init({ dataDir: DATA_DIR, chain });
|
||||
mailer.init({ dataDir: DATA_DIR });
|
||||
messages.init({ dataDir: DATA_DIR });
|
||||
reports.init({ dataDir: DATA_DIR });
|
||||
chatbot.init({ dataDir: DATA_DIR, chain });
|
||||
setTimeout(() => ads.dailySweep().catch(e => console.error('sweep', e.message)), 60 * 1000);
|
||||
setInterval(() => ads.dailySweep().catch(e => console.error('sweep', e.message)), 60 * 60 * 1000);
|
||||
@@ -705,6 +707,24 @@ const server = http.createServer(async (req, res) => {
|
||||
return json(res, 200, { ok: true, balanceWei: BigInt(nb || '0x0').toString() });
|
||||
} catch (e) { return json(res, 502, { error: 'Faucet is unavailable right now.' }); }
|
||||
}
|
||||
// -- report an ad (auto-approved ads need a safety valve): store + notify admin
|
||||
if (p === '/api/report-ad' && req.method === 'POST') {
|
||||
const b = await readBody(req);
|
||||
if (!Number(b.campaignId)) return json(res, 400, { error: 'Which ad?' });
|
||||
const s = await auth.fromRequest(req);
|
||||
const who = (s && s.email) || '';
|
||||
const rec = await reports.add(b.campaignId, who, b.reason, b.note);
|
||||
try {
|
||||
const adminEmail = siteConfig().adminEmail || process.env.ADMIN_EMAIL || '';
|
||||
if (adminEmail && mailer.hasKey()) {
|
||||
mailer.send(adminEmail, 'Ad reported on InstantAdPay (campaign #' + rec.campaignId + ')',
|
||||
'A member flagged an ad.\n\nCampaign: #' + rec.campaignId + '\nReason: ' + rec.reason
|
||||
+ '\nReported by: ' + (who || 'anonymous') + '\nNote: ' + (String(b.note || '').slice(0, 500) || '(none)')
|
||||
+ '\n\nPause or review it from the admin.').catch(() => {});
|
||||
}
|
||||
} catch (e) {}
|
||||
return json(res, 200, { ok: true });
|
||||
}
|
||||
if (p === '/api/my/chat/send' && req.method === 'POST') {
|
||||
const s = await auth.fromRequest(req);
|
||||
if (!s || !s.email) return json(res, 401, { error: 'Sign in first.' });
|
||||
|
||||
Reference in New Issue
Block a user