This commit is contained in:
FifthWit 2025-12-02 16:25:44 -06:00
parent e5f46e5396
commit ce112ff652
11 changed files with 2100 additions and 1313 deletions

1
.gitignore vendored
View file

@ -11,3 +11,4 @@ pnpm-lock.yaml
.metrics_weekly.json .metrics_weekly.json
.metrics_monthly.json .metrics_monthly.json
.metrics_daily.json .metrics_daily.json
generated

3257
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -15,10 +15,11 @@
"eslint-plugin-prettier": "^5.4.0", "eslint-plugin-prettier": "^5.4.0",
"nitropack": "latest", "nitropack": "latest",
"prettier": "^3.5.3", "prettier": "^3.5.3",
"prisma": "^6.4.1" "prisma": "^7.0.1"
}, },
"dependencies": { "dependencies": {
"@prisma/client": "^6.4.1", "@prisma/adapter-pg": "^7.0.1",
"@prisma/client": "^7.0.1",
"cheerio": "^1.0.0", "cheerio": "^1.0.0",
"dotenv": "^16.4.7", "dotenv": "^16.4.7",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",

8
prisma.config.ts Normal file
View file

@ -0,0 +1,8 @@
import 'dotenv/config';
import { defineConfig, env } from 'prisma/config';
export default defineConfig({
datasource: {
url: env('DATABASE_URL'),
},
});

View file

@ -1,11 +1,11 @@
generator client { generator client {
provider = "prisma-client-js" provider = "prisma-client"
output = "../node_modules/.prisma/client" output = "../generated"
moduleFormat = "esm"
} }
datasource db { datasource db {
provider = "postgresql" provider = "postgresql"
url = env("DATABASE_URL")
} }
model bookmarks { model bookmarks {
@ -136,7 +136,7 @@ model user_group_order {
user_id String @db.VarChar(255) user_id String @db.VarChar(255)
group_order String[] @default([]) group_order String[] @default([])
created_at DateTime @default(now()) @db.Timestamptz(0) created_at DateTime @default(now()) @db.Timestamptz(0)
updated_at DateTime @updatedAt @db.Timestamptz(0) updated_at DateTime @default(now()) @db.Timestamptz(0)
@@unique([user_id], map: "user_group_order_user_id_unique") @@unique([user_id], map: "user_group_order_user_id_unique")
} }

View file

@ -1,7 +1,5 @@
import { useAuth } from '#imports'; import { useAuth } from '#imports';
import { PrismaClient } from '@prisma/client'; import { prisma } from '#imports';
const prisma = new PrismaClient();
export default defineEventHandler(async event => { export default defineEventHandler(async event => {
const userId = event.context.params?.id; const userId = event.context.params?.id;

View file

@ -1,7 +1,5 @@
import { useAuth } from '#imports'; import { useAuth } from '#imports';
import { PrismaClient } from '@prisma/client'; import { prisma } from '#imports';
const prisma = new PrismaClient();
export default defineEventHandler(async event => { export default defineEventHandler(async event => {
const userId = event.context.params?.id; const userId = event.context.params?.id;

View file

@ -1,8 +1,6 @@
import { useAuth } from '#imports'; import { useAuth } from '#imports';
import { PrismaClient } from '@prisma/client';
import { z } from 'zod'; import { z } from 'zod';
import { prisma } from '#imports';
const prisma = new PrismaClient();
const listItemSchema = z.object({ const listItemSchema = z.object({
tmdb_id: z.string(), tmdb_id: z.string(),

View file

@ -1,9 +1,7 @@
import { useAuth } from '#imports'; import { useAuth } from '#imports';
import { PrismaClient } from '@prisma/client'; import { prisma } from '~/utils/prisma';
import { z } from 'zod'; import { z } from 'zod';
const prisma = new PrismaClient();
const listItemSchema = z.object({ const listItemSchema = z.object({
tmdb_id: z.string(), tmdb_id: z.string(),
type: z.enum(['movie', 'tv']), type: z.enum(['movie', 'tv']),

View file

@ -1,7 +1,8 @@
import { useAuth } from '~/utils/auth'; import { useAuth } from '~/utils/auth';
import { z } from 'zod'; import { z } from 'zod';
import { scopedLogger } from '~/utils/logger'; import { scopedLogger } from '~/utils/logger';
import { user_settings } from '@prisma/client'; import type { user_settings } from '@prisma/client';
import { prisma } from '~/utils/prisma';
const log = scopedLogger('user-settings'); const log = scopedLogger('user-settings');
@ -64,9 +65,9 @@ export default defineEventHandler(async event => {
if (event.method === 'GET') { if (event.method === 'GET') {
try { try {
const settings = await prisma.user_settings.findUnique({ const settings = (await prisma.user_settings.findUnique({
where: { id: userId }, where: { id: userId },
}) as unknown as user_settings | null; })) as unknown as user_settings | null;
return { return {
id: userId, id: userId,
@ -124,7 +125,7 @@ export default defineEventHandler(async event => {
application_theme: validatedBody.applicationTheme ?? null, application_theme: validatedBody.applicationTheme ?? null,
application_language: validatedBody.applicationLanguage, application_language: validatedBody.applicationLanguage,
default_subtitle_language: validatedBody.defaultSubtitleLanguage ?? null, default_subtitle_language: validatedBody.defaultSubtitleLanguage ?? null,
proxy_urls: validatedBody.proxyUrls === null ? [] : (validatedBody.proxyUrls || []), proxy_urls: validatedBody.proxyUrls === null ? [] : validatedBody.proxyUrls || [],
trakt_key: validatedBody.traktKey ?? null, trakt_key: validatedBody.traktKey ?? null,
febbox_key: validatedBody.febboxKey ?? null, febbox_key: validatedBody.febboxKey ?? null,
debrid_token: validatedBody.debridToken ?? null, debrid_token: validatedBody.debridToken ?? null,
@ -154,47 +155,81 @@ export default defineEventHandler(async event => {
}; };
const updateData: Partial<typeof createData> = {}; const updateData: Partial<typeof createData> = {};
if (Object.prototype.hasOwnProperty.call(body, 'applicationTheme')) updateData.application_theme = createData.application_theme; if (Object.prototype.hasOwnProperty.call(body, 'applicationTheme'))
if (Object.prototype.hasOwnProperty.call(body, 'applicationLanguage')) updateData.application_language = createData.application_language; updateData.application_theme = createData.application_theme;
if (Object.prototype.hasOwnProperty.call(body, 'defaultSubtitleLanguage')) updateData.default_subtitle_language = createData.default_subtitle_language; if (Object.prototype.hasOwnProperty.call(body, 'applicationLanguage'))
if (Object.prototype.hasOwnProperty.call(body, 'proxyUrls')) updateData.proxy_urls = createData.proxy_urls; updateData.application_language = createData.application_language;
if (Object.prototype.hasOwnProperty.call(body, 'traktKey')) updateData.trakt_key = createData.trakt_key; if (Object.prototype.hasOwnProperty.call(body, 'defaultSubtitleLanguage'))
if (Object.prototype.hasOwnProperty.call(body, 'febboxKey')) updateData.febbox_key = createData.febbox_key; updateData.default_subtitle_language = createData.default_subtitle_language;
if (Object.prototype.hasOwnProperty.call(body, 'debridToken')) updateData.debrid_token = createData.debrid_token; if (Object.prototype.hasOwnProperty.call(body, 'proxyUrls'))
if (Object.prototype.hasOwnProperty.call(body, 'debridService')) updateData.debrid_service = createData.debrid_service; updateData.proxy_urls = createData.proxy_urls;
if (Object.prototype.hasOwnProperty.call(body, 'enableThumbnails')) updateData.enable_thumbnails = createData.enable_thumbnails; if (Object.prototype.hasOwnProperty.call(body, 'traktKey'))
if (Object.prototype.hasOwnProperty.call(body, 'enableAutoplay')) updateData.enable_autoplay = createData.enable_autoplay; updateData.trakt_key = createData.trakt_key;
if (Object.prototype.hasOwnProperty.call(body, 'enableSkipCredits')) updateData.enable_skip_credits = createData.enable_skip_credits; if (Object.prototype.hasOwnProperty.call(body, 'febboxKey'))
if (Object.prototype.hasOwnProperty.call(body, 'enableDiscover')) updateData.enable_discover = createData.enable_discover; updateData.febbox_key = createData.febbox_key;
if (Object.prototype.hasOwnProperty.call(body, 'enableFeatured')) updateData.enable_featured = createData.enable_featured; if (Object.prototype.hasOwnProperty.call(body, 'debridToken'))
if (Object.prototype.hasOwnProperty.call(body, 'enableDetailsModal')) updateData.enable_details_modal = createData.enable_details_modal; updateData.debrid_token = createData.debrid_token;
if (Object.prototype.hasOwnProperty.call(body, 'enableImageLogos')) updateData.enable_image_logos = createData.enable_image_logos; if (Object.prototype.hasOwnProperty.call(body, 'debridService'))
if (Object.prototype.hasOwnProperty.call(body, 'enableCarouselView')) updateData.enable_carousel_view = createData.enable_carousel_view; updateData.debrid_service = createData.debrid_service;
if (Object.prototype.hasOwnProperty.call(body, 'forceCompactEpisodeView')) updateData.force_compact_episode_view = createData.force_compact_episode_view; if (Object.prototype.hasOwnProperty.call(body, 'enableThumbnails'))
if (Object.prototype.hasOwnProperty.call(body, 'sourceOrder')) updateData.source_order = createData.source_order; updateData.enable_thumbnails = createData.enable_thumbnails;
if (Object.prototype.hasOwnProperty.call(body, 'enableSourceOrder')) updateData.enable_source_order = createData.enable_source_order; if (Object.prototype.hasOwnProperty.call(body, 'enableAutoplay'))
if (Object.prototype.hasOwnProperty.call(body, 'disabledSources')) updateData.disabled_sources = createData.disabled_sources; updateData.enable_autoplay = createData.enable_autoplay;
if (Object.prototype.hasOwnProperty.call(body, 'embedOrder')) updateData.embed_order = createData.embed_order; if (Object.prototype.hasOwnProperty.call(body, 'enableSkipCredits'))
if (Object.prototype.hasOwnProperty.call(body, 'enableEmbedOrder')) updateData.enable_embed_order = createData.enable_embed_order; updateData.enable_skip_credits = createData.enable_skip_credits;
if (Object.prototype.hasOwnProperty.call(body, 'disabledEmbeds')) updateData.disabled_embeds = createData.disabled_embeds; if (Object.prototype.hasOwnProperty.call(body, 'enableDiscover'))
if (Object.prototype.hasOwnProperty.call(body, 'proxyTmdb')) updateData.proxy_tmdb = createData.proxy_tmdb; updateData.enable_discover = createData.enable_discover;
if (Object.prototype.hasOwnProperty.call(body, 'enableLowPerformanceMode')) updateData.enable_low_performance_mode = createData.enable_low_performance_mode; if (Object.prototype.hasOwnProperty.call(body, 'enableFeatured'))
if (Object.prototype.hasOwnProperty.call(body, 'enableNativeSubtitles')) updateData.enable_native_subtitles = createData.enable_native_subtitles; updateData.enable_featured = createData.enable_featured;
if (Object.prototype.hasOwnProperty.call(body, 'enableHoldToBoost')) updateData.enable_hold_to_boost = createData.enable_hold_to_boost; if (Object.prototype.hasOwnProperty.call(body, 'enableDetailsModal'))
if (Object.prototype.hasOwnProperty.call(body, 'homeSectionOrder')) updateData.home_section_order = createData.home_section_order; updateData.enable_details_modal = createData.enable_details_modal;
if (Object.prototype.hasOwnProperty.call(body, 'manualSourceSelection')) updateData.manual_source_selection = createData.manual_source_selection; if (Object.prototype.hasOwnProperty.call(body, 'enableImageLogos'))
if (Object.prototype.hasOwnProperty.call(body, 'enableDoubleClickToSeek')) updateData.enable_double_click_to_seek = createData.enable_double_click_to_seek; updateData.enable_image_logos = createData.enable_image_logos;
if (Object.prototype.hasOwnProperty.call(body, 'enableCarouselView'))
updateData.enable_carousel_view = createData.enable_carousel_view;
if (Object.prototype.hasOwnProperty.call(body, 'forceCompactEpisodeView'))
updateData.force_compact_episode_view = createData.force_compact_episode_view;
if (Object.prototype.hasOwnProperty.call(body, 'sourceOrder'))
updateData.source_order = createData.source_order;
if (Object.prototype.hasOwnProperty.call(body, 'enableSourceOrder'))
updateData.enable_source_order = createData.enable_source_order;
if (Object.prototype.hasOwnProperty.call(body, 'disabledSources'))
updateData.disabled_sources = createData.disabled_sources;
if (Object.prototype.hasOwnProperty.call(body, 'embedOrder'))
updateData.embed_order = createData.embed_order;
if (Object.prototype.hasOwnProperty.call(body, 'enableEmbedOrder'))
updateData.enable_embed_order = createData.enable_embed_order;
if (Object.prototype.hasOwnProperty.call(body, 'disabledEmbeds'))
updateData.disabled_embeds = createData.disabled_embeds;
if (Object.prototype.hasOwnProperty.call(body, 'proxyTmdb'))
updateData.proxy_tmdb = createData.proxy_tmdb;
if (Object.prototype.hasOwnProperty.call(body, 'enableLowPerformanceMode'))
updateData.enable_low_performance_mode = createData.enable_low_performance_mode;
if (Object.prototype.hasOwnProperty.call(body, 'enableNativeSubtitles'))
updateData.enable_native_subtitles = createData.enable_native_subtitles;
if (Object.prototype.hasOwnProperty.call(body, 'enableHoldToBoost'))
updateData.enable_hold_to_boost = createData.enable_hold_to_boost;
if (Object.prototype.hasOwnProperty.call(body, 'homeSectionOrder'))
updateData.home_section_order = createData.home_section_order;
if (Object.prototype.hasOwnProperty.call(body, 'manualSourceSelection'))
updateData.manual_source_selection = createData.manual_source_selection;
if (Object.prototype.hasOwnProperty.call(body, 'enableDoubleClickToSeek'))
updateData.enable_double_click_to_seek = createData.enable_double_click_to_seek;
log.info('Preparing to upsert settings', { userId, updateData, createData: { id: userId, ...createData } }); log.info('Preparing to upsert settings', {
userId,
updateData,
createData: { id: userId, ...createData },
});
const settings = await prisma.user_settings.upsert({ const settings = (await prisma.user_settings.upsert({
where: { id: userId }, where: { id: userId },
update: updateData, update: updateData,
create: { create: {
id: userId, id: userId,
...createData, ...createData,
}, },
}) as unknown as user_settings; })) as unknown as user_settings;
log.info('Settings updated successfully', { userId }); log.info('Settings updated successfully', { userId });

View file

@ -1,9 +1,14 @@
import { PrismaClient } from '@prisma/client'; import { PrismaPg } from '@prisma/adapter-pg';
import { PrismaClient } from '../../generated/client';
const adapter = new PrismaPg({
connectionString: process.env.DATABASE_URL,
});
const globalForPrisma = globalThis as unknown as { const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined; prisma: PrismaClient | undefined;
}; };
export const prisma = globalForPrisma.prisma ?? new PrismaClient(); export const prisma = new PrismaClient({ adapter });
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma; if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;