2023-10-22 17:44:08 +00:00
|
|
|
import {
|
|
|
|
OpenApiMetaInfo,
|
|
|
|
router,
|
|
|
|
workspaceOwnerProcedure,
|
|
|
|
workspaceProcedure,
|
|
|
|
} from '../trpc';
|
2023-09-28 10:01:04 +00:00
|
|
|
import { z } from 'zod';
|
|
|
|
import { getWebsiteOnlineUserCount } from '../../model/website';
|
2023-10-06 07:04:55 +00:00
|
|
|
import { prisma } from '../../model/_client';
|
2023-10-06 14:08:15 +00:00
|
|
|
import {
|
|
|
|
EVENT_COLUMNS,
|
|
|
|
FILTER_COLUMNS,
|
2023-10-22 17:44:08 +00:00
|
|
|
OPENAPI_TAG,
|
2023-10-06 14:08:15 +00:00
|
|
|
SESSION_COLUMNS,
|
2023-10-22 16:37:30 +00:00
|
|
|
hostnameRegex,
|
2023-10-06 14:08:15 +00:00
|
|
|
} from '../../utils/const';
|
|
|
|
import { parseDateRange } from '../../utils/common';
|
|
|
|
import { getSessionMetrics, getPageviewMetrics } from '../../model/website';
|
2023-10-22 17:44:08 +00:00
|
|
|
import { websiteInfoSchema } from '../../model/_schema';
|
|
|
|
import { OpenApiMeta } from 'trpc-openapi';
|
2023-09-28 10:01:04 +00:00
|
|
|
|
|
|
|
export const websiteRouter = router({
|
|
|
|
onlineCount: workspaceProcedure
|
2023-10-22 17:44:08 +00:00
|
|
|
.meta(
|
|
|
|
buildWebsiteOpenapi({
|
|
|
|
method: 'GET',
|
|
|
|
path: '/onlineCount',
|
|
|
|
})
|
|
|
|
)
|
2023-09-28 10:01:04 +00:00
|
|
|
.input(
|
|
|
|
z.object({
|
|
|
|
websiteId: z.string(),
|
|
|
|
})
|
|
|
|
)
|
2023-10-22 17:44:08 +00:00
|
|
|
.output(z.number())
|
2023-09-28 10:01:04 +00:00
|
|
|
.query(async ({ input }) => {
|
|
|
|
const websiteId = input.websiteId;
|
|
|
|
|
|
|
|
const count = await getWebsiteOnlineUserCount(websiteId);
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}),
|
2023-11-12 15:49:02 +00:00
|
|
|
all: workspaceProcedure
|
|
|
|
.meta({
|
|
|
|
openapi: {
|
|
|
|
method: 'GET',
|
|
|
|
path: `/workspace/{workspaceId}/website/all`,
|
|
|
|
tags: [OPENAPI_TAG.WEBSITE],
|
|
|
|
protect: true,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
.output(z.array(websiteInfoSchema))
|
|
|
|
.query(async ({ input }) => {
|
|
|
|
const { workspaceId } = input;
|
|
|
|
|
|
|
|
const websites = await prisma.website.findMany({
|
|
|
|
where: {
|
|
|
|
workspaceId,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
return websites;
|
|
|
|
}),
|
2023-10-06 07:04:55 +00:00
|
|
|
info: workspaceProcedure
|
2023-10-22 17:44:08 +00:00
|
|
|
.meta(
|
|
|
|
buildWebsiteOpenapi({
|
|
|
|
method: 'GET',
|
|
|
|
path: '/info',
|
|
|
|
})
|
|
|
|
)
|
2023-10-06 07:04:55 +00:00
|
|
|
.input(
|
|
|
|
z.object({
|
|
|
|
websiteId: z.string(),
|
|
|
|
})
|
|
|
|
)
|
2023-10-22 17:44:08 +00:00
|
|
|
.output(websiteInfoSchema.nullable())
|
2023-10-06 07:04:55 +00:00
|
|
|
.query(async ({ input }) => {
|
2023-10-14 17:01:34 +00:00
|
|
|
const { workspaceId, websiteId } = input;
|
2023-10-06 07:04:55 +00:00
|
|
|
|
|
|
|
const website = await prisma.website.findUnique({
|
|
|
|
where: {
|
|
|
|
id: websiteId,
|
2023-10-14 17:01:34 +00:00
|
|
|
workspaceId,
|
2023-10-06 07:04:55 +00:00
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
return website;
|
|
|
|
}),
|
2023-10-06 14:08:15 +00:00
|
|
|
metrics: workspaceProcedure
|
2023-10-22 17:44:08 +00:00
|
|
|
.meta(
|
|
|
|
buildWebsiteOpenapi({
|
|
|
|
method: 'GET',
|
|
|
|
path: '/metrics',
|
|
|
|
})
|
|
|
|
)
|
2023-10-06 14:08:15 +00:00
|
|
|
.input(
|
|
|
|
z.object({
|
|
|
|
websiteId: z.string(),
|
|
|
|
type: z.enum([
|
|
|
|
'url',
|
|
|
|
'language',
|
|
|
|
'referrer',
|
|
|
|
'browser',
|
|
|
|
'os',
|
|
|
|
'device',
|
|
|
|
'country',
|
|
|
|
'event',
|
|
|
|
]),
|
|
|
|
startAt: z.number(),
|
|
|
|
endAt: z.number(),
|
|
|
|
url: z.string().optional(),
|
|
|
|
referrer: z.string().optional(),
|
|
|
|
title: z.string().optional(),
|
|
|
|
os: z.string().optional(),
|
|
|
|
browser: z.string().optional(),
|
|
|
|
device: z.string().optional(),
|
|
|
|
country: z.string().optional(),
|
|
|
|
region: z.string().optional(),
|
|
|
|
city: z.string().optional(),
|
|
|
|
language: z.string().optional(),
|
|
|
|
event: z.string().optional(),
|
|
|
|
})
|
|
|
|
)
|
2023-10-07 09:28:30 +00:00
|
|
|
.output(
|
|
|
|
z.array(
|
|
|
|
z.object({
|
2023-10-07 11:32:46 +00:00
|
|
|
x: z.string().nullable(),
|
|
|
|
y: z.number(),
|
2023-10-07 09:28:30 +00:00
|
|
|
})
|
|
|
|
)
|
|
|
|
)
|
2023-10-06 14:08:15 +00:00
|
|
|
.query(async ({ input }) => {
|
|
|
|
const {
|
|
|
|
websiteId,
|
|
|
|
type,
|
|
|
|
startAt,
|
|
|
|
endAt,
|
|
|
|
url,
|
|
|
|
referrer,
|
|
|
|
title,
|
|
|
|
os,
|
|
|
|
browser,
|
|
|
|
device,
|
|
|
|
country,
|
|
|
|
region,
|
|
|
|
city,
|
|
|
|
language,
|
|
|
|
event,
|
|
|
|
} = input;
|
|
|
|
|
|
|
|
const { startDate, endDate } = await parseDateRange({
|
|
|
|
websiteId,
|
|
|
|
startAt,
|
|
|
|
endAt,
|
|
|
|
});
|
|
|
|
|
|
|
|
const filters = {
|
|
|
|
startDate,
|
|
|
|
endDate,
|
|
|
|
url,
|
|
|
|
referrer,
|
|
|
|
title,
|
|
|
|
os,
|
|
|
|
browser,
|
|
|
|
device,
|
|
|
|
country,
|
|
|
|
region,
|
|
|
|
city,
|
|
|
|
language,
|
|
|
|
event,
|
|
|
|
};
|
|
|
|
|
|
|
|
const column = FILTER_COLUMNS[type] || type;
|
|
|
|
|
|
|
|
if (SESSION_COLUMNS.includes(type)) {
|
|
|
|
const data = await getSessionMetrics(websiteId, column, filters);
|
|
|
|
|
|
|
|
if (type === 'language') {
|
|
|
|
const combined: Record<string, any> = {};
|
|
|
|
|
|
|
|
for (const { x, y } of data) {
|
|
|
|
const key = String(x).toLowerCase().split('-')[0];
|
|
|
|
|
|
|
|
if (combined[key] === undefined) {
|
|
|
|
combined[key] = { x: key, y };
|
|
|
|
} else {
|
|
|
|
combined[key].y += y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-07 11:32:46 +00:00
|
|
|
return Object.values(combined).map((d) => ({
|
|
|
|
x: d.x,
|
|
|
|
y: Number(d.y),
|
|
|
|
}));
|
2023-10-06 14:08:15 +00:00
|
|
|
}
|
|
|
|
|
2023-10-07 11:32:46 +00:00
|
|
|
return data.map((d) => ({ x: d.x, y: Number(d.y) }));
|
2023-10-06 14:08:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (EVENT_COLUMNS.includes(type)) {
|
|
|
|
const data = await getPageviewMetrics(websiteId, column, filters);
|
|
|
|
|
2023-10-07 11:32:46 +00:00
|
|
|
return data.map((d) => ({ x: d.x, y: Number(d.y) }));
|
2023-10-06 14:08:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return [];
|
|
|
|
}),
|
2023-10-14 16:39:14 +00:00
|
|
|
updateInfo: workspaceOwnerProcedure
|
2023-10-22 17:44:08 +00:00
|
|
|
.meta(
|
|
|
|
buildWebsiteOpenapi({
|
|
|
|
method: 'PUT',
|
|
|
|
path: '/update',
|
|
|
|
})
|
|
|
|
)
|
2023-10-14 16:39:14 +00:00
|
|
|
.input(
|
|
|
|
z.object({
|
2023-10-14 16:51:03 +00:00
|
|
|
websiteId: z.string().cuid2(),
|
2023-10-14 16:39:14 +00:00
|
|
|
name: z.string().max(100),
|
2023-10-22 16:37:30 +00:00
|
|
|
domain: z.union([
|
|
|
|
z.string().max(500).regex(hostnameRegex),
|
|
|
|
z.string().max(500).ip(),
|
|
|
|
]),
|
2023-10-20 16:18:48 +00:00
|
|
|
monitorId: z.string().cuid2().nullish(),
|
2023-10-14 16:39:14 +00:00
|
|
|
})
|
|
|
|
)
|
2023-10-22 17:44:08 +00:00
|
|
|
.output(websiteInfoSchema)
|
2023-10-14 16:39:14 +00:00
|
|
|
.mutation(async ({ input }) => {
|
2023-10-14 16:51:03 +00:00
|
|
|
const { workspaceId, websiteId, name, domain, monitorId } = input;
|
2023-10-14 16:39:14 +00:00
|
|
|
|
|
|
|
const websiteInfo = await prisma.website.update({
|
|
|
|
where: {
|
|
|
|
id: websiteId,
|
|
|
|
workspaceId,
|
|
|
|
},
|
|
|
|
data: {
|
|
|
|
name,
|
|
|
|
domain,
|
2023-10-14 16:51:03 +00:00
|
|
|
monitorId,
|
2023-10-14 16:39:14 +00:00
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
return websiteInfo;
|
|
|
|
}),
|
2023-09-28 10:01:04 +00:00
|
|
|
});
|
2023-10-22 17:44:08 +00:00
|
|
|
|
|
|
|
function buildWebsiteOpenapi(meta: OpenApiMetaInfo): OpenApiMeta {
|
|
|
|
return {
|
|
|
|
openapi: {
|
|
|
|
tags: [OPENAPI_TAG.WEBSITE],
|
|
|
|
protect: true,
|
|
|
|
...meta,
|
|
|
|
path: `/workspace/{workspaceId}/website/{websiteId}${meta.path}`,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|