From 5b1a60f6531de2342bf699746b2ff717a7924607 Mon Sep 17 00:00:00 2001 From: martbost Date: Mon, 7 Sep 2026 06:13:19 -0500 Subject: [PATCH] Fix profile persistence + badge ribbon alignment; 24h sessions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - BUG: /api/me never returned avatarUrl/bio/socials/lineBanner fields, so the Profile pane repopulated empty on reload — data WAS saved, just not surfaced. Now returned so avatar, bio, and social links persist visibly. - Achievement badge: member name drops the "@" and sits on a per-badge ribbonY (spark .728 / surge .779 / circuit .713 / nexus .709), gold with dark outline. - Sessions now expire in 24h (was 30d) so members re-login daily and see the login ad each day. Co-Authored-By: Claude Opus 4.8 --- auth.js | 2 +- public/assets/my.js | 14 +++++++------- public/my.html | 10 +++++----- server.js | 6 +++++- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/auth.js b/auth.js index 5375231..cd4e2a5 100644 --- a/auth.js +++ b/auth.js @@ -14,7 +14,7 @@ let IS_PROD = false; let SITE = 'instantadpay.com'; const CHALLENGE_TTL = 10 * 60 * 1000; -const SESSION_TTL = 30 * 24 * 60 * 60 * 1000; // 30 days +const SESSION_TTL = 24 * 60 * 60 * 1000; // 24h — forces a daily re-login so the login ad shows each day const challenges = new Map(); // addressLower -> {message, exp} // ---- JSON session fallback ---- diff --git a/public/assets/my.js b/public/assets/my.js index 88e190f..07fd3c4 100644 --- a/public/assets/my.js +++ b/public/assets/my.js @@ -41,11 +41,11 @@ } // achievement badges: the milestone ladder as unlockable medals + share-to-image - const BADGES = [ - { key: 'payouts', label: 'Spark', sub: 'payouts on', img: '/badges/badge-spark.jpg' }, - { key: 'firstBuyer', label: 'Surge', sub: 'first qualifying buyer', img: '/badges/badge-surge.jpg' }, - { key: 'level2', label: 'Circuit', sub: '2 qualifying buyers', img: '/badges/badge-circuit.jpg' }, - { key: 'level3', label: 'Nexus', sub: 'fully qualified', img: '/badges/badge-nexus.jpg' } + const BADGES = [ // ribbonY = vertical center of each badge's name ribbon (art differs per tier) + { key: 'payouts', label: 'Spark', sub: 'payouts on', img: '/badges/badge-spark.jpg', ribbonY: 0.728 }, + { key: 'firstBuyer', label: 'Surge', sub: 'first qualifying buyer', img: '/badges/badge-surge.jpg', ribbonY: 0.779 }, + { key: 'level2', label: 'Circuit', sub: '2 qualifying buyers', img: '/badges/badge-circuit.jpg', ribbonY: 0.713 }, + { key: 'level3', label: 'Nexus', sub: 'fully qualified', img: '/badges/badge-nexus.jpg', ribbonY: 0.709 } ]; let lastMilestones = []; function renderBadges(d) { @@ -74,7 +74,7 @@ // composite the ornate badge template with the member's @username on the ribbon function shareBadge(key, d) { const b = BADGES.find(x => x.key === key); if (!b) return; - const who = d.username ? '@' + d.username : d.memberId ? 'member #' + d.memberId : ''; + const who = d.username ? d.username : d.memberId ? 'member #' + d.memberId : ''; const img = new Image(); img.onload = () => { const c = document.createElement('canvas'); c.width = img.width; c.height = img.height; @@ -84,7 +84,7 @@ x.textAlign = 'center'; x.textBaseline = 'middle'; x.font = 'bold ' + Math.round(c.width * 0.055) + 'px Sora, "Segoe UI", sans-serif'; - const y = c.height * 0.82; + const y = c.height * (b.ribbonY || 0.75); x.lineJoin = 'round'; x.lineWidth = Math.max(3, Math.round(c.width * 0.008)); x.strokeStyle = 'rgba(4,20,15,.9)'; diff --git a/public/my.html b/public/my.html index 172ddcd..39a824c 100644 --- a/public/my.html +++ b/public/my.html @@ -4,7 +4,7 @@ Member area | InstantAdPay - + @@ -627,9 +627,9 @@ - - - - + + + + diff --git a/server.js b/server.js index a6a2467..27be9bd 100644 --- a/server.js +++ b/server.js @@ -432,7 +432,11 @@ const server = http.createServer(async (req, res) => { const out = { signedIn: true, email: s.email || (acct && acct.email) || null, address: s.address || (acct && acct.address) || null, memberId, username: (acct && acct.username) || null, - refCode: (acct && acct.code) || null, sponsorId }; + refCode: (acct && acct.code) || null, sponsorId, + // profile + line-banner fields so the Profile pane repopulates on reload (were being saved but not returned) + avatarUrl: (acct && acct.avatarUrl) || null, bio: (acct && acct.bio) || null, + socials: (acct && acct.socials) || null, + lineBannerUrl: (acct && acct.lineBannerUrl) || null, lineTargetUrl: (acct && acct.lineTargetUrl) || null }; if (memberId) { try { const mm = await chain.member(memberId);