@serifpersia/dango 3.3.9 → 3.4.0

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 (180) hide show
  1. package/README.md +1 -1
  2. package/client/dist/assets/{AnimeCard-URCiiy5W.js → AnimeCard-CJy1SXH3.js} +1 -1
  3. package/client/dist/assets/{AnimeInfo-e3z3er9t.js → AnimeInfo-BoXlVvZ-.js} +1 -1
  4. package/client/dist/assets/{AnimeInfoPage-B7Djlmy_.js → AnimeInfoPage-BxbA_7AV.js} +2 -2
  5. package/client/dist/assets/Asmr--Zc8G9zv.js +2 -0
  6. package/client/dist/assets/AsmrResetProgressModal-CKYm-8cp.js +1 -0
  7. package/client/dist/assets/AvSyncCalibrator-3yjZS7s6.js +10 -0
  8. package/client/dist/assets/Home-rrhOGQcg.js +1 -0
  9. package/client/dist/assets/Insights-Bay90Yeg.js +1 -0
  10. package/client/dist/assets/{LibraryListPage-B0qWqhb_.js → LibraryListPage-HzuDavbb.js} +1 -1
  11. package/client/dist/assets/{ListeningList-CSqD8j1C.js → ListeningList-DYcQnnsG.js} +1 -1
  12. package/client/dist/assets/{Manga-Cg2YuObF.js → Manga-D8CT9aO0.js} +1 -1
  13. package/client/dist/assets/{MangaInfoPage-DcArhEsy.js → MangaInfoPage-DGK6CYp1.js} +1 -1
  14. package/client/dist/assets/MangaReadPage-B9XoDKF9.js +1 -0
  15. package/client/dist/assets/{MangaResetProgressModal-D_yT3d1v.js → MangaResetProgressModal-QjZyIAjl.js} +1 -1
  16. package/client/dist/assets/Mature-CHt_-Hz5.js +1 -0
  17. package/client/dist/assets/MediaCard-B5584Cq9.js +1 -0
  18. package/client/dist/assets/Music-HeLrEZ2d.js +1 -0
  19. package/client/dist/assets/OptionListMenu-CJ90CdwI.js +1 -0
  20. package/client/dist/assets/Player-BYtEax_z.js +56 -0
  21. package/client/dist/assets/PlayerSettings-4-cUAY4B.js +1 -0
  22. package/client/dist/assets/QueueOptionsButton-Ba0dg_82.js +1 -0
  23. package/client/dist/assets/{QueueRail-B7f1-e6i.js → QueueRail-O7f4RqjB.js} +1 -1
  24. package/client/dist/assets/Radio-DPZxQnyD.js +1 -0
  25. package/client/dist/assets/{ReadingList-BaIem8H1.js → ReadingList-n6rr8ygy.js} +1 -1
  26. package/client/dist/assets/{ResetProgressModal-DES5Y-Fl.js → ResetProgressModal-CcStlbhP.js} +1 -1
  27. package/client/dist/assets/Search-C2wrWOdy.js +1 -0
  28. package/client/dist/assets/Settings-CluUYs8U.js +21 -0
  29. package/client/dist/assets/{SkeletonGrid-BxfgjP4i.js → SkeletonGrid-CLEPY9nP.js} +1 -1
  30. package/client/dist/assets/Trackers-CTGPxs0V.js +2 -0
  31. package/client/dist/assets/Tv-BwHlDdi8.js +4 -0
  32. package/client/dist/assets/Tv-ZEA45gXK.css +1 -0
  33. package/client/dist/assets/TvInfo-CXnrLekr.js +1 -0
  34. package/client/dist/assets/TvPopup-BekfiNcX.js +1 -0
  35. package/client/dist/assets/{TvSearch-BWDwWnHe.js → TvSearch-DuqR-z3v.js} +1 -1
  36. package/client/dist/assets/{TvWatchlist-D3n5_4MI.js → TvWatchlist-CBvgFrA0.js} +1 -1
  37. package/client/dist/assets/Watchlist-mj8mTOam.js +1 -0
  38. package/client/dist/assets/{hls-BO_L1WoN.js → hls-Boje3IOE.js} +1 -1
  39. package/client/dist/assets/index-BXwhFWB_.js +178 -0
  40. package/client/dist/assets/useAnimeInfoData-DJ7A4B4T.js +1 -0
  41. package/client/dist/assets/useAsmr-DIwIsJXF.js +1 -0
  42. package/client/dist/assets/{useAsmrPopup-D_o9-QlR.js → useAsmrPopup-e4zNN9ig.js} +1 -1
  43. package/client/dist/assets/useMangaLibrary-C9Lci5N4.js +1 -0
  44. package/client/dist/assets/{useMangaPopup-TFzkC-tG.js → useMangaPopup-B8nnpvyi.js} +1 -1
  45. package/client/dist/assets/useProviders-B5Qr4I1y.js +1 -0
  46. package/client/dist/assets/useTv-Dm8MCYlL.js +1 -0
  47. package/client/dist/assets/useTvLibrary-B7jEk2Q7.js +1 -0
  48. package/client/dist/assets/{vendor-query-BXRECSI6.js → vendor-query-BUTye89G.js} +1 -1
  49. package/client/dist/index.html +2 -2
  50. package/orchestrator.js +1 -2
  51. package/package.json +2 -6
  52. package/server/dist/app-auth.js +9 -29
  53. package/server/dist/app-hono.js +621 -0
  54. package/server/dist/config.js +0 -1
  55. package/server/dist/db/drizzle.js +32 -0
  56. package/server/dist/db/schema-anime.js +64 -0
  57. package/server/dist/db/schema-asmr.js +25 -0
  58. package/server/dist/db/schema-manga.js +31 -0
  59. package/server/dist/db/schema-tv.js +33 -0
  60. package/server/dist/db.js +75 -4
  61. package/server/dist/discord-gateway.js +1 -1
  62. package/server/dist/google.js +0 -7
  63. package/server/dist/hono/asmr.js +1 -0
  64. package/server/dist/hono/auth.js +56 -0
  65. package/server/dist/hono/music.js +419 -0
  66. package/server/dist/{controllers/proxy.controller.js → hono/proxy.js} +390 -394
  67. package/server/dist/hono/tv.js +197 -0
  68. package/server/dist/hono/watchlist.js +733 -0
  69. package/server/dist/lib/anilist.js +3 -3
  70. package/server/dist/lib/insights.js +185 -0
  71. package/server/dist/lib/ipc.js +1 -2
  72. package/server/dist/lib/kitsu.js +1 -28
  73. package/server/dist/lib/mal-import.js +291 -0
  74. package/server/dist/lib/migration.js +20 -34
  75. package/server/dist/lib/offline-db.js +36 -36
  76. package/server/dist/lib/tracker/manga-sync.service.js +21 -21
  77. package/server/dist/lib/tracker/sync.service.js +20 -20
  78. package/server/dist/lib/translate.js +79 -0
  79. package/server/dist/lib/ytmusic.js +0 -2
  80. package/server/dist/providers/title-matching.js +0 -1
  81. package/server/dist/repositories/asmr.repository.js +90 -87
  82. package/server/dist/repositories/insights.repository.js +24 -13
  83. package/server/dist/repositories/library.repository.js +33 -93
  84. package/server/dist/repositories/manga.repository.js +108 -116
  85. package/server/dist/repositories/notifications.repository.js +50 -31
  86. package/server/dist/repositories/queue.repository.js +36 -25
  87. package/server/dist/repositories/settings.repository.js +18 -8
  88. package/server/dist/repositories/shows-meta.repository.js +54 -41
  89. package/server/dist/repositories/temp-show-ids.repository.js +43 -31
  90. package/server/dist/repositories/tv.repository.js +98 -115
  91. package/server/dist/repositories/watched-episodes.repository.js +52 -20
  92. package/server/dist/repositories/watchlist.repository.js +36 -48
  93. package/server/dist/server.js +59 -151
  94. package/server/dist/sync.js +41 -4
  95. package/server/dist/trpc/context.js +7 -0
  96. package/server/dist/trpc/errors.js +21 -0
  97. package/server/dist/trpc/index.js +19 -0
  98. package/server/dist/trpc/router.js +49 -0
  99. package/server/dist/trpc/routers/asmr-library.js +122 -0
  100. package/server/dist/trpc/routers/asmr-progress.js +133 -0
  101. package/server/dist/trpc/routers/asmr.js +87 -0
  102. package/server/dist/trpc/routers/auth.js +247 -0
  103. package/server/dist/trpc/routers/continue-watching.js +83 -0
  104. package/server/dist/trpc/routers/data.js +1039 -0
  105. package/server/dist/trpc/routers/discord.js +288 -0
  106. package/server/dist/trpc/routers/discovery.js +15 -0
  107. package/server/dist/trpc/routers/insights.js +45 -0
  108. package/server/dist/trpc/routers/manga-library.js +280 -0
  109. package/server/dist/trpc/routers/manga-progress.js +181 -0
  110. package/server/dist/trpc/routers/manga.js +201 -0
  111. package/server/dist/{controllers/music.controller.js → trpc/routers/music.js} +155 -288
  112. package/server/dist/trpc/routers/notifications.js +80 -0
  113. package/server/dist/trpc/routers/progress.js +15 -0
  114. package/server/dist/trpc/routers/queue.js +95 -0
  115. package/server/dist/{routes/radio.routes.js → trpc/routers/radio.js} +30 -23
  116. package/server/dist/trpc/routers/settings.js +105 -0
  117. package/server/dist/trpc/routers/tracker.js +219 -0
  118. package/server/dist/trpc/routers/tv-library.js +145 -0
  119. package/server/dist/trpc/routers/tv-progress.js +171 -0
  120. package/server/dist/trpc/routers/tv.js +565 -0
  121. package/server/dist/trpc/routers/watchlist.js +564 -0
  122. package/server/dist/trpc/validation.js +159 -0
  123. package/server/dist/utils/security.utils.js +0 -18
  124. package/server/package.json +5 -10
  125. package/client/dist/assets/Asmr-C28TfSvX.js +0 -2
  126. package/client/dist/assets/AsmrResetProgressModal-6Xh71AgR.js +0 -1
  127. package/client/dist/assets/AvSyncCalibrator-BwCtRW0P.js +0 -10
  128. package/client/dist/assets/Home-BJdbHYja.js +0 -1
  129. package/client/dist/assets/Insights-CjRy42YE.js +0 -1
  130. package/client/dist/assets/MangaReadPage-YCETZRte.js +0 -1
  131. package/client/dist/assets/Mature-BGxRWkV5.js +0 -1
  132. package/client/dist/assets/MediaCard-CAzoxsRS.js +0 -1
  133. package/client/dist/assets/Music-Cgo-yQ7E.js +0 -1
  134. package/client/dist/assets/OptionListMenu--Owe9Jle.js +0 -1
  135. package/client/dist/assets/Player-yRXG2BrP.js +0 -56
  136. package/client/dist/assets/PlayerSettings-DrZLa0CC.js +0 -1
  137. package/client/dist/assets/QueueOptionsButton-BVZyPyKf.js +0 -1
  138. package/client/dist/assets/Radio-BFjxlzuF.js +0 -1
  139. package/client/dist/assets/Search-BOBo0Ogh.js +0 -1
  140. package/client/dist/assets/Settings-DOvNQ0gX.js +0 -21
  141. package/client/dist/assets/Trackers-DZ9KqOWv.js +0 -2
  142. package/client/dist/assets/Tv-5mATEU4p.js +0 -4
  143. package/client/dist/assets/Tv-C2SWcVzN.css +0 -1
  144. package/client/dist/assets/TvInfo-CF-5C5Fs.js +0 -1
  145. package/client/dist/assets/TvPopup-Dfqifj3s.js +0 -1
  146. package/client/dist/assets/Watchlist-B8MHNsAU.js +0 -1
  147. package/client/dist/assets/index-D2RkUJIN.js +0 -178
  148. package/client/dist/assets/useAnimeInfoData-BWShDHqE.js +0 -1
  149. package/client/dist/assets/useAsmr-FyfD9G7b.js +0 -1
  150. package/client/dist/assets/useMangaLibrary-BJU5x8Vz.js +0 -1
  151. package/client/dist/assets/useProviders-DDRxa8vl.js +0 -1
  152. package/client/dist/assets/useTv-Bh6WmNnZ.js +0 -1
  153. package/client/dist/assets/useTvLibrary-_Pqm8re1.js +0 -1
  154. package/server/dist/controllers/asmr-library.controller.js +0 -288
  155. package/server/dist/controllers/auth.controller.js +0 -225
  156. package/server/dist/controllers/data.controller.js +0 -767
  157. package/server/dist/controllers/insights.controller.js +0 -203
  158. package/server/dist/controllers/manga-library.controller.js +0 -350
  159. package/server/dist/controllers/settings.controller.js +0 -609
  160. package/server/dist/controllers/tv-library.controller.js +0 -314
  161. package/server/dist/controllers/watchlist.controller.js +0 -1069
  162. package/server/dist/request-logger.js +0 -16
  163. package/server/dist/routes/asmr-library.routes.js +0 -23
  164. package/server/dist/routes/asmr.routes.js +0 -81
  165. package/server/dist/routes/auth.routes.js +0 -25
  166. package/server/dist/routes/data.routes.js +0 -47
  167. package/server/dist/routes/discord-gateway.routes.js +0 -51
  168. package/server/dist/routes/insights.routes.js +0 -35
  169. package/server/dist/routes/lan-auth.routes.js +0 -46
  170. package/server/dist/routes/manga-library.routes.js +0 -25
  171. package/server/dist/routes/manga.routes.js +0 -141
  172. package/server/dist/routes/music.routes.js +0 -20
  173. package/server/dist/routes/providers.routes.js +0 -17
  174. package/server/dist/routes/proxy.routes.js +0 -12
  175. package/server/dist/routes/settings.routes.js +0 -24
  176. package/server/dist/routes/tracker.routes.js +0 -287
  177. package/server/dist/routes/translate.routes.js +0 -88
  178. package/server/dist/routes/tv-library.routes.js +0 -23
  179. package/server/dist/routes/tv.routes.js +0 -629
  180. package/server/dist/routes/watchlist.routes.js +0 -341
