docs: add openapi for global and website
This commit is contained in:
parent
243b1dad81
commit
594f3124ef
@ -47,6 +47,7 @@ app.use('/telemetry', telemetryRouter);
|
|||||||
|
|
||||||
if (env.allowOpenapi) {
|
if (env.allowOpenapi) {
|
||||||
app.use('/open/_ui', swaggerUI.serve, swaggerUI.setup(trpcOpenapiDocument));
|
app.use('/open/_ui', swaggerUI.serve, swaggerUI.setup(trpcOpenapiDocument));
|
||||||
|
app.use('/open/_document', (req, res) => res.send(trpcOpenapiDocument));
|
||||||
app.use('/open', trpcOpenapiHttpHandler);
|
app.use('/open', trpcOpenapiHttpHandler);
|
||||||
}
|
}
|
||||||
app.use('/trpc', trpcExpressMiddleware);
|
app.use('/trpc', trpcExpressMiddleware);
|
||||||
|
@ -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(),
|
||||||
|
});
|
||||||
|
@ -273,7 +273,7 @@ export async function getWebsiteOnlineUserCount(
|
|||||||
Ret[]
|
Ret[]
|
||||||
>`SELECT count(distinct "sessionId") x FROM "WebsiteEvent" where "websiteId" = ${websiteId} AND "createdAt" >= ${startAt}`;
|
>`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(
|
export async function getSessionMetrics(
|
||||||
|
@ -19,9 +19,13 @@ export const trpcOpenapiHttpHandler = createOpenApiHttpHandler({
|
|||||||
createContext,
|
createContext,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const description = `
|
||||||
|
<h3>Insight into everything</h3>
|
||||||
|
<p>Github: <a href="https://github.com/msgbyte/tianji" target="_blank">https://github.com/msgbyte/tianji</a></p>
|
||||||
|
`.trim();
|
||||||
export const trpcOpenapiDocument = generateOpenApiDocument(appRouter, {
|
export const trpcOpenapiDocument = generateOpenApiDocument(appRouter, {
|
||||||
title: 'Tianji OpenAPI',
|
title: 'Tianji OpenAPI',
|
||||||
description: 'Insight into everything',
|
description,
|
||||||
version: '1.0.0',
|
version: '1.0.0',
|
||||||
baseUrl: '/open',
|
baseUrl: '/open',
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,24 @@
|
|||||||
|
import { z } from 'zod';
|
||||||
import { publicProcedure, router } from '../trpc';
|
import { publicProcedure, router } from '../trpc';
|
||||||
|
import { OPENAPI_TAG } from '../../utils/const';
|
||||||
|
|
||||||
export const globalRouter = router({
|
export const globalRouter = router({
|
||||||
config: publicProcedure.query(async ({ input }) => {
|
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 {
|
return {
|
||||||
allowRegister: checkEnvTrusty(process.env.ALLOW_REGISTER),
|
allowRegister: checkEnvTrusty(process.env.ALLOW_REGISTER),
|
||||||
websiteId: process.env.WEBSITE_ID,
|
websiteId: process.env.WEBSITE_ID,
|
||||||
|
@ -1,23 +1,38 @@
|
|||||||
import { router, workspaceOwnerProcedure, workspaceProcedure } from '../trpc';
|
import {
|
||||||
|
OpenApiMetaInfo,
|
||||||
|
router,
|
||||||
|
workspaceOwnerProcedure,
|
||||||
|
workspaceProcedure,
|
||||||
|
} from '../trpc';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { getWebsiteOnlineUserCount } from '../../model/website';
|
import { getWebsiteOnlineUserCount } from '../../model/website';
|
||||||
import { prisma } from '../../model/_client';
|
import { prisma } from '../../model/_client';
|
||||||
import {
|
import {
|
||||||
EVENT_COLUMNS,
|
EVENT_COLUMNS,
|
||||||
FILTER_COLUMNS,
|
FILTER_COLUMNS,
|
||||||
|
OPENAPI_TAG,
|
||||||
SESSION_COLUMNS,
|
SESSION_COLUMNS,
|
||||||
hostnameRegex,
|
hostnameRegex,
|
||||||
} from '../../utils/const';
|
} from '../../utils/const';
|
||||||
import { parseDateRange } from '../../utils/common';
|
import { parseDateRange } from '../../utils/common';
|
||||||
import { getSessionMetrics, getPageviewMetrics } from '../../model/website';
|
import { getSessionMetrics, getPageviewMetrics } from '../../model/website';
|
||||||
|
import { websiteInfoSchema } from '../../model/_schema';
|
||||||
|
import { OpenApiMeta } from 'trpc-openapi';
|
||||||
|
|
||||||
export const websiteRouter = router({
|
export const websiteRouter = router({
|
||||||
onlineCount: workspaceProcedure
|
onlineCount: workspaceProcedure
|
||||||
|
.meta(
|
||||||
|
buildWebsiteOpenapi({
|
||||||
|
method: 'GET',
|
||||||
|
path: '/onlineCount',
|
||||||
|
})
|
||||||
|
)
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
websiteId: z.string(),
|
websiteId: z.string(),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
.output(z.number())
|
||||||
.query(async ({ input }) => {
|
.query(async ({ input }) => {
|
||||||
const websiteId = input.websiteId;
|
const websiteId = input.websiteId;
|
||||||
|
|
||||||
@ -26,11 +41,18 @@ export const websiteRouter = router({
|
|||||||
return count;
|
return count;
|
||||||
}),
|
}),
|
||||||
info: workspaceProcedure
|
info: workspaceProcedure
|
||||||
|
.meta(
|
||||||
|
buildWebsiteOpenapi({
|
||||||
|
method: 'GET',
|
||||||
|
path: '/info',
|
||||||
|
})
|
||||||
|
)
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
websiteId: z.string(),
|
websiteId: z.string(),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
.output(websiteInfoSchema.nullable())
|
||||||
.query(async ({ input }) => {
|
.query(async ({ input }) => {
|
||||||
const { workspaceId, websiteId } = input;
|
const { workspaceId, websiteId } = input;
|
||||||
|
|
||||||
@ -44,6 +66,12 @@ export const websiteRouter = router({
|
|||||||
return website;
|
return website;
|
||||||
}),
|
}),
|
||||||
metrics: workspaceProcedure
|
metrics: workspaceProcedure
|
||||||
|
.meta(
|
||||||
|
buildWebsiteOpenapi({
|
||||||
|
method: 'GET',
|
||||||
|
path: '/metrics',
|
||||||
|
})
|
||||||
|
)
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
websiteId: z.string(),
|
websiteId: z.string(),
|
||||||
@ -157,6 +185,12 @@ export const websiteRouter = router({
|
|||||||
return [];
|
return [];
|
||||||
}),
|
}),
|
||||||
updateInfo: workspaceOwnerProcedure
|
updateInfo: workspaceOwnerProcedure
|
||||||
|
.meta(
|
||||||
|
buildWebsiteOpenapi({
|
||||||
|
method: 'PUT',
|
||||||
|
path: '/update',
|
||||||
|
})
|
||||||
|
)
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
websiteId: z.string().cuid2(),
|
websiteId: z.string().cuid2(),
|
||||||
@ -168,6 +202,7 @@ export const websiteRouter = router({
|
|||||||
monitorId: z.string().cuid2().nullish(),
|
monitorId: z.string().cuid2().nullish(),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
.output(websiteInfoSchema)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input }) => {
|
||||||
const { workspaceId, websiteId, name, domain, monitorId } = input;
|
const { workspaceId, websiteId, name, domain, monitorId } = input;
|
||||||
|
|
||||||
@ -186,3 +221,14 @@ export const websiteRouter = router({
|
|||||||
return websiteInfo;
|
return websiteInfo;
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function buildWebsiteOpenapi(meta: OpenApiMetaInfo): OpenApiMeta {
|
||||||
|
return {
|
||||||
|
openapi: {
|
||||||
|
tags: [OPENAPI_TAG.WEBSITE],
|
||||||
|
protect: true,
|
||||||
|
...meta,
|
||||||
|
path: `/workspace/{workspaceId}/website/{websiteId}${meta.path}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@ -17,6 +17,8 @@ export function createContext({ req }: { req: IncomingMessage }) {
|
|||||||
type Context = inferAsyncReturnType<typeof createContext>;
|
type Context = inferAsyncReturnType<typeof createContext>;
|
||||||
const t = initTRPC.context<Context>().meta<OpenApiMeta>().create();
|
const t = initTRPC.context<Context>().meta<OpenApiMeta>().create();
|
||||||
|
|
||||||
|
export type OpenApiMetaInfo = NonNullable<OpenApiMeta['openapi']>;
|
||||||
|
|
||||||
export const middleware = t.middleware;
|
export const middleware = t.middleware;
|
||||||
export const router = t.router;
|
export const router = t.router;
|
||||||
export const publicProcedure = t.procedure;
|
export const publicProcedure = t.procedure;
|
||||||
|
@ -115,7 +115,9 @@ export const FILTER_COLUMNS = {
|
|||||||
export const DEFAULT_RESET_DATE = '2000-01-01';
|
export const DEFAULT_RESET_DATE = '2000-01-01';
|
||||||
|
|
||||||
export enum OPENAPI_TAG {
|
export enum OPENAPI_TAG {
|
||||||
|
GLOBAL = 'Global',
|
||||||
USER = 'User',
|
USER = 'User',
|
||||||
|
WEBSITE = 'Website',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const hostnameRegex =
|
export const hostnameRegex =
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
export * from './server';
|
export * from './server';
|
||||||
export * from './monitor';
|
export * from './monitor';
|
||||||
export * from './utils';
|
export * from './utils';
|
||||||
|
|
||||||
export type { MaybePromise } from '@trpc/server';
|
|
||||||
|
@ -2,3 +2,5 @@ export type ExactType<T, U extends Partial<T>> = Omit<T, keyof U> & U;
|
|||||||
|
|
||||||
export type PickRequired<T, U extends keyof T> = Omit<T, keyof U> &
|
export type PickRequired<T, U extends keyof T> = Omit<T, keyof U> &
|
||||||
Required<Pick<T, U>>;
|
Required<Pick<T, U>>;
|
||||||
|
|
||||||
|
export type { MaybePromise } from '@trpc/server';
|
||||||
|
Loading…
Reference in New Issue
Block a user