Skip to content

Commit

Permalink
feat: #1243 add api routes
Browse files Browse the repository at this point in the history
  • Loading branch information
manuel-rw committed Nov 23, 2024
1 parent 9fb14df commit 12b910e
Show file tree
Hide file tree
Showing 23 changed files with 458 additions and 2,458 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import type { OpenAPIV3 } from "openapi-types";
import type { OpenAPIObject } from "openapi3-ts/oas31";
import SwaggerUI from "swagger-ui-react";

// workaround for CSS that cannot be processed by next.js, https://github.com/swagger-api/swagger-ui/issues/10045
Expand All @@ -9,7 +9,7 @@ import "../swagger-ui-overrides.css";
import "../swagger-ui.css";

interface SwaggerUIClientProps {
document: OpenAPIV3.Document;
document: OpenAPIObject;
}

export const SwaggerUIClient = ({ document }: SwaggerUIClientProps) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ export const DeleteUserButton = ({ user }: DeleteUserButtonProps) => {
children: t("user.action.delete.confirm", { username: user.name }),
// eslint-disable-next-line no-restricted-syntax
async onConfirm() {
await mutateUserDeletionAsync(user.id);
await mutateUserDeletionAsync({
userId: user.id,
});
},
}),
[user, mutateUserDeletionAsync, openConfirmModal, t],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { showErrorNotification, showSuccessNotification } from "@homarr/notifica
import { useI18n } from "@homarr/translation/client";
import type { z } from "@homarr/validation";
import { validation } from "@homarr/validation";
import type { DayOfWeek } from "@mantine/dates";

dayjs.extend(localeData);

Expand Down Expand Up @@ -43,7 +44,7 @@ export const FirstDayOfWeek = ({ user }: FirstDayOfWeekProps) => {
});
const form = useZodForm(validation.user.firstDayOfWeek, {
initialValues: {
firstDayOfWeek: user.firstDayOfWeek,
firstDayOfWeek: user.firstDayOfWeek as DayOfWeek,
},
});

Expand Down
2 changes: 1 addition & 1 deletion apps/nextjs/src/app/api/[...trpc]/route.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { headers } from "next/headers";
import { userAgent } from "next/server";
import type { NextRequest } from "next/server";
import { createOpenApiFetchHandler } from "trpc-swagger/build/index.mjs";
import { createOpenApiFetchHandler } from "trpc-to-openapi";

import { appRouter, createTRPCContext } from "@homarr/api";
import { hashPasswordAsync } from "@homarr/auth";
Expand Down
5 changes: 0 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,5 @@
"packageManager": "pnpm@9.14.2",
"engines": {
"node": ">=22.11.0"
},
"pnpm": {
"patchedDependencies": {
"trpc-swagger@1.2.6": "patches/trpc-swagger@1.2.6.patch"
}
}
}
2 changes: 1 addition & 1 deletion packages/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"next": "^14.2.18",
"react": "^18.3.1",
"superjson": "2.2.1",
"trpc-swagger": "^1.2.6"
"trpc-to-openapi": "^2.0.2"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/open-api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { generateOpenApiDocument } from "trpc-swagger";
import { generateOpenApiDocument } from "trpc-to-openapi";

import { appRouter } from "./root";

Expand Down
52 changes: 9 additions & 43 deletions packages/api/src/router/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,17 @@ import { TRPCError } from "@trpc/server";

import { asc, createId, eq, inArray, like } from "@homarr/db";
import { apps } from "@homarr/db/schema/sqlite";
import { selectAppSchema } from "@homarr/db/validationSchemas/app";
import { validation, z } from "@homarr/validation";

import { convertIntersectionToZodObject } from "../schema-merger";
import { createTRPCRouter, permissionRequiredProcedure, protectedProcedure, publicProcedure } from "../trpc";
import { canUserSeeAppAsync } from "./app/app-access-control";

