chore:little fixes and formating and linting and patches

This commit is contained in:
2026-03-31 15:50:54 +02:00
parent a1ce71ffb6
commit 9b197abcfa
815 changed files with 22960 additions and 8982 deletions

View File

@@ -1,23 +1,18 @@
import { chartTypes } from '@openpanel/constants';
import type { IClickhouseSession } from '@openpanel/db';
import {
ch,
clix,
type IClickhouseEvent,
type IClickhouseProfile,
TABLE_NAMES,
ch,
clix,
} from '@openpanel/db';
import { ChartEngine } from '@openpanel/db';
import { getCache } from '@openpanel/redis';
import { zReportInput } from '@openpanel/validation';
import { tool } from 'ai';
import { z } from 'zod';
export function getReport({
projectId,
}: {
projectId: string;
}) {
export function getReport({ projectId }: { projectId: string }) {
return tool({
description: `Generate a report (a chart) for
- ${chartTypes.area}
@@ -67,11 +62,7 @@ export function getReport({
},
});
}
export function getConversionReport({
projectId,
}: {
projectId: string;
}) {
export function getConversionReport({ projectId }: { projectId: string }) {
return tool({
description:
'Generate a report (a chart) for conversions between two actions a unique user took.',
@@ -92,11 +83,7 @@ export function getConversionReport({
},
});
}
export function getFunnelReport({
projectId,
}: {
projectId: string;
}) {
export function getFunnelReport({ projectId }: { projectId: string }) {
return tool({
description:
'Generate a report (a chart) for funnel between two or more actions a unique user (session_id or profile_id) took.',
@@ -118,11 +105,7 @@ export function getFunnelReport({
});
}
export function getProfiles({
projectId,
}: {
projectId: string;
}) {
export function getProfiles({ projectId }: { projectId: string }) {
return tool({
description: 'Get profiles',
parameters: z.object({
@@ -188,11 +171,7 @@ export function getProfiles({
});
}
export function getProfile({
projectId,
}: {
projectId: string;
}) {
export function getProfile({ projectId }: { projectId: string }) {
return tool({
description: 'Get a specific profile',
parameters: z.object({
@@ -276,11 +255,7 @@ export function getProfile({
});
}
export function getEvents({
projectId,
}: {
projectId: string;
}) {
export function getEvents({ projectId }: { projectId: string }) {
return tool({
description: 'Get events for a project or specific profile',
parameters: z.object({
@@ -369,11 +344,7 @@ export function getEvents({
});
}
export function getSessions({
projectId,
}: {
projectId: string;
}) {
export function getSessions({ projectId }: { projectId: string }) {
return tool({
description: 'Get sessions for a project or specific profile',
parameters: z.object({
@@ -458,11 +429,7 @@ export function getSessions({
});
}
export function getAllEventNames({
projectId,
}: {
projectId: string;
}) {
export function getAllEventNames({ projectId }: { projectId: string }) {
return tool({
description: 'Get the top 50 event names in a comma separated list',
parameters: z.object({}),

View File

@@ -14,11 +14,7 @@ export const getChatModel = () => {
}
};
export const getChatSystemPrompt = ({
projectId,
}: {
projectId: string;
}) => {
export const getChatSystemPrompt = ({ projectId }: { projectId: string }) => {
return `You're an product and web analytics expert. Don't generate more than the user asks for. Follow all rules listed below!
## General:
- projectId: \`${projectId}\`

View File

@@ -1,5 +1,3 @@
import type { FastifyRequest, RawRequestDefaultExpression } from 'fastify';
import { verifyPassword } from '@openpanel/common/server';
import type { IServiceClientWithProject } from '@openpanel/db';
import { ClientType, getClientByIdCached } from '@openpanel/db';
@@ -10,6 +8,7 @@ import type {
IProjectFilterProfileId,
ITrackHandlerPayload,
} from '@openpanel/validation';
import type { FastifyRequest, RawRequestDefaultExpression } from 'fastify';
import { path } from 'ramda';
const cleanDomain = (domain: string) =>
@@ -31,7 +30,7 @@ export class SdkAuthError extends Error {
clientId?: string;
clientSecret?: string;
origin?: string;
},
}
) {
super(message);
this.name = 'SdkAuthError';
@@ -43,7 +42,7 @@ export class SdkAuthError extends Error {
export async function validateSdkRequest(
req: FastifyRequest<{
Body: ITrackHandlerPayload | DeprecatedPostEventPayload;
}>,
}>
): Promise<IServiceClientWithProject> {
const { headers, clientIp } = req;
const clientIdNew = headers['openpanel-client-id'] as string;
@@ -70,7 +69,7 @@ export async function validateSdkRequest(
if (
!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(
clientId,
clientId
)
) {
throw createError('Ingestion: Client ID must be a valid UUIDv4');
@@ -88,7 +87,7 @@ export async function validateSdkRequest(
// Filter out blocked IPs
const ipFilter = client.project.filters.filter(
(filter): filter is IProjectFilterIp => filter.type === 'ip',
(filter): filter is IProjectFilterIp => filter.type === 'ip'
);
if (ipFilter.some((filter) => filter.ip === clientIp)) {
throw createError('Ingestion: IP address is blocked by project filter');
@@ -96,7 +95,7 @@ export async function validateSdkRequest(
// Filter out blocked profile ids
const profileFilter = client.project.filters.filter(
(filter): filter is IProjectFilterProfileId => filter.type === 'profile_id',
(filter): filter is IProjectFilterProfileId => filter.type === 'profile_id'
);
const profileId =
path<string | undefined>(['payload', 'profileId'], req.body) || // Track handler
@@ -113,12 +112,11 @@ export async function validateSdkRequest(
// Only allow revenue tracking if it was sent with a client secret
// or if the project has allowUnsafeRevenueTracking enabled
if (
!client.project.allowUnsafeRevenueTracking &&
!clientSecret &&
!(client.project.allowUnsafeRevenueTracking || clientSecret) &&
typeof revenue !== 'undefined'
) {
throw createError(
'Ingestion: Revenue tracking is not allowed without a client secret',
'Ingestion: Revenue tracking is not allowed without a client secret'
);
}
@@ -132,7 +130,7 @@ export async function validateSdkRequest(
// support wildcard domains `*.foo.com`
if (cleanedDomain.includes('*')) {
const regex = new RegExp(
`${cleanedDomain.replaceAll('.', '\\.').replaceAll('*', '.+?')}`,
`${cleanedDomain.replaceAll('.', '\\.').replaceAll('*', '.+?')}`
);
return regex.test(origin || '');
@@ -157,7 +155,7 @@ export async function validateSdkRequest(
`client:auth:${clientId}:${Buffer.from(clientSecret).toString('base64')}`,
60 * 5,
async () => await verifyPassword(clientSecret, client.secret!),
true,
true
);
if (isVerified) {
return client;
@@ -168,14 +166,14 @@ export async function validateSdkRequest(
}
export async function validateExportRequest(
headers: RawRequestDefaultExpression['headers'],
headers: RawRequestDefaultExpression['headers']
): Promise<IServiceClientWithProject> {
const clientId = headers['openpanel-client-id'] as string;
const clientSecret = (headers['openpanel-client-secret'] as string) || '';
if (
!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(
clientId,
clientId
)
) {
throw new Error('Export: Client ID must be a valid UUIDv4');
@@ -203,14 +201,14 @@ export async function validateExportRequest(
}
export async function validateImportRequest(
headers: RawRequestDefaultExpression['headers'],
headers: RawRequestDefaultExpression['headers']
): Promise<IServiceClientWithProject> {
const clientId = headers['openpanel-client-id'] as string;
const clientSecret = (headers['openpanel-client-secret'] as string) || '';
if (
!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(
clientId,
clientId
)
) {
throw new Error('Import: Client ID must be a valid UUIDv4');
@@ -238,14 +236,14 @@ export async function validateImportRequest(
}
export async function validateManageRequest(
headers: RawRequestDefaultExpression['headers'],
headers: RawRequestDefaultExpression['headers']
): Promise<IServiceClientWithProject> {
const clientId = headers['openpanel-client-id'] as string;
const clientSecret = (headers['openpanel-client-secret'] as string) || '';
if (
!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(
clientId,
clientId
)
) {
throw new Error('Manage: Client ID must be a valid UUIDv4');
@@ -263,7 +261,7 @@ export async function validateManageRequest(
if (client.type !== ClientType.root) {
throw new Error(
'Manage: Only root clients are allowed to manage resources',
'Manage: Only root clients are allowed to manage resources'
);
}

View File

@@ -20,10 +20,10 @@ export async function isDuplicatedEvent({
origin,
projectId,
},
'md5',
'md5'
)}`,
'1',
100,
100
);
if (locked) {

View File

@@ -4,7 +4,7 @@ export class LogError extends Error {
constructor(
message: string,
payload?: Record<string, unknown>,
options?: ErrorOptions,
options?: ErrorOptions
) {
super(message, options);
this.name = 'LogError';
@@ -26,7 +26,7 @@ export class HttpError extends Error {
fingerprint?: string;
extra?: Record<string, unknown>;
error?: Error | unknown;
},
}
) {
super(message);
this.name = 'HttpError';

View File

@@ -29,7 +29,7 @@ export function isShuttingDown() {
export async function shutdown(
fastify: FastifyInstance,
signal: string,
exitCode = 0,
exitCode = 0
) {
if (isShuttingDown()) {
logger.warn('Shutdown already in progress, ignoring signal', { signal });
@@ -96,7 +96,7 @@ export async function shutdown(
if (redis.status === 'ready') {
await redis.quit();
}
}),
})
);
logger.info('Redis connections closed');
} catch (error) {

View File

@@ -3,14 +3,21 @@ import { getSafeJson } from '@openpanel/json';
export const parseQueryString = (obj: Record<string, any>): any => {
return Object.fromEntries(
Object.entries(obj).map(([k, v]) => {
if (typeof v === 'object') return [k, parseQueryString(v)];
if (typeof v === 'object') {
return [k, parseQueryString(v)];
}
if (
/^-?[0-9]+(\.[0-9]+)?$/i.test(v) &&
!Number.isNaN(Number.parseFloat(v))
)
) {
return [k, Number.parseFloat(v)];
if (v === 'true') return [k, true];
if (v === 'false') return [k, false];
}
if (v === 'true') {
return [k, true];
}
if (v === 'false') {
return [k, false];
}
if (typeof v === 'string') {
if (getSafeJson(v) !== null) {
return [k, getSafeJson(v)];
@@ -18,6 +25,6 @@ export const parseQueryString = (obj: Record<string, any>): any => {
return [k, v];
}
return [k, null];
}),
})
);
};

View File

@@ -19,7 +19,7 @@ function findBestFavicon(favicons: UrlMetaData['favicons']) {
(favicon) =>
favicon.rel === 'shortcut icon' ||
favicon.rel === 'icon' ||
favicon.rel === 'apple-touch-icon',
favicon.rel === 'apple-touch-icon'
);
if (match) {