diff --git a/api/api/v1/accounts/:id/block.ts b/api/api/v1/accounts/:id/block.ts index a53d316a..595e25a5 100644 --- a/api/api/v1/accounts/:id/block.ts +++ b/api/api/v1/accounts/:id/block.ts @@ -1,10 +1,8 @@ -import { apiRoute, auth } from "@/api"; +import { apiRoute, auth, withUserParam } from "@/api"; import { createRoute } from "@hono/zod-openapi"; -import { Relationship, User } from "@versia/kit/db"; +import { Relationship } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import { z } from "zod"; -import { ApiError } from "~/classes/errors/api-error"; -import { ErrorSchema } from "~/types/api"; const route = createRoute({ method: "post", @@ -20,6 +18,7 @@ const route = createRoute({ RolePermissions.ViewAccounts, ], }), + withUserParam, ] as const, responses: { 200: { @@ -30,14 +29,6 @@ const route = createRoute({ }, }, }, - 404: { - description: "User not found", - content: { - "application/json": { - schema: ErrorSchema, - }, - }, - }, }, request: { params: z.object({ @@ -48,14 +39,8 @@ const route = createRoute({ export default apiRoute((app) => app.openapi(route, async (context) => { - const { id } = context.req.valid("param"); const { user } = context.get("auth"); - - const otherUser = await User.fromId(id); - - if (!otherUser) { - throw new ApiError(404, "User not found"); - } + const otherUser = context.get("user"); const foundRelationship = await Relationship.fromOwnerAndSubject( user, diff --git a/api/api/v1/accounts/:id/follow.ts b/api/api/v1/accounts/:id/follow.ts index 9457183f..c6db8951 100644 --- a/api/api/v1/accounts/:id/follow.ts +++ b/api/api/v1/accounts/:id/follow.ts @@ -1,11 +1,9 @@ -import { apiRoute, auth } from "@/api"; +import { apiRoute, auth, withUserParam } from "@/api"; import { createRoute } from "@hono/zod-openapi"; -import { Relationship, User } from "@versia/kit/db"; +import { Relationship } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import ISO6391 from "iso-639-1"; import { z } from "zod"; -import { ApiError } from "~/classes/errors/api-error"; -import { ErrorSchema } from "~/types/api"; const schemas = { param: z.object({ @@ -37,6 +35,7 @@ const route = createRoute({ RolePermissions.ViewAccounts, ], }), + withUserParam, ] as const, responses: { 200: { @@ -47,14 +46,6 @@ const route = createRoute({ }, }, }, - 404: { - description: "User not found", - content: { - "application/json": { - schema: ErrorSchema, - }, - }, - }, }, request: { params: schemas.param, @@ -70,15 +61,9 @@ const route = createRoute({ export default apiRoute((app) => app.openapi(route, async (context) => { - const { id } = context.req.valid("param"); const { user } = context.get("auth"); const { reblogs, notify, languages } = context.req.valid("json"); - - const otherUser = await User.fromId(id); - - if (!otherUser) { - throw new ApiError(404, "User not found"); - } + const otherUser = context.get("user"); let relationship = await Relationship.fromOwnerAndSubject( user, diff --git a/api/api/v1/accounts/:id/followers.ts b/api/api/v1/accounts/:id/followers.ts index 805a2fc7..548f8770 100644 --- a/api/api/v1/accounts/:id/followers.ts +++ b/api/api/v1/accounts/:id/followers.ts @@ -1,11 +1,9 @@ -import { apiRoute, auth } from "@/api"; +import { apiRoute, auth, withUserParam } from "@/api"; import { createRoute } from "@hono/zod-openapi"; import { Timeline, User } from "@versia/kit/db"; import { RolePermissions, Users } from "@versia/kit/tables"; import { and, gt, gte, lt, sql } from "drizzle-orm"; import { z } from "zod"; -import { ApiError } from "~/classes/errors/api-error"; -import { ErrorSchema } from "~/types/api"; const schemas = { query: z.object({ @@ -34,6 +32,7 @@ const route = createRoute({ RolePermissions.ViewAccounts, ], }), + withUserParam, ] as const, request: { params: schemas.param, @@ -53,30 +52,15 @@ const route = createRoute({ }, }, }, - 404: { - description: "The specified account was not found", - content: { - "application/json": { - schema: ErrorSchema, - }, - }, - }, }, }); export default apiRoute((app) => app.openapi(route, async (context) => { - const { id } = context.req.valid("param"); const { max_id, since_id, min_id, limit } = context.req.valid("query"); - - const otherUser = await User.fromId(id); + const otherUser = context.get("user"); // TODO: Add follower/following privacy settings - - if (!otherUser) { - throw new ApiError(404, "User not found"); - } - const { objects, link } = await Timeline.getUserTimeline( and( max_id ? lt(Users.id, max_id) : undefined, diff --git a/api/api/v1/accounts/:id/following.ts b/api/api/v1/accounts/:id/following.ts index f12e00c1..272f8b28 100644 --- a/api/api/v1/accounts/:id/following.ts +++ b/api/api/v1/accounts/:id/following.ts @@ -1,11 +1,9 @@ -import { apiRoute, auth } from "@/api"; +import { apiRoute, auth, withUserParam } from "@/api"; import { createRoute } from "@hono/zod-openapi"; import { Timeline, User } from "@versia/kit/db"; import { RolePermissions, Users } from "@versia/kit/tables"; import { and, gt, gte, lt, sql } from "drizzle-orm"; import { z } from "zod"; -import { ApiError } from "~/classes/errors/api-error"; -import { ErrorSchema } from "~/types/api"; const schemas = { query: z.object({ @@ -34,6 +32,7 @@ const route = createRoute({ RolePermissions.ViewAccounts, ], }), + withUserParam, ] as const, request: { params: schemas.param, @@ -54,27 +53,13 @@ const route = createRoute({ }, }, }, - 404: { - description: "User not found", - content: { - "application/json": { - schema: ErrorSchema, - }, - }, - }, }, }); export default apiRoute((app) => app.openapi(route, async (context) => { - const { id } = context.req.valid("param"); const { max_id, since_id, min_id } = context.req.valid("query"); - - const otherUser = await User.fromId(id); - - if (!otherUser) { - throw new ApiError(404, "User not found"); - } + const otherUser = context.get("user"); // TODO: Add follower/following privacy settings diff --git a/api/api/v1/accounts/:id/index.ts b/api/api/v1/accounts/:id/index.ts index 00ce43b5..bed7d8b2 100644 --- a/api/api/v1/accounts/:id/index.ts +++ b/api/api/v1/accounts/:id/index.ts @@ -1,10 +1,8 @@ -import { apiRoute, auth } from "@/api"; +import { apiRoute, auth, withUserParam } from "@/api"; import { createRoute } from "@hono/zod-openapi"; import { User } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import { z } from "zod"; -import { ApiError } from "~/classes/errors/api-error"; -import { ErrorSchema } from "~/types/api"; const route = createRoute({ method: "get", @@ -16,6 +14,7 @@ const route = createRoute({ auth: false, permissions: [RolePermissions.ViewAccounts], }), + withUserParam, ] as const, request: { params: z.object({ @@ -31,28 +30,14 @@ const route = createRoute({ }, }, }, - 404: { - description: "User not found", - content: { - "application/json": { - schema: ErrorSchema, - }, - }, - }, }, }); export default apiRoute((app) => - app.openapi(route, async (context) => { - const { id } = context.req.valid("param"); + app.openapi(route, (context) => { const { user } = context.get("auth"); + const otherUser = context.get("user"); - const foundUser = await User.fromId(id); - - if (!foundUser) { - throw new ApiError(404, "User not found"); - } - - return context.json(foundUser.toApi(user?.id === foundUser.id), 200); + return context.json(otherUser.toApi(user?.id === otherUser.id), 200); }), ); diff --git a/api/api/v1/accounts/:id/mute.ts b/api/api/v1/accounts/:id/mute.ts index 052f5f74..41b393f2 100644 --- a/api/api/v1/accounts/:id/mute.ts +++ b/api/api/v1/accounts/:id/mute.ts @@ -1,10 +1,8 @@ -import { apiRoute, auth } from "@/api"; +import { apiRoute, auth, withUserParam } from "@/api"; import { createRoute } from "@hono/zod-openapi"; -import { Relationship, User } from "@versia/kit/db"; +import { Relationship } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import { z } from "zod"; -import { ApiError } from "~/classes/errors/api-error"; -import { ErrorSchema } from "~/types/api"; const schemas = { param: z.object({ @@ -35,6 +33,7 @@ const route = createRoute({ RolePermissions.ViewAccounts, ], }), + withUserParam, ] as const, request: { params: schemas.param, @@ -55,29 +54,15 @@ const route = createRoute({ }, }, }, - 404: { - description: "User not found", - content: { - "application/json": { - schema: ErrorSchema, - }, - }, - }, }, }); export default apiRoute((app) => app.openapi(route, async (context) => { - const { id } = context.req.valid("param"); const { user } = context.get("auth"); // TODO: Add duration support const { notifications } = context.req.valid("json"); - - const otherUser = await User.fromId(id); - - if (!otherUser) { - throw new ApiError(404, "User not found"); - } + const otherUser = context.get("user"); const foundRelationship = await Relationship.fromOwnerAndSubject( user, diff --git a/api/api/v1/accounts/:id/note.ts b/api/api/v1/accounts/:id/note.ts index c6d1877b..5533b91c 100644 --- a/api/api/v1/accounts/:id/note.ts +++ b/api/api/v1/accounts/:id/note.ts @@ -1,10 +1,8 @@ -import { apiRoute, auth } from "@/api"; +import { apiRoute, auth, withUserParam } from "@/api"; import { createRoute } from "@hono/zod-openapi"; -import { Relationship, User } from "@versia/kit/db"; +import { Relationship } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import { z } from "zod"; -import { ApiError } from "~/classes/errors/api-error"; -import { ErrorSchema } from "~/types/api"; const schemas = { param: z.object({ @@ -29,6 +27,7 @@ const route = createRoute({ RolePermissions.ViewAccounts, ], }), + withUserParam, ] as const, request: { params: schemas.param, @@ -49,28 +48,14 @@ const route = createRoute({ }, }, }, - 404: { - description: "User not found", - content: { - "application/json": { - schema: ErrorSchema, - }, - }, - }, }, }); export default apiRoute((app) => app.openapi(route, async (context) => { - const { id } = context.req.valid("param"); const { user } = context.get("auth"); const { comment } = context.req.valid("json"); - - const otherUser = await User.fromId(id); - - if (!otherUser) { - throw new ApiError(404, "User not found"); - } + const otherUser = context.get("user"); const foundRelationship = await Relationship.fromOwnerAndSubject( user, diff --git a/api/api/v1/accounts/:id/pin.ts b/api/api/v1/accounts/:id/pin.ts index 5276f79b..d33f7407 100644 --- a/api/api/v1/accounts/:id/pin.ts +++ b/api/api/v1/accounts/:id/pin.ts @@ -1,10 +1,8 @@ -import { apiRoute, auth } from "@/api"; +import { apiRoute, auth, withUserParam } from "@/api"; import { createRoute } from "@hono/zod-openapi"; -import { Relationship, User } from "@versia/kit/db"; +import { Relationship } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import { z } from "zod"; -import { ApiError } from "~/classes/errors/api-error"; -import { ErrorSchema } from "~/types/api"; const route = createRoute({ method: "post", @@ -20,6 +18,7 @@ const route = createRoute({ RolePermissions.ViewAccounts, ], }), + withUserParam, ] as const, request: { params: z.object({ @@ -35,27 +34,13 @@ const route = createRoute({ }, }, }, - 404: { - description: "User not found", - content: { - "application/json": { - schema: ErrorSchema, - }, - }, - }, }, }); export default apiRoute((app) => app.openapi(route, async (context) => { - const { id } = context.req.valid("param"); const { user } = context.get("auth"); - - const otherUser = await User.fromId(id); - - if (!otherUser) { - throw new ApiError(404, "User not found"); - } + const otherUser = context.get("user"); const foundRelationship = await Relationship.fromOwnerAndSubject( user, diff --git a/api/api/v1/accounts/:id/refetch.ts b/api/api/v1/accounts/:id/refetch.ts index 222b6c56..f5da0f71 100644 --- a/api/api/v1/accounts/:id/refetch.ts +++ b/api/api/v1/accounts/:id/refetch.ts @@ -1,4 +1,4 @@ -import { apiRoute, auth } from "@/api"; +import { apiRoute, auth, withUserParam } from "@/api"; import { createRoute } from "@hono/zod-openapi"; import { User } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; @@ -17,6 +17,7 @@ const route = createRoute({ scopes: ["write:accounts"], permissions: [RolePermissions.ViewAccounts], }), + withUserParam, ] as const, request: { params: z.object({ @@ -32,14 +33,6 @@ const route = createRoute({ }, }, }, - 404: { - description: "User not found", - content: { - "application/json": { - schema: ErrorSchema, - }, - }, - }, 400: { description: "User is local", content: { @@ -53,13 +46,7 @@ const route = createRoute({ export default apiRoute((app) => app.openapi(route, async (context) => { - const { id } = context.req.valid("param"); - - const otherUser = await User.fromId(id); - - if (!otherUser) { - throw new ApiError(404, "User not found"); - } + const otherUser = context.get("user"); if (otherUser.isLocal()) { throw new ApiError(400, "Cannot refetch a local user"); diff --git a/api/api/v1/accounts/:id/remove_from_followers.ts b/api/api/v1/accounts/:id/remove_from_followers.ts index e545ddf8..2ee7a465 100644 --- a/api/api/v1/accounts/:id/remove_from_followers.ts +++ b/api/api/v1/accounts/:id/remove_from_followers.ts @@ -1,10 +1,8 @@ -import { apiRoute, auth } from "@/api"; +import { apiRoute, auth, withUserParam } from "@/api"; import { createRoute } from "@hono/zod-openapi"; -import { Relationship, User } from "@versia/kit/db"; +import { Relationship } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import { z } from "zod"; -import { ApiError } from "~/classes/errors/api-error"; -import { ErrorSchema } from "~/types/api"; const route = createRoute({ method: "post", @@ -20,6 +18,7 @@ const route = createRoute({ RolePermissions.ViewAccounts, ], }), + withUserParam, ] as const, request: { params: z.object({ @@ -35,27 +34,13 @@ const route = createRoute({ }, }, }, - 404: { - description: "User not found", - content: { - "application/json": { - schema: ErrorSchema, - }, - }, - }, }, }); export default apiRoute((app) => app.openapi(route, async (context) => { - const { id } = context.req.valid("param"); const { user } = context.get("auth"); - - const otherUser = await User.fromId(id); - - if (!otherUser) { - throw new ApiError(404, "User not found"); - } + const otherUser = context.get("user"); const oppositeRelationship = await Relationship.fromOwnerAndSubject( otherUser, diff --git a/api/api/v1/accounts/:id/roles/:role_id/index.ts b/api/api/v1/accounts/:id/roles/:role_id/index.ts index 23c34339..af3840e5 100644 --- a/api/api/v1/accounts/:id/roles/:role_id/index.ts +++ b/api/api/v1/accounts/:id/roles/:role_id/index.ts @@ -1,6 +1,6 @@ -import { apiRoute, auth } from "@/api"; +import { apiRoute, auth, withUserParam } from "@/api"; import { createRoute } from "@hono/zod-openapi"; -import { Role, User } from "@versia/kit/db"; +import { Role } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import { z } from "zod"; import { ApiError } from "~/classes/errors/api-error"; @@ -22,6 +22,7 @@ const routePost = createRoute({ auth: true, permissions: [RolePermissions.ManageRoles], }), + withUserParam, ] as const, request: { params: schemas.param, @@ -30,9 +31,8 @@ const routePost = createRoute({ 204: { description: "Role assigned", }, - 404: { - description: "User or role not found", + description: "Role not found", content: { "application/json": { schema: ErrorSchema, @@ -59,6 +59,7 @@ const routeDelete = createRoute({ auth: true, permissions: [RolePermissions.ManageRoles], }), + withUserParam, ] as const, request: { params: schemas.param, @@ -67,9 +68,8 @@ const routeDelete = createRoute({ 204: { description: "Role removed", }, - 404: { - description: "User or role not found", + description: "Role not found", content: { "application/json": { schema: ErrorSchema, @@ -90,19 +90,14 @@ const routeDelete = createRoute({ export default apiRoute((app) => { app.openapi(routePost, async (context) => { const { user } = context.get("auth"); - const { id, role_id } = context.req.valid("param"); + const { role_id } = context.req.valid("param"); + const targetUser = context.get("user"); - const targetUser = await User.fromId(id); const role = await Role.fromId(role_id); if (!role) { throw new ApiError(404, "Role not found"); } - - if (!targetUser) { - throw new ApiError(404, "User not found"); - } - // Priority check const userRoles = await Role.getUserRoles(user.id, user.data.isAdmin); @@ -125,19 +120,15 @@ export default apiRoute((app) => { app.openapi(routeDelete, async (context) => { const { user } = context.get("auth"); - const { id, role_id } = context.req.valid("param"); + const { role_id } = context.req.valid("param"); + const targetUser = context.get("user"); - const targetUser = await User.fromId(id); const role = await Role.fromId(role_id); if (!role) { throw new ApiError(404, "Role not found"); } - if (!targetUser) { - throw new ApiError(404, "User not found"); - } - // Priority check const userRoles = await Role.getUserRoles(user.id, user.data.isAdmin); diff --git a/api/api/v1/accounts/:id/roles/index.ts b/api/api/v1/accounts/:id/roles/index.ts index d3718d05..fedf9adc 100644 --- a/api/api/v1/accounts/:id/roles/index.ts +++ b/api/api/v1/accounts/:id/roles/index.ts @@ -1,9 +1,7 @@ -import { apiRoute, auth } from "@/api"; +import { apiRoute, auth, withUserParam } from "@/api"; import { createRoute } from "@hono/zod-openapi"; -import { Role, User } from "@versia/kit/db"; +import { Role } from "@versia/kit/db"; import { z } from "zod"; -import { ApiError } from "~/classes/errors/api-error"; -import { ErrorSchema } from "~/types/api"; const route = createRoute({ method: "get", @@ -13,6 +11,7 @@ const route = createRoute({ auth({ auth: false, }), + withUserParam, ] as const, request: { params: z.object({ @@ -28,26 +27,12 @@ const route = createRoute({ }, }, }, - 404: { - description: "User not found", - content: { - "application/json": { - schema: ErrorSchema, - }, - }, - }, }, }); export default apiRoute((app) => { app.openapi(route, async (context) => { - const { id } = context.req.valid("param"); - - const targetUser = await User.fromId(id); - - if (!targetUser) { - throw new ApiError(404, "User not found"); - } + const targetUser = context.get("user"); const roles = await Role.getUserRoles( targetUser.id, diff --git a/api/api/v1/accounts/:id/statuses.ts b/api/api/v1/accounts/:id/statuses.ts index 04b30cf6..71d24c22 100644 --- a/api/api/v1/accounts/:id/statuses.ts +++ b/api/api/v1/accounts/:id/statuses.ts @@ -1,11 +1,9 @@ -import { apiRoute, auth } from "@/api"; +import { apiRoute, auth, withUserParam } from "@/api"; import { createRoute } from "@hono/zod-openapi"; -import { Note, Timeline, User } from "@versia/kit/db"; +import { Note, Timeline } from "@versia/kit/db"; import { Notes, RolePermissions } from "@versia/kit/tables"; import { and, eq, gt, gte, inArray, isNull, lt, or, sql } from "drizzle-orm"; import { z } from "zod"; -import { ApiError } from "~/classes/errors/api-error"; -import { ErrorSchema } from "~/types/api"; const schemas = { param: z.object({ @@ -53,6 +51,7 @@ const route = createRoute({ ], scopes: ["read:statuses"], }), + withUserParam, ] as const, request: { params: schemas.param, @@ -72,27 +71,13 @@ const route = createRoute({ }, }, }, - 404: { - description: "User not found", - content: { - "application/json": { - schema: ErrorSchema, - }, - }, - }, }, }); export default apiRoute((app) => app.openapi(route, async (context) => { - const { id } = context.req.valid("param"); const { user } = context.get("auth"); - - const otherUser = await User.fromId(id); - - if (!otherUser) { - throw new ApiError(404, "User not found"); - } + const otherUser = context.get("user"); const { max_id, @@ -110,7 +95,7 @@ export default apiRoute((app) => max_id ? lt(Notes.id, max_id) : undefined, since_id ? gte(Notes.id, since_id) : undefined, min_id ? gt(Notes.id, min_id) : undefined, - eq(Notes.authorId, id), + eq(Notes.authorId, otherUser.id), only_media ? sql`EXISTS (SELECT 1 FROM "Attachments" WHERE "Attachments"."noteId" = ${Notes.id})` : undefined, diff --git a/api/api/v1/accounts/:id/unblock.ts b/api/api/v1/accounts/:id/unblock.ts index 162478f7..fe892b08 100644 --- a/api/api/v1/accounts/:id/unblock.ts +++ b/api/api/v1/accounts/:id/unblock.ts @@ -1,10 +1,8 @@ -import { apiRoute, auth } from "@/api"; +import { apiRoute, auth, withUserParam } from "@/api"; import { createRoute } from "@hono/zod-openapi"; -import { Relationship, User } from "@versia/kit/db"; +import { Relationship } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import { z } from "zod"; -import { ApiError } from "~/classes/errors/api-error"; -import { ErrorSchema } from "~/types/api"; const route = createRoute({ method: "post", @@ -20,6 +18,7 @@ const route = createRoute({ RolePermissions.ViewAccounts, ], }), + withUserParam, ] as const, request: { params: z.object({ @@ -35,27 +34,13 @@ const route = createRoute({ }, }, }, - 404: { - description: "User not found", - content: { - "application/json": { - schema: ErrorSchema, - }, - }, - }, }, }); export default apiRoute((app) => app.openapi(route, async (context) => { - const { id } = context.req.valid("param"); const { user } = context.get("auth"); - - const otherUser = await User.fromId(id); - - if (!otherUser) { - throw new ApiError(404, "User not found"); - } + const otherUser = context.get("user"); const foundRelationship = await Relationship.fromOwnerAndSubject( user, diff --git a/api/api/v1/accounts/:id/unfollow.ts b/api/api/v1/accounts/:id/unfollow.ts index 2c24fe9e..19cf3b57 100644 --- a/api/api/v1/accounts/:id/unfollow.ts +++ b/api/api/v1/accounts/:id/unfollow.ts @@ -1,9 +1,8 @@ -import { apiRoute, auth } from "@/api"; +import { apiRoute, auth, withUserParam } from "@/api"; import { createRoute } from "@hono/zod-openapi"; -import { Relationship, User } from "@versia/kit/db"; +import { Relationship } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import { z } from "zod"; -import { ApiError } from "~/classes/errors/api-error"; import { ErrorSchema } from "~/types/api"; const route = createRoute({ @@ -20,6 +19,7 @@ const route = createRoute({ RolePermissions.ViewAccounts, ], }), + withUserParam, ] as const, request: { params: z.object({ @@ -35,15 +35,6 @@ const route = createRoute({ }, }, }, - - 404: { - description: "User not found", - content: { - "application/json": { - schema: ErrorSchema, - }, - }, - }, 500: { description: "Failed to unfollow user during federation", content: { @@ -57,14 +48,8 @@ const route = createRoute({ export default apiRoute((app) => app.openapi(route, async (context) => { - const { id } = context.req.valid("param"); const { user } = context.get("auth"); - - const otherUser = await User.fromId(id); - - if (!otherUser) { - throw new ApiError(404, "User not found"); - } + const otherUser = context.get("user"); const foundRelationship = await Relationship.fromOwnerAndSubject( user, diff --git a/api/api/v1/accounts/:id/unmute.ts b/api/api/v1/accounts/:id/unmute.ts index 3977276c..c9ebe777 100644 --- a/api/api/v1/accounts/:id/unmute.ts +++ b/api/api/v1/accounts/:id/unmute.ts @@ -1,10 +1,8 @@ -import { apiRoute, auth } from "@/api"; +import { apiRoute, auth, withUserParam } from "@/api"; import { createRoute } from "@hono/zod-openapi"; -import { Relationship, User } from "@versia/kit/db"; +import { Relationship } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import { z } from "zod"; -import { ApiError } from "~/classes/errors/api-error"; -import { ErrorSchema } from "~/types/api"; const route = createRoute({ method: "post", @@ -20,6 +18,7 @@ const route = createRoute({ RolePermissions.ViewAccounts, ], }), + withUserParam, ] as const, request: { params: z.object({ @@ -35,28 +34,13 @@ const route = createRoute({ }, }, }, - - 404: { - description: "User not found", - content: { - "application/json": { - schema: ErrorSchema, - }, - }, - }, }, }); export default apiRoute((app) => app.openapi(route, async (context) => { - const { id } = context.req.valid("param"); const { user } = context.get("auth"); - - const otherUser = await User.fromId(id); - - if (!otherUser) { - throw new ApiError(404, "User not found"); - } + const otherUser = context.get("user"); const foundRelationship = await Relationship.fromOwnerAndSubject( user, diff --git a/api/api/v1/accounts/:id/unpin.ts b/api/api/v1/accounts/:id/unpin.ts index a729957f..ee4baf93 100644 --- a/api/api/v1/accounts/:id/unpin.ts +++ b/api/api/v1/accounts/:id/unpin.ts @@ -1,10 +1,8 @@ -import { apiRoute, auth } from "@/api"; +import { apiRoute, auth, withUserParam } from "@/api"; import { createRoute } from "@hono/zod-openapi"; -import { Relationship, User } from "@versia/kit/db"; +import { Relationship } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import { z } from "zod"; -import { ApiError } from "~/classes/errors/api-error"; -import { ErrorSchema } from "~/types/api"; const route = createRoute({ method: "post", @@ -20,6 +18,7 @@ const route = createRoute({ RolePermissions.ViewAccounts, ], }), + withUserParam, ] as const, request: { params: z.object({ @@ -35,28 +34,13 @@ const route = createRoute({ }, }, }, - - 404: { - description: "User not found", - content: { - "application/json": { - schema: ErrorSchema, - }, - }, - }, }, }); export default apiRoute((app) => app.openapi(route, async (context) => { - const { id } = context.req.valid("param"); const { user } = context.get("auth"); - - const otherUser = await User.fromId(id); - - if (!otherUser) { - throw new ApiError(404, "User not found"); - } + const otherUser = context.get("user"); const foundRelationship = await Relationship.fromOwnerAndSubject( user, diff --git a/api/api/v1/statuses/:id/context.ts b/api/api/v1/statuses/:id/context.ts index 7e26d503..b21e58e4 100644 --- a/api/api/v1/statuses/:id/context.ts +++ b/api/api/v1/statuses/:id/context.ts @@ -1,9 +1,8 @@ -import { apiRoute, auth } from "@/api"; +import { apiRoute, auth, withNoteParam } from "@/api"; import { createRoute } from "@hono/zod-openapi"; import { Note } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import { z } from "zod"; -import { ApiError } from "~/classes/errors/api-error"; import { ErrorSchema } from "~/types/api"; const route = createRoute({ @@ -14,6 +13,7 @@ const route = createRoute({ auth: false, permissions: [RolePermissions.ViewNotes], }), + withNoteParam, ] as const, summary: "Get status context", request: { @@ -46,19 +46,12 @@ const route = createRoute({ export default apiRoute((app) => app.openapi(route, async (context) => { - const { id } = context.req.valid("param"); - const { user } = context.get("auth"); + const note = context.get("note"); - const foundStatus = await Note.fromId(id, user?.id); + const ancestors = await note.getAncestors(user ?? null); - if (!foundStatus) { - throw new ApiError(404, "Note not found"); - } - - const ancestors = await foundStatus.getAncestors(user ?? null); - - const descendants = await foundStatus.getDescendants(user ?? null); + const descendants = await note.getDescendants(user ?? null); return context.json( { diff --git a/api/api/v1/statuses/:id/favourite.test.ts b/api/api/v1/statuses/:id/favourite.test.ts index 381db73d..1c097343 100644 --- a/api/api/v1/statuses/:id/favourite.test.ts +++ b/api/api/v1/statuses/:id/favourite.test.ts @@ -32,7 +32,6 @@ describe("/api/v1/statuses/:id/favourite", () => { }, }, ); - expect(response.status).toBe(200); const json = (await response.json()) as ApiStatus; diff --git a/api/api/v1/statuses/:id/favourite.ts b/api/api/v1/statuses/:id/favourite.ts index 287b54c8..e89bd84e 100644 --- a/api/api/v1/statuses/:id/favourite.ts +++ b/api/api/v1/statuses/:id/favourite.ts @@ -1,10 +1,8 @@ -import { apiRoute, auth } from "@/api"; +import { apiRoute, auth, withNoteParam } from "@/api"; import { createRoute } from "@hono/zod-openapi"; import { Note } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import { z } from "zod"; -import { ApiError } from "~/classes/errors/api-error"; -import { ErrorSchema } from "~/types/api"; const route = createRoute({ method: "post", @@ -18,6 +16,7 @@ const route = createRoute({ RolePermissions.ViewNotes, ], }), + withNoteParam, ] as const, request: { params: z.object({ @@ -33,29 +32,13 @@ const route = createRoute({ }, }, }, - - 404: { - description: "Record not found", - content: { - "application/json": { - schema: ErrorSchema, - }, - }, - }, }, }); export default apiRoute((app) => app.openapi(route, async (context) => { - const { id } = context.req.valid("param"); - const { user } = context.get("auth"); - - const note = await Note.fromId(id, user?.id); - - if (!(note && (await note?.isViewableByUser(user)))) { - throw new ApiError(404, "Note not found"); - } + const note = context.get("note"); await user.like(note); diff --git a/api/api/v1/statuses/:id/favourited_by.ts b/api/api/v1/statuses/:id/favourited_by.ts index e9b6efd9..5287b248 100644 --- a/api/api/v1/statuses/:id/favourited_by.ts +++ b/api/api/v1/statuses/:id/favourited_by.ts @@ -1,11 +1,9 @@ -import { apiRoute, auth } from "@/api"; +import { apiRoute, auth, withNoteParam } from "@/api"; import { createRoute } from "@hono/zod-openapi"; -import { Note, Timeline, User } from "@versia/kit/db"; +import { Timeline, User } from "@versia/kit/db"; import { RolePermissions, Users } from "@versia/kit/tables"; import { and, gt, gte, lt, sql } from "drizzle-orm"; import { z } from "zod"; -import { ApiError } from "~/classes/errors/api-error"; -import { ErrorSchema } from "~/types/api"; const schemas = { query: z.object({ @@ -31,6 +29,7 @@ const route = createRoute({ RolePermissions.ViewNoteLikes, ], }), + withNoteParam, ] as const, request: { params: schemas.param, @@ -45,30 +44,13 @@ const route = createRoute({ }, }, }, - - 404: { - description: "Record not found", - content: { - "application/json": { - schema: ErrorSchema, - }, - }, - }, }, }); export default apiRoute((app) => app.openapi(route, async (context) => { const { max_id, since_id, min_id, limit } = context.req.valid("query"); - const { id } = context.req.valid("param"); - - const { user } = context.get("auth"); - - const note = await Note.fromId(id, user?.id); - - if (!(note && (await note?.isViewableByUser(user)))) { - throw new ApiError(404, "Note not found"); - } + const note = context.get("note"); const { objects, link } = await Timeline.getUserTimeline( and( diff --git a/api/api/v1/statuses/:id/index.ts b/api/api/v1/statuses/:id/index.ts index 6908b019..10ea9981 100644 --- a/api/api/v1/statuses/:id/index.ts +++ b/api/api/v1/statuses/:id/index.ts @@ -1,4 +1,4 @@ -import { apiRoute, auth, jsonOrForm } from "@/api"; +import { apiRoute, auth, jsonOrForm, withNoteParam } from "@/api"; import { createRoute } from "@hono/zod-openapi"; import { Attachment, Note } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; @@ -75,6 +75,7 @@ const routeGet = createRoute({ auth: false, permissions: [RolePermissions.ViewNotes], }), + withNoteParam, ] as const, request: { params: schemas.param, @@ -111,6 +112,7 @@ const routeDelete = createRoute({ RolePermissions.ViewNotes, ], }), + withNoteParam, ] as const, request: { params: schemas.param, @@ -156,6 +158,7 @@ const routePut = createRoute({ ], }), jsonOrForm(), + withNoteParam, ] as const, request: { params: schemas.param, @@ -190,14 +193,6 @@ const routePut = createRoute({ }, }, }, - 404: { - description: "Record not found", - content: { - "application/json": { - schema: ErrorSchema, - }, - }, - }, 422: { description: "Invalid media IDs", content: { @@ -211,27 +206,15 @@ const routePut = createRoute({ export default apiRoute((app) => { app.openapi(routeGet, async (context) => { - const { id } = context.req.valid("param"); const { user } = context.get("auth"); - - const note = await Note.fromId(id, user?.id); - - if (!(note && (await note?.isViewableByUser(user)))) { - throw new ApiError(404, "Note not found"); - } + const note = context.get("note"); return context.json(await note.toApi(user), 200); }); app.openapi(routeDelete, async (context) => { - const { id } = context.req.valid("param"); const { user } = context.get("auth"); - - const note = await Note.fromId(id, user?.id); - - if (!(note && (await note?.isViewableByUser(user)))) { - throw new ApiError(404, "Note not found"); - } + const note = context.get("note"); if (note.author.id !== user.id) { throw new ApiError(401, "Unauthorized"); @@ -246,14 +229,8 @@ export default apiRoute((app) => { }); app.openapi(routePut, async (context) => { - const { id } = context.req.valid("param"); const { user } = context.get("auth"); - - const note = await Note.fromId(id, user?.id); - - if (!(note && (await note?.isViewableByUser(user)))) { - throw new ApiError(404, "Note not found"); - } + const note = context.get("note"); if (note.author.id !== user.id) { throw new ApiError(401, "Unauthorized"); diff --git a/api/api/v1/statuses/:id/pin.ts b/api/api/v1/statuses/:id/pin.ts index 6bab4f78..899412e6 100644 --- a/api/api/v1/statuses/:id/pin.ts +++ b/api/api/v1/statuses/:id/pin.ts @@ -1,4 +1,4 @@ -import { apiRoute, auth } from "@/api"; +import { apiRoute, auth, withNoteParam } from "@/api"; import { createRoute } from "@hono/zod-openapi"; import { Note, db } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; @@ -7,12 +7,6 @@ import { z } from "zod"; import { ApiError } from "~/classes/errors/api-error"; import { ErrorSchema } from "~/types/api"; -const schemas = { - param: z.object({ - id: z.string().uuid(), - }), -}; - const route = createRoute({ method: "post", path: "/api/v1/statuses/{id}/pin", @@ -25,9 +19,12 @@ const route = createRoute({ RolePermissions.ViewNotes, ], }), + withNoteParam, ] as const, request: { - params: schemas.param, + params: z.object({ + id: z.string().uuid(), + }), }, responses: { 200: { @@ -46,14 +43,6 @@ const route = createRoute({ }, }, }, - 404: { - description: "Record not found", - content: { - "application/json": { - schema: ErrorSchema, - }, - }, - }, 422: { description: "Already pinned", content: { @@ -67,16 +56,10 @@ const route = createRoute({ export default apiRoute((app) => app.openapi(route, async (context) => { - const { id } = context.req.valid("param"); const { user } = context.get("auth"); + const note = context.get("note"); - const foundStatus = await Note.fromId(id, user?.id); - - if (!foundStatus) { - throw new ApiError(404, "Note not found"); - } - - if (foundStatus.author.id !== user.id) { + if (note.author.id !== user.id) { throw new ApiError(401, "Unauthorized"); } @@ -84,7 +67,7 @@ export default apiRoute((app) => await db.query.UserToPinnedNotes.findFirst({ where: (userPinnedNote, { and, eq }): SQL | undefined => and( - eq(userPinnedNote.noteId, foundStatus.data.id), + eq(userPinnedNote.noteId, note.data.id), eq(userPinnedNote.userId, user.id), ), }) @@ -92,8 +75,8 @@ export default apiRoute((app) => throw new ApiError(422, "Already pinned"); } - await user.pin(foundStatus); + await user.pin(note); - return context.json(await foundStatus.toApi(user), 200); + return context.json(await note.toApi(user), 200); }), ); diff --git a/api/api/v1/statuses/:id/reblog.ts b/api/api/v1/statuses/:id/reblog.ts index 4ab0bfd0..6651fae4 100644 --- a/api/api/v1/statuses/:id/reblog.ts +++ b/api/api/v1/statuses/:id/reblog.ts @@ -1,4 +1,4 @@ -import { apiRoute, auth, jsonOrForm } from "@/api"; +import { apiRoute, auth, jsonOrForm, withNoteParam } from "@/api"; import { createRoute } from "@hono/zod-openapi"; import { Note } from "@versia/kit/db"; import { Notes, RolePermissions } from "@versia/kit/tables"; @@ -29,6 +29,7 @@ const route = createRoute({ ], }), jsonOrForm(), + withNoteParam, ] as const, request: { params: schemas.param, @@ -55,15 +56,6 @@ const route = createRoute({ }, }, }, - - 404: { - description: "Record not found", - content: { - "application/json": { - schema: ErrorSchema, - }, - }, - }, 422: { description: "Already reblogged", content: { @@ -85,15 +77,9 @@ const route = createRoute({ export default apiRoute((app) => app.openapi(route, async (context) => { - const { id } = context.req.valid("param"); const { visibility } = context.req.valid("json"); const { user } = context.get("auth"); - - const note = await Note.fromId(id, user.id); - - if (!(note && (await note?.isViewableByUser(user)))) { - throw new ApiError(404, "Note not found"); - } + const note = context.get("note"); const existingReblog = await Note.fromSql( and(eq(Notes.authorId, user.id), eq(Notes.reblogId, note.data.id)), diff --git a/api/api/v1/statuses/:id/reblogged_by.ts b/api/api/v1/statuses/:id/reblogged_by.ts index a2a3d476..72a06c5b 100644 --- a/api/api/v1/statuses/:id/reblogged_by.ts +++ b/api/api/v1/statuses/:id/reblogged_by.ts @@ -1,11 +1,9 @@ -import { apiRoute, auth } from "@/api"; +import { apiRoute, auth, withNoteParam } from "@/api"; import { createRoute } from "@hono/zod-openapi"; -import { Note, Timeline, User } from "@versia/kit/db"; +import { Timeline, User } from "@versia/kit/db"; import { RolePermissions, Users } from "@versia/kit/tables"; import { and, gt, gte, lt, sql } from "drizzle-orm"; import { z } from "zod"; -import { ApiError } from "~/classes/errors/api-error"; -import { ErrorSchema } from "~/types/api"; const schemas = { param: z.object({ @@ -31,6 +29,7 @@ const route = createRoute({ RolePermissions.ViewNoteBoosts, ], }), + withNoteParam, ] as const, request: { params: schemas.param, @@ -45,29 +44,13 @@ const route = createRoute({ }, }, }, - - 404: { - description: "Record not found", - content: { - "application/json": { - schema: ErrorSchema, - }, - }, - }, }, }); export default apiRoute((app) => app.openapi(route, async (context) => { - const { id } = context.req.valid("param"); const { max_id, min_id, since_id, limit } = context.req.valid("query"); - const { user } = context.get("auth"); - - const note = await Note.fromId(id, user.id); - - if (!(note && (await note?.isViewableByUser(user)))) { - throw new ApiError(404, "Note not found"); - } + const note = context.get("note"); const { objects, link } = await Timeline.getUserTimeline( and( diff --git a/api/api/v1/statuses/:id/source.ts b/api/api/v1/statuses/:id/source.ts index 486392ad..a54a53da 100644 --- a/api/api/v1/statuses/:id/source.ts +++ b/api/api/v1/statuses/:id/source.ts @@ -1,11 +1,8 @@ -import { apiRoute, auth } from "@/api"; +import { apiRoute, auth, withNoteParam } from "@/api"; import { createRoute } from "@hono/zod-openapi"; import type { StatusSource as ApiStatusSource } from "@versia/client/types"; -import { Note } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import { z } from "zod"; -import { ApiError } from "~/classes/errors/api-error"; -import { ErrorSchema } from "~/types/api"; const route = createRoute({ method: "get", @@ -19,6 +16,7 @@ const route = createRoute({ RolePermissions.ViewNotes, ], }), + withNoteParam, ] as const, request: { params: z.object({ @@ -38,28 +36,12 @@ const route = createRoute({ }, }, }, - - 404: { - description: "Record not found", - content: { - "application/json": { - schema: ErrorSchema, - }, - }, - }, }, }); export default apiRoute((app) => - app.openapi(route, async (context) => { - const { id } = context.req.valid("param"); - const { user } = context.get("auth"); - - const note = await Note.fromId(id, user.id); - - if (!(note && (await note?.isViewableByUser(user)))) { - throw new ApiError(404, "Note not found"); - } + app.openapi(route, (context) => { + const note = context.get("note"); return context.json( { diff --git a/api/api/v1/statuses/:id/unfavourite.ts b/api/api/v1/statuses/:id/unfavourite.ts index 7914a11e..09c75dd1 100644 --- a/api/api/v1/statuses/:id/unfavourite.ts +++ b/api/api/v1/statuses/:id/unfavourite.ts @@ -1,10 +1,8 @@ -import { apiRoute, auth } from "@/api"; +import { apiRoute, auth, withNoteParam } from "@/api"; import { createRoute } from "@hono/zod-openapi"; import { Note } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import { z } from "zod"; -import { ApiError } from "~/classes/errors/api-error"; -import { ErrorSchema } from "~/types/api"; const route = createRoute({ method: "post", @@ -18,6 +16,7 @@ const route = createRoute({ RolePermissions.ViewNotes, ], }), + withNoteParam, ] as const, request: { params: z.object({ @@ -33,28 +32,13 @@ const route = createRoute({ }, }, }, - - 404: { - description: "Record not found", - content: { - "application/json": { - schema: ErrorSchema, - }, - }, - }, }, }); export default apiRoute((app) => app.openapi(route, async (context) => { - const { id } = context.req.valid("param"); const { user } = context.get("auth"); - - const note = await Note.fromId(id, user.id); - - if (!(note && (await note?.isViewableByUser(user)))) { - throw new ApiError(404, "Note not found"); - } + const note = context.get("note"); await user.unlike(note); diff --git a/api/api/v1/statuses/:id/unpin.ts b/api/api/v1/statuses/:id/unpin.ts index 2849a251..bf5561fa 100644 --- a/api/api/v1/statuses/:id/unpin.ts +++ b/api/api/v1/statuses/:id/unpin.ts @@ -1,4 +1,4 @@ -import { apiRoute, auth } from "@/api"; +import { apiRoute, auth, withNoteParam } from "@/api"; import { createRoute } from "@hono/zod-openapi"; import { Note } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; @@ -18,6 +18,7 @@ const route = createRoute({ RolePermissions.ViewNotes, ], }), + withNoteParam, ] as const, request: { params: z.object({ @@ -41,38 +42,24 @@ const route = createRoute({ }, }, }, - 404: { - description: "Record not found", - content: { - "application/json": { - schema: ErrorSchema, - }, - }, - }, }, }); export default apiRoute((app) => app.openapi(route, async (context) => { - const { id } = context.req.valid("param"); const { user } = context.get("auth"); + const note = context.get("note"); - const status = await Note.fromId(id, user.id); - - if (!status) { - throw new ApiError(404, "Note not found"); - } - - if (status.author.id !== user.id) { + if (note.author.id !== user.id) { throw new ApiError(401, "Unauthorized"); } - await user.unpin(status); + await user.unpin(note); - if (!status) { + if (!note) { throw new ApiError(404, "Note not found"); } - return context.json(await status.toApi(user), 200); + return context.json(await note.toApi(user), 200); }), ); diff --git a/api/api/v1/statuses/:id/unreblog.ts b/api/api/v1/statuses/:id/unreblog.ts index 471092b8..e56f2082 100644 --- a/api/api/v1/statuses/:id/unreblog.ts +++ b/api/api/v1/statuses/:id/unreblog.ts @@ -1,4 +1,4 @@ -import { apiRoute, auth } from "@/api"; +import { apiRoute, auth, withNoteParam } from "@/api"; import { createRoute } from "@hono/zod-openapi"; import { Note } from "@versia/kit/db"; import { Notes, RolePermissions } from "@versia/kit/tables"; @@ -19,6 +19,7 @@ const route = createRoute({ RolePermissions.ViewNotes, ], }), + withNoteParam, ] as const, request: { params: z.object({ @@ -34,15 +35,6 @@ const route = createRoute({ }, }, }, - - 404: { - description: "Record not found", - content: { - "application/json": { - schema: ErrorSchema, - }, - }, - }, 422: { description: "Not already reblogged", content: { @@ -58,13 +50,7 @@ export default apiRoute((app) => app.openapi(route, async (context) => { const { id } = context.req.valid("param"); const { user } = context.get("auth"); - - const note = await Note.fromId(id, user.id); - - // Check if user is authorized to view this status (if it's private) - if (!(note && (await note?.isViewableByUser(user)))) { - throw new ApiError(404, "Note not found"); - } + const note = context.get("note"); const existingReblog = await Note.fromSql( and(eq(Notes.authorId, user.id), eq(Notes.reblogId, note.data.id)), diff --git a/bun.lockb b/bun.lockb index 4b2e5973..53d081ee 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 7ccbbeab..fb7682d7 100644 --- a/package.json +++ b/package.json @@ -113,6 +113,7 @@ "@hono/prometheus": "^1.0.1", "@hono/swagger-ui": "^0.5.0", "@hono/zod-openapi": "0.18.3", + "@hono/zod-validator": "^0.4.2", "@inquirer/confirm": "^5.1.1", "@inquirer/input": "^4.1.1", "@json2csv/plainjs": "^7.0.6", diff --git a/utils/api.ts b/utils/api.ts index e97f0101..8cb16283 100644 --- a/utils/api.ts +++ b/utils/api.ts @@ -1,11 +1,13 @@ import type { OpenAPIHono } from "@hono/zod-openapi"; +import { zValidator } from "@hono/zod-validator"; import { getLogger } from "@logtape/logtape"; -import { Application, Token, db } from "@versia/kit/db"; +import { Application, Note, Token, User, db } from "@versia/kit/db"; import { Challenges, type RolePermissions } from "@versia/kit/tables"; import { extractParams, verifySolution } from "altcha-lib"; import chalk from "chalk"; import { type SQL, eq } from "drizzle-orm"; import type { Context, MiddlewareHandler } from "hono"; +import { every } from "hono/combine"; import { createMiddleware } from "hono/factory"; import { anyOf, @@ -22,7 +24,7 @@ import { oneOrMore, } from "magic-regexp"; import { type ParsedQs, parse } from "qs"; -import type { z } from "zod"; +import { z } from "zod"; import { fromZodError } from "zod-validation-error"; import { ApiError } from "~/classes/errors/api-error"; import type { AuthData } from "~/classes/functions/user"; @@ -292,6 +294,85 @@ export const auth = (options: { }); }; +type WithIdParam = { + in: { param: { id: string } }; + out: { param: { id: string } }; +}; + +/** + * Middleware to check if a note exists and is viewable by the user. + * + * Useful in /api/v1/statuses/:id/* routes + * @returns MiddlewareHandler + */ +export const withNoteParam = every( + zValidator("param", z.object({ id: z.string().uuid() }), handleZodError), + createMiddleware< + HonoEnv & { + Variables: { + note: Note; + }; + }, + string, + WithIdParam + >(async (context, next) => { + const { id } = context.req.valid("param"); + const { user } = context.get("auth"); + + const note = await Note.fromId(id, user?.id); + + if (!(note && (await note.isViewableByUser(user)))) { + throw new ApiError(404, "Note not found"); + } + + context.set("note", note); + + await next(); + }), +) as MiddlewareHandler< + HonoEnv & { + Variables: { + note: Note; + }; + } +>; + +/** + * Middleware to check if a user exists + * + * Useful in /api/v1/accounts/:id/* routes + * @returns MiddlewareHandler + */ +export const withUserParam = every( + zValidator("param", z.object({ id: z.string().uuid() }), handleZodError), + createMiddleware< + HonoEnv & { + Variables: { + user: User; + }; + }, + string, + WithIdParam + >(async (context, next) => { + const { id } = context.req.valid("param"); + const user = await User.fromId(id); + + if (!user) { + throw new ApiError(404, "User not found"); + } + + context.set("user", user); + + await next(); + }), +) as MiddlewareHandler< + HonoEnv & { + Variables: { + user: User; + }; + } +>; + // Helper function to parse form data async function parseFormData(context: Context): Promise<{ parsed: ParsedQs;