@sansenjian/qq-music-api 2.2.1 → 2.2.7

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 (344) hide show
  1. package/CHANGELOG.md +120 -42
  2. package/dist/app.js +58 -0
  3. package/dist/config/user-info.js +59 -0
  4. package/dist/index.js +3 -0
  5. package/dist/jest.config.js +40 -0
  6. package/dist/middlewares/koa-cors.js +63 -0
  7. package/dist/module/apis/UCommon/UCommon.js +10 -0
  8. package/dist/module/apis/album/getAlbumInfo.js +23 -0
  9. package/dist/module/apis/comments/getComments.js +24 -0
  10. package/dist/module/apis/digitalAlbum/getDigitalAlbumLists.js +22 -0
  11. package/dist/module/apis/downloadQQMusic.js +48 -0
  12. package/dist/module/apis/extend/getPlaylistTags.js +158 -0
  13. package/dist/module/apis/music/getLyric.js +32 -0
  14. package/dist/module/apis/mv/getMvByTag.js +23 -0
  15. package/dist/module/apis/radio/getRadioLists.js +26 -0
  16. package/dist/module/apis/rank/getTopLists.js +35 -0
  17. package/dist/module/apis/recommend/getDailyRecommend.js +124 -0
  18. package/dist/module/apis/recommend/getPersonalRecommend.js +114 -0
  19. package/dist/module/apis/search/getHotKey.js +25 -0
  20. package/dist/module/apis/search/getSearchByKey.js +32 -0
  21. package/dist/module/apis/search/getSmartbox.js +40 -0
  22. package/dist/module/apis/singers/getSimilarSinger.js +24 -0
  23. package/dist/module/apis/singers/getSingerDesc.js +24 -0
  24. package/dist/module/apis/singers/getSingerMv.js +23 -0
  25. package/dist/module/apis/singers/getSingerStarNum.js +23 -0
  26. package/dist/module/apis/songLists/songListCategories.js +23 -0
  27. package/dist/module/apis/songLists/songListDetail.js +28 -0
  28. package/dist/module/apis/songLists/songLists.js +35 -0
  29. package/dist/module/apis/u_common.js +56 -0
  30. package/dist/module/apis/user/checkQQLoginQr.js +189 -0
  31. package/dist/module/apis/user/getQQLoginQr.js +23 -0
  32. package/dist/module/apis/user/getUserAvatar.js +25 -0
  33. package/dist/module/apis/user/getUserLikedSongs.js +129 -0
  34. package/dist/module/apis/user/getUserPlaylists.js +138 -0
  35. package/dist/module/apis/y_common.js +69 -0
  36. package/dist/module/config.js +24 -0
  37. package/dist/module/index.js +70 -0
  38. package/dist/package.json +117 -0
  39. package/dist/routers/context/batchGetSongInfo.js +53 -0
  40. package/dist/routers/context/batchGetSongLists.js +37 -0
  41. package/dist/routers/context/checkQQLoginQr.js +16 -0
  42. package/dist/routers/context/cookies.js +36 -0
  43. package/dist/routers/context/getAlbumInfo.js +29 -0
  44. package/dist/routers/context/getComments.js +38 -0
  45. package/dist/routers/context/getDailyRecommend.js +45 -0
  46. package/dist/routers/context/getDigitalAlbumLists.js +16 -0
  47. package/dist/routers/context/getDownloadQQMusic.js +16 -0
  48. package/dist/routers/context/getHotkey.js +21 -0
  49. package/dist/routers/context/getImageUrl.js +25 -0
  50. package/dist/routers/context/getLyric.js +29 -0
  51. package/dist/routers/context/getMusicPlay.js +91 -0
  52. package/dist/routers/context/getMv.js +57 -0
  53. package/dist/routers/context/getMvByTag.js +16 -0
  54. package/dist/routers/context/getMvPlay.js +105 -0
  55. package/dist/routers/context/getNewDisks.js +52 -0
  56. package/dist/routers/context/getPersonalRecommend.js +54 -0
  57. package/dist/routers/context/getPlaylistTags.js +60 -0
  58. package/dist/routers/context/getQQLoginQr.js +14 -0
  59. package/dist/routers/context/getRadioLists.js +16 -0
  60. package/dist/routers/context/getRanks.js +60 -0
  61. package/dist/routers/context/getRecommend.js +88 -0
  62. package/dist/routers/context/getSearchByKey.js +32 -0
  63. package/dist/routers/context/getSimilarSinger.js +27 -0
  64. package/dist/routers/context/getSingerAlbum.js +54 -0
  65. package/dist/routers/context/getSingerDesc.js +28 -0
  66. package/dist/routers/context/getSingerHotsong.js +54 -0
  67. package/dist/routers/context/getSingerList.js +51 -0
  68. package/dist/routers/context/getSingerMv.js +36 -0
  69. package/dist/routers/context/getSingerStarNum.js +27 -0
  70. package/dist/routers/context/getSmartbox.js +27 -0
  71. package/dist/routers/context/getSongInfo.js +47 -0
  72. package/dist/routers/context/getSongListCategories.js +23 -0
  73. package/dist/routers/context/getSongListDetail.js +19 -0
  74. package/dist/routers/context/getSongLists.js +25 -0
  75. package/dist/routers/context/getTicketInfo.js +47 -0
  76. package/dist/routers/context/getTopLists.js +16 -0
  77. package/dist/routers/context/getUserAvatar.js +48 -0
  78. package/dist/routers/context/getUserLikedSongs.js +24 -0
  79. package/dist/routers/context/getUserPlaylists.js +24 -0
  80. package/dist/routers/context/index.js +107 -0
  81. package/dist/routers/router.js +69 -0
  82. package/dist/routers/types.js +2 -0
  83. package/dist/routers/util.js +188 -0
  84. package/dist/types/api.js +55 -0
  85. package/dist/util/apiResponse.js +88 -0
  86. package/dist/util/colors.js +19 -0
  87. package/dist/util/cookie.js +26 -0
  88. package/dist/util/loginUtils.js +30 -0
  89. package/dist/util/lyricParse.js +72 -0
  90. package/dist/util/request.js +109 -0
  91. package/docs-dist/404.html +24 -0
  92. package/docs-dist/CHANGELOG-ARCHITECTURE.html +131 -0
  93. package/docs-dist/COOKIE_CONFIG_GUIDE.html +39 -0
  94. package/docs-dist/README.html +447 -0
  95. package/docs-dist/TEST_USER_PLAYLISTS.html +42 -0
  96. package/docs-dist/USER_AVATAR_GUIDE.html +100 -0
  97. package/docs-dist/api/comments.html +48 -0
  98. package/docs-dist/api/index.html +27 -0
  99. package/docs-dist/api/music.html +51 -0
  100. package/docs-dist/api/other.html +33 -0
  101. package/docs-dist/api/playlist.html +77 -0
  102. package/docs-dist/api/rank.html +48 -0
  103. package/docs-dist/api/search.html +62 -0
  104. package/docs-dist/api/singer.html +47 -0
  105. package/docs-dist/api/user.html +64 -0
  106. package/docs-dist/assets/CHANGELOG-ARCHITECTURE.md.BOe0ZtyR.js +105 -0
  107. package/docs-dist/assets/CHANGELOG-ARCHITECTURE.md.BOe0ZtyR.lean.js +1 -0
  108. package/docs-dist/assets/COOKIE_CONFIG_GUIDE.md.D68AwXR2.js +13 -0
  109. package/docs-dist/assets/COOKIE_CONFIG_GUIDE.md.D68AwXR2.lean.js +1 -0
  110. package/docs-dist/assets/README.md.ZJQGJ1Gb.js +421 -0
  111. package/docs-dist/assets/README.md.ZJQGJ1Gb.lean.js +1 -0
  112. package/docs-dist/assets/TEST_USER_PLAYLISTS.md.C02575X2.js +16 -0
  113. package/docs-dist/assets/TEST_USER_PLAYLISTS.md.C02575X2.lean.js +1 -0
  114. package/docs-dist/assets/USER_AVATAR_GUIDE.md.BOqjn5Cm.js +74 -0
  115. package/docs-dist/assets/USER_AVATAR_GUIDE.md.BOqjn5Cm.lean.js +1 -0
  116. package/docs-dist/assets/api_comments.md.DADvndEA.js +22 -0
  117. package/docs-dist/assets/api_comments.md.DADvndEA.lean.js +1 -0
  118. package/docs-dist/assets/api_index.md.D5IASxxG.js +1 -0
  119. package/docs-dist/assets/api_index.md.D5IASxxG.lean.js +1 -0
  120. package/docs-dist/assets/api_music.md.BgB8NmZq.js +25 -0
  121. package/docs-dist/assets/api_music.md.BgB8NmZq.lean.js +1 -0
  122. package/docs-dist/assets/api_other.md.BkRWXX2z.js +7 -0
  123. package/docs-dist/assets/api_other.md.BkRWXX2z.lean.js +1 -0
  124. package/docs-dist/assets/api_playlist.md.Dc0hTrZ4.js +51 -0
  125. package/docs-dist/assets/api_playlist.md.Dc0hTrZ4.lean.js +1 -0
  126. package/docs-dist/assets/api_rank.md.DRisCFyT.js +22 -0
  127. package/docs-dist/assets/api_rank.md.DRisCFyT.lean.js +1 -0
  128. package/docs-dist/assets/api_search.md.DNnMUZK0.js +36 -0
  129. package/docs-dist/assets/api_search.md.DNnMUZK0.lean.js +1 -0
  130. package/docs-dist/assets/api_singer.md.DCmuxQkk.js +21 -0
  131. package/docs-dist/assets/api_singer.md.DCmuxQkk.lean.js +1 -0
  132. package/docs-dist/assets/api_user.md.Cjm9GG3z.js +38 -0
  133. package/docs-dist/assets/api_user.md.Cjm9GG3z.lean.js +1 -0
  134. package/docs-dist/assets/app.Dx_1wB58.js +1 -0
  135. package/docs-dist/assets/chunks/@localSearchIndexroot.CMY5EIwU.js +1 -0
  136. package/docs-dist/assets/chunks/VPLocalSearchBox.DwKWtsdX.js +9 -0
  137. package/docs-dist/assets/chunks/framework.o40iizuP.js +19 -0
  138. package/docs-dist/assets/chunks/theme.pGVgJ9Cx.js +2 -0
  139. package/docs-dist/assets/guide_architecture.md.DGtNyuMH.js +258 -0
  140. package/docs-dist/assets/guide_architecture.md.DGtNyuMH.lean.js +1 -0
  141. package/docs-dist/assets/guide_authentication.md.mtI5LfCw.js +4 -0
  142. package/docs-dist/assets/guide_authentication.md.mtI5LfCw.lean.js +1 -0
  143. package/docs-dist/assets/guide_index.md.B-0SG46T.js +1 -0
  144. package/docs-dist/assets/guide_index.md.B-0SG46T.lean.js +1 -0
  145. package/docs-dist/assets/guide_installation.md.k-KpAfxv.js +7 -0
  146. package/docs-dist/assets/guide_installation.md.k-KpAfxv.lean.js +1 -0
  147. package/docs-dist/assets/guide_quickstart.md.Bff_KFOD.js +13 -0
  148. package/docs-dist/assets/guide_quickstart.md.Bff_KFOD.lean.js +1 -0
  149. package/docs-dist/assets/index.md.xrs-uIyo.js +1 -0
  150. package/docs-dist/assets/index.md.xrs-uIyo.lean.js +1 -0
  151. package/docs-dist/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
  152. package/docs-dist/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
  153. package/docs-dist/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
  154. package/docs-dist/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
  155. package/docs-dist/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
  156. package/docs-dist/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
  157. package/docs-dist/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
  158. package/docs-dist/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
  159. package/docs-dist/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
  160. package/docs-dist/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
  161. package/docs-dist/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
  162. package/docs-dist/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
  163. package/docs-dist/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
  164. package/docs-dist/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
  165. package/docs-dist/assets/reference_response-format.md.DKYTK6uJ.js +12 -0
  166. package/docs-dist/assets/reference_response-format.md.DKYTK6uJ.lean.js +1 -0
  167. package/docs-dist/assets/style.DM4qKDd4.css +1 -0
  168. package/docs-dist/guide/architecture.html +284 -0
  169. package/docs-dist/guide/authentication.html +30 -0
  170. package/docs-dist/guide/index.html +27 -0
  171. package/docs-dist/guide/installation.html +33 -0
  172. package/docs-dist/guide/quickstart.html +39 -0
  173. package/docs-dist/hashmap.json +1 -0
  174. package/docs-dist/index.html +27 -0
  175. package/docs-dist/logo.svg +4 -0
  176. package/docs-dist/reference/response-format.html +38 -0
  177. package/docs-dist/version.json +7 -0
  178. package/docs-dist/vp-icons.css +1 -0
  179. package/package.json +28 -17
  180. package/.babelrc +0 -7
  181. package/.dockerignore +0 -5
  182. package/.editorconfig +0 -31
  183. package/.eslintrc.json +0 -22
  184. package/.github/workflows/deploy-docs.yml +0 -54
  185. package/.github/workflows/release.yml +0 -37
  186. package/.github/workflows/test.yml +0 -70
  187. package/.husky/commit-msg +0 -1
  188. package/.husky/pre-commit +0 -1
  189. package/.prettierignore +0 -1
  190. package/.prettierrc +0 -9
  191. package/AGENTS.md +0 -153
  192. package/Dockerfile +0 -18
  193. package/QQ/351/237/263/344/271/220-v0.xmind +0 -0
  194. package/QQ/351/237/263/344/271/220-v1.xmind +0 -0
  195. package/app.ts +0 -68
  196. package/commitlint.config.js +0 -20
  197. package/config/user-info.ts +0 -71
  198. package/index.ts +0 -1
  199. package/jest.config.ts +0 -41
  200. package/middlewares/koa-cors.ts +0 -81
  201. package/module/apis/UCommon/UCommon.ts +0 -13
  202. package/module/apis/album/getAlbumInfo.ts +0 -22
  203. package/module/apis/comments/getComments.ts +0 -23
  204. package/module/apis/digitalAlbum/getDigitalAlbumLists.ts +0 -23
  205. package/module/apis/downloadQQMusic.ts +0 -51
  206. package/module/apis/music/getLyric.ts +0 -34
  207. package/module/apis/mv/getMvByTag.ts +0 -24
  208. package/module/apis/radio/getRadioLists.ts +0 -27
  209. package/module/apis/rank/getTopLists.ts +0 -37
  210. package/module/apis/search/getHotKey.ts +0 -24
  211. package/module/apis/search/getSearchByKey.ts +0 -31
  212. package/module/apis/search/getSmartbox.ts +0 -43
  213. package/module/apis/singers/getSimilarSinger.ts +0 -25
  214. package/module/apis/singers/getSingerDesc.ts +0 -25
  215. package/module/apis/singers/getSingerMv.ts +0 -24
  216. package/module/apis/singers/getSingerStarNum.ts +0 -24
  217. package/module/apis/songLists/songListCategories.ts +0 -22
  218. package/module/apis/songLists/songListDetail.ts +0 -27
  219. package/module/apis/songLists/songLists.ts +0 -35
  220. package/module/apis/u_common.ts +0 -29
  221. package/module/apis/user/checkQQLoginQr.ts +0 -230
  222. package/module/apis/user/getQQLoginQr.ts +0 -28
  223. package/module/apis/user/getUserAvatar.ts +0 -32
  224. package/module/apis/user/getUserLikedSongs.ts +0 -145
  225. package/module/apis/user/getUserPlaylists.ts +0 -163
  226. package/module/apis/y_common.ts +0 -44
  227. package/module/config.ts +0 -24
  228. package/module/index.ts +0 -95
  229. package/music.png +0 -0
  230. package/pnpm-workspace.yaml +0 -2
  231. package/public/index.html +0 -430
  232. package/routers/context/batchGetSongInfo.ts +0 -60
  233. package/routers/context/batchGetSongLists.ts +0 -46
  234. package/routers/context/checkQQLoginQr.ts +0 -19
  235. package/routers/context/cookies.ts +0 -38
  236. package/routers/context/getAlbumInfo.ts +0 -31
  237. package/routers/context/getComments.ts +0 -51
  238. package/routers/context/getDigitalAlbumLists.ts +0 -18
  239. package/routers/context/getDownloadQQMusic.ts +0 -17
  240. package/routers/context/getHotkey.ts +0 -25
  241. package/routers/context/getImageUrl.ts +0 -29
  242. package/routers/context/getLyric.ts +0 -32
  243. package/routers/context/getMusicPlay.ts +0 -102
  244. package/routers/context/getMv.ts +0 -61
  245. package/routers/context/getMvByTag.ts +0 -18
  246. package/routers/context/getMvPlay.ts +0 -114
  247. package/routers/context/getNewDisks.ts +0 -58
  248. package/routers/context/getQQLoginQr.ts +0 -16
  249. package/routers/context/getRadioLists.ts +0 -18
  250. package/routers/context/getRanks.ts +0 -67
  251. package/routers/context/getRecommend.ts +0 -92
  252. package/routers/context/getSearchByKey.ts +0 -34
  253. package/routers/context/getSimilarSinger.ts +0 -29
  254. package/routers/context/getSingerAlbum.ts +0 -58
  255. package/routers/context/getSingerDesc.ts +0 -30
  256. package/routers/context/getSingerHotsong.ts +0 -58
  257. package/routers/context/getSingerList.ts +0 -56
  258. package/routers/context/getSingerMv.ts +0 -41
  259. package/routers/context/getSingerStarNum.ts +0 -29
  260. package/routers/context/getSmartbox.ts +0 -27
  261. package/routers/context/getSongInfo.ts +0 -51
  262. package/routers/context/getSongListCategories.ts +0 -23
  263. package/routers/context/getSongListDetail.ts +0 -22
  264. package/routers/context/getSongLists.ts +0 -30
  265. package/routers/context/getTicketInfo.ts +0 -51
  266. package/routers/context/getTopLists.ts +0 -18
  267. package/routers/context/getUserAvatar.ts +0 -53
  268. package/routers/context/getUserLikedSongs.ts +0 -28
  269. package/routers/context/getUserPlaylists.ts +0 -29
  270. package/routers/context/index.ts +0 -87
  271. package/routers/router.ts +0 -88
  272. package/routers/types.ts +0 -18
  273. package/routers/util.ts +0 -231
  274. package/screenshot/album-image.png +0 -0
  275. package/screenshot/batchGetSongInfo.png +0 -0
  276. package/screenshot/batchGetSongLists.png +0 -0
  277. package/screenshot/downloadQQMusic.png +0 -0
  278. package/screenshot/get-album-image.png +0 -0
  279. package/screenshot/get-play-all-data.png +0 -0
  280. package/screenshot/get-song-album-id.png +0 -0
  281. package/screenshot/get-song-id.png +0 -0
  282. package/screenshot/get-song-image.png +0 -0
  283. package/screenshot/getAlbumInfo.png +0 -0
  284. package/screenshot/getComments-id.png +0 -0
  285. package/screenshot/getComments-param.png +0 -0
  286. package/screenshot/getComments.png +0 -0
  287. package/screenshot/getDigitalAlbumLists.png +0 -0
  288. package/screenshot/getLyric-parse.png +0 -0
  289. package/screenshot/getLyric.png +0 -0
  290. package/screenshot/getMusicPlay.png +0 -0
  291. package/screenshot/getMv.png +0 -0
  292. package/screenshot/getMvByTag.png +0 -0
  293. package/screenshot/getMvPlay.png +0 -0
  294. package/screenshot/getNewDisks.png +0 -0
  295. package/screenshot/getRadioLists.png +0 -0
  296. package/screenshot/getRanks.png +0 -0
  297. package/screenshot/getRecommend.png +0 -0
  298. package/screenshot/getSearchByKey.png +0 -0
  299. package/screenshot/getSimilarSinger.png +0 -0
  300. package/screenshot/getSingerAlbum.png +0 -0
  301. package/screenshot/getSingerDesc.png +0 -0
  302. package/screenshot/getSingerHotsong.png +0 -0
  303. package/screenshot/getSingerList.png +0 -0
  304. package/screenshot/getSingerMv-default.png +0 -0
  305. package/screenshot/getSingerMv-listen.png +0 -0
  306. package/screenshot/getSingerMv-time.png +0 -0
  307. package/screenshot/getSingerStarNum.png +0 -0
  308. package/screenshot/getSmartbox.png +0 -0
  309. package/screenshot/getSongInfo.png +0 -0
  310. package/screenshot/getSongListCategories.png +0 -0
  311. package/screenshot/getSongListDetail.png +0 -0
  312. package/screenshot/getSongLists-params.png +0 -0
  313. package/screenshot/getSongLists.png +0 -0
  314. package/screenshot/getTicketInfo.png +0 -0
  315. package/screenshot/getTopLists.png +0 -0
  316. package/screenshot/gethotkey.png +0 -0
  317. package/screenshot/just-get-play-url.png +0 -0
  318. package/screenshot/musicPlay.png +0 -0
  319. package/screenshot/new-feature-error-tips.png +0 -0
  320. package/screenshot/normalize-cookie.png +0 -0
  321. package/screenshot/qq-music-v0.png +0 -0
  322. package/screenshot/qq-music.png +0 -0
  323. package/screenshot/song-image.png +0 -0
  324. package/screenshot/song-quality-128.png +0 -0
  325. package/screenshot/song-quality-m4a.png +0 -0
  326. package/scripts/build-images.js +0 -36
  327. package/scripts/commit-push.sh +0 -103
  328. package/tests/integration/api/api.test.ts +0 -852
  329. package/tests/integration/middleware/cors.test.ts +0 -41
  330. package/tests/setup/jest.setup.ts +0 -15
  331. package/tests/setup/testUtils.ts +0 -35
  332. package/tests/unit/util/request.test.ts +0 -177
  333. package/tsconfig.json +0 -20
  334. package/tsconfig.test.json +0 -8
  335. package/types/api.ts +0 -105
  336. package/types/global.d.ts +0 -26
  337. package/types/index.d.ts +0 -97
  338. package/util/apiResponse.ts +0 -97
  339. package/util/colors.ts +0 -31
  340. package/util/cookie.ts +0 -40
  341. package/util/loginUtils.ts +0 -26
  342. package/util/lyricParse.ts +0 -86
  343. package/util/request.ts +0 -141
  344. package/vercel.json +0 -15
