diff --git a/src/server/main.ts b/src/server/main.ts index 0445ce9..6c79456 100644 --- a/src/server/main.ts +++ b/src/server/main.ts @@ -47,6 +47,7 @@ app.use('/telemetry', telemetryRouter); if (env.allowOpenapi) { app.use('/open/_ui', swaggerUI.serve, swaggerUI.setup(trpcOpenapiDocument)); + app.use('/open/_document', (req, res) => res.send(trpcOpenapiDocument)); app.use('/open', trpcOpenapiHttpHandler); } app.use('/trpc', trpcExpressMiddleware); diff --git a/src/server/model/_schema/index.ts b/src/server/model/_schema/index.ts index 9d32562..726d5b9 100644 --- a/src/server/model/_schema/index.ts +++ b/src/server/model/_schema/index.ts @@ -21,3 +21,16 @@ export const userInfoSchema = z.object({ }) ), }); + +export const websiteInfoSchema = z.object({ + id: z.string(), + workspaceId: z.string(), + name: z.string(), + domain: z.string().nullable(), + shareId: z.string().nullable(), + resetAt: z.date().nullable(), + monitorId: z.string().nullable(), + createdAt: z.date(), + updatedAt: z.date(), + deletedAt: z.date().nullable(), +}); diff --git a/src/server/model/website.ts b/src/server/model/website.ts index a7708aa..4dfdf88 100644 --- a/src/server/model/website.ts +++ b/src/server/model/website.ts @@ -273,7 +273,7 @@ export async function getWebsiteOnlineUserCount( Ret[] >`SELECT count(distinct "sessionId") x FROM "WebsiteEvent" where "websiteId" = ${websiteId} AND "createdAt" >= ${startAt}`; - return res?.[0].x ?? 0; + return Number(res?.[0].x ?? 0); } export async function getSessionMetrics( diff --git a/src/server/trpc/index.ts b/src/server/trpc/index.ts index 671fe98..30625c8 100644 --- a/src/server/trpc/index.ts +++ b/src/server/trpc/index.ts @@ -19,9 +19,13 @@ export const trpcOpenapiHttpHandler = createOpenApiHttpHandler({ createContext, }); +const description = ` +

Insight into everything

+

Github: https://github.com/msgbyte/tianji

+`.trim(); export const trpcOpenapiDocument = generateOpenApiDocument(appRouter, { title: 'Tianji OpenAPI', - description: 'Insight into everything', + description, version: '1.0.0', baseUrl: '/open', }); diff --git a/src/server/trpc/routers/global.ts b/src/server/trpc/routers/global.ts index 8720889..e0fd494 100644 --- a/src/server/trpc/routers/global.ts +++ b/src/server/trpc/routers/global.ts @@ -1,12 +1,29 @@ +import { z } from 'zod'; import { publicProcedure, router } from '../trpc'; +import { OPENAPI_TAG } from '../../utils/const'; export const globalRouter = router({ - config: publicProcedure.query(async ({ input }) => { - return { - allowRegister: checkEnvTrusty(process.env.ALLOW_REGISTER), - websiteId: process.env.WEBSITE_ID, - }; - }), + config: publicProcedure + .meta({ + openapi: { + method: 'GET', + path: '/global/config', + tags: [OPENAPI_TAG.GLOBAL], + }, + }) + .input(z.void()) + .output( + z.object({ + allowRegister: z.boolean(), + websiteId: z.string().optional(), + }) + ) + .query(async ({ input }) => { + return { + allowRegister: checkEnvTrusty(process.env.ALLOW_REGISTER), + websiteId: process.env.WEBSITE_ID, + }; + }), }); function checkEnvTrusty(env: string | undefined): boolean { diff --git a/src/server/trpc/routers/website.ts b/src/server/trpc/routers/website.ts index 347e622..6c1ffe3 100644 --- a/src/server/trpc/routers/website.ts +++ b/src/server/trpc/routers/website.ts @@ -1,23 +1,38 @@ -import { router, workspaceOwnerProcedure, workspaceProcedure } from '../trpc'; +import { + OpenApiMetaInfo, + router, + workspaceOwnerProcedure, + workspaceProcedure, +} from '../trpc'; import { z } from 'zod'; import { getWebsiteOnlineUserCount } from '../../model/website'; import { prisma } from '../../model/_client'; import { EVENT_COLUMNS, FILTER_COLUMNS, + OPENAPI_TAG, SESSION_COLUMNS, hostnameRegex, } from '../../utils/const'; import { parseDateRange } from '../../utils/common'; import { getSessionMetrics, getPageviewMetrics } from '../../model/website'; +import { websiteInfoSchema } from '../../model/_schema'; +import { OpenApiMeta } from 'trpc-openapi'; export const websiteRouter = router({ onlineCount: workspaceProcedure + .meta( + buildWebsiteOpenapi({ + method: 'GET', + path: '/onlineCount', + }) + ) .input( z.object({ websiteId: z.string(), }) ) + .output(z.number()) .query(async ({ input }) => { const websiteId = input.websiteId; @@ -26,11 +41,18 @@ export const websiteRouter = router({ return count; }), info: workspaceProcedure + .meta( + buildWebsiteOpenapi({ + method: 'GET', + path: '/info', + }) + ) .input( z.object({ websiteId: z.string(), }) ) + .output(websiteInfoSchema.nullable()) .query(async ({ input }) => { const { workspaceId, websiteId } = input; @@ -44,6 +66,12 @@ export const websiteRouter = router({ return website; }), metrics: workspaceProcedure + .meta( + buildWebsiteOpenapi({ + method: 'GET', + path: '/metrics', + }) + ) .input( z.object({ websiteId: z.string(), @@ -157,6 +185,12 @@ export const websiteRouter = router({ return []; }), updateInfo: workspaceOwnerProcedure + .meta( + buildWebsiteOpenapi({ + method: 'PUT', + path: '/update', + }) + ) .input( z.object({ websiteId: z.string().cuid2(), @@ -168,6 +202,7 @@ export const websiteRouter = router({ monitorId: z.string().cuid2().nullish(), }) ) + .output(websiteInfoSchema) .mutation(async ({ input }) => { const { workspaceId, websiteId, name, domain, monitorId } = input; @@ -186,3 +221,14 @@ export const websiteRouter = router({ return websiteInfo; }), }); + +function buildWebsiteOpenapi(meta: OpenApiMetaInfo): OpenApiMeta { + return { + openapi: { + tags: [OPENAPI_TAG.WEBSITE], + protect: true, + ...meta, + path: `/workspace/{workspaceId}/website/{websiteId}${meta.path}`, + }, + }; +} diff --git a/src/server/trpc/trpc.ts b/src/server/trpc/trpc.ts index 1371a77..0509f75 100644 --- a/src/server/trpc/trpc.ts +++ b/src/server/trpc/trpc.ts @@ -17,6 +17,8 @@ export function createContext({ req }: { req: IncomingMessage }) { type Context = inferAsyncReturnType; const t = initTRPC.context().meta().create(); +export type OpenApiMetaInfo = NonNullable; + export const middleware = t.middleware; export const router = t.router; export const publicProcedure = t.procedure; diff --git a/src/server/utils/const.ts b/src/server/utils/const.ts index 0ca870d..cbbc977 100644 --- a/src/server/utils/const.ts +++ b/src/server/utils/const.ts @@ -115,7 +115,9 @@ export const FILTER_COLUMNS = { export const DEFAULT_RESET_DATE = '2000-01-01'; export enum OPENAPI_TAG { + GLOBAL = 'Global', USER = 'User', + WEBSITE = 'Website', } export const hostnameRegex = diff --git a/src/types/index.ts b/src/types/index.ts index 2d783e7..6438088 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,5 +1,3 @@ export * from './server'; export * from './monitor'; export * from './utils'; - -export type { MaybePromise } from '@trpc/server'; diff --git a/src/types/utils.ts b/src/types/utils.ts index f33a9e6..370b565 100644 --- a/src/types/utils.ts +++ b/src/types/utils.ts @@ -2,3 +2,5 @@ export type ExactType> = Omit & U; export type PickRequired = Omit & Required>; + +export type { MaybePromise } from '@trpc/server';