feat(analytics): retire Rybbit and add SEO/GEO discovery tracking
Document Umami as the sole analytics platform, remove Rybbit proxy and tracker code, and classify public page traffic with discovery_channel for adm SEO/GEO dashboards. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import { ensureMindSpaceConfig, loadMindSpaceConfig, loadMindSpaceConfigCached } from '../mindspace-config.mjs';
|
||||
import { resolveMindSpaceRybbitConfig } from '../mindspace-rybbit.mjs';
|
||||
import { createMindSearchConfigService } from '../mindsearch-config.mjs';
|
||||
import { createMindSpaceService } from '../mindspace.mjs';
|
||||
import {
|
||||
@@ -109,10 +108,6 @@ export async function bootstrapPortalDomainServices({
|
||||
scriptPath: '/analytics/script.js',
|
||||
};
|
||||
}
|
||||
const resolvedRybbitConfig = resolveMindSpaceRybbitConfig(env, {
|
||||
idSecret: storedMindSpaceConfig?.analytics?.idSecret ?? '',
|
||||
});
|
||||
|
||||
const scheduleService = createScheduleServiceFn(pool, {
|
||||
defaultTimezone:
|
||||
env.H5_DEFAULT_TIMEZONE || 'Asia/Shanghai',
|
||||
@@ -289,7 +284,6 @@ export async function bootstrapPortalDomainServices({
|
||||
return {
|
||||
mindSearchConfigService,
|
||||
mindSpaceAnalyticsConfig: resolvedAnalyticsConfig,
|
||||
mindSpaceRybbitConfig: resolvedRybbitConfig,
|
||||
scheduleService,
|
||||
scheduledTaskService,
|
||||
pageDataService,
|
||||
|
||||
@@ -61,8 +61,6 @@ function createBootstrapSetup(overrides = {}) {
|
||||
env: {
|
||||
H5_DEFAULT_TIMEZONE: 'Asia/Singapore',
|
||||
PLAZA_REDIS_URL: 'redis://plaza',
|
||||
MEMIND_RYBBIT_ENABLED: 'true',
|
||||
MEMIND_RYBBIT_SITE_ID: '2',
|
||||
},
|
||||
runtime: {
|
||||
maxFileBytes: 100,
|
||||
@@ -325,9 +323,6 @@ test('domain bootstrap preserves schema and service assembly order', async () =>
|
||||
result.mindSpaceAnalyticsConfig.scriptPath,
|
||||
'/analytics/script.js',
|
||||
);
|
||||
assert.equal(result.mindSpaceRybbitConfig.enabled, true);
|
||||
assert.equal(result.mindSpaceRybbitConfig.siteId, '2');
|
||||
assert.equal(result.mindSpaceRybbitConfig.idSecret, 'secret-1');
|
||||
});
|
||||
|
||||
test('domain bootstrap preserves delayed user, snapshot, and Page Data dependencies', async () => {
|
||||
|
||||
@@ -4,9 +4,6 @@ import {
|
||||
resolveAnalyticsPlan,
|
||||
sendMindSpaceAnalyticsEvent,
|
||||
} from '../mindspace-analytics.mjs';
|
||||
import {
|
||||
sendMindSpaceRybbitEvent,
|
||||
} from '../mindspace-rybbit.mjs';
|
||||
import { createPageEditSessionService } from '../mindspace-page-edit-session.mjs';
|
||||
import { createNotificationDispatcher } from '../notification-dispatcher.mjs';
|
||||
import { startScheduleReminderWorker } from '../schedule-reminder-worker.mjs';
|
||||
@@ -39,7 +36,6 @@ export async function bootstrapPortalIntegrationServices({
|
||||
chatIntentRouter,
|
||||
systemDisclosurePolicyService,
|
||||
mindSpaceAnalyticsConfig,
|
||||
mindSpaceRybbitConfig,
|
||||
subscriptionService,
|
||||
apiTarget,
|
||||
apiSecret,
|
||||
@@ -54,8 +50,6 @@ export async function bootstrapPortalIntegrationServices({
|
||||
resolveAnalyticsPlanFn = resolveAnalyticsPlan,
|
||||
sendMindSpaceAnalyticsEventFn =
|
||||
sendMindSpaceAnalyticsEvent,
|
||||
sendMindSpaceRybbitEventFn =
|
||||
sendMindSpaceRybbitEvent,
|
||||
createNotificationDispatcherFn =
|
||||
createNotificationDispatcher,
|
||||
startScheduleReminderWorkerFn =
|
||||
@@ -214,10 +208,6 @@ export async function bootstrapPortalIntegrationServices({
|
||||
config: mindSpaceAnalyticsConfig,
|
||||
...analyticsPayload,
|
||||
});
|
||||
void sendMindSpaceRybbitEventFn({
|
||||
config: mindSpaceRybbitConfig,
|
||||
...analyticsPayload,
|
||||
});
|
||||
}
|
||||
},
|
||||
applySessionLlmProvider: (sessionId) =>
|
||||
|
||||
@@ -124,7 +124,6 @@ function createSetup(overrides = {}) {
|
||||
},
|
||||
sessionSnapshotService,
|
||||
mindSpaceAnalyticsConfig: { enabled: true },
|
||||
mindSpaceRybbitConfig: { enabled: true },
|
||||
subscriptionService,
|
||||
apiTarget: 'http://api',
|
||||
apiSecret: 'secret',
|
||||
@@ -164,10 +163,6 @@ function createSetup(overrides = {}) {
|
||||
calls.push(['analytics', event]);
|
||||
return Promise.resolve();
|
||||
},
|
||||
sendMindSpaceRybbitEventFn(event) {
|
||||
calls.push(['rybbit', event]);
|
||||
return Promise.resolve();
|
||||
},
|
||||
createNotificationDispatcherFn(receivedOptions) {
|
||||
calls.push(['notification-dispatcher']);
|
||||
notificationOptions = receivedOptions;
|
||||
@@ -425,13 +420,6 @@ test('preserves generated-page analytics projection', async () => {
|
||||
analyticsCall[1].generatedAt,
|
||||
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/,
|
||||
);
|
||||
const rybbitCall = setup.calls.find(
|
||||
([name]) => name === 'rybbit',
|
||||
);
|
||||
assert.deepEqual(rybbitCall[1], {
|
||||
...analyticsCall[1],
|
||||
config: { enabled: true },
|
||||
});
|
||||
});
|
||||
|
||||
test('preserves notification, recharge, reminder, and page-edit wiring', async () => {
|
||||
|
||||
@@ -13,8 +13,9 @@ import {
|
||||
resolveAnalyticsPlan,
|
||||
} from '../mindspace-analytics.mjs';
|
||||
import {
|
||||
injectMindSpaceRybbit,
|
||||
} from '../mindspace-rybbit.mjs';
|
||||
appendDiscoveryPropagationParams,
|
||||
classifyDiscoveryTrafficSource,
|
||||
} from '../mindspace-analytics-discovery.mjs';
|
||||
import {
|
||||
rewriteBrokenMindSpacePublicImageUrls,
|
||||
rewritePublicationCanonicalAssetUrls,
|
||||
@@ -58,8 +59,8 @@ async function decoratePublicationHtmlAnalytics(
|
||||
{
|
||||
result,
|
||||
analyticsConfig,
|
||||
rybbitConfig,
|
||||
viewer = null,
|
||||
referrerHint = '',
|
||||
getAuthPool = () => null,
|
||||
getUserAuth = () => null,
|
||||
getMindSpacePages = () => null,
|
||||
@@ -122,17 +123,9 @@ async function decoratePublicationHtmlAnalytics(
|
||||
pageId,
|
||||
publicationId,
|
||||
viewerIdentity: buildViewerAnalyticsIdentity(viewer, analyticsConfig),
|
||||
referrerHint,
|
||||
config: analyticsConfig,
|
||||
});
|
||||
decorated = injectMindSpaceRybbit(decorated, {
|
||||
ownerId: result?.ownerId ?? '',
|
||||
ownerSegment,
|
||||
ownerLabel,
|
||||
pageId,
|
||||
publicationId,
|
||||
channel: 'publication',
|
||||
config: rybbitConfig,
|
||||
});
|
||||
return decorated;
|
||||
}
|
||||
|
||||
@@ -141,7 +134,6 @@ export function createPortalPublishedPageDelivery({
|
||||
renderLongImage =
|
||||
renderLongImageBuffer,
|
||||
analyticsConfig = { enabled: false },
|
||||
rybbitConfig = { enabled: false },
|
||||
getAuthPool = () => null,
|
||||
getUserAuth = () => null,
|
||||
getMindSpacePages = () => null,
|
||||
@@ -179,6 +171,9 @@ export function createPortalPublishedPageDelivery({
|
||||
);
|
||||
}
|
||||
const origin = resolveRequestOrigin(req);
|
||||
const referrerHint = String(
|
||||
req.headers.referer ?? req.headers.referrer ?? req.get?.('referer') ?? '',
|
||||
).trim();
|
||||
const originalPath =
|
||||
req.originalUrl || req.url || '';
|
||||
const sharePath = removeQueryParam(
|
||||
@@ -243,8 +238,8 @@ export function createPortalPublishedPageDelivery({
|
||||
html = await decoratePublicationHtmlAnalytics(html, {
|
||||
result,
|
||||
analyticsConfig,
|
||||
rybbitConfig,
|
||||
viewer: req.currentUser ?? null,
|
||||
referrerHint,
|
||||
getAuthPool,
|
||||
getUserAuth,
|
||||
getMindSpacePages,
|
||||
@@ -345,10 +340,16 @@ export function createPortalPublishedPageDelivery({
|
||||
if (canWrapWithShell) {
|
||||
const title =
|
||||
detectPublishedPageTitle(html);
|
||||
const rawUrl = appendQueryParam(
|
||||
sharePath || originalPath,
|
||||
'view',
|
||||
'raw',
|
||||
const rawUrl = appendDiscoveryPropagationParams(
|
||||
appendQueryParam(
|
||||
sharePath || originalPath,
|
||||
'view',
|
||||
'raw',
|
||||
),
|
||||
classifyDiscoveryTrafficSource({
|
||||
referrer: referrerHint,
|
||||
pageHost: origin ? new URL(origin).hostname : '',
|
||||
}),
|
||||
);
|
||||
const longImageUrl = appendQueryParam(
|
||||
sharePath || originalPath,
|
||||
@@ -386,8 +387,8 @@ export function createPortalPublishedPageDelivery({
|
||||
shellHtml = await decoratePublicationHtmlAnalytics(shellHtml, {
|
||||
result,
|
||||
analyticsConfig,
|
||||
rybbitConfig,
|
||||
viewer: req.currentUser ?? null,
|
||||
referrerHint,
|
||||
getAuthPool,
|
||||
getUserAuth,
|
||||
getMindSpacePages,
|
||||
|
||||
@@ -61,16 +61,16 @@ function createResult(overrides = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
test('injects rybbit tracker for publication HTML delivery', async () => {
|
||||
test('injects umami tracker for publication HTML delivery', async () => {
|
||||
const sendPublishedPage =
|
||||
createPortalPublishedPageDelivery({
|
||||
async registerLongImageArtifact() {},
|
||||
rybbitConfig: {
|
||||
analyticsConfig: {
|
||||
enabled: true,
|
||||
siteId: '2',
|
||||
websiteId: 'local-website',
|
||||
idSecret: 'secret',
|
||||
scriptPath: '/rybbit/script.js',
|
||||
hostPath: '/rybbit',
|
||||
scriptPath: '/analytics/script.js',
|
||||
hostPath: '/analytics',
|
||||
},
|
||||
});
|
||||
const response = createResponse();
|
||||
@@ -85,8 +85,8 @@ test('injects rybbit tracker for publication HTML delivery', async () => {
|
||||
},
|
||||
{ raw: true },
|
||||
);
|
||||
assert.match(response.body, /src="\/rybbit\/script\.js"/);
|
||||
assert.match(response.body, /data-site-id="2"/);
|
||||
assert.match(response.body, /src="\/analytics\/script\.js"/);
|
||||
assert.match(response.body, /data-website-id="local-website"/);
|
||||
assert.doesNotMatch(response.body, /user-123/);
|
||||
});
|
||||
|
||||
@@ -97,7 +97,12 @@ test('wraps a normal full HTML publication in the share shell', async () => {
|
||||
});
|
||||
const response = createResponse();
|
||||
await sendPublishedPage(
|
||||
createRequest(),
|
||||
createRequest({
|
||||
headers: {
|
||||
host: 'portal.example',
|
||||
referer: 'https://www.google.com/search?q=demo',
|
||||
},
|
||||
}),
|
||||
response,
|
||||
createResult(),
|
||||
);
|
||||
@@ -110,6 +115,14 @@ test('wraps a normal full HTML publication in the share shell', async () => {
|
||||
response.body,
|
||||
/view=raw/,
|
||||
);
|
||||
assert.match(
|
||||
response.body,
|
||||
/memind_discovery=seo/,
|
||||
);
|
||||
assert.match(
|
||||
response.body,
|
||||
/memind_discovery_source=google/,
|
||||
);
|
||||
assert.equal(
|
||||
response.headers['Cache-Control'],
|
||||
'public, max-age=60',
|
||||
|
||||
@@ -10,9 +10,6 @@ import {
|
||||
resolveAnalyticsPlan,
|
||||
sendMindSpaceAnalyticsEvent,
|
||||
} from '../mindspace-analytics.mjs';
|
||||
import {
|
||||
sendMindSpaceRybbitEvent,
|
||||
} from '../mindspace-rybbit.mjs';
|
||||
import {
|
||||
markPageDeliveryContractReady,
|
||||
preparePageDeliveryContract,
|
||||
@@ -49,7 +46,6 @@ export function attachPortalSessionRoutes(
|
||||
getMindSpacePublicFinish = () => null,
|
||||
getMemoryV2 = () => null,
|
||||
getMindSpaceAnalyticsConfig = () => null,
|
||||
getMindSpaceRybbitConfig = () => null,
|
||||
ownsAgentSession,
|
||||
unregisterAgentSessionForUser,
|
||||
beginSessionPageDelivery,
|
||||
@@ -72,8 +68,6 @@ export function attachPortalSessionRoutes(
|
||||
hasPublicHtmlWriteRequestInSessionEvent,
|
||||
sendMindSpaceAnalyticsEventFn =
|
||||
sendMindSpaceAnalyticsEvent,
|
||||
sendMindSpaceRybbitEventFn =
|
||||
sendMindSpaceRybbitEvent,
|
||||
resolveAnalyticsOwnerSegmentFn =
|
||||
resolveAnalyticsOwnerSegment,
|
||||
resolveAnalyticsOwnerLabelFn =
|
||||
@@ -466,11 +460,6 @@ export function attachPortalSessionRoutes(
|
||||
getMindSpaceAnalyticsConfig(),
|
||||
...analyticsPayload,
|
||||
});
|
||||
void sendMindSpaceRybbitEventFn({
|
||||
config:
|
||||
getMindSpaceRybbitConfig(),
|
||||
...analyticsPayload,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
|
||||
@@ -5,9 +5,6 @@ import {
|
||||
resolveAnalyticsOwnerSegment,
|
||||
resolveAnalyticsPlan,
|
||||
} from '../mindspace-analytics.mjs';
|
||||
import {
|
||||
injectMindSpaceRybbit,
|
||||
} from '../mindspace-rybbit.mjs';
|
||||
import {
|
||||
buildPublishedHtmlViewContext,
|
||||
resolveMindSpacePageDataContext,
|
||||
@@ -63,7 +60,6 @@ function deliveryNotFoundMessage(reason) {
|
||||
export function createPortalWorkspacePublicationDelivery({
|
||||
internalAgentSecret,
|
||||
analyticsConfig,
|
||||
rybbitConfig,
|
||||
getAuthPool = () => null,
|
||||
getUserAuth = () => null,
|
||||
getMindSpacePages = () => null,
|
||||
@@ -266,24 +262,11 @@ export function createPortalWorkspacePublicationDelivery({
|
||||
req.currentUser ?? null,
|
||||
analyticsConfig,
|
||||
),
|
||||
referrerHint: String(
|
||||
req.headers.referer ?? req.headers.referrer ?? req.get?.('referer') ?? '',
|
||||
).trim(),
|
||||
config: analyticsConfig,
|
||||
});
|
||||
html = injectMindSpaceRybbit(html, {
|
||||
ownerId: delivery.ownerId ?? '',
|
||||
ownerSegment:
|
||||
resolveAnalyticsOwnerSegment(
|
||||
pageOwner ?? {},
|
||||
),
|
||||
ownerLabel: resolveAnalyticsOwnerLabel(
|
||||
pageOwner ?? {},
|
||||
),
|
||||
pageId: pageDataContext?.pageId ?? '',
|
||||
publicationId:
|
||||
pageDataContext?.publicationId ??
|
||||
pageDataContext?.publication_id ??
|
||||
'',
|
||||
config: rybbitConfig,
|
||||
});
|
||||
const mindSpaceConfig = getAuthPool()
|
||||
? await getMindSpaceConfig(getAuthPool()).catch(() => ({ seoGeo: { enabled: false } }))
|
||||
: { seoGeo: { enabled: false } };
|
||||
|
||||
@@ -99,7 +99,6 @@ function createSetup(overrides = {}) {
|
||||
createPortalWorkspacePublicationDelivery({
|
||||
internalAgentSecret: 'test-secret',
|
||||
analyticsConfig: { enabled: false },
|
||||
rybbitConfig: { enabled: false },
|
||||
getAuthPool: () => null,
|
||||
getUserAuth: () => userAuth,
|
||||
getMindSpacePages: () => null,
|
||||
|
||||
Reference in New Issue
Block a user