Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions backend/src/server/plugins/auth/inject-identity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,19 @@ export const injectIdentity = fp(
async (server: FastifyZodProvider, opt: { shouldForwardWritesToPrimaryInstance?: boolean }) => {
server.decorateRequest("auth", null);
server.decorateRequest("shouldForwardWritesToPrimaryInstance", Boolean(opt.shouldForwardWritesToPrimaryInstance));

// Hoisted outside onRequest hook to avoid per-request function allocation on this hot path
const fireIdentifyForUser = (user: TUsers) => {
const distinctId = user.username ?? user.email ?? "";
if (distinctId) {
void server.services.telemetry.identifyUser(distinctId, {
email: user.email ?? undefined,
username: user.username,
userId: user.id
});
}
};

server.addHook("onRequest", async (req) => {
const appCfg = getConfig();

Expand Down Expand Up @@ -184,6 +197,7 @@ export const injectIdentity = fp(
isMfaVerified: token.isMfaVerified,
token
};
fireIdentifyForUser(user);
break;
}
case AuthMode.MCP_JWT: {
Expand All @@ -205,6 +219,7 @@ export const injectIdentity = fp(
isMfaVerified: token.isMfaVerified,
token
};
fireIdentifyForUser(user);
break;
}
case AuthMode.IDENTITY_ACCESS_TOKEN: {
Expand Down
40 changes: 24 additions & 16 deletions backend/src/server/routes/v1/admin-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -643,14 +643,18 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {

const adminDistinctId = user.user.username ?? user.user.email ?? "";
if (adminDistinctId) {
void server.services.telemetry.identifyUser(adminDistinctId, {
email: user.user.email ?? undefined,
username: user.user.username,
userId: user.user.id,
firstName: user.user.firstName ?? undefined,
lastName: user.user.lastName ?? undefined,
superAdmin: true
});
void server.services.telemetry.identifyUser(
adminDistinctId,
{
email: user.user.email ?? undefined,
username: user.user.username,
userId: user.user.id,
firstName: user.user.firstName ?? undefined,
lastName: user.user.lastName ?? undefined,
superAdmin: true
},
{ skipDedup: true }
);
}

void res.setCookie("jid", token.refresh, {
Expand Down Expand Up @@ -804,14 +808,18 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {

const bootstrapDistinctId = user.user.username ?? user.user.email ?? "";
if (bootstrapDistinctId) {
void server.services.telemetry.identifyUser(bootstrapDistinctId, {
email: user.user.email ?? undefined,
username: user.user.username,
userId: user.user.id,
firstName: user.user.firstName ?? undefined,
lastName: user.user.lastName ?? undefined,
superAdmin: true
});
void server.services.telemetry.identifyUser(
bootstrapDistinctId,
{
email: user.user.email ?? undefined,
username: user.user.username,
userId: user.user.id,
firstName: user.user.firstName ?? undefined,
lastName: user.user.lastName ?? undefined,
superAdmin: true
},
{ skipDedup: true }
);
}

return {
Expand Down
68 changes: 42 additions & 26 deletions backend/src/server/routes/v1/sso-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,17 @@ export const registerOauthMiddlewares = (server: FastifyZodProvider) => {

const googleDistinctId = user.username ?? user.email ?? "";
if (googleDistinctId) {
void server.services.telemetry.identifyUser(googleDistinctId, {
email: user.email ?? undefined,
username: user.username,
userId: user.id,
firstName: user.firstName ?? undefined,
lastName: user.lastName ?? undefined
});
void server.services.telemetry.identifyUser(
googleDistinctId,
{
email: user.email ?? undefined,
username: user.username,
userId: user.id,
firstName: user.firstName ?? undefined,
lastName: user.lastName ?? undefined
},
{ skipDedup: true }
);
}

if (appCfg.OTEL_TELEMETRY_COLLECTION_ENABLED) {
Expand Down Expand Up @@ -162,13 +166,17 @@ export const registerOauthMiddlewares = (server: FastifyZodProvider) => {

const githubDistinctId = user.username ?? user.email ?? "";
if (githubDistinctId) {
void server.services.telemetry.identifyUser(githubDistinctId, {
email: user.email ?? undefined,
username: user.username,
userId: user.id,
firstName: user.firstName ?? undefined,
lastName: user.lastName ?? undefined
});
void server.services.telemetry.identifyUser(
githubDistinctId,
{
email: user.email ?? undefined,
username: user.username,
userId: user.id,
firstName: user.firstName ?? undefined,
lastName: user.lastName ?? undefined
},
{ skipDedup: true }
);
}

if (appCfg.OTEL_TELEMETRY_COLLECTION_ENABLED) {
Expand Down Expand Up @@ -237,13 +245,17 @@ export const registerOauthMiddlewares = (server: FastifyZodProvider) => {

const gitlabDistinctId = user.username ?? user.email ?? "";
if (gitlabDistinctId) {
void server.services.telemetry.identifyUser(gitlabDistinctId, {
email: user.email ?? undefined,
username: user.username,
userId: user.id,
firstName: user.firstName ?? undefined,
lastName: user.lastName ?? undefined
});
void server.services.telemetry.identifyUser(
gitlabDistinctId,
{
email: user.email ?? undefined,
username: user.username,
userId: user.id,
firstName: user.firstName ?? undefined,
lastName: user.lastName ?? undefined
},
{ skipDedup: true }
);
}

if (appCfg.OTEL_TELEMETRY_COLLECTION_ENABLED) {
Expand Down Expand Up @@ -593,11 +605,15 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {

const tokenExchangeDistinctId = data.user.username ?? data.user.email ?? "";
if (tokenExchangeDistinctId) {
void server.services.telemetry.identifyUser(tokenExchangeDistinctId, {
email: data.user.email ?? undefined,
username: data.user.username,
userId: data.user.userId
});
void server.services.telemetry.identifyUser(
tokenExchangeDistinctId,
{
email: data.user.email ?? undefined,
username: data.user.username,
userId: data.user.userId
},
{ skipDedup: true }
);
}

if ([AuthMethod.GOOGLE, AuthMethod.GITHUB, AuthMethod.GITLAB].includes(data.decodedProviderToken.authMethod)) {
Expand Down
28 changes: 18 additions & 10 deletions backend/src/server/routes/v3/login-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,11 +157,15 @@ export const registerLoginRouter = async (server: FastifyZodProvider) => {

const login2DistinctId = data.user.username ?? data.user.email ?? "";
if (login2DistinctId) {
void server.services.telemetry.identifyUser(login2DistinctId, {
email: data.user.email ?? undefined,
username: data.user.username,
userId: data.user.userId
});
void server.services.telemetry.identifyUser(
login2DistinctId,
{
email: data.user.email ?? undefined,
username: data.user.username,
userId: data.user.userId
},
{ skipDedup: true }
);
}

void res.setCookie("jid", data.token.refresh, {
Expand Down Expand Up @@ -232,11 +236,15 @@ export const registerLoginRouter = async (server: FastifyZodProvider) => {

const loginDistinctId = user.username ?? user.email ?? "";
if (loginDistinctId) {
void server.services.telemetry.identifyUser(loginDistinctId, {
email: user.email ?? undefined,
username: user.username,
userId: user.userId
});
void server.services.telemetry.identifyUser(
loginDistinctId,
{
email: user.email ?? undefined,
username: user.username,
userId: user.userId
},
{ skipDedup: true }
);
}

void res.setCookie("jid", tokens.refreshToken, {
Expand Down
48 changes: 28 additions & 20 deletions backend/src/server/routes/v3/signup-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,16 +160,20 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => {

const signupDistinctId = user.username ?? user.email ?? "";
if (signupDistinctId) {
void server.services.telemetry.identifyUser(signupDistinctId, {
email: user.email ?? undefined,
username: user.username,
userId: user.id,
firstName: user.firstName ?? undefined,
lastName: user.lastName ?? undefined,
isEmailVerified: user.isEmailVerified ?? undefined,
isMfaEnabled: user.isMfaEnabled ?? undefined,
superAdmin: user.superAdmin ?? undefined
});
void server.services.telemetry.identifyUser(
signupDistinctId,
{
email: user.email ?? undefined,
username: user.username,
userId: user.id,
firstName: user.firstName ?? undefined,
lastName: user.lastName ?? undefined,
isEmailVerified: user.isEmailVerified ?? undefined,
isMfaEnabled: user.isMfaEnabled ?? undefined,
superAdmin: user.superAdmin ?? undefined
},
{ skipDedup: true }
);
}

void res.setCookie("jid", refreshToken, {
Expand Down Expand Up @@ -236,16 +240,20 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => {

const inviteDistinctId = user.username ?? user.email ?? "";
if (inviteDistinctId) {
void server.services.telemetry.identifyUser(inviteDistinctId, {
email: user.email ?? undefined,
username: user.username,
userId: user.id,
firstName: user.firstName ?? undefined,
lastName: user.lastName ?? undefined,
isEmailVerified: user.isEmailVerified ?? undefined,
isMfaEnabled: user.isMfaEnabled ?? undefined,
superAdmin: user.superAdmin ?? undefined
});
void server.services.telemetry.identifyUser(
inviteDistinctId,
{
email: user.email ?? undefined,
username: user.username,
userId: user.id,
firstName: user.firstName ?? undefined,
lastName: user.lastName ?? undefined,
isEmailVerified: user.isEmailVerified ?? undefined,
isMfaEnabled: user.isMfaEnabled ?? undefined,
superAdmin: user.superAdmin ?? undefined
},
{ skipDedup: true }
);
}

void res.setCookie("jid", refreshToken, {
Expand Down
35 changes: 32 additions & 3 deletions backend/src/services/telemetry/telemetry-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,13 @@ To opt into telemetry, you can set "TELEMETRY_ENABLED=true" within the environme
}
};

const identifyUser = (
const TELEMETRY_IDENTIFY_CACHE_KEY_PREFIX = "telemetry-identify";
const TELEMETRY_IDENTIFY_CACHE_TTL = 600; // 10 minutes

// In-memory fallback dedup set to limit blast radius during Redis outages
const inMemoryIdentifyDedup = new Set<string>();

const identifyUser = async (
distinctId: string,
properties: {
email?: string;
Expand All @@ -382,12 +388,35 @@ To opt into telemetry, you can set "TELEMETRY_ENABLED=true" within the environme
isMfaEnabled?: boolean;
isEmailVerified?: boolean;
superAdmin?: boolean;
}
},
{ skipDedup }: { skipDedup?: boolean } = {}
) => {
if (postHog && distinctId) {
const instanceType = licenseService.getInstanceType();
if (instanceType === InstanceType.Cloud) {
postHog.identify({ distinctId, properties });
if (!skipDedup) {
try {
const cacheKey = `${TELEMETRY_IDENTIFY_CACHE_KEY_PREFIX}:${distinctId}`;
Comment thread
0xArshdeep marked this conversation as resolved.
// Atomic SET NX + EX: only the first caller within the TTL window proceeds
const wasSet = await keyStore.setItemWithExpiryNX(cacheKey, TELEMETRY_IDENTIFY_CACHE_TTL, "1");
if (!wasSet) return;
} catch (error) {
logger.error(error, `Failed to check PostHog identify dedup cache for distinctId=${distinctId}`);
// In-memory fallback to limit blast radius during Redis outage
if (inMemoryIdentifyDedup.has(distinctId)) return;
inMemoryIdentifyDedup.add(distinctId);
const timer = setTimeout(
() => inMemoryIdentifyDedup.delete(distinctId),
TELEMETRY_IDENTIFY_CACHE_TTL * 1000
);
timer.unref();
}
}
Comment thread
0xArshdeep marked this conversation as resolved.
try {
postHog.identify({ distinctId, properties });
} catch (err) {
logger.error(err, `Failed to call postHog.identify for distinctId=${distinctId}`);
}
}
}
};
Expand Down
Loading