Admin: wall fallback ads editor (House ads pane) + /api/admin/wall-ads

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-09-09 15:45:49 -05:00
parent 2ac6549171
commit b86dbfd9e9
3 changed files with 79 additions and 1 deletions
+24
View File
@@ -1543,6 +1543,30 @@ const server = http.createServer(async (req, res) => {
try { const r = await drip.sendStep(ADMIN_EMAIL, Number(b.step) || 0, ADMIN_EMAIL); return json(res, r.error ? 400 : 200, r); }
catch (e) { return json(res, 502, { error: 'Send failed: ' + e.message }); }
}
// wall fallback ads: shown in wall positions a member has not earned or filled, when no upline banner exists
if (p === '/api/admin/wall-ads' && req.method === 'GET') {
if (!isAdmin(req)) return json(res, 401, { error: 'auth' });
let saved = null; try { saved = JSON.parse(fs.readFileSync(path.join(DATA_DIR, 'admin-wall-ads.json'), 'utf8')); } catch (e) {}
return json(res, 200, { ads: Array.isArray(saved) ? saved : [], defaults: getAdminWallAds(), usingDefaults: !Array.isArray(saved) || !saved.length });
}
if (p === '/api/admin/wall-ads' && req.method === 'PATCH') {
if (!isAdmin(req)) return json(res, 401, { error: 'auth' });
const b = await readBody(req);
const src = Array.isArray(b.ads) ? b.ads.slice(0, 20) : [];
const out = [];
for (const o of src) {
const name = String((o && o.name) || '').trim().slice(0, 60);
const targetUrl = String((o && o.targetUrl) || '').trim();
const bannerUrl = String((o && o.bannerUrl) || '').trim();
if (!targetUrl) continue;
if (!/^https:\/\/[^\s]+$/i.test(targetUrl)) return json(res, 400, { error: 'Every wall ad needs an https:// link (' + (name || targetUrl) + ').' });
if (bannerUrl && !/^(\/uploads\/[a-z0-9]{24}\.(png|jpg|webp|gif)|https:\/\/[^\s]+)$/i.test(bannerUrl)) return json(res, 400, { error: 'Banner must be an uploaded image or an https image URL (' + (name || targetUrl) + ').' });
out.push({ name: name || 'InstantAdPay', targetUrl, bannerUrl: bannerUrl || null });
}
const file = path.join(DATA_DIR, 'admin-wall-ads.json');
if (out.length) fs.writeFileSync(file, JSON.stringify(out, null, 2)); else { try { fs.unlinkSync(file); } catch (e) {} }
return json(res, 200, { ok: true, ads: out, usingDefaults: !out.length });
}
if (p === '/api/admin/site' && req.method === 'GET') {
if (!isAdmin(req)) return json(res, 401, { error: 'auth' });
return json(res, 200, { site: siteConfig() });