update formating and schemas

This commit is contained in:
Pas 2025-03-12 22:34:56 -06:00
parent c9bb651995
commit 950c1e051a
7 changed files with 163 additions and 156 deletions

1
.gitignore vendored
View file

@ -7,3 +7,4 @@ dist
.env .env
.vscode .vscode
.metrics.json .metrics.json
.metrics.json

View file

@ -5,7 +5,7 @@ const bookmarkMetaSchema = z.object({
title: z.string(), title: z.string(),
year: z.number().optional(), year: z.number().optional(),
poster: z.string().optional(), poster: z.string().optional(),
type: z.enum(['movie', 'tv']), type: z.enum(['movie', 'show']),
}); });
const bookmarkDataSchema = z.object({ const bookmarkDataSchema = z.object({
@ -33,7 +33,6 @@ export default defineEventHandler(async (event) => {
return bookmarks.map(bookmark => ({ return bookmarks.map(bookmark => ({
tmdbId: bookmark.tmdb_id, tmdbId: bookmark.tmdb_id,
userId: bookmark.user_id,
meta: bookmark.meta, meta: bookmark.meta,
updatedAt: bookmark.updated_at updatedAt: bookmark.updated_at
})); }));
@ -68,7 +67,6 @@ export default defineEventHandler(async (event) => {
results.push({ results.push({
tmdbId: bookmark.tmdb_id, tmdbId: bookmark.tmdb_id,
userId: bookmark.user_id,
meta: bookmark.meta, meta: bookmark.meta,
updatedAt: bookmark.updated_at updatedAt: bookmark.updated_at
}); });
@ -111,7 +109,6 @@ export default defineEventHandler(async (event) => {
return { return {
tmdbId: bookmark.tmdb_id, tmdbId: bookmark.tmdb_id,
userId: bookmark.user_id,
meta: bookmark.meta, meta: bookmark.meta,
updatedAt: bookmark.updated_at updatedAt: bookmark.updated_at
}; };

View file

@ -1,3 +1,13 @@
import { useAuth } from '~/utils/auth';
import { z } from 'zod';
const bookmarkMetaSchema = z.object({
title: z.string(),
year: z.number(),
poster: z.string().optional(),
type: z.enum(['movie', 'show'])
});
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
const userId = getRouterParam(event, 'id') const userId = getRouterParam(event, 'id')
const tmdbId = getRouterParam(event, 'tmdbid') const tmdbId = getRouterParam(event, 'tmdbid')
@ -12,34 +22,35 @@ export default defineEventHandler(async (event) => {
} }
if (event.method === "POST") { if (event.method === "POST") {
const body = await readBody(event); const body = await readBody(event);
const bookmark = await prisma.bookmarks.create({ const validatedBody = bookmarkMetaSchema.parse(body);
data: {
user_id: session.user, const bookmark = await prisma.bookmarks.create({
tmdb_id: tmdbId, data: {
meta: body.meta, user_id: session.user,
updated_at: new Date()
}
});
return {
tmdbId: bookmark.tmdb_id,
userId: bookmark.user_id,
meta: bookmark.meta,
updatedAt: bookmark.updated_at
};
} else if (event.method === "DELETE") {
await prisma.bookmarks.delete({
where: {
tmdb_id_user_id: {
tmdb_id: tmdbId, tmdb_id: tmdbId,
user_id: session.user meta: validatedBody,
updated_at: new Date()
} }
} });
});
return {
return { success: true, tmdbId }; tmdbId: bookmark.tmdb_id,
} meta: bookmark.meta,
updatedAt: bookmark.updated_at
};
} else if (event.method === "DELETE") {
await prisma.bookmarks.delete({
where: {
tmdb_id_user_id: {
tmdb_id: tmdbId,
user_id: session.user
}
}
});
return { success: true, tmdbId };
}
throw createError({ throw createError({
statusCode: 405, statusCode: 405,

View file

@ -4,16 +4,16 @@ import { randomUUID } from 'crypto';
const progressMetaSchema = z.object({ const progressMetaSchema = z.object({
title: z.string(), title: z.string(),
year: z.number().optional(),
poster: z.string().optional(), poster: z.string().optional(),
type: z.enum(['movie', 'tv', 'show']), type: z.enum(['movie', 'show'])
year: z.number().optional()
}); });
const progressItemSchema = z.object({ const progressItemSchema = z.object({
meta: progressMetaSchema, meta: progressMetaSchema,
tmdbId: z.string(), tmdbId: z.string(),
duration: z.number().transform((n) => Math.round(n)), duration: z.number().transform((n) => n.toString()),
watched: z.number().transform((n) => Math.round(n)), watched: z.number().transform((n) => n.toString()),
seasonId: z.string().optional(), seasonId: z.string().optional(),
episodeId: z.string().optional(), episodeId: z.string().optional(),
seasonNumber: z.number().optional(), seasonNumber: z.number().optional(),
@ -45,7 +45,7 @@ export default defineEventHandler(async (event) => {
if (session.user !== userId) { if (session.user !== userId) {
throw createError({ throw createError({
statusCode: 403, statusCode: 403,
message: 'Cannot modify user other than yourself' message: 'Cannot access other user information'
}); });
} }
@ -57,15 +57,18 @@ export default defineEventHandler(async (event) => {
return items.map(item => ({ return items.map(item => ({
id: item.id, id: item.id,
tmdbId: item.tmdb_id, tmdbId: item.tmdb_id,
userId: item.user_id, episode: {
seasonId: item.season_id, id: item.episode_id || null,
episodeId: item.episode_id, number: item.episode_number || null
seasonNumber: item.season_number, },
episodeNumber: item.episode_number, season: {
id: item.season_id || null,
number: item.season_number || null
},
meta: item.meta, meta: item.meta,
duration: Number(item.duration), duration: item.duration.toString(),
watched: Number(item.watched), watched: item.watched.toString(),
updatedAt: item.updated_at updatedAt: item.updated_at.toISOString()
})); }));
} }

View file

@ -4,21 +4,21 @@ import { randomUUID } from 'crypto';
const progressMetaSchema = z.object({ const progressMetaSchema = z.object({
title: z.string(), title: z.string(),
poster: z.string().optional(), type: z.enum(['movie', 'show']),
type: z.enum(['movie', 'tv', 'show']), year: z.number(),
year: z.number().optional() poster: z.string().optional()
}); });
const progressItemSchema = z.object({ const progressItemSchema = z.object({
meta: progressMetaSchema, meta: progressMetaSchema,
tmdbId: z.string(), tmdbId: z.string(),
duration: z.number().transform((n) => Math.round(n)), duration: z.number(),
watched: z.number().transform((n) => Math.round(n)), watched: z.number(),
seasonId: z.string().optional(), seasonId: z.string().optional(),
episodeId: z.string().optional(), episodeId: z.string().optional(),
seasonNumber: z.number().optional(), seasonNumber: z.number().optional(),
episodeNumber: z.number().optional(), episodeNumber: z.number().optional(),
updatedAt: z.string().datetime({ offset: true }).optional(), updatedAt: z.string().datetime({ offset: true }).optional()
}); });
// 13th July 2021 - movie-web epoch // 13th July 2021 - movie-web epoch
@ -90,50 +90,61 @@ export default defineEventHandler(async (event) => {
} }
} }
for (const newItem of newItems) { // Create new items
for (const item of newItems) {
itemsToUpsert.push({ itemsToUpsert.push({
id: randomUUID(), id: randomUUID(),
tmdb_id: newItem.tmdbId, tmdb_id: item.tmdbId,
user_id: userId, user_id: userId,
season_id: newItem.seasonId || null, season_id: item.seasonId || null,
episode_id: newItem.episodeId || null, episode_id: item.episodeId || null,
season_number: newItem.seasonNumber || null, season_number: item.seasonNumber || null,
episode_number: newItem.episodeNumber || null, episode_number: item.episodeNumber || null,
duration: BigInt(newItem.duration), duration: BigInt(item.duration),
watched: BigInt(newItem.watched), watched: BigInt(item.watched),
meta: newItem.meta, meta: item.meta,
updated_at: defaultAndCoerceDateTime(newItem.updatedAt) updated_at: defaultAndCoerceDateTime(item.updatedAt)
}); });
} }
const result = await prisma.$transaction( // Upsert all items
itemsToUpsert.map(item => const results = [];
prisma.progress_items.upsert({ for (const item of itemsToUpsert) {
where: { const result = await prisma.progress_items.upsert({
id: item.id where: {
}, tmdb_id_user_id_season_id_episode_id: {
update: { tmdb_id: item.tmdb_id,
watched: item.watched, user_id: item.user_id,
duration: item.duration, season_id: item.season_id,
meta: item.meta, episode_id: item.episode_id
updated_at: item.updated_at }
}, },
create: item create: item,
}) update: {
) duration: item.duration,
); watched: item.watched,
meta: item.meta,
updated_at: item.updated_at
}
});
results.push({
id: result.id,
tmdbId: result.tmdb_id,
episode: {
id: result.episode_id || null,
number: result.episode_number || null
},
season: {
id: result.season_id || null,
number: result.season_number || null
},
meta: result.meta,
duration: result.duration.toString(),
watched: result.watched.toString(),
updatedAt: result.updated_at.toISOString()
});
}
return result.map(item => ({ return results;
id: item.id,
tmdbId: item.tmdb_id,
userId: item.user_id,
seasonId: item.season_id,
episodeId: item.episode_id,
seasonNumber: item.season_number,
episodeNumber: item.episode_number,
meta: item.meta,
duration: Number(item.duration),
watched: Number(item.watched),
updatedAt: item.updated_at
}));
}); });

View file

@ -18,12 +18,10 @@ export default defineEventHandler(async (event) => {
return sessions.map(s => ({ return sessions.map(s => ({
id: s.id, id: s.id,
user: s.user, userId: s.user,
createdAt: s.created_at, createdAt: s.created_at.toISOString(),
accessedAt: s.accessed_at, accessedAt: s.accessed_at.toISOString(),
expiresAt: s.expires_at,
device: s.device, device: s.device,
userAgent: s.user_agent, userAgent: s.user_agent
current: s.id === session.id
})); }));
}); });

View file

@ -2,12 +2,12 @@ import { useAuth } from '~/utils/auth';
import { z } from 'zod'; import { z } from 'zod';
const userSettingsSchema = z.object({ const userSettingsSchema = z.object({
application_theme: z.string().optional(), applicationTheme: z.string().nullable().optional(),
application_language: z.string().optional(), applicationLanguage: z.string(),
default_subtitle_language: z.string().optional(), defaultSubtitleLanguage: z.string().nullable().optional(),
proxy_urls: z.array(z.string()).optional(), proxyUrls: z.array(z.string()).nullable().optional(),
trakt_key: z.string().optional(), traktKey: z.string().nullable().optional(),
febbox_key: z.string().optional() febboxKey: z.string().nullable().optional()
}); });
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
@ -22,46 +22,60 @@ export default defineEventHandler(async (event) => {
}); });
} }
if (event.method === 'GET') {
const settings = await prisma.user_settings.findUnique({
where: { id: userId }
});
return {
id: userId,
applicationTheme: settings?.application_theme || null,
applicationLanguage: settings?.application_language || 'en',
defaultSubtitleLanguage: settings?.default_subtitle_language || null,
proxyUrls: settings?.proxy_urls || null,
traktKey: settings?.trakt_key || null,
febboxKey: settings?.febbox_key || null
};
}
if (event.method === 'PUT') { if (event.method === 'PUT') {
try { try {
const body = await readBody(event); const body = await readBody(event);
const validatedSettings = userSettingsSchema.parse(body); const validatedBody = userSettingsSchema.parse(body);
const existingSettings = await prisma.user_settings.findUnique({ const data = {
where: { id: userId } application_theme: validatedBody.applicationTheme,
application_language: validatedBody.applicationLanguage,
default_subtitle_language: validatedBody.defaultSubtitleLanguage ?? null,
proxy_urls: validatedBody.proxyUrls ?? null,
trakt_key: validatedBody.traktKey ?? null,
febbox_key: validatedBody.febboxKey ?? null
};
const settings = await prisma.user_settings.upsert({
where: { id: userId },
update: data,
create: {
id: userId,
...data
}
}); });
let settings;
if (existingSettings) {
settings = await prisma.user_settings.update({
where: { id: userId },
data: validatedSettings
});
} else {
settings = await prisma.user_settings.create({
data: {
id: userId,
...validatedSettings
}
});
}
return { return {
settings: { id: userId,
applicationTheme: settings.application_theme, applicationTheme: settings.application_theme,
applicationLanguage: settings.application_language, applicationLanguage: settings.application_language,
defaultSubtitleLanguage: settings.default_subtitle_language, defaultSubtitleLanguage: settings.default_subtitle_language,
proxyUrls: settings.proxy_urls, proxyUrls: settings.proxy_urls,
traktKey: settings.trakt_key, traktKey: settings.trakt_key,
febboxKey: settings.febbox_key febboxKey: settings.febbox_key
}
}; };
} catch (error) { } catch (error) {
if (error instanceof z.ZodError) { if (error instanceof z.ZodError) {
throw createError({ throw createError({
statusCode: 400, statusCode: 400,
message: 'Invalid settings data' message: 'Invalid settings data',
cause: error.errors
}); });
} }
@ -70,34 +84,6 @@ export default defineEventHandler(async (event) => {
message: 'Failed to update settings' message: 'Failed to update settings'
}); });
} }
} else if (event.method === 'GET') {
const settings = await prisma.user_settings.findUnique({
where: { id: userId }
});
if (!settings) {
return {
settings: {
applicationTheme: null,
applicationLanguage: null,
defaultSubtitleLanguage: null,
proxyUrls: [],
traktKey: null,
febboxKey: null
}
};
}
return {
settings: {
applicationTheme: settings.application_theme,
applicationLanguage: settings.application_language,
defaultSubtitleLanguage: settings.default_subtitle_language,
proxyUrls: settings.proxy_urls,
traktKey: settings.trakt_key,
febboxKey: settings.febbox_key
}
};
} }
throw createError({ throw createError({