mirror of
https://github.com/movie-web/backend.git
synced 2025-09-13 18:13:26 +00:00
Add progress importing endpoint
This commit is contained in:
@@ -6,8 +6,20 @@ import {
|
|||||||
import { StatusError } from '@/services/error';
|
import { StatusError } from '@/services/error';
|
||||||
import { handle } from '@/services/handler';
|
import { handle } from '@/services/handler';
|
||||||
import { makeRouter } from '@/services/router';
|
import { makeRouter } from '@/services/router';
|
||||||
|
import { randomUUID } from 'crypto';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
const progressItemSchema = z.object({
|
||||||
|
meta: progressMetaSchema,
|
||||||
|
tmdbId: z.string(),
|
||||||
|
duration: z.number(),
|
||||||
|
watched: z.number(),
|
||||||
|
seasonId: z.string().optional(),
|
||||||
|
episodeId: z.string().optional(),
|
||||||
|
seasonNumber: z.number().optional(),
|
||||||
|
episodeNumber: z.number().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
export const userProgressRouter = makeRouter((app) => {
|
export const userProgressRouter = makeRouter((app) => {
|
||||||
app.put(
|
app.put(
|
||||||
'/users/:uid/progress/:tmdbid',
|
'/users/:uid/progress/:tmdbid',
|
||||||
@@ -17,15 +29,7 @@ export const userProgressRouter = makeRouter((app) => {
|
|||||||
uid: z.string(),
|
uid: z.string(),
|
||||||
tmdbid: z.string(),
|
tmdbid: z.string(),
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: progressItemSchema,
|
||||||
meta: progressMetaSchema,
|
|
||||||
duration: z.number(),
|
|
||||||
watched: z.number(),
|
|
||||||
seasonId: z.string().optional(),
|
|
||||||
episodeId: z.string().optional(),
|
|
||||||
seasonNumber: z.number().optional(),
|
|
||||||
episodeNumber: z.number().optional(),
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
handle(async ({ auth, params, body, em }) => {
|
handle(async ({ auth, params, body, em }) => {
|
||||||
@@ -63,6 +67,83 @@ export const userProgressRouter = makeRouter((app) => {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
app.put(
|
||||||
|
'/users/:uid/progress/import',
|
||||||
|
{
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
uid: z.string(),
|
||||||
|
}),
|
||||||
|
body: z.array(progressItemSchema),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
handle(async ({ auth, params, body: newItems, em, req, limiter }) => {
|
||||||
|
await auth.assert();
|
||||||
|
|
||||||
|
if (auth.user.id !== params.uid)
|
||||||
|
throw new StatusError('Cannot modify user other than yourself', 403);
|
||||||
|
|
||||||
|
const existingItems = await em.find(ProgressItem, { userId: params.uid });
|
||||||
|
|
||||||
|
for (const newItem of newItems) {
|
||||||
|
const existingItem = existingItems.find(
|
||||||
|
(item) =>
|
||||||
|
item.tmdbId == newItem.tmdbId &&
|
||||||
|
item.seasonId == newItem.seasonId &&
|
||||||
|
item.episodeId == newItem.episodeId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (existingItem) {
|
||||||
|
if (existingItem.watched < newItem.watched) {
|
||||||
|
existingItem.updatedAt = new Date();
|
||||||
|
existingItem.watched = newItem.watched;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
existingItems.push({
|
||||||
|
id: randomUUID(),
|
||||||
|
duration: newItem.duration,
|
||||||
|
episodeId: newItem.episodeId,
|
||||||
|
episodeNumber: newItem.episodeNumber,
|
||||||
|
meta: newItem.meta,
|
||||||
|
seasonId: newItem.seasonId,
|
||||||
|
seasonNumber: newItem.seasonNumber,
|
||||||
|
tmdbId: newItem.tmdbId,
|
||||||
|
userId: params.uid,
|
||||||
|
watched: newItem.watched,
|
||||||
|
updatedAt: new Date(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const progressItems = await em.upsertMany(ProgressItem, existingItems);
|
||||||
|
|
||||||
|
await em.flush();
|
||||||
|
|
||||||
|
await limiter?.assertAndBump(req, {
|
||||||
|
id: 'progress_import',
|
||||||
|
max: 5,
|
||||||
|
window: '10m',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Construct a response that only has the items that were requested to be updated in the same order
|
||||||
|
// ! is used on find as the item *should* always exist if the code above works correctly
|
||||||
|
const newItemResponses = newItems
|
||||||
|
.map(
|
||||||
|
(newItem) =>
|
||||||
|
progressItems.find(
|
||||||
|
(item) =>
|
||||||
|
item.tmdbId == newItem.tmdbId &&
|
||||||
|
item.seasonId == newItem.seasonId &&
|
||||||
|
item.episodeId == newItem.episodeId,
|
||||||
|
)!,
|
||||||
|
)
|
||||||
|
.map(formatProgressItem);
|
||||||
|
|
||||||
|
return newItemResponses;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
app.delete(
|
app.delete(
|
||||||
'/users/:uid/progress/:tmdbid',
|
'/users/:uid/progress/:tmdbid',
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user