diff --git a/src/v2/routes/asset/delete-asset.ts b/src/v2/routes/asset/delete-asset.ts new file mode 100644 index 00000000..e2a145bf --- /dev/null +++ b/src/v2/routes/asset/delete-asset.ts @@ -0,0 +1,81 @@ +import { OpenAPIHono } from "@hono/zod-openapi" +import { getConnection } from "@/v2/db/turso" +import { eq } from "drizzle-orm" +import { asset } from "@/v2/db/schema" +import { createRoute } from "@hono/zod-openapi" +import { GenericResponses } from "@/v2/lib/response-schemas" +import { z } from "@hono/zod-openapi" + +const deleteAssetByIdSchema = z.object({ + id: z.string().openapi({ + param: { + name: "id", + in: "path", + description: "The ID of the asset to delete.", + example: "1", + required: true, + }, + }), +}) + +const deleteAssetByIdResponseSchema = z.object({ + success: z.literal(true), +}) + +const deleteAssetByIdRoute = createRoute({ + path: "/{id}", + method: "delete", + description: + "Delete an asset from their ID. Must be the owner of the asset or an admin.", + tags: ["Asset"], + request: { + params: deleteAssetByIdSchema, + }, + responses: { + 200: { + description: "True if the asset was deleted.", + content: { + "application/json": { + schema: deleteAssetByIdResponseSchema, + }, + }, + }, + ...GenericResponses, + }, +}) + +const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() + +handler.openapi(deleteAssetByIdRoute, async (ctx) => { + const assetId = ctx.req.valid("param").id + + const { drizzle } = await getConnection(ctx.env) + + const [existingAsset] = await drizzle + .select({ id: asset.id }) + .from(asset) + .where(eq(asset.id, parseInt(assetId))) + .limit(1) + + if (!existingAsset) { + return ctx.json( + { + success: true, + message: "Asset not found", + }, + 400 + ) + } + + await drizzle.delete(asset).where(eq(asset.id, parseInt(assetId))) + // await ctx.env.FILES_BUCKET.delete(asset.url) + + return ctx.json( + { + success: true, + }, + 200 + ) +}) + +export default handler diff --git a/src/v2/routes/asset/delete/id/[id]/openapi.ts b/src/v2/routes/asset/delete/id/[id]/openapi.ts deleted file mode 100644 index 994956de..00000000 --- a/src/v2/routes/asset/delete/id/[id]/openapi.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { createRoute } from "@hono/zod-openapi" -import { deleteAssetByIdResponseSchema, deleteAssetByIdSchema } from "./schema" -import { GenericResponses } from "@/v2/lib/response-schemas" - -export const deleteAssetByIdRoute = createRoute({ - path: "/{id}", - method: "delete", - description: - "Delete an asset from their ID. Must be the owner of the asset or an admin.", - tags: ["Asset"], - request: { - params: deleteAssetByIdSchema, - }, - responses: { - 200: { - description: "True if the asset was deleted.", - content: { - "application/json": { - schema: deleteAssetByIdResponseSchema, - }, - }, - }, - ...GenericResponses, - }, -}) diff --git a/src/v2/routes/asset/delete/id/[id]/route.ts b/src/v2/routes/asset/delete/id/[id]/route.ts deleted file mode 100644 index 3d695abf..00000000 --- a/src/v2/routes/asset/delete/id/[id]/route.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { OpenAPIHono } from "@hono/zod-openapi" -import { deleteAssetByIdRoute } from "./openapi" -import { getConnection } from "@/v2/db/turso" -import { eq } from "drizzle-orm" -import { asset } from "@/v2/db/schema" - -const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() - -handler.openapi(deleteAssetByIdRoute, async (ctx) => { - const assetId = ctx.req.valid("param").id - - const { drizzle } = await getConnection(ctx.env) - - const [existingAsset] = await drizzle - .select({ id: asset.id }) - .from(asset) - .where(eq(asset.id, parseInt(assetId))) - .limit(1) - - if (!existingAsset) { - return ctx.json( - { - success: true, - message: "Asset not found", - }, - 400 - ) - } - - await drizzle.delete(asset).where(eq(asset.id, parseInt(assetId))) - // await ctx.env.FILES_BUCKET.delete(asset.url) - - return ctx.json( - { - success: true, - }, - 200 - ) -}) - -export default handler diff --git a/src/v2/routes/asset/delete/id/[id]/schema.ts b/src/v2/routes/asset/delete/id/[id]/schema.ts deleted file mode 100644 index 5030439f..00000000 --- a/src/v2/routes/asset/delete/id/[id]/schema.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { z } from "@hono/zod-openapi" - -export const deleteAssetByIdSchema = z.object({ - id: z.string().openapi({ - param: { - name: "id", - in: "path", - description: "The ID of the asset to delete.", - example: "1", - required: true, - }, - }), -}) - -export const deleteAssetByIdResponseSchema = z.object({ - success: z.literal(true), -}) diff --git a/src/v2/routes/collection/asset/add/id/[id]/openapi.ts b/src/v2/routes/asset/get-asset-comments.ts similarity index 100% rename from src/v2/routes/collection/asset/add/id/[id]/openapi.ts rename to src/v2/routes/asset/get-asset-comments.ts diff --git a/src/v2/routes/asset/likes/all/route.ts b/src/v2/routes/asset/get-asset-likes.ts similarity index 52% rename from src/v2/routes/asset/likes/all/route.ts rename to src/v2/routes/asset/get-asset-likes.ts index fb9b88ea..70abe2ba 100644 --- a/src/v2/routes/asset/likes/all/route.ts +++ b/src/v2/routes/asset/get-asset-likes.ts @@ -1,7 +1,48 @@ import { OpenAPIHono } from "@hono/zod-openapi" -import { allAssetLikesRoute } from "./openapi" import { getConnection } from "@/v2/db/turso" import { AuthSessionManager } from "@/v2/lib/managers/auth/user-session-manager" +import { createRoute } from "@hono/zod-openapi" +import { GenericResponses } from "@/v2/lib/response-schemas" +import { z } from "@hono/zod-openapi" +import { + selectAssetSchema, + selectAssetTagAssetSchema, + selectAssetTagSchema, + selectAssetLikesSchema, +} from "@/v2/db/schema" + +const allAssetLikesSchema = z.object({ + success: z.literal(true), + likes: z.array( + selectAssetLikesSchema.extend({ + asset: selectAssetSchema.extend({ + assetTagAsset: z.array( + selectAssetTagAssetSchema.extend({ + assetTag: selectAssetTagSchema, + }) + ), + }), + }) + ), +}) + +const allAssetLikesRoute = createRoute({ + path: "/", + method: "get", + description: "All your liked assets.", + tags: ["Asset"], + responses: { + 200: { + description: "Array of your liked assets.", + content: { + "application/json": { + schema: allAssetLikesSchema, + }, + }, + }, + ...GenericResponses, + }, +}) const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() diff --git a/src/v2/routes/asset/get-asset.ts b/src/v2/routes/asset/get-asset.ts new file mode 100644 index 00000000..6a5a3984 --- /dev/null +++ b/src/v2/routes/asset/get-asset.ts @@ -0,0 +1,138 @@ +import { OpenAPIHono } from "@hono/zod-openapi" +import { getConnection } from "@/v2/db/turso" +import { asset } from "@/v2/db/schema" +import { eq, sql } from "drizzle-orm" +import { createRoute } from "@hono/zod-openapi" +import { GenericResponses } from "@/v2/lib/response-schemas" +import { z } from "@hono/zod-openapi" +import { + selectAssetCategorySchema, + selectGameSchema, + selectAssetSchema, + selectAssetTagAssetSchema, + selectAssetTagSchema, + selectUserSchema, +} from "@/v2/db/schema" + +const getAssetByIdSchema = z.object({ + id: z.string().openapi({ + param: { + name: "id", + in: "path", + description: "The ID of the asset to retrieve.", + required: true, + }, + }), +}) + +const getAssetByIdResponseSchema = z.object({ + success: z.literal(true), + // mmm nested schemas + asset: selectAssetSchema.extend({ + assetTagAsset: z.array( + selectAssetTagAssetSchema.extend({ + assetTag: selectAssetTagSchema, + }) + ), + }), + authUser: selectUserSchema.pick({ + id: true, + avatarUrl: true, + displayName: true, + username: true, + usernameColour: true, + pronouns: true, + verified: true, + bio: true, + dateJoined: true, + plan: true, + role: true, + }), + game: selectGameSchema, + assetCategory: selectAssetCategorySchema, + // similarAssets: selectAssetSchema.array(), +}) + +const getAssetByIdRoute = createRoute({ + path: "/{id}", + method: "get", + description: "Get an asset by their ID.", + tags: ["Asset"], + request: { + params: getAssetByIdSchema, + }, + responses: { + 200: { + description: "The found asset & similar assets are returned.", + content: { + "application/json": { + schema: getAssetByIdResponseSchema, + }, + }, + }, + ...GenericResponses, + }, +}) + +const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() + +handler.openapi(getAssetByIdRoute, async (ctx) => { + const assetId = ctx.req.valid("param").id + + const { drizzle } = await getConnection(ctx.env) + + const foundAsset = await drizzle.query.asset.findFirst({ + where: (asset, { eq }) => eq(asset.id, parseInt(assetId)), + with: { + assetTagAsset: { + with: { + assetTag: true, + }, + }, + authUser: { + columns: { + id: true, + avatarUrl: true, + displayName: true, + username: true, + usernameColour: true, + pronouns: true, + verified: true, + bio: true, + dateJoined: true, + plan: true, + role: true, + }, + }, + game: true, + assetCategory: true, + }, + }) + + if (!foundAsset) { + return ctx.json( + { + success: false, + message: "Asset not found", + }, + 400 + ) + } + + await drizzle + .update(asset) + .set({ + viewCount: sql`${asset.viewCount} + 1`, + }) + .where(eq(asset.id, parseInt(assetId))) + + return ctx.json( + { + success: true, + asset: foundAsset, + }, + 200 + ) +}) + +export default handler diff --git a/src/v2/routes/asset/get/handler.ts b/src/v2/routes/asset/get/handler.ts deleted file mode 100644 index 36be2d76..00000000 --- a/src/v2/routes/asset/get/handler.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { OpenAPIHono } from "@hono/zod-openapi" -import GetAssetByIDRoute from "@/v2/routes/asset/get/id/[id]/route" - -const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() - -handler.route("/id", GetAssetByIDRoute) - -export default handler diff --git a/src/v2/routes/asset/get/id/[id]/openapi.ts b/src/v2/routes/asset/get/id/[id]/openapi.ts deleted file mode 100644 index cbaa48c3..00000000 --- a/src/v2/routes/asset/get/id/[id]/openapi.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { createRoute } from "@hono/zod-openapi" -import { getAssetByIdSchema, getAssetByIdResponseSchema } from "./schema" -import { GenericResponses } from "@/v2/lib/response-schemas" - -export const getAssetByIdRoute = createRoute({ - path: "/{id}", - method: "get", - description: "Get an asset by their ID.", - tags: ["Asset"], - request: { - params: getAssetByIdSchema, - }, - responses: { - 200: { - description: "The found asset & similar assets are returned.", - content: { - "application/json": { - schema: getAssetByIdResponseSchema, - }, - }, - }, - ...GenericResponses, - }, -}) diff --git a/src/v2/routes/asset/get/id/[id]/route.ts b/src/v2/routes/asset/get/id/[id]/route.ts deleted file mode 100644 index 12f95494..00000000 --- a/src/v2/routes/asset/get/id/[id]/route.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { OpenAPIHono } from "@hono/zod-openapi" -import { getAssetByIdRoute } from "./openapi" -import { getConnection } from "@/v2/db/turso" -import { asset } from "@/v2/db/schema" -import { eq, sql } from "drizzle-orm" - -const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() - -handler.openapi(getAssetByIdRoute, async (ctx) => { - const assetId = ctx.req.valid("param").id - - const { drizzle } = await getConnection(ctx.env) - - const foundAsset = await drizzle.query.asset.findFirst({ - where: (asset, { eq }) => eq(asset.id, parseInt(assetId)), - with: { - assetTagAsset: { - with: { - assetTag: true, - }, - }, - authUser: { - columns: { - id: true, - avatarUrl: true, - displayName: true, - username: true, - usernameColour: true, - pronouns: true, - verified: true, - bio: true, - dateJoined: true, - plan: true, - role: true, - }, - }, - game: true, - assetCategory: true, - }, - }) - - if (!foundAsset) { - return ctx.json( - { - success: false, - message: "Asset not found", - }, - 400 - ) - } - - await drizzle - .update(asset) - .set({ - viewCount: sql`${asset.viewCount} + 1`, - }) - .where(eq(asset.id, parseInt(assetId))) - - return ctx.json( - { - success: true, - asset: foundAsset, - }, - 200 - ) -}) - -export default handler diff --git a/src/v2/routes/asset/get/id/[id]/schema.ts b/src/v2/routes/asset/get/id/[id]/schema.ts deleted file mode 100644 index 396f70e9..00000000 --- a/src/v2/routes/asset/get/id/[id]/schema.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { z } from "@hono/zod-openapi" -import { - selectAssetCategorySchema, - selectGameSchema, - selectAssetSchema, - selectAssetTagAssetSchema, - selectAssetTagSchema, - selectUserSchema, -} from "@/v2/db/schema" - -export const getAssetByIdSchema = z.object({ - id: z.string().openapi({ - param: { - name: "id", - in: "path", - description: "The ID of the asset to retrieve.", - required: true, - }, - }), -}) - -export const getAssetByIdResponseSchema = z.object({ - success: z.literal(true), - // mmm nested schemas - asset: selectAssetSchema.extend({ - assetTagAsset: z.array( - selectAssetTagAssetSchema.extend({ - assetTag: selectAssetTagSchema, - }) - ), - }), - authUser: selectUserSchema.pick({ - id: true, - avatarUrl: true, - displayName: true, - username: true, - usernameColour: true, - pronouns: true, - verified: true, - bio: true, - dateJoined: true, - plan: true, - role: true, - }), - game: selectGameSchema, - assetCategory: selectAssetCategorySchema, - // similarAssets: selectAssetSchema.array(), -}) diff --git a/src/v2/routes/asset/handler.ts b/src/v2/routes/asset/handler.ts index 5b37d7b8..c31337d2 100644 --- a/src/v2/routes/asset/handler.ts +++ b/src/v2/routes/asset/handler.ts @@ -1,18 +1,29 @@ import { OpenAPIHono } from "@hono/zod-openapi" -import AssetGetRoute from "@/v2/routes/asset/get/handler" -import AssetSearchRoute from "@/v2/routes/asset/search/handler" -import AssetModifyRoute from "@/v2/routes/asset/modify/id/[id]/route" -import AssetUploadRoute from "@/v2/routes/asset/upload/route" -import AssetDeleteRoute from "@/v2/routes/asset/delete/id/[id]/route" -import AssetLikesRoute from "@/v2/routes/asset/likes/handler" + +import SearchAssetRoute from "@/v2/routes/asset/search-assets" +import GetAssetRoute from "@/v2/routes/asset/get-asset" + +import UploadAssetRoute from "@/v2/routes/asset/upload-asset" +import ModifyAssetRoute from "@/v2/routes/asset/modify-asset" +import DeleteAssetRoute from "@/v2/routes/asset/delete-asset" + +import LikeAssetByIdRoute from "@/v2/routes/asset/like-asset" +import UnlikeAssetByIdRoute from "@/v2/routes/asset/unlike-asset" + +import GetAssetLikesRoute from "@/v2/routes/asset/get-asset-likes" const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() -handler.route("/get", AssetGetRoute) -handler.route("/search", AssetSearchRoute) -handler.route("/modify/id", AssetModifyRoute) -handler.route("/upload", AssetUploadRoute) -handler.route("/delete/id", AssetDeleteRoute) -handler.route("/likes", AssetLikesRoute) +handler.route("/search", SearchAssetRoute) +handler.route("/get", GetAssetRoute) + +handler.route("/upload", UploadAssetRoute) +handler.route("/modify", ModifyAssetRoute) +handler.route("/delete", DeleteAssetRoute) + +handler.route("/like", LikeAssetByIdRoute) +handler.route("/unlike", UnlikeAssetByIdRoute) + +handler.route("/likes", GetAssetLikesRoute) export default handler diff --git a/src/v2/routes/asset/likes/like/id/[id]/route.ts b/src/v2/routes/asset/like-asset.ts similarity index 67% rename from src/v2/routes/asset/likes/like/id/[id]/route.ts rename to src/v2/routes/asset/like-asset.ts index 61877225..9273cbec 100644 --- a/src/v2/routes/asset/likes/like/id/[id]/route.ts +++ b/src/v2/routes/asset/like-asset.ts @@ -1,9 +1,48 @@ import { OpenAPIHono } from "@hono/zod-openapi" -import { likeAssetByIdRoute } from "./openapi" import { getConnection } from "@/v2/db/turso" import { AuthSessionManager } from "@/v2/lib/managers/auth/user-session-manager" import { asset, assetLikes } from "@/v2/db/schema" import { and, eq } from "drizzle-orm" +import { createRoute } from "@hono/zod-openapi" +import { GenericResponses } from "@/v2/lib/response-schemas" +import { z } from "@hono/zod-openapi" + +const likeAssetByIdSchema = z.object({ + id: z.string().openapi({ + param: { + description: "The id of the asset to like.", + example: "1", + in: "path", + name: "id", + required: true, + }, + }), +}) + +const likeAssetByIdResponseSchema = z.object({ + success: z.literal(true), +}) + +const likeAssetByIdRoute = createRoute({ + path: "/{id}", + method: "post", + description: "Like an asset from their ID.", + tags: ["Asset"], + request: { + params: likeAssetByIdSchema, + }, + responses: { + 200: { + description: "True if the asset was liked.", + content: { + "application/json": { + schema: likeAssetByIdResponseSchema, + }, + }, + }, + ...GenericResponses, + }, +}) const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() diff --git a/src/v2/routes/asset/likes/all/openapi.ts b/src/v2/routes/asset/likes/all/openapi.ts deleted file mode 100644 index f14cadc1..00000000 --- a/src/v2/routes/asset/likes/all/openapi.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { createRoute } from "@hono/zod-openapi" -import { allAssetLikesSchema } from "./schema" -import { GenericResponses } from "@/v2/lib/response-schemas" - -export const allAssetLikesRoute = createRoute({ - path: "/", - method: "get", - description: "All liked assets.", - tags: ["Asset"], - responses: { - 200: { - description: "Array of your liked assets.", - content: { - "application/json": { - schema: allAssetLikesSchema, - }, - }, - }, - ...GenericResponses, - }, -}) diff --git a/src/v2/routes/asset/likes/all/schema.ts b/src/v2/routes/asset/likes/all/schema.ts deleted file mode 100644 index ac44a066..00000000 --- a/src/v2/routes/asset/likes/all/schema.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { z } from "@hono/zod-openapi" -import { - selectAssetSchema, - selectAssetTagAssetSchema, - selectAssetTagSchema, - selectAssetLikesSchema, -} from "@/v2/db/schema" - -export const allAssetLikesSchema = z.object({ - success: z.literal(true), - likes: z.array( - selectAssetLikesSchema.extend({ - asset: selectAssetSchema.extend({ - assetTagAsset: z.array( - selectAssetTagAssetSchema.extend({ - assetTag: selectAssetTagSchema, - }) - ), - }), - }) - ), -}) diff --git a/src/v2/routes/asset/likes/handler.ts b/src/v2/routes/asset/likes/handler.ts deleted file mode 100644 index f6a2dba0..00000000 --- a/src/v2/routes/asset/likes/handler.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { OpenAPIHono } from "@hono/zod-openapi" -import UnlikeAssetByIdRoute from "@/v2/routes/asset/likes/unlike/id/[id]/route" -import LikeAssetByIdRoute from "@/v2/routes/asset/likes/like/id/[id]/route" -import AllAssetLikesRoute from "@/v2/routes/asset/likes/all/route" - -const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() - -handler.route("/unlike", UnlikeAssetByIdRoute) -handler.route("/like", LikeAssetByIdRoute) -handler.route("/all", AllAssetLikesRoute) - -export default handler diff --git a/src/v2/routes/asset/likes/like/id/[id]/openapi.ts b/src/v2/routes/asset/likes/like/id/[id]/openapi.ts deleted file mode 100644 index 2d344e61..00000000 --- a/src/v2/routes/asset/likes/like/id/[id]/openapi.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { createRoute } from "@hono/zod-openapi" -import { likeAssetByIdSchema, likeAssetByIdResponseSchema } from "./schema" -import { GenericResponses } from "@/v2/lib/response-schemas" - -export const likeAssetByIdRoute = createRoute({ - path: "/{id}", - method: "post", - description: "Like an asset from their ID.", - tags: ["Asset"], - request: { - params: likeAssetByIdSchema, - }, - responses: { - 200: { - description: "True if the asset was liked.", - content: { - "application/json": { - schema: likeAssetByIdResponseSchema, - }, - }, - }, - ...GenericResponses, - }, -}) diff --git a/src/v2/routes/asset/likes/like/id/[id]/schema.ts b/src/v2/routes/asset/likes/like/id/[id]/schema.ts deleted file mode 100644 index 20c21581..00000000 --- a/src/v2/routes/asset/likes/like/id/[id]/schema.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { z } from "@hono/zod-openapi" - -export const likeAssetByIdSchema = z.object({ - id: z.string().openapi({ - param: { - description: "The id of the asset to like.", - example: "1", - in: "path", - name: "id", - required: true, - }, - }), -}) - -export const likeAssetByIdResponseSchema = z.object({ - success: z.literal(true), -}) diff --git a/src/v2/routes/asset/likes/unlike/id/[id]/openapi.ts b/src/v2/routes/asset/likes/unlike/id/[id]/openapi.ts deleted file mode 100644 index 8073a96a..00000000 --- a/src/v2/routes/asset/likes/unlike/id/[id]/openapi.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { createRoute } from "@hono/zod-openapi" -import { unlikeAssetByIdSchema, unlikeAssetByIdResponseSchema } from "./schema" -import { GenericResponses } from "@/v2/lib/response-schemas" - -export const unlikeAssetByIdRoute = createRoute({ - path: "/{id}", - method: "post", - description: "Unlike an asset from their ID.", - tags: ["Asset"], - request: { - params: unlikeAssetByIdSchema, - }, - responses: { - 200: { - description: "True if the asset was unliked.", - content: { - "application/json": { - schema: unlikeAssetByIdResponseSchema, - }, - }, - }, - ...GenericResponses, - }, -}) diff --git a/src/v2/routes/asset/likes/unlike/id/[id]/schema.ts b/src/v2/routes/asset/likes/unlike/id/[id]/schema.ts deleted file mode 100644 index bbe6155a..00000000 --- a/src/v2/routes/asset/likes/unlike/id/[id]/schema.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { z } from "@hono/zod-openapi" - -export const unlikeAssetByIdSchema = z.object({ - id: z.string().openapi({ - param: { - description: "The id of the asset to unlike.", - example: "1", - in: "path", - name: "id", - required: true, - }, - }), -}) - -export const unlikeAssetByIdResponseSchema = z.object({ - success: z.literal(true), -}) diff --git a/src/v2/routes/asset/modify/id/[id]/route.ts b/src/v2/routes/asset/modify-asset.ts similarity index 61% rename from src/v2/routes/asset/modify/id/[id]/route.ts rename to src/v2/routes/asset/modify-asset.ts index b642ef00..f0311bfd 100644 --- a/src/v2/routes/asset/modify/id/[id]/route.ts +++ b/src/v2/routes/asset/modify-asset.ts @@ -1,10 +1,88 @@ import { OpenAPIHono } from "@hono/zod-openapi" import { getConnection } from "@/v2/db/turso" -import { modifyAssetRoute } from "./openapi" import { AuthSessionManager } from "@/v2/lib/managers/auth/user-session-manager" import { asset, assetTag, assetTagAsset } from "@/v2/db/schema" import { and, eq } from "drizzle-orm" import { SplitQueryByCommas } from "@/v2/lib/helpers/split-query-by-commas" +import { createRoute } from "@hono/zod-openapi" +import { GenericResponses } from "@/v2/lib/response-schemas" +import { z } from "@hono/zod-openapi" + +const modifyAssetPathSchema = z.object({ + id: z.string().openapi({ + param: { + description: "The id of the asset to modify.", + example: "1", + in: "path", + required: true, + }, + }), +}) + +const modifyAssetSchema = z.object({ + name: z + .string() + .min(3) + .max(32) + .openapi({ + description: "The name of the asset.", + example: "keqing-nobg.png", + }) + .optional(), + tags: z + .string() + .openapi({ + description: "Comma seperated list of tags for the asset.", + example: "official,1.0", + }) + .optional(), + assetCategoryId: z + .string() + .openapi({ + description: "The asset category ID for the asset.", + example: "splash-art", + }) + .optional(), + gameId: z + .string() + .openapi({ + description: "The game ID for the asset.", + example: "genshin-impact", + }) + .optional(), +}) + +const modifyAssetResponseSchema = z.object({ + success: z.literal(true), +}) + +const modifyAssetRoute = createRoute({ + path: "/{id}", + method: "patch", + description: "Modify an existing asset.", + tags: ["Asset"], + request: { + params: modifyAssetPathSchema, + body: { + content: { + "application/json": { + schema: modifyAssetSchema, + }, + }, + }, + }, + responses: { + 200: { + description: "Returns the asset's new attributes", + content: { + "application/json": { + schema: modifyAssetResponseSchema, + }, + }, + }, + ...GenericResponses, + }, +}) const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() diff --git a/src/v2/routes/asset/modify/id/[id]/openapi.ts b/src/v2/routes/asset/modify/id/[id]/openapi.ts deleted file mode 100644 index 24561fa0..00000000 --- a/src/v2/routes/asset/modify/id/[id]/openapi.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { createRoute } from "@hono/zod-openapi" -import { GenericResponses } from "@/v2/lib/response-schemas" -import { - modifyAssetSchema, - modifyAssetResponseSchema, - modifyAssetPathSchema, -} from "./schema" - -export const modifyAssetRoute = createRoute({ - path: "/{id}", - method: "patch", - description: "Modify an existing asset.", - tags: ["Asset"], - request: { - params: modifyAssetPathSchema, - body: { - content: { - "application/json": { - schema: modifyAssetSchema, - }, - }, - }, - }, - responses: { - 200: { - description: "Returns the asset's new attributes", - content: { - "application/json": { - schema: modifyAssetResponseSchema, - }, - }, - }, - ...GenericResponses, - }, -}) diff --git a/src/v2/routes/asset/modify/id/[id]/schema.ts b/src/v2/routes/asset/modify/id/[id]/schema.ts deleted file mode 100644 index 1bea70d4..00000000 --- a/src/v2/routes/asset/modify/id/[id]/schema.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { z } from "zod" - -export const modifyAssetPathSchema = z.object({ - id: z.string().openapi({ - param: { - description: "The id of the asset to modify.", - example: "1", - in: "path", - required: true, - }, - }), -}) - -export const modifyAssetSchema = z.object({ - name: z - .string() - .min(3) - .max(32) - .openapi({ - description: "The name of the asset.", - example: "keqing-nobg.png", - }) - .optional(), - tags: z - .string() - .openapi({ - description: "Comma seperated list of tags for the asset.", - example: "official,1.0", - }) - .optional(), - assetCategoryId: z - .string() - .openapi({ - description: "The asset category ID for the asset.", - example: "splash-art", - }) - .optional(), - gameId: z - .string() - .openapi({ - description: "The game ID for the asset.", - example: "genshin-impact", - }) - .optional(), -}) - -export const modifyAssetResponseSchema = z.object({ - success: z.literal(true), -}) diff --git a/src/v2/routes/asset/search-assets.ts b/src/v2/routes/asset/search-assets.ts new file mode 100644 index 00000000..34f9340e --- /dev/null +++ b/src/v2/routes/asset/search-assets.ts @@ -0,0 +1,162 @@ +import { OpenAPIHono } from "@hono/zod-openapi" +import { getConnection } from "@/v2/db/turso" +import { SplitQueryByCommas } from "@/v2/lib/helpers/split-query-by-commas" +import { createRoute } from "@hono/zod-openapi" +import { GenericResponses } from "@/v2/lib/response-schemas" +import { z } from "@hono/zod-openapi" +import { + selectAssetSchema, + selectAssetTagAssetSchema, + selectAssetTagSchema, +} from "@/v2/db/schema" + +export const assetSearchAllFilterResponseSchema = z.object({ + success: z.literal(true), + assets: z.array( + selectAssetSchema.extend({ + assetTagAsset: z.array( + selectAssetTagAssetSchema.extend({ + assetTag: selectAssetTagSchema, + }) + ), + }) + ), +}) + +export const assetSearchAllFilterSchema = z + .object({ + name: z.string().openapi({ + param: { + description: + "The name of the asset(s) to retrieve. Doesn't have to be exact, uses 'like' operator to search.", + name: "name", + in: "query", + example: "keqing", + required: false, + }, + }), + game: z.string().openapi({ + param: { + description: + "The game id(s) of the asset(s) to retrieve. Comma seperated.", + name: "game", + in: "query", + example: "genshin-impact,honkai-impact-3rd", + required: false, + }, + }), + category: z.string().openapi({ + param: { + description: + "The category id(s) of the asset(s) to retrieve. Comma seperated.", + name: "category", + in: "query", + example: "character-sheets,splash-art", + required: false, + }, + }), + tags: z.string().openapi({ + param: { + description: "The tag id(s) of the asset(s) to retrieve.", + name: "tags", + in: "query", + example: "official,fanmade", + required: false, + }, + }), + offset: z.string().openapi({ + param: { + description: + "The offset of the asset(s) to retrieve. For pagination / infinite scrolling.", + name: "offset", + example: "0", + in: "query", + required: false, + }, + }), + }) + .partial() + +export type assetSearchAllFilter = z.infer + +const assetSearchAllFilterRoute = createRoute({ + path: "/", + method: "get", + description: "Filter all assets", + tags: ["Asset"], + request: { + query: assetSearchAllFilterSchema, + }, + responses: { + 200: { + description: "Found assets", + content: { + "application/json": { + schema: assetSearchAllFilterResponseSchema, + }, + }, + }, + ...GenericResponses, + }, +}) + +const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() + +handler.openapi(assetSearchAllFilterRoute, async (ctx) => { + const { drizzle } = await getConnection(ctx.env) + + const { name, game, category, tags, offset } = ctx.req.valid("query") + + const gameList = game ? SplitQueryByCommas(game.toLowerCase()) : null + const categoryList = category + ? SplitQueryByCommas(category.toLowerCase()) + : null + const searchQuery = name ?? null + const tagList = tags ? SplitQueryByCommas(tags.toLowerCase()) : null + + // is this bad for performance? probably + const assets = await drizzle.query.asset.findMany({ + where: (asset, { and, or, like, eq, sql }) => + and( + tagList && tagList.length > 0 + ? or( + ...tagList.map( + (t) => + sql`EXISTS (SELECT 1 FROM assetTagAsset WHERE assetTagAsset.asset_id = ${asset.id} AND assetTagAsset.asset_tag_id = ${t})` + ) + ) + : undefined, + searchQuery ? like(asset.name, `%${searchQuery}%`) : undefined, + gameList + ? or(...gameList.map((game) => eq(asset.gameId, game))) + : undefined, + categoryList + ? or( + ...categoryList.map((category) => + eq(asset.assetCategoryId, category) + ) + ) + : undefined, + eq(asset.status, "approved") + ), + limit: 100, + offset: offset ? parseInt(offset) : 0, + with: { + assetTagAsset: { + with: { + assetTag: true, + }, + }, + }, + }) + + return ctx.json( + { + success: true, + assets, + }, + 200 + ) +}) + +export default handler diff --git a/src/v2/routes/asset/search/all/openapi.ts b/src/v2/routes/asset/search/all/openapi.ts deleted file mode 100644 index ba5cfb5f..00000000 --- a/src/v2/routes/asset/search/all/openapi.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { createRoute } from "@hono/zod-openapi" -import { - assetSearchAllFilterSchema, - assetSearchAllFilterResponseSchema, -} from "./schema" -import { GenericResponses } from "@/v2/lib/response-schemas" - -export const assetSearchAllFilterRoute = createRoute({ - path: "/", - method: "get", - description: "Filter all assets", - tags: ["Asset"], - request: { - query: assetSearchAllFilterSchema, - }, - responses: { - 200: { - description: "Found assets", - content: { - "application/json": { - schema: assetSearchAllFilterResponseSchema, - }, - }, - }, - ...GenericResponses, - }, -}) diff --git a/src/v2/routes/asset/search/all/route.ts b/src/v2/routes/asset/search/all/route.ts deleted file mode 100644 index 80dbe264..00000000 --- a/src/v2/routes/asset/search/all/route.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { OpenAPIHono } from "@hono/zod-openapi" -import { assetSearchAllFilterRoute } from "./openapi" -import { getConnection } from "@/v2/db/turso" -import { SplitQueryByCommas } from "@/v2/lib/helpers/split-query-by-commas" - -const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() - -handler.openapi(assetSearchAllFilterRoute, async (ctx) => { - const { drizzle } = await getConnection(ctx.env) - - const { name, game, category, tags, offset } = ctx.req.valid("query") - - const gameList = game ? SplitQueryByCommas(game.toLowerCase()) : null - const categoryList = category - ? SplitQueryByCommas(category.toLowerCase()) - : null - const searchQuery = name ?? null - const tagList = tags ? SplitQueryByCommas(tags.toLowerCase()) : null - - // is this bad for performance? probably - const assets = await drizzle.query.asset.findMany({ - where: (asset, { and, or, like, eq, sql }) => - and( - tagList && tagList.length > 0 - ? or( - ...tagList.map( - (t) => - sql`EXISTS (SELECT 1 FROM assetTagAsset WHERE assetTagAsset.asset_id = ${asset.id} AND assetTagAsset.asset_tag_id = ${t})` - ) - ) - : undefined, - searchQuery ? like(asset.name, `%${searchQuery}%`) : undefined, - gameList - ? or(...gameList.map((game) => eq(asset.gameId, game))) - : undefined, - categoryList - ? or( - ...categoryList.map((category) => - eq(asset.assetCategoryId, category) - ) - ) - : undefined, - eq(asset.status, "approved") - ), - limit: 100, - offset: offset ? parseInt(offset) : 0, - with: { - assetTagAsset: { - with: { - assetTag: true, - }, - }, - }, - }) - - return ctx.json( - { - success: true, - assets, - }, - 200 - ) -}) - -export default handler diff --git a/src/v2/routes/asset/search/all/schema.ts b/src/v2/routes/asset/search/all/schema.ts deleted file mode 100644 index e163fc30..00000000 --- a/src/v2/routes/asset/search/all/schema.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { z } from "@hono/zod-openapi" -import { - selectAssetSchema, - selectAssetTagAssetSchema, - selectAssetTagSchema, -} from "@/v2/db/schema" - -export const assetSearchAllFilterResponseSchema = z.object({ - success: z.literal(true), - // mmm nested schemas - assets: z.array( - selectAssetSchema.extend({ - assetTagAsset: z.array( - selectAssetTagAssetSchema.extend({ - assetTag: selectAssetTagSchema, - }) - ), - }) - ), -}) - -export const assetSearchAllFilterSchema = z - .object({ - name: z.string().openapi({ - param: { - description: - "The name of the asset(s) to retrieve. Doesn't have to be exact, uses 'like' operator to search.", - name: "name", - in: "query", - example: "keqing", - required: false, - }, - }), - game: z.string().openapi({ - param: { - description: - "The game id(s) of the asset(s) to retrieve. Comma seperated.", - name: "game", - in: "query", - example: "genshin-impact,honkai-impact-3rd", - required: false, - }, - }), - category: z.string().openapi({ - param: { - description: - "The category id(s) of the asset(s) to retrieve. Comma seperated.", - name: "category", - in: "query", - example: "character-sheets,splash-art", - required: false, - }, - }), - tags: z.string().openapi({ - param: { - description: "The tag id(s) of the asset(s) to retrieve.", - name: "tags", - in: "query", - example: "official,fanmade", - required: false, - }, - }), - offset: z.string().openapi({ - param: { - description: - "The offset of the asset(s) to retrieve. For pagination / infinite scrolling.", - name: "offset", - example: "0", - in: "query", - required: false, - }, - }), - }) - .partial() - -export type assetSearchAllFilter = z.infer diff --git a/src/v2/routes/asset/search/handler.ts b/src/v2/routes/asset/search/handler.ts deleted file mode 100644 index f3a08324..00000000 --- a/src/v2/routes/asset/search/handler.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { OpenAPIHono } from "@hono/zod-openapi" -import FilterAssetSearchAllRoute from "@/v2/routes/asset/search/all/route" - -const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() - -handler.route("/all", FilterAssetSearchAllRoute) - -export default handler diff --git a/src/v2/routes/asset/likes/unlike/id/[id]/route.ts b/src/v2/routes/asset/unlike-asset.ts similarity index 68% rename from src/v2/routes/asset/likes/unlike/id/[id]/route.ts rename to src/v2/routes/asset/unlike-asset.ts index e33f6ce6..911173d7 100644 --- a/src/v2/routes/asset/likes/unlike/id/[id]/route.ts +++ b/src/v2/routes/asset/unlike-asset.ts @@ -1,9 +1,48 @@ import { OpenAPIHono } from "@hono/zod-openapi" -import { unlikeAssetByIdRoute } from "./openapi" import { getConnection } from "@/v2/db/turso" import { AuthSessionManager } from "@/v2/lib/managers/auth/user-session-manager" import { asset, assetLikes } from "@/v2/db/schema" import { and, eq } from "drizzle-orm" +import { createRoute } from "@hono/zod-openapi" +import { GenericResponses } from "@/v2/lib/response-schemas" +import { z } from "@hono/zod-openapi" + +const unlikeAssetByIdSchema = z.object({ + id: z.string().openapi({ + param: { + description: "The id of the asset to unlike.", + example: "1", + in: "path", + name: "id", + required: true, + }, + }), +}) + +const unlikeAssetByIdResponseSchema = z.object({ + success: z.literal(true), +}) + +const unlikeAssetByIdRoute = createRoute({ + path: "/{id}", + method: "post", + description: "Unlike an asset from their ID.", + tags: ["Asset"], + request: { + params: unlikeAssetByIdSchema, + }, + responses: { + 200: { + description: "True if the asset was unliked.", + content: { + "application/json": { + schema: unlikeAssetByIdResponseSchema, + }, + }, + }, + ...GenericResponses, + }, +}) const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() diff --git a/src/v2/routes/asset/upload/route.ts b/src/v2/routes/asset/upload-asset.ts similarity index 50% rename from src/v2/routes/asset/upload/route.ts rename to src/v2/routes/asset/upload-asset.ts index 617e55ca..5aea818a 100644 --- a/src/v2/routes/asset/upload/route.ts +++ b/src/v2/routes/asset/upload-asset.ts @@ -1,9 +1,93 @@ import { OpenAPIHono } from "@hono/zod-openapi" -import { uploadAssetRoute } from "./openapi" import { AuthSessionManager } from "@/v2/lib/managers/auth/user-session-manager" import { getConnection } from "@/v2/db/turso" import { SplitQueryByCommas } from "@/v2/lib/helpers/split-query-by-commas" import { assetTagAsset } from "@/v2/db/schema" +import { createRoute } from "@hono/zod-openapi" +import { GenericResponses } from "@/v2/lib/response-schemas" +import { z } from "@hono/zod-openapi" + +const AcceptedImageType = "image/png" +const MaxFileSize = 5 * 1024 * 1024 + +const uploadAssetSchema = z.object({ + asset: z + .any() + .openapi({ + description: "The image of the asset to upload.", + example: "asset", + }) + .refine((files) => files?.length == 1, "An image is required.") + .refine( + (files) => files?.[0]?.size <= MaxFileSize, + `Max file size is 5MB)` + ) + .refine( + (files) => files?.[0]?.type === AcceptedImageType, + `Only ${AcceptedImageType} is accepted.` + ), + name: z.string().min(3).max(32).openapi({ + description: "The name of the asset.", + example: "keqing-nobg.png", + }), + tags: z + .string() + .openapi({ + description: "Comma seperated list of tags for the asset.", + example: "official,1.0", + }) + .optional(), + assetCategoryId: z.string().openapi({ + description: "The asset category ID for the asset.", + example: "splash-art", + }), + gameId: z.string().openapi({ + description: "The game ID for the asset.", + example: "genshin-impact", + }), + assetIsSuggestive: z + .string() + .min(1) + .max(1) + .openapi({ + description: "If the asset contains suggestive content 0 or 1.", + example: "1", + }) + .transform((value) => parseInt(value)) + .refine((value) => value === 1 || value === 0), +}) + +const uploadAssetResponseSchema = z.object({ + success: z.literal(true), +}) + +const uploadAssetRoute = createRoute({ + path: "/", + method: "post", + description: "Upload a new asset.", + tags: ["Asset"], + request: { + body: { + content: { + "multipart/form-data": { + schema: uploadAssetSchema, + }, + }, + }, + }, + responses: { + 200: { + description: "The uploaded asset.", + content: { + "application/json": { + schema: uploadAssetResponseSchema, + }, + }, + }, + ...GenericResponses, + }, +}) + const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() handler.openapi(uploadAssetRoute, async (ctx) => { @@ -82,7 +166,6 @@ handler.openapi(uploadAssetRoute, async (ctx) => { return ctx.json( { success: true, - asset: createdAsset[0], }, 200 ) diff --git a/src/v2/routes/asset/upload/openapi.ts b/src/v2/routes/asset/upload/openapi.ts deleted file mode 100644 index d88821c4..00000000 --- a/src/v2/routes/asset/upload/openapi.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { createRoute } from "@hono/zod-openapi" -import { uploadAssetSchema } from "./schema" - -export const uploadAssetRoute = createRoute({ - path: "/", - method: "post", - description: "Upload a new asset.", - tags: ["Asset"], - request: { - body: { - content: { - "multipart/form-data": { - schema: uploadAssetSchema, - }, - }, - }, - }, - responses: { - 200: { - description: "Returns the new asset.", - }, - 400: { - description: "Bad request.", - }, - 401: { - description: "Unauthorized.", - }, - 500: { - description: "Internal server error.", - }, - }, -}) diff --git a/src/v2/routes/asset/upload/schema.ts b/src/v2/routes/asset/upload/schema.ts deleted file mode 100644 index 6bad6a7c..00000000 --- a/src/v2/routes/asset/upload/schema.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { z } from "@hono/zod-openapi" - -const AcceptedImageType = "image/png" -const MaxFileSize = 5 * 1024 * 1024 - -export const uploadAssetSchema = z.object({ - asset: z - .any() - .openapi({ - description: "The image of the asset to upload.", - example: "asset", - }) - .refine((files) => files?.length == 1, "An image is required.") - .refine( - (files) => files?.[0]?.size <= MaxFileSize, - `Max file size is 5MB)` - ) - .refine( - (files) => files?.[0]?.type === AcceptedImageType, - `Only ${AcceptedImageType} is accepted.` - ), - name: z.string().min(3).max(32).openapi({ - description: "The name of the asset.", - example: "keqing-nobg.png", - }), - tags: z - .string() - .openapi({ - description: "Comma seperated list of tags for the asset.", - example: "official,1.0", - }) - .optional(), - assetCategoryId: z.string().openapi({ - description: "The asset category ID for the asset.", - example: "splash-art", - }), - gameId: z.string().openapi({ - description: "The game ID for the asset.", - example: "genshin-impact", - }), - assetIsSuggestive: z - .string() - .min(1) - .max(1) - .openapi({ - description: "If the asset contains suggestive content 0 or 1.", - example: "1", - }) - .transform((value) => parseInt(value)) - .refine((value) => value === 1 || value === 0), -}) diff --git a/src/v2/routes/collection/asset/handler.ts b/src/v2/routes/collection/asset/handler.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/collection/asset/remove/id/[id]/openapi.ts b/src/v2/routes/collection/asset/remove/id/[id]/openapi.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/collection/asset/remove/id/[id]/route.ts b/src/v2/routes/collection/asset/remove/id/[id]/route.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/collection/asset/remove/id/[id]/schema.ts b/src/v2/routes/collection/asset/remove/id/[id]/schema.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/collection/create/openapi.ts b/src/v2/routes/collection/create/openapi.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/collection/create/route.ts b/src/v2/routes/collection/create/route.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/collection/create/schema.ts b/src/v2/routes/collection/create/schema.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/collection/delete/id/[id]/openapi.ts b/src/v2/routes/collection/delete/id/[id]/openapi.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/collection/delete/id/[id]/route.ts b/src/v2/routes/collection/delete/id/[id]/route.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/collection/delete/id/[id]/schema.ts b/src/v2/routes/collection/delete/id/[id]/schema.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/collection/get/id/[id]/openapi.ts b/src/v2/routes/collection/get/id/[id]/openapi.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/collection/get/id/[id]/route.ts b/src/v2/routes/collection/get/id/[id]/route.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/collection/get/id/[id]/schema.ts b/src/v2/routes/collection/get/id/[id]/schema.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/collection/get/user/id/[id]/openapi.ts b/src/v2/routes/collection/get/user/id/[id]/openapi.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/collection/get/user/id/[id]/route.ts b/src/v2/routes/collection/get/user/id/[id]/route.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/collection/get/user/id/[id]/schema.ts b/src/v2/routes/collection/get/user/id/[id]/schema.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/collection/get/user/username/[username]/openapi.ts b/src/v2/routes/collection/get/user/username/[username]/openapi.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/collection/get/user/username/[username]/route.ts b/src/v2/routes/collection/get/user/username/[username]/route.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/collection/get/user/username/[username]/schema.ts b/src/v2/routes/collection/get/user/username/[username]/schema.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/collection/handler.ts b/src/v2/routes/collection/handler.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/contributors/all-contributors.ts b/src/v2/routes/contributors/all-contributors.ts new file mode 100644 index 00000000..8c6b0b3a --- /dev/null +++ b/src/v2/routes/contributors/all-contributors.ts @@ -0,0 +1,67 @@ +import { OpenAPIHono } from "@hono/zod-openapi" +import { getConnection } from "@/v2/db/turso" +import { authUser } from "@/v2/db/schema" +import { eq } from "drizzle-orm" +import { createRoute } from "@hono/zod-openapi" +import { selectUserSchema } from "@/v2/db/schema" +import { z } from "zod" + +const contributorListSchema = z.object({ + success: z.literal(true), + contributors: selectUserSchema + .pick({ + id: true, + username: true, + avatarUrl: true, + plan: true, + role: true, + }) + .array(), +}) + +const contributorsRoute = createRoute({ + path: "/", + method: "get", + description: "Get a list of all contributors.", + tags: ["Contributors"], + responses: { + 200: { + description: "All Contributors.", + content: { + "application/json": { + schema: contributorListSchema, + }, + }, + }, + 500: { + description: "Internal server error.", + }, + }, +}) + +const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() + +handler.openapi(contributorsRoute, async (ctx) => { + const { drizzle } = await getConnection(ctx.env) + + const contributors = await drizzle + .select({ + id: authUser.id, + username: authUser.username, + avatarUrl: authUser.avatarUrl, + plan: authUser.plan, + role: authUser.role, + }) + .from(authUser) + .where(eq(authUser.isContributor, true)) + + return ctx.json( + { + success: true, + contributors, + }, + 200 + ) +}) + +export default handler diff --git a/src/v2/routes/contributors/get/all/openapi.ts b/src/v2/routes/contributors/get/all/openapi.ts deleted file mode 100644 index 161aa1c6..00000000 --- a/src/v2/routes/contributors/get/all/openapi.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { createRoute } from "@hono/zod-openapi" -import { selectUserSchema } from "@/v2/db/schema" -import { z } from "zod" - -const contributorListSchema = z.object({ - success: z.literal(true), - contributors: selectUserSchema - .pick({ - id: true, - username: true, - avatarUrl: true, - plan: true, - role: true, - }) - .array(), -}) -export const contributorsRoute = createRoute({ - path: "/all", - method: "get", - description: "Get a list of all contributors.", - tags: ["Contributors"], - responses: { - 200: { - description: "All Contributors.", - content: { - "application/json": { - schema: contributorListSchema, - }, - }, - }, - 500: { - description: "Internal server error.", - }, - }, -}) diff --git a/src/v2/routes/contributors/get/all/route.ts b/src/v2/routes/contributors/get/all/route.ts deleted file mode 100644 index 3cef5713..00000000 --- a/src/v2/routes/contributors/get/all/route.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { OpenAPIHono } from "@hono/zod-openapi" -import { contributorsRoute } from "./openapi" -import { getConnection } from "@/v2/db/turso" -import { authUser } from "@/v2/db/schema" -import { eq } from "drizzle-orm" - -const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() - -handler.openapi(contributorsRoute, async (ctx) => { - const { drizzle } = await getConnection(ctx.env) - - const contributors = await drizzle - .select({ - id: authUser.id, - username: authUser.username, - avatarUrl: authUser.avatarUrl, - plan: authUser.plan, - role: authUser.role, - }) - .from(authUser) - .where(eq(authUser.isContributor, true)) - - return ctx.json( - { - success: true, - contributors, - }, - 200 - ) -}) - -export default handler diff --git a/src/v2/routes/contributors/handler.ts b/src/v2/routes/contributors/handler.ts index 88944d67..5b383683 100644 --- a/src/v2/routes/contributors/handler.ts +++ b/src/v2/routes/contributors/handler.ts @@ -1,8 +1,8 @@ import { OpenAPIHono } from "@hono/zod-openapi" -import AllContributorsRoute from "@/v2/routes/contributors/get/all/route" +import AllContributorsRoute from "./all-contributors" const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() -handler.route("/get/list", AllContributorsRoute) +handler.route("/all", AllContributorsRoute) export default handler diff --git a/src/v2/routes/favorite/asset/add/id/[id]/openapi.ts b/src/v2/routes/favorite/asset/add/id/[id]/openapi.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/favorite/asset/add/id/[id]/route.ts b/src/v2/routes/favorite/asset/add/id/[id]/route.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/favorite/asset/add/id/[id]/schema.ts b/src/v2/routes/favorite/asset/add/id/[id]/schema.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/favorite/asset/remove/id/[id]/openapi.ts b/src/v2/routes/favorite/asset/remove/id/[id]/openapi.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/favorite/asset/remove/id/[id]/route.ts b/src/v2/routes/favorite/asset/remove/id/[id]/route.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/favorite/asset/remove/id/[id]/schema.ts b/src/v2/routes/favorite/asset/remove/id/[id]/schema.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/favorite/get/user/username/[username]/openapi.ts b/src/v2/routes/favorite/get/user/username/[username]/openapi.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/favorite/get/user/username/[username]/route.ts b/src/v2/routes/favorite/get/user/username/[username]/route.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/favorite/get/user/username/[username]/schema.ts b/src/v2/routes/favorite/get/user/username/[username]/schema.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/favorite/modify/openapi.ts b/src/v2/routes/favorite/modify/openapi.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/favorite/modify/route.ts b/src/v2/routes/favorite/modify/route.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/favorite/modify/schema.ts b/src/v2/routes/favorite/modify/schema.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/favorite/route.ts b/src/v2/routes/favorite/route.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/game/all-games.ts b/src/v2/routes/game/all-games.ts new file mode 100644 index 00000000..835ed66d --- /dev/null +++ b/src/v2/routes/game/all-games.ts @@ -0,0 +1,48 @@ +import { OpenAPIHono } from "@hono/zod-openapi" +import { getConnection } from "@/v2/db/turso" +import { game } from "@/v2/db/schema" +import { GenericResponses } from "@/v2/lib/response-schemas" +import { createRoute } from "@hono/zod-openapi" +import { z } from "@hono/zod-openapi" +import { selectGameSchema } from "@/v2/db/schema" + +export const getAllGamesResponse = z.object({ + success: z.literal(true), + games: selectGameSchema.array(), +}) + +const getAllGamesRoute = createRoute({ + path: "/all", + method: "get", + description: "Get all games.", + tags: ["Game"], + responses: { + 200: { + description: "All games.", + content: { + "application/json": { + schema: getAllGamesResponse, + }, + }, + }, + ...GenericResponses, + }, +}) + +const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() + +handler.openapi(getAllGamesRoute, async (ctx) => { + const { drizzle } = await getConnection(ctx.env) + + const games = (await drizzle.select().from(game)) ?? [] + + return ctx.json( + { + success: true, + games, + }, + 200 + ) +}) + +export default handler diff --git a/src/v2/routes/game/create/route.ts b/src/v2/routes/game/create-game.ts similarity index 50% rename from src/v2/routes/game/create/route.ts rename to src/v2/routes/game/create-game.ts index 4f4671d7..77b0e6a8 100644 --- a/src/v2/routes/game/create/route.ts +++ b/src/v2/routes/game/create-game.ts @@ -1,9 +1,66 @@ import { OpenAPIHono } from "@hono/zod-openapi" -import { createGameRoute } from "./openapi" import { game } from "@/v2/db/schema" import { eq } from "drizzle-orm" import { AuthSessionManager } from "@/v2/lib/managers/auth/user-session-manager" import { getConnection } from "@/v2/db/turso" +import { createRoute } from "@hono/zod-openapi" +import { GenericResponses } from "@/v2/lib/response-schemas" +import { z } from "@hono/zod-openapi" +import { selectGameSchema } from "@/v2/db/schema" + +export const createGameSchema = z.object({ + name: z.string().min(3).max(32).openapi({ + description: "The name of the game.", + example: "honkai-star-rail", + }), + formattedName: z.string().min(3).max(64).openapi({ + description: "The formatted name of the game.", + example: "Honkai: Star Rail", + }), + possibleSuggestiveContent: z + .string() + .min(0) + .max(1) + .openapi({ + description: + "If the game contains suggestive content. 1 = Yes, 0 = No.", + example: "1", + }) + .transform((value) => parseInt(value)) + .refine((value) => value === 1 || value === 0), +}) + +export const createGameResponse = z.object({ + success: z.literal(true), + game: selectGameSchema, +}) + +const createGameRoute = createRoute({ + path: "/", + method: "post", + description: "Create a new game.", + tags: ["Game"], + request: { + body: { + content: { + "application/json": { + schema: createGameSchema, + }, + }, + }, + }, + responses: { + 200: { + description: "Returns the new game.", + content: { + "application/json": { + schema: createGameResponse, + }, + }, + }, + ...GenericResponses, + }, +}) const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() diff --git a/src/v2/routes/game/create/openapi.ts b/src/v2/routes/game/create/openapi.ts deleted file mode 100644 index b6d5cc40..00000000 --- a/src/v2/routes/game/create/openapi.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { createRoute } from "@hono/zod-openapi" -import { createGameSchema, createGameResponse } from "./schema" -import { GenericResponses } from "@/v2/lib/response-schemas" - -export const createGameRoute = createRoute({ - path: "/", - method: "post", - description: "Create a new game.", - tags: ["Game"], - request: { - body: { - content: { - "application/json": { - schema: createGameSchema, - }, - }, - }, - }, - responses: { - 200: { - description: "Returns the new game.", - content: { - "application/json": { - schema: createGameResponse, - }, - }, - }, - ...GenericResponses, - }, -}) diff --git a/src/v2/routes/game/create/schema.ts b/src/v2/routes/game/create/schema.ts deleted file mode 100644 index 530710d6..00000000 --- a/src/v2/routes/game/create/schema.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { z } from "@hono/zod-openapi" -import { selectGameSchema } from "@/v2/db/schema" - -export const createGameSchema = z.object({ - name: z.string().min(3).max(32).openapi({ - description: "The name of the game.", - example: "honkai-star-rail", - }), - formattedName: z.string().min(3).max(64).openapi({ - description: "The formatted name of the game.", - example: "Honkai: Star Rail", - }), - possibleSuggestiveContent: z - .string() - .min(0) - .max(1) - .openapi({ - description: - "If the game contains suggestive content. 1 = Yes, 0 = No.", - example: "1", - }) - .transform((value) => parseInt(value)) - .refine((value) => value === 1 || value === 0), -}) - -export const createGameResponse = z.object({ - success: z.literal(true), - game: selectGameSchema, -}) diff --git a/src/v2/routes/game/delete/id/[id]/route.ts b/src/v2/routes/game/delete-game.ts similarity index 59% rename from src/v2/routes/game/delete/id/[id]/route.ts rename to src/v2/routes/game/delete-game.ts index 47f96676..ae4e97e8 100644 --- a/src/v2/routes/game/delete/id/[id]/route.ts +++ b/src/v2/routes/game/delete-game.ts @@ -1,9 +1,48 @@ import { OpenAPIHono } from "@hono/zod-openapi" -import { deleteGameRoute } from "./openapi" import { getConnection } from "@/v2/db/turso" import { AuthSessionManager } from "@/v2/lib/managers/auth/user-session-manager" import { game } from "@/v2/db/schema" import { eq } from "drizzle-orm" +import { createRoute } from "@hono/zod-openapi" +import { GenericResponses } from "@/v2/lib/response-schemas" +import { z } from "@hono/zod-openapi" + +export const deleteGameSchema = z.object({ + id: z.string().openapi({ + param: { + name: "id", + in: "path", + description: "The ID of the game to delete.", + example: "genshin-impact", + required: true, + }, + }), +}) + +export const deleteGameResponse = z.object({ + success: z.literal(true), +}) + +const deleteGameRoute = createRoute({ + path: "/{id}", + method: "delete", + description: "Delete a game & all its related assets.", + tags: ["Game"], + request: { + params: deleteGameSchema, + }, + responses: { + 200: { + description: "Returns boolean indicating success.", + content: { + "application/json": { + schema: deleteGameResponse, + }, + }, + }, + ...GenericResponses, + }, +}) const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() diff --git a/src/v2/routes/game/delete/id/[id]/openapi.ts b/src/v2/routes/game/delete/id/[id]/openapi.ts deleted file mode 100644 index b595ac15..00000000 --- a/src/v2/routes/game/delete/id/[id]/openapi.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { createRoute } from "@hono/zod-openapi" -import { deleteGameSchema, deleteGameResponse } from "./schema" -import { GenericResponses } from "@/v2/lib/response-schemas" - -export const deleteGameRoute = createRoute({ - path: "/{id}", - method: "delete", - description: "Delete a game & all its related assets.", - tags: ["Game"], - request: { - params: deleteGameSchema, - }, - responses: { - 200: { - description: "Returns boolean indicating success.", - content: { - "application/json": { - schema: deleteGameResponse, - }, - }, - }, - ...GenericResponses, - }, -}) diff --git a/src/v2/routes/game/delete/id/[id]/schema.ts b/src/v2/routes/game/delete/id/[id]/schema.ts deleted file mode 100644 index 5c189923..00000000 --- a/src/v2/routes/game/delete/id/[id]/schema.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { z } from "@hono/zod-openapi" - -export const deleteGameSchema = z.object({ - id: z.string().openapi({ - param: { - name: "id", - in: "path", - description: "The ID of the game to delete.", - example: "genshin-impact", - required: true, - }, - }), -}) - -export const deleteGameResponse = z.object({ - success: z.literal(true), -}) diff --git a/src/v2/routes/game/get-game.ts b/src/v2/routes/game/get-game.ts new file mode 100644 index 00000000..2e5c15e2 --- /dev/null +++ b/src/v2/routes/game/get-game.ts @@ -0,0 +1,75 @@ +import { OpenAPIHono, createRoute } from "@hono/zod-openapi" +import { game } from "@/v2/db/schema" +import { getConnection } from "@/v2/db/turso" +import { eq } from "drizzle-orm" +import { z } from "@hono/zod-openapi" +import { selectGameSchema } from "@/v2/db/schema" +import { GenericResponses } from "@/v2/lib/response-schemas" + +const getGameByIdSchema = z.object({ + id: z.string().openapi({ + param: { + name: "id", + in: "path", + description: "The ID of the game to retrieve.", + example: "genshin-impact", + required: true, + }, + }), +}) + +const getGameByIDResponse = z.object({ + success: z.literal(true), + game: selectGameSchema, +}) + +const getGameByIdRoute = createRoute({ + path: "/{id}", + method: "get", + description: "Get a game by their ID.", + tags: ["Game"], + request: { + params: getGameByIdSchema, + }, + responses: { + 200: { + description: "Game was found.", + content: { + "application/json": { + schema: getGameByIDResponse, + }, + }, + }, + ...GenericResponses, + }, +}) + +const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() + +handler.openapi(getGameByIdRoute, async (ctx) => { + const id = ctx.req.valid("param").id + + const { drizzle } = await getConnection(ctx.env) + + const [foundGame] = await drizzle.select().from(game).where(eq(game.id, id)) + + if (!foundGame) { + return ctx.json( + { + success: false, + message: "Game not found", + }, + 400 + ) + } + + return ctx.json( + { + success: true, + game: foundGame, + }, + 200 + ) +}) + +export default handler diff --git a/src/v2/routes/game/get/all/openapi.ts b/src/v2/routes/game/get/all/openapi.ts deleted file mode 100644 index 6a2d6af1..00000000 --- a/src/v2/routes/game/get/all/openapi.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { GenericResponses } from "@/v2/lib/response-schemas" -import { createRoute } from "@hono/zod-openapi" -import { getAllGamesResponse } from "./schema" - -export const getAllGamesRoute = createRoute({ - path: "/all", - method: "get", - description: "Get all games.", - tags: ["Game"], - responses: { - 200: { - description: "All games.", - content: { - "application/json": { - schema: getAllGamesResponse, - }, - }, - }, - ...GenericResponses, - }, -}) diff --git a/src/v2/routes/game/get/all/route.ts b/src/v2/routes/game/get/all/route.ts deleted file mode 100644 index 88fbeedd..00000000 --- a/src/v2/routes/game/get/all/route.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { OpenAPIHono } from "@hono/zod-openapi" -import { getAllGamesRoute } from "./openapi" -import { getConnection } from "@/v2/db/turso" -import { game } from "@/v2/db/schema" - -const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() - -handler.openapi(getAllGamesRoute, async (ctx) => { - const { drizzle } = await getConnection(ctx.env) - - const games = (await drizzle.select().from(game)) ?? [] - - return ctx.json( - { - success: true, - games, - }, - 200 - ) -}) - -export default handler diff --git a/src/v2/routes/game/get/all/schema.ts b/src/v2/routes/game/get/all/schema.ts deleted file mode 100644 index 2aa38409..00000000 --- a/src/v2/routes/game/get/all/schema.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { z } from "@hono/zod-openapi" -import { selectGameSchema } from "@/v2/db/schema" - -export const getAllGamesResponse = z.object({ - success: z.literal(true), - games: selectGameSchema.array(), -}) diff --git a/src/v2/routes/game/get/handler.ts b/src/v2/routes/game/get/handler.ts deleted file mode 100644 index a6be1d7c..00000000 --- a/src/v2/routes/game/get/handler.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { OpenAPIHono } from "@hono/zod-openapi" -import GetGameByNameRoute from "./name/[name]/route" -import GetGameByIdRoute from "./id/[id]/route" -import GetAllGamesRoute from "./all/route" - -const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() - -handler.route("/name", GetGameByNameRoute) -handler.route("/id", GetGameByIdRoute) -handler.route("/list", GetAllGamesRoute) - -export default handler diff --git a/src/v2/routes/game/get/id/[id]/openapi.ts b/src/v2/routes/game/get/id/[id]/openapi.ts deleted file mode 100644 index 1b310476..00000000 --- a/src/v2/routes/game/get/id/[id]/openapi.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { createRoute } from "@hono/zod-openapi" -import { getGameByIdSchema, getGameByIDResponse } from "./schema" -import { GenericResponses } from "@/v2/lib/response-schemas" - -export const getGameByIdRoute = createRoute({ - path: "/{id}", - method: "get", - description: "Get a game by their ID.", - tags: ["Game"], - request: { - params: getGameByIdSchema, - }, - responses: { - 200: { - description: "Game was found.", - content: { - "application/json": { - schema: getGameByIDResponse, - }, - }, - }, - ...GenericResponses, - }, -}) diff --git a/src/v2/routes/game/get/id/[id]/route.ts b/src/v2/routes/game/get/id/[id]/route.ts deleted file mode 100644 index 0bacc6d9..00000000 --- a/src/v2/routes/game/get/id/[id]/route.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { OpenAPIHono } from "@hono/zod-openapi" -import { getGameByIdRoute } from "./openapi" -import { game } from "@/v2/db/schema" -import { getConnection } from "@/v2/db/turso" -import { eq } from "drizzle-orm" - -const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() - -handler.openapi(getGameByIdRoute, async (ctx) => { - const id = ctx.req.valid("param").id - - const { drizzle } = await getConnection(ctx.env) - - const [foundGame] = await drizzle.select().from(game).where(eq(game.id, id)) - - if (!foundGame) { - return ctx.json( - { - success: false, - message: "Game not found", - }, - 400 - ) - } - - return ctx.json( - { - success: true, - game: foundGame, - }, - 200 - ) -}) - -export default handler diff --git a/src/v2/routes/game/get/id/[id]/schema.ts b/src/v2/routes/game/get/id/[id]/schema.ts deleted file mode 100644 index 507dae66..00000000 --- a/src/v2/routes/game/get/id/[id]/schema.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { z } from "@hono/zod-openapi" -import { selectGameSchema } from "@/v2/db/schema" - -export const getGameByIdSchema = z.object({ - id: z.string().openapi({ - param: { - name: "id", - in: "path", - description: "The ID of the game to retrieve.", - example: "genshin-impact", - required: true, - }, - }), -}) - -export const getGameByIDResponse = z.object({ - success: z.literal(true), - game: selectGameSchema, -}) diff --git a/src/v2/routes/game/get/name/[name]/openapi.ts b/src/v2/routes/game/get/name/[name]/openapi.ts deleted file mode 100644 index 98a2f585..00000000 --- a/src/v2/routes/game/get/name/[name]/openapi.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { createRoute } from "@hono/zod-openapi" -import { getGameByNameSchema, getGameByNameResponse } from "./schema" -import { GenericResponses } from "@/v2/lib/response-schemas" - -export const getGameByNameRoute = createRoute({ - path: "/{name}", - method: "get", - description: "Search for games by their name.", - tags: ["Game"], - request: { - params: getGameByNameSchema, - }, - responses: { - 200: { - description: "Game was found.", - content: { - "application/json": { - schema: getGameByNameResponse, - }, - }, - }, - ...GenericResponses, - }, -}) diff --git a/src/v2/routes/game/get/name/[name]/route.ts b/src/v2/routes/game/get/name/[name]/route.ts deleted file mode 100644 index b8681fba..00000000 --- a/src/v2/routes/game/get/name/[name]/route.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { OpenAPIHono } from "@hono/zod-openapi" -import { getGameByNameRoute } from "./openapi" -import { game } from "@/v2/db/schema" -import { eq } from "drizzle-orm" -import { getConnection } from "@/v2/db/turso" - -const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() - -handler.openapi(getGameByNameRoute, async (ctx) => { - const name = ctx.req.valid("param").name - - const { drizzle } = await getConnection(ctx.env) - - const [foundGame] = await drizzle - .select() - .from(game) - .where(eq(game.name, name)) - - if (!foundGame) { - return ctx.json( - { - success: true, - message: "Game not found", - }, - 400 - ) - } - - return ctx.json( - { - success: true, - game: foundGame, - }, - 200 - ) -}) - -export default handler diff --git a/src/v2/routes/game/get/name/[name]/schema.ts b/src/v2/routes/game/get/name/[name]/schema.ts deleted file mode 100644 index 9a9430e8..00000000 --- a/src/v2/routes/game/get/name/[name]/schema.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { z } from "@hono/zod-openapi" -import { selectGameSchema } from "@/v2/db/schema" - -export const getGameByNameSchema = z.object({ - name: z.string().openapi({ - param: { - name: "name", - in: "path", - required: true, - description: "The name of the game to retrieve.", - example: "genshin-impact", - }, - }), -}) - -export const getGameByNameResponse = z.object({ - success: z.literal(true), - game: selectGameSchema, -}) diff --git a/src/v2/routes/game/handler.ts b/src/v2/routes/game/handler.ts index 9c8fc173..dac3175f 100644 --- a/src/v2/routes/game/handler.ts +++ b/src/v2/routes/game/handler.ts @@ -1,14 +1,18 @@ import { OpenAPIHono } from "@hono/zod-openapi" -import GameGetRoute from "./get/handler" -import GameCreateRoute from "./create/route" -import DeleteGameRoute from "./delete/id/[id]/route" -import ModifyGameRoute from "./modify/id/[id]/route" +import GameCreateRoute from "./create-game" +import DeleteGameRoute from "./delete-game" +import ModifyGameRoute from "./modify-game" +import GameGetRoute from "./get-game" +import AllGamesRoute from "./all-games" const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() handler.route("/get", GameGetRoute) +handler.route("/modify", ModifyGameRoute) +handler.route("/delete", DeleteGameRoute) + handler.route("/create", GameCreateRoute) -handler.route("/modify/id", ModifyGameRoute) -handler.route("/delete/id", DeleteGameRoute) + +handler.route("/list", AllGamesRoute) export default handler diff --git a/src/v2/routes/game/modify-game.ts b/src/v2/routes/game/modify-game.ts new file mode 100644 index 00000000..88048ab2 --- /dev/null +++ b/src/v2/routes/game/modify-game.ts @@ -0,0 +1,136 @@ +import { OpenAPIHono } from "@hono/zod-openapi" +import { eq } from "drizzle-orm" +import { AuthSessionManager } from "@/v2/lib/managers/auth/user-session-manager" +import { getConnection } from "@/v2/db/turso" +import { game } from "@/v2/db/schema" +import { createRoute } from "@hono/zod-openapi" +import { GenericResponses } from "@/v2/lib/response-schemas" +import { z } from "@hono/zod-openapi" +import { selectGameSchema } from "@/v2/db/schema" + +export const modifyGamePathSchema = z.object({ + id: z.string().openapi({ + param: { + name: "id", + description: "The id of the game to modify.", + example: "honkai-star-rail", + in: "path", + required: true, + }, + }), +}) + +const modifyGameSchema = z.object({ + name: z.string().min(3).max(32).openapi({ + description: "The new name of the game.", + example: "honkai-star-rail", + }), + formattedName: z.string().min(3).max(64).openapi({ + description: "The new formatted name of the game.", + example: "Honkai: Star Rail", + }), + possibleSuggestiveContent: z + .string() + .min(0) + .max(1) + .openapi({ + description: + "If the game contains suggestive content. 1 = Yes, 0 = No.", + example: "1", + }) + .transform((value) => parseInt(value)) + .refine((value) => value === 1 || value === 0), +}) + +const modifyGameResponseSchema = z.object({ + success: z.literal(true), + game: selectGameSchema, +}) + +export const modifyGameRoute = createRoute({ + path: "/{id}", + method: "patch", + description: "Modify an existing game.", + tags: ["Game"], + request: { + params: modifyGamePathSchema, + body: { + content: { + "application/json": { + schema: modifyGameSchema, + }, + }, + }, + }, + responses: { + 200: { + description: "Returns the game's attributes", + content: { + "application/json": { + schema: modifyGameResponseSchema, + }, + }, + }, + ...GenericResponses, + }, +}) + +const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() + +handler.openapi(modifyGameRoute, async (ctx) => { + const authSessionManager = new AuthSessionManager(ctx) + + const { user } = await authSessionManager.validateSession() + + if (!user || user.role != "creator") { + return ctx.json( + { + success: false, + message: "Unauthorized", + }, + 401 + ) + } + + const { name, formattedName, possibleSuggestiveContent } = + ctx.req.valid("json") + const { id } = ctx.req.valid("param") + + const { drizzle } = getConnection(ctx.env) + + const [existingGame] = await drizzle + .select({ id: game.id }) + .from(game) + .where(eq(game.id, id)) + + if (!existingGame.id) { + return ctx.json( + { + success: false, + message: "Game with ID not found", + }, + 400 + ) + } + + const [updatedGame] = await drizzle + .update(game) + .set({ + name, + formattedName, + possibleSuggestiveContent: Boolean(possibleSuggestiveContent), + lastUpdated: new Date().toISOString(), + }) + .where(eq(game.id, id)) + .returning() + + return ctx.json( + { + success: true, + game: updatedGame, + }, + 200 + ) +}) + +export default handler diff --git a/src/v2/routes/game/modify/id/[id]/openapi.ts b/src/v2/routes/game/modify/id/[id]/openapi.ts deleted file mode 100644 index cd78355a..00000000 --- a/src/v2/routes/game/modify/id/[id]/openapi.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { createRoute } from "@hono/zod-openapi" -import { - modifyGameSchema, - modifyGameResponseSchema, - modifyGamePathSchema, -} from "./schema" -import { GenericResponses } from "@/v2/lib/response-schemas" - -export const modifyGameRoute = createRoute({ - path: "/{id}", - method: "patch", - description: "Modify an existing game.", - tags: ["Game"], - request: { - params: modifyGamePathSchema, - body: { - content: { - "application/json": { - schema: modifyGameSchema, - }, - }, - }, - }, - responses: { - 200: { - description: "Returns the game's attributes", - content: { - "application/json": { - schema: modifyGameResponseSchema, - }, - }, - }, - ...GenericResponses, - }, -}) diff --git a/src/v2/routes/game/modify/id/[id]/route.ts b/src/v2/routes/game/modify/id/[id]/route.ts deleted file mode 100644 index aaf79045..00000000 --- a/src/v2/routes/game/modify/id/[id]/route.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { OpenAPIHono } from "@hono/zod-openapi" -import { modifyGameRoute } from "./openapi" -import { eq } from "drizzle-orm" -import { AuthSessionManager } from "@/v2/lib/managers/auth/user-session-manager" -import { getConnection } from "@/v2/db/turso" -import { game } from "@/v2/db/schema" - -const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() - -handler.openapi(modifyGameRoute, async (ctx) => { - const authSessionManager = new AuthSessionManager(ctx) - - const { user } = await authSessionManager.validateSession() - - if (!user || user.role != "creator") { - return ctx.json( - { - success: false, - message: "Unauthorized", - }, - 401 - ) - } - - const { name, formattedName, possibleSuggestiveContent } = - ctx.req.valid("json") - const { id } = ctx.req.valid("param") - - const { drizzle } = getConnection(ctx.env) - - const [existingGame] = await drizzle - .select({ id: game.id }) - .from(game) - .where(eq(game.id, id)) - - if (!existingGame.id) { - return ctx.json( - { - success: false, - message: "Game with ID not found", - }, - 400 - ) - } - - const [updatedGame] = await drizzle - .update(game) - .set({ - name, - formattedName, - possibleSuggestiveContent: Boolean(possibleSuggestiveContent), - lastUpdated: new Date().toISOString(), - }) - .where(eq(game.id, id)) - .returning() - - return ctx.json( - { - success: true, - game: updatedGame, - }, - 200 - ) -}) - -export default handler diff --git a/src/v2/routes/game/modify/id/[id]/schema.ts b/src/v2/routes/game/modify/id/[id]/schema.ts deleted file mode 100644 index c9363471..00000000 --- a/src/v2/routes/game/modify/id/[id]/schema.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { z } from "@hono/zod-openapi" -import { selectGameSchema } from "@/v2/db/schema" - -export const modifyGamePathSchema = z.object({ - id: z.string().openapi({ - param: { - name: "id", - description: "The id of the game to modify.", - example: "honkai-star-rail", - in: "path", - required: true, - }, - }), -}) - -export const modifyGameSchema = z.object({ - name: z.string().min(3).max(32).openapi({ - description: "The new name of the game.", - example: "honkai-star-rail", - }), - formattedName: z.string().min(3).max(64).openapi({ - description: "The new formatted name of the game.", - example: "Honkai: Star Rail", - }), - possibleSuggestiveContent: z - .string() - .min(0) - .max(1) - .openapi({ - description: - "If the game contains suggestive content. 1 = Yes, 0 = No.", - example: "1", - }) - .transform((value) => parseInt(value)) - .refine((value) => value === 1 || value === 0), -}) - -export const modifyGameResponseSchema = z.object({ - success: z.literal(true), - game: selectGameSchema, -}) diff --git a/src/v2/routes/handler.ts b/src/v2/routes/handler.ts index 6c594ebc..632f6ec7 100644 --- a/src/v2/routes/handler.ts +++ b/src/v2/routes/handler.ts @@ -4,17 +4,15 @@ import GameRoute from "@/v2/routes/game/handler" import AssetRoute from "@/v2/routes/asset/handler" import ContributorRoute from "@/v2/routes/contributors/handler" import AuthRoute from "@/v2/routes/auth/handler" -// import CategoryRoute from "@/v2/routes/category/handler" -// import RequestFormRoute from "@/v2/routes/requests/handler" dromzeh: need to figure out financial/supporter side of things before I can implement this +import RequestFormRoute from "@/v2/routes/requests/handler" const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() handler.route("/game", GameRoute) -// handler.route("/category", CategoryRoute) handler.route("/asset", AssetRoute) handler.route("/user", UserRoute) -handler.route("/contributor", ContributorRoute) +handler.route("/contributors", ContributorRoute) handler.route("/auth", AuthRoute) -// handler.route("/request", RequestFormRoute) +handler.route("/request", RequestFormRoute) export default handler diff --git a/src/v2/routes/requests/all-requests.ts b/src/v2/routes/requests/all-requests.ts new file mode 100644 index 00000000..4bd77200 --- /dev/null +++ b/src/v2/routes/requests/all-requests.ts @@ -0,0 +1,78 @@ +import { OpenAPIHono } from "@hono/zod-openapi" +import { RequestFormManager } from "@/v2/lib/managers/request-form/request-form-manager" +import { getConnection } from "@/v2/db/turso" +import { createRoute } from "@hono/zod-openapi" +import { GenericResponses } from "@/v2/lib/response-schemas" +import { z } from "@hono/zod-openapi" +import { selectRequestFormSchema } from "@/v2/db/schema" + +const viewAllRequestsSchema = z + .object({ + offset: z.string().openapi({ + param: { + description: + "The offset of requests to return. This is used for pagination.", + name: "offset", + example: "0", + in: "query", + required: false, + }, + }), + }) + .partial() + +const requestFormSchema = z.object({ + ...selectRequestFormSchema.shape, + upvotesCount: z.number().optional(), +}) + +const viewAllRequestsResponseSchema = z.object({ + success: z.literal(true), + requests: z.array(requestFormSchema), +}) + +const getAllRequestsRoute = createRoute({ + path: "/", + method: "get", + description: + "Get all requests. This will also return all associated upvotes count.", + tags: ["Requests"], + request: { + query: viewAllRequestsSchema, + }, + responses: { + 200: { + description: "List of all submitted requests.", + content: { + "application/json": { + schema: viewAllRequestsResponseSchema, + }, + }, + }, + ...GenericResponses, + }, +}) + +const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() + +handler.openapi(getAllRequestsRoute, async (ctx) => { + const { offset } = ctx.req.valid("query") ?? { offset: "0" } + + const { drizzle } = await getConnection(ctx.env) + + const requestFormManager = new RequestFormManager(drizzle) + + const allRequests = await requestFormManager.getRequestFormEntries( + parseInt(offset) + ) + + return ctx.json( + { + success: true, + requests: allRequests, + }, + 200 + ) +}) + +export default handler diff --git a/src/v2/routes/requests/create-request.ts b/src/v2/routes/requests/create-request.ts new file mode 100644 index 00000000..3df71cd5 --- /dev/null +++ b/src/v2/routes/requests/create-request.ts @@ -0,0 +1,104 @@ +import { OpenAPIHono } from "@hono/zod-openapi" +import { AuthSessionManager } from "@/v2/lib/managers/auth/user-session-manager" +import { RequestFormManager } from "@/v2/lib/managers/request-form/request-form-manager" +import { getConnection } from "@/v2/db/turso" +import { createRoute } from "@hono/zod-openapi" +import { GenericResponses } from "@/v2/lib/response-schemas" +import { z } from "@hono/zod-openapi" +import { selectRequestFormSchema } from "@/v2/db/schema" +import type { requestArea } from "@/v2/db/schema" + +const createRequestFormEntrySchema = z.object({ + title: z.string().min(3).max(32).openapi({ + description: "The title of the request.", + example: "Add HSR UI assets", + }), + area: z + .string() + .min(3) + .max(32) + .openapi({ + description: "The area of the request.", + example: "asset", + }) + .transform((value) => value as requestArea), + description: z.string().min(3).max(256).openapi({ + description: "The description of the request.", + example: + "Add the UI assets for Honkai: Star Rail, including the logo and other UI elements.", + }), +}) + +const createRequestFormEntryResponse = z.object({ + success: z.literal(true), + response: selectRequestFormSchema, +}) + +const createRequestFormEntryRoute = createRoute({ + path: "/", + method: "post", + description: "Create a new entry into the request form.", + tags: ["Requests"], + request: { + body: { + content: { + "application/json": { + schema: createRequestFormEntrySchema, + }, + }, + }, + }, + responses: { + 200: { + description: "Returns the new request form entry.", + content: { + "application/json": { + schema: createRequestFormEntryResponse, + }, + }, + }, + ...GenericResponses, + }, +}) + +const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() + +handler.openapi(createRequestFormEntryRoute, async (ctx) => { + const { area, title, description } = ctx.req.valid("json") + + const authSessionManager = new AuthSessionManager(ctx) + + const { user } = await authSessionManager.validateSession() + + if (!user || user.role != "creator" || user.plan == "supporter") { + return ctx.json( + { + success: false, + message: + "Unauthorized. Only supporters can create request entries.", + }, + 401 + ) + } + + const { drizzle } = await getConnection(ctx.env) + + const requestFormManager = new RequestFormManager(drizzle) + + const [newRequestEntry] = await requestFormManager.createRequestFormEntry( + user.id, + title, + area, + description + ) + + return ctx.json( + { + success: true, + response: newRequestEntry, + }, + 200 + ) +}) + +export default handler diff --git a/src/v2/routes/requests/form/delete/[id]/route.ts b/src/v2/routes/requests/delete-request.ts similarity index 59% rename from src/v2/routes/requests/form/delete/[id]/route.ts rename to src/v2/routes/requests/delete-request.ts index fcd291bc..d7a89798 100644 --- a/src/v2/routes/requests/form/delete/[id]/route.ts +++ b/src/v2/routes/requests/delete-request.ts @@ -1,8 +1,48 @@ import { OpenAPIHono } from "@hono/zod-openapi" -import { deleteRequestByIdRoute } from "./openapi" import { AuthSessionManager } from "@/v2/lib/managers/auth/user-session-manager" import { RequestFormManager } from "@/v2/lib/managers/request-form/request-form-manager" import { getConnection } from "@/v2/db/turso" +import { createRoute } from "@hono/zod-openapi" +import { GenericResponses } from "@/v2/lib/response-schemas" +import { z } from "@hono/zod-openapi" + +export const deleteRequestByIdSchema = z.object({ + id: z.string().openapi({ + param: { + name: "id", + in: "path", + description: "The ID of the request to delete.", + example: "1", + required: true, + }, + }), +}) + +export const deleteRequestByIdResponseSchema = z.object({ + success: z.literal(true), +}) + +const deleteRequestByIdRoute = createRoute({ + path: "/{id}", + method: "delete", + description: + "Delete a request by its ID. This will also delete all associated upvotes.", + tags: ["Requests"], + request: { + params: deleteRequestByIdSchema, + }, + responses: { + 200: { + description: "True if the request was deleted successfully.", + content: { + "application/json": { + schema: deleteRequestByIdResponseSchema, + }, + }, + }, + ...GenericResponses, + }, +}) const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() diff --git a/src/v2/routes/requests/form/create/openapi.ts b/src/v2/routes/requests/form/create/openapi.ts deleted file mode 100644 index 711bdcb4..00000000 --- a/src/v2/routes/requests/form/create/openapi.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { createRoute } from "@hono/zod-openapi" -import { - createRequestFormEntryResponse, - createRequestFormEntrySchema, -} from "./schema" -import { GenericResponses } from "@/v2/lib/response-schemas" - -export const createRequestFormEntryRoute = createRoute({ - path: "/", - method: "post", - description: "Create a new entry into the request form.", - tags: ["Requests"], - request: { - body: { - content: { - "application/json": { - schema: createRequestFormEntrySchema, - }, - }, - }, - }, - responses: { - 200: { - description: "Returns the new request form entry.", - content: { - "application/json": { - schema: createRequestFormEntryResponse, - }, - }, - }, - ...GenericResponses, - }, -}) diff --git a/src/v2/routes/requests/form/create/route.ts b/src/v2/routes/requests/form/create/route.ts deleted file mode 100644 index adf7ed4e..00000000 --- a/src/v2/routes/requests/form/create/route.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { OpenAPIHono } from "@hono/zod-openapi" -import { createRequestFormEntryRoute } from "./openapi" -import { AuthSessionManager } from "@/v2/lib/managers/auth/user-session-manager" -import { RequestFormManager } from "@/v2/lib/managers/request-form/request-form-manager" -import { getConnection } from "@/v2/db/turso" - -const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() - -handler.openapi(createRequestFormEntryRoute, async (ctx) => { - const { area, title, description } = ctx.req.valid("json") - - const authSessionManager = new AuthSessionManager(ctx) - - const { user } = await authSessionManager.validateSession() - - if (!user || user.role != "creator" || user.plan == "supporter") { - return ctx.json( - { - success: false, - message: - "Unauthorized. Only supporters can create request entries.", - }, - 401 - ) - } - - const { drizzle } = await getConnection(ctx.env) - - const requestFormManager = new RequestFormManager(drizzle) - - const [newRequestEntry] = await requestFormManager.createRequestFormEntry( - user.id, - title, - area, - description - ) - - return ctx.json( - { - success: true, - response: newRequestEntry, - }, - 200 - ) -}) - -export default handler diff --git a/src/v2/routes/requests/form/create/schema.ts b/src/v2/routes/requests/form/create/schema.ts deleted file mode 100644 index 37d72d41..00000000 --- a/src/v2/routes/requests/form/create/schema.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { z } from "@hono/zod-openapi" -import { selectRequestFormSchema } from "@/v2/db/schema" -import type { requestArea } from "@/v2/db/schema" - -export const createRequestFormEntrySchema = z.object({ - title: z.string().min(3).max(32).openapi({ - description: "The title of the request.", - example: "Add HSR UI assets", - }), - area: z - .string() - .min(3) - .max(32) - .openapi({ - description: "The area of the request.", - example: "asset", - }) - .transform((value) => value as requestArea), - description: z.string().min(3).max(256).openapi({ - description: "The description of the request.", - example: - "Add the UI assets for Honkai: Star Rail, including the logo and other UI elements.", - }), -}) - -export const createRequestFormEntryResponse = z.object({ - success: z.literal(true), - response: selectRequestFormSchema, -}) diff --git a/src/v2/routes/requests/form/delete/[id]/openapi.ts b/src/v2/routes/requests/form/delete/[id]/openapi.ts deleted file mode 100644 index 3bfc1a73..00000000 --- a/src/v2/routes/requests/form/delete/[id]/openapi.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { createRoute } from "@hono/zod-openapi" -import { - deleteRequestByIdResponseSchema, - deleteRequestByIdSchema, -} from "./schema" -import { GenericResponses } from "@/v2/lib/response-schemas" - -export const deleteRequestByIdRoute = createRoute({ - path: "/{id}", - method: "delete", - description: - "Delete a request by its ID. This will also delete all associated upvotes.", - tags: ["Requests"], - request: { - params: deleteRequestByIdSchema, - }, - responses: { - 200: { - description: "True if the request was deleted successfully.", - content: { - "application/json": { - schema: deleteRequestByIdResponseSchema, - }, - }, - }, - ...GenericResponses, - }, -}) diff --git a/src/v2/routes/requests/form/delete/[id]/schema.ts b/src/v2/routes/requests/form/delete/[id]/schema.ts deleted file mode 100644 index 3bbd8594..00000000 --- a/src/v2/routes/requests/form/delete/[id]/schema.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { z } from "@hono/zod-openapi" - -export const deleteRequestByIdSchema = z.object({ - id: z.string().openapi({ - param: { - name: "id", - in: "path", - description: "The ID of the request to delete.", - example: "1", - required: true, - }, - }), -}) - -export const deleteRequestByIdResponseSchema = z.object({ - success: z.literal(true), -}) diff --git a/src/v2/routes/requests/form/upvote/[id]/openapi.ts b/src/v2/routes/requests/form/upvote/[id]/openapi.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/requests/form/upvote/[id]/route.ts b/src/v2/routes/requests/form/upvote/[id]/route.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/requests/form/upvote/[id]/schema.ts b/src/v2/routes/requests/form/upvote/[id]/schema.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/requests/form/view/all/openapi.ts b/src/v2/routes/requests/form/view/all/openapi.ts deleted file mode 100644 index 5af4de48..00000000 --- a/src/v2/routes/requests/form/view/all/openapi.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { createRoute } from "@hono/zod-openapi" -import { viewAllRequestsResponseSchema, viewAllRequestsSchema } from "./schema" -import { GenericResponses } from "@/v2/lib/response-schemas" - -export const getAllRequestsRoute = createRoute({ - path: "/", - method: "get", - description: - "Get all requests. This will also return all associated upvotes count.", - tags: ["Requests"], - request: { - query: viewAllRequestsSchema, - }, - responses: { - 200: { - description: "List of all submitted requests.", - content: { - "application/json": { - schema: viewAllRequestsResponseSchema, - }, - }, - }, - ...GenericResponses, - }, -}) diff --git a/src/v2/routes/requests/form/view/all/route.ts b/src/v2/routes/requests/form/view/all/route.ts deleted file mode 100644 index fefba538..00000000 --- a/src/v2/routes/requests/form/view/all/route.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { OpenAPIHono } from "@hono/zod-openapi" -import { getAllRequestsRoute } from "./openapi" -import { RequestFormManager } from "@/v2/lib/managers/request-form/request-form-manager" -import { getConnection } from "@/v2/db/turso" - -const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() - -handler.openapi(getAllRequestsRoute, async (ctx) => { - const { offset } = ctx.req.valid("query") ?? { offset: "0" } - - const { drizzle } = await getConnection(ctx.env) - - const requestFormManager = new RequestFormManager(drizzle) - - const allRequests = await requestFormManager.getRequestFormEntries( - parseInt(offset) - ) - - return ctx.json( - { - success: true, - requests: allRequests, - }, - 200 - ) -}) - -export default handler diff --git a/src/v2/routes/requests/form/view/all/schema.ts b/src/v2/routes/requests/form/view/all/schema.ts deleted file mode 100644 index bf29a845..00000000 --- a/src/v2/routes/requests/form/view/all/schema.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { z } from "@hono/zod-openapi" -import { selectRequestFormSchema } from "@/v2/db/schema" - -export const viewAllRequestsSchema = z - .object({ - offset: z.string().openapi({ - param: { - description: - "The offset of requests to return. This is used for pagination.", - name: "offset", - example: "0", - in: "query", - required: false, - }, - }), - }) - .partial() - -const requestFormSchema = z.object({ - ...selectRequestFormSchema.shape, - upvotesCount: z.number().optional(), -}) - -export const viewAllRequestsResponseSchema = z.object({ - success: z.literal(true), - requests: z.array(requestFormSchema), -}) diff --git a/src/v2/routes/requests/handler.ts b/src/v2/routes/requests/handler.ts index 11ca9d6a..43c28bee 100644 --- a/src/v2/routes/requests/handler.ts +++ b/src/v2/routes/requests/handler.ts @@ -1,12 +1,12 @@ import { OpenAPIHono } from "@hono/zod-openapi" -import RequestFormCreateRoute from "@/v2/routes/requests/form/create/route" -import RequestFormDeleteRoute from "@/v2/routes/requests/form/delete/[id]/route" -import ViewAllRequestsRoute from "@/v2/routes/requests/form/view/all/route" +import ViewAllRequestsRoute from "./all-requests" +import CreateRequestRoute from "./create-request" +import DeleteRequestRoute from "./delete-request" const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() -handler.route("/create/form", RequestFormCreateRoute) -handler.route("/delete/form", RequestFormDeleteRoute) handler.route("/view/all", ViewAllRequestsRoute) +handler.route("/create", CreateRequestRoute) +handler.route("/delete", DeleteRequestRoute) export default handler diff --git a/src/v2/routes/collection/asset/add/id/[id]/route.ts b/src/v2/routes/requests/upvote-request.ts similarity index 100% rename from src/v2/routes/collection/asset/add/id/[id]/route.ts rename to src/v2/routes/requests/upvote-request.ts diff --git a/src/v2/routes/collection/asset/add/id/[id]/schema.ts b/src/v2/routes/requests/view-request.ts similarity index 100% rename from src/v2/routes/collection/asset/add/id/[id]/schema.ts rename to src/v2/routes/requests/view-request.ts diff --git a/src/v2/routes/user/follows/follow/id/[id]/route.ts b/src/v2/routes/user/follow-user.ts similarity index 74% rename from src/v2/routes/user/follows/follow/id/[id]/route.ts rename to src/v2/routes/user/follow-user.ts index b9d86687..242b5be4 100644 --- a/src/v2/routes/user/follows/follow/id/[id]/route.ts +++ b/src/v2/routes/user/follow-user.ts @@ -1,9 +1,47 @@ import { OpenAPIHono } from "@hono/zod-openapi" -import { followUserByIdRoute } from "./openapi" import { getConnection } from "@/v2/db/turso" import { AuthSessionManager } from "@/v2/lib/managers/auth/user-session-manager" import { and, eq, or } from "drizzle-orm" import { userFollowing, userBlocked } from "@/v2/db/schema" +import { createRoute } from "@hono/zod-openapi" +import { GenericResponses } from "@/v2/lib/response-schemas" +import { z } from "@hono/zod-openapi" + +const followUserByIdSchema = z.object({ + id: z.string().openapi({ + param: { + description: "The id of the user to follow.", + in: "path", + name: "id", + required: true, + }, + }), +}) + +const followUserByIdResponseSchema = z.object({ + success: z.literal(true), +}) + +export const followUserByIdRoute = createRoute({ + path: "/{id}", + method: "post", + description: "Follow a user from their ID.", + tags: ["User"], + request: { + params: followUserByIdSchema, + }, + responses: { + 200: { + description: "True if the user was followed.", + content: { + "application/json": { + schema: followUserByIdResponseSchema, + }, + }, + }, + ...GenericResponses, + }, +}) const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() diff --git a/src/v2/routes/user/follows/follow/id/[id]/openapi.ts b/src/v2/routes/user/follows/follow/id/[id]/openapi.ts deleted file mode 100644 index f82b23f5..00000000 --- a/src/v2/routes/user/follows/follow/id/[id]/openapi.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { createRoute } from "@hono/zod-openapi" -import { followUserByIdResponseSchema, followUserByIdSchema } from "./schema" -import { GenericResponses } from "@/v2/lib/response-schemas" - -export const followUserByIdRoute = createRoute({ - path: "/{id}", - method: "post", - description: "Follow a user from their ID.", - tags: ["User"], - request: { - params: followUserByIdSchema, - }, - responses: { - 200: { - description: "True if the user was followed.", - content: { - "application/json": { - schema: followUserByIdResponseSchema, - }, - }, - }, - ...GenericResponses, - }, -}) diff --git a/src/v2/routes/user/follows/follow/id/[id]/schema.ts b/src/v2/routes/user/follows/follow/id/[id]/schema.ts deleted file mode 100644 index 3bc0a4d0..00000000 --- a/src/v2/routes/user/follows/follow/id/[id]/schema.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { z } from "@hono/zod-openapi" - -export const followUserByIdSchema = z.object({ - id: z.string().openapi({ - param: { - description: "The id of the user to follow.", - in: "path", - name: "id", - required: true, - }, - }), -}) - -export const followUserByIdResponseSchema = z.object({ - success: z.literal(true), -}) diff --git a/src/v2/routes/user/follows/followers/id/[id]/openapi.ts b/src/v2/routes/user/follows/followers/id/[id]/openapi.ts deleted file mode 100644 index 211484bc..00000000 --- a/src/v2/routes/user/follows/followers/id/[id]/openapi.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { createRoute } from "@hono/zod-openapi" -import { - viewUserFollowsbyIdSchema, - viewUserFollowsbyIdOffsetSchema, - viewUserFollowsbyIdResponseSchema, -} from "./schema" -import { GenericResponses } from "@/v2/lib/response-schemas" - -export const viewUserFollowsByIdRoute = createRoute({ - path: "/{id}", - method: "get", - description: "View a user's followers from their ID.", - tags: ["User"], - request: { - params: viewUserFollowsbyIdSchema, - query: viewUserFollowsbyIdOffsetSchema, - }, - responses: { - 200: { - description: - "List of a user's followers. Only 100 showed at a time, use pagination.", - content: { - "application/json": { - schema: viewUserFollowsbyIdResponseSchema, - }, - }, - }, - ...GenericResponses, - }, -}) diff --git a/src/v2/routes/user/follows/followers/id/[id]/route.ts b/src/v2/routes/user/follows/followers/id/[id]/route.ts deleted file mode 100644 index 2b6cb120..00000000 --- a/src/v2/routes/user/follows/followers/id/[id]/route.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { OpenAPIHono } from "@hono/zod-openapi" -import { viewUserFollowsByIdRoute } from "./openapi" -import { getConnection } from "@/v2/db/turso" - -const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() - -handler.openapi(viewUserFollowsByIdRoute, async (ctx) => { - const { id } = ctx.req.valid("param") - const { offset } = ctx.req.valid("query") - - const { drizzle } = await getConnection(ctx.env) - - const followers = await drizzle.query.userFollowing.findMany({ - where: (userFollowing, { eq }) => eq(userFollowing.followingId, id), - with: { - follower: { - columns: { - id: true, - avatarUrl: true, - username: true, - plan: true, - verified: true, - displayName: true, - }, - }, - }, - limit: 100, - offset: offset ? parseInt(offset) : 0, - }) - - return ctx.json( - { - success: true, - followers, - }, - 200 - ) -}) - -export default handler diff --git a/src/v2/routes/user/follows/followers/id/[id]/schema.ts b/src/v2/routes/user/follows/followers/id/[id]/schema.ts deleted file mode 100644 index 2455e265..00000000 --- a/src/v2/routes/user/follows/followers/id/[id]/schema.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { z } from "@hono/zod-openapi" -import { selectUserFollowingSchema, selectUserSchema } from "@/v2/db/schema" - -export const viewUserFollowsbyIdSchema = z.object({ - id: z.string().openapi({ - param: { - description: "User ID to view who follows them", - in: "path", - name: "id", - required: true, - }, - }), -}) - -export const viewUserFollowsbyIdOffsetSchema = z.object({ - offset: z - .string() - .optional() - .openapi({ - param: { - description: "The offset to start at, optional.", - in: "query", - name: "offset", - required: false, - }, - }), -}) - -export const viewUserFollowsbyIdResponseSchema = z.object({ - success: z.literal(true), - followers: z.array( - selectUserFollowingSchema.extend({ - follower: selectUserSchema.pick({ - id: true, - avatarUrl: true, - username: true, - plan: true, - verified: true, - displayName: true, - }), - }) - ), -}) diff --git a/src/v2/routes/user/follows/following/id/[id]/count/openapi.ts b/src/v2/routes/user/follows/following/id/[id]/count/openapi.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/user/follows/following/id/[id]/count/route.ts b/src/v2/routes/user/follows/following/id/[id]/count/route.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/user/follows/following/id/[id]/count/schema.ts b/src/v2/routes/user/follows/following/id/[id]/count/schema.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/user/follows/following/id/[id]/openapi.ts b/src/v2/routes/user/follows/following/id/[id]/openapi.ts deleted file mode 100644 index 8f7ebf06..00000000 --- a/src/v2/routes/user/follows/following/id/[id]/openapi.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { createRoute } from "@hono/zod-openapi" -import { - viewUserfollowingbyIdSchema, - viewUserfollowingbyIdResponseSchema, - viewUserFollowingOffsetSchema, -} from "./schema" -import { GenericResponses } from "@/v2/lib/response-schemas" - -export const viewUserfollowingbyIdRoute = createRoute({ - path: "/{id}", - method: "get", - description: "View who a user's following from their ID.", - tags: ["User"], - request: { - params: viewUserfollowingbyIdSchema, - query: viewUserFollowingOffsetSchema, - }, - responses: { - 200: { - description: - "List of who a user's following. Only 100 showed at a time, use pagination.", - content: { - "application/json": { - schema: viewUserfollowingbyIdResponseSchema, - }, - }, - }, - ...GenericResponses, - }, -}) diff --git a/src/v2/routes/user/follows/following/id/[id]/route.ts b/src/v2/routes/user/follows/following/id/[id]/route.ts deleted file mode 100644 index afd12e1d..00000000 --- a/src/v2/routes/user/follows/following/id/[id]/route.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { OpenAPIHono } from "@hono/zod-openapi" -import { viewUserfollowingbyIdRoute } from "./openapi" -import { getConnection } from "@/v2/db/turso" - -const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() - -handler.openapi(viewUserfollowingbyIdRoute, async (ctx) => { - const { id } = ctx.req.valid("param") - const { offset } = ctx.req.valid("query") - - const { drizzle } = await getConnection(ctx.env) - - const following = await drizzle.query.userFollowing.findMany({ - where: (userFollowing, { eq }) => eq(userFollowing.followerId, id), - with: { - following: { - columns: { - id: true, - avatarUrl: true, - username: true, - plan: true, - verified: true, - displayName: true, - }, - }, - }, - limit: 100, - offset: offset ? parseInt(offset) : 0, - }) - - return ctx.json( - { - success: true, - following, - }, - 200 - ) -}) - -export default handler diff --git a/src/v2/routes/user/follows/following/id/[id]/schema.ts b/src/v2/routes/user/follows/following/id/[id]/schema.ts deleted file mode 100644 index 020800bc..00000000 --- a/src/v2/routes/user/follows/following/id/[id]/schema.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { z } from "@hono/zod-openapi" -import { selectUserFollowingSchema, selectUserSchema } from "@/v2/db/schema" - -export const viewUserfollowingbyIdSchema = z.object({ - id: z.string().openapi({ - param: { - description: "User ID to view who they're following", - in: "path", - name: "id", - required: true, - }, - }), -}) - -export const viewUserFollowingOffsetSchema = z.object({ - offset: z - .string() - .optional() - .openapi({ - param: { - description: "The offset to start at, optional.", - in: "query", - name: "offset", - required: false, - }, - }), -}) - -export const viewUserfollowingbyIdResponseSchema = z.object({ - success: z.literal(true), - following: z.array( - selectUserFollowingSchema.extend({ - following: selectUserSchema.pick({ - id: true, - avatarUrl: true, - username: true, - plan: true, - verified: true, - displayName: true, - }), - }) - ), -}) diff --git a/src/v2/routes/user/follows/handler.ts b/src/v2/routes/user/follows/handler.ts deleted file mode 100644 index e4908316..00000000 --- a/src/v2/routes/user/follows/handler.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { OpenAPIHono } from "@hono/zod-openapi" -import UserFollowRoute from "@/v2/routes/user/follows/follow/id/[id]/route" -import UserUnfollowRoute from "@/v2/routes/user/follows/unfollow/id/[id]/route" - -import GetUsersFollowingRoute from "@/v2/routes/user/follows/following/id/[id]/route" -import GetUsersFollowersRoute from "@/v2/routes/user/follows/followers/id/[id]/route" - -const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() - -handler.route("/follow/id", UserFollowRoute) -handler.route("/unfollow/id", UserUnfollowRoute) - -handler.route("/following/id", GetUsersFollowingRoute) -handler.route("/followers/id", GetUsersFollowersRoute) - -export default handler diff --git a/src/v2/routes/user/follows/unfollow/id/[id]/openapi.ts b/src/v2/routes/user/follows/unfollow/id/[id]/openapi.ts deleted file mode 100644 index 33b6edb9..00000000 --- a/src/v2/routes/user/follows/unfollow/id/[id]/openapi.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { createRoute } from "@hono/zod-openapi" -import { - unfollowUserByIdResponseSchema, - unfollowUserByIdSchema, -} from "./schema" -import { GenericResponses } from "@/v2/lib/response-schemas" - -export const unFollowUserByIdRoute = createRoute({ - path: "/{id}", - method: "post", - description: "Follow a user from their ID.", - tags: ["User"], - request: { - params: unfollowUserByIdSchema, - }, - responses: { - 200: { - description: "True if the user was unfollowed.", - content: { - "application/json": { - schema: unfollowUserByIdResponseSchema, - }, - }, - }, - ...GenericResponses, - }, -}) diff --git a/src/v2/routes/user/follows/unfollow/id/[id]/schema.ts b/src/v2/routes/user/follows/unfollow/id/[id]/schema.ts deleted file mode 100644 index 8f5b7d18..00000000 --- a/src/v2/routes/user/follows/unfollow/id/[id]/schema.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { z } from "@hono/zod-openapi" - -export const unfollowUserByIdSchema = z.object({ - id: z.string().openapi({ - param: { - description: "The id of the user to unfollow.", - in: "path", - name: "id", - required: true, - }, - }), -}) - -export const unfollowUserByIdResponseSchema = z.object({ - success: z.literal(true), -}) diff --git a/src/v2/routes/user/get-user.ts b/src/v2/routes/user/get-user.ts new file mode 100644 index 00000000..62d624c0 --- /dev/null +++ b/src/v2/routes/user/get-user.ts @@ -0,0 +1,92 @@ +import { OpenAPIHono } from "@hono/zod-openapi" +import { getConnection } from "@/v2/db/turso" +import { eq } from "drizzle-orm" +import { authUser } from "@/v2/db/schema" +import { createRoute } from "@hono/zod-openapi" +import { GenericResponses } from "@/v2/lib/response-schemas" +import { z } from "@hono/zod-openapi" +import { selectUserSchema } from "@/v2/db/schema" + +const getUserByIdSchema = z.object({ + id: z.string().openapi({ + param: { + name: "id", + in: "path", + required: true, + description: "The ID of the user to retrieve.", + }, + }), +}) + +const getUserByIdResponseSchema = z.object({ + success: z.literal(true), + user: selectUserSchema.pick({ + id: true, + avatarUrl: true, + displayName: true, + username: true, + usernameColour: true, + pronouns: true, + verified: true, + bio: true, + dateJoined: true, + plan: true, + role: true, + }), +}) + +const getUserByIdRoute = createRoute({ + path: "/{id}", + method: "get", + description: "Get a user by their ID.", + tags: ["User"], + request: { + params: getUserByIdSchema, + }, + responses: { + 200: { + description: "The user was found.", + content: { + "application/json": { + schema: getUserByIdResponseSchema, + }, + }, + }, + ...GenericResponses, + }, +}) + +const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() + +handler.openapi(getUserByIdRoute, async (ctx) => { + const userId = ctx.req.valid("param").id + + const { drizzle } = await getConnection(ctx.env) + + const [user] = await drizzle + .select({ + id: authUser.id, + avatarUrl: authUser.avatarUrl, + displayName: authUser.displayName, + username: authUser.username, + usernameColour: authUser.usernameColour, + pronouns: authUser.pronouns, + verified: authUser.verified, + bio: authUser.bio, + dateJoined: authUser.dateJoined, + plan: authUser.plan, + role: authUser.role, + }) + .from(authUser) + .where(eq(authUser.id, userId)) + + return ctx.json( + { + success: true, + user, + }, + 200 + ) +}) + +export default handler diff --git a/src/v2/routes/user/get/handler.ts b/src/v2/routes/user/get/handler.ts deleted file mode 100644 index 8fe46dab..00000000 --- a/src/v2/routes/user/get/handler.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { OpenAPIHono } from "@hono/zod-openapi" -import SearchUserByIdRoute from "@/v2/routes/user/get/id/[id]/route" -import SearchUserByNameRoute from "@/v2/routes/user/get/username/[username]/route" - -const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() - -handler.route("/id", SearchUserByIdRoute) -handler.route("/username", SearchUserByNameRoute) - -export default handler diff --git a/src/v2/routes/user/get/id/[id]/openapi.ts b/src/v2/routes/user/get/id/[id]/openapi.ts deleted file mode 100644 index 09f1bbd4..00000000 --- a/src/v2/routes/user/get/id/[id]/openapi.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { createRoute } from "@hono/zod-openapi" -import { getUserByIdSchema, getUserByIdResponseSchema } from "./schema" -import { GenericResponses } from "@/v2/lib/response-schemas" - -export const getUserByIdRoute = createRoute({ - path: "/{id}", - method: "get", - description: "Get a user by their ID.", - tags: ["User"], - request: { - params: getUserByIdSchema, - }, - responses: { - 200: { - description: "The user was found.", - content: { - "application/json": { - schema: getUserByIdResponseSchema, - }, - }, - }, - ...GenericResponses, - }, -}) diff --git a/src/v2/routes/user/get/id/[id]/route.ts b/src/v2/routes/user/get/id/[id]/route.ts deleted file mode 100644 index 5434a2d2..00000000 --- a/src/v2/routes/user/get/id/[id]/route.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { OpenAPIHono } from "@hono/zod-openapi" -import { getUserByIdRoute } from "./openapi" -import { getConnection } from "@/v2/db/turso" -import { eq } from "drizzle-orm" -import { authUser } from "@/v2/db/schema" - -const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() - -handler.openapi(getUserByIdRoute, async (ctx) => { - const userId = ctx.req.valid("param").id - - const { drizzle } = await getConnection(ctx.env) - - const [user] = await drizzle - .select({ - id: authUser.id, - avatarUrl: authUser.avatarUrl, - displayName: authUser.displayName, - username: authUser.username, - usernameColour: authUser.usernameColour, - pronouns: authUser.pronouns, - verified: authUser.verified, - bio: authUser.bio, - dateJoined: authUser.dateJoined, - plan: authUser.plan, - role: authUser.role, - }) - .from(authUser) - .where(eq(authUser.id, userId)) - - return ctx.json( - { - success: true, - user, - }, - 200 - ) -}) - -export default handler diff --git a/src/v2/routes/user/get/id/[id]/schema.ts b/src/v2/routes/user/get/id/[id]/schema.ts deleted file mode 100644 index 7f0bc71d..00000000 --- a/src/v2/routes/user/get/id/[id]/schema.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { z } from "@hono/zod-openapi" -import { selectUserSchema } from "@/v2/db/schema" - -export const getUserByIdSchema = z.object({ - id: z.string().openapi({ - param: { - name: "id", - in: "path", - required: true, - description: "The ID of the user to retrieve.", - }, - }), -}) - -export const getUserByIdResponseSchema = z.object({ - success: z.literal(true), - user: selectUserSchema.pick({ - id: true, - avatarUrl: true, - displayName: true, - username: true, - usernameColour: true, - pronouns: true, - verified: true, - bio: true, - dateJoined: true, - plan: true, - role: true, - }), -}) diff --git a/src/v2/routes/user/get/username/[username]/openapi.ts b/src/v2/routes/user/get/username/[username]/openapi.ts deleted file mode 100644 index 05984399..00000000 --- a/src/v2/routes/user/get/username/[username]/openapi.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { createRoute } from "@hono/zod-openapi" -import { getUserByNameSchema } from "./schema" -import { GenericResponses } from "@/v2/lib/response-schemas" -import { getUserByNameResponseSchema } from "./schema" - -export const getUserByNameRoute = createRoute({ - path: "/{username}", - method: "get", - description: "Get a user by their exact username.", - tags: ["User"], - request: { - params: getUserByNameSchema, - }, - responses: { - 200: { - description: "The user was found.", - content: { - "application/json": { - schema: getUserByNameResponseSchema, - }, - }, - }, - ...GenericResponses, - }, -}) diff --git a/src/v2/routes/user/get/username/[username]/route.ts b/src/v2/routes/user/get/username/[username]/route.ts deleted file mode 100644 index 08fe7128..00000000 --- a/src/v2/routes/user/get/username/[username]/route.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { OpenAPIHono } from "@hono/zod-openapi" -import { getUserByNameRoute } from "./openapi" -import { authUser } from "@/v2/db/schema" -import { eq } from "drizzle-orm" -import { getConnection } from "@/v2/db/turso" - -const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() - -handler.openapi(getUserByNameRoute, async (ctx) => { - const username = ctx.req.valid("param").username - - const { drizzle } = await getConnection(ctx.env) - - const [user] = await drizzle - .select({ - id: authUser.id, - avatarUrl: authUser.avatarUrl, - displayName: authUser.displayName, - username: authUser.username, - usernameColour: authUser.usernameColour, - pronouns: authUser.pronouns, - verified: authUser.verified, - bio: authUser.bio, - dateJoined: authUser.dateJoined, - plan: authUser.plan, - role: authUser.role, - }) - .from(authUser) - .where(eq(authUser.username, username)) - - return ctx.json( - { - success: true, - user, - }, - 200 - ) -}) - -export default handler diff --git a/src/v2/routes/user/get/username/[username]/schema.ts b/src/v2/routes/user/get/username/[username]/schema.ts deleted file mode 100644 index 55c25ba0..00000000 --- a/src/v2/routes/user/get/username/[username]/schema.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { z } from "@hono/zod-openapi" -import { selectUserSchema } from "@/v2/db/schema" - -export const getUserByNameSchema = z.object({ - username: z.string().openapi({ - param: { - name: "username", - in: "path", - required: true, - description: "The exact Username of the user to retrieve.", - }, - }), -}) - -export const getUserByNameResponseSchema = z.object({ - success: z.literal(true), - user: selectUserSchema.pick({ - id: true, - avatarUrl: true, - displayName: true, - username: true, - usernameColour: true, - pronouns: true, - verified: true, - bio: true, - dateJoined: true, - plan: true, - role: true, - }), -}) diff --git a/src/v2/routes/user/handler.ts b/src/v2/routes/user/handler.ts index 37ed4599..b3119bfc 100644 --- a/src/v2/routes/user/handler.ts +++ b/src/v2/routes/user/handler.ts @@ -1,12 +1,21 @@ import { OpenAPIHono } from "@hono/zod-openapi" -import UserGetRoute from "@/v2/routes/user/get/handler" -import UserSearchRoute from "@/v2/routes/user/search/handler" -import UserFollowRoute from "@/v2/routes/user/follows/handler" + +import GetUserRoute from "./get-user" +import SearchUserRoute from "./search-users" + +import FollowUserRoute from "./follow-user" +import UnfollowUserRoute from "./unfollow-user" + +import UserFollowingRoute from "./user-following" +import UserFollowersRoute from "./user-followers" const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() -handler.route("/get", UserGetRoute) -handler.route("/search", UserSearchRoute) -handler.route("/follows", UserFollowRoute) +handler.route("/get", GetUserRoute) +handler.route("/search", SearchUserRoute) +handler.route("/follow", FollowUserRoute) +handler.route("/unfollow", UnfollowUserRoute) +handler.route("/following", UserFollowingRoute) +handler.route("/followers", UserFollowersRoute) export default handler diff --git a/src/v2/routes/user/search-users.ts b/src/v2/routes/user/search-users.ts new file mode 100644 index 00000000..171e9c0b --- /dev/null +++ b/src/v2/routes/user/search-users.ts @@ -0,0 +1,92 @@ +import { OpenAPIHono } from "@hono/zod-openapi" +import { authUser } from "@/v2/db/schema" +import { or, like } from "drizzle-orm" +import { getConnection } from "@/v2/db/turso" +import { createRoute } from "@hono/zod-openapi" +import { GenericResponses } from "@/v2/lib/response-schemas" +import { z } from "@hono/zod-openapi" +import { selectUserSchema } from "@/v2/db/schema" + +const getUsersByNameSchema = z.object({ + username: z.string().openapi({ + param: { + name: "username", + in: "path", + required: true, + description: "The username of the user(s) to retrieve.", + }, + }), +}) + +const searchUsersByUsernameSchema = z.object({ + success: z.literal(true), + users: selectUserSchema + .pick({ + id: true, + avatarUrl: true, + displayName: true, + username: true, + usernameColour: true, + pronouns: true, + verified: true, + bio: true, + dateJoined: true, + plan: true, + role: true, + }) + .array(), +}) + +const searchUsersByUsernameRoute = createRoute({ + path: "/{username}", + method: "get", + description: "Search for users by their username.", + tags: ["User"], + request: { + params: getUsersByNameSchema, + }, + responses: { + 200: { + description: "User(s) were found.", + content: { + "application/json": { + schema: searchUsersByUsernameSchema, + }, + }, + }, + ...GenericResponses, + }, +}) + +const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() + +handler.openapi(searchUsersByUsernameRoute, async (ctx) => { + const userQuery = ctx.req.valid("param").username + + const { drizzle } = await getConnection(ctx.env) + + const users = await drizzle + .select({ + id: authUser.id, + avatarUrl: authUser.avatarUrl, + displayName: authUser.displayName, + username: authUser.username, + usernameColour: authUser.usernameColour, + pronouns: authUser.pronouns, + verified: authUser.verified, + bio: authUser.bio, + dateJoined: authUser.dateJoined, + plan: authUser.plan, + role: authUser.role, + }) + .from(authUser) + .where(or(like(authUser.username, `%${userQuery}%`))) + .limit(25) + + return ctx.json({ + success: true, + users, + }) +}) + +export default handler diff --git a/src/v2/routes/user/search/handler.ts b/src/v2/routes/user/search/handler.ts deleted file mode 100644 index 7bd1d472..00000000 --- a/src/v2/routes/user/search/handler.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { OpenAPIHono } from "@hono/zod-openapi" -import SearchUsersByUsernameRoute from "./username/[username]/route" - -const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() - -handler.route("/username", SearchUsersByUsernameRoute) - -export default handler diff --git a/src/v2/routes/user/search/username/[username]/openapi.ts b/src/v2/routes/user/search/username/[username]/openapi.ts deleted file mode 100644 index 436fa11b..00000000 --- a/src/v2/routes/user/search/username/[username]/openapi.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { createRoute } from "@hono/zod-openapi" -import { getUsersByNameSchema, searchUsersByUsernameSchema } from "./schema" -import { GenericResponses } from "@/v2/lib/response-schemas" - -export const searchUsersByUsernameRoute = createRoute({ - path: "/{username}", - method: "get", - description: "Search for users by their username.", - tags: ["User"], - request: { - params: getUsersByNameSchema, - }, - responses: { - 200: { - description: "User(s) were found.", - content: { - "application/json": { - schema: searchUsersByUsernameSchema, - }, - }, - }, - ...GenericResponses, - }, -}) diff --git a/src/v2/routes/user/search/username/[username]/route.ts b/src/v2/routes/user/search/username/[username]/route.ts deleted file mode 100644 index 5cc23341..00000000 --- a/src/v2/routes/user/search/username/[username]/route.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { OpenAPIHono } from "@hono/zod-openapi" -import { searchUsersByUsernameRoute } from "./openapi" -import { authUser } from "@/v2/db/schema" -import { or, like } from "drizzle-orm" -import { getConnection } from "@/v2/db/turso" - -const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() - -handler.openapi(searchUsersByUsernameRoute, async (ctx) => { - const userQuery = ctx.req.valid("param").username - - const { drizzle } = await getConnection(ctx.env) - - const users = await drizzle - .select({ - id: authUser.id, - avatarUrl: authUser.avatarUrl, - displayName: authUser.displayName, - username: authUser.username, - usernameColour: authUser.usernameColour, - pronouns: authUser.pronouns, - verified: authUser.verified, - bio: authUser.bio, - dateJoined: authUser.dateJoined, - plan: authUser.plan, - role: authUser.role, - }) - .from(authUser) - .where(or(like(authUser.username, `%${userQuery}%`))) - .limit(25) - - return ctx.json({ - success: true, - users, - }) -}) - -export default handler diff --git a/src/v2/routes/user/search/username/[username]/schema.ts b/src/v2/routes/user/search/username/[username]/schema.ts deleted file mode 100644 index 27daaab5..00000000 --- a/src/v2/routes/user/search/username/[username]/schema.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { z } from "@hono/zod-openapi" -import { selectUserSchema } from "@/v2/db/schema" - -export const getUsersByNameSchema = z.object({ - username: z.string().openapi({ - param: { - name: "username", - in: "path", - required: true, - description: "The username of the user(s) to retrieve.", - }, - }), -}) - -export const searchUsersByUsernameSchema = z.object({ - success: z.literal(true), - users: selectUserSchema - .pick({ - id: true, - avatarUrl: true, - displayName: true, - username: true, - usernameColour: true, - pronouns: true, - verified: true, - bio: true, - dateJoined: true, - plan: true, - role: true, - }) - .array(), -}) diff --git a/src/v2/routes/user/follows/unfollow/id/[id]/route.ts b/src/v2/routes/user/unfollow-user.ts similarity index 75% rename from src/v2/routes/user/follows/unfollow/id/[id]/route.ts rename to src/v2/routes/user/unfollow-user.ts index 6e1d72ab..054c6123 100644 --- a/src/v2/routes/user/follows/unfollow/id/[id]/route.ts +++ b/src/v2/routes/user/unfollow-user.ts @@ -1,9 +1,47 @@ import { OpenAPIHono } from "@hono/zod-openapi" -import { unFollowUserByIdRoute } from "./openapi" import { getConnection } from "@/v2/db/turso" import { AuthSessionManager } from "@/v2/lib/managers/auth/user-session-manager" import { userFollowing, userBlocked } from "@/v2/db/schema" import { and, eq, or } from "drizzle-orm" +import { createRoute } from "@hono/zod-openapi" +import { GenericResponses } from "@/v2/lib/response-schemas" +import { z } from "@hono/zod-openapi" + +const unfollowUserByIdSchema = z.object({ + id: z.string().openapi({ + param: { + description: "The id of the user to unfollow.", + in: "path", + name: "id", + required: true, + }, + }), +}) + +const unfollowUserByIdResponseSchema = z.object({ + success: z.literal(true), +}) + +const unFollowUserByIdRoute = createRoute({ + path: "/{id}", + method: "post", + description: "Follow a user from their ID.", + tags: ["User"], + request: { + params: unfollowUserByIdSchema, + }, + responses: { + 200: { + description: "True if the user was unfollowed.", + content: { + "application/json": { + schema: unfollowUserByIdResponseSchema, + }, + }, + }, + ...GenericResponses, + }, +}) const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() diff --git a/src/v2/routes/user/user-followers.ts b/src/v2/routes/user/user-followers.ts new file mode 100644 index 00000000..140c0272 --- /dev/null +++ b/src/v2/routes/user/user-followers.ts @@ -0,0 +1,107 @@ +import { OpenAPIHono } from "@hono/zod-openapi" +import { getConnection } from "@/v2/db/turso" +import { createRoute } from "@hono/zod-openapi" +import { GenericResponses } from "@/v2/lib/response-schemas" +import { z } from "@hono/zod-openapi" +import { selectUserFollowingSchema, selectUserSchema } from "@/v2/db/schema" + +const viewUserFollowsbyIdSchema = z.object({ + id: z.string().openapi({ + param: { + description: "User ID to view who follows them", + in: "path", + name: "id", + required: true, + }, + }), +}) + +const viewUserFollowsbyIdOffsetSchema = z.object({ + offset: z + .string() + .optional() + .openapi({ + param: { + description: "The offset to start at, optional.", + in: "query", + name: "offset", + required: false, + }, + }), +}) + +export const viewUserFollowsbyIdResponseSchema = z.object({ + success: z.literal(true), + followers: z.array( + selectUserFollowingSchema.extend({ + follower: selectUserSchema.pick({ + id: true, + avatarUrl: true, + username: true, + plan: true, + verified: true, + displayName: true, + }), + }) + ), +}) + +const viewUserFollowsByIdRoute = createRoute({ + path: "/{id}", + method: "get", + description: "View a user's followers from their ID.", + tags: ["User"], + request: { + params: viewUserFollowsbyIdSchema, + query: viewUserFollowsbyIdOffsetSchema, + }, + responses: { + 200: { + description: + "List of a user's followers. Only 100 showed at a time, use pagination.", + content: { + "application/json": { + schema: viewUserFollowsbyIdResponseSchema, + }, + }, + }, + ...GenericResponses, + }, +}) + +const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() + +handler.openapi(viewUserFollowsByIdRoute, async (ctx) => { + const { id } = ctx.req.valid("param") + const { offset } = ctx.req.valid("query") + + const { drizzle } = await getConnection(ctx.env) + + const followers = await drizzle.query.userFollowing.findMany({ + where: (userFollowing, { eq }) => eq(userFollowing.followingId, id), + with: { + follower: { + columns: { + id: true, + avatarUrl: true, + username: true, + plan: true, + verified: true, + displayName: true, + }, + }, + }, + limit: 100, + offset: offset ? parseInt(offset) : 0, + }) + + return ctx.json( + { + success: true, + followers, + }, + 200 + ) +}) + +export default handler diff --git a/src/v2/routes/user/user-following.ts b/src/v2/routes/user/user-following.ts new file mode 100644 index 00000000..0021c12d --- /dev/null +++ b/src/v2/routes/user/user-following.ts @@ -0,0 +1,107 @@ +import { OpenAPIHono } from "@hono/zod-openapi" +import { getConnection } from "@/v2/db/turso" +import { createRoute } from "@hono/zod-openapi" +import { GenericResponses } from "@/v2/lib/response-schemas" +import { z } from "@hono/zod-openapi" +import { selectUserFollowingSchema, selectUserSchema } from "@/v2/db/schema" + +const viewUserfollowingbyIdSchema = z.object({ + id: z.string().openapi({ + param: { + description: "User ID to view who they're following", + in: "path", + name: "id", + required: true, + }, + }), +}) + +const viewUserFollowingOffsetSchema = z.object({ + offset: z + .string() + .optional() + .openapi({ + param: { + description: "The offset to start at, optional.", + in: "query", + name: "offset", + required: false, + }, + }), +}) + +const viewUserfollowingbyIdResponseSchema = z.object({ + success: z.literal(true), + following: z.array( + selectUserFollowingSchema.extend({ + following: selectUserSchema.pick({ + id: true, + avatarUrl: true, + username: true, + plan: true, + verified: true, + displayName: true, + }), + }) + ), +}) + +const viewUserfollowingbyIdRoute = createRoute({ + path: "/{id}", + method: "get", + description: "View who a user's following from their ID.", + tags: ["User"], + request: { + params: viewUserfollowingbyIdSchema, + query: viewUserFollowingOffsetSchema, + }, + responses: { + 200: { + description: + "List of who a user's following. Only 100 showed at a time, use pagination.", + content: { + "application/json": { + schema: viewUserfollowingbyIdResponseSchema, + }, + }, + }, + ...GenericResponses, + }, +}) + +const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() + +handler.openapi(viewUserfollowingbyIdRoute, async (ctx) => { + const { id } = ctx.req.valid("param") + const { offset } = ctx.req.valid("query") + + const { drizzle } = await getConnection(ctx.env) + + const following = await drizzle.query.userFollowing.findMany({ + where: (userFollowing, { eq }) => eq(userFollowing.followerId, id), + with: { + following: { + columns: { + id: true, + avatarUrl: true, + username: true, + plan: true, + verified: true, + displayName: true, + }, + }, + }, + limit: 100, + offset: offset ? parseInt(offset) : 0, + }) + + return ctx.json( + { + success: true, + following, + }, + 200 + ) +}) + +export default handler