import crypto from 'node:crypto'; const ANALYTICS_MARKER = 'data-memind-analytics="1"'; export function resolveMindSpaceAnalyticsConfig(env = process.env) { const enabled = String(env.MEMIND_ANALYTICS_ENABLED ?? '').toLowerCase() === 'true'; const websiteId = String(env.MEMIND_ANALYTICS_WEBSITE_ID ?? '').trim(); const secret = String(env.MEMIND_ANALYTICS_ID_SECRET ?? '').trim(); return { enabled: enabled && Boolean(websiteId) && Boolean(secret), websiteId, idSecret: secret, analyticsUrl: String(env.MEMIND_ANALYTICS_URL ?? 'http://127.0.0.1:3100').trim() || 'http://127.0.0.1:3100', scriptPath: String(env.MEMIND_ANALYTICS_SCRIPT_PATH ?? '/analytics/script.js').trim() || '/analytics/script.js', hostPath: String(env.MEMIND_ANALYTICS_HOST_PATH ?? '/analytics').trim() || '/analytics', domains: String(env.MEMIND_ANALYTICS_DOMAINS ?? '').trim(), }; } export function pseudonymizeAnalyticsId(value, secret) { const normalized = String(value ?? '').trim(); const key = String(secret ?? '').trim(); if (!normalized || !key) return ''; return crypto.createHmac('sha256', key).update(normalized).digest('hex').slice(0, 32); } export function resolveAnalyticsOwnerSegment(user = {}) { if (user?.role === 'admin') return 'admin'; const plan = String(user?.planType ?? user?.plan_type ?? 'free').trim().toLowerCase(); return `plan:${plan || 'free'}`; } export function resolveAnalyticsOwnerLabel(user = {}) { const label = String(user?.displayName ?? user?.display_name ?? user?.username ?? '').trim(); return label.replace(/[\r\n\t]+/g, ' ').slice(0, 80) || '未命名用户'; } export function sendMindSpaceAnalyticsEvent({ config, eventName, ownerId, pageId = '', publicationId = '', agentRunId = '', channel = 'h5', ownerSegment = 'unknown', ownerLabel = '未命名用户', url = '', } = {}) { if (!config?.enabled || !config.websiteId || !config.idSecret || !eventName) return Promise.resolve(false); const owner = pseudonymizeAnalyticsId(ownerId, config.idSecret); if (!owner) return Promise.resolve(false); const endpoint = `${String(config.analyticsUrl || 'http://127.0.0.1:3100').replace(/\/$/, '')}/api/send`; const payload = { website: config.websiteId, hostname: '127.0.0.1', url: url || '/', name: String(eventName), data: { owner_id: owner, page_id: String(pageId || ''), publication_id: String(publicationId || ''), agent_run_id: String(agentRunId || ''), channel, owner_segment: String(ownerSegment || 'unknown'), owner_label: resolveAnalyticsOwnerLabel({ displayName: ownerLabel }), }, }; return fetch(endpoint, { method: 'POST', headers: { 'content-type': 'application/json', 'user-agent': 'Memind/local-analytics' }, body: JSON.stringify({ type: 'event', payload }), signal: AbortSignal.timeout(1500), }).then((response) => response.ok).catch(() => false); } function jsonForInlineScript(value) { return JSON.stringify(value) .replaceAll('<', '\\u003c') .replaceAll('>', '\\u003e') .replaceAll('&', '\\u0026'); } export function injectMindSpaceAnalytics(html, { ownerId, pageId = '', publicationId = '', ownerSegment = 'unknown', ownerLabel = '未命名用户', channel = 'h5', config = resolveMindSpaceAnalyticsConfig(), } = {}) { const source = String(html ?? ''); if (!config?.enabled || !config.websiteId || !/^\s*(`; if (/<\/head>/i.test(source)) return source.replace(/<\/head>/i, `${block}`); return source.replace(/