export const appRouter = createTRPCRouter({
all: protectedProcedure
.input(z.void())
.output(
z.array(
z.object({
name: z.string(),
id: z.string(),
description: z.string().nullable(),
iconUrl: z.string(),
href: z.string().nullable(),
}),
),
)
.output(z.array(selectAppSchema))
.meta({ openapi: { method: "GET", path: "/api/apps", tags: ["apps"], protect: true } })
.query(({ ctx }) => {
return ctx.db.query.apps.findMany({
Expand All @@ -29,17 +21,7 @@ export const appRouter = createTRPCRouter({
}),
search: protectedProcedure
.input(z.object({ query: z.string(), limit: z.number().min(1).max(100).default(10) }))
.output(
z.array(
z.object({
name: z.string(),
id: z.string(),
description: z.string().nullable(),
iconUrl: z.string(),
href: z.string().nullable(),
}),
),
)
.output(z.array(selectAppSchema))
.meta({ openapi: { method: "GET", path: "/api/apps/search", tags: ["apps"], protect: true } })
.query(({ ctx, input }) => {
return ctx.db.query.apps.findMany({
Expand All @@ -50,17 +32,7 @@ export const appRouter = createTRPCRouter({
}),
selectable: protectedProcedure
.input(z.void())
.output(
z.array(
z.object({
name: z.string(),
id: z.string(),
iconUrl: z.string(),
description: z.string().nullable(),
href: z.string().nullable(),
}),
),
)
.output(z.array(selectAppSchema.pick({ id: true, name: true, iconUrl: true, href: true, description: true })))
.meta({
openapi: {
method: "GET",
Expand All @@ -83,15 +55,7 @@ export const appRouter = createTRPCRouter({
}),
byId: publicProcedure
.input(validation.common.byId)
.output(
z.object({
name: z.string(),
id: z.string(),
description: z.string().nullable(),
iconUrl: z.string(),
href: z.string().nullable(),
}),
)
.output(selectAppSchema)
.meta({ openapi: { method: "GET", path: "/api/apps/{id}", tags: ["apps"], protect: true } })
.query(async ({ ctx, input }) => {
const app = await ctx.db.query.apps.findFirst({
Expand Down Expand Up @@ -136,7 +100,9 @@ export const appRouter = createTRPCRouter({
}),
update: permissionRequiredProcedure
.requiresPermission("app-modify-all")
.input(validation.app.edit)
.input(convertIntersectionToZodObject(validation.app.edit))
.output(z.void())
.meta({ openapi: { method: "PATCH", path: "/api/apps/{id}", tags: ["apps"], protect: true } })
.mutation(async ({ ctx, input }) => {
const app = await ctx.db.query.apps.findFirst({
where: eq(apps.id, input.id),
Expand Down
47 changes: 32 additions & 15 deletions packages/api/src/router/invite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,51 @@ import { TRPCError } from "@trpc/server";

import { asc, createId, eq } from "@homarr/db";
import { invites } from "@homarr/db/schema/sqlite";
import { selectInviteSchema } from "@homarr/db/validationSchemas/invite";
import { z } from "@homarr/validation";

import { createTRPCRouter, protectedProcedure } from "../trpc";
import { throwIfCredentialsDisabled } from "./invite/checks";

export const inviteRouter = createTRPCRouter({
getAll: protectedProcedure.query(async ({ ctx }) => {
throwIfCredentialsDisabled();
const dbInvites = await ctx.db.query.invites.findMany({
orderBy: asc(invites.expirationDate),
columns: {
token: false,
},
with: {
creator: {
columns: {
getAll: protectedProcedure
.output(
z.array(
selectInviteSchema
.pick({
id: true,
name: true,
expirationDate: true,
})
.extend({ creator: z.object({ name: z.string().nullable(), id: z.string() }) }),
),
)
.input(z.undefined())
.meta({ openapi: { method: "GET", path: "/api/invites", tags: ["invites"], protect: true } })
.query(async ({ ctx }) => {
throwIfCredentialsDisabled();
return await ctx.db.query.invites.findMany({
orderBy: asc(invites.expirationDate),
columns: {
token: false,
},
with: {
creator: {
columns: {
id: true,
name: true,
},
},
},
},
});
return dbInvites;
}),
});
}),
createInvite: protectedProcedure
.input(
z.object({
expirationDate: z.date(),
}),
)
.output(z.object({ id: z.string(), token: z.string() }))
.meta({ openapi: { method: "POST", path: "/api/invites", tags: ["invites"], protect: true } })
.mutation(async ({ ctx, input }) => {
throwIfCredentialsDisabled();
const id = createId();
Expand All @@ -56,6 +71,8 @@ export const inviteRouter = createTRPCRouter({
id: z.string(),
}),
)
.output(z.undefined())
.meta({ openapi: { method: "DELETE", path: "/api/invites/{id}", tags: ["invites"], protect: true } })
.mutation(async ({ ctx, input }) => {
throwIfCredentialsDisabled();
const dbInvite = await ctx.db.query.invites.findFirst({
Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/router/test/user.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ describe("delete should delete user", () => {

await db.insert(schema.users).values(initialUsers);

await caller.delete(defaultOwnerId);
await caller.delete({ userId: defaultOwnerId });

const usersInDb = await db.select().from(schema.users);
expect(usersInDb).toHaveLength(2);
Expand Down
Loading

0 comments on commit 12b910e

Please sign in to comment.