fix(analytics): stop identifying Public visitors as page creators

Public generated pages were calling umami.identify(owner_id), collapsing
every visitor into the creator session. Anonymous visits now skip identify;
logged-in viewers identify with their own distinct id.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-08-05 13:05:18 +08:00
parent 9e50681d96
commit e2014e05e6
4 changed files with 83 additions and 10 deletions
+17 -1
View File
@@ -68,6 +68,20 @@ export function resolveAnalyticsOwnerLabel(user = {}) {
return label.replace(/[\r\n\t]+/g, ' ').slice(0, 80) || '未命名用户';
}
export function buildViewerAnalyticsIdentity(viewer, config = {}) {
if (!viewer?.id) return null;
const distinctId = resolveAnalyticsIdentity(viewer.id, config);
if (!distinctId) return null;
return {
distinctId,
username: resolveAnalyticsOwnerLabel(viewer),
ownerSegment: resolveAnalyticsOwnerSegment(viewer),
planType: resolveAnalyticsPlan(viewer),
channel: 'public',
identityMode: config.identityMode === 'raw' ? 'raw' : 'pseudonymous',
};
}
export function buildProductAnalyticsContext({ config, user = null } = {}) {
if (!config?.enabled || !config.websiteId) return { enabled: false };
const distinctId = user?.id ? resolveAnalyticsIdentity(user.id, config) : '';
@@ -152,6 +166,7 @@ export function injectMindSpaceAnalytics(html, {
planType = 'unknown',
generatedAt = '',
channel = 'h5',
viewerIdentity = null,
config = resolveMindSpaceAnalyticsConfig(),
} = {}) {
const source = String(html ?? '');
@@ -167,7 +182,8 @@ export function injectMindSpaceAnalytics(html, {
`data-host-url="${config.hostPath}"`,
];
if (config.domains) attrs.push(`data-domains="${config.domains.replaceAll('"', '&quot;')}"`);
const block = `<script ${attrs.join(' ')} defer src="${config.scriptPath}"></script><script ${ANALYTICS_MARKER}>(function(){var d=${jsonForInlineScript(metadata)},seen={};function safeTarget(h){if(!h)return'';try{var u=new URL(h,location.href);return u.origin===location.origin?u.pathname:'external:'+u.hostname;}catch{return'';}}function t(n,x){if(!window.umami||typeof window.umami.track!=='function')return;var p=Object.assign({},d,{page_url:location.href,page_route:location.pathname,page_title:document.title},x||{});window.umami.track(n,p);}function identify(){if(!window.umami||typeof window.umami.identify!=='function')return;window.umami.identify(d.owner_id,{username:d.username,memind_page_url:location.href,owner_segment:d.owner_segment,plan_type:d.plan_type,channel:d.channel,surface:d.surface,identity_mode:d.identity_mode});}function pageview(){if(!window.umami||typeof window.umami.track!=='function')return;window.umami.track();}function once(n,x){if(seen[n])return;seen[n]=1;t(n,x);}function ready(){identify();pageview();document.addEventListener('click',function(e){var el=e.target&&e.target.closest?e.target.closest('a,button,[role="button"],[data-umami-event]'):null;if(!el)return;var custom=el.getAttribute('data-umami-event');var href=el.tagName==='A'?el.getAttribute('href')||'':'';t(custom||'page_click',{element:el.tagName.toLowerCase(),element_id:el.id||'',event_label:(custom||el.getAttribute('aria-label')||'').slice(0,100),target_path:safeTarget(href)});},{passive:true});document.addEventListener('submit',function(e){var form=e.target;t('page_form_submit',{form_id:form&&form.id||'',form_action:safeTarget(form&&form.getAttribute('action')||'')});},{passive:true});var marks=[25,50,75,90];function scroll(){var h=document.documentElement.scrollHeight-window.innerHeight;if(h<=0){once('page_scroll_100');return;}var pct=Math.round(window.scrollY/h*100);marks.forEach(function(m){if(pct>=m)once('page_scroll_'+m);});}window.addEventListener('scroll',scroll,{passive:true});setTimeout(function(){once('page_engaged_10s');},10000);setTimeout(function(){once('page_engaged_30s');},30000);scroll();}if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',ready,{once:true});}else{ready();}})();</script>`;
const viewerJson = viewerIdentity ? jsonForInlineScript(viewerIdentity) : 'null';
const block = `<script ${attrs.join(' ')} defer src="${config.scriptPath}"></script><script ${ANALYTICS_MARKER}>(function(){var d=${jsonForInlineScript(metadata)},v=${viewerJson},seen={};function safeTarget(h){if(!h)return'';try{var u=new URL(h,location.href);return u.origin===location.origin?u.pathname:'external:'+u.hostname;}catch{return'';}}function t(n,x){if(!window.umami||typeof window.umami.track!=='function')return;var p=Object.assign({},d,{page_url:location.href,page_route:location.pathname,page_title:document.title},x||{});window.umami.track(n,p);}function identifyViewer(){if(!v||!v.distinctId||!window.umami||typeof window.umami.identify!=='function')return;window.umami.identify(v.distinctId,{username:v.username||'',memind_page_url:location.href,owner_segment:v.ownerSegment||'',plan_type:v.planType||'',channel:v.channel||'public',surface:'generated_page',identity_mode:v.identityMode||'pseudonymous'});}function pageview(){if(!window.umami||typeof window.umami.track!=='function')return;window.umami.track();}function once(n,x){if(seen[n])return;seen[n]=1;t(n,x);}function ready(){identifyViewer();pageview();document.addEventListener('click',function(e){var el=e.target&&e.target.closest?e.target.closest('a,button,[role="button"],[data-umami-event]'):null;if(!el)return;var custom=el.getAttribute('data-umami-event');var href=el.tagName==='A'?el.getAttribute('href')||'':'';t(custom||'page_click',{element:el.tagName.toLowerCase(),element_id:el.id||'',event_label:(custom||el.getAttribute('aria-label')||'').slice(0,100),target_path:safeTarget(href)});},{passive:true});document.addEventListener('submit',function(e){var form=e.target;t('page_form_submit',{form_id:form&&form.id||'',form_action:safeTarget(form&&form.getAttribute('action')||'')});},{passive:true});var marks=[25,50,75,90];function scroll(){var h=document.documentElement.scrollHeight-window.innerHeight;if(h<=0){once('page_scroll_100');return;}var pct=Math.round(window.scrollY/h*100);marks.forEach(function(m){if(pct>=m)once('page_scroll_'+m);});}window.addEventListener('scroll',scroll,{passive:true});setTimeout(function(){once('page_engaged_10s');},10000);setTimeout(function(){once('page_engaged_30s');},30000);scroll();}if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',ready,{once:true});}else{ready();}})();</script>`;
if (/<\/head>/i.test(source)) return source.replace(/<\/head>/i, `${block}</head>`);
return source.replace(/<body\b/i, `${block}<body`);
}