import { useAuth } from '~/utils/auth'; import { z } from 'zod'; import { scopedLogger } from '~/utils/logger'; import { user_settings } from '@prisma/client'; const log = scopedLogger('user-settings'); const userSettingsSchema = z.object({ applicationTheme: z.string().nullable().optional(), applicationLanguage: z.string().optional().default('en'), defaultSubtitleLanguage: z.string().nullable().optional(), proxyUrls: z.array(z.string()).nullable().optional(), traktKey: z.string().nullable().optional(), febboxKey: z.string().nullable().optional(), realDebridKey: z.string().nullable().optional(), enableThumbnails: z.boolean().optional().default(false), enableAutoplay: z.boolean().optional().default(true), enableSkipCredits: z.boolean().optional().default(true), enableDiscover: z.boolean().optional().default(true), enableFeatured: z.boolean().optional().default(false), enableDetailsModal: z.boolean().optional().default(false), enableImageLogos: z.boolean().optional().default(true), enableCarouselView: z.boolean().optional().default(false), forceCompactEpisodeView: z.boolean().optional().default(false), sourceOrder: z.array(z.string()).optional().default([]), enableSourceOrder: z.boolean().optional().default(false), disabledSources: z.array(z.string()).optional().default([]), embedOrder: z.array(z.string()).optional().default([]), enableEmbedOrder: z.boolean().optional().default(false), disabledEmbeds: z.array(z.string()).optional().default([]), proxyTmdb: z.boolean().optional().default(false), enableLowPerformanceMode: z.boolean().optional().default(false), enableNativeSubtitles: z.boolean().optional().default(false), enableHoldToBoost: z.boolean().optional().default(false), homeSectionOrder: z.array(z.string()).optional().default([]), manualSourceSelection: z.boolean().optional().default(false), enableDoubleClickToSeek: z.boolean().optional().default(false), }); 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: 'Permission denied', }); } // First check if user exists const user = await prisma.users.findUnique({ where: { id: userId }, }); if (!user) { throw createError({ statusCode: 404, message: 'User not found', }); } if (event.method === 'GET') { try { const settings = await prisma.user_settings.findUnique({ where: { id: userId }, }) as unknown as user_settings | null; return { id: userId, applicationTheme: settings?.application_theme || null, applicationLanguage: settings?.application_language || 'en', defaultSubtitleLanguage: settings?.default_subtitle_language || null, proxyUrls: settings?.proxy_urls.length === 0 ? null : settings?.proxy_urls || null, traktKey: settings?.trakt_key || null, febboxKey: settings?.febbox_key || null, realDebridKey: settings?.real_debrid_key || null, enableThumbnails: settings?.enable_thumbnails ?? false, enableAutoplay: settings?.enable_autoplay ?? true, enableSkipCredits: settings?.enable_skip_credits ?? true, enableDiscover: settings?.enable_discover ?? true, enableFeatured: settings?.enable_featured ?? false, enableDetailsModal: settings?.enable_details_modal ?? false, enableImageLogos: settings?.enable_image_logos ?? true, enableCarouselView: settings?.enable_carousel_view ?? false, forceCompactEpisodeView: settings?.force_compact_episode_view ?? false, sourceOrder: settings?.source_order || [], enableSourceOrder: settings?.enable_source_order ?? false, disabledSources: settings?.disabled_sources || [], embedOrder: settings?.embed_order || [], enableEmbedOrder: settings?.enable_embed_order ?? false, disabledEmbeds: settings?.disabled_embeds || [], proxyTmdb: settings?.proxy_tmdb ?? false, enableLowPerformanceMode: settings?.enable_low_performance_mode ?? false, enableNativeSubtitles: settings?.enable_native_subtitles ?? false, enableHoldToBoost: settings?.enable_hold_to_boost ?? false, homeSectionOrder: settings?.home_section_order || [], manualSourceSelection: settings?.manual_source_selection ?? false, enableDoubleClickToSeek: settings?.enable_double_click_to_seek ?? false, }; } catch (error) { log.error('Failed to get user settings', { userId, error: error instanceof Error ? error.message : String(error), }); throw createError({ statusCode: 500, message: 'Failed to get user settings', }); } } if (event.method === 'PUT') { try { const body = await readBody(event); log.info('Updating user settings', { userId, body }); const validatedBody = userSettingsSchema.parse(body); const data = { application_theme: validatedBody.applicationTheme ?? null, application_language: validatedBody.applicationLanguage, default_subtitle_language: validatedBody.defaultSubtitleLanguage ?? null, proxy_urls: validatedBody.proxyUrls === null ? [] : validatedBody.proxyUrls || [], trakt_key: validatedBody.traktKey ?? null, febbox_key: validatedBody.febboxKey ?? null, real_debrid_key: validatedBody.realDebridKey ?? null, enable_thumbnails: validatedBody.enableThumbnails, enable_autoplay: validatedBody.enableAutoplay, enable_skip_credits: validatedBody.enableSkipCredits, enable_discover: validatedBody.enableDiscover, enable_featured: validatedBody.enableFeatured, enable_details_modal: validatedBody.enableDetailsModal, enable_image_logos: validatedBody.enableImageLogos, enable_carousel_view: validatedBody.enableCarouselView, force_compact_episode_view: validatedBody.forceCompactEpisodeView, source_order: validatedBody.sourceOrder || [], enable_source_order: validatedBody.enableSourceOrder, disabled_sources: validatedBody.disabledSources || [], embed_order: validatedBody.embedOrder || [], enable_embed_order: validatedBody.enableEmbedOrder, disabled_embeds: validatedBody.disabledEmbeds || [], proxy_tmdb: validatedBody.proxyTmdb, enable_low_performance_mode: validatedBody.enableLowPerformanceMode, enable_native_subtitles: validatedBody.enableNativeSubtitles, enable_hold_to_boost: validatedBody.enableHoldToBoost, home_section_order: validatedBody.homeSectionOrder || [], manual_source_selection: validatedBody.manualSourceSelection, enable_double_click_to_seek: validatedBody.enableDoubleClickToSeek, }; log.info('Preparing to upsert settings', { userId, data }); const settings = await prisma.user_settings.upsert({ where: { id: userId }, update: data, create: { id: userId, ...data, }, }) as unknown as user_settings; 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.length === 0 ? null : settings.proxy_urls, traktKey: settings.trakt_key, febboxKey: settings.febbox_key, realDebridKey: settings.real_debrid_key, enableThumbnails: settings.enable_thumbnails, enableAutoplay: settings.enable_autoplay, enableSkipCredits: settings.enable_skip_credits, enableDiscover: settings.enable_discover, enableFeatured: settings.enable_featured, enableDetailsModal: settings.enable_details_modal, enableImageLogos: settings.enable_image_logos, enableCarouselView: settings.enable_carousel_view, forceCompactEpisodeView: settings.force_compact_episode_view, sourceOrder: settings.source_order, enableSourceOrder: settings.enable_source_order, disabledSources: settings.disabled_sources, embedOrder: settings.embed_order, enableEmbedOrder: settings.enable_embed_order, disabledEmbeds: settings.disabled_embeds, proxyTmdb: settings.proxy_tmdb, enableLowPerformanceMode: settings.enable_low_performance_mode, enableNativeSubtitles: settings.enable_native_subtitles, enableHoldToBoost: settings.enable_hold_to_boost, homeSectionOrder: settings.home_section_order, manualSourceSelection: settings.manual_source_selection, enableDoubleClickToSeek: settings.enable_double_click_to_seek, }; } catch (error) { log.error('Failed to update user settings', { userId, error: error instanceof Error ? error.message : String(error), }); if (error instanceof z.ZodError) { throw createError({ statusCode: 400, message: 'Invalid settings data', cause: error.errors, }); } throw createError({ statusCode: 500, message: 'Failed to update settings', cause: error instanceof Error ? error.message : 'Unknown error', }); } } throw createError({ statusCode: 405, message: 'Method not allowed', }); });