rename mixan to OPENPANEL!

This commit is contained in:
Carl-Gerhard Lindesvärd
2024-03-11 13:40:46 +01:00
parent 6d4f9010d4
commit e6c0bc2ec8
201 changed files with 1193 additions and 1047 deletions

View File

@@ -1,4 +1,4 @@
import type { IChartEventFilter, IGetChartDataInput } from '@mixan/validation';
import type { IChartEventFilter, IGetChartDataInput } from '@openpanel/validation';
import { formatClickhouseDate } from '../clickhouse-client';
import type { SqlBuilderObject } from '../sql-builder';

View File

@@ -1,22 +1,22 @@
import { omit, uniq } from 'ramda';
import { v4 as uuid } from 'uuid';
import { omit, uniq } from "ramda";
import { v4 as uuid } from "uuid";
import { randomSplitName, toDots } from '@mixan/common';
import { redis, redisPub } from '@mixan/redis';
import type { IChartEventFilter } from '@mixan/validation';
import { randomSplitName, toDots } from "@openpanel/common";
import { redis, redisPub } from "@openpanel/redis";
import type { IChartEventFilter } from "@openpanel/validation";
import {
ch,
chQuery,
convertClickhouseDateToJs,
formatClickhouseDate,
} from '../clickhouse-client';
import type { EventMeta, Prisma } from '../prisma-client';
import { db } from '../prisma-client';
import { createSqlBuilder } from '../sql-builder';
import { getEventFiltersWhereClause } from './chart.service';
import { getProfileById, getProfiles, upsertProfile } from './profile.service';
import type { IServiceProfile } from './profile.service';
} from "../clickhouse-client";
import type { EventMeta, Prisma } from "../prisma-client";
import { db } from "../prisma-client";
import { createSqlBuilder } from "../sql-builder";
import { getEventFiltersWhereClause } from "./chart.service";
import { getProfileById, getProfiles, upsertProfile } from "./profile.service";
import type { IServiceProfile } from "./profile.service";
export interface IClickhouseEvent {
id: string;
@@ -49,7 +49,7 @@ export interface IClickhouseEvent {
}
export function transformEvent(
event: IClickhouseEvent
event: IClickhouseEvent,
): IServiceCreateEventPayload {
return {
id: event.id,
@@ -124,7 +124,7 @@ export async function getLiveVisitors(projectId: string) {
export async function getEvents(
sql: string,
options: GetEventsOptions = {}
options: GetEventsOptions = {},
): Promise<IServiceCreateEventPayload[]> {
const events = await chQuery<IClickhouseEvent>(sql);
if (options.profile) {
@@ -155,17 +155,17 @@ export async function getEvents(
}
export async function createEvent(
payload: Omit<IServiceCreateEventPayload, 'id'>
payload: Omit<IServiceCreateEventPayload, "id">,
) {
if (!payload.profileId) {
payload.profileId = payload.deviceId;
}
console.log(
`create event ${payload.name} for deviceId: ${payload.deviceId} profileId ${payload.profileId}`
`create event ${payload.name} for deviceId: ${payload.deviceId} profileId ${payload.profileId}`,
);
const exists = await getProfileById(payload.profileId);
if (!exists && payload.profileId !== '') {
if (!exists && payload.profileId !== "") {
const { firstName, lastName } = randomSplitName();
await upsertProfile({
id: payload.profileId,
@@ -198,40 +198,40 @@ export async function createEvent(
profile_id: payload.profileId,
project_id: payload.projectId,
session_id: payload.sessionId,
properties: toDots(omit(['_path'], payload.properties)),
path: payload.path ?? '',
properties: toDots(omit(["_path"], payload.properties)),
path: payload.path ?? "",
created_at: formatClickhouseDate(payload.createdAt),
country: payload.country ?? '',
city: payload.city ?? '',
region: payload.region ?? '',
os: payload.os ?? '',
os_version: payload.osVersion ?? '',
browser: payload.browser ?? '',
browser_version: payload.browserVersion ?? '',
device: payload.device ?? '',
brand: payload.brand ?? '',
model: payload.model ?? '',
country: payload.country ?? "",
city: payload.city ?? "",
region: payload.region ?? "",
os: payload.os ?? "",
os_version: payload.osVersion ?? "",
browser: payload.browser ?? "",
browser_version: payload.browserVersion ?? "",
device: payload.device ?? "",
brand: payload.brand ?? "",
model: payload.model ?? "",
duration: payload.duration,
referrer: payload.referrer ?? '',
referrer_name: payload.referrerName ?? '',
referrer_type: payload.referrerType ?? '',
referrer: payload.referrer ?? "",
referrer_name: payload.referrerName ?? "",
referrer_type: payload.referrerType ?? "",
};
const res = await ch.insert({
table: 'events',
table: "events",
values: [event],
format: 'JSONEachRow',
format: "JSONEachRow",
clickhouse_settings: {
date_time_input_format: 'best_effort',
date_time_input_format: "best_effort",
},
});
redisPub.publish('event', JSON.stringify(transformEvent(event)));
redisPub.publish("event", JSON.stringify(transformEvent(event)));
redis.set(
`live:event:${event.project_id}:${event.profile_id}`,
'',
'EX',
60 * 5
"",
"EX",
60 * 5,
);
return {
@@ -270,7 +270,7 @@ export async function getEventList({
if (events && events.length > 0) {
sb.where.events = `name IN (${join(
events.map((n) => `'${n}'`),
','
",",
)})`;
}
@@ -285,7 +285,7 @@ export async function getEventList({
// sb.where.cursor = `created_at <= '${formatClickhouseDate(cursor)}'`;
// }
sb.orderBy.created_at = 'created_at DESC';
sb.orderBy.created_at = "created_at DESC";
return getEvents(getSql(), { profile: true, meta: true });
}
@@ -295,7 +295,7 @@ export async function getEventsCount({
profileId,
events,
filters,
}: Omit<GetEventListOptions, 'cursor' | 'take'>) {
}: Omit<GetEventListOptions, "cursor" | "take">) {
const { sb, getSql, join } = createSqlBuilder();
sb.where.projectId = `project_id = '${projectId}'`;
if (profileId) {
@@ -305,7 +305,7 @@ export async function getEventsCount({
if (events && events.length > 0) {
sb.where.events = `name IN (${join(
events.map((n) => `'${n}'`),
','
",",
)})`;
}
@@ -317,7 +317,7 @@ export async function getEventsCount({
}
const res = await chQuery<{ count: number }>(
getSql().replace('*', 'count(*) as count')
getSql().replace("*", "count(*) as count"),
);
return res[0]?.count ?? 0;
@@ -339,8 +339,8 @@ export function createBotEvent({
path,
}: CreateBotEventPayload) {
return ch.insert({
table: 'events_bots',
format: 'JSONEachRow',
table: "events_bots",
format: "JSONEachRow",
values: [
{
name,

View File

@@ -1,17 +1,17 @@
import { toDots, toObject } from '@mixan/common';
import type { IChartEventFilter } from '@mixan/validation';
import { toDots, toObject } from "@openpanel/common";
import type { IChartEventFilter } from "@openpanel/validation";
import { ch, chQuery } from '../clickhouse-client';
import { createSqlBuilder } from '../sql-builder';
import { getEventFiltersWhereClause } from './chart.service';
import { ch, chQuery } from "../clickhouse-client";
import { createSqlBuilder } from "../sql-builder";
import { getEventFiltersWhereClause } from "./chart.service";
export async function getProfileById(id: string) {
if (id === '') {
if (id === "") {
return null;
}
const [profile] = await chQuery<IClickhouseProfile>(
`SELECT * FROM profiles WHERE id = '${id}' ORDER BY created_at DESC LIMIT 1`
`SELECT * FROM profiles WHERE id = '${id}' ORDER BY created_at DESC LIMIT 1`,
);
if (!profile) {
@@ -30,15 +30,15 @@ interface GetProfileListOptions {
function getProfileSelectFields() {
return [
'id',
'argMax(first_name, created_at) as first_name',
'argMax(last_name, created_at) as last_name',
'argMax(email, created_at) as email',
'argMax(avatar, created_at) as avatar',
'argMax(properties, created_at) as properties',
'argMax(project_id, created_at) as project_id',
'max(created_at) as max_created_at',
].join(', ');
"id",
"argMax(first_name, created_at) as first_name",
"argMax(last_name, created_at) as last_name",
"argMax(email, created_at) as email",
"argMax(avatar, created_at) as avatar",
"argMax(properties, created_at) as properties",
"argMax(project_id, created_at) as project_id",
"max(created_at) as max_created_at",
].join(", ");
}
interface GetProfilesOptions {
@@ -53,9 +53,9 @@ export async function getProfiles({ ids }: GetProfilesOptions) {
`SELECT
${getProfileSelectFields()}
FROM profiles
WHERE id IN (${ids.map((id) => `'${id}'`).join(',')})
WHERE id IN (${ids.map((id) => `'${id}'`).join(",")})
GROUP BY id
`
`,
);
return data.map(transformProfile);
@@ -85,7 +85,7 @@ export async function getProfileList({
}
sb.limit = take;
sb.offset = (cursor ?? 0) * take;
sb.orderBy.created_at = 'max_created_at DESC';
sb.orderBy.created_at = "max_created_at DESC";
const data = await chQuery<IClickhouseProfile>(getSql());
return data.map(transformProfile);
}
@@ -93,9 +93,9 @@ export async function getProfileList({
export async function getProfileListCount({
projectId,
filters,
}: Omit<GetProfileListOptions, 'cursor' | 'take'>) {
}: Omit<GetProfileListOptions, "cursor" | "take">) {
const { sb, getSql } = createSqlBuilder();
sb.select.count = 'count(id) as count';
sb.select.count = "count(id) as count";
sb.from = getProfileInnerSelect(projectId);
if (filters) {
sb.where = {
@@ -109,7 +109,7 @@ export async function getProfileListCount({
export async function getProfilesByExternalId(
externalId: string | null,
projectId: string
projectId: string,
) {
if (externalId === null) {
return [];
@@ -121,7 +121,7 @@ export async function getProfilesByExternalId(
FROM profiles
GROUP BY id
HAVING project_id = '${projectId}' AND external_id = '${externalId}'
`
`,
);
return data.map(transformProfile);
@@ -129,7 +129,7 @@ export async function getProfilesByExternalId(
export type IServiceProfile = Omit<
IClickhouseProfile,
'max_created_at' | 'properties'
"max_created_at" | "properties"
> & {
createdAt: Date;
properties: Record<string, unknown>;
@@ -177,27 +177,27 @@ export async function upsertProfile({
projectId,
}: IServiceUpsertProfile) {
const [profile] = await chQuery<IClickhouseProfile>(
`SELECT * FROM profiles WHERE id = '${id}' AND project_id = '${projectId}' ORDER BY created_at DESC LIMIT 1`
`SELECT * FROM profiles WHERE id = '${id}' AND project_id = '${projectId}' ORDER BY created_at DESC LIMIT 1`,
);
await ch.insert({
table: 'profiles',
format: 'JSONEachRow',
table: "profiles",
format: "JSONEachRow",
clickhouse_settings: {
date_time_input_format: 'best_effort',
date_time_input_format: "best_effort",
},
values: [
{
id,
first_name: firstName ?? profile?.first_name ?? '',
last_name: lastName ?? profile?.last_name ?? '',
email: email ?? profile?.email ?? '',
avatar: avatar ?? profile?.avatar ?? '',
first_name: firstName ?? profile?.first_name ?? "",
last_name: lastName ?? profile?.last_name ?? "",
email: email ?? profile?.email ?? "",
avatar: avatar ?? profile?.avatar ?? "",
properties: toDots({
...(profile?.properties ?? {}),
...(properties ?? {}),
}),
project_id: projectId ?? profile?.project_id ?? '',
project_id: projectId ?? profile?.project_id ?? "",
created_at: new Date(),
},
],

View File

@@ -1,4 +1,4 @@
import { alphabetIds, lineTypes, timeRanges } from '@mixan/constants';
import { alphabetIds, lineTypes, timeRanges } from "@openpanel/constants";
import type {
IChartBreakdown,
IChartEvent,
@@ -6,42 +6,42 @@ import type {
IChartInput,
IChartLineType,
IChartRange,
} from '@mixan/validation';
} from "@openpanel/validation";
import { db } from '../prisma-client';
import type { Report as DbReport } from '../prisma-client';
import { db } from "../prisma-client";
import type { Report as DbReport } from "../prisma-client";
export type IServiceReport = Awaited<ReturnType<typeof getReportById>>;
export function transformFilter(
filter: Partial<IChartEventFilter>,
index: number
index: number,
): IChartEventFilter {
return {
id: filter.id ?? alphabetIds[index] ?? 'A',
name: filter.name ?? 'Unknown Filter',
operator: filter.operator ?? 'is',
id: filter.id ?? alphabetIds[index] ?? "A",
name: filter.name ?? "Unknown Filter",
operator: filter.operator ?? "is",
value:
typeof filter.value === 'string' ? [filter.value] : filter.value ?? [],
typeof filter.value === "string" ? [filter.value] : filter.value ?? [],
};
}
export function transformReportEvent(
event: Partial<IChartEvent>,
index: number
index: number,
): IChartEvent {
return {
segment: event.segment ?? 'event',
segment: event.segment ?? "event",
filters: (event.filters ?? []).map(transformFilter),
id: event.id ?? alphabetIds[index]!,
name: event.name || 'unknown_event',
name: event.name || "unknown_event",
displayName: event.displayName,
property: event.property,
};
}
export function transformReport(
report: DbReport
report: DbReport,
): IChartInput & { id: string } {
return {
id: report.id,
@@ -51,11 +51,11 @@ export function transformReport(
chartType: report.chart_type,
lineType: (report.line_type as IChartLineType) ?? lineTypes.monotone,
interval: report.interval,
name: report.name || 'Untitled',
range: (report.range as IChartRange) ?? timeRanges['1m'],
name: report.name || "Untitled",
range: (report.range as IChartRange) ?? timeRanges["1m"],
previous: report.previous ?? false,
formula: report.formula ?? undefined,
metric: report.metric ?? 'sum',
metric: report.metric ?? "sum",
unit: report.unit ?? undefined,
};
}