mirror of
https://github.com/p-stream/backend.git
synced 2026-03-11 17:55:35 +00:00
Reapply with fixes
I think I got it this time
This commit is contained in:
parent
2e27016e81
commit
96e9d832e5
10 changed files with 421 additions and 214 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -7,3 +7,4 @@ dist
|
|||
.env
|
||||
.vscode
|
||||
.metrics.json
|
||||
.metrics.json
|
||||
|
|
|
|||
|
|
@ -32,9 +32,18 @@ const metricsProviderSchema = z.object({
|
|||
const metricsProviderInputSchema = z.object({
|
||||
items: z.array(metricsProviderSchema).max(10).min(1),
|
||||
tool: z.string().optional(),
|
||||
batchId: z.string().optional(),
|
||||
});
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
// Handle both POST and PUT methods
|
||||
if (event.method !== 'POST' && event.method !== 'PUT') {
|
||||
throw createError({
|
||||
statusCode: 405,
|
||||
message: 'Method not allowed'
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
await ensureMetricsInitialized();
|
||||
|
||||
|
|
|
|||
2
server/routes/metrics/providers.put.ts
Normal file
2
server/routes/metrics/providers.put.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
// Redirect to the POST handler which now supports both methods
|
||||
export { default } from './providers.post';
|
||||
|
|
@ -5,7 +5,7 @@ const bookmarkMetaSchema = z.object({
|
|||
title: z.string(),
|
||||
year: z.number().optional(),
|
||||
poster: z.string().optional(),
|
||||
type: z.enum(['movie', 'tv']),
|
||||
type: z.enum(['movie', 'show']),
|
||||
});
|
||||
|
||||
const bookmarkDataSchema = z.object({
|
||||
|
|
@ -33,7 +33,6 @@ export default defineEventHandler(async (event) => {
|
|||
|
||||
return bookmarks.map(bookmark => ({
|
||||
tmdbId: bookmark.tmdb_id,
|
||||
userId: bookmark.user_id,
|
||||
meta: bookmark.meta,
|
||||
updatedAt: bookmark.updated_at
|
||||
}));
|
||||
|
|
@ -68,7 +67,6 @@ export default defineEventHandler(async (event) => {
|
|||
|
||||
results.push({
|
||||
tmdbId: bookmark.tmdb_id,
|
||||
userId: bookmark.user_id,
|
||||
meta: bookmark.meta,
|
||||
updatedAt: bookmark.updated_at
|
||||
});
|
||||
|
|
@ -111,7 +109,6 @@ export default defineEventHandler(async (event) => {
|
|||
|
||||
return {
|
||||
tmdbId: bookmark.tmdb_id,
|
||||
userId: bookmark.user_id,
|
||||
meta: bookmark.meta,
|
||||
updatedAt: bookmark.updated_at
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,25 @@
|
|||
import { useAuth } from '~/utils/auth';
|
||||
import { z } from 'zod';
|
||||
import { scopedLogger } from '~/utils/logger';
|
||||
|
||||
const log = scopedLogger('user-bookmarks');
|
||||
|
||||
const bookmarkMetaSchema = z.object({
|
||||
title: z.string(),
|
||||
year: z.number(),
|
||||
poster: z.string().optional(),
|
||||
type: z.enum(['movie', 'show'])
|
||||
});
|
||||
|
||||
// Support both formats: direct fields or nested under meta
|
||||
const bookmarkRequestSchema = z.object({
|
||||
meta: bookmarkMetaSchema.optional(),
|
||||
tmdbId: z.string().optional()
|
||||
});
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const userId = getRouterParam(event, 'id')
|
||||
const tmdbId = getRouterParam(event, 'tmdbid')
|
||||
const userId = getRouterParam(event, 'id');
|
||||
const tmdbId = getRouterParam(event, 'tmdbid');
|
||||
|
||||
const session = await useAuth().getCurrentSession();
|
||||
|
||||
|
|
@ -12,37 +31,81 @@ export default defineEventHandler(async (event) => {
|
|||
}
|
||||
|
||||
if (event.method === "POST") {
|
||||
const body = await readBody(event);
|
||||
const bookmark = await prisma.bookmarks.create({
|
||||
data: {
|
||||
user_id: session.user,
|
||||
tmdb_id: tmdbId,
|
||||
meta: body.meta,
|
||||
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,
|
||||
user_id: session.user
|
||||
try {
|
||||
const body = await readBody(event);
|
||||
log.info('Creating bookmark', { userId, tmdbId, body });
|
||||
|
||||
// Parse and validate the request body
|
||||
const validatedRequest = bookmarkRequestSchema.parse(body);
|
||||
|
||||
// Extract the meta data - either directly from meta field or from the root
|
||||
const metaData = validatedRequest.meta || body;
|
||||
|
||||
// Validate the meta data separately
|
||||
const validatedMeta = bookmarkMetaSchema.parse(metaData);
|
||||
|
||||
const bookmark = await prisma.bookmarks.create({
|
||||
data: {
|
||||
user_id: session.user,
|
||||
tmdb_id: tmdbId,
|
||||
meta: validatedMeta,
|
||||
updated_at: new Date()
|
||||
}
|
||||
});
|
||||
|
||||
log.info('Bookmark created successfully', { userId, tmdbId });
|
||||
|
||||
return {
|
||||
tmdbId: bookmark.tmdb_id,
|
||||
meta: bookmark.meta,
|
||||
updatedAt: bookmark.updated_at
|
||||
};
|
||||
} catch (error) {
|
||||
log.error('Failed to create bookmark', {
|
||||
userId,
|
||||
tmdbId,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
});
|
||||
|
||||
if (error instanceof z.ZodError) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: JSON.stringify(error.errors, null, 2)
|
||||
});
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
return { success: true, tmdbId };
|
||||
}
|
||||
} else if (event.method === "DELETE") {
|
||||
log.info('Deleting bookmark', { userId, tmdbId });
|
||||
|
||||
try {
|
||||
await prisma.bookmarks.delete({
|
||||
where: {
|
||||
tmdb_id_user_id: {
|
||||
tmdb_id: tmdbId,
|
||||
user_id: session.user
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
log.info('Bookmark deleted successfully', { userId, tmdbId });
|
||||
|
||||
return { success: true, tmdbId };
|
||||
} catch (error) {
|
||||
log.error('Failed to delete bookmark', {
|
||||
userId,
|
||||
tmdbId,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
});
|
||||
|
||||
// If bookmark doesn't exist, still return success
|
||||
return { success: true, tmdbId };
|
||||
}
|
||||
}
|
||||
|
||||
throw createError({
|
||||
statusCode: 405,
|
||||
message: 'Method not allowed'
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
78
server/routes/users/[id]/index.ts
Normal file
78
server/routes/users/[id]/index.ts
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
import { useAuth } from '~/utils/auth';
|
||||
import { z } from 'zod';
|
||||
import { scopedLogger } from '~/utils/logger';
|
||||
|
||||
const log = scopedLogger('user-profile');
|
||||
|
||||
const userProfileSchema = z.object({
|
||||
profile: z.object({
|
||||
icon: z.string(),
|
||||
colorA: z.string(),
|
||||
colorB: z.string()
|
||||
})
|
||||
});
|
||||
|
||||
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 other users'
|
||||
});
|
||||
}
|
||||
|
||||
if (event.method === 'PATCH') {
|
||||
try {
|
||||
const body = await readBody(event);
|
||||
log.info('Updating user profile', { userId, body });
|
||||
|
||||
const validatedBody = userProfileSchema.parse(body);
|
||||
|
||||
const user = await prisma.users.update({
|
||||
where: { id: userId },
|
||||
data: {
|
||||
profile: validatedBody.profile
|
||||
}
|
||||
});
|
||||
|
||||
log.info('User profile updated successfully', { userId });
|
||||
|
||||
return {
|
||||
id: user.id,
|
||||
publicKey: user.public_key,
|
||||
namespace: user.namespace,
|
||||
profile: user.profile,
|
||||
permissions: user.permissions,
|
||||
createdAt: user.created_at,
|
||||
lastLoggedIn: user.last_logged_in
|
||||
};
|
||||
} catch (error) {
|
||||
log.error('Failed to update user profile', {
|
||||
userId,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
});
|
||||
|
||||
if (error instanceof z.ZodError) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: 'Invalid profile data',
|
||||
cause: error.errors
|
||||
});
|
||||
}
|
||||
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
message: 'Failed to update user profile',
|
||||
cause: error instanceof Error ? error.message : 'Unknown error'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
throw createError({
|
||||
statusCode: 405,
|
||||
message: 'Method not allowed'
|
||||
});
|
||||
});
|
||||
|
|
@ -4,16 +4,16 @@ import { randomUUID } from 'crypto';
|
|||
|
||||
const progressMetaSchema = z.object({
|
||||
title: z.string(),
|
||||
year: z.number().optional(),
|
||||
poster: z.string().optional(),
|
||||
type: z.enum(['movie', 'tv', 'show']),
|
||||
year: z.number().optional()
|
||||
type: z.enum(['movie', 'show'])
|
||||
});
|
||||
|
||||
const progressItemSchema = z.object({
|
||||
meta: progressMetaSchema,
|
||||
tmdbId: z.string(),
|
||||
duration: z.number().transform((n) => Math.round(n)),
|
||||
watched: z.number().transform((n) => Math.round(n)),
|
||||
duration: z.number().transform((n) => n.toString()),
|
||||
watched: z.number().transform((n) => n.toString()),
|
||||
seasonId: z.string().optional(),
|
||||
episodeId: z.string().optional(),
|
||||
seasonNumber: z.number().optional(),
|
||||
|
|
@ -45,7 +45,7 @@ export default defineEventHandler(async (event) => {
|
|||
if (session.user !== userId) {
|
||||
throw createError({
|
||||
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 => ({
|
||||
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,
|
||||
episode: {
|
||||
id: item.episode_id || null,
|
||||
number: item.episode_number || null
|
||||
},
|
||||
season: {
|
||||
id: item.season_id || null,
|
||||
number: item.season_number || null
|
||||
},
|
||||
meta: item.meta,
|
||||
duration: Number(item.duration),
|
||||
watched: Number(item.watched),
|
||||
updatedAt: item.updated_at
|
||||
duration: item.duration.toString(),
|
||||
watched: item.watched.toString(),
|
||||
updatedAt: item.updated_at.toISOString()
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,24 +1,27 @@
|
|||
import { useAuth } from '~/utils/auth';
|
||||
import { z } from 'zod';
|
||||
import { randomUUID } from 'crypto';
|
||||
import { scopedLogger } from '~/utils/logger';
|
||||
|
||||
const log = scopedLogger('progress-import');
|
||||
|
||||
const progressMetaSchema = z.object({
|
||||
title: z.string(),
|
||||
poster: z.string().optional(),
|
||||
type: z.enum(['movie', 'tv', 'show']),
|
||||
year: z.number().optional()
|
||||
type: z.enum(['movie', 'show']),
|
||||
year: z.number(),
|
||||
poster: z.string().optional()
|
||||
});
|
||||
|
||||
const progressItemSchema = z.object({
|
||||
meta: progressMetaSchema,
|
||||
tmdbId: z.string(),
|
||||
duration: z.number().transform((n) => Math.round(n)),
|
||||
watched: z.number().transform((n) => Math.round(n)),
|
||||
duration: z.number(),
|
||||
watched: z.number(),
|
||||
seasonId: z.string().optional(),
|
||||
episodeId: z.string().optional(),
|
||||
seasonNumber: 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
|
||||
|
|
@ -49,91 +52,130 @@ export default defineEventHandler(async (event) => {
|
|||
});
|
||||
}
|
||||
|
||||
const body = await readBody(event);
|
||||
const validatedBody = z.array(progressItemSchema).parse(body);
|
||||
|
||||
const existingItems = await prisma.progress_items.findMany({
|
||||
where: { user_id: userId }
|
||||
});
|
||||
|
||||
const newItems = [...validatedBody];
|
||||
const itemsToUpsert = [];
|
||||
|
||||
for (const existingItem of existingItems) {
|
||||
const newItemIndex = newItems.findIndex(
|
||||
(item) =>
|
||||
item.tmdbId === existingItem.tmdb_id &&
|
||||
item.seasonId === existingItem.season_id &&
|
||||
item.episodeId === existingItem.episode_id
|
||||
);
|
||||
try {
|
||||
const body = await readBody(event);
|
||||
const validatedBody = z.array(progressItemSchema).parse(body);
|
||||
|
||||
if (newItemIndex > -1) {
|
||||
const newItem = newItems[newItemIndex];
|
||||
|
||||
if (Number(existingItem.watched) < newItem.watched) {
|
||||
itemsToUpsert.push({
|
||||
id: existingItem.id,
|
||||
tmdb_id: existingItem.tmdb_id,
|
||||
user_id: existingItem.user_id,
|
||||
season_id: existingItem.season_id,
|
||||
episode_id: existingItem.episode_id,
|
||||
season_number: existingItem.season_number,
|
||||
episode_number: existingItem.episode_number,
|
||||
duration: BigInt(newItem.duration),
|
||||
watched: BigInt(newItem.watched),
|
||||
meta: newItem.meta,
|
||||
updated_at: defaultAndCoerceDateTime(newItem.updatedAt)
|
||||
});
|
||||
}
|
||||
|
||||
newItems.splice(newItemIndex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (const newItem of newItems) {
|
||||
itemsToUpsert.push({
|
||||
id: randomUUID(),
|
||||
tmdb_id: newItem.tmdbId,
|
||||
user_id: userId,
|
||||
season_id: newItem.seasonId || null,
|
||||
episode_id: newItem.episodeId || null,
|
||||
season_number: newItem.seasonNumber || null,
|
||||
episode_number: newItem.episodeNumber || null,
|
||||
duration: BigInt(newItem.duration),
|
||||
watched: BigInt(newItem.watched),
|
||||
meta: newItem.meta,
|
||||
updated_at: defaultAndCoerceDateTime(newItem.updatedAt)
|
||||
const existingItems = await prisma.progress_items.findMany({
|
||||
where: { user_id: userId }
|
||||
});
|
||||
|
||||
const newItems = [...validatedBody];
|
||||
const itemsToUpsert = [];
|
||||
|
||||
for (const existingItem of existingItems) {
|
||||
const newItemIndex = newItems.findIndex(
|
||||
(item) =>
|
||||
item.tmdbId === existingItem.tmdb_id &&
|
||||
item.seasonId === (existingItem.season_id === '\n' ? null : existingItem.season_id) &&
|
||||
item.episodeId === (existingItem.episode_id === '\n' ? null : existingItem.episode_id)
|
||||
);
|
||||
|
||||
if (newItemIndex > -1) {
|
||||
const newItem = newItems[newItemIndex];
|
||||
|
||||
if (Number(existingItem.watched) < newItem.watched) {
|
||||
const isMovie = newItem.meta.type === 'movie';
|
||||
itemsToUpsert.push({
|
||||
id: existingItem.id,
|
||||
tmdb_id: existingItem.tmdb_id,
|
||||
user_id: existingItem.user_id,
|
||||
season_id: isMovie ? '\n' : existingItem.season_id,
|
||||
episode_id: isMovie ? '\n' : existingItem.episode_id,
|
||||
season_number: existingItem.season_number,
|
||||
episode_number: existingItem.episode_number,
|
||||
duration: BigInt(newItem.duration),
|
||||
watched: BigInt(newItem.watched),
|
||||
meta: newItem.meta,
|
||||
updated_at: defaultAndCoerceDateTime(newItem.updatedAt)
|
||||
});
|
||||
}
|
||||
|
||||
newItems.splice(newItemIndex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Create new items
|
||||
for (const item of newItems) {
|
||||
const isMovie = item.meta.type === 'movie';
|
||||
itemsToUpsert.push({
|
||||
id: randomUUID(),
|
||||
tmdb_id: item.tmdbId,
|
||||
user_id: userId,
|
||||
season_id: isMovie ? '\n' : (item.seasonId || null),
|
||||
episode_id: isMovie ? '\n' : (item.episodeId || null),
|
||||
season_number: isMovie ? null : item.seasonNumber,
|
||||
episode_number: isMovie ? null : item.episodeNumber,
|
||||
duration: BigInt(item.duration),
|
||||
watched: BigInt(item.watched),
|
||||
meta: item.meta,
|
||||
updated_at: defaultAndCoerceDateTime(item.updatedAt)
|
||||
});
|
||||
}
|
||||
|
||||
// Upsert all items
|
||||
const results = [];
|
||||
for (const item of itemsToUpsert) {
|
||||
try {
|
||||
const result = await prisma.progress_items.upsert({
|
||||
where: {
|
||||
tmdb_id_user_id_season_id_episode_id: {
|
||||
tmdb_id: item.tmdb_id,
|
||||
user_id: item.user_id,
|
||||
season_id: item.season_id,
|
||||
episode_id: item.episode_id
|
||||
}
|
||||
},
|
||||
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 === '\n' ? null : result.episode_id,
|
||||
number: result.episode_number
|
||||
},
|
||||
season: {
|
||||
id: result.season_id === '\n' ? null : result.season_id,
|
||||
number: result.season_number
|
||||
},
|
||||
meta: result.meta,
|
||||
duration: result.duration.toString(),
|
||||
watched: result.watched.toString(),
|
||||
updatedAt: result.updated_at.toISOString()
|
||||
});
|
||||
} catch (error) {
|
||||
log.error('Failed to upsert progress item', {
|
||||
userId,
|
||||
tmdbId: item.tmdb_id,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
} catch (error) {
|
||||
log.error('Failed to import progress', {
|
||||
userId,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
});
|
||||
|
||||
if (error instanceof z.ZodError) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: 'Invalid progress data',
|
||||
cause: error.errors
|
||||
});
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
const result = await prisma.$transaction(
|
||||
itemsToUpsert.map(item =>
|
||||
prisma.progress_items.upsert({
|
||||
where: {
|
||||
id: item.id
|
||||
},
|
||||
update: {
|
||||
watched: item.watched,
|
||||
duration: item.duration,
|
||||
meta: item.meta,
|
||||
updated_at: item.updated_at
|
||||
},
|
||||
create: item
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
return result.map(item => ({
|
||||
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
|
||||
}));
|
||||
});
|
||||
|
|
@ -18,12 +18,10 @@ export default defineEventHandler(async (event) => {
|
|||
|
||||
return sessions.map(s => ({
|
||||
id: s.id,
|
||||
user: s.user,
|
||||
createdAt: s.created_at,
|
||||
accessedAt: s.accessed_at,
|
||||
expiresAt: s.expires_at,
|
||||
userId: s.user,
|
||||
createdAt: s.created_at.toISOString(),
|
||||
accessedAt: s.accessed_at.toISOString(),
|
||||
device: s.device,
|
||||
userAgent: s.user_agent,
|
||||
current: s.id === session.id
|
||||
userAgent: s.user_agent
|
||||
}));
|
||||
});
|
||||
|
|
@ -1,13 +1,16 @@
|
|||
import { useAuth } from '~/utils/auth';
|
||||
import { z } from 'zod';
|
||||
import { scopedLogger } from '~/utils/logger';
|
||||
|
||||
const log = scopedLogger('user-settings');
|
||||
|
||||
const userSettingsSchema = z.object({
|
||||
application_theme: z.string().optional(),
|
||||
application_language: z.string().optional(),
|
||||
default_subtitle_language: z.string().optional(),
|
||||
proxy_urls: z.array(z.string()).optional(),
|
||||
trakt_key: z.string().optional(),
|
||||
febbox_key: z.string().optional()
|
||||
applicationTheme: z.string().nullable().optional(),
|
||||
applicationLanguage: z.string(),
|
||||
defaultSubtitleLanguage: z.string().nullable().optional(),
|
||||
proxyUrls: z.array(z.string()).nullable().optional(),
|
||||
traktKey: z.string().nullable().optional(),
|
||||
febboxKey: z.string().nullable().optional()
|
||||
});
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
|
|
@ -22,82 +25,93 @@ export default defineEventHandler(async (event) => {
|
|||
});
|
||||
}
|
||||
|
||||
if (event.method === 'PUT') {
|
||||
try {
|
||||
const body = await readBody(event);
|
||||
const validatedSettings = userSettingsSchema.parse(body);
|
||||
|
||||
const existingSettings = await prisma.user_settings.findUnique({
|
||||
where: { id: userId }
|
||||
});
|
||||
|
||||
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 {
|
||||
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
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: 'Invalid settings data'
|
||||
});
|
||||
}
|
||||
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
message: 'Failed to update settings'
|
||||
});
|
||||
}
|
||||
} else if (event.method === 'GET') {
|
||||
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: {
|
||||
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') {
|
||||
try {
|
||||
const body = await readBody(event);
|
||||
log.info('Updating user settings', { userId, body });
|
||||
|
||||
const validatedBody = userSettingsSchema.parse(body);
|
||||
|
||||
// Handle proxyUrls properly - ensure it's an array or empty array when null
|
||||
const proxyUrls = validatedBody.proxyUrls === null ? [] : (validatedBody.proxyUrls || []);
|
||||
|
||||
const data = {
|
||||
application_theme: validatedBody.applicationTheme ?? null,
|
||||
application_language: validatedBody.applicationLanguage,
|
||||
default_subtitle_language: validatedBody.defaultSubtitleLanguage ?? null,
|
||||
proxy_urls: proxyUrls,
|
||||
trakt_key: validatedBody.traktKey ?? null,
|
||||
febbox_key: validatedBody.febboxKey ?? null
|
||||
};
|
||||
|
||||
log.info('Preparing to upsert settings', {
|
||||
userId,
|
||||
data: { ...data, proxy_urls: Array.isArray(data.proxy_urls) ? data.proxy_urls.length : 'not an array' }
|
||||
});
|
||||
|
||||
const settings = await prisma.user_settings.upsert({
|
||||
where: { id: userId },
|
||||
update: data,
|
||||
create: {
|
||||
id: userId,
|
||||
...data
|
||||
}
|
||||
});
|
||||
|
||||
log.info('Settings updated successfully', { userId });
|
||||
|
||||
return {
|
||||
id: userId,
|
||||
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
|
||||
};
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
log.error('Validation error in settings update', {
|
||||
userId,
|
||||
errors: error.errors
|
||||
});
|
||||
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: 'Invalid settings data',
|
||||
cause: error.errors
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Log the specific error for debugging
|
||||
log.error('Failed to update settings', {
|
||||
userId,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
stack: error instanceof Error ? error.stack : undefined
|
||||
});
|
||||
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
message: 'Failed to update settings',
|
||||
cause: error instanceof Error ? error.message : 'Unknown error'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
throw createError({
|
||||
|
|
|
|||
Loading…
Reference in a new issue