tianji/src/server/trpc/routers/telemetry.ts

361 lines
8.1 KiB
TypeScript
Raw Normal View History

2024-02-17 16:47:22 +00:00
import { z } from 'zod';
import {
OpenApiMetaInfo,
router,
workspaceOwnerProcedure,
workspaceProcedure,
} from '../trpc';
2024-02-27 12:36:56 +00:00
import {
EVENT_COLUMNS,
FILTER_COLUMNS,
OPENAPI_TAG,
SESSION_COLUMNS,
} from '../../utils/const';
2024-02-17 16:47:22 +00:00
import { prisma } from '../../model/_client';
import { TelemetryModelSchema } from '../../prisma/zod';
import { OpenApiMeta } from 'trpc-openapi';
2024-02-27 16:34:02 +00:00
import {
baseFilterSchema,
baseStatsSchema,
statsItemType,
} from '../../model/_schema/filter';
2024-02-27 12:36:56 +00:00
import {
getTelemetryPageview,
getTelemetryPageviewMetrics,
getTelemetrySession,
getTelemetrySessionMetrics,
2024-02-27 16:34:02 +00:00
getTelemetryStats,
2024-03-03 16:48:51 +00:00
getTelemetryUrlMetrics,
2024-02-27 12:36:56 +00:00
} from '../../model/telemetry';
import { BaseQueryFilters } from '../../utils/prisma';
2024-02-27 16:34:02 +00:00
import dayjs from 'dayjs';
2024-02-17 16:47:22 +00:00
export const telemetryRouter = router({
all: workspaceProcedure
.meta(
buildTelemetryOpenapi({
method: 'GET',
path: '/all',
})
)
.output(z.array(TelemetryModelSchema))
.query(async ({ input }) => {
const { workspaceId } = input;
const res = await prisma.telemetry.findMany({
where: {
workspaceId,
},
orderBy: {
updatedAt: 'desc',
},
});
2024-02-27 16:34:02 +00:00
return res;
}),
info: workspaceProcedure
.meta(
buildTelemetryOpenapi({
method: 'GET',
path: '/info',
})
)
.input(
z.object({
telemetryId: z.string(),
})
)
.output(TelemetryModelSchema.nullable())
.query(async ({ input }) => {
const { workspaceId, telemetryId } = input;
const res = await prisma.telemetry.findUnique({
where: {
workspaceId,
id: telemetryId,
},
});
2024-02-17 16:47:22 +00:00
return res;
}),
2024-02-22 16:07:30 +00:00
eventCount: workspaceProcedure
.meta(
buildTelemetryOpenapi({
method: 'GET',
path: '/eventCount',
})
)
.input(
z.object({
telemetryId: z.string(),
})
)
.output(z.number())
.query(async ({ input }) => {
const { workspaceId, telemetryId } = input;
const count = await prisma.telemetryEvent.count({
where: {
workspaceId,
telemetryId,
},
});
return count;
}),
2024-02-17 16:47:22 +00:00
upsert: workspaceOwnerProcedure
.meta(
buildTelemetryOpenapi({
method: 'POST',
path: '/upsert',
})
)
.input(
z.object({
telemetryId: z.string().optional(),
name: z.string(),
})
)
.output(TelemetryModelSchema)
.mutation(async ({ input }) => {
const { workspaceId, telemetryId, name } = input;
if (telemetryId) {
return prisma.telemetry.update({
where: {
id: telemetryId,
workspaceId,
},
data: {
name,
},
});
} else {
return prisma.telemetry.create({
data: {
workspaceId,
name,
},
});
}
}),
2024-02-27 12:36:56 +00:00
pageviews: workspaceProcedure
.meta(
buildTelemetryOpenapi({
method: 'GET',
path: '/pageviews',
})
)
.input(
z
.object({
telemetryId: z.string(),
startAt: z.number(),
endAt: z.number(),
unit: z.string().optional(),
})
.merge(baseFilterSchema.partial())
)
.output(z.object({ pageviews: z.any(), sessions: z.any() }))
.query(async ({ input }) => {
const { telemetryId, startAt, endAt, url, country, region, city } = input;
const startDate = new Date(startAt);
const endDate = new Date(endAt);
// const { startDate, endDate, unit } = await parseDateRange({
// websiteId,
// startAt: Number(startAt),
// endAt: Number(endAt),
// unit: String(input.unit),
// });
const filters = {
startDate,
endDate,
unit: input.unit,
url,
country,
region,
city,
};
const [pageviews, sessions] = await Promise.all([
getTelemetryPageview(telemetryId, filters as BaseQueryFilters),
getTelemetrySession(telemetryId, filters as BaseQueryFilters),
]);
return {
pageviews,
sessions,
};
}),
metrics: workspaceProcedure
.meta(
buildTelemetryOpenapi({
method: 'GET',
path: '/metrics',
})
)
.input(
z
.object({
2024-03-03 16:48:51 +00:00
telemetryId: z.string(),
type: z.enum(['source', 'url', 'referrer', 'country']),
2024-02-27 12:36:56 +00:00
startAt: z.number(),
endAt: z.number(),
})
.merge(baseFilterSchema.partial())
)
.output(
z.array(
z.object({
x: z.string().nullable(),
y: z.number(),
})
)
)
.query(async ({ input }) => {
2024-03-03 16:48:51 +00:00
const { telemetryId, type, startAt, endAt, url, country, region, city } =
2024-02-27 12:36:56 +00:00
input;
const startDate = new Date(startAt);
const endDate = new Date(endAt);
// const { startDate, endDate } = await parseDateRange({
// websiteId,
// startAt,
// endAt,
// });
const filters = {
startDate,
endDate,
url,
country,
region,
city,
};
2024-03-03 16:48:51 +00:00
if (type === 'source') {
const data = await getTelemetryUrlMetrics(telemetryId, filters);
return data.map((d) => ({ x: d.x, y: Number(d.y) }));
}
2024-02-27 12:36:56 +00:00
const column = FILTER_COLUMNS[type] || type;
if (SESSION_COLUMNS.includes(type)) {
const data = await getTelemetrySessionMetrics(
2024-03-03 16:48:51 +00:00
telemetryId,
2024-02-27 12:36:56 +00:00
column,
filters
);
return data.map((d) => ({ x: d.x, y: Number(d.y) }));
}
if (EVENT_COLUMNS.includes(type)) {
const data = await getTelemetryPageviewMetrics(
2024-03-03 16:48:51 +00:00
telemetryId,
2024-02-27 12:36:56 +00:00
column,
filters
);
return data.map((d) => ({ x: d.x, y: Number(d.y) }));
}
return [];
}),
2024-02-27 16:34:02 +00:00
stats: workspaceProcedure
.meta(
buildTelemetryOpenapi({
method: 'GET',
path: '/stats',
})
)
.input(
z
.object({
telemetryId: z.string(),
startAt: z.number(),
endAt: z.number(),
unit: z.string().optional(),
})
.merge(baseFilterSchema.partial())
)
.output(baseStatsSchema)
.query(async ({ input }) => {
const {
telemetryId,
timezone,
url,
country,
region,
city,
startAt,
endAt,
} = input;
const startDate = new Date(startAt);
const endDate = new Date(endAt);
// const { startDate, endDate, unit } = await parseDateRange({
// telemetryId,
// startAt: Number(startAt),
// endAt: Number(endAt),
// unit: input.unit,
// });
const diff = dayjs(endDate).diff(startDate, 'minutes');
const prevStartDate = dayjs(startDate).subtract(diff, 'minutes').toDate();
const prevEndDate = dayjs(endDate).subtract(diff, 'minutes').toDate();
const filters = {
startDate,
endDate,
timezone,
unit: input.unit,
url,
country,
region,
city,
} as BaseQueryFilters;
const [metrics, prevPeriod] = await Promise.all([
getTelemetryStats(telemetryId, {
...filters,
startDate,
endDate,
}),
getTelemetryStats(telemetryId, {
...filters,
startDate: prevStartDate,
endDate: prevEndDate,
}),
]);
const stats = Object.keys(metrics[0]).reduce((obj, key) => {
const current = Number(metrics[0][key]) || 0;
const prev = Number(prevPeriod[0][key]) || 0;
obj[key] = {
value: current,
prev,
};
return obj;
}, {} as Record<string, { value: number; prev: number }>);
return baseStatsSchema.parse(stats);
}),
2024-02-17 16:47:22 +00:00
});
function buildTelemetryOpenapi(meta: OpenApiMetaInfo): OpenApiMeta {
return {
openapi: {
tags: [OPENAPI_TAG.TELEMETRY],
protect: true,
...meta,
path: `/workspace/{workspaceId}${meta.path}`,
},
};
}