Banner ads: required IAB size picker (width/height stored, shown in row, maps to NAS)
Also lands dormant server-side scaffolding for video ads (type, tiers, validation) — not yet exposed in UI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -105,6 +105,8 @@ async function bootstrap() {
|
||||
// solo message bodies are sanitized rich text; TEXT gives them room
|
||||
await alterSafe('ALTER TABLE campaigns MODIFY body TEXT NULL');
|
||||
await alterSafe('ALTER TABLE campaigns ADD COLUMN cta_label VARCHAR(40) NULL');
|
||||
await alterSafe('ALTER TABLE campaigns ADD COLUMN width INT NULL'); // banner size (IAB) → also NAS width
|
||||
await alterSafe('ALTER TABLE campaigns ADD COLUMN height INT NULL'); // banner size (IAB) → also NAS height
|
||||
await q(`CREATE TABLE IF NOT EXISTS burns (
|
||||
id VARCHAR(32) PRIMARY KEY,
|
||||
member_id INT NOT NULL,
|
||||
|
||||
+7
-2
@@ -323,6 +323,9 @@
|
||||
const r = await (await fetch('/api/my/campaigns')).json();
|
||||
if (r.error) return;
|
||||
lastRates = r.rates;
|
||||
// populate the banner-size dropdown once (ids map to NAS width/height)
|
||||
if (r.bannerSizes && $('cSize') && !$('cSize').options.length)
|
||||
$('cSize').innerHTML = r.bannerSizes.map(s => '<option value="' + s.id + '">' + s.label + '</option>').join('');
|
||||
soloHint();
|
||||
$('rateLine').textContent = 'Available to spend: ' + r.availableCredits.toLocaleString()
|
||||
+ (r.earnedCredits ? ' (' + r.purchasedCredits.toLocaleString() + ' purchased + ' + r.earnedCredits + ' earned)' : '')
|
||||
@@ -337,7 +340,8 @@
|
||||
tbl.className = 'tablewrap';
|
||||
tbl.innerHTML = '<table><thead><tr><th>Name</th><th>Type</th><th class="num">Views</th><th class="num">Clicks</th>'
|
||||
+ '<th class="num">Spent</th><th class="num">Budget</th><th>Status</th><th></th></tr></thead><tbody>'
|
||||
+ r.campaigns.map(c => '<tr><td><b>' + c.name + '</b></td><td>' + c.type + '</td>'
|
||||
+ r.campaigns.map(c => '<tr><td><b>' + c.name + '</b></td>'
|
||||
+ '<td>' + c.type + (c.type === 'banner' && c.width ? ' <span class="muted small">' + c.width + '×' + c.height + '</span>' : '') + '</td>'
|
||||
+ '<td class="num">' + c.imps.toLocaleString() + '</td><td class="num">' + c.clicks + '</td>'
|
||||
+ '<td class="num">' + c.spent + '</td><td class="num">' + c.budget + '</td>'
|
||||
+ '<td>' + (c.status === 'out' ? '<span class="badge amber">budget spent</span>' : c.status) + '</td>'
|
||||
@@ -405,6 +409,7 @@
|
||||
$('cType').addEventListener('change', () => {
|
||||
const t = $('cType').value;
|
||||
$('cImageRow').hidden = t !== 'banner'; // only banners carry a creative; login frames its URL
|
||||
$('cSizeRow').hidden = t !== 'banner';
|
||||
$('cTitleRow').hidden = t !== 'text' && t !== 'solo';
|
||||
$('cBodyRow').hidden = t !== 'text';
|
||||
$('cSoloRow').hidden = t !== 'solo';
|
||||
@@ -417,7 +422,7 @@
|
||||
let soloBody = $('cSoloEd').innerHTML;
|
||||
if ($('cSoloRaw') && !$('cSoloRaw').hidden) soloBody = $('cSoloRaw').value;
|
||||
await api('/api/my/campaigns', { type: $('cType').value, name: $('cName').value,
|
||||
targetUrl: $('cTarget').value, imageUrl: $('cImage').value,
|
||||
targetUrl: $('cTarget').value, imageUrl: $('cImage').value, size: $('cSize').value,
|
||||
title: $('cTitle').value,
|
||||
body: $('cType').value === 'solo' ? soloBody : $('cBody').value,
|
||||
ctaLabel: $('cCtaLabel').value,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<title>The contract | InstantAdPay</title>
|
||||
<meta name="description" content="Plain-language review of the InstantAdPay settlement contract: what it does, what nobody can change, what the operator can and cannot touch, and how to verify all of it yourself.">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Sora:wght@600;700;800&display=swap">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=20260905v">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=20260905w">
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
@@ -129,8 +129,8 @@
|
||||
<div class="small">Advertising services with a performance referral program. Not an investment product; no income guarantees. Crypto transactions are irreversible. Never spend what you cannot afford.</div>
|
||||
</footer>
|
||||
</div>
|
||||
<script src="/assets/common.js?v=20260905v"></script>
|
||||
<script src="/assets/contract.js?v=20260905v"></script>
|
||||
<script src="/assets/chat.js?v=20260905v"></script>
|
||||
<script src="/assets/common.js?v=20260905w"></script>
|
||||
<script src="/assets/contract.js?v=20260905w"></script>
|
||||
<script src="/assets/chat.js?v=20260905w"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+5
-5
@@ -5,7 +5,7 @@
|
||||
<title>InstantAdPay: advertise and earn, locked in code</title>
|
||||
<meta name="description" content="Real ad packages with instant on-chain settlement. Every purchase pays the sponsor line in the same transaction, verifiable by anyone on the live ledger.">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Sora:wght@600;700;800&display=swap">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=20260905v">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=20260905w">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -410,9 +410,9 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script src="/assets/common.js?v=20260905v"></script>
|
||||
<script src="/assets/wallet.js?v=20260905v"></script>
|
||||
<script src="/assets/home.js?v=20260905v"></script>
|
||||
<script src="/assets/chat.js?v=20260905v"></script>
|
||||
<script src="/assets/common.js?v=20260905w"></script>
|
||||
<script src="/assets/wallet.js?v=20260905w"></script>
|
||||
<script src="/assets/home.js?v=20260905w"></script>
|
||||
<script src="/assets/chat.js?v=20260905w"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+4
-4
@@ -5,7 +5,7 @@
|
||||
<title>Live ledger | InstantAdPay</title>
|
||||
<meta name="description" content="Every purchase, payout, and pass-up on InstantAdPay, streamed straight from the blockchain with a verify link on every line.">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Sora:wght@600;700;800&display=swap">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=20260905v">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=20260905w">
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
@@ -25,8 +25,8 @@
|
||||
<div>InstantAdPay · <a href="/">how it works</a> · <a id="contractLink" href="#" target="_blank" rel="noopener">contract source ↗</a></div>
|
||||
</footer>
|
||||
</div>
|
||||
<script src="/assets/common.js?v=20260905v"></script>
|
||||
<script src="/assets/ledger.js?v=20260905v"></script>
|
||||
<script src="/assets/chat.js?v=20260905v"></script>
|
||||
<script src="/assets/common.js?v=20260905w"></script>
|
||||
<script src="/assets/ledger.js?v=20260905w"></script>
|
||||
<script src="/assets/chat.js?v=20260905w"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+6
-5
@@ -4,7 +4,7 @@
|
||||
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Member area | InstantAdPay</title>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Sora:wght@600;700;800&display=swap">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=20260905v">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=20260905w">
|
||||
</head>
|
||||
<body class="bo-body">
|
||||
|
||||
@@ -254,6 +254,7 @@
|
||||
ad viewer, so those URLs must allow framing — we check them the moment you submit. Login and
|
||||
solo links open in a fresh tab, so any working page is fine.</p>
|
||||
<p id="cImageRow"><input id="cImage" placeholder="Image URL (banner/login ads)" style="width:100%"></p>
|
||||
<p id="cSizeRow" hidden><select id="cSize" style="width:100%"></select></p>
|
||||
<p id="cTitleRow" hidden><input id="cTitle" placeholder="Headline (max 60)" style="width:100%"></p>
|
||||
<p id="cBodyRow" hidden><input id="cBody" placeholder="Ad text (max 140)" style="width:100%"></p>
|
||||
<div id="cSoloRow" hidden>
|
||||
@@ -448,9 +449,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/assets/common.js?v=20260905v"></script>
|
||||
<script src="/assets/wallet.js?v=20260905v"></script>
|
||||
<script src="/assets/my.js?v=20260905v"></script>
|
||||
<script src="/assets/chat.js?v=20260905v"></script>
|
||||
<script src="/assets/common.js?v=20260905w"></script>
|
||||
<script src="/assets/wallet.js?v=20260905w"></script>
|
||||
<script src="/assets/my.js?v=20260905w"></script>
|
||||
<script src="/assets/chat.js?v=20260905w"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+3
-3
@@ -4,7 +4,7 @@
|
||||
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Transaction | InstantAdPay</title>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Sora:wght@600;700;800&display=swap">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=20260905v">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=20260905w">
|
||||
</head>
|
||||
<body>
|
||||
<div id="nav"></div>
|
||||
@@ -33,7 +33,7 @@
|
||||
<p><a href="/ledger">← Back to the live ledger</a> · <a href="/contract">Read the contract review</a></p>
|
||||
</div>
|
||||
</section>
|
||||
<script src="/assets/common.js?v=20260905v"></script>
|
||||
<script src="/assets/tx.js?v=20260905v"></script>
|
||||
<script src="/assets/common.js?v=20260905w"></script>
|
||||
<script src="/assets/tx.js?v=20260905w"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Viewing ad — InstantAdPay</title>
|
||||
<link rel="stylesheet" href="/assets/site.css?v=20260905v">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=20260905w">
|
||||
<style>
|
||||
html,body{height:100%;margin:0;overflow:hidden}
|
||||
.vw{display:flex;flex-direction:column;height:100vh;height:100dvh;background:var(--bg,#04110c);color:var(--ink,#e8fff7)}
|
||||
@@ -43,6 +43,6 @@
|
||||
</div>
|
||||
<iframe class="vframe" id="vFrame" sandbox="allow-scripts allow-same-origin allow-forms allow-popups" referrerpolicy="no-referrer" title="Advertiser site"></iframe>
|
||||
</div>
|
||||
<script src="/assets/view.js?v=20260905v"></script>
|
||||
<script src="/assets/view.js?v=20260905w"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+3
-3
@@ -4,7 +4,7 @@
|
||||
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Banner wall | InstantAdPay</title>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Sora:wght@600;700;800&display=swap">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=20260905v">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=20260905w">
|
||||
</head>
|
||||
<body>
|
||||
<div id="nav"></div>
|
||||
@@ -26,7 +26,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<script src="/assets/common.js?v=20260905v"></script>
|
||||
<script src="/assets/wall.js?v=20260905v"></script>
|
||||
<script src="/assets/common.js?v=20260905w"></script>
|
||||
<script src="/assets/wall.js?v=20260905w"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -712,7 +712,7 @@ const server = http.createServer(async (req, res) => {
|
||||
const s = await auth.fromRequest(req);
|
||||
if (!s || !s.email) return json(res, 401, { error: 'Sign in first.' });
|
||||
const memberId = await auth.refreshMemberId(s);
|
||||
const out = { campaigns: await ads.listCampaigns(s.email), rates: ads.rates() };
|
||||
const out = { campaigns: await ads.listCampaigns(s.email), rates: ads.rates(), bannerSizes: ads.bannerSizes() };
|
||||
out.purchasedCredits = memberId ? await ads.availableCredits(memberId) : 0;
|
||||
out.earnedCredits = await ads.earnedBalance(s.email);
|
||||
out.availableCredits = out.purchasedCredits + out.earnedCredits;
|
||||
|
||||
Reference in New Issue
Block a user