fix: better validation of events + clean up (#267)

This commit is contained in:
Carl-Gerhard Lindesvärd
2026-01-07 11:58:11 +01:00
parent 6d9e3ce8e5
commit 3c085e445d
22 changed files with 387 additions and 387 deletions

View File

@@ -3,6 +3,11 @@ import { differenceInDays, isSameDay, isSameMonth } from 'date-fns';
export const DEFAULT_ASPECT_RATIO = 0.5625;
export const NOT_SET_VALUE = '(not set)';
export const RESERVED_EVENT_NAMES = [
'session_start',
'session_end',
] as const;
export const timeWindows = {
'30min': {
key: '30min',

View File

@@ -8,6 +8,13 @@ export type ILogger = winston.Logger;
const logLevel = process.env.LOG_LEVEL ?? 'info';
const silent = process.env.LOG_SILENT === 'true';
// Add colors for custom levels (fatal, warn, trace) that aren't in default color schemes
winston.addColors({
fatal: 'red',
warn: 'yellow',
trace: 'gray',
});
export function createLogger({ name }: { name: string }): ILogger {
const service = [process.env.LOG_PREFIX, name, process.env.NODE_ENV ?? 'dev']
.filter(Boolean)

View File

@@ -14,7 +14,6 @@
"groupmq": "catalog:"
},
"devDependencies": {
"@openpanel/sdk": "workspace:*",
"@openpanel/tsconfig": "workspace:*",
"@types/node": "catalog:",
"typescript": "catalog:"

View File

@@ -8,8 +8,8 @@ import type {
} from '@openpanel/db';
import { createLogger } from '@openpanel/logger';
import { getRedisGroupQueue, getRedisQueue } from '@openpanel/redis';
import type { TrackPayload } from '@openpanel/sdk';
import { Queue as GroupQueue } from 'groupmq';
import type { ITrackPayload } from '../../validation';
export const EVENTS_GROUP_QUEUES_SHARDS = Number.parseInt(
process.env.EVENTS_GROUP_QUEUES_SHARDS || '1',
@@ -32,7 +32,7 @@ export interface EventsQueuePayloadIncomingEvent {
type: 'incomingEvent';
payload: {
projectId: string;
event: TrackPayload & {
event: ITrackPayload & {
timestamp: string | number;
isTimestampFromThePast: boolean;
};

View File

@@ -1,36 +1 @@
export * from './src/index';
// Deprecated types for beta version of the SDKs
// Still used in api/event.controller.ts and api/profile.controller.ts
export interface OpenpanelEventOptions {
profileId?: string;
}
export interface PostEventPayload {
name: string;
timestamp: string;
profileId?: string;
properties?: Record<string, unknown> & OpenpanelEventOptions;
}
export interface UpdateProfilePayload {
profileId: string;
firstName?: string;
lastName?: string;
email?: string;
avatar?: string;
properties?: Record<string, unknown>;
}
export interface IncrementProfilePayload {
profileId: string;
property: string;
value: number;
}
export interface DecrementProfilePayload {
profileId?: string;
property: string;
value: number;
}

View File

@@ -9,8 +9,9 @@
"dependencies": {},
"devDependencies": {
"@openpanel/tsconfig": "workspace:*",
"@openpanel/validation": "workspace:*",
"@types/node": "catalog:",
"tsup": "^7.2.0",
"typescript": "catalog:"
}
}
}

View File

@@ -1,31 +1,20 @@
import type {
IAliasPayload as AliasPayload,
IDecrementPayload as DecrementPayload,
IIdentifyPayload as IdentifyPayload,
IIncrementPayload as IncrementPayload,
ITrackHandlerPayload as TrackHandlerPayload,
ITrackPayload as TrackPayload,
} from '@openpanel/validation';
import { Api } from './api';
export type TrackHandlerPayload =
| {
type: 'track';
payload: TrackPayload;
}
| {
type: 'increment';
payload: IncrementPayload;
}
| {
type: 'decrement';
payload: DecrementPayload;
}
| {
type: 'alias';
payload: AliasPayload;
}
| {
type: 'identify';
payload: IdentifyPayload;
};
export type TrackPayload = {
name: string;
properties?: Record<string, unknown>;
profileId?: string;
export type {
AliasPayload,
DecrementPayload,
IdentifyPayload,
IncrementPayload,
TrackHandlerPayload,
TrackPayload,
};
export type TrackProperties = {
@@ -33,32 +22,6 @@ export type TrackProperties = {
profileId?: string;
};
export type IdentifyPayload = {
profileId: string;
firstName?: string;
lastName?: string;
email?: string;
avatar?: string;
properties?: Record<string, unknown>;
};
export type AliasPayload = {
profileId: string;
alias: string;
};
export type IncrementPayload = {
profileId: string;
property: string;
value?: number;
};
export type DecrementPayload = {
profileId: string;
property: string;
value?: number;
};
export type OpenPanelOptions = {
clientId: string;
clientSecret?: string;

View File

@@ -555,3 +555,4 @@ export const zCreateImport = z.object({
export type ICreateImport = z.infer<typeof zCreateImport>;
export * from './types.insights';
export * from './track.validation';

View File

@@ -0,0 +1,104 @@
import { z } from 'zod';
import { RESERVED_EVENT_NAMES } from '@openpanel/constants';
export const zTrackPayload = z
.object({
name: z.string().min(1),
properties: z.record(z.unknown()).optional(),
profileId: z.string().optional(),
})
.refine((data) => !RESERVED_EVENT_NAMES.includes(data.name as any), {
message: `Event name cannot be one of the reserved names: ${RESERVED_EVENT_NAMES.join(', ')}`,
path: ['name'],
});
export const zIdentifyPayload = z.object({
profileId: z.string().min(1),
firstName: z.string().optional(),
lastName: z.string().optional(),
email: z.string().email().optional(),
avatar: z.string().url().optional(),
properties: z.record(z.unknown()).optional(),
});
export const zIncrementPayload = z.object({
profileId: z.string().min(1),
property: z.string().min(1),
value: z.number().positive().optional(),
});
export const zDecrementPayload = z.object({
profileId: z.string().min(1),
property: z.string().min(1),
value: z.number().positive().optional(),
});
export const zAliasPayload = z.object({
profileId: z.string().min(1),
alias: z.string().min(1),
});
export const zTrackHandlerPayload = z.discriminatedUnion('type', [
z.object({
type: z.literal('track'),
payload: zTrackPayload,
}),
z.object({
type: z.literal('identify'),
payload: zIdentifyPayload,
}),
z.object({
type: z.literal('increment'),
payload: zIncrementPayload,
}),
z.object({
type: z.literal('decrement'),
payload: zDecrementPayload,
}),
z.object({
type: z.literal('alias'),
payload: zAliasPayload,
}),
]);
export type ITrackPayload = z.infer<typeof zTrackPayload>;
export type IIdentifyPayload = z.infer<typeof zIdentifyPayload>;
export type IIncrementPayload = z.infer<typeof zIncrementPayload>;
export type IDecrementPayload = z.infer<typeof zDecrementPayload>;
export type IAliasPayload = z.infer<typeof zAliasPayload>;
export type ITrackHandlerPayload = z.infer<typeof zTrackHandlerPayload>;
// Deprecated types for beta version of the SDKs
export interface DeprecatedOpenpanelEventOptions {
profileId?: string;
}
export interface DeprecatedPostEventPayload {
name: string;
timestamp: string;
profileId?: string;
properties?: Record<string, unknown> & DeprecatedOpenpanelEventOptions;
}
export interface DeprecatedUpdateProfilePayload {
profileId: string;
firstName?: string;
lastName?: string;
email?: string;
avatar?: string;
properties?: Record<string, unknown>;
}
export interface DeprecatedIncrementProfilePayload {
profileId: string;
property: string;
value: number;
}
export interface DeprecatedDecrementProfilePayload {
profileId?: string;
property: string;
value: number;
}