diff --git a/src/client/components/WebsiteOnlineCount.tsx b/src/client/components/WebsiteOnlineCount.tsx
new file mode 100644
index 0000000..50ce6f1
--- /dev/null
+++ b/src/client/components/WebsiteOnlineCount.tsx
@@ -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 (
+
+
+
{count} current visitor
+
+ );
+ }
+
+ return null;
+});
+WebsiteOnlineCount.displayName = 'WebsiteOnlineCount';
diff --git a/src/client/components/WebsiteOverview.tsx b/src/client/components/WebsiteOverview.tsx
index 2ad4749..08aea41 100644
--- a/src/client/components/WebsiteOverview.tsx
+++ b/src/client/components/WebsiteOverview.tsx
@@ -23,6 +23,7 @@ import { useEvent } from '../hooks/useEvent';
import { MetricCard } from './MetricCard';
import { formatNumber, formatShortTime } from '../utils/common';
import { useTheme } from '../hooks/useTheme';
+import { WebsiteOnlineCount } from './WebsiteOnlineCount';
interface WebsiteOverviewProps {
workspaceId: string;
@@ -110,6 +111,13 @@ const WebsiteOverviewItem: React.FC<{
status: 'health',
}))}
/>
+
+
+
+
diff --git a/src/server/model/website.ts b/src/server/model/website.ts
index cbe6d9c..4719962 100644
--- a/src/server/model/website.ts
+++ b/src/server/model/website.ts
@@ -10,6 +10,7 @@ import {
URL_LENGTH,
} from '../utils/const';
import type { DynamicData } from '../utils/types';
+import dayjs from 'dayjs';
export interface WebsiteEventPayload {
data?: object;
@@ -255,3 +256,20 @@ export async function saveWebsiteSessionData(data: {
}),
]);
}
+
+export async function getWebsiteOnlineUserCount(
+ websiteId: string
+): Promise {
+ 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;
+}
diff --git a/src/server/trpc/index.ts b/src/server/trpc/index.ts
index 449a4a0..1a23390 100644
--- a/src/server/trpc/index.ts
+++ b/src/server/trpc/index.ts
@@ -1,12 +1,10 @@
import * as trpcExpress from '@trpc/server/adapters/express';
-import { createContext, publicProcedure, router } from './trpc';
-import { z } from 'zod';
+import { createContext, router } from './trpc';
import { notificationRouter } from './routers/notification';
+import { websiteRouter } from './routers/website';
const appRouter = router({
- debug: publicProcedure.input(z.string()).query((opts) => {
- return { id: opts.input, name: 'Bilbo' };
- }),
+ website: websiteRouter,
notification: notificationRouter,
});
diff --git a/src/server/trpc/routers/website.ts b/src/server/trpc/routers/website.ts
new file mode 100644
index 0000000..984ef33
--- /dev/null
+++ b/src/server/trpc/routers/website.ts
@@ -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;
+ }),
+});