@serifpersia/dango 3.3.8 → 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-D4qo4289.js → AnimeCard-CJy1SXH3.js} +1 -1
  3. package/client/dist/assets/{AnimeInfo-BdRRm4So.js → AnimeInfo-BoXlVvZ-.js} +1 -1
  4. package/client/dist/assets/{AnimeInfoPage-DeJNAKMR.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-D3hTqTRU.js → LibraryListPage-HzuDavbb.js} +1 -1
  11. package/client/dist/assets/{ListeningList-BgWsfu40.js → ListeningList-DYcQnnsG.js} +1 -1
  12. package/client/dist/assets/{Manga-Dn-CYins.js → Manga-D8CT9aO0.js} +1 -1
  13. package/client/dist/assets/{MangaInfoPage-EmQoK7Bv.js → MangaInfoPage-DGK6CYp1.js} +1 -1
  14. package/client/dist/assets/MangaReadPage-B9XoDKF9.js +1 -0
  15. package/client/dist/assets/{MangaResetProgressModal-Dian37PQ.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-B7DeSFoA.js → QueueRail-O7f4RqjB.js} +1 -1
  24. package/client/dist/assets/Radio-DPZxQnyD.js +1 -0
  25. package/client/dist/assets/{ReadingList-BT8kUjCB.js → ReadingList-n6rr8ygy.js} +1 -1
  26. package/client/dist/assets/{ResetProgressModal-DWeiO-hs.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-B5loz7ii.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-BYwCf5kO.js → TvSearch-DuqR-z3v.js} +1 -1
  36. package/client/dist/assets/{TvWatchlist-BiJU6pKT.js → TvWatchlist-CBvgFrA0.js} +1 -1
  37. package/client/dist/assets/Watchlist-mj8mTOam.js +1 -0
  38. package/client/dist/assets/{hls-C11FOWHj.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-cT_Uu41e.js → useAsmrPopup-e4zNN9ig.js} +1 -1
  43. package/client/dist/assets/useMangaLibrary-C9Lci5N4.js +1 -0
  44. package/client/dist/assets/{useMangaPopup-PnmSctHu.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 +15 -4
  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} +235 -295
  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-D0EGRYsv.js +0 -2
  126. package/client/dist/assets/AsmrResetProgressModal-Gs0GmVaT.js +0 -1
  127. package/client/dist/assets/AvSyncCalibrator-D8hqarVB.js +0 -10
  128. package/client/dist/assets/Home-BnBzjFkW.js +0 -1
  129. package/client/dist/assets/Insights-i9QgY3nE.js +0 -1
  130. package/client/dist/assets/MangaReadPage-xJB8K2YU.js +0 -1
  131. package/client/dist/assets/Mature-DQtXKOXc.js +0 -1
  132. package/client/dist/assets/MediaCard-DpKoY-1j.js +0 -1
  133. package/client/dist/assets/Music-BP43Zqmq.js +0 -1
  134. package/client/dist/assets/OptionListMenu-Bj9ebsW3.js +0 -1
  135. package/client/dist/assets/Player-O2Cy2GF1.js +0 -56
  136. package/client/dist/assets/PlayerSettings-DYZ272Yp.js +0 -1
  137. package/client/dist/assets/QueueOptionsButton-lv26-6jp.js +0 -1
  138. package/client/dist/assets/Radio-qQBYnVfY.js +0 -1
  139. package/client/dist/assets/Search-BkvPYN33.js +0 -1
  140. package/client/dist/assets/Settings-CyZEBF6S.js +0 -21
  141. package/client/dist/assets/Trackers-JK-aXBYC.js +0 -2
  142. package/client/dist/assets/Tv-BwXWJxMy.js +0 -4
  143. package/client/dist/assets/Tv-C2SWcVzN.css +0 -1
  144. package/client/dist/assets/TvInfo-Dzyq6ucH.js +0 -1
  145. package/client/dist/assets/TvPopup-PlCU30FO.js +0 -1
  146. package/client/dist/assets/Watchlist-BOEeGXH9.js +0 -1
  147. package/client/dist/assets/index-CtoKRNLc.js +0 -178
  148. package/client/dist/assets/useAnimeInfoData-kr3WOIWH.js +0 -1
  149. package/client/dist/assets/useAsmr-CB9THf0m.js +0 -1
  150. package/client/dist/assets/useMangaLibrary-rM6z3_0E.js +0 -1
  151. package/client/dist/assets/useProviders-B3VB7A9M.js +0 -1
  152. package/client/dist/assets/useTv-BTUKMH7Q.js +0 -1
  153. package/client/dist/assets/useTvLibrary-C0sX-ilZ.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
