diff --git a/src/index.ts b/src/index.ts index fe898a6..7fd646e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -61,4 +61,4 @@ app.onError((err, ctx) => { ) }) -export default app \ No newline at end of file +export default app diff --git a/src/v2/db/schema/collections/user-collections.ts b/src/v2/db/schema/collections/user-collections.ts index 4c12f46..9c5dda6 100644 --- a/src/v2/db/schema/collections/user-collections.ts +++ b/src/v2/db/schema/collections/user-collections.ts @@ -114,7 +114,7 @@ export const selectUserCollectionAssetSchema = export const collectionRelations = relations( userCollection, ({ one, many }) => ({ - user: one(authUser, { + authUser: one(authUser, { fields: [userCollection.userId], references: [authUser.id], relationName: "collection_auth_user", diff --git a/src/v2/routes/collection/create-collection.ts b/src/v2/routes/collection/create-collection.ts index ee4582c..98e6643 100644 --- a/src/v2/routes/collection/create-collection.ts +++ b/src/v2/routes/collection/create-collection.ts @@ -23,7 +23,8 @@ const openRoute = createRoute({ path: "/collection/create", method: "post", summary: "Create a new collection.", - description: "Create a new collection, accent colours available for supporters", + description: + "Create a new collection, accent colours available for supporters", tags: ["Collection"], request: { body: { @@ -36,7 +37,8 @@ const openRoute = createRoute({ }, responses: { 200: { - description: "Returns the collection + true if the collection was made.", + description: + "Returns the collection + true if the collection was made.", content: { "application/json": { schema: responseSchema, @@ -68,14 +70,16 @@ export const CreateCollectionRoute = (handler: AppHandler) => { const { drizzle } = await getConnection(ctx.env) - const [newCollection] = await drizzle.insert(userCollection) + const [newCollection] = await drizzle + .insert(userCollection) .values({ name: name, userId: user.id, description: description, isPublic: Boolean(isPublic), accentColour: accentColour as ColourType, - }).returning() + }) + .returning() return ctx.json( { diff --git a/src/v2/routes/collection/delete-collection.ts b/src/v2/routes/collection/delete-collection.ts index d7a3499..73f0fca 100644 --- a/src/v2/routes/collection/delete-collection.ts +++ b/src/v2/routes/collection/delete-collection.ts @@ -61,14 +61,13 @@ export const DeleteCollectionRoute = (handler: AppHandler) => { 401 ) } - + const { id } = ctx.req.valid("param") const { drizzle } = await getConnection(ctx.env) - const [existingCollection] = await drizzle - .select().from(userCollection) - + const [existingCollection] = await drizzle.select().from(userCollection) + if (!existingCollection) { return ctx.json( { @@ -90,7 +89,7 @@ export const DeleteCollectionRoute = (handler: AppHandler) => { } await drizzle.delete(userCollection).where(eq(userCollection.id, id)) - + return ctx.json( { success: true, diff --git a/src/v2/routes/collection/get-collection-assets b/src/v2/routes/collection/get-collection-assets new file mode 100644 index 0000000..e69de29 diff --git a/src/v2/routes/collection/get-collection.ts b/src/v2/routes/collection/get-collection.ts index e69de29..1e2bae5 100644 --- a/src/v2/routes/collection/get-collection.ts +++ b/src/v2/routes/collection/get-collection.ts @@ -0,0 +1,165 @@ +import { AppHandler } from "../handler" +import { getConnection } from "@/v2/db/turso" +import { + userCollection, + userCollectionCollaborators, + selectUserCollectionSchema, + selectUserSchema, +} 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" +import { AuthSessionManager } from "@/v2/lib/managers/auth/user-session-manager" + +const paramsSchema = z.object({ + id: z.string().openapi({ + param: { + name: "id", + in: "path", + description: "The ID of the collection to retrieve.", + required: true, + }, + }), +}) + +const responseSchema = z.object({ + success: z.literal(true), + collection: selectUserCollectionSchema + .pick({ + id: true, + name: true, + accentColour: true, + isPublic: true, + userId: true, + }) + .extend({ + authUser: selectUserSchema.pick({ + id: true, + avatarUrl: true, + displayName: true, + username: true, + usernameColour: true, + plan: true, + role: true, + }), + }), +}) + +const openRoute = createRoute({ + path: "/{id}", + method: "get", + summary: "Get a collection", + description: + "Get a collection by its ID. If you do not have access to the collection (it is private/you do not have edit permission), it will not be returned.", + tags: ["Asset"], + request: { + params: paramsSchema, + }, + responses: { + 200: { + description: "Basic information about the collection is returned.", + content: { + "application/json": { + schema: responseSchema, + }, + }, + }, + ...GenericResponses, + }, +}) + +export const GetCollectionByIdRoute = (handler: AppHandler) => { + handler.openapi(openRoute, async (ctx) => { + const { id } = ctx.req.valid("param") + + const { drizzle } = await getConnection(ctx.env) + + const [validCollection] = await drizzle + .select({ + id: userCollection.id, + userId: userCollection.userId, + isPublic: userCollection.isPublic, + }) + .from(userCollection) + .where(eq(userCollection.id, id)) + + if (!validCollection) { + return ctx.json( + { + success: false, + message: "Collection not found", + }, + 404 + ) + } + + const authSessionManager = new AuthSessionManager(ctx) + + const { user } = await authSessionManager.validateSession() + + if (!validCollection.isPublic) { + if (!user) { + return ctx.json( + { + success: false, + message: "Unauthorized", + }, + 401 + ) + } + + const [collaborator] = await drizzle + .select() + .from(userCollectionCollaborators) + .where( + and( + eq(userCollectionCollaborators.collectionId, id), + eq(userCollectionCollaborators.collaboratorId, user.id) + ) + ) + + if (!collaborator && validCollection.userId != user.id) { + return ctx.json( + { + success: false, + message: "Unauthorized", + }, + 401 + ) + } + } + + const collectionInfo = await drizzle.query.userCollection.findFirst({ + columns: { + id: true, + name: true, + accentColour: true, + isPublic: true, + userId: true, + }, + where: (collection, { eq }) => eq(collection.id, id), + with: { + authUser: { + columns: { + id: true, + avatarUrl: true, + displayName: true, + username: true, + usernameColour: true, + plan: true, + role: true, + }, + }, + }, + }) + + return ctx.json( + { + success: true, + collection: collectionInfo, + }, + 200 + ) + }) +} diff --git a/src/v2/routes/collection/handler.ts b/src/v2/routes/collection/handler.ts index c8c8629..678274e 100644 --- a/src/v2/routes/collection/handler.ts +++ b/src/v2/routes/collection/handler.ts @@ -1,10 +1,10 @@ -import { OpenAPIHono } from "@hono/zod-openapi"; -import { CreateCollectionRoute } from "./create-collection"; -import { DeleteCollectionRoute } from "./delete-collection"; +import { OpenAPIHono } from "@hono/zod-openapi" +import { CreateCollectionRoute } from "./create-collection" +import { DeleteCollectionRoute } from "./delete-collection" -const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>(); +const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() -CreateCollectionRoute(handler); -DeleteCollectionRoute(handler); +CreateCollectionRoute(handler) +DeleteCollectionRoute(handler) -export default handler; \ No newline at end of file +export default handler