feat: add website online count

This commit is contained in:
moonrailgun 2023-09-28 18:01:04 +08:00
parent a3eb5a14eb
commit 6d1c4e7d17
5 changed files with 74 additions and 5 deletions

View File

@ -0,0 +1,26 @@
import React from 'react';
import { trpc } from '../api/trpc';
export const WebsiteOnlineCount: React.FC<{
workspaceId: string;
websiteId: string;
}> = React.memo((props) => {
const { workspaceId, websiteId } = props;
const { data: count } = trpc.website.onlineCount.useQuery({
workspaceId,
websiteId,
});
if (typeof count === 'number' && count > 0) {
return (
<div className="flex items-center space-x-2">
<div className="w-2.5 h-2.5 rounded-full bg-green-500" />
<span>{count} current visitor</span>
</div>
);
}
return null;
});
WebsiteOnlineCount.displayName = 'WebsiteOnlineCount';

View File

@ -23,6 +23,7 @@ import { useEvent } from '../hooks/useEvent';
import { MetricCard } from './MetricCard'; import { MetricCard } from './MetricCard';
import { formatNumber, formatShortTime } from '../utils/common'; import { formatNumber, formatShortTime } from '../utils/common';
import { useTheme } from '../hooks/useTheme'; import { useTheme } from '../hooks/useTheme';
import { WebsiteOnlineCount } from './WebsiteOnlineCount';
interface WebsiteOverviewProps { interface WebsiteOverviewProps {
workspaceId: string; workspaceId: string;
@ -110,6 +111,13 @@ const WebsiteOverviewItem: React.FC<{
status: 'health', status: 'health',
}))} }))}
/> />
<div className="ml-4 text-base font-normal">
<WebsiteOnlineCount
workspaceId={props.website.workspaceId}
websiteId={props.website.id}
/>
</div>
</div> </div>
<div> <div>

View File

@ -10,6 +10,7 @@ import {
URL_LENGTH, URL_LENGTH,
} from '../utils/const'; } from '../utils/const';
import type { DynamicData } from '../utils/types'; import type { DynamicData } from '../utils/types';
import dayjs from 'dayjs';
export interface WebsiteEventPayload { export interface WebsiteEventPayload {
data?: object; data?: object;
@ -255,3 +256,20 @@ export async function saveWebsiteSessionData(data: {
}), }),
]); ]);
} }
export async function getWebsiteOnlineUserCount(
websiteId: string
): Promise<number> {
const startAt = dayjs().subtract(5, 'minutes').toDate();
interface Ret {
x: number;
}
const res = await prisma.$queryRaw<
Ret[]
>`SELECT count(distinct "sessionId") x FROM "WebsiteEvent" where "websiteId" = ${websiteId}::uuid AND "createdAt" >= ${startAt}`;
console.log('res', res);
return res?.[0].x ?? 0;
}

View File

@ -1,12 +1,10 @@
import * as trpcExpress from '@trpc/server/adapters/express'; import * as trpcExpress from '@trpc/server/adapters/express';
import { createContext, publicProcedure, router } from './trpc'; import { createContext, router } from './trpc';
import { z } from 'zod';
import { notificationRouter } from './routers/notification'; import { notificationRouter } from './routers/notification';
import { websiteRouter } from './routers/website';
const appRouter = router({ const appRouter = router({
debug: publicProcedure.input(z.string()).query((opts) => { website: websiteRouter,
return { id: opts.input, name: 'Bilbo' };
}),
notification: notificationRouter, notification: notificationRouter,
}); });

View File

@ -0,0 +1,19 @@
import { router, workspaceProcedure } from '../trpc';
import { z } from 'zod';
import { getWebsiteOnlineUserCount } from '../../model/website';
export const websiteRouter = router({
onlineCount: workspaceProcedure
.input(
z.object({
websiteId: z.string(),
})
)
.query(async ({ input }) => {
const websiteId = input.websiteId;
const count = await getWebsiteOnlineUserCount(websiteId);
return count;
}),
});