@@ -0,0 +1,64 @@
1
+ import { integer, primaryKey, real, sqliteTable, text } from 'drizzle-orm/sqlite-core';
2
+ export const watchedEpisodes = sqliteTable('watched_episodes', {
3
+ showId: text('showId').notNull(),
4
+ episodeNumber: text('episodeNumber').notNull(),
5
+ watchedAt: text('watchedAt'),
6
+ currentTime: real('currentTime'),
7
+ duration: real('duration'),
8
+ }, (t) => [primaryKey({ columns: [t.showId, t.episodeNumber] })]);
9
+ export const watchlist = sqliteTable('watchlist', {
10
+ id: text('id').notNull(),
11
+ name: text('name'),
12
+ thumbnail: text('thumbnail'),
13
+ status: text('status'),
14
+ nativeName: text('nativeName'),
15
+ englishName: text('englishName'),
16
+ type: text('type'),
17
+ }, (t) => [primaryKey({ columns: [t.id] })]);
18
+ export const showsMeta = sqliteTable('shows_meta', {
19
+ id: text('id').notNull(),
20
+ name: text('name'),
21
+ thumbnail: text('thumbnail'),
22
+ nativeName: text('nativeName'),
23
+ englishName: text('englishName'),
24
+ episodeCount: integer('episodeCount'),
25
+ status: text('status'),
26
+ genres: text('genres'),
27
+ popularityScore: integer('popularityScore'),
28
+ type: text('type'),
29
+ anilistId: integer('anilistId'),
30
+ isAdult: integer('isAdult'),
31
+ episodeDuration: integer('episodeDuration'),
32
+ }, (t) => [primaryKey({ columns: [t.id] })]);
33
+ export const settings = sqliteTable('settings', {
34
+ key: text('key').notNull(),
35
+ value: text('value'),
36
+ }, (t) => [primaryKey({ columns: [t.key] })]);
37
+ export const queue = sqliteTable('queue', {
38
+ id: integer('id'),
39
+ showId: text('showId').notNull(),
40
+ episodeNumber: text('episodeNumber').notNull(),
41
+ queueOrder: integer('queue_order').notNull(),
42
+ });
43
+ export const discoveredNotifications = sqliteTable('discovered_notifications', {
44
+ showId: text('showId').notNull(),
45
+ episodeNumber: text('episodeNumber').notNull(),
46
+ discoveredAt: text('discoveredAt'),
47
+ }, (t) => [primaryKey({ columns: [t.showId, t.episodeNumber] })]);
48
+ export const dismissedNotifications = sqliteTable('dismissed_notifications', {
49
+ showId: text('showId').notNull(),
50
+ episodeNumber: text('episodeNumber').notNull(),
51
+ dismissedAt: text('dismissedAt'),
52
+ }, (t) => [primaryKey({ columns: [t.showId, t.episodeNumber] })]);
53
+ export const tempShowIds = sqliteTable('temp_show_ids', {
54
+ id: text('id').notNull(),
55
+ provider: text('provider').notNull(),
56
+ nativeId: text('nativeId').notNull(),
57
+ title: text('title').notNull(),
58
+ thumbnail: text('thumbnail'),
59
+ createdAt: integer('createdAt').notNull(),
60
+ }, (t) => [primaryKey({ columns: [t.id] })]);
61
+ export const legacyIdMapping = sqliteTable('legacy_id_mapping', {
62
+ legacyId: text('legacyId').notNull(),
63
+ numericId: text('numericId'),
64
+ }, (t) => [primaryKey({ columns: [t.legacyId] })]);
@@ -0,0 +1,25 @@
1
+ import { integer, primaryKey, real, sqliteTable, text } from 'drizzle-orm/sqlite-core';
2
+ export const asmrLibrary = sqliteTable('asmr_library', {
3
+ id: text('id').notNull(),
4
+ rjCode: text('rjCode').notNull(),
5
+ title: text('title'),
6
+ thumbnail: text('thumbnail'),
7
+ status: text('status'),
8
+ isAdult: integer('isAdult'),
9
+ lastTrackIndex: integer('lastTrackIndex'),
10
+ lastTrackLabel: text('lastTrackLabel'),
11
+ lastPosition: real('lastPosition'),
12
+ updatedAt: integer('updatedAt'),
13
+ }, (t) => [primaryKey({ columns: [t.id] })]);
14
+ export const asmrProgress = sqliteTable('asmr_progress', {
15
+ workId: text('workId').notNull(),
16
+ trackIndex: integer('trackIndex').notNull(),
17
+ trackLabel: text('trackLabel'),
18
+ currentTime: real('currentTime'),
19
+ duration: real('duration'),
20
+ updatedAt: integer('updatedAt'),
21
+ title: text('title'),
22
+ thumbnail: text('thumbnail'),
23
+ rjCode: text('rjCode'),
24
+ isAdult: integer('isAdult'),
25
+ }, (t) => [primaryKey({ columns: [t.workId, t.trackIndex] })]);
@@ -0,0 +1,31 @@
1
+ import { integer, primaryKey, sqliteTable, text } from 'drizzle-orm/sqlite-core';
2
+ export const mangaLibrary = sqliteTable('manga_library', {
3
+ id: text('id').notNull(),
4
+ provider: text('provider').notNull(),
5
+ mangaId: text('mangaId').notNull(),
6
+ title: text('title'),
7
+ cover: text('cover'),
8
+ status: text('status'),
9
+ author: text('author'),
10
+ altTitle: text('altTitle'),
11
+ contentRating: text('contentRating'),
12
+ lastChapterId: text('lastChapterId'),
13
+ lastChapterNumber: text('lastChapterNumber'),
14
+ lastPage: integer('lastPage'),
15
+ updatedAt: integer('updatedAt'),
16
+ anilistId: integer('anilistId'),
17
+ anilistIdSource: text('anilistIdSource'),
18
+ }, (t) => [primaryKey({ columns: [t.id] })]);
19
+ export const mangaProgress = sqliteTable('manga_progress', {
20
+ mangaId: text('mangaId').notNull(),
21
+ chapterId: text('chapterId').notNull(),
22
+ chapterNumber: text('chapterNumber'),
23
+ page: integer('page'),
24
+ pageCount: integer('pageCount'),
25
+ updatedAt: integer('updatedAt'),
26
+ title: text('title'),
27
+ cover: text('cover'),
28
+ provider: text('provider'),
29
+ altTitle: text('altTitle'),
30
+ contentRating: text('contentRating'),
31
+ }, (t) => [primaryKey({ columns: [t.mangaId, t.chapterId] })]);
@@ -0,0 +1,33 @@
1
+ import { integer, primaryKey, real, sqliteTable, text } from 'drizzle-orm/sqlite-core';
2
+ export const tvLibrary = sqliteTable('tv_library', {
3
+ id: text('id').notNull(),
4
+ tmdbId: integer('tmdbId').notNull(),
5
+ mediaType: text('mediaType').notNull(),
6
+ title: text('title'),
7
+ poster: text('poster'),
8
+ backdrop: text('backdrop'),
9
+ year: text('year'),
10
+ overview: text('overview'),
11
+ status: text('status'),
12
+ adult: integer('adult'),
13
+ lastSeason: integer('lastSeason'),
14
+ lastEpisode: integer('lastEpisode'),
15
+ updatedAt: integer('updatedAt'),
16
+ }, (t) => [primaryKey({ columns: [t.id] })]);
17
+ export const tvProgress = sqliteTable('tv_progress', {
18
+ mediaId: text('mediaId').notNull(),
19
+ season: integer('season').notNull(),
20
+ episode: integer('episode').notNull(),
21
+ currentTime: real('currentTime'),
22
+ duration: real('duration'),
23
+ updatedAt: integer('updatedAt'),
24
+ title: text('title'),
25
+ poster: text('poster'),
26
+ backdrop: text('backdrop'),
27
+ year: text('year'),
28
+ overview: text('overview'),
29
+ tmdbId: integer('tmdbId'),
30
+ mediaType: text('mediaType'),
31
+ adult: integer('adult'),
32
+ completed: integer('completed'),
33
+ }, (t) => [primaryKey({ columns: [t.mediaId, t.season, t.episode] })]);
package/server/dist/db.js CHANGED
@@ -1,11 +1,15 @@
1
1
  import { DatabaseSync } from 'node:sqlite';
