mirror of
https://github.com/p-stream/backend.git
synced 2026-05-08 00:39:29 +00:00
Small updates
This commit is contained in:
parent
a5baed2ee4
commit
ed08f2daab
8 changed files with 102 additions and 84 deletions
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- A unique constraint covering the columns `[user_id,name]` on the table `lists` will be added. If there are existing duplicate values, this will fail.
|
||||||
|
- A unique constraint covering the columns `[user,device]` on the table `sessions` will be added. If there are existing duplicate values, this will fail.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "bookmarks_user_id_idx" ON "bookmarks" USING HASH ("user_id");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "lists_user_id_name_unique" ON "lists"("user_id", "name");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "progress_items_user_id_idx" ON "progress_items" USING HASH ("user_id");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "sessions_user_idx" ON "sessions" USING HASH ("user");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "sessions_user_device_unique" ON "sessions"("user", "device");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "watch_history_user_id_watched_at_idx" ON "watch_history"("user_id", "watched_at" DESC);
|
||||||
|
|
@ -51,6 +51,7 @@ model lists {
|
||||||
public Boolean @default(false)
|
public Boolean @default(false)
|
||||||
list_items list_items[]
|
list_items list_items[]
|
||||||
|
|
||||||
|
@@unique([user_id, name], map: "lists_user_id_name_unique")
|
||||||
@@index([user_id], map: "lists_user_id_index")
|
@@index([user_id], map: "lists_user_id_index")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,6 +87,7 @@ model sessions {
|
||||||
device String
|
device String
|
||||||
user_agent String
|
user_agent String
|
||||||
|
|
||||||
|
@@unique([user, device], map: "sessions_user_device_unique")
|
||||||
@@index([user], type: Hash)
|
@@index([user], type: Hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { prisma } from '#imports';
|
||||||
export default defineEventHandler(async event => {
|
export default defineEventHandler(async event => {
|
||||||
const id = event.context.params?.id;
|
const id = event.context.params?.id;
|
||||||
const listInfo = await prisma.lists.findUnique({
|
const listInfo = await prisma.lists.findUnique({
|
||||||
|
relationLoadStrategy: 'join',
|
||||||
where: {
|
where: {
|
||||||
id: id,
|
id: id,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -39,18 +39,13 @@ export default defineEventHandler(async event => {
|
||||||
const body = await readBody(event);
|
const body = await readBody(event);
|
||||||
const validatedBody = updateSessionSchema.parse(body);
|
const validatedBody = updateSessionSchema.parse(body);
|
||||||
|
|
||||||
if (validatedBody.deviceName) {
|
// Use update return value directly — no redundant findUnique
|
||||||
await prisma.sessions.update({
|
const updatedSession = validatedBody.deviceName
|
||||||
where: { id: sessionId },
|
? await prisma.sessions.update({
|
||||||
data: {
|
where: { id: sessionId },
|
||||||
device: validatedBody.deviceName,
|
data: { device: validatedBody.deviceName },
|
||||||
},
|
})
|
||||||
});
|
: targetedSession;
|
||||||
}
|
|
||||||
|
|
||||||
const updatedSession = await prisma.sessions.findUnique({
|
|
||||||
where: { id: sessionId },
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: updatedSession.id,
|
id: updatedSession.id,
|
||||||
|
|
@ -65,16 +60,7 @@ export default defineEventHandler(async event => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.method === 'DELETE') {
|
if (event.method === 'DELETE') {
|
||||||
const sid = event.context.params?.sid;
|
// targetedSession already validated above — no redundant findUnique or session bump needed
|
||||||
const sessionExists = await prisma.sessions.findUnique({
|
|
||||||
where: { id: sid },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!sessionExists) {
|
|
||||||
return { success: true };
|
|
||||||
}
|
|
||||||
const session = await useAuth().getSessionAndBump(sid);
|
|
||||||
|
|
||||||
await prisma.sessions.delete({
|
await prisma.sessions.delete({
|
||||||
where: { id: sessionId },
|
where: { id: sessionId },
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ export default defineEventHandler(async event => {
|
||||||
const validatedBody = updateListSchema.parse(body);
|
const validatedBody = updateListSchema.parse(body);
|
||||||
|
|
||||||
const list = await prisma.lists.findUnique({
|
const list = await prisma.lists.findUnique({
|
||||||
|
relationLoadStrategy: 'join',
|
||||||
where: { id: validatedBody.list_id },
|
where: { id: validatedBody.list_id },
|
||||||
include: { list_items: true },
|
include: { list_items: true },
|
||||||
});
|
});
|
||||||
|
|
@ -100,6 +101,7 @@ export default defineEventHandler(async event => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return tx.lists.findUnique({
|
return tx.lists.findUnique({
|
||||||
|
relationLoadStrategy: 'join',
|
||||||
where: { id: list.id },
|
where: { id: list.id },
|
||||||
include: { list_items: true },
|
include: { list_items: true },
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -40,47 +40,57 @@ export default defineEventHandler(async event => {
|
||||||
|
|
||||||
const validatedBody = createListSchema.parse(parsedBody);
|
const validatedBody = createListSchema.parse(parsedBody);
|
||||||
|
|
||||||
const result = await prisma.$transaction(async tx => {
|
try {
|
||||||
const existing = await tx.lists.findFirst({
|
const result = await prisma.$transaction(async tx => {
|
||||||
where: { user_id: userId, name: validatedBody.name }
|
// App-level guard for a clean 409 message
|
||||||
|
const existing = await tx.lists.findFirst({
|
||||||
|
where: { user_id: userId, name: validatedBody.name },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (existing) {
|
||||||
|
throw createError({ statusCode: 409, message: 'A list with this name already exists' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const now = new Date();
|
||||||
|
const newList = await tx.lists.create({
|
||||||
|
data: {
|
||||||
|
id: uuidv7(),
|
||||||
|
user_id: userId,
|
||||||
|
name: validatedBody.name,
|
||||||
|
description: validatedBody.description || null,
|
||||||
|
public: validatedBody.public || false,
|
||||||
|
updated_at: now,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (validatedBody.items && validatedBody.items.length > 0) {
|
||||||
|
await tx.list_items.createMany({
|
||||||
|
data: validatedBody.items.map(item => ({
|
||||||
|
id: uuidv7(),
|
||||||
|
list_id: newList.id,
|
||||||
|
tmdb_id: item.tmdb_id,
|
||||||
|
type: item.type,
|
||||||
|
})),
|
||||||
|
skipDuplicates: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return tx.lists.findUnique({
|
||||||
|
relationLoadStrategy: 'join',
|
||||||
|
where: { id: newList.id },
|
||||||
|
include: { list_items: true },
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (existing) {
|
return {
|
||||||
|
list: result,
|
||||||
|
message: 'List created successfully',
|
||||||
|
};
|
||||||
|
} catch (err: any) {
|
||||||
|
// DB-level safety net: catch unique constraint violation from @@unique([user_id, name])
|
||||||
|
if (err.code === 'P2002') {
|
||||||
throw createError({ statusCode: 409, message: 'A list with this name already exists' });
|
throw createError({ statusCode: 409, message: 'A list with this name already exists' });
|
||||||
}
|
}
|
||||||
|
throw err;
|
||||||
const now = new Date();
|
}
|
||||||
const newList = await tx.lists.create({
|
|
||||||
data: {
|
|
||||||
id: uuidv7(),
|
|
||||||
user_id: userId,
|
|
||||||
name: validatedBody.name,
|
|
||||||
description: validatedBody.description || null,
|
|
||||||
public: validatedBody.public || false,
|
|
||||||
updated_at: now,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (validatedBody.items && validatedBody.items.length > 0) {
|
|
||||||
await tx.list_items.createMany({
|
|
||||||
data: validatedBody.items.map(item => ({
|
|
||||||
id: uuidv7(),
|
|
||||||
list_id: newList.id,
|
|
||||||
tmdb_id: item.tmdb_id,
|
|
||||||
type: item.type, // Type is mapped here
|
|
||||||
})),
|
|
||||||
skipDuplicates: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return tx.lists.findUnique({
|
|
||||||
where: { id: newList.id },
|
|
||||||
include: { list_items: true },
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
list: result,
|
|
||||||
message: 'List created successfully',
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -144,27 +144,14 @@ export default defineEventHandler(async event => {
|
||||||
if (body.seasonId) whereClause.season_id = body.seasonId;
|
if (body.seasonId) whereClause.season_id = body.seasonId;
|
||||||
if (body.episodeId) whereClause.episode_id = body.episodeId;
|
if (body.episodeId) whereClause.episode_id = body.episodeId;
|
||||||
|
|
||||||
const itemsToDelete = await prisma.watch_history.findMany({
|
// Use deleteMany return count directly — no redundant findMany
|
||||||
where: whereClause,
|
const { count } = await prisma.watch_history.deleteMany({
|
||||||
});
|
|
||||||
|
|
||||||
if (itemsToDelete.length === 0) {
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
count: 0,
|
|
||||||
tmdbId,
|
|
||||||
episodeId: body.episodeId,
|
|
||||||
seasonId: body.seasonId,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
await prisma.watch_history.deleteMany({
|
|
||||||
where: whereClause,
|
where: whereClause,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
count: itemsToDelete.length,
|
count,
|
||||||
tmdbId,
|
tmdbId,
|
||||||
episodeId: body.episodeId,
|
episodeId: body.episodeId,
|
||||||
seasonId: body.seasonId,
|
seasonId: body.seasonId,
|
||||||
|
|
|
||||||
|
|
@ -40,13 +40,19 @@ export function useAuth() {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const expiryDate = new Date(now.getTime() + SESSION_EXPIRY_MS);
|
const expiryDate = new Date(now.getTime() + SESSION_EXPIRY_MS);
|
||||||
|
|
||||||
// Cleanup existing session for this device
|
// Atomic upsert — backed by @@unique([user, device]) in schema
|
||||||
await prisma.sessions.deleteMany({
|
return await prisma.sessions.upsert({
|
||||||
where: { user, device },
|
where: {
|
||||||
});
|
sessions_user_device_unique: { user, device },
|
||||||
|
},
|
||||||
return await prisma.sessions.create({
|
update: {
|
||||||
data: {
|
id: uuidv7(),
|
||||||
|
user_agent: userAgent,
|
||||||
|
created_at: now,
|
||||||
|
accessed_at: now,
|
||||||
|
expires_at: expiryDate,
|
||||||
|
},
|
||||||
|
create: {
|
||||||
id: uuidv7(),
|
id: uuidv7(),
|
||||||
user,
|
user,
|
||||||
device,
|
device,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue