mirror of
https://github.com/NeteaseCloudMusicApiEnhanced/api-enhanced.git
synced 2026-10-20 22:51:23 +08:00
6.3 KiB
6.3 KiB
NeteaseCloudMusicApiEnhanced Agent 说明
快速开始
- 包管理器:开发与 CI 使用
pnpm,Dockerfile也用 pnpm(pnpm@9 --frozen-lockfile,匹配pnpm-lock.yaml)。仓库没有yarn.lock,不要引入 yarn。 - Node 版本:README 推荐 Node 22+;
package.json的engines声明>=12;CI/打包在 Node 18–24 上运行。现代 Node 均可。 - 环境变量:
server.js调用了dotenv.config(),本地.env会被自动加载;所有支持的变量见.env.prod.example。
常用命令
- 安装依赖:
pnpm i - 启动服务:
pnpm start(等价node app.js);热重载开发:pnpm dev(nodemon) - 跑测试:
pnpm test(Mocha,超时 60s) - Lint:
pnpm lint;自动修复:pnpm lint-fix - 文档格式化检查/修复:
pnpm docs:check/pnpm docs:format - 打包独立二进制:
pnpm pkgwin/pkglinux/pkgmacos
目录内非路由文件
plugins/——内部上传辅助(upload.js、songUpload.js),被module/*require,本身不是路由。module_example/——接口模板与main.js库调用示例(含test.js演示login_cellphone→song_url链路),不是运行时代码。examples/get_static_moddef.js——用getModulesDefinitions(..., false)(doRequire=false)把全部路由导出为moddef.json。改路由注册逻辑时可跑它核对。module_types/为空目录;类型声明在根目录interface.d.ts(types字段)。
架构
app.js(也是bin)——服务入口。先确保os.tmpdir()里存在anonymous_token,执行generateConfig()刷新匿名 cookie 与 xeapi 公钥,再调用server.serveNcmApi()。server.js——Express 工厂。constructServer()自动扫描module/*.js,每个文件注册一条路由(文件名_转/,如album_new.js→/album/new;特例daily_signin/fm_trash/personal_fm硬编码在server.js的special对象里)。serveNcmApi()监听PORT(默认 3000)/HOST。main.js——作为依赖被引入时的入口(main字段)。把每个module/*导出为同名函数name(data),另导出server、serveNcmApi、getModulesDefinitions。module/*.js——每个接口一个文件,标准写法:module.exports = (query, request) => request(path, data, createOption(query))。createOption在util/option.js,负责 crypto、cookie(回退到NETEASE_COOKIE)、proxy、realIP/randomCNIP、headers、timeout。util/request.js——唯一的对外 HTTP 层(axios)。按crypto(api/eapi/weapi/linuxapi/xeapi)加密并设置 IP 头;在 require 时同步读取os.tmpdir()里的anonymous_token与xeapi_public_key。util/config.json——运行时配置:网易域名 +APP_CONF.encrypt: true(默认走 eapi 加密)。已被 git 跟踪,改动会改变全局默认行为。index.js/index.mjs——require('./app.js')的薄包装,供 Vercel(vercel.json)和 ESM 导入使用。server.js里有一段非显而易见的逻辑:环境变量ENABLE_GENERAL_UNBLOCK=true时,/song/url/v1的响应会被自动解灰(走@neteasecloudmusicapienhanced/unblockmusic-utils);另外query.noCookie为真时不向响应写Set-Cookie。
新增/修改接口
- 新建
module/xxx.js会自动挂载路由,无需注册;文件名即路由。 - 照抄同目录模块的写法(选对
crypto),用createOption(query)生成请求选项。 - 改文件名/路由会破坏已有客户端,尽量保持旧路径兼容。
测试
pnpm test跑server.test.js+main.test.js。server.test.js在before()里启动真实服务器,test/*.test.js全部请求真实网易云 API——必须联网,且可能因上游风控/限流偶发失败。main.test.js是纯单测,不需要网络。- CI 门禁只看稳定项:
ci-check.yml在 Node 18/22/24 上跑pnpm lint+main.test.js+pnpm docs:check;完整集成测试(server.test.js)是continue-on-error,不阻塞合并。本地跑全量测试失败先怀疑上游风控。 - 测试使用
power-assert(经intelli-espower-loader),普通assert写法也会输出详细 diff。 - 只跑单个用例:
pnpm exec mocha -r intelli-espower-loader -t 60000 --grep "<describe/it 名字>" server.test.js main.test.js --exit - 只跑离线单测:
pnpm exec mocha -r intelli-espower-loader -t 60000 main.test.js --exit
坑与注意
- 改
package.json的version会触发自动发布:release-on-version-change.yml按 paths 过滤——只要 push 到main时package.json有改动且 version 变了,就自动打 tag + GitHub Release(pkg三平台二进制)、推送 Docker 镜像(Docker Hub + GHCR 多架构)、pnpm publish到 npm;tag 已存在则跳过。别顺手改版本号,也别往package.json里写无关改动凑提交。 - 没有实际 git hooks:
package.json里配了lint-staged和 husky,但.husky/下只有_脚手架目录、没有真正的 hook 文件,commit 时不会自动跑任何检查,自己记得pnpm lint-fix。 - 代理环境变量已失效:README 里关于
http_proxy/https_proxy的警告来自旧request库时代;现在util/request.js用 axios + 自定义 keep-alive agent,且显式proxy: false,环境变量代理不会生效。按请求走query.proxy参数(支持 PAC 和 http 隧道)。 - 启动令牌在系统临时目录:
anonymous_token、xeapi_public_key存放在os.tmpdir(),util/request.js在 require 时同步读取。文件过期或被清空就重启服务(或调用generateConfig());首次启动先写空文件再刷新。 docs:check校验的是public/docs/home.md(docsify 站点),不是 README;脚本默认只处理该文件,换行会被统一为\n、连续空行压缩到 2 行,PR 改动它后记得跑pnpm docs:format。- ESLint 9 flat config:
eslint.config.js,风格由eslint-plugin-prettier强制(2 空格缩进、单引号、分号、endOfLine: auto),并globalIgnores(['**/public/'])——public/下的前端页面不参与 lint。 pnpm-workspace.yaml是单包工作区(packages: ['.']),并带allowBuilds(core-js/es5-ext)与minimumReleaseAgeExclude白名单;改依赖安装行为时先看它。