2
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
3
  import fs from 'fs';
3
4
  import path from 'path';
4
5
  import logger from './logger.js';
6
+ const txOwner = new AsyncLocalStorage();
5
7
  export class DatabaseWrapper {
6
8
  db;
7
9
  isClosed = false;
8
10
  statementCache = new Map();
11
+ txDepth = 0;
12
+ txQueue = Promise.resolve();
9
13
  constructor(_dbPath, db) {
10
14
  this.db = db;
11
15
  }
@@ -31,15 +35,82 @@ export class DatabaseWrapper {
31
35
  }
32
36
  }
33
37
  serialize(cb) {
34
- this.db.exec('BEGIN IMMEDIATE');
38
+ const outer = this.txDepth === 0;
39
+ const level = this.txDepth;
40
+ this.run(outer ? 'BEGIN IMMEDIATE' : `SAVEPOINT dango_sp_${level}`);
41
+ this.txDepth = level + 1;
35
42
  try {
36
- cb();
37
- this.db.exec('COMMIT');
43
+ txOwner.run(this, cb);
38
44
  }
39
45
  catch (e) {
40
- this.db.exec('ROLLBACK');
46
+ this.txDepth = level;
47
+ try {
48
+ this.run(outer ? 'ROLLBACK' : `ROLLBACK TO SAVEPOINT dango_sp_${level}`);
49
+ }
50
+ catch {
51
+ // ignore
52
+ }
41
53
  throw e;
42
54
  }
55
+ this.txDepth = level;
56
+ this.run(outer ? 'COMMIT' : `RELEASE SAVEPOINT dango_sp_${level}`);
57
+ }
58
+ acquireTxQueue() {
59
+ const prev = this.txQueue;
60
+ let release;
61
+ const current = new Promise((res) => {
62
+ release = res;
63
+ });
64
+ this.txQueue = prev.then(() => current);
65
+ return prev.then(() => release);
66
+ }
67
+ async transact(fn) {
68
+ if (txOwner.getStore() === this) {
69
+ const level = this.txDepth;
70
+ this.run(`SAVEPOINT dango_sp_${level}`);
71
+ this.txDepth = level + 1;
72
+ try {
73
+ await fn(this);
74
+ }
75
+ catch (e) {
76
+ this.txDepth = level;
77
+ try {
78
+ this.run(`ROLLBACK TO SAVEPOINT dango_sp_${level}`);
79
+ }
80
+ catch {
81
+ // ignore
82
+ }
83
+ throw e;
84
+ }
85
+ this.txDepth = level;
86
+ this.run(`RELEASE SAVEPOINT dango_sp_${level}`);
87
+ return;
88
+ }
89
+ const release = await this.acquireTxQueue();
90
+ try {
91
+ await txOwner.run(this, async () => {
92
+ this.run('BEGIN IMMEDIATE');
93
+ this.txDepth = 1;
94
+ try {
95
+ await fn(this);
96
+ }
97
+ catch (e) {
98
+ this.txDepth = 0;
99
+ try {
100
+ this.run('ROLLBACK');
101
+ }
102
+ catch {
103
+ // ignore
104
+ }
105
+ throw e;
106
+ }
107
+ this.txDepth = 0;
108
+ this.run('COMMIT');
109
+ });
110
+ }
111
+ finally {
112
+ release();
113
+ }
43
114
  }
