Added custom lists

This commit is contained in:
FifthWit 2025-04-02 21:04:46 -05:00
parent 05bbc4720d
commit b99d88cf2e
6 changed files with 310 additions and 0 deletions

View file

@ -0,0 +1,30 @@
-- CreateTable
CREATE TABLE "lists" (
"id" UUID NOT NULL,
"user_id" VARCHAR(255) NOT NULL,
"name" VARCHAR(255) NOT NULL,
"description" VARCHAR(255),
"created_at" TIMESTAMPTZ(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMPTZ(0) NOT NULL,
CONSTRAINT "lists_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "list_items" (
"id" UUID NOT NULL,
"list_id" UUID NOT NULL,
"tmdb_id" VARCHAR(255) NOT NULL,
"added_at" TIMESTAMPTZ(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "list_items_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE INDEX "lists_user_id_index" ON "lists"("user_id");
-- CreateIndex
CREATE UNIQUE INDEX "list_items_list_id_tmdb_id_unique" ON "list_items"("list_id", "tmdb_id");
-- AddForeignKey
ALTER TABLE "list_items" ADD CONSTRAINT "list_items_list_id_fkey" FOREIGN KEY ("list_id") REFERENCES "lists"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

View file

@ -76,4 +76,26 @@ model users {
permissions String[] permissions String[]
ratings Json @default("[]") ratings Json @default("[]")
profile Json profile Json
}
model lists {
id String @id @db.Uuid @default(uuid())
user_id String @db.VarChar(255)
name String @db.VarChar(255)
description String? @db.VarChar(255)
created_at DateTime @default(now()) @db.Timestamptz(0)
updated_at DateTime @updatedAt @db.Timestamptz(0)
list_items list_items[]
@@index([user_id], map: "lists_user_id_index")
}
model list_items {
id String @id @db.Uuid @default(uuid())
list_id String @db.Uuid
tmdb_id String @db.VarChar(255)
added_at DateTime @default(now()) @db.Timestamptz(0)
list lists @relation(fields: [list_id], references: [id])
@@unique([list_id, tmdb_id], map: "list_items_list_id_tmdb_id_unique")
} }

View file

@ -0,0 +1,49 @@
import { useAuth } from "#imports";
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
export default defineEventHandler(async (event) => {
const userId = event.context.params?.id;
const listId = event.context.params?.listId;
const session = await useAuth().getCurrentSession();
if (session.user !== userId) {
throw createError({
statusCode: 403,
message: "Cannot delete lists for other users",
});
}
const list = await prisma.lists.findUnique({
where: { id: listId },
});
if (!list) {
throw createError({
statusCode: 404,
message: "List not found",
});
}
if (list.user_id !== userId) {
throw createError({
statusCode: 403,
message: "Cannot delete lists you don't own",
});
}
await prisma.$transaction(async (tx) => {
await tx.list_items.deleteMany({
where: { list_id: listId },
});
await tx.lists.delete({
where: { id: listId },
});
});
return {
id: listId,
message: "List deleted successfully",
};
});

View file

@ -0,0 +1,29 @@
import { useAuth } from "#imports";
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
export default defineEventHandler(async (event) => {
const userId = event.context.params?.id;
const session = await useAuth().getCurrentSession();
if (session.user !== userId) {
throw createError({
statusCode: 403,
message: "Cannot access other user information",
});
}
const lists = await prisma.lists.findMany({
where: {
user_id: userId,
},
include: {
list_items: true,
},
});
return {
lists,
};
});

View file

@ -0,0 +1,108 @@
import { useAuth } from "#imports";
import { PrismaClient } from "@prisma/client";
import { z } from "zod";
const prisma = new PrismaClient();
// Schema for validating the request body
const listItemSchema = z.object({
tmdb_id: z.string(),
});
const updateListSchema = z.object({
list_id: z.string().uuid(),
name: z.string().min(1).max(255).optional(),
description: z.string().max(255).optional().nullable(),
addItems: z.array(listItemSchema).optional(),
removeItems: z.array(listItemSchema).optional(),
});
export default defineEventHandler(async (event) => {
const userId = event.context.params?.id;
const session = await useAuth().getCurrentSession();
if (session.user !== userId) {
throw createError({
statusCode: 403,
message: "Cannot modify lists for other users",
});
}
const body = await readBody(event);
const validatedBody = updateListSchema.parse(body);
const list = await prisma.lists.findUnique({
where: { id: validatedBody.list_id },
include: { list_items: true },
});
if (!list) {
throw createError({
statusCode: 404,
message: "List not found",
});
}
if (list.user_id !== userId) {
throw createError({
statusCode: 403,
message: "Cannot modify lists you don't own",
});
}
const result = await prisma.$transaction(async (tx) => {
if (validatedBody.name || validatedBody.description !== undefined) {
await tx.lists.update({
where: { id: list.id },
data: {
name: validatedBody.name || list.name,
description:
validatedBody.description !== undefined
? validatedBody.description
: list.description,
},
});
}
if (validatedBody.addItems && validatedBody.addItems.length > 0) {
const existingTmdbIds = list.list_items.map((item) => item.tmdb_id);
const itemsToAdd = validatedBody.addItems.filter(
(item) => !existingTmdbIds.includes(item.tmdb_id)
);
if (itemsToAdd.length > 0) {
await tx.list_items.createMany({
data: itemsToAdd.map((item) => ({
list_id: list.id,
tmdb_id: item.tmdb_id,
})),
skipDuplicates: true,
});
}
}
if (validatedBody.removeItems && validatedBody.removeItems.length > 0) {
const tmdbIdsToRemove = validatedBody.removeItems.map(
(item) => item.tmdb_id
);
await tx.list_items.deleteMany({
where: {
list_id: list.id,
tmdb_id: { in: tmdbIdsToRemove },
},
});
}
return tx.lists.findUnique({
where: { id: list.id },
include: { list_items: true },
});
});
return {
list: result,
message: "List updated successfully",
};
});

View file

@ -0,0 +1,72 @@
import { useAuth } from "#imports";
import { PrismaClient } from "@prisma/client";
import { z } from "zod";
const prisma = new PrismaClient();
// Schema for validating the request body
const listItemSchema = z.object({
tmdb_id: z.string(),
});
const createListSchema = z.object({
name: z.string().min(1).max(255),
description: z.string().max(255).optional().nullable(),
items: z.array(listItemSchema).optional(),
});
export default defineEventHandler(async (event) => {
const userId = event.context.params?.id;
const session = await useAuth().getCurrentSession();
if (session.user !== userId) {
throw createError({
statusCode: 403,
message: "Cannot modify user other than yourself",
});
}
const body = await readBody(event);
let parsedBody;
try {
parsedBody = typeof body === "string" ? JSON.parse(body) : body;
} catch (error) {
throw createError({
statusCode: 400,
message: "Invalid request body format",
});
}
const validatedBody = createListSchema.parse(parsedBody);
const result = await prisma.$transaction(async (tx) => {
const newList = await tx.lists.create({
data: {
user_id: userId,
name: validatedBody.name,
description: validatedBody.description || null,
},
});
if (validatedBody.items && validatedBody.items.length > 0) {
await tx.list_items.createMany({
data: validatedBody.items.map((item) => ({
list_id: newList.id,
tmdb_id: item.tmdb_id,
})),
skipDuplicates: true,
});
}
return tx.lists.findUnique({
where: { id: newList.id },
include: { list_items: true },
});
});
return {
list: result,
message: "List created successfully",
};
});