mirror of
https://github.com/p-stream/backend.git
synced 2026-01-11 20:10:33 +00:00
Added custom lists
This commit is contained in:
parent
05bbc4720d
commit
b99d88cf2e
6 changed files with 310 additions and 0 deletions
30
prisma/migrations/20250403013111_added_lists/migration.sql
Normal file
30
prisma/migrations/20250403013111_added_lists/migration.sql
Normal 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;
|
||||
|
|
@ -76,4 +76,26 @@ model users {
|
|||
permissions String[]
|
||||
ratings Json @default("[]")
|
||||
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")
|
||||
}
|
||||
49
server/routes/users/[id]/lists/[listId].delete.ts
Normal file
49
server/routes/users/[id]/lists/[listId].delete.ts
Normal 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",
|
||||
};
|
||||
});
|
||||
29
server/routes/users/[id]/lists/index.get.ts
Normal file
29
server/routes/users/[id]/lists/index.get.ts
Normal 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,
|
||||
};
|
||||
});
|
||||
108
server/routes/users/[id]/lists/index.patch.ts
Normal file
108
server/routes/users/[id]/lists/index.patch.ts
Normal 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",
|
||||
};
|
||||
});
|
||||
72
server/routes/users/[id]/lists/index.post.ts
Normal file
72
server/routes/users/[id]/lists/index.post.ts
Normal 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",
|
||||
};
|
||||
});
|
||||
Loading…
Reference in a new issue