feat: implement openapi (#482)

This commit is contained in:
Manuel
2024-08-25 18:03:32 +02:00
committed by GitHub
parent a3520e4dfd
commit f0cd45c813
17 changed files with 14986 additions and 637 deletions

View File

@@ -1,5 +1,6 @@
import type { inferRouterInputs, inferRouterOutputs } from "@trpc/server";
import { openApiDocument } from "./open-api";
import type { AppRouter } from "./root";
import { appRouter } from "./root";
import { createCallerFactory, createTRPCContext } from "./trpc";
@@ -29,5 +30,5 @@ type RouterInputs = inferRouterInputs<AppRouter>;
**/
type RouterOutputs = inferRouterOutputs<AppRouter>;
export { createTRPCContext, appRouter, createCaller };
export { createTRPCContext, appRouter, createCaller, openApiDocument };
export type { AppRouter, RouterInputs, RouterOutputs };

View File

@@ -0,0 +1,11 @@
import { generateOpenApiDocument } from "trpc-swagger";
import { appRouter } from "./root";
export const openApiDocument = (base: string) =>
generateOpenApiDocument(appRouter, {
title: "Homarr API documentation",
version: "1.0.0",
baseUrl: base,
docsUrl: "https://homarr.dev",
});

View File

@@ -69,12 +69,16 @@ export const userRouter = createTRPCRouter({
// Delete invite as it's used
await ctx.db.delete(invites).where(inviteWhere);
}),
create: publicProcedure.input(validation.user.create).mutation(async ({ ctx, input }) => {
throwIfCredentialsDisabled();
await checkUsernameAlreadyTakenAndThrowAsync(ctx.db, "credentials", input.username);
create: publicProcedure
.meta({ openapi: { method: "POST", path: "/api/users", tags: ["users"] } })
.input(validation.user.create)
.output(z.void())
.mutation(async ({ ctx, input }) => {
throwIfCredentialsDisabled();
await checkUsernameAlreadyTakenAndThrowAsync(ctx.db, "credentials", input.username);
await createUserAsync(ctx.db, input);
}),
await createUserAsync(ctx.db, input);
}),
setProfileImage: protectedProcedure
.input(
z.object({
@@ -126,20 +130,33 @@ export const userRouter = createTRPCRouter({
})
.where(eq(users.id, input.userId));
}),
getAll: publicProcedure.query(async ({ ctx }) => {
return await ctx.db.query.users.findMany({
columns: {
id: true,
name: true,
email: true,
emailVerified: true,
image: true,
provider: true,
},
});
}),
selectable: publicProcedure.query(async ({ ctx }) => {
return await ctx.db.query.users.findMany({
getAll: publicProcedure
.input(z.void())
.output(
z.array(
z.object({
id: z.string(),
name: z.string().nullable(),
email: z.string().nullable(),
emailVerified: z.date().nullable(),
image: z.string().nullable(),
}),
),
)
.meta({ openapi: { method: "GET", path: "/api/users", tags: ["users"] } })
.query(({ ctx }) => {
return ctx.db.query.users.findMany({
columns: {
id: true,
name: true,
email: true,
emailVerified: true,
image: true,
},
});
}),
selectable: publicProcedure.query(({ ctx }) => {
return ctx.db.query.users.findMany({
columns: {
id: true,
name: true,

View File

@@ -8,6 +8,7 @@
*/
import { initTRPC, TRPCError } from "@trpc/server";
import superjson from "superjson";
import type { OpenApiMeta } from "trpc-swagger";
import type { Session } from "@homarr/auth";
import { FlattenError } from "@homarr/common";
@@ -46,17 +47,20 @@ export const createTRPCContext = (opts: { headers: Headers; session: Session | n
* This is where the trpc api is initialized, connecting the context and
* transformer
*/
const t = initTRPC.context<typeof createTRPCContext>().create({
transformer: superjson,
errorFormatter: ({ shape, error }) => ({
...shape,
data: {
...shape.data,
zodError: error.cause instanceof ZodError ? error.cause.flatten() : null,
error: error.cause instanceof FlattenError ? error.cause.flatten() : null,
},
}),
});
const t = initTRPC
.context<typeof createTRPCContext>()
.meta<OpenApiMeta>()
.create({
transformer: superjson,
errorFormatter: ({ shape, error }) => ({
...shape,
data: {
...shape.data,
zodError: error.cause instanceof ZodError ? error.cause.flatten() : null,
error: error.cause instanceof FlattenError ? error.cause.flatten() : null,
},
}),
});
/**
* Create a server-side caller