44
115
  close(cb) {
45
116
  if (this.isClosed) {
@@ -339,7 +339,7 @@ class DiscordGatewayService {
339
339
  }
340
340
  }
341
341
  else if (msg.op === 11) {
342
- // heartbeat ack
342
+ // ignore
343
343
  }
344
344
  else if (msg.op === 0) {
345
345
  if (msg.t === 'READY') {
@@ -13,8 +13,6 @@ export class GoogleDriveService {
13
13
  tokens = {};
14
14
  folderIdCache = new Map();
15
15
  constructor() {
16
- // Client ID/secret now live in Cloudflare Worker (GOOGLE_AUTH_WORKER_URL).
17
- // Legacy user-owned .env credentials are optional fallback only.
18
16
  if (!CONFIG.GOOGLE_AUTH_WORKER_URL && !CONFIG.GOOGLE_CLIENT_ID) {
19
17
  logger.error('GOOGLE_AUTH_WORKER_URL is missing and no GOOGLE_CLIENT_ID fallback!');
20
18
  }
@@ -47,7 +45,6 @@ export class GoogleDriveService {
47
45
  }
48
46
  }
49
47
  getGoogleClientConfig() {
50
- // Legacy fallback only. Preferred path is Worker (no secret in dango).
51
48
  return {
52
49
  clientId: CONFIG.GOOGLE_CLIENT_ID || '',
53
50
  clientSecret: CONFIG.GOOGLE_CLIENT_SECRET || '',
@@ -95,7 +92,6 @@ export class GoogleDriveService {
95
92
  if (!this.tokens.refresh_token) {
96
93
  throw new Error('Missing refresh token');
97
94
  }
98
- // Preferred: secret stays in Worker
99
95
  if (this.useWorker()) {
100
96
  await this.refreshViaWorker();
101
97
  return;
@@ -206,7 +202,6 @@ export class GoogleDriveService {
206
202
  return !!this.tokens.refresh_token;
207
203
  }
208
204
  async getAuthUrl() {
209
- // Preferred: Worker builds URL with bundled client_id, no secret needed here
210
205
  if (this.useWorker()) {
211
206
  try {
212
207
  const url = new URL(`${CONFIG.GOOGLE_AUTH_WORKER_URL}/auth-url`);
@@ -235,7 +230,6 @@ export class GoogleDriveService {
235
230
  return url.toString();
236
231
  }
237
232
  async handleCallback(code) {
238
- // Preferred: Worker exchanges code with secret, dango never sees secret
239
233
  if (this.useWorker()) {
240
234
  try {
241
235
  const data = await this.postJson(`${CONFIG.GOOGLE_AUTH_WORKER_URL}/exchange`, { code, redirect_uri: CONFIG.GOOGLE_REDIRECT_URI });
@@ -247,7 +241,6 @@ export class GoogleDriveService {
247
241
  throw error;
248
242
  }
249
243
  }
250
- // Legacy fallback: user-owned client_id/secret in .env
251
244
  const { clientId, clientSecret } = this.getGoogleClientConfig();
252
245
  const params = new URLSearchParams({
253
246
  code,
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,56 @@
1
+ import logger from '../logger.js';
2
+ import { googleDriveService } from '../google.js';
3
+ import { CONFIG } from '../config.js';
4
+ import { updateEnvFile } from '../utils/env.utils.js';
5
+ export function registerAuth(app, getDbs, runSync) {
6
+ app.get('/api/auth/google/callback', async (c) => {
7
+ const code = c.req.query('code');
8
+ if (!code) {
9
+ return c.text('No code provided', 400);
10
+ }
11
+ await googleDriveService.handleCallback(code);
12
+ const user = await googleDriveService.getUserProfile();
13
+ await updateEnvFile({ SYNC_PROVIDER: 'google' });
14
+ logger.info('User logged in. Syncing database (please wait)...');
15
+ const dbs = getDbs();
16
+ try {
17
+ await runSync(dbs.db, dbs.mangaDb, 'google');
18
+ }
19
+ catch (err) {
20
+ logger.error({ err }, 'Post-login sync failed');
21
+ }
22
+ const responseHtml = `
23
+ <html>
24
+ <head><meta name="viewport" content="width=device-width, initial-scale=1" /></head>
25
+ <body>
26
+ <h1>Authentication Successful</h1>
27
+ <p>Database synced. Returning to dango...</p>
28
+ <p><a href="/?google_auth=success">Tap here if you are not redirected</a></p>
29
+ <script>
30
+ (function () {
31
+ var payload = { type: 'GOOGLE_AUTH_SUCCESS', user: ${JSON.stringify(user)} };
32
+ try {
33
+ if (window.opener && !window.opener.closed) {
34
+ window.opener.postMessage(payload, window.location.origin);
35
+ try {
36
+ window.opener.postMessage(payload, 'http://localhost:${CONFIG.PORT}');
37
+ } catch (e) {}
38
+ try {
39
+ window.opener.postMessage(payload, 'http://127.0.0.1:${CONFIG.PORT}');
40
+ } catch (e) {}
41
+ setTimeout(function () { window.close(); }, 300);
42
+ setTimeout(function () { window.location.href = '/?google_auth=success'; }, 1500);
43
+ } else {
44
+ window.location.href = '/?google_auth=success';
45
+ }
46
+ } catch (e) {
47
+ window.location.href = '/?google_auth=success';
48
+ }
49
+ })();
50
+ </script>
51
+ </body>
52
+ </html>
53
+ `;
54
+ return c.html(responseHtml);
55
+ });
56
+ }