Viral links served in place, not redirected: Facebook follows redirects and og:url, so every share was losing the member's name; og:url now carries the ref, canonical stays clean; QA walk updated
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
+7
-3
@@ -120,13 +120,17 @@ if (MODE === 'member' || MODE === 'all') {
|
|||||||
if (!/^https:\/\/instantadpay\.com\/.*[?&]ref=[a-z0-9_]+$/i.test(vl)) note('bug', L + 'promo/viral', 'builder link not rendered: ' + vl);
|
if (!/^https:\/\/instantadpay\.com\/.*[?&]ref=[a-z0-9_]+$/i.test(vl)) note('bug', L + 'promo/viral', 'builder link not rendered: ' + vl);
|
||||||
const me = await page.evaluate(() => fetch('/api/me').then(r => r.json()));
|
const me = await page.evaluate(() => fetch('/api/me').then(r => r.json()));
|
||||||
const tok = me.username || me.refCode;
|
const tok = me.username || me.refCode;
|
||||||
const rr = await page.request.get(LOCAL + '/blog?ref=' + tok + '&x=1', { maxRedirects: 0 });
|
// served in place (no redirect: Facebook drops the name otherwise), cookie on the response, og:url carries the ref
|
||||||
|
const rr = await page.request.get(LOCAL + '/?ref=' + tok + '&x=1', { maxRedirects: 0 });
|
||||||
const sc = (rr.headersArray().filter(h => h.name.toLowerCase() === 'set-cookie').map(h => h.value)).join('; ');
|
const sc = (rr.headersArray().filter(h => h.name.toLowerCase() === 'set-cookie').map(h => h.value)).join('; ');
|
||||||
if (rr.status() !== 302 || rr.headers()['location'] !== '/blog?x=1') note('bug', L + 'viral/redirect', 'expected 302 to /blog?x=1, got ' + rr.status() + ' ' + rr.headers()['location']);
|
const rb = await rr.text();
|
||||||
|
if (rr.status() !== 200 || !/<title>/.test(rb)) note('bug', L + 'viral/inplace', 'expected the page at the decorated address, got ' + rr.status());
|
||||||
if (!new RegExp('iap\\.sponsor=' + tok + ';').test(sc) || !/iap\.angle=page;/.test(sc)) note('bug', L + 'viral/cookie', 'sponsor/angle cookie not set: ' + sc);
|
if (!new RegExp('iap\\.sponsor=' + tok + ';').test(sc) || !/iap\.angle=page;/.test(sc)) note('bug', L + 'viral/cookie', 'sponsor/angle cookie not set: ' + sc);
|
||||||
|
if (!new RegExp('property="og:url" content="[^"]*[?&]ref=' + tok + '"').test(rb)) note('bug', L + 'viral/og', 'og:url does not carry the ref');
|
||||||
const ru = await page.request.get(LOCAL + '/?ref=nobody_zz9', { maxRedirects: 0 });
|
const ru = await page.request.get(LOCAL + '/?ref=nobody_zz9', { maxRedirects: 0 });
|
||||||
const su = (ru.headersArray().filter(h => h.name.toLowerCase() === 'set-cookie').map(h => h.value)).join('; ');
|
const su = (ru.headersArray().filter(h => h.name.toLowerCase() === 'set-cookie').map(h => h.value)).join('; ');
|
||||||
if (ru.status() !== 302 || /iap\.sponsor=/.test(su)) note('bug', L + 'viral/unknown', 'unknown ref must redirect without a sponsor cookie: ' + ru.status() + ' ' + su);
|
const ub = await ru.text();
|
||||||
|
if (ru.status() !== 200 || /iap\.sponsor=/.test(su) || /ref=nobody_zz9/.test(ub)) note('bug', L + 'viral/unknown', 'unknown ref must serve the plain page with no cookie and a clean og:url: ' + ru.status() + ' ' + su);
|
||||||
const rj = await page.request.get(LOCAL + '/join/' + tok + '?ref=' + tok, { maxRedirects: 0 });
|
const rj = await page.request.get(LOCAL + '/join/' + tok + '?ref=' + tok, { maxRedirects: 0 });
|
||||||
if (rj.status() !== 200) note('bug', L + 'viral/join', '/join keeps its own ?ref handling, got ' + rj.status());
|
if (rj.status() !== 200) note('bug', L + 'viral/join', '/join keeps its own ?ref handling, got ' + rj.status());
|
||||||
console.log('ok viral links: builder + redirect + cookie');
|
console.log('ok viral links: builder + redirect + cookie');
|
||||||
|
|||||||
@@ -1078,8 +1078,10 @@ const server = http.createServer(async (req, res) => {
|
|||||||
if (req.method === 'GET' && (TRAFFIC_PAGES.has(p) || /^\/(join|from|wall|blog)\/[^/]+$/.test(p))) traffic.hit(p.startsWith('/blog/') ? '/blog/*' : p, req.headers.referer, req.headers['user-agent']);
|
if (req.method === 'GET' && (TRAFFIC_PAGES.has(p) || /^\/(join|from|wall|blog)\/[^/]+$/.test(p))) traffic.hit(p.startsWith('/blog/') ? '/blog/*' : p, req.headers.referer, req.headers['user-agent']);
|
||||||
// -- viral links (Marty, 2026-09-21): ANY public page + ?ref=<username|code|id> is that member's
|
// -- viral links (Marty, 2026-09-21): ANY public page + ?ref=<username|code|id> is that member's
|
||||||
// referral link. Same 30-day last-touch sponsor cookie as /join, the view counts under the
|
// referral link. Same 30-day last-touch sponsor cookie as /join, the view counts under the
|
||||||
// "page" hook in link stats, then a redirect to the clean URL so canonical links stay clean
|
// "page" hook in link stats. Served IN PLACE, not redirected (2026-09-21): Facebook's crawler follows
|
||||||
// for search engines. /join, /from and the APIs keep their own meaning of ?ref.
|
// redirects and canonicalizes a share to og:url, so the redirect (and a clean og:url) dropped the member's
|
||||||
|
// name from every share. og:url now carries the ref; rel=canonical stays clean for search engines.
|
||||||
|
// /join, /from and the APIs keep their own meaning of ?ref.
|
||||||
if ((req.method === 'GET' || req.method === 'HEAD') && u.searchParams.has('ref') && !p.startsWith('/api/') && !p.startsWith('/admin') && !/^\/(join|from)\//.test(p) && !/\.[a-z0-9]{2,5}$/i.test(p)) {
|
if ((req.method === 'GET' || req.method === 'HEAD') && u.searchParams.has('ref') && !p.startsWith('/api/') && !p.startsWith('/admin') && !/^\/(join|from)\//.test(p) && !/\.[a-z0-9]{2,5}$/i.test(p)) {
|
||||||
const raw = String(u.searchParams.get('ref') || '').trim().toLowerCase().slice(0, 40);
|
const raw = String(u.searchParams.get('ref') || '').trim().toLowerCase().slice(0, 40);
|
||||||
const tok = JOIN_ALIASES[raw] || raw;
|
const tok = JOIN_ALIASES[raw] || raw;
|
||||||
@@ -1095,8 +1097,25 @@ const server = http.createServer(async (req, res) => {
|
|||||||
if (!parseCookies(req)['iap.ref']) set.push('iap.ref=' + encodeURIComponent(coach.refHost(req.headers.referer)) + cookieTail); // first-touch source
|
if (!parseCookies(req)['iap.ref']) set.push('iap.ref=' + encodeURIComponent(coach.refHost(req.headers.referer)) + cookieTail); // first-touch source
|
||||||
if (req.method === 'GET') coach.recordView(tok, 'page', req.headers.referer);
|
if (req.method === 'GET') coach.recordView(tok, 'page', req.headers.referer);
|
||||||
}
|
}
|
||||||
res.writeHead(302, baseHeaders(Object.assign({ Location: clean, 'Cache-Control': 'no-store' }, set.length ? { 'Set-Cookie': set } : {})));
|
if (known) {
|
||||||
return res.end();
|
const wh = res.writeHead.bind(res); let html = false;
|
||||||
|
res.writeHead = function (code, headers) {
|
||||||
|
headers = Object.assign({}, headers || {});
|
||||||
|
if (set.length) headers['Set-Cookie'] = [].concat(headers['Set-Cookie'] || [], set);
|
||||||
|
html = /text\/html/i.test(String(headers['Content-Type'] || '')); if (html) delete headers['Content-Length'];
|
||||||
|
return wh(code, headers);
|
||||||
|
};
|
||||||
|
const end = res.end.bind(res);
|
||||||
|
res.end = function (chunk, enc, cb) {
|
||||||
|
if (html && chunk) {
|
||||||
|
let s = Buffer.isBuffer(chunk) ? chunk.toString('utf8') : String(chunk);
|
||||||
|
s = s.replace(/(<meta property="og:url" content=")([^"]*)(")/, (m, a, url, c) => a + url + (url.includes('?') ? '&' : '?') + 'ref=' + encodeURIComponent(tok) + c);
|
||||||
|
return end(s, 'utf8', cb);
|
||||||
|
}
|
||||||
|
return end(chunk, enc, cb);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
req.url = clean; // the routes below see the clean path; u already has the ref removed
|
||||||
}
|
}
|
||||||
// -- join links: /join/<memberId or share code> — LAST-touch cookie (Marty,
|
// -- join links: /join/<memberId or share code> — LAST-touch cookie (Marty,
|
||||||
// 2026-09-10): the link a visitor opened most recently is the sponsor shown
|
// 2026-09-10): the link a visitor opened most recently is the sponsor shown
|
||||||
|
|||||||
Reference in New Issue
Block a user