@@ -1,288 +0,0 @@
1
- import logger from '../logger.js';
2
- import { performAsmrWriteTransaction } from '../sync.js';
3
- import { dbAll } from '../utils/db-utils.js';
4
- import { SettingsRepository } from '../repositories/settings.repository.js';
5
- import { buildAsmrId, ASMR_STATUSES, AsmrLibraryRepository, AsmrProgressRepository, } from '../repositories/asmr.repository.js';
6
- function asmrDb(req) {
7
- const db = req.asmrDb;
8
- if (!db || db.isClosedCheck())
9
- throw new Error('ASMR database is not ready');
10
- return db;
11
- }
12
- export class AsmrLibraryController {
13
- getLibrary = async (req, res) => {
14
- try {
15
- const { status, page: pageStr, limit: limitStr } = req.query;
16
- const page = Math.max(parseInt(pageStr) || 1, 1);
17
- const limit = Math.min(Math.max(parseInt(limitStr) || 24, 1), 100);
18
- const offset = (page - 1) * limit;
19
- const db = asmrDb(req);
20
- const rows = AsmrLibraryRepository.getAll(db, status, limit, offset);
21
- const total = AsmrLibraryRepository.getCount(db, status);
22
- res.json({ data: rows, total, page, limit });
23
- }
24
- catch (err) {
25
- logger.error({ err }, 'Failed to get ASMR library');
26
- res.status(500).json({ error: 'Failed to load listening list' });
27
- }
28
- };
29
- checkLibrary = async (req, res) => {
30
- try {
31
- const item = AsmrLibraryRepository.getById(asmrDb(req), req.params.id);
32
- res.json({ inLibrary: !!item, status: item?.status ?? null });
33
- }
34
- catch {
35
- res.json({ inLibrary: false, status: null });
36
- }
37
- };
38
- getLibraryIds = async (req, res) => {
39
- try {
40
- res.json({ ids: AsmrLibraryRepository.getIds(asmrDb(req)) });
41
- }
42
- catch {
43
- res.json({ ids: [] });
44
- }
45
- };
46
- addToLibrary = async (req, res) => {
47
- const { rjCode, id: idRaw, title, thumbnail, status, isAdult } = req.body ?? {};
48
- const rawCode = String(rjCode || idRaw || '').trim();
49
- if (!rawCode) {
50
- return res.status(400).json({ error: 'rjCode is required' });
51
- }
52
- const id = idRaw && String(idRaw).trim() ? buildAsmrId(String(idRaw)) : buildAsmrId(rawCode);
53
- const cleanStatus = typeof status === 'string' && ASMR_STATUSES.includes(status)
54
- ? status
55
- : 'Listening';
56
- try {
57
- await performAsmrWriteTransaction(asmrDb(req), (tx) => {
58
- AsmrLibraryRepository.upsert(tx, {
59
- id,
60
- rjCode: buildAsmrId(rawCode),
61
- title: String(title || ''),
62
- thumbnail: String(thumbnail || ''),
63
- status: cleanStatus,
64
- isAdult: isAdult === true || isAdult === 1,
65
- });
66
- });
67
- res.json({ success: true, id });
68
- }
69
- catch (err) {
70
- logger.error({ err }, 'Failed to add ASMR work to library');
71
- res.status(500).json({ error: 'Failed to add to listening list' });
72
- }
73
- };
74
- removeFromLibrary = async (req, res) => {
75
- const { id } = req.body ?? {};
76
- if (!id)
77
- return res.status(400).json({ error: 'id is required' });
78
- try {
79
- await performAsmrWriteTransaction(asmrDb(req), (tx) => {
80
- AsmrLibraryRepository.delete(tx, String(id));
81
- AsmrProgressRepository.deleteByWork(tx, String(id));
82
- });
83
- res.json({ success: true });
84
- }
85
- catch (err) {
86
- logger.error({ err }, 'Failed to remove ASMR work from library');
87
- res.status(500).json({ error: 'Failed to remove from listening list' });
88
- }
89
- };
90
- updateStatus = async (req, res) => {
91
- const { id, status } = req.body ?? {};
92
- if (!id || !ASMR_STATUSES.includes(status)) {
93
- return res.status(400).json({ error: 'id and a valid status are required' });
94
- }
95
- try {
96
- await performAsmrWriteTransaction(asmrDb(req), (tx) => {
97
- AsmrLibraryRepository.updateStatus(tx, String(id), String(status));
98
- });
99
- res.json({ success: true });
100
- }
101
- catch (err) {
102
- logger.error({ err }, 'Failed to update ASMR status');
103
- res.status(500).json({ error: 'Failed to update status' });
104
- }
105
- };
106
- getProgress = async (req, res) => {
107
- try {
108
- const rows = AsmrProgressRepository.getByWork(asmrDb(req), req.params.workId);
109
- res.json({ progress: rows });
110
- }
111
- catch {
112
- res.json({ progress: [] });
113
- }
114
- };
115
- getLatestProgress = async (req, res) => {
116
- try {
117
- const row = AsmrProgressRepository.getLatest(asmrDb(req), req.params.workId);
118
- res.json(row || { currentTime: 0, duration: 0 });
119
- }
120
- catch {
121
- res.json({ currentTime: 0, duration: 0 });
122
- }
123
- };
124
- saveProgress = async (req, res) => {
125
- const { workId, trackIndex, trackLabel, currentTime, duration, title, thumbnail, rjCode, isAdult, } = req.body ?? {};
126
- if (!workId) {
127
- return res.status(400).json({ error: 'workId is required' });
128
- }
129
- const trackIndexNum = Math.max(Math.round(Number(trackIndex)) || 0, 0);
130
- const timeNum = Math.max(Number(currentTime) || 0, 0);
131
- const durationNum = Math.max(Number(duration) || 0, 0);
132
- try {
133
- await performAsmrWriteTransaction(asmrDb(req), (tx) => {
134
- AsmrProgressRepository.upsert(tx, {
135
- workId: String(workId),
136
- trackIndex: trackIndexNum,
137
- trackLabel: String(trackLabel || ''),
138
- currentTime: timeNum,
139
- duration: durationNum,
140
- title: title ?? null,
141
- thumbnail: thumbnail ?? null,
142
- rjCode: rjCode ?? null,
143
- isAdult: isAdult != null ? Number(isAdult) : null,
144
- });
145
- AsmrLibraryRepository.touchProgress(tx, String(workId), {
146
- trackIndex: trackIndexNum,
147
- trackLabel: String(trackLabel || ''),
148
- position: timeNum,
149
- });
150
- });
151
- res.json({ success: true });
152
- }
153
- catch (err) {
154
- logger.error({ err }, 'Failed to save ASMR progress');
155
- res.status(500).json({ error: 'Failed to save progress' });
156
- }
157
- };
158
- getContinueListening = async (req, res) => {
159
- try {
160
- const limit = Math.min(Math.max(parseInt(req.query.limit) || 24, 1), 100);
161
- const ignoreAdultRow = await SettingsRepository.getByKey(req.db, 'asmrIgnoreAdultContent');
162
- const ignoreAdult = ignoreAdultRow ? ignoreAdultRow.value !== 'false' : true;
163
- const listOnlyRow = await SettingsRepository.getByKey(req.db, 'asmrCwWatchlistOnly');
164
- const listOnly = listOnlyRow
165
- ? listOnlyRow.value === 'true' || listOnlyRow.value === '1'
166
- : false;
167
- const rows = AsmrProgressRepository.getContinueListening(asmrDb(req), limit).filter((row) => {
168
- if (ignoreAdult && row.isAdult === 1)
169
- return false;
170
- if (listOnly && row.watchlistStatus !== 'Listening')
171
- return false;
172
- return true;
173
- });
174
- res.json({ data: rows, total: rows.length });
175
- }
176
- catch {
177
- res.json({ data: [], total: 0 });
178
- }
179
- };
180
- async getAdultNonListWorkIds(db) {
181
- const rows = await dbAll(db, `SELECT DISTINCT p.workId as workId
182
- FROM asmr_progress p
183
- LEFT JOIN asmr_library l ON l.id = p.workId
184
- WHERE COALESCE(l.isAdult, p.isAdult) = 1
185
- AND l.id IS NULL`);
186
- return rows.map((r) => r.workId);
187
- }
188
- getAdultContinueListeningCount = async (req, res) => {
189
- try {
190
- const ids = await this.getAdultNonListWorkIds(asmrDb(req));
191
- res.json({ count: ids.length });
192
- }
193
- catch {
194
- res.json({ count: 0 });
195
- }
196
- };
197
- purgeAdultContinueListening = async (req, res) => {
198
- try {
199
- const ids = await this.getAdultNonListWorkIds(asmrDb(req));
200
- if (ids.length > 0) {
201
- await performAsmrWriteTransaction(asmrDb(req), (tx) => {
202
- for (const id of ids)
203
- AsmrProgressRepository.deleteByWork(tx, id);
204
- });
205
- }
206
- res.json({ success: true, removed: ids.length });
207
- }
208
- catch (err) {
209
- logger.error({ err }, 'Failed to purge adult ASMR progress');
210
- res.status(500).json({ error: 'Failed to purge adult entries' });
211
- }
212
- };
213
- removeProgress = async (req, res) => {
214
- const { workId, trackIndex } = req.body ?? {};
215
- if (!workId)
216
- return res.status(400).json({ error: 'workId is required' });
217
- try {
218
- await performAsmrWriteTransaction(asmrDb(req), (tx) => {
219
- if (trackIndex !== undefined && trackIndex !== null) {
220
- AsmrProgressRepository.deleteTrack(tx, String(workId), Number(trackIndex));
221
- }
222
- else {
223
- AsmrProgressRepository.deleteByWork(tx, String(workId));
224
- }
225
- });
226
- res.json({ success: true });
227
- }
228
- catch (err) {
229
- logger.error({ err }, 'Failed to remove ASMR progress');
230
- res.status(500).json({ error: 'Failed to reset progress' });
231
- }
232
- };
233
- batchUpdateStatus = async (req, res) => {
234
- const { ids: idsRaw, status } = req.body ?? {};
235
- if (!Array.isArray(idsRaw) || idsRaw.length === 0) {
236
- return res.status(400).json({ error: 'ids must be a non-empty array' });
237
- }
238
- if (!status || !ASMR_STATUSES.includes(status)) {
239
- return res.status(400).json({ error: 'a valid status is required' });
240
- }
241
- const ids = idsRaw.map((id) => String(id));
242
- try {
243
- await performAsmrWriteTransaction(asmrDb(req), (tx) => {
244
- AsmrLibraryRepository.updateStatusMany(tx, ids, String(status));
245
- });
246
- res.json({ success: true, updated: ids.length });
247
- }
248
- catch (err) {
249
- logger.error({ err }, 'Failed to batch update ASMR status');
250
- res.status(500).json({ error: 'Failed to update statuses' });
251
- }
252
- };
253
- batchRemove = async (req, res) => {
254
- const { ids: idsRaw } = req.body ?? {};
255
- if (!Array.isArray(idsRaw) || idsRaw.length === 0) {
256
- return res.status(400).json({ error: 'ids must be a non-empty array' });
257
- }
258
- const ids = idsRaw.map((id) => String(id));
259
- try {
260
- await performAsmrWriteTransaction(asmrDb(req), (tx) => {
261
- AsmrLibraryRepository.deleteMany(tx, ids);
262
- AsmrProgressRepository.deleteMany(tx, ids);
263
- });
264
- res.json({ success: true, removed: ids.length });
265
- }
266
- catch (err) {
267
- logger.error({ err }, 'Failed to batch remove ASMR works');
268
- res.status(500).json({ error: 'Failed to remove from listening list' });
269
- }
270
- };
271
- batchRemoveProgress = async (req, res) => {
272
- const { ids: idsRaw } = req.body ?? {};
273
- if (!Array.isArray(idsRaw) || idsRaw.length === 0) {
274
- return res.status(400).json({ error: 'ids must be a non-empty array' });
275
- }
276
- const ids = idsRaw.map((id) => String(id));
277
- try {
278
- await performAsmrWriteTransaction(asmrDb(req), (tx) => {
279
- AsmrProgressRepository.deleteMany(tx, ids);
280
- });
281
- res.json({ success: true, removed: ids.length });
282
- }
283
- catch (err) {
284
- logger.error({ err }, 'Failed to batch remove ASMR progress');
285
- res.status(500).json({ error: 'Failed to reset progress' });
286
- }
287
- };
288
- }
@@ -1,225 +0,0 @@
1
- import logger from '../logger.js';
2
- import { googleDriveService } from '../google.js';
3
- import { githubSyncService } from '../github-sync.js';
4
- import { initSyncProvider } from '../sync.js';
5
- import { CONFIG } from '../config.js';
6
- import { rcloneService } from '../rclone.js';
7
- export class AuthController {
8
- runSyncSequence;
9
- constructor(runSyncSequence) {
10
- this.runSyncSequence = runSyncSequence;
11
- }
12
- getConfigStatus = (_req, res) => {
13
- // New flow: Worker holds ID+secret, no local .env needed.
14
- // Legacy fallback: user-owned GOOGLE_CLIENT_ID + GOOGLE_CLIENT_SECRET.
15
- const useWorker = !!CONFIG.GOOGLE_AUTH_WORKER_URL;
16
- const hasLegacyConfig = !!process.env.GOOGLE_CLIENT_ID && !!process.env.GOOGLE_CLIENT_SECRET;
17
- res.json({ hasConfig: useWorker || hasLegacyConfig, useWorker });
18
- };
19
- getGoogleAuthSettings = (_req, res) => {
20
- res.json({
21
- useWorker: !!CONFIG.GOOGLE_AUTH_WORKER_URL,
22
- hasCustomWorkerUrl: !!process.env.GOOGLE_AUTH_WORKER_URL,
23
- hasCustomClientId: !!process.env.GOOGLE_CLIENT_ID,
24
- hasClientSecret: !!process.env.GOOGLE_CLIENT_SECRET,
25
- });
26
- };
27
- updateGoogleAuthSettings = async (req, res) => {
28
- const { clientId, clientSecret, workerUrl } = req.body;
29
- const { updateEnvFile } = await import('../utils/env.utils.js');
30
- const updates = {};
31
- if (typeof clientId === 'string') {
32
- updates.GOOGLE_CLIENT_ID = clientId;
33
- }
34
- if (typeof clientSecret === 'string') {
35
- updates.GOOGLE_CLIENT_SECRET = clientSecret;
36
- }
37
- if (typeof workerUrl === 'string') {
38
- updates.GOOGLE_AUTH_WORKER_URL = workerUrl;
39
- }
40
- await updateEnvFile(updates);
41
- if (typeof workerUrl === 'string') {
42
- ;
43
- CONFIG.GOOGLE_AUTH_WORKER_URL = workerUrl;
44
- }
45
- if (typeof clientId === 'string') {
46
- ;
47
- CONFIG.GOOGLE_CLIENT_ID = clientId || undefined;
48
- }
49
- if (typeof clientSecret === 'string') {
50
- ;
51
- CONFIG.GOOGLE_CLIENT_SECRET =
52
- clientSecret || undefined;
53
- }
54
- await initSyncProvider();
55
- res.json({ success: true });
56
- };
57
- getGitHubAuthOverride = (_req, res) => {
58
- res.json({ hasCustomClientId: !!process.env.GITHUB_CLIENT_ID });
59
- };
60
- updateGitHubAuthSettings = async (req, res) => {
61
- const { clientId } = req.body;
62
- const { updateEnvFile } = await import('../utils/env.utils.js');
63
- if (typeof clientId !== 'string') {
64
- return res.status(400).json({ error: 'clientId required' });
65
- }
66
- await updateEnvFile({ GITHUB_CLIENT_ID: clientId });
67
- res.json({ success: true });
68
- };
69
- getRcloneSettings = async (_req, res) => {
70
- const remotes = await rcloneService.listRemotes();
71
- res.json({
72
- remote: CONFIG.RCLONE_REMOTE || '',
73
- availableRemotes: remotes,
74
- activeRemote: rcloneService.isActive() ? rcloneService.getRemoteName() : null,
75
- });
76
- };
77
- getSyncSettings = async (_req, res) => {
78
- const { getActiveProvider } = await import('../sync.js');
79
- res.json({
80
- activeProvider: process.env.SYNC_PROVIDER || 'default',
81
- actualActiveProvider: getActiveProvider(),
82
- authenticatedProviders: {
83
- github: githubSyncService.isAuthenticated(),
84
- google: googleDriveService.isAuthenticated(),
85
- rclone: rcloneService.isActive(),
86
- },
87
- });
88
- };
89
- updateSyncProvider = async (req, res) => {
90
- const { provider } = req.body;
91
- const { updateEnvFile } = await import('../utils/env.utils.js');
92
- const value = provider === 'default' ? '' : provider;
93
- await updateEnvFile({ SYNC_PROVIDER: value });
94
- await initSyncProvider();
95
- res.json({ success: true, activeProvider: process.env.SYNC_PROVIDER || 'default' });
96
- };
97
- getGitHubAuthStatus = async (_req, res) => {
98
- try {
99
- const user = await githubSyncService.getUserProfile();
100
- res.json({
101
- authenticated: !!user,
102
- user,
103
- device: githubSyncService.getDeviceState(),
104
- hasCustomClientId: !!process.env.GITHUB_CLIENT_ID,
105
- });
106
- }
107
- catch (error) {
108
- logger.error({ err: error }, 'Failed to fetch GitHub auth status');
109
- res.json({
110
- authenticated: false,
111
- user: null,
112
- device: githubSyncService.getDeviceState(),
113
- hasCustomClientId: !!process.env.GITHUB_CLIENT_ID,
114
- });
115
- }
116
- };
117
- startGitHubDeviceAuth = async (req, res) => {
118
- const mangaDb = req.mangaDb;
119
- const runSync = this.runSyncSequence;
120
- const state = await githubSyncService.startDeviceAuth(req.db, (db, provider) => runSync(db, mangaDb, provider));
121
- res.json(state);
122
- };
123
- pollGitHubDeviceAuth = (_req, res) => {
124
- res.json(githubSyncService.getDeviceState());
125
- };
126
- logoutGitHub = async (_req, res) => {
127
- await githubSyncService.logout();
128
- const { updateEnvFile } = await import('../utils/env.utils.js');
129
- await updateEnvFile({ SYNC_PROVIDER: '' });
130
- await initSyncProvider();
131
- res.json({ success: true });
132
- };
133
- updateRcloneSettings = async (req, res) => {
134
- const { remote } = req.body;
135
- const { updateEnvFile } = await import('../utils/env.utils.js');
136
- await updateEnvFile({
137
- RCLONE_REMOTE: remote,
138
- SYNC_PROVIDER: 'rclone',
139
- });
140
- await this.runSyncSequence(req.db, req.mangaDb, 'rclone');
141
- res.json({ success: true });
142
- };
143
- getAuthUrl = async (_req, res) => {
144
- const url = await googleDriveService.getAuthUrl();
145
- res.json({ url });
146
- };
147
- loginGoogle = async (req, res) => {
148
- if (googleDriveService.isAuthenticated()) {
149
- const user = await googleDriveService.getUserProfile();
150
- if (user) {
151
- const { updateEnvFile } = await import('../utils/env.utils.js');
152
- await updateEnvFile({ SYNC_PROVIDER: 'google' });
153
- await this.runSyncSequence(req.db, req.mangaDb, 'google');
154
- return res.json({ url: null, authenticated: true });
155
- }
156
- else {
157
- logger.warn('Google tokens found but invalid. Clearing and requesting new auth.');
158
- await googleDriveService.logout();
159
- }
160
- }
161
- const url = await googleDriveService.getAuthUrl();
162
- res.json({ url, authenticated: false });
163
- };
164
- handleCallback = async (req, res) => {
165
- const code = req.query.code;
166
- if (!code) {
167
- return res.status(400).send('No code provided');
168
- }
169
- await googleDriveService.handleCallback(code);
170
- const user = await googleDriveService.getUserProfile();
171
- const { updateEnvFile } = await import('../utils/env.utils.js');
172
- await updateEnvFile({ SYNC_PROVIDER: 'google' });
173
- logger.info('User logged in. Syncing database (please wait)...');
174
- try {
175
- await this.runSyncSequence(req.db, req.mangaDb, 'google');
176
- }
177
- catch (err) {
178
- logger.error({ err }, 'Post-login sync failed');
179
- }
180
- const responseHtml = `
181
- <html>
182
- <head><meta name="viewport" content="width=device-width, initial-scale=1" /></head>
183
- <body>
184
- <h1>Authentication Successful</h1>
185
- <p>Database synced. Returning to dango...</p>
186
- <p><a href="/?google_auth=success">Tap here if you are not redirected</a></p>
187
- <script>
188
- (function () {
189
- var payload = { type: 'GOOGLE_AUTH_SUCCESS', user: ${JSON.stringify(user)} };
190
- try {
191
- if (window.opener && !window.opener.closed) {
192
- window.opener.postMessage(payload, window.location.origin);
193
- try {
194
- window.opener.postMessage(payload, 'http://localhost:${CONFIG.PORT}');
195
- } catch (e) {}
196
- try {
197
- window.opener.postMessage(payload, 'http://127.0.0.1:${CONFIG.PORT}');
198
- } catch (e) {}
199
- setTimeout(function () { window.close(); }, 300);
200
- setTimeout(function () { window.location.href = '/?google_auth=success'; }, 1500);
201
- } else {
202
- window.location.href = '/?google_auth=success';
203
- }
204
- } catch (e) {
205
- window.location.href = '/?google_auth=success';
206
- }
207
- })();
208
- </script>
209
- </body>
210
- </html>
211
- `;
212
- res.send(responseHtml);
213
- };
214
- getUserProfile = async (_req, res) => {
215
- const user = await googleDriveService.getUserProfile();
216
- res.json(user);
217
- };
218
- logout = async (_req, res) => {
219
- await googleDriveService.logout();
220
- const { updateEnvFile } = await import('../utils/env.utils.js');
221
- await updateEnvFile({ SYNC_PROVIDER: '' });
222
- await initSyncProvider();
223
- res.json({ success: true });
224
- };
225
- }