@serifpersia/dango 3.6.7 → 3.6.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/README.md +1 -1
  2. package/client/dist/assets/{AnimeCard-C3P9zWi9.js → AnimeCard-DraA_TF9.js} +1 -1
  3. package/client/dist/assets/{AnimeInfo-Et6687Tl.js → AnimeInfo-BJ7pzs-P.js} +1 -1
  4. package/client/dist/assets/{AnimeInfoPage-DX4eNnlH.js → AnimeInfoPage-B_esUr5C.js} +2 -2
  5. package/client/dist/assets/{Asmr-CCdEWrFi.js → Asmr-Ckuh2xfW.js} +1 -1
  6. package/client/dist/assets/{AsmrResetProgressModal-DgEWBJOw.js → AsmrResetProgressModal-BS6UFJN4.js} +1 -1
  7. package/client/dist/assets/{AvSyncCalibrator-CQ2fjf2E.js → AvSyncCalibrator-DwHCd9yB.js} +1 -1
  8. package/client/dist/assets/{Home-DUkUTTTy.js → Home-DuM1EuvN.js} +1 -1
  9. package/client/dist/assets/{Insights-QCh4xW12.js → Insights-D3Pd9c9V.js} +1 -1
  10. package/client/dist/assets/{LibraryListPage-BoTxRqHz.js → LibraryListPage-Dm2yVn0-.js} +1 -1
  11. package/client/dist/assets/{ListeningList-DguKjI_t.js → ListeningList-D-zt6EXZ.js} +1 -1
  12. package/client/dist/assets/{Manga-HydR-L7o.js → Manga-JwprMKBk.js} +1 -1
  13. package/client/dist/assets/{MangaInfoPage-CBakEI9a.js → MangaInfoPage-DaeyJl2p.js} +1 -1
  14. package/client/dist/assets/{MangaReadPage-DwAR3iPy.js → MangaReadPage-COg0fHI9.js} +1 -1
  15. package/client/dist/assets/{MangaResetProgressModal-DGzl8vsO.js → MangaResetProgressModal-kguXv0_M.js} +1 -1
  16. package/client/dist/assets/{Mature-Pi0TxKuF.js → Mature-IHDaasrs.js} +1 -1
  17. package/client/dist/assets/{MediaCard--cuFxOCS.js → MediaCard-CpwsrXo5.js} +1 -1
  18. package/client/dist/assets/{Music-CQpyXTLH.js → Music-Dks4tPeK.js} +1 -1
  19. package/client/dist/assets/{OptionListMenu-B3D39dGS.js → OptionListMenu-11CcAsy8.js} +1 -1
  20. package/client/dist/assets/{Player-CsH4x_I_.js → Player-DQoEQkHL.js} +2 -2
  21. package/client/dist/assets/{PlayerSettings-DGxBsi3F.js → PlayerSettings-D4Ud4kv4.js} +1 -1
  22. package/client/dist/assets/{QueueOptionsButton-Djq4Cpex.js → QueueOptionsButton-B8PlPKjC.js} +1 -1
  23. package/client/dist/assets/{QueueRail-DOrYgNVX.js → QueueRail-B4Dok7U3.js} +1 -1
  24. package/client/dist/assets/{Radio-DF6g6odd.js → Radio-BHK6LSk4.js} +1 -1
  25. package/client/dist/assets/{ReadingList-DwYoBrNk.js → ReadingList-BCsxIz57.js} +1 -1
  26. package/client/dist/assets/{ResetProgressModal-CMF86mX6.js → ResetProgressModal-BXHxSfy-.js} +1 -1
  27. package/client/dist/assets/{Search-DFkhuk-x.js → Search-PK1ds07O.js} +1 -1
  28. package/client/dist/assets/{Settings-DBB2r0VQ.js → Settings-Bxk_x55l.js} +1 -1
  29. package/client/dist/assets/{SkeletonGrid-DJelJgAv.js → SkeletonGrid-CpJCZSRN.js} +1 -1
  30. package/client/dist/assets/{Trackers-DGYXFhYZ.js → Trackers-RieCh_84.js} +1 -1
  31. package/client/dist/assets/{Tv-C5MDGD2Z.js → Tv-DLytPJUj.js} +1 -1
  32. package/client/dist/assets/{TvInfo-DrKuK13u.js → TvInfo-C8djKebR.js} +1 -1
  33. package/client/dist/assets/{TvPopup-DtHilRDT.js → TvPopup-40JGGJDk.js} +1 -1
  34. package/client/dist/assets/{TvSearch-CzNxdu_w.js → TvSearch-A3hbospf.js} +1 -1
  35. package/client/dist/assets/{TvWatchlist-D7p4br6S.js → TvWatchlist-DuXjqmZM.js} +1 -1
  36. package/client/dist/assets/{Watchlist-DrOzswl2.js → Watchlist-B9wGBh0u.js} +1 -1
  37. package/client/dist/assets/{hls-BGCX7mJy.js → hls-CZ5X3Ya-.js} +1 -1
  38. package/client/dist/assets/{index-CnfZwsds.js → index-dRhc9-ol.js} +2 -2
  39. package/client/dist/assets/{useAnimeInfoData-DYXWBPF-.js → useAnimeInfoData-ZzDtLfvD.js} +1 -1
  40. package/client/dist/assets/{useAsmr-BbhQ59bk.js → useAsmr-CKOdHgol.js} +1 -1
  41. package/client/dist/assets/{useAsmrPopup-SymV1J-b.js → useAsmrPopup-SFHPhpcl.js} +1 -1
  42. package/client/dist/assets/{useMangaLibrary-DuesWQaE.js → useMangaLibrary-CKATe31P.js} +1 -1
  43. package/client/dist/assets/{useMangaPopup-D57OVGUr.js → useMangaPopup-C75QuYgY.js} +1 -1
  44. package/client/dist/assets/{useProviders-mpQb_dix.js → useProviders-CMtvZbEi.js} +1 -1
  45. package/client/dist/assets/{useTv-DGflCsSI.js → useTv-DUX4zYMr.js} +1 -1
  46. package/client/dist/assets/{useTvLibrary-BqeSmyc4.js → useTvLibrary-By4Gt-R_.js} +1 -1
  47. package/client/dist/index.html +1 -1
  48. package/package.json +2 -2
  49. package/server/dist/app-auth.js +1 -157
  50. package/server/dist/app-hono.js +7 -557
  51. package/server/dist/config.js +1 -174
  52. package/server/dist/db/drizzle.js +1 -16
  53. package/server/dist/db/schema-anime.js +1 -61
  54. package/server/dist/db/schema-asmr.js +1 -13
  55. package/server/dist/db/schema-manga.js +1 -31
  56. package/server/dist/db/schema-tv.js +1 -18
  57. package/server/dist/db.js +1 -217
  58. package/server/dist/discord-gateway.js +1 -385
  59. package/server/dist/discord-rpc.js +1 -413
  60. package/server/dist/github-sync.js +1 -508
  61. package/server/dist/google.js +1 -741
  62. package/server/dist/hono/asmr.js +1 -10
  63. package/server/dist/hono/auth.js +3 -26
  64. package/server/dist/hono/music.js +1 -209
  65. package/server/dist/hono/proxy.js +36 -783
  66. package/server/dist/hono/tv.js +5 -200
  67. package/server/dist/hono/watchlist.js +2 -338
  68. package/server/dist/lib/anilist.js +26 -1458
  69. package/server/dist/lib/anischedule.js +1 -191
  70. package/server/dist/lib/insights.js +1 -185
  71. package/server/dist/lib/ipc.js +1 -23
  72. package/server/dist/lib/kitsu.js +1 -517
  73. package/server/dist/lib/mal-import.js +1 -291
  74. package/server/dist/lib/mal.js +1 -957
  75. package/server/dist/lib/migration.js +1 -310
  76. package/server/dist/lib/offline-db.js +4 -384
  77. package/server/dist/lib/potoken.js +1 -135
  78. package/server/dist/lib/recommendations.service.js +1 -216
  79. package/server/dist/lib/temp-ids.js +1 -3
  80. package/server/dist/lib/tmdb.js +1 -72
  81. package/server/dist/lib/tracker/anilist-tracker.js +8 -245
  82. package/server/dist/lib/tracker/manga-sync.service.js +1 -773
  83. package/server/dist/lib/tracker/sync-state.js +1 -21
  84. package/server/dist/lib/tracker/sync.service.js +1 -476
  85. package/server/dist/lib/translate.js +3 -79
  86. package/server/dist/lib/ump.js +1 -106
  87. package/server/dist/lib/ytmusic.js +1 -162
  88. package/server/dist/logger.js +1 -16
  89. package/server/dist/providers/manga/manga.types.js +0 -1
  90. package/server/dist/providers/provider.interface.js +0 -1
  91. package/server/dist/providers/remote-loader.js +2 -614
  92. package/server/dist/providers/remote-types.js +1 -159
  93. package/server/dist/providers/title-matching.js +1 -226
  94. package/server/dist/providers/tv.types.js +0 -1
  95. package/server/dist/rclone.js +2 -120
  96. package/server/dist/repositories/asmr.repository.js +11 -46
  97. package/server/dist/repositories/insights.repository.js +11 -25
  98. package/server/dist/repositories/library.repository.js +3 -94
  99. package/server/dist/repositories/mal-cache.repository.js +1 -52
  100. package/server/dist/repositories/manga.repository.js +14 -58
  101. package/server/dist/repositories/notifications.repository.js +6 -52
  102. package/server/dist/repositories/queue.repository.js +5 -58
  103. package/server/dist/repositories/settings.repository.js +2 -29
  104. package/server/dist/repositories/shows-meta.repository.js +3 -56
  105. package/server/dist/repositories/temp-show-ids.repository.js +3 -63
  106. package/server/dist/repositories/tv.repository.js +11 -54
  107. package/server/dist/repositories/watched-episodes.repository.js +6 -55
  108. package/server/dist/repositories/watchlist.repository.js +3 -54
  109. package/server/dist/scripts/backfill-episode-duration.js +1 -42
  110. package/server/dist/server.js +1 -310
  111. package/server/dist/shipped-defaults.js +1 -6
  112. package/server/dist/sync-payload.js +1 -91
  113. package/server/dist/sync.js +3 -647
  114. package/server/dist/trpc/context.js +1 -7
  115. package/server/dist/trpc/errors.js +1 -15
  116. package/server/dist/trpc/index.js +1 -18
  117. package/server/dist/trpc/router.js +1 -49
  118. package/server/dist/trpc/routers/asmr-library.js +1 -112
  119. package/server/dist/trpc/routers/asmr-progress.js +1 -121
  120. package/server/dist/trpc/routers/asmr.js +1 -87
  121. package/server/dist/trpc/routers/auth.js +1 -194
  122. package/server/dist/trpc/routers/continue-watching.js +2 -58
  123. package/server/dist/trpc/routers/data.js +1 -1026
  124. package/server/dist/trpc/routers/discord.js +1 -285
  125. package/server/dist/trpc/routers/discovery.js +1 -11
  126. package/server/dist/trpc/routers/insights.js +1 -14
  127. package/server/dist/trpc/routers/manga-library.js +1 -263
  128. package/server/dist/trpc/routers/manga-progress.js +1 -169
  129. package/server/dist/trpc/routers/manga.js +1 -201
  130. package/server/dist/trpc/routers/music.js +1 -655
  131. package/server/dist/trpc/routers/notifications.js +1 -80
  132. package/server/dist/trpc/routers/progress.js +1 -109
  133. package/server/dist/trpc/routers/queue.js +1 -93
  134. package/server/dist/trpc/routers/radio.js +1 -145
  135. package/server/dist/trpc/routers/settings.js +1 -96
  136. package/server/dist/trpc/routers/tracker.js +1 -213
  137. package/server/dist/trpc/routers/tv-library.js +1 -135
  138. package/server/dist/trpc/routers/tv-progress.js +1 -159
  139. package/server/dist/trpc/routers/tv.js +1 -551
  140. package/server/dist/trpc/routers/watchlist.js +2 -549
  141. package/server/dist/trpc/validation.js +1 -258
  142. package/server/dist/utils/cache.utils.js +1 -25
  143. package/server/dist/utils/cookie.utils.js +1 -13
  144. package/server/dist/utils/db-utils.js +5 -17
  145. package/server/dist/utils/env.utils.js +5 -77
  146. package/server/dist/utils/http.utils.js +1 -82
  147. package/server/dist/utils/request-context.js +1 -2
  148. package/server/dist/utils/security.utils.js +1 -76
  149. package/server/package.json +3 -2