@@ -1,41 +0,0 @@
1
- // CORS 中间件单元测试
2
-
3
- import cors from '../../../middlewares/koa-cors';
4
-
5
- describe('CORS Middleware', () => {
6
- test('should set CORS headers', async () => {
7
- const ctx = {
8
- method: 'GET',
9
- set: jest.fn(),
10
- get: jest.fn().mockReturnValue('http://localhost:3000'),
11
- vary: jest.fn(),
12
- status: 200,
13
- body: {}
14
- } as unknown as Parameters<ReturnType<typeof cors>>[0];
15
-
16
- const next = jest.fn().mockResolvedValue(undefined);
17
-
18
- await cors()(ctx, next);
19
-
20
- expect(ctx.set).toHaveBeenCalled();
21
- expect(ctx.vary).toHaveBeenCalledWith('Origin');
22
- expect(next).toHaveBeenCalled();
23
- });
24
-
25
- test('should handle preflight request', async () => {
26
- const ctx = {
27
- method: 'OPTIONS',
28
- set: jest.fn(),
29
- get: jest.fn().mockReturnValue('GET'),
30
- vary: jest.fn(),
31
- status: 200
32
- } as unknown as Parameters<ReturnType<typeof cors>>[0];
33
-
34
- const next = jest.fn().mockResolvedValue(undefined);
35
-
36
- await cors()(ctx, next);
37
-
38
- expect(ctx.status).toBe(204);
39
- expect(ctx.set).toHaveBeenCalledWith('Access-Control-Allow-Methods', expect.any(String));
40
- });
41
- });
@@ -1,15 +0,0 @@
1
- // Jest 测试环境设置文件
2
- // 扩展 Jest 的 expect 断言
3
-
4
- // 设置测试超时时间
5
- jest.setTimeout(10000);
6
-
7
- // 抑制测试中的 console.log 输出(可选)
8
- // global.console = {
9
- // ...console,
10
- // log: jest.fn(),
11
- // debug: jest.fn(),
12
- // info: jest.fn(),
13
- // warn: jest.fn(),
14
- // error: jest.fn(),
15
- // };
@@ -1,35 +0,0 @@
1
- // 测试工具函数
2
-
3
- import Koa from 'koa';
4
-
5
- /**
6
- * 创建测试用的 Koa 应用实例
7
- */
8
- export function createTestApp(): Koa {
9
- return new Koa();
10
- }
11
-
12
- /**
13
- * 等待指定毫秒数
14
- */
15
- export function sleep(ms: number): Promise<void> {
16
- return new Promise(resolve => setTimeout(resolve, ms));
17
- }
18
-
19
- /**
20
- * 生成随机字符串
21
- */
22
- export function randomString(length: number = 10): string {
23
- return Math.random().toString(36).substring(2, length + 2);
24
- }
25
-
26
- /**
27
- * Mock 响应数据生成器
28
- */
29
- export function createMockResponse<T>(data: T, code: number = 0) {
30
- return {
31
- code,
32
- data,
33
- message: code === 0 ? 'success' : 'error',
34
- };
35
- }
@@ -1,177 +0,0 @@
1
- const mockService = jest.fn();
2
- const mockInterceptors = {
3
- request: { use: jest.fn() },
4
- response: { use: jest.fn() }
5
- };
6
- (mockService as jest.Mock & { interceptors: typeof mockInterceptors }).interceptors = mockInterceptors;
7
-
8
- jest.mock('axios', () => ({
9
- __esModule: true,
10
- default: {
11
- create: jest.fn(() => mockService)
12
- },
13
- create: jest.fn(() => mockService)
14
- }));
15
-
16
- import type { AxiosRequestConfig } from 'axios';
17
- import request from '../../../util/request';
18
-
19
- describe('request util', () => {
20
- beforeEach(() => {
21
- mockService.mockClear();
22
-
23
- global.userInfo = {
24
- loginUin: '123456',
25
- cookie: 'uin=o123456; qm_keyst=abc',
26
- cookieList: ['uin=o123456', 'qm_keyst=abc'],
27
- cookieObject: {
28
- uin: 'o123456',
29
- qm_keyst: 'abc'
30
- },
31
- refreshData: () => undefined
32
- };
33
- });
34
-
35
- const getLastConfig = (): AxiosRequestConfig => {
36
- expect(mockService).toHaveBeenCalled();
37
- const calls = mockService.mock.calls;
38
- return calls[calls.length - 1]?.[0] as AxiosRequestConfig;
39
- };
40
-
41
- const getRequestInterceptor = () => {
42
- const interceptor = mockInterceptors.request.use.mock.calls[0]?.[0] as
43
- | ((config: AxiosRequestConfig) => AxiosRequestConfig)
44
- | undefined;
45
-
46
- expect(interceptor).toBeDefined();
47
- return interceptor as (config: AxiosRequestConfig) => AxiosRequestConfig;
48
- };
49
-
50
- test('should use c.y.qq.com as default base URL', async () => {
51
- await request('/test-path');
52
-
53
- expect(getLastConfig().url).toBe('https://c.y.qq.com/test-path');
54
- });
55
-
56
- test('should use y.qq.com when isUUrl is y', async () => {
57
- await request({
58
- url: '/test-path',
59
- isUUrl: 'y'
60
- });
61
-
62
- expect(getLastConfig().url).toBe('https://y.qq.com/test-path');
63
- });
64
-
65
- test('should use raw url when isUUrl is u', async () => {
66
- await request({
67
- url: 'https://u.y.qq.com/cgi-bin/musicu.fcg',
68
- isUUrl: 'u'
69
- });
70
-
71
- expect(getLastConfig().url).toBe('https://u.y.qq.com/cgi-bin/musicu.fcg');
72
- });
73
-
74
- test('should fallback to c.y.qq.com for unknown base type', async () => {
75
- await request('/fallback-path', 'GET', undefined, 'c');
76
-
77
- expect(getLastConfig().url).toBe('https://c.y.qq.com/fallback-path');
78
- });
79
-
80
- test('should inject Cookie from global.userInfo when no cookie header is provided', async () => {
81
- await request({
82
- url: '/cookie-test',
83
- options: {
84
- headers: {}
85
- }
86
- });
87
-
88
- expect(getLastConfig().headers).toMatchObject({
89
- Cookie: 'uin=o123456; qm_keyst=abc'
90
- });
91
- });
92
-
93
- test('should preserve explicit Cookie header without overriding it', async () => {
94
- await request({
95
- url: '/cookie-test',
96
- options: {
97
- headers: {
98
- Cookie: 'custom=value'
99
- }
100
- }
101
- });
102
-
103
- expect(getLastConfig().headers).toMatchObject({
104
- Cookie: 'custom=value'
105
- });
106
- });
107
-
108
- test('should move headers.cookies to Cookie and remove cookies field', async () => {
109
- await request({
110
- url: '/cookie-rename',
111
- options: {
112
- headers: {
113
- cookies: 'foo=bar'
114
- }
115
- }
116
- });
117
-
118
- const headers = getLastConfig().headers as Record<string, string>;
119
- expect(headers.Cookie).toBe('foo=bar');
120
- expect(headers.cookies).toBeUndefined();
121
- });
122
-
123
- test('should set lowercase method in axios config', async () => {
124
- await request({
125
- url: '/method-test',
126
- method: 'POST'
127
- });
128
-
129
- expect(getLastConfig().method).toBe('post');
130
- });
131
-
132
- test('should keep provided data on POST request', async () => {
133
- const payload = { foo: 'bar' };
134
-
135
- await request({
136
- url: '/post-test',
137
- method: 'POST',
138
- options: {
139
- data: payload
140
- }
141
- });
142
-
143
- expect(getLastConfig().data).toEqual(payload);
144
- });
145
-
146
- test('should allow request interceptor to add default Content-Type for POST body', () => {
147
- const interceptor = getRequestInterceptor();
148
-
149
- const result = interceptor({
150
- method: 'post',
151
- data: { foo: 'bar' },
152
- headers: {}
153
- });
154
-
155
- expect(result.headers).toMatchObject({
156
- 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
157
- 'User-Agent':
158
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
159
- });
160
- });
161
-
162
- test('should not overwrite existing Content-Type in request interceptor', () => {
163
- const interceptor = getRequestInterceptor();
164
-
165
- const result = interceptor({
166
- method: 'patch',
167
- data: { foo: 'bar' },
168
- headers: {
169
- 'Content-Type': 'application/json'
170
- }
171
- });
172
-
173
- expect(result.headers).toMatchObject({
174
- 'Content-Type': 'application/json'
175
- });
176
- });
177
- });
package/tsconfig.json DELETED
@@ -1,20 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2020",
4
- "module": "commonjs",
5
- "lib": ["es2020"],
6
- "outDir": "dist",
7
- "rootDir": "./",
8
- "strict": true,
9
- "esModuleInterop": true,
10
- "skipLibCheck": true,
11
- "forceConsistentCasingInFileNames": true,
12
- "moduleResolution": "node",
13
- "resolveJsonModule": true,
14
- "noImplicitAny": true,
15
- "typeRoots": ["./node_modules/@types", "./types"],
16
- "types": ["node"]
17
- },
18
- "include": ["**/*.ts", "types/**/*.d.ts"],
19
- "exclude": ["node_modules", "dist", "tests"]
20
- }
@@ -1,8 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "types": ["node", "jest"]
5
- },
6
- "include": ["tests/**/*.ts", "types/**/*.d.ts"],
7
- "exclude": ["node_modules", "dist"]
8
- }
package/types/api.ts DELETED
@@ -1,105 +0,0 @@
1
- /**
2
- * API 响应基础结构
3
- */
4
- export interface ApiResponse {
5
- status: number;
6
- body: {
7
- response?: any;
8
- error?: any;
9
- message?: string;
10
- isOk?: boolean;
11
- refresh?: boolean;
12
- data?: any;
13
- [key: string]: any;
14
- };
15
- }
16
-
17
- /**
18
- * API 函数参数选项
19
- */
20
- export interface ApiOptions {
21
- method?: string;
22
- params?: Record<string, any>;
23
- option?: any;
24
- isFormat?: boolean | string;
25
- [key: string]: any;
26
- }
27
-
28
- export interface HandleApiOptions<TInput = any, TOutput = any> {
29
- /** 成功时的数据转换函数 */
30
- transformData?: (data: TInput) => TOutput;
31
- /** 自定义状态码 */
32
- customStatus?: number;
33
- /** 是否记录错误日志 */
34
- logError?: boolean;
35
- }
36
-
37
- /**
38
- * API 函数类型定义
39
- */
40
- export type ApiFunction<T extends ApiOptions = ApiOptions> = (
41
- options: T
42
- ) => Promise<ApiResponse>;
43
-
44
- /**
45
- * 成功响应创建器
46
- */
47
- export function createSuccessResponse(
48
- data: any,
49
- status: number = 200
50
- ): ApiResponse {
51
- return {
52
- status,
53
- body: {
54
- response: data,
55
- },
56
- };
57
- }
58
-
59
- /**
60
- * 错误响应创建器
61
- */
62
- export function createErrorResponse(
63
- error: any,
64
- status: number = 500
65
- ): ApiResponse {
66
- return {
67
- status,
68
- body: {
69
- error,
70
- },
71
- };
72
- }
73
-
74
- /**
75
- * API Promise 处理器 - 自动处理成功和错误情况
76
- */
77
- export async function handleApi<TInput = any, TOutput = TInput>(
78
- promise: Promise<TInput>,
79
- options?: HandleApiOptions<TInput, TOutput>
80
- ): Promise<ApiResponse> {
81
- try {
82
- const result = await promise;
83
- const responseData = options?.transformData
84
- ? options.transformData((result as any).data || result)
85
- : (result as any).data || result;
86
-
87
- return {
88
- status: options?.customStatus || 200,
89
- body: {
90
- response: responseData,
91
- },
92
- };
93
- } catch (error) {
94
- if (options?.logError !== false) {
95
- console.log('API error:', error);
96
- }
97
-
98
- return {
99
- status: options?.customStatus || 500,
100
- body: {
101
- error,
102
- },
103
- };
104
- }
105
- }
package/types/global.d.ts DELETED
@@ -1,26 +0,0 @@
1
- /**
2
- * 全局 UserInfo 类型定义
3
- * 用于 global.userInfo 的类型声明
4
- * 注意:实际运行时 userInfo 还包含 uin, cookieList, cookieObject 等属性
5
- */
6
- export interface UserInfo {
7
- loginUin: string;
8
- uin?: string;
9
- cookie: string;
10
- cookieList: string[];
11
- cookieObject: Record<string, string>;
12
- refreshData: (cookie: string) => any;
13
- [key: string]: any; // 允许索引签名以支持其他动态属性
14
- }
15
-
16
- /**
17
- * 全局变量声明
18
- */
19
- declare global {
20
- var userInfo: UserInfo;
21
- }
22
-
23
- /**
24
- * 导出类型供其他模块使用
25
- */
26
- export type { UserInfo };
package/types/index.d.ts DELETED
@@ -1,97 +0,0 @@
1
- import type { UserInfo } from './global';
2
-
3
- declare global {
4
- var userInfo: UserInfo;
5
- }
6
-
7
- // 导出 UserInfo 类型供其他模块使用
8
- export type { UserInfo };
9
-
10
- declare module 'koa' {
11
- interface Context {
12
- request: Request & {
13
- body?: any;
14
- rawBody?: string;
15
- };
16
- response: Response;
17
- req: any;
18
- res: any;
19
- app: Application;
20
- cookies: {
21
- get: (name: string) => string | undefined;
22
- set: (name: string, value: string, options?: any) => void;
23
- };
24
- query: Record<string, any>;
25
- params: Record<string, string>;
26
- path: string;
27
- url: string;
28
- method: string;
29
- headers: Record<string, string>;
30
- status: number;
31
- body: any;
32
- get: (field: string) => string;
33
- set: (field: string, value: string) => void;
34
- }
35
-
36
- interface Request {
37
- header: Record<string, string>;
38
- headers: Record<string, string>;
39
- method: string;
40
- url: string;
41
- path: string;
42
- query: Record<string, any>;
43
- body?: any;
44
- rawBody?: string;
45
- }
46
-
47
- interface Response {
48
- status: number;
49
- body: any;
50
- headers: Record<string, string>;
51
- get: (field: string) => string;
52
- set: (field: string, value: string) => void;
53
- }
54
-
55
- interface Application {
56
- use: (middleware: any) => this;
57
- listen: (port: number, callback?: () => void) => any;
58
- }
59
-
60
- type Middleware = (ctx: Context, next: () => Promise<void>) => Promise<void>;
61
-
62
- export class Koa {
63
- constructor();
64
- use(fn: Middleware): this;
65
- listen(port: number, hostname?: string, backlog?: number, listeningListener?: () => void): any;
66
- }
67
-
68
- export default Koa;
69
- export { Context, Request, Response, Middleware };
70
- }
71
-
72
- declare module 'koa-bodyparser' {
73
- function bodyParser(options?: any): any;
74
- export = bodyParser;
75
- }
76
-
77
- declare module 'koa-static' {
78
- function serve(root: string, opts?: any): any;
79
- export = serve;
80
- }
81
-
82
- declare module '@koa/router' {
83
- class Router {
84
- constructor(opts?: any);
85
- get(path: string, ...middleware: any[]): this;
86
- post(path: string, ...middleware: any[]): this;
87
- use(...middleware: any[]): this;
88
- routes(): any;
89
- allowedMethods(): any;
90
- }
91
- export = Router;
92
- }
93
-
94
- declare module 'koa-cors' {
95
- function cors(options?: any): any;
96
- export = cors;
97
- }
@@ -1,97 +0,0 @@
1
- import type { ApiResponse } from '../types/api';
2
-
3
- /**
4
- * 创建成功的 API 响应
5
- * @param data 响应数据
6
- * @param status HTTP 状态码,默认 200
7
- */
8
- export function successResponse(data: any, status: number = 200): ApiResponse {
9
- return {
10
- status,
11
- body: {
12
- response: data,
13
- },
14
- };
15
- }
16
-
17
- /**
18
- * 创建错误的 API 响应
19
- * @param error 错误信息
20
- * @param status HTTP 状态码,默认 500
21
- */
22
- export function errorResponse(error: any, status: number = 500): ApiResponse {
23
- return {
24
- status,
25
- body: {
26
- error,
27
- },
28
- };
29
- }
30
-
31
- /**
32
- * 处理 API Promise,自动包装成功和错误响应
33
- * @param promise API 请求 Promise
34
- * @param options 可选配置
35
- */
36
- export async function handleApi<T = any>(
37
- promise: Promise<T>,
38
- options?: {
39
- /** 成功时的数据转换函数 */
40
- transformData?: (data: T) => any;
41
- /** 自定义状态码 */
42
- customStatus?: number;
43
- /** 是否记录错误日志 */
44
- logError?: boolean;
45
- }
46
- ): Promise<ApiResponse> {
47
- try {
48
- const result = await promise;
49
- const resultAny = result as any;
50
- const responseData = options?.transformData
51
- ? options.transformData(resultAny.data || result)
52
- : resultAny.data || result;
53
-
54
- return {
55
- status: options?.customStatus || 200,
56
- body: {
57
- response: responseData,
58
- },
59
- };
60
- } catch (error) {
61
- if (options?.logError !== false) {
62
- console.log('API error:', error);
63
- }
64
-
65
- return {
66
- status: options?.customStatus || 500,
67
- body: {
68
- error,
69
- },
70
- };
71
- }
72
- }
73
-
74
- /**
75
- * 创建自定义结构的响应
76
- * @param body 响应体
77
- * @param status HTTP 状态码
78
- */
79
- export function customResponse(body: any, status: number = 200): ApiResponse {
80
- return {
81
- status,
82
- body,
83
- };
84
- }
85
-
86
- /**
87
- * 处理 400 错误响应(参数错误)
88
- * @param message 错误消息
89
- */
90
- export function badRequest(message: string): ApiResponse {
91
- return {
92
- status: 400,
93
- body: {
94
- response: message,
95
- },
96
- };
97
- }
package/util/colors.ts DELETED
@@ -1,31 +0,0 @@
1
- import chalk from 'chalk';
2
-
3
- type ColorFunction = (text: string) => string;
4
-
5
- interface Colors {
6
- silly: ColorFunction;
7
- input: ColorFunction;
8
- verbose: ColorFunction;
9
- prompt: ColorFunction;
10
- info: ColorFunction;
11
- data: ColorFunction;
12
- help: ColorFunction;
13
- warn: ColorFunction;
14
- debug: ColorFunction;
15
- error: ColorFunction;
16
- }
17
-
18
- const colors: Colors = {
19
- silly: (text: string) => chalk.hex('#ff69b4')(text),
20
- input: (text: string) => chalk.grey(text),
21
- verbose: (text: string) => chalk.cyan(text),
22
- prompt: (text: string) => chalk.white(text),
23
- info: (text: string) => chalk.green(text),
24
- data: (text: string) => chalk.grey(text),
25
- help: (text: string) => chalk.cyan(text),
26
- warn: (text: string) => chalk.yellow(text),
27
- debug: (text: string) => chalk.blue(text),
28
- error: (text: string) => chalk.red(text)
29
- };
30
-
31
- export default colors;
package/util/cookie.ts DELETED
@@ -1,40 +0,0 @@
1
- import { Context, Next } from 'koa';
2
-
3
- // Define the shape of global.userInfo
4
- declare global {
5
- var userInfo: {
6
- cookie?: string;
7
- cookieList?: string[];
8
- loginUin?: string;
9
- };
10
- }
11
-
12
- const SAFE_COOKIE_NAMES = new Set(['qqmusic_key', 'qqmusic_uin']);
13
-
14
- const cookieMiddleware = () => async (ctx: Context, next: Next) => {
15
- if (global.userInfo?.cookie) {
16
- // Extend Request interface if needed, or just cast
17
- (ctx.request as any).cookie = global.userInfo.cookie;
18
- }
19
-
20
- if (Array.isArray(global.userInfo?.cookieList)) {
21
- global.userInfo.cookieList.forEach((cookie: string) => {
22
- const [key, ...valueParts] = cookie.split('=');
23
- const normalizedKey = key?.trim();
24
- const value = valueParts.join('=').trim();
25
-
26
- if (normalizedKey && value && SAFE_COOKIE_NAMES.has(normalizedKey)) {
27
- ctx.cookies.set(normalizedKey, value, {
28
- // 仅同步前端业务必需且可公开的 Cookie,避免敏感登录态透传
29
- overwrite: true,
30
- httpOnly: false,
31
- sameSite: 'lax'
32
- });
33
- }
34
- });
35
- }
36
-
37
- await next();
38
- };
39
-
40
- export default cookieMiddleware;