feat: add telemetry list
This commit is contained in:
parent
dd0ad8c5de
commit
5e720abb11
@ -9,7 +9,7 @@ import { Register } from './pages/Register';
|
||||
import { QueryClientProvider } from '@tanstack/react-query';
|
||||
import { queryClient } from './api/cache';
|
||||
import { TokenLoginContainer } from './components/TokenLoginContainer';
|
||||
import React, { Suspense, useRef } from 'react';
|
||||
import React, { useRef } from 'react';
|
||||
import { trpc, trpcClient } from './api/trpc';
|
||||
import { MonitorPage } from './pages/Monitor';
|
||||
import { WebsitePage } from './pages/Website';
|
||||
@ -19,7 +19,7 @@ import { ConfigProvider, theme } from 'antd';
|
||||
import clsx from 'clsx';
|
||||
import { useSettingsStore } from './store/settings';
|
||||
import { StatusPage } from './pages/Status';
|
||||
import { Loading } from './components/Loading';
|
||||
import { TelemetryPage } from './pages/Telemetry';
|
||||
|
||||
export const AppRoutes: React.FC = React.memo(() => {
|
||||
const { info } = useUserStore();
|
||||
@ -35,6 +35,7 @@ export const AppRoutes: React.FC = React.memo(() => {
|
||||
<Route path="/monitor/*" element={<MonitorPage />} />
|
||||
<Route path="/website/*" element={<WebsitePage />} />
|
||||
<Route path="/servers" element={<Servers />} />
|
||||
<Route path="/telemetry/*" element={<TelemetryPage />} />
|
||||
<Route path="/settings/*" element={<SettingsPage />} />
|
||||
</Route>
|
||||
) : (
|
||||
|
146
src/client/components/telemetry/TelemetryList.tsx
Normal file
146
src/client/components/telemetry/TelemetryList.tsx
Normal file
@ -0,0 +1,146 @@
|
||||
import { t } from '@i18next-toolkit/react';
|
||||
import { Button, Form, Input, Modal, Table } from 'antd';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { AppRouterOutput, trpc } from '../../api/trpc';
|
||||
import { useCurrentWorkspaceId } from '../../store/user';
|
||||
import { type ColumnsType } from 'antd/es/table/interface';
|
||||
import {
|
||||
BarChartOutlined,
|
||||
EditOutlined,
|
||||
PlusOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { useNavigate } from 'react-router';
|
||||
import { PageHeader } from '../PageHeader';
|
||||
import { useEvent } from '../../hooks/useEvent';
|
||||
|
||||
type TelemetryInfo = AppRouterOutput['telemetry']['all'][number];
|
||||
|
||||
export const TelemetryList: React.FC = React.memo(() => {
|
||||
const workspaceId = useCurrentWorkspaceId();
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [form] = Form.useForm<{ id?: string; name: string }>();
|
||||
const upsertTelemetryMutation = trpc.telemetry.upsert.useMutation();
|
||||
const utils = trpc.useUtils();
|
||||
|
||||
const handleAddTelemetry = useEvent(async () => {
|
||||
await form.validateFields();
|
||||
const values = form.getFieldsValue();
|
||||
|
||||
await upsertTelemetryMutation.mutateAsync({
|
||||
telemetryId: values.id,
|
||||
workspaceId,
|
||||
name: values.name,
|
||||
});
|
||||
|
||||
utils.telemetry.all.refetch();
|
||||
|
||||
setIsModalOpen(false);
|
||||
|
||||
form.resetFields();
|
||||
});
|
||||
|
||||
const handleEditTelemetry = useEvent(async (info: TelemetryInfo) => {
|
||||
setIsModalOpen(true);
|
||||
form.setFieldsValue({
|
||||
id: info.id,
|
||||
name: info.name,
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<PageHeader
|
||||
title={t('Telemetry')}
|
||||
action={
|
||||
<div>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
size="large"
|
||||
onClick={() => setIsModalOpen(true)}
|
||||
>
|
||||
{t('Add Telemetry')}
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
|
||||
<TelemetryListTable onEdit={handleEditTelemetry} />
|
||||
|
||||
<Modal
|
||||
title={t('Add Telemetry')}
|
||||
open={isModalOpen}
|
||||
okButtonProps={{
|
||||
loading: upsertTelemetryMutation.isLoading,
|
||||
}}
|
||||
onOk={() => handleAddTelemetry()}
|
||||
onCancel={() => setIsModalOpen(false)}
|
||||
>
|
||||
<Form layout="vertical" form={form}>
|
||||
<Form.Item name="id" hidden={true} />
|
||||
<Form.Item
|
||||
label={t('Telemetry Name')}
|
||||
name="name"
|
||||
tooltip={t('Telemetry Name to Display')}
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
TelemetryList.displayName = 'TelemetryList';
|
||||
|
||||
const TelemetryListTable: React.FC<{
|
||||
onEdit: (info: TelemetryInfo) => void;
|
||||
}> = React.memo((props) => {
|
||||
const workspaceId = useCurrentWorkspaceId();
|
||||
const { data = [], isLoading } = trpc.telemetry.all.useQuery({
|
||||
workspaceId,
|
||||
});
|
||||
const navigate = useNavigate();
|
||||
|
||||
const columns = useMemo((): ColumnsType<TelemetryInfo> => {
|
||||
return [
|
||||
{
|
||||
dataIndex: 'name',
|
||||
title: t('Name'),
|
||||
},
|
||||
{
|
||||
key: 'action',
|
||||
render: (_, record) => {
|
||||
return (
|
||||
<div className="flex gap-2 justify-end">
|
||||
<Button
|
||||
icon={<EditOutlined />}
|
||||
onClick={() => props.onEdit(record)}
|
||||
>
|
||||
{t('Edit')}
|
||||
</Button>
|
||||
<Button
|
||||
icon={<BarChartOutlined />}
|
||||
onClick={() => {
|
||||
navigate(`/telemetry/${record.id}`);
|
||||
}}
|
||||
>
|
||||
{t('View')}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
] as ColumnsType<TelemetryInfo>;
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Table
|
||||
loading={isLoading}
|
||||
dataSource={data}
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
/>
|
||||
);
|
||||
});
|
||||
TelemetryListTable.displayName = 'TelemetryListTable';
|
@ -12,6 +12,7 @@ import { useIsMobile } from '../hooks/useIsMobile';
|
||||
import { RiMenuUnfoldLine } from 'react-icons/ri';
|
||||
import { useTranslation } from '@i18next-toolkit/react';
|
||||
import { LanguageSelector } from '../components/LanguageSelector';
|
||||
import { useGlobalConfig } from '../hooks/useConfig';
|
||||
|
||||
export const Layout: React.FC = React.memo(() => {
|
||||
const [params] = useSearchParams();
|
||||
@ -34,6 +35,7 @@ export const Layout: React.FC = React.memo(() => {
|
||||
const showHeader = !params.has('hideHeader');
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation();
|
||||
const { alphaMode } = useGlobalConfig();
|
||||
|
||||
const accountEl = (
|
||||
<Dropdown
|
||||
@ -117,6 +119,14 @@ export const Layout: React.FC = React.memo(() => {
|
||||
label={t('Servers')}
|
||||
onClick={() => setOpenDraw(false)}
|
||||
/>
|
||||
{alphaMode && (
|
||||
<MobileNavItem
|
||||
to="/telemetry"
|
||||
label={t('Telemetry')}
|
||||
onClick={() => setOpenDraw(false)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<MobileNavItem
|
||||
to="/settings"
|
||||
label={t('Settings')}
|
||||
@ -147,6 +157,7 @@ export const Layout: React.FC = React.memo(() => {
|
||||
<NavItem to="/monitor" label={t('Monitor')} />
|
||||
<NavItem to="/website" label={t('Website')} />
|
||||
<NavItem to="/servers" label={t('Servers')} />
|
||||
<NavItem to="/telemetry" label={t('Telemetry')} />
|
||||
<NavItem to="/settings" label={t('Settings')} />
|
||||
</div>
|
||||
|
||||
|
7
src/client/pages/Telemetry/index.tsx
Normal file
7
src/client/pages/Telemetry/index.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
import React from 'react';
|
||||
import { TelemetryList } from '../../components/telemetry/TelemetryList';
|
||||
|
||||
export const TelemetryPage: React.FC = React.memo(() => {
|
||||
return <TelemetryList />;
|
||||
});
|
||||
TelemetryPage.displayName = 'TelemetryPage';
|
@ -0,0 +1,35 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "TelemetryEvent" ADD COLUMN "telemetryId" VARCHAR(30);
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "TelemetrySession" ADD COLUMN "telemetryId" VARCHAR(30);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Telemetry" (
|
||||
"id" VARCHAR(30) NOT NULL,
|
||||
"workspaceId" VARCHAR(30) NOT NULL,
|
||||
"name" VARCHAR(100) NOT NULL,
|
||||
"createdAt" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMPTZ(6) NOT NULL,
|
||||
"deletedAt" TIMESTAMPTZ(6),
|
||||
|
||||
CONSTRAINT "Telemetry_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Telemetry_id_key" ON "Telemetry"("id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Telemetry_workspaceId_idx" ON "Telemetry"("workspaceId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Telemetry_createdAt_idx" ON "Telemetry"("createdAt");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Telemetry" ADD CONSTRAINT "Telemetry_workspaceId_fkey" FOREIGN KEY ("workspaceId") REFERENCES "Workspace"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "TelemetrySession" ADD CONSTRAINT "TelemetrySession_telemetryId_fkey" FOREIGN KEY ("telemetryId") REFERENCES "Telemetry"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "TelemetryEvent" ADD CONSTRAINT "TelemetryEvent_telemetryId_fkey" FOREIGN KEY ("telemetryId") REFERENCES "Telemetry"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
@ -49,6 +49,7 @@ model Workspace {
|
||||
notifications Notification[]
|
||||
monitors Monitor[]
|
||||
monitorStatusPages MonitorStatusPage[]
|
||||
telemetryList Telemetry[]
|
||||
|
||||
// for user currentWorkspace
|
||||
selectedUsers User[] // user list who select this workspace, not use in most of case
|
||||
@ -201,9 +202,27 @@ model WebsiteSessionData {
|
||||
@@index([sessionId])
|
||||
}
|
||||
|
||||
model Telemetry {
|
||||
id String @id @unique @default(cuid()) @db.VarChar(30)
|
||||
workspaceId String @db.VarChar(30)
|
||||
name String @db.VarChar(100)
|
||||
createdAt DateTime @default(now()) @db.Timestamptz(6)
|
||||
updatedAt DateTime @updatedAt @db.Timestamptz(6)
|
||||
deletedAt DateTime? @db.Timestamptz(6)
|
||||
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id], onUpdate: Cascade, onDelete: Cascade)
|
||||
|
||||
sessions TelemetrySession[]
|
||||
events TelemetryEvent[]
|
||||
|
||||
@@index([workspaceId])
|
||||
@@index([createdAt])
|
||||
}
|
||||
|
||||
model TelemetrySession {
|
||||
id String @id @unique @db.Uuid
|
||||
workspaceId String @db.VarChar(30)
|
||||
telemetryId String? @db.VarChar(30) // if null, means Default
|
||||
hostname String? @db.VarChar(100)
|
||||
browser String? @db.VarChar(20)
|
||||
os String? @db.VarChar(20)
|
||||
@ -217,6 +236,7 @@ model TelemetrySession {
|
||||
accuracyRadius Int?
|
||||
createdAt DateTime @default(now()) @db.Timestamptz(6)
|
||||
|
||||
telemetry Telemetry? @relation(fields: [telemetryId], references: [id], onUpdate: Cascade, onDelete: Cascade)
|
||||
telemetryEvent TelemetryEvent[]
|
||||
|
||||
@@index([createdAt])
|
||||
@ -225,8 +245,9 @@ model TelemetrySession {
|
||||
|
||||
model TelemetryEvent {
|
||||
id String @id() @default(cuid()) @db.VarChar(30)
|
||||
sessionId String @db.Uuid
|
||||
workspaceId String @db.VarChar(30)
|
||||
telemetryId String? @db.VarChar(30) // if null, means Default
|
||||
sessionId String @db.Uuid
|
||||
eventName String? @db.VarChar(100)
|
||||
urlOrigin String @db.VarChar(500)
|
||||
urlPath String @db.VarChar(500)
|
||||
@ -236,7 +257,8 @@ model TelemetryEvent {
|
||||
payload Json? @db.Json // Other payload info get from query params, should be a object
|
||||
createdAt DateTime @default(now()) @db.Timestamptz(6)
|
||||
|
||||
session TelemetrySession @relation(fields: [sessionId], references: [id], onUpdate: Cascade, onDelete: Cascade)
|
||||
telemetry Telemetry? @relation(fields: [telemetryId], references: [id], onUpdate: Cascade, onDelete: Cascade)
|
||||
session TelemetrySession @relation(fields: [sessionId], references: [id], onUpdate: Cascade, onDelete: Cascade)
|
||||
|
||||
@@index([createdAt])
|
||||
@@index([sessionId])
|
||||
|
@ -6,6 +6,7 @@ export * from "./websitesession"
|
||||
export * from "./websiteevent"
|
||||
export * from "./websiteeventdata"
|
||||
export * from "./websitesessiondata"
|
||||
export * from "./telemetry"
|
||||
export * from "./telemetrysession"
|
||||
export * from "./telemetryevent"
|
||||
export * from "./notification"
|
||||
|
29
src/server/prisma/zod/telemetry.ts
Normal file
29
src/server/prisma/zod/telemetry.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import * as z from "zod"
|
||||
import * as imports from "./schemas"
|
||||
import { CompleteWorkspace, RelatedWorkspaceModelSchema, CompleteTelemetrySession, RelatedTelemetrySessionModelSchema, CompleteTelemetryEvent, RelatedTelemetryEventModelSchema } from "./index"
|
||||
|
||||
export const TelemetryModelSchema = z.object({
|
||||
id: z.string(),
|
||||
workspaceId: z.string(),
|
||||
name: z.string(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
deletedAt: z.date().nullish(),
|
||||
})
|
||||
|
||||
export interface CompleteTelemetry extends z.infer<typeof TelemetryModelSchema> {
|
||||
workspace: CompleteWorkspace
|
||||
sessions: CompleteTelemetrySession[]
|
||||
events: CompleteTelemetryEvent[]
|
||||
}
|
||||
|
||||
/**
|
||||
* RelatedTelemetryModelSchema contains all relations on your model in addition to the scalars
|
||||
*
|
||||
* NOTE: Lazy required in case of potential circular dependencies within schema
|
||||
*/
|
||||
export const RelatedTelemetryModelSchema: z.ZodSchema<CompleteTelemetry> = z.lazy(() => TelemetryModelSchema.extend({
|
||||
workspace: RelatedWorkspaceModelSchema,
|
||||
sessions: RelatedTelemetrySessionModelSchema.array(),
|
||||
events: RelatedTelemetryEventModelSchema.array(),
|
||||
}))
|
@ -1,6 +1,6 @@
|
||||
import * as z from "zod"
|
||||
import * as imports from "./schemas"
|
||||
import { CompleteTelemetrySession, RelatedTelemetrySessionModelSchema } from "./index"
|
||||
import { CompleteTelemetry, RelatedTelemetryModelSchema, CompleteTelemetrySession, RelatedTelemetrySessionModelSchema } from "./index"
|
||||
|
||||
// Helper schema for JSON fields
|
||||
type Literal = boolean | number | string
|
||||
@ -10,8 +10,9 @@ const jsonSchema: z.ZodSchema<Json> = z.lazy(() => z.union([literalSchema, z.arr
|
||||
|
||||
export const TelemetryEventModelSchema = z.object({
|
||||
id: z.string(),
|
||||
sessionId: z.string(),
|
||||
workspaceId: z.string(),
|
||||
telemetryId: z.string().nullish(),
|
||||
sessionId: z.string(),
|
||||
eventName: z.string().nullish(),
|
||||
urlOrigin: z.string(),
|
||||
urlPath: z.string(),
|
||||
@ -23,6 +24,7 @@ export const TelemetryEventModelSchema = z.object({
|
||||
})
|
||||
|
||||
export interface CompleteTelemetryEvent extends z.infer<typeof TelemetryEventModelSchema> {
|
||||
telemetry?: CompleteTelemetry | null
|
||||
session: CompleteTelemetrySession
|
||||
}
|
||||
|
||||
@ -32,5 +34,6 @@ export interface CompleteTelemetryEvent extends z.infer<typeof TelemetryEventMod
|
||||
* NOTE: Lazy required in case of potential circular dependencies within schema
|
||||
*/
|
||||
export const RelatedTelemetryEventModelSchema: z.ZodSchema<CompleteTelemetryEvent> = z.lazy(() => TelemetryEventModelSchema.extend({
|
||||
telemetry: RelatedTelemetryModelSchema.nullish(),
|
||||
session: RelatedTelemetrySessionModelSchema,
|
||||
}))
|
||||
|
@ -1,10 +1,11 @@
|
||||
import * as z from "zod"
|
||||
import * as imports from "./schemas"
|
||||
import { CompleteTelemetryEvent, RelatedTelemetryEventModelSchema } from "./index"
|
||||
import { CompleteTelemetry, RelatedTelemetryModelSchema, CompleteTelemetryEvent, RelatedTelemetryEventModelSchema } from "./index"
|
||||
|
||||
export const TelemetrySessionModelSchema = z.object({
|
||||
id: z.string(),
|
||||
workspaceId: z.string(),
|
||||
telemetryId: z.string().nullish(),
|
||||
hostname: z.string().nullish(),
|
||||
browser: z.string().nullish(),
|
||||
os: z.string().nullish(),
|
||||
@ -20,6 +21,7 @@ export const TelemetrySessionModelSchema = z.object({
|
||||
})
|
||||
|
||||
export interface CompleteTelemetrySession extends z.infer<typeof TelemetrySessionModelSchema> {
|
||||
telemetry?: CompleteTelemetry | null
|
||||
telemetryEvent: CompleteTelemetryEvent[]
|
||||
}
|
||||
|
||||
@ -29,5 +31,6 @@ export interface CompleteTelemetrySession extends z.infer<typeof TelemetrySessio
|
||||
* NOTE: Lazy required in case of potential circular dependencies within schema
|
||||
*/
|
||||
export const RelatedTelemetrySessionModelSchema: z.ZodSchema<CompleteTelemetrySession> = z.lazy(() => TelemetrySessionModelSchema.extend({
|
||||
telemetry: RelatedTelemetryModelSchema.nullish(),
|
||||
telemetryEvent: RelatedTelemetryEventModelSchema.array(),
|
||||
}))
|
||||
|
@ -1,6 +1,6 @@
|
||||
import * as z from "zod"
|
||||
import * as imports from "./schemas"
|
||||
import { CompleteWorkspacesOnUsers, RelatedWorkspacesOnUsersModelSchema, CompleteWebsite, RelatedWebsiteModelSchema, CompleteNotification, RelatedNotificationModelSchema, CompleteMonitor, RelatedMonitorModelSchema, CompleteMonitorStatusPage, RelatedMonitorStatusPageModelSchema, CompleteUser, RelatedUserModelSchema, CompleteWorkspaceDailyUsage, RelatedWorkspaceDailyUsageModelSchema, CompleteWorkspaceAuditLog, RelatedWorkspaceAuditLogModelSchema } from "./index"
|
||||
import { CompleteWorkspacesOnUsers, RelatedWorkspacesOnUsersModelSchema, CompleteWebsite, RelatedWebsiteModelSchema, CompleteNotification, RelatedNotificationModelSchema, CompleteMonitor, RelatedMonitorModelSchema, CompleteMonitorStatusPage, RelatedMonitorStatusPageModelSchema, CompleteTelemetry, RelatedTelemetryModelSchema, CompleteUser, RelatedUserModelSchema, CompleteWorkspaceDailyUsage, RelatedWorkspaceDailyUsageModelSchema, CompleteWorkspaceAuditLog, RelatedWorkspaceAuditLogModelSchema } from "./index"
|
||||
|
||||
// Helper schema for JSON fields
|
||||
type Literal = boolean | number | string
|
||||
@ -30,6 +30,7 @@ export interface CompleteWorkspace extends z.infer<typeof WorkspaceModelSchema>
|
||||
notifications: CompleteNotification[]
|
||||
monitors: CompleteMonitor[]
|
||||
monitorStatusPages: CompleteMonitorStatusPage[]
|
||||
telemetryList: CompleteTelemetry[]
|
||||
selectedUsers: CompleteUser[]
|
||||
workspaceDailyUsage: CompleteWorkspaceDailyUsage[]
|
||||
workspaceAuditLog: CompleteWorkspaceAuditLog[]
|
||||
@ -46,6 +47,7 @@ export const RelatedWorkspaceModelSchema: z.ZodSchema<CompleteWorkspace> = z.laz
|
||||
notifications: RelatedNotificationModelSchema.array(),
|
||||
monitors: RelatedMonitorModelSchema.array(),
|
||||
monitorStatusPages: RelatedMonitorStatusPageModelSchema.array(),
|
||||
telemetryList: RelatedTelemetryModelSchema.array(),
|
||||
selectedUsers: RelatedUserModelSchema.array(),
|
||||
workspaceDailyUsage: RelatedWorkspaceDailyUsageModelSchema.array(),
|
||||
workspaceAuditLog: RelatedWorkspaceAuditLogModelSchema.array(),
|
||||
|
@ -1,20 +1,20 @@
|
||||
import { z } from 'zod';
|
||||
import { router, workspaceProcedure } from '../trpc';
|
||||
import { OpenApiMetaInfo, router, workspaceProcedure } from '../trpc';
|
||||
import { OPENAPI_TAG } from '../../utils/const';
|
||||
import { WorkspaceAuditLogModelSchema } from '../../prisma/zod';
|
||||
import { prisma } from '../../model/_client';
|
||||
import { fetchDataByCursor } from '../../utils/prisma';
|
||||
import { OpenApiMeta } from 'trpc-openapi';
|
||||
|
||||
export const auditLogRouter = router({
|
||||
fetchByCursor: workspaceProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
.meta(
|
||||
buildAuditLogOpenapi({
|
||||
method: 'GET',
|
||||
path: '/fetchByCursor',
|
||||
tags: [OPENAPI_TAG.AUDIT_LOG],
|
||||
description: 'Fetch workspace audit log',
|
||||
},
|
||||
})
|
||||
})
|
||||
)
|
||||
.input(
|
||||
z.object({
|
||||
limit: z.number().min(1).max(100).default(50),
|
||||
@ -47,3 +47,14 @@ export const auditLogRouter = router({
|
||||
};
|
||||
}),
|
||||
});
|
||||
|
||||
function buildAuditLogOpenapi(meta: OpenApiMetaInfo): OpenApiMeta {
|
||||
return {
|
||||
openapi: {
|
||||
tags: [OPENAPI_TAG.AUDIT_LOG],
|
||||
protect: true,
|
||||
...meta,
|
||||
path: `/audit${meta.path}`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -1,18 +1,18 @@
|
||||
import { z } from 'zod';
|
||||
import { router, workspaceProcedure } from '../trpc';
|
||||
import { OpenApiMetaInfo, router, workspaceProcedure } from '../trpc';
|
||||
import { OPENAPI_TAG } from '../../utils/const';
|
||||
import { prisma } from '../../model/_client';
|
||||
import { OpenApiMeta } from 'trpc-openapi';
|
||||
|
||||
export const billingRouter = router({
|
||||
usage: workspaceProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
.meta(
|
||||
buildBillingOpenapi({
|
||||
method: 'GET',
|
||||
path: '/usage',
|
||||
tags: [OPENAPI_TAG.BILLING],
|
||||
description: 'get workspace usage',
|
||||
},
|
||||
})
|
||||
})
|
||||
)
|
||||
.input(
|
||||
z.object({
|
||||
startAt: z.number(),
|
||||
@ -51,3 +51,14 @@ export const billingRouter = router({
|
||||
};
|
||||
}),
|
||||
});
|
||||
|
||||
function buildBillingOpenapi(meta: OpenApiMetaInfo): OpenApiMeta {
|
||||
return {
|
||||
openapi: {
|
||||
tags: [OPENAPI_TAG.BILLING],
|
||||
protect: true,
|
||||
...meta,
|
||||
path: `/billing${meta.path}`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import { globalRouter } from './global';
|
||||
import { serverStatusRouter } from './serverStatus';
|
||||
import { auditLogRouter } from './auditLog';
|
||||
import { billingRouter } from './billing';
|
||||
import { telemetryRouter } from './telemetry';
|
||||
|
||||
export const appRouter = router({
|
||||
global: globalRouter,
|
||||
@ -16,6 +17,7 @@ export const appRouter = router({
|
||||
website: websiteRouter,
|
||||
notification: notificationRouter,
|
||||
monitor: monitorRouter,
|
||||
telemetry: telemetryRouter,
|
||||
serverStatus: serverStatusRouter,
|
||||
auditLog: auditLogRouter,
|
||||
billing: billingRouter,
|
||||
|
83
src/server/trpc/routers/telemetry.ts
Normal file
83
src/server/trpc/routers/telemetry.ts
Normal file
@ -0,0 +1,83 @@
|
||||
import { z } from 'zod';
|
||||
import {
|
||||
OpenApiMetaInfo,
|
||||
router,
|
||||
workspaceOwnerProcedure,
|
||||
workspaceProcedure,
|
||||
} from '../trpc';
|
||||
import { OPENAPI_TAG } from '../../utils/const';
|
||||
import { prisma } from '../../model/_client';
|
||||
import { TelemetryModelSchema } from '../../prisma/zod';
|
||||
import { OpenApiMeta } from 'trpc-openapi';
|
||||
|
||||
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',
|
||||
},
|
||||
});
|
||||
|
||||
return res;
|
||||
}),
|
||||
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,
|
||||
},
|
||||
});
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
||||
function buildTelemetryOpenapi(meta: OpenApiMetaInfo): OpenApiMeta {
|
||||
return {
|
||||
openapi: {
|
||||
tags: [OPENAPI_TAG.TELEMETRY],
|
||||
protect: true,
|
||||
...meta,
|
||||
path: `/workspace/{workspaceId}${meta.path}`,
|
||||
},
|
||||
};
|
||||
}
|
@ -109,4 +109,5 @@ export enum OPENAPI_TAG {
|
||||
MONITOR = 'Monitor',
|
||||
AUDIT_LOG = 'AuditLog',
|
||||
BILLING = 'Billing',
|
||||
TELEMETRY = 'Telemetry',
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user