Merge branch 'feature_bugfix/20260116_V2' of https://git.valuefrontier.cn/vf/vf_react into feature_bugfix/20260116_V2
This commit is contained in:
@@ -20,11 +20,16 @@ REACT_APP_API_URL=https://api.valuefrontier.cn
|
||||
# PostHog 分析配置(生产环境)
|
||||
# PostHog API Key(从 PostHog 项目设置中获取)
|
||||
REACT_APP_POSTHOG_KEY=phc_xKlRyG69Bx7hgOdFeCeLUvQWvSjw18ZKFgCwCeYezWF
|
||||
# PostHog API Host(使用 PostHog Cloud)
|
||||
REACT_APP_POSTHOG_HOST=https://app.posthog.com
|
||||
# PostHog API Host(自托管实例)
|
||||
REACT_APP_POSTHOG_HOST=http://101.43.133.214:7050
|
||||
# 启用会话录制(Session Recording)用于回放用户操作、排查问题
|
||||
REACT_APP_ENABLE_SESSION_RECORDING=true
|
||||
|
||||
# PostHog Cloud 双写配置(验证自托管有效性 + Cloud 兜底)
|
||||
REACT_APP_POSTHOG_CLOUD_KEY=phc_xxxxxxx
|
||||
REACT_APP_POSTHOG_CLOUD_HOST=https://us.posthog.com
|
||||
REACT_APP_POSTHOG_DUAL_WRITE=true
|
||||
|
||||
# React 构建优化配置
|
||||
# 禁用 source map 生成(生产环境不需要,提升打包速度和安全性)
|
||||
GENERATE_SOURCEMAP=false
|
||||
|
||||
@@ -8,6 +8,7 @@ let isInitialized = false;
|
||||
/**
|
||||
* Initialize PostHog SDK
|
||||
* Should be called once when the app starts
|
||||
* 支持双写模式:同时发送到自托管和 Cloud
|
||||
*/
|
||||
export const initPostHog = () => {
|
||||
// 开发环境禁用 PostHog(减少日志噪音,仅生产环境启用)
|
||||
@@ -26,6 +27,7 @@ export const initPostHog = () => {
|
||||
|
||||
const apiKey = process.env.REACT_APP_POSTHOG_KEY;
|
||||
const apiHost = process.env.REACT_APP_POSTHOG_HOST || 'https://app.posthog.com';
|
||||
const dualWrite = process.env.REACT_APP_POSTHOG_DUAL_WRITE === 'true';
|
||||
|
||||
if (!apiKey) {
|
||||
console.warn('⚠️ PostHog API key not found. Analytics will be disabled.');
|
||||
@@ -35,6 +37,7 @@ export const initPostHog = () => {
|
||||
isInitializing = true;
|
||||
|
||||
try {
|
||||
// 主实例(自托管)
|
||||
posthog.init(apiKey, {
|
||||
api_host: apiHost,
|
||||
|
||||
@@ -94,6 +97,26 @@ export const initPostHog = () => {
|
||||
},
|
||||
});
|
||||
|
||||
// Cloud 实例(双写备份)
|
||||
if (dualWrite) {
|
||||
const cloudKey = process.env.REACT_APP_POSTHOG_CLOUD_KEY;
|
||||
const cloudHost = process.env.REACT_APP_POSTHOG_CLOUD_HOST || 'https://us.posthog.com';
|
||||
|
||||
if (cloudKey && cloudKey !== 'phc_xxxxxxx') {
|
||||
posthog.init(cloudKey, {
|
||||
api_host: cloudHost,
|
||||
capture_pageview: true,
|
||||
capture_pageleave: true,
|
||||
autocapture: true,
|
||||
session_recording: { enabled: false }, // Cloud 不需要录屏,节省流量
|
||||
}, 'cloud'); // 第三个参数是实例名称
|
||||
|
||||
console.log('[PostHog] 双写模式已启用:自托管 + Cloud');
|
||||
} else {
|
||||
console.warn('[PostHog] 双写模式已启用但 Cloud API Key 未配置');
|
||||
}
|
||||
}
|
||||
|
||||
isInitialized = true;
|
||||
} catch (error) {
|
||||
// 忽略 AbortError(通常由热重载或快速导航引起)
|
||||
@@ -116,6 +139,7 @@ export const getPostHog = () => {
|
||||
/**
|
||||
* Identify user with PostHog
|
||||
* Call this after successful login/registration
|
||||
* 双写模式下同时同步到 Cloud
|
||||
*
|
||||
* @param {string} userId - Unique user identifier
|
||||
* @param {object} userProperties - User properties (email, name, subscription_tier, etc.)
|
||||
@@ -126,16 +150,31 @@ export const identifyUser = (userId, userProperties = {}) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const properties = {
|
||||
email: userProperties.email,
|
||||
username: userProperties.username,
|
||||
subscription_tier: userProperties.subscription_tier || 'free',
|
||||
role: userProperties.role,
|
||||
registration_date: userProperties.registration_date,
|
||||
last_login: new Date().toISOString(),
|
||||
...userProperties,
|
||||
};
|
||||
|
||||
try {
|
||||
posthog.identify(userId, {
|
||||
email: userProperties.email,
|
||||
username: userProperties.username,
|
||||
subscription_tier: userProperties.subscription_tier || 'free',
|
||||
role: userProperties.role,
|
||||
registration_date: userProperties.registration_date,
|
||||
last_login: new Date().toISOString(),
|
||||
...userProperties,
|
||||
});
|
||||
// 主实例
|
||||
posthog.identify(userId, properties);
|
||||
|
||||
// 双写到 Cloud
|
||||
if (process.env.REACT_APP_POSTHOG_DUAL_WRITE === 'true') {
|
||||
try {
|
||||
const cloudInstance = posthog.getInstance('cloud');
|
||||
if (cloudInstance) {
|
||||
cloudInstance.identify(userId, properties);
|
||||
}
|
||||
} catch (e) {
|
||||
// 静默失败,不影响主流程
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ User identification failed:', error);
|
||||
}
|
||||
@@ -157,16 +196,32 @@ export const setUserProperties = (properties) => {
|
||||
|
||||
/**
|
||||
* Track custom event
|
||||
* 双写模式下同时发送到 Cloud
|
||||
*
|
||||
* @param {string} eventName - Name of the event
|
||||
* @param {object} properties - Event properties
|
||||
*/
|
||||
export const trackEvent = (eventName, properties = {}) => {
|
||||
const eventProperties = {
|
||||
...properties,
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
|
||||
try {
|
||||
posthog.capture(eventName, {
|
||||
...properties,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
// 主实例
|
||||
posthog.capture(eventName, eventProperties);
|
||||
|
||||
// 双写到 Cloud
|
||||
if (process.env.REACT_APP_POSTHOG_DUAL_WRITE === 'true') {
|
||||
try {
|
||||
const cloudInstance = posthog.getInstance('cloud');
|
||||
if (cloudInstance) {
|
||||
cloudInstance.capture(eventName, eventProperties);
|
||||
}
|
||||
} catch (e) {
|
||||
// 静默失败,不影响主流程
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ Event tracking failed:', error);
|
||||
}
|
||||
@@ -220,10 +275,24 @@ export const trackPageView = (pagePath, properties = {}) => {
|
||||
/**
|
||||
* Reset user session
|
||||
* Call this on logout
|
||||
* 双写模式下同时重置 Cloud 实例
|
||||
*/
|
||||
export const resetUser = () => {
|
||||
try {
|
||||
// 主实例
|
||||
posthog.reset();
|
||||
|
||||
// 双写:重置 Cloud 实例
|
||||
if (process.env.REACT_APP_POSTHOG_DUAL_WRITE === 'true') {
|
||||
try {
|
||||
const cloudInstance = posthog.getInstance('cloud');
|
||||
if (cloudInstance) {
|
||||
cloudInstance.reset();
|
||||
}
|
||||
} catch (e) {
|
||||
// 静默失败
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ Session reset failed:', error);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user