Transferred ShieldAI-related files mistakenly placed in ~/code/FrenoCorp:
- Services: spamshield (feature-flags, audit-logger, error-handler), voiceprint (config, service, feature-flags), darkwatch (pipeline, scan, scheduler, watchlist, webhook)
- Packages: shared-analytics, shared-auth, shared-ui, shared-utils (new); shared-billing, jobs supplemented with unique FC files
- Server: alerts (FC version newer), routes (spamshield, darkwatch, voiceprint)
- Config: turbo.json, tsconfig.base.json, vite/vitest configs, drizzle, Dockerfile
- VoicePrint ML service
- Examples
Pending: apps/{api,web,mobile}/ structured merge, shared-db/db mapping
Co-Authored-By: Paperclip <noreply@paperclip.ing>
118 lines
2.5 KiB
TypeScript
118 lines
2.5 KiB
TypeScript
import { Analytics } from '@segment/analytics-node';
|
|
import { analyticsEnv, EventType, eventPropertiesSchema } from '../config/analytics.config';
|
|
import { hashPhoneNumber } from '../utils/phone-hash';
|
|
|
|
// Mixpanel service
|
|
export class MixpanelService {
|
|
private client: Analytics;
|
|
|
|
constructor() {
|
|
this.client = new Analytics({
|
|
apiKey: analyticsEnv.MIXPANEL_TOKEN,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Track an event in Mixpanel
|
|
*/
|
|
async track(
|
|
event: EventType,
|
|
distinctId: string,
|
|
properties?: Record<string, any>
|
|
): Promise<void> {
|
|
const validatedProperties = eventPropertiesSchema.parse(properties);
|
|
|
|
this.client.track({
|
|
event,
|
|
distinctId,
|
|
properties: {
|
|
...validatedProperties,
|
|
...properties,
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Identify a user
|
|
*/
|
|
async identify(userId: string, traits?: Record<string, any>): Promise<void> {
|
|
this.client.identify({
|
|
distinctId: userId,
|
|
traits,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Group users by subscription tier
|
|
*/
|
|
async group(groupId: string, groupKey: string, traits?: Record<string, any>): Promise<void> {
|
|
this.client.group({
|
|
groupKey,
|
|
groupId,
|
|
traits,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Track user sign-up
|
|
*/
|
|
async userSignedUp(userId: string, plan?: string, referrer?: string): Promise<void> {
|
|
await this.track(EventType.USER_SIGNED_UP, userId, {
|
|
plan,
|
|
referrer,
|
|
timestamp: new Date(),
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Track subscription upgrade
|
|
*/
|
|
async userUpgraded(userId: string, fromTier: string, toTier: string, mrr: number): Promise<void> {
|
|
await this.track(EventType.USER_UPGRADED, userId, {
|
|
fromTier,
|
|
toTier,
|
|
mrr,
|
|
timestamp: new Date(),
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Track exposure detection
|
|
*/
|
|
async exposureDetected(
|
|
userId: string,
|
|
exposureType: string,
|
|
severity: string,
|
|
source: string
|
|
): Promise<void> {
|
|
await this.track(EventType.EXPOSURE_DETECTED, userId, {
|
|
exposureType,
|
|
severity,
|
|
source,
|
|
timestamp: new Date(),
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Track spam detection
|
|
*/
|
|
async spamBlocked(userId: string, phoneNumber: string, confidence: number, method: string): Promise<void> {
|
|
await this.track(EventType.SPAM_BLOCKED, userId, {
|
|
phoneNumber: hashPhoneNumber(phoneNumber),
|
|
confidence,
|
|
method,
|
|
timestamp: new Date(),
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Flush pending events
|
|
*/
|
|
async flush(): Promise<void> {
|
|
await this.client.flush();
|
|
}
|
|
}
|
|
|
|
// Export instance
|
|
export const mixpanelService = new MixpanelService();
|