@@ -1,7 +1 @@
1
- import { getTokenFromHeaders, hasAppPassword, validateLanSession } from '../app-auth.js';
2
- export function buildTrpcContext(deps, authorization, cookie) {
3
- return {
4
- ...deps,
5
- lanAuthed: !hasAppPassword() || validateLanSession(getTokenFromHeaders(authorization, cookie)),
6
- };
7
- }
1
+ import{getTokenFromHeaders as t,hasAppPassword as o,validateLanSession as p}from"../app-auth.js";function n(e,r,a){return{...e,lanAuthed:!o()||p(t(r,a))}}export{n as buildTrpcContext};
@@ -1,15 +1 @@
1
- import { TRPCError } from '@trpc/server';
2
- export function authRequiredError(provider) {
3
- return new TRPCError({
4
- code: 'FORBIDDEN',
5
- message: 'AUTH_REQUIRED',
6
- cause: { code: 'AUTH_REQUIRED', provider },
7
- });
8
- }
9
- export function lanAuthRequiredError() {
10
- return new TRPCError({
11
- code: 'UNAUTHORIZED',
12
- message: 'LAN_AUTH_REQUIRED',
13
- cause: { code: 'LAN_AUTH_REQUIRED' },
14
- });
15
- }
1
+ import{TRPCError as r}from"@trpc/server";function E(e){return new r({code:"FORBIDDEN",message:"AUTH_REQUIRED",cause:{code:"AUTH_REQUIRED",provider:e}})}function R(){return new r({code:"UNAUTHORIZED",message:"LAN_AUTH_REQUIRED",cause:{code:"LAN_AUTH_REQUIRED"}})}export{E as authRequiredError,R as lanAuthRequiredError};
@@ -1,18 +1 @@
1
- import { initTRPC } from '@trpc/server';
2
- import { lanAuthRequiredError } from './errors.js';
3
- const t = initTRPC.context().create({
4
- errorFormatter: ({ shape, error }) => ({
5
- ...shape,
6
- data: {
7
- ...shape.data,
8
- cause: error.cause ?? null,
9
- },
10
- }),
11
- });
12
- export const router = t.router;
13
- const lanGuard = t.middleware(({ ctx, next }) => {
14
- if (!ctx.lanAuthed)
15
- throw lanAuthRequiredError();
16
- return next({ ctx });
17
- });
18
- export const protectedProcedure = t.procedure.use(lanGuard);
1
+ import{initTRPC as o}from"@trpc/server";import{lanAuthRequiredError as n}from"./errors.js";const e=o.context().create({errorFormatter:({shape:r,error:t})=>({...r,data:{...r.data,cause:t.cause??null}})}),d=e.router,c=e.middleware(({ctx:r,next:t})=>{if(!r.lanAuthed)throw n();return t({ctx:r})}),p=e.procedure.use(c);export{p as protectedProcedure,d as router};
@@ -1,49 +1 @@
1
- import { router } from './index.js';
2
- import { settingsRouter } from './routers/settings.js';
3
- import { queueRouter } from './routers/queue.js';
4
- import { watchlistRouter } from './routers/watchlist.js';
5
- import { progressRouter } from './routers/progress.js';
6
- import { notificationsRouter } from './routers/notifications.js';
7
- import { continueWatchingRouter } from './routers/continue-watching.js';
8
- import { discoveryRouter } from './routers/discovery.js';
9
- import { mangaLibraryRouter } from './routers/manga-library.js';
10
- import { mangaProgressRouter } from './routers/manga-progress.js';
11
- import { asmrLibraryRouter } from './routers/asmr-library.js';
12
- import { asmrProgressRouter } from './routers/asmr-progress.js';
13
- import { tvLibraryRouter } from './routers/tv-library.js';
14
- import { tvProgressRouter } from './routers/tv-progress.js';
15
- import { tvRouter } from './routers/tv.js';
16
- import { mangaRouter } from './routers/manga.js';
17
- import { asmrRouter } from './routers/asmr.js';
18
- import { musicRouter } from './routers/music.js';
19
- import { radioRouter } from './routers/radio.js';
20
- import { trackerRouter } from './routers/tracker.js';
21
- import { insightsRouter } from './routers/insights.js';
22
- import { authRouter } from './routers/auth.js';
23
- import { dataRouter } from './routers/data.js';
24
- import { discordRouter } from './routers/discord.js';
25
- export const appRouter = router({
26
- settings: settingsRouter,
27
- queue: queueRouter,
28
- watchlist: watchlistRouter,
29
- progress: progressRouter,
30
- notifications: notificationsRouter,
31
- continueWatching: continueWatchingRouter,
32
- discovery: discoveryRouter,
33
- mangaLibrary: mangaLibraryRouter,
34
- mangaProgress: mangaProgressRouter,
35
- asmrLibrary: asmrLibraryRouter,
36
- asmrProgress: asmrProgressRouter,
37
- tvLibrary: tvLibraryRouter,
38
- tvProgress: tvProgressRouter,
39
- tv: tvRouter,
40
- manga: mangaRouter,
41
- asmr: asmrRouter,
42
- music: musicRouter,
43
- radio: radioRouter,
44
- tracker: trackerRouter,
45
- insights: insightsRouter,
46
- auth: authRouter,
47
- data: dataRouter,
48
- discord: discordRouter,
49
- });
1
+ import{router as r}from"./index.js";import{settingsRouter as o}from"./routers/settings.js";import{queueRouter as t}from"./routers/queue.js";import{watchlistRouter as m}from"./routers/watchlist.js";import{progressRouter as i}from"./routers/progress.js";import{notificationsRouter as e}from"./routers/notifications.js";import{continueWatchingRouter as a}from"./routers/continue-watching.js";import{discoveryRouter as s}from"./routers/discovery.js";import{mangaLibraryRouter as u}from"./routers/manga-library.js";import{mangaProgressRouter as p}from"./routers/manga-progress.js";import{asmrLibraryRouter as f}from"./routers/asmr-library.js";import{asmrProgressRouter as R}from"./routers/asmr-progress.js";import{tvLibraryRouter as n}from"./routers/tv-library.js";import{tvProgressRouter as g}from"./routers/tv-progress.js";import{tvRouter as c}from"./routers/tv.js";import{mangaRouter as d}from"./routers/manga.js";import{asmrRouter as y}from"./routers/asmr.js";import{musicRouter as h}from"./routers/music.js";import{radioRouter as v}from"./routers/radio.js";import{trackerRouter as b}from"./routers/tracker.js";import{insightsRouter as L}from"./routers/insights.js";import{authRouter as P}from"./routers/auth.js";import{dataRouter as k}from"./routers/data.js";import{discordRouter as l}from"./routers/discord.js";const V=r({settings:o,queue:t,watchlist:m,progress:i,notifications:e,continueWatching:a,discovery:s,mangaLibrary:u,mangaProgress:p,asmrLibrary:f,asmrProgress:R,tvLibrary:n,tvProgress:g,tv:c,manga:d,asmr:y,music:h,radio:v,tracker:b,insights:L,auth:P,data:k,discord:l});export{V as appRouter};
@@ -1,112 +1 @@
1
- import { TRPCError } from '@trpc/server';
2
- import { protectedProcedure, router } from '../index.js';
3
- import { performAsmrWriteTransactionAsync } from '../../sync.js';
4
- import { buildAsmrId, ASMR_STATUSES, AsmrLibraryRepository, AsmrProgressRepository, } from '../../repositories/asmr.repository.js';
5
- import { defineSchema, reqObj, reqStr, optStr, reqStrArray, readPageLimit } from '../validation.js';
6
- const asmrLibraryListInput = () => defineSchema((value) => {
7
- const obj = reqObj(value);
8
- const out = { ...readPageLimit(obj) };
9
- const status = optStr(obj, 'status');
10
- if (status !== undefined)
11
- out.status = status;
12
- return out;
13
- });
14
- const asmrLibraryIdInput = () => defineSchema((value) => ({
15
- id: reqStr(reqObj(value), 'id'),
16
- }));
17
- const asmrLibraryAddInput = () => defineSchema((value) => {
18
- const obj = reqObj(value);
19
- return {
20
- rjCode: optStr(obj, 'rjCode'),
21
- id: optStr(obj, 'id'),
22
- title: optStr(obj, 'title'),
23
- thumbnail: optStr(obj, 'thumbnail'),
24
- status: optStr(obj, 'status'),
25
- isAdult: obj.isAdult,
26
- };
27
- });
28
- const asmrLibraryStatusInput = () => defineSchema((value) => {
29
- const obj = reqObj(value);
30
- return { id: reqStr(obj, 'id'), status: reqStr(obj, 'status') };
31
- });
32
- const asmrLibraryBatchStatusInput = () => defineSchema((value) => {
33
- const obj = reqObj(value);
34
- return { ids: reqStrArray(obj, 'ids', true), status: reqStr(obj, 'status') };
35
- });
36
- const asmrLibraryIdsInput = () => defineSchema((value) => ({
37
- ids: reqStrArray(reqObj(value), 'ids', true),
38
- }));
39
- export const asmrLibraryRouter = router({
40
- list: protectedProcedure.input(asmrLibraryListInput()).query(async ({ ctx, input }) => {
41
- const page = Math.max(parseInt(String(input.page ?? 1)) || 1, 1);
42
- const limit = Math.min(Math.max(parseInt(String(input.limit ?? 24)) || 24, 1), 100);
43
- const offset = (page - 1) * limit;
44
- const rows = await AsmrLibraryRepository.getAll(ctx.asmrDb, input.status, limit, offset);
45
- const total = await AsmrLibraryRepository.getCount(ctx.asmrDb, input.status);
46
- return { data: rows, total, page, limit };
47
- }),
48
- ids: protectedProcedure.query(async ({ ctx }) => {
49
- return { ids: await AsmrLibraryRepository.getIds(ctx.asmrDb) };
50
- }),
51
- check: protectedProcedure.input(asmrLibraryIdInput()).query(async ({ ctx, input }) => {
52
- const item = await AsmrLibraryRepository.getById(ctx.asmrDb, input.id);
53
- return { inLibrary: !!item, status: item?.status ?? null };
54
- }),
55
- add: protectedProcedure.input(asmrLibraryAddInput()).mutation(async ({ ctx, input }) => {
56
- const rawCode = String(input.rjCode || input.id || '').trim();
57
- if (!rawCode) {
58
- throw new TRPCError({ code: 'BAD_REQUEST', message: 'rjCode is required' });
59
- }
60
- const id = input.id && input.id.trim() ? buildAsmrId(input.id) : buildAsmrId(rawCode);
61
- const cleanStatus = typeof input.status === 'string' && ASMR_STATUSES.includes(input.status)
62
- ? input.status
63
- : 'Listening';
64
- await performAsmrWriteTransactionAsync(ctx.asmrDb, async (tx) => {
65
- await AsmrLibraryRepository.upsert(tx, {
66
- id,
67
- rjCode: buildAsmrId(rawCode),
68
- title: String(input.title || ''),
69
- thumbnail: String(input.thumbnail || ''),
70
- status: cleanStatus,
71
- isAdult: input.isAdult === true || input.isAdult === 1,
72
- });
73
- });
74
- return { success: true, id };
75
- }),
76
- remove: protectedProcedure.input(asmrLibraryIdInput()).mutation(async ({ ctx, input }) => {
77
- await performAsmrWriteTransactionAsync(ctx.asmrDb, async (tx) => {
78
- await AsmrLibraryRepository.delete(tx, String(input.id));
79
- await AsmrProgressRepository.deleteByWork(tx, String(input.id));
80
- });
81
- return { success: true };
82
- }),
83
- setStatus: protectedProcedure.input(asmrLibraryStatusInput()).mutation(async ({ ctx, input }) => {
84
- if (!input.id || !ASMR_STATUSES.includes(input.status)) {
85
- throw new TRPCError({ code: 'BAD_REQUEST', message: 'id and a valid status are required' });
86
- }
87
- await performAsmrWriteTransactionAsync(ctx.asmrDb, async (tx) => {
88
- await AsmrLibraryRepository.updateStatus(tx, String(input.id), String(input.status));
89
- });
90
- return { success: true };
91
- }),
92
- batchStatus: protectedProcedure
93
- .input(asmrLibraryBatchStatusInput())
94
- .mutation(async ({ ctx, input }) => {
95
- if (!input.status || !ASMR_STATUSES.includes(input.status)) {
96
- throw new TRPCError({ code: 'BAD_REQUEST', message: 'a valid status is required' });
97
- }
98
- const ids = input.ids.map((id) => String(id));
99
- await performAsmrWriteTransactionAsync(ctx.asmrDb, async (tx) => {
100
- await AsmrLibraryRepository.updateStatusMany(tx, ids, String(input.status));
101
- });
102
- return { success: true, updated: ids.length };
103
- }),
104
- removeMany: protectedProcedure.input(asmrLibraryIdsInput()).mutation(async ({ ctx, input }) => {
105
- const ids = input.ids.map((id) => String(id));
106
- await performAsmrWriteTransactionAsync(ctx.asmrDb, async (tx) => {
107
- await AsmrLibraryRepository.deleteMany(tx, ids);
108
- await AsmrProgressRepository.deleteMany(tx, ids);
109
- });
110
- return { success: true, removed: ids.length };
111
- }),
112
- });
1
+ import{TRPCError as b}from"@trpc/server";import{protectedProcedure as e,router as L}from"../index.js";import{performAsmrWriteTransactionAsync as o}from"../../sync.js";import{buildAsmrId as p,ASMR_STATUSES as A,AsmrLibraryRepository as i,AsmrProgressRepository as g}from"../../repositories/asmr.repository.js";import{defineSchema as n,reqObj as u,reqStr as m,optStr as d,reqStrArray as l,readPageLimit as w}from"../validation.js";const h=()=>n(r=>{const t=u(r),s={...w(t)},a=d(t,"status");return a!==void 0&&(s.status=a),s}),I=()=>n(r=>({id:m(u(r),"id")})),f=()=>n(r=>{const t=u(r);return{rjCode:d(t,"rjCode"),id:d(t,"id"),title:d(t,"title"),thumbnail:d(t,"thumbnail"),status:d(t,"status"),isAdult:t.isAdult}}),D=()=>n(r=>{const t=u(r);return{id:m(t,"id"),status:m(t,"status")}}),j=()=>n(r=>{const t=u(r);return{ids:l(t,"ids",!0),status:m(t,"status")}}),q=()=>n(r=>({ids:l(u(r),"ids",!0)})),T=L({list:e.input(h()).query(async({ctx:r,input:t})=>{const s=Math.max(parseInt(String(t.page??1))||1,1),a=Math.min(Math.max(parseInt(String(t.limit??24))||24,1),100),c=(s-1)*a,y=await i.getAll(r.asmrDb,t.status,a,c),S=await i.getCount(r.asmrDb,t.status);return{data:y,total:S,page:s,limit:a}}),ids:e.query(async({ctx:r})=>({ids:await i.getIds(r.asmrDb)})),check:e.input(I()).query(async({ctx:r,input:t})=>{const s=await i.getById(r.asmrDb,t.id);return{inLibrary:!!s,status:s?.status??null}}),add:e.input(f()).mutation(async({ctx:r,input:t})=>{const s=String(t.rjCode||t.id||"").trim();if(!s)throw new b({code:"BAD_REQUEST",message:"rjCode is required"});const a=t.id&&t.id.trim()?p(t.id):p(s),c=typeof t.status=="string"&&A.includes(t.status)?t.status:"Listening";return await o(r.asmrDb,async y=>{await i.upsert(y,{id:a,rjCode:p(s),title:String(t.title||""),thumbnail:String(t.thumbnail||""),status:c,isAdult:t.isAdult===!0||t.isAdult===1})}),{success:!0,id:a}}),remove:e.input(I()).mutation(async({ctx:r,input:t})=>(await o(r.asmrDb,async s=>{await i.delete(s,String(t.id)),await g.deleteByWork(s,String(t.id))}),{success:!0})),setStatus:e.input(D()).mutation(async({ctx:r,input:t})=>{if(!t.id||!A.includes(t.status))throw new b({code:"BAD_REQUEST",message:"id and a valid status are required"});return await o(r.asmrDb,async s=>{await i.updateStatus(s,String(t.id),String(t.status))}),{success:!0}}),batchStatus:e.input(j()).mutation(async({ctx:r,input:t})=>{if(!t.status||!A.includes(t.status))throw new b({code:"BAD_REQUEST",message:"a valid status is required"});const s=t.ids.map(a=>String(a));return await o(r.asmrDb,async a=>{await i.updateStatusMany(a,s,String(t.status))}),{success:!0,updated:s.length}}),removeMany:e.input(q()).mutation(async({ctx:r,input:t})=>{const s=t.ids.map(a=>String(a));return await o(r.asmrDb,async a=>{await i.deleteMany(a,s),await g.deleteMany(a,s)}),{success:!0,removed:s.length}})});export{T as asmrLibraryRouter};
@@ -1,121 +1 @@
1
- import { protectedProcedure, router } from '../index.js';
2
- import { performAsmrWriteTransactionAsync } from '../../sync.js';
3
- import { adultNonListIds } from '../../utils/db-utils.js';
4
- import { SettingsRepository } from '../../repositories/settings.repository.js';
5
- import { AsmrLibraryRepository, AsmrProgressRepository, } from '../../repositories/asmr.repository.js';
6
- import { defineSchema, reqObj, reqStr, reqStrArray, strOrNull } from '../validation.js';
7
- const getAdultNonListWorkIds = (db) => adultNonListIds(db, 'asmr_progress', 'asmr_library', 'workId', 'COALESCE(l.isAdult, p.isAdult) = 1');
8
- const asmrProgressIdsInput = () => defineSchema((value) => ({
9
- ids: reqStrArray(reqObj(value), 'ids', true),
10
- }));
11
- const asmrProgressWorkInput = () => defineSchema((value) => ({
12
- workId: reqStr(reqObj(value), 'workId'),
13
- }));
14
- const asmrProgressSaveInput = () => defineSchema((value) => {
15
- const obj = reqObj(value);
16
- return {
17
- workId: reqStr(obj, 'workId'),
18
- trackIndex: obj.trackIndex,
19
- trackLabel: obj.trackLabel,
20
- currentTime: obj.currentTime,
21
- duration: obj.duration,
22
- title: strOrNull(obj, 'title'),
23
- thumbnail: strOrNull(obj, 'thumbnail'),
24
- rjCode: strOrNull(obj, 'rjCode'),
25
- isAdult: obj.isAdult ?? null,
26
- };
27
- });
28
- const asmrProgressRemoveInput = () => defineSchema((value) => {
29
- const obj = reqObj(value);
30
- return { workId: reqStr(obj, 'workId'), trackIndex: obj.trackIndex ?? undefined };
31
- });
32
- const asmrContinueListeningInput = () => defineSchema((value) => {
33
- const obj = reqObj(value);
34
- const out = {};
35
- if (obj.limit !== undefined && obj.limit !== null && obj.limit !== '') {
36
- const limit = Number(obj.limit);
37
- if (Number.isFinite(limit))
38
- out.limit = limit;
39
- }
40
- return out;
41
- });
42
- export const asmrProgressRouter = router({
43
- getByWork: protectedProcedure.input(asmrProgressWorkInput()).query(async ({ ctx, input }) => {
44
- const rows = await AsmrProgressRepository.getByWork(ctx.asmrDb, input.workId);
45
- return { progress: rows };
46
- }),
47
- latest: protectedProcedure.input(asmrProgressWorkInput()).query(async ({ ctx, input }) => {
48
- const row = await AsmrProgressRepository.getLatest(ctx.asmrDb, input.workId);
49
- return row || { currentTime: 0, duration: 0 };
50
- }),
51
- save: protectedProcedure.input(asmrProgressSaveInput()).mutation(async ({ ctx, input }) => {
52
- const trackIndexNum = Math.max(Math.round(Number(input.trackIndex)) || 0, 0);
53
- const timeNum = Math.max(Number(input.currentTime) || 0, 0);
54
- const durationNum = Math.max(Number(input.duration) || 0, 0);
55
- await performAsmrWriteTransactionAsync(ctx.asmrDb, async (tx) => {
56
- await AsmrProgressRepository.upsert(tx, {
57
- workId: String(input.workId),
58
- trackIndex: trackIndexNum,
59
- trackLabel: String(input.trackLabel || ''),
60
- currentTime: timeNum,
61
- duration: durationNum,
62
- title: input.title ?? null,
63
- thumbnail: input.thumbnail ?? null,
64
- rjCode: input.rjCode ?? null,
65
- isAdult: input.isAdult != null ? Number(input.isAdult) : null,
66
- });
67
- await AsmrLibraryRepository.touchProgress(tx, String(input.workId), {
68
- trackIndex: trackIndexNum,
69
- trackLabel: String(input.trackLabel || ''),
70
- position: timeNum,
71
- });
72
- });
73
- return { success: true };
74
- }),
75
- remove: protectedProcedure.input(asmrProgressRemoveInput()).mutation(async ({ ctx, input }) => {
76
- await performAsmrWriteTransactionAsync(ctx.asmrDb, async (tx) => {
77
- if (input.trackIndex !== undefined && input.trackIndex !== null) {
78
- await AsmrProgressRepository.deleteTrack(tx, String(input.workId), Number(input.trackIndex));
79
- }
80
- else {
81
- await AsmrProgressRepository.deleteByWork(tx, String(input.workId));
82
- }
83
- });
84
- return { success: true };
85
- }),
86
- removeMany: protectedProcedure.input(asmrProgressIdsInput()).mutation(async ({ ctx, input }) => {
87
- const ids = input.ids.map((id) => String(id));
88
- await performAsmrWriteTransactionAsync(ctx.asmrDb, async (tx) => {
89
- await AsmrProgressRepository.deleteMany(tx, ids);
90
- });
91
- return { success: true, removed: ids.length };
92
- }),
93
- continueListening: protectedProcedure
94
- .input(asmrContinueListeningInput())
95
- .query(async ({ ctx, input }) => {
96
- const limit = Math.min(Math.max(parseInt(String(input.limit ?? 24)) || 24, 1), 100);
97
- const { ignoreAdult, listOnly } = await SettingsRepository.readContinueFlags(ctx.db, 'asmrIgnoreAdultContent', 'asmrCwWatchlistOnly');
98
- const rows = (await AsmrProgressRepository.getContinueListening(ctx.asmrDb, limit)).filter((row) => {
99
- if (ignoreAdult && row.isAdult === 1)
100
- return false;
101
- if (listOnly && row.watchlistStatus !== 'Listening')
102
- return false;
103
- return true;
104
- });
105
- return { data: rows, total: rows.length };
106
- }),
107
- adultCount: protectedProcedure.query(async ({ ctx }) => {
108
- const ids = await getAdultNonListWorkIds(ctx.asmrDb);
109
- return { count: ids.length };
110
- }),
111
- purgeAdult: protectedProcedure.mutation(async ({ ctx }) => {
112
- const ids = await getAdultNonListWorkIds(ctx.asmrDb);
113
- if (ids.length > 0) {
114
- await performAsmrWriteTransactionAsync(ctx.asmrDb, async (tx) => {
115
- for (const id of ids)
116
- await AsmrProgressRepository.deleteByWork(tx, id);
117
- });
118
- }
119
- return { success: true, removed: ids.length };
120
- }),
121
- });
1
+ import{protectedProcedure as s,router as p}from"../index.js";import{performAsmrWriteTransactionAsync as m}from"../../sync.js";import{adultNonListIds as w}from"../../utils/db-utils.js";import{SettingsRepository as b}from"../../repositories/settings.repository.js";import{AsmrLibraryRepository as y,AsmrProgressRepository as o}from"../../repositories/asmr.repository.js";import{defineSchema as i,reqObj as u,reqStr as l,reqStrArray as A,strOrNull as c}from"../validation.js";const I=t=>w(t,"asmr_progress","asmr_library","workId","COALESCE(l.isAdult, p.isAdult) = 1"),f=()=>i(t=>({ids:A(u(t),"ids",!0)})),k=()=>i(t=>({workId:l(u(t),"workId")})),h=()=>i(t=>{const r=u(t);return{workId:l(r,"workId"),trackIndex:r.trackIndex,trackLabel:r.trackLabel,currentTime:r.currentTime,duration:r.duration,title:c(r,"title"),thumbnail:c(r,"thumbnail"),rjCode:c(r,"rjCode"),isAdult:r.isAdult??null}}),L=()=>i(t=>{const r=u(t);return{workId:l(r,"workId"),trackIndex:r.trackIndex??void 0}}),P=()=>i(t=>{const r=u(t),e={};if(r.limit!==void 0&&r.limit!==null&&r.limit!==""){const n=Number(r.limit);Number.isFinite(n)&&(e.limit=n)}return e}),D=p({getByWork:s.input(k()).query(async({ctx:t,input:r})=>({progress:await o.getByWork(t.asmrDb,r.workId)})),latest:s.input(k()).query(async({ctx:t,input:r})=>await o.getLatest(t.asmrDb,r.workId)||{currentTime:0,duration:0}),save:s.input(h()).mutation(async({ctx:t,input:r})=>{const e=Math.max(Math.round(Number(r.trackIndex))||0,0),n=Math.max(Number(r.currentTime)||0,0),d=Math.max(Number(r.duration)||0,0);return await m(t.asmrDb,async a=>{await o.upsert(a,{workId:String(r.workId),trackIndex:e,trackLabel:String(r.trackLabel||""),currentTime:n,duration:d,title:r.title??null,thumbnail:r.thumbnail??null,rjCode:r.rjCode??null,isAdult:r.isAdult!=null?Number(r.isAdult):null}),await y.touchProgress(a,String(r.workId),{trackIndex:e,trackLabel:String(r.trackLabel||""),position:n})}),{success:!0}}),remove:s.input(L()).mutation(async({ctx:t,input:r})=>(await m(t.asmrDb,async e=>{r.trackIndex!==void 0&&r.trackIndex!==null?await o.deleteTrack(e,String(r.workId),Number(r.trackIndex)):await o.deleteByWork(e,String(r.workId))}),{success:!0})),removeMany:s.input(f()).mutation(async({ctx:t,input:r})=>{const e=r.ids.map(n=>String(n));return await m(t.asmrDb,async n=>{await o.deleteMany(n,e)}),{success:!0,removed:e.length}}),continueListening:s.input(P()).query(async({ctx:t,input:r})=>{const e=Math.min(Math.max(parseInt(String(r.limit??24))||24,1),100),{ignoreAdult:n,listOnly:d}=await b.readContinueFlags(t.db,"asmrIgnoreAdultContent","asmrCwWatchlistOnly"),a=(await o.getContinueListening(t.asmrDb,e)).filter(g=>!(n&&g.isAdult===1||d&&g.watchlistStatus!=="Listening"));return{data:a,total:a.length}}),adultCount:s.query(async({ctx:t})=>({count:(await I(t.asmrDb)).length})),purgeAdult:s.mutation(async({ctx:t})=>{const r=await I(t.asmrDb);return r.length>0&&await m(t.asmrDb,async e=>{for(const n of r)await o.deleteByWork(e,n)}),{success:!0,removed:r.length}})});export{D as asmrProgressRouter};
@@ -1,87 +1 @@
1
- import { protectedProcedure, router } from '../index.js';
2
- import { defineSchema, optStr, reqObj, reqStr } from '../validation.js';
3
- import { authRequiredError } from '../errors.js';
4
- import logger from '../../logger.js';
5
- async function cached(ctx, key, ttl, produce) {
6
- const hit = ctx.apiCache.get(key);
7
- if (hit)
8
- return hit;
9
- const body = await produce();
10
- if (ttl !== undefined)
11
- ctx.apiCache.set(key, body, ttl);
12
- else
13
- ctx.apiCache.set(key, body);
14
- return body;
15
- }
16
- const asmrBrowseInput = () => defineSchema((value) => {
17
- if (value === undefined || value === null)
18
- return {};
19
- const obj = reqObj(value);
20
- const out = {};
21
- const q = optStr(obj, 'q');
22
- if (q !== undefined)
23
- out.q = q;
24
- if (obj.page !== undefined)
25
- out.page = parseInt(String(obj.page), 10) || 1;
26
- const sort = optStr(obj, 'sort');
27
- if (sort !== undefined)
28
- out.sort = sort;
29
- const rating = optStr(obj, 'rating');
30
- if (rating !== undefined)
31
- out.rating = rating;
32
- return out;
33
- });
34
- const asmrWorkInput = () => defineSchema((value) => ({ rj: reqStr(reqObj(value), 'rj') }));
35
- export const asmrRouter = router({
36
- browse: protectedProcedure.input(asmrBrowseInput()).query(async ({ ctx, input }) => {
37
- const q = input.q || '';
38
- const page = input.page || 1;
39
- const sort = input.sort || '';
40
- const rating = input.rating || '';
41
- return cached(ctx, `route-asmr-browse-${q}-${page}-${sort}-${rating}-${sort === 'random' ? Date.now() : ''}`, 300, async () => {
42
- try {
43
- const provider = ctx.getJasmr();
44
- if (!provider)
45
- return { shows: [], hasNext: false };
46
- return await provider.browse({ query: q, page, sort, rating });
47
- }
48
- catch (err) {
49
- if (err.message === 'AUTH_REQUIRED')
50
- throw authRequiredError('jasmr');
51
- logger.error({ err }, '[Asmr] browse failed');
52
- return { shows: [], hasNext: false };
53
- }
54
- });
55
- }),
56
- work: protectedProcedure.input(asmrWorkInput()).query(async ({ ctx, input }) => {
57
- const rj = input.rj;
58
- return cached(ctx, `route-asmr-work-${rj}`, 1800, async () => {
59
- try {
60
- const provider = ctx.getJasmr();
61
- if (!provider) {
62
- return { rjCode: rj, description: '', tracks: [], images: [], chapters: [] };
63
- }
64
- const rjCode = String(rj).trim().toUpperCase();
65
- const episodes = await provider.getEpisodes(rjCode);
66
- const [streams, images, chapters] = await Promise.all([
67
- provider.getStreamUrls(rjCode, '1'),
68
- provider.getImages(rjCode),
69
- provider.getChapters(rjCode),
70
- ]);
71
- return {
72
- rjCode,
73
- description: episodes?.description || '',
74
- tracks: streams?.[0]?.links || [],
75
- images,
76
- chapters,
77
- };
78
- }
79
- catch (err) {
80
- if (err.message === 'AUTH_REQUIRED')
81
- throw authRequiredError('jasmr');
82
- logger.error({ err, rj }, '[Asmr] work fetch failed');
83
- return { rjCode: rj, description: '', tracks: [], images: [], chapters: [] };
84
- }
85
- });
86
- }),
87
- });
1
+ import{protectedProcedure as c,router as h}from"../index.js";import{defineSchema as p,optStr as i,reqObj as m,reqStr as j}from"../validation.js";import{authRequiredError as u}from"../errors.js";import d from"../../logger.js";async function g(s,e,r,t){const o=s.apiCache.get(e);if(o)return o;const n=await t();return r!==void 0?s.apiCache.set(e,n,r):s.apiCache.set(e,n),n}const I=()=>p(s=>{if(s==null)return{};const e=m(s),r={},t=i(e,"q");t!==void 0&&(r.q=t),e.page!==void 0&&(r.page=parseInt(String(e.page),10)||1);const o=i(e,"sort");o!==void 0&&(r.sort=o);const n=i(e,"rating");return n!==void 0&&(r.rating=n),r}),q=()=>p(s=>({rj:j(m(s),"rj")})),k=h({browse:c.input(I()).query(async({ctx:s,input:e})=>{const r=e.q||"",t=e.page||1,o=e.sort||"",n=e.rating||"";return g(s,`route-asmr-browse-${r}-${t}-${o}-${n}-${o==="random"?Date.now():""}`,300,async()=>{try{const a=s.getJasmr();return a?await a.browse({query:r,page:t,sort:o,rating:n}):{shows:[],hasNext:!1}}catch(a){if(a.message==="AUTH_REQUIRED")throw u("jasmr");return d.error({err:a},"[Asmr] browse failed"),{shows:[],hasNext:!1}}})}),work:c.input(q()).query(async({ctx:s,input:e})=>{const r=e.rj;return g(s,`route-asmr-work-${r}`,1800,async()=>{try{const t=s.getJasmr();if(!t)return{rjCode:r,description:"",tracks:[],images:[],chapters:[]};const o=String(r).trim().toUpperCase(),n=await t.getEpisodes(o),[a,f,w]=await Promise.all([t.getStreamUrls(o,"1"),t.getImages(o),t.getChapters(o)]);return{rjCode:o,description:n?.description||"",tracks:a?.[0]?.links||[],images:f,chapters:w}}catch(t){if(t.message==="AUTH_REQUIRED")throw u("jasmr");return d.error({err:t,rj:r},"[Asmr] work fetch failed"),{rjCode:r,description:"",tracks:[],images:[],chapters:[]}}})})});export{k as asmrRouter};
@@ -1,194 +1 @@
1
- import logger from '../../logger.js';
2
- import { TRPCError } from '@trpc/server';
3
- import { googleDriveService } from '../../google.js';
4
- import { githubSyncService } from '../../github-sync.js';
5
- import { initSyncProvider, getActiveProvider, runFullSyncSequence } from '../../sync.js';
6
- import { CONFIG } from '../../config.js';
7
- import { rcloneService } from '../../rclone.js';
8
- import { updateEnvFile } from '../../utils/env.utils.js';
9
- import { protectedProcedure, router } from '../index.js';
10
- import { defineSchema, reqObj } from '../validation.js';
11
- async function runSyncFromCtx(ctx, database, preferredProvider) {
12
- const remoteFolder = CONFIG.IS_DEV ? CONFIG.REMOTE_FOLDER_DEV : CONFIG.REMOTE_FOLDER_PROD;
13
- await runFullSyncSequence({ db: database, mangaDb: ctx.mangaDb, tvDb: ctx.tvDb, asmrDb: ctx.asmrDb }, { remoteFolder, preferredProvider });
14
- }
15
- const googleAuthSaveInput = () => defineSchema((value) => {
16
- const obj = reqObj(value);
17
- return { clientId: obj.clientId, clientSecret: obj.clientSecret, workerUrl: obj.workerUrl };
18
- });
19
- const githubAuthSaveInput = () => defineSchema((value) => {
20
- const obj = reqObj(value);
21
- if (typeof obj.clientId !== 'string')
22
- throw new Error('clientId required');
23
- return { clientId: obj.clientId };
24
- });
25
- const syncProviderInput = () => defineSchema((value) => ({
26
- provider: reqObj(value).provider,
27
- }));
28
- const rcloneSaveInput = () => defineSchema((value) => ({
29
- remote: reqObj(value).remote,
30
- }));
31
- const serverPortInput = () => defineSchema((value) => ({
32
- port: reqObj(value).port,
33
- }));
34
- export const authRouter = router({
35
- configStatus: protectedProcedure.query(async () => {
36
- const useWorker = !!CONFIG.GOOGLE_AUTH_WORKER_URL;
37
- const hasLegacyConfig = !!process.env.GOOGLE_CLIENT_ID && !!process.env.GOOGLE_CLIENT_SECRET;
38
- return { hasConfig: useWorker || hasLegacyConfig, useWorker };
39
- }),
40
- googleAuth: protectedProcedure.query(async () => {
41
- return {
42
- useWorker: !!CONFIG.GOOGLE_AUTH_WORKER_URL,
43
- hasCustomWorkerUrl: !!process.env.GOOGLE_AUTH_WORKER_URL,
44
- hasCustomClientId: !!process.env.GOOGLE_CLIENT_ID,
45
- hasClientSecret: !!process.env.GOOGLE_CLIENT_SECRET,
46
- };
47
- }),
48
- saveGoogleAuth: protectedProcedure.input(googleAuthSaveInput()).mutation(async ({ input }) => {
49
- const { clientId, clientSecret, workerUrl } = input;
50
- const updates = {};
51
- if (typeof clientId === 'string') {
52
- updates.GOOGLE_CLIENT_ID = clientId;
53
- }
54
- if (typeof clientSecret === 'string') {
55
- updates.GOOGLE_CLIENT_SECRET = clientSecret;
56
- }
57
- if (typeof workerUrl === 'string') {
58
- updates.GOOGLE_AUTH_WORKER_URL = workerUrl;
59
- }
60
- await updateEnvFile(updates);
61
- if (typeof workerUrl === 'string') {
62
- ;
63
- CONFIG.GOOGLE_AUTH_WORKER_URL = workerUrl;
64
- }
65
- if (typeof clientId === 'string') {
66
- ;
67
- CONFIG.GOOGLE_CLIENT_ID = clientId || undefined;
68
- }
69
- if (typeof clientSecret === 'string') {
70
- ;
71
- CONFIG.GOOGLE_CLIENT_SECRET =
72
- clientSecret || undefined;
73
- }
74
- await initSyncProvider();
75
- return { success: true };
76
- }),
77
- githubAuth: protectedProcedure.query(async () => {
78
- return { hasCustomClientId: !!process.env.GITHUB_CLIENT_ID };
79
- }),
80
- saveGithubAuth: protectedProcedure.input(githubAuthSaveInput()).mutation(async ({ input }) => {
81
- await updateEnvFile({ GITHUB_CLIENT_ID: input.clientId });
82
- return { success: true };
83
- }),
84
- rcloneSettings: protectedProcedure.query(async () => {
85
- const remotes = await rcloneService.listRemotes();
86
- return {
87
- remote: CONFIG.RCLONE_REMOTE || '',
88
- availableRemotes: remotes,
89
- activeRemote: rcloneService.isActive() ? rcloneService.getRemoteName() : null,
90
- };
91
- }),
92
- syncSettings: protectedProcedure.query(async () => {
93
- return {
94
- activeProvider: process.env.SYNC_PROVIDER || 'default',
95
- actualActiveProvider: getActiveProvider(),
96
- authenticatedProviders: {
97
- github: githubSyncService.isAuthenticated(),
98
- google: googleDriveService.isAuthenticated(),
99
- rclone: rcloneService.isActive(),
100
- },
101
- };
102
- }),
103
- saveSyncProvider: protectedProcedure.input(syncProviderInput()).mutation(async ({ input }) => {
104
- const { provider } = input;
105
- const value = provider === 'default' ? '' : provider;
106
- await updateEnvFile({ SYNC_PROVIDER: value });
107
- await initSyncProvider();
108
- return { success: true, activeProvider: process.env.SYNC_PROVIDER || 'default' };
109
- }),
110
- githubStatus: protectedProcedure.query(async () => {
111
- try {
112
- const user = await githubSyncService.getUserProfile();
113
- return {
114
- authenticated: !!user,
115
- user,
116
- device: githubSyncService.getDeviceState(),
117
- hasCustomClientId: !!process.env.GITHUB_CLIENT_ID,
118
- };
119
- }
120
- catch (error) {
121
- logger.error({ err: error }, 'Failed to fetch GitHub auth status');
122
- return {
123
- authenticated: false,
124
- user: null,
125
- device: githubSyncService.getDeviceState(),
126
- hasCustomClientId: !!process.env.GITHUB_CLIENT_ID,
127
- };
128
- }
129
- }),
130
- githubStart: protectedProcedure.mutation(async ({ ctx }) => {
131
- const state = await githubSyncService.startDeviceAuth(ctx.db, (db, provider) => runSyncFromCtx(ctx, db, provider));
132
- return state;
133
- }),
134
- githubPoll: protectedProcedure.query(async () => {
135
- return githubSyncService.getDeviceState();
136
- }),
137
- githubLogout: protectedProcedure.mutation(async () => {
138
- await githubSyncService.logout();
139
- await updateEnvFile({ SYNC_PROVIDER: '' });
140
- await initSyncProvider();
141
- return { success: true };
142
- }),
143
- saveRclone: protectedProcedure.input(rcloneSaveInput()).mutation(async ({ ctx, input }) => {
144
- const { remote } = input;
145
- await updateEnvFile({
146
- RCLONE_REMOTE: remote,
147
- SYNC_PROVIDER: 'rclone',
148
- });
149
- await runSyncFromCtx(ctx, ctx.db, 'rclone');
150
- return { success: true };
151
- }),
152
- setServerPort: protectedProcedure.input(serverPortInput()).mutation(async ({ input }) => {
153
- const port = Number(input.port);
154
- if (!Number.isInteger(port) || port < 1 || port > 65535) {
155
- throw new TRPCError({
156
- code: 'BAD_REQUEST',
157
- message: 'Port must be an integer between 1 and 65535',
158
- });
159
- }
160
- await updateEnvFile({ PORT: String(port) });
161
- logger.info({ port }, 'Server port saved. Restart required to apply.');
162
- return { success: true, port };
163
- }),
164
- googleAuthUrl: protectedProcedure.query(async () => {
165
- const url = await googleDriveService.getAuthUrl();
166
- return { url };
167
- }),
168
- googleLogin: protectedProcedure.mutation(async ({ ctx }) => {
169
- if (googleDriveService.isAuthenticated()) {
170
- const user = await googleDriveService.getUserProfile();
171
- if (user) {
172
- await updateEnvFile({ SYNC_PROVIDER: 'google' });
173
- await runSyncFromCtx(ctx, ctx.db, 'google');
174
- return { url: null, authenticated: true };
175
- }
176
- else {
177
- logger.warn('Google tokens found but invalid. Clearing and requesting new auth.');
178
- await googleDriveService.logout();
179
- }
180
- }
181
- const url = await googleDriveService.getAuthUrl();
182
- return { url, authenticated: false };
183
- }),
184
- user: protectedProcedure.query(async () => {
185
- const user = await googleDriveService.getUserProfile();
186
- return user;
187
- }),
188
- logout: protectedProcedure.mutation(async () => {
189
- await googleDriveService.logout();
190
- await updateEnvFile({ SYNC_PROVIDER: '' });
191
- await initSyncProvider();
192
- return { success: true };
193
- }),
194
- });
1
+ import p from"../../logger.js";import{TRPCError as _}from"@trpc/server";import{googleDriveService as s}from"../../google.js";import{githubSyncService as u}from"../../github-sync.js";import{initSyncProvider as E,getActiveProvider as v,runFullSyncSequence as I}from"../../sync.js";import{CONFIG as o}from"../../config.js";import{rcloneService as d}from"../../rclone.js";import{updateEnvFile as a}from"../../utils/env.utils.js";import{protectedProcedure as r,router as O}from"../index.js";import{defineSchema as c,reqObj as l}from"../validation.js";async function m(e,t,n){const i=o.IS_DEV?o.REMOTE_FOLDER_DEV:o.REMOTE_FOLDER_PROD;await I({db:t,mangaDb:e.mangaDb,tvDb:e.tvDb,asmrDb:e.asmrDb},{remoteFolder:i,preferredProvider:n})}const R=()=>c(e=>{const t=l(e);return{clientId:t.clientId,clientSecret:t.clientSecret,workerUrl:t.workerUrl}}),y=()=>c(e=>{const t=l(e);if(typeof t.clientId!="string")throw new Error("clientId required");return{clientId:t.clientId}}),w=()=>c(e=>({provider:l(e).provider})),f=()=>c(e=>({remote:l(e).remote})),C=()=>c(e=>({port:l(e).port})),A=O({configStatus:r.query(async()=>{const e=!!o.GOOGLE_AUTH_WORKER_URL,t=!!process.env.GOOGLE_CLIENT_ID&&!!process.env.GOOGLE_CLIENT_SECRET;return{hasConfig:e||t,useWorker:e}}),googleAuth:r.query(async()=>({useWorker:!!o.GOOGLE_AUTH_WORKER_URL,hasCustomWorkerUrl:!!process.env.GOOGLE_AUTH_WORKER_URL,hasCustomClientId:!!process.env.GOOGLE_CLIENT_ID,hasClientSecret:!!process.env.GOOGLE_CLIENT_SECRET})),saveGoogleAuth:r.input(R()).mutation(async({input:e})=>{const{clientId:t,clientSecret:n,workerUrl:i}=e,g={};return typeof t=="string"&&(g.GOOGLE_CLIENT_ID=t),typeof n=="string"&&(g.GOOGLE_CLIENT_SECRET=n),typeof i=="string"&&(g.GOOGLE_AUTH_WORKER_URL=i),await a(g),typeof i=="string"&&(o.GOOGLE_AUTH_WORKER_URL=i),typeof t=="string"&&(o.GOOGLE_CLIENT_ID=t||void 0),typeof n=="string"&&(o.GOOGLE_CLIENT_SECRET=n||void 0),await E(),{success:!0}}),githubAuth:r.query(async()=>({hasCustomClientId:!!process.env.GITHUB_CLIENT_ID})),saveGithubAuth:r.input(y()).mutation(async({input:e})=>(await a({GITHUB_CLIENT_ID:e.clientId}),{success:!0})),rcloneSettings:r.query(async()=>{const e=await d.listRemotes();return{remote:o.RCLONE_REMOTE||"",availableRemotes:e,activeRemote:d.isActive()?d.getRemoteName():null}}),syncSettings:r.query(async()=>({activeProvider:process.env.SYNC_PROVIDER||"default",actualActiveProvider:v(),authenticatedProviders:{github:u.isAuthenticated(),google:s.isAuthenticated(),rclone:d.isActive()}})),saveSyncProvider:r.input(w()).mutation(async({input:e})=>{const{provider:t}=e;return await a({SYNC_PROVIDER:t==="default"?"":t}),await E(),{success:!0,activeProvider:process.env.SYNC_PROVIDER||"default"}}),githubStatus:r.query(async()=>{try{const e=await u.getUserProfile();return{authenticated:!!e,user:e,device:u.getDeviceState(),hasCustomClientId:!!process.env.GITHUB_CLIENT_ID}}catch(e){return p.error({err:e},"Failed to fetch GitHub auth status"),{authenticated:!1,user:null,device:u.getDeviceState(),hasCustomClientId:!!process.env.GITHUB_CLIENT_ID}}}),githubStart:r.mutation(async({ctx:e})=>await u.startDeviceAuth(e.db,(n,i)=>m(e,n,i))),githubPoll:r.query(async()=>u.getDeviceState()),githubLogout:r.mutation(async()=>(await u.logout(),await a({SYNC_PROVIDER:""}),await E(),{success:!0})),saveRclone:r.input(f()).mutation(async({ctx:e,input:t})=>{const{remote:n}=t;return await a({RCLONE_REMOTE:n,SYNC_PROVIDER:"rclone"}),await m(e,e.db,"rclone"),{success:!0}}),setServerPort:r.input(C()).mutation(async({input:e})=>{const t=Number(e.port);if(!Number.isInteger(t)||t<1||t>65535)throw new _({code:"BAD_REQUEST",message:"Port must be an integer between 1 and 65535"});return await a({PORT:String(t)}),p.info({port:t},"Server port saved. Restart required to apply."),{success:!0,port:t}}),googleAuthUrl:r.query(async()=>({url:await s.getAuthUrl()})),googleLogin:r.mutation(async({ctx:e})=>{if(s.isAuthenticated()){if(await s.getUserProfile())return await a({SYNC_PROVIDER:"google"}),await m(e,e.db,"google"),{url:null,authenticated:!0};p.warn("Google tokens found but invalid. Clearing and requesting new auth."),await s.logout()}return{url:await s.getAuthUrl(),authenticated:!1}}),user:r.query(async()=>await s.getUserProfile()),logout:r.mutation(async()=>(await s.logout(),await a({SYNC_PROVIDER:""}),await E(),{success:!0}))});export{A as authRouter};