合并自建歌曲源支线(selfhost-source)+ 版本号按约定修正为 1.6.0
版本号:
- 1.5.11 越界(规则 3 规定 patch ≤ 10)→ 修正为 1.6.0(进位到 minor、patch 归 0)
- versionCode 43 不变:43 从未对外发布,本次修正只是把命名拉回约定
合并支线(原在工作区根目录、App 仓库之外,DESIGN.md 自称"支线"):
- selfhost-source/server/ 自建歌曲源服务器(纯 Python 标准库、零 pip 依赖)
- selfhost-source/deploy/、DESIGN.md、LIBRARY.md、build_inventory.py
- selfhost-source/inventory/ 小清单(playlists.csv、song-stats.md 等)
- ⚠️ inventory 生成物(songs-*.csv / singers-top.csv,共 84MB)不入库,
已写入 .gitignore;需要时用 build_inventory.py 依据 muse.db 重新生成
同时入库的 App 侧支线改动(此前为工作树未提交状态):
- Whoami.kt(whoami 设备心跳)、DeviceIdentity.kt(设备身份伪装)
- PushDownload.kt / PushDownloadClient.kt(点歌推送)
- AdVideoFilter 带 query/fragment 的地址闸门;KTV_BRIDGE_API 11 → 13
- 修 ktv_api.js 的 global.KtvBridgeApiVersion(12 → 13,与注释及 Kotlin 两处校验一致)
- 对应单测:WhoamiTest / DeviceIdentityTest / PushDownloadTest / AdVideoFilterTest(共 39 例)
文档:
- DESIGN.md 的"在 App 仓库之外"约定改为"已并入仓库统一版本化"
- AGENTS.md 版本号行更新为 1.6.0/43,并新增支线并入说明
验证:assembleRelease + testReleaseUnitTest 全过(39 例)
This commit is contained in:
@@ -0,0 +1,245 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
麦动KTV 自建歌曲源 —— IIS **API 站**配置(站点 `mdmuse.kasugano.cn`,HTTPS 443)
|
||||
把这个文件放到该站点的根目录,文件名必须是 `web.config`。
|
||||
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ 本目录一共有三份配置,**这一份只管 API**(另一个站点只管文件下载): │
|
||||
│ * `api-site/web.config` ← 你正在看的:mdmuse.kasugano.cn(HTTPS 443) │
|
||||
│ * 同级的「媒体站」目录 md.kasugano.cn(HTTPS **4433**)只提供文件下载 │
|
||||
│ * `web.config`(上级目录) 只是"该看哪两份"的索引,本身不含任何规则 │
|
||||
│ 文件名与完整步骤见 `../README.md`(第 5 节「建两个站点」)。 │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
作用:把 App 需要的接口反代到内网 Linux 上跑的本服务
|
||||
(默认 `http://192.168.100.232:6677`,**必须替换成你自己的 IP:端口**)。
|
||||
|
||||
前置条件(缺一个就会 500.19 或 502,完整步骤见 `../README.md`):
|
||||
① IIS 装好 **URL Rewrite 2.1** 与 **Application Request Routing (ARR) 3.0**;
|
||||
② 启用 ARR 代理(ARR → Server Proxy Settings → 勾 Enable proxy → Apply),
|
||||
对应 `../applicationHost.config.snippet.xml` 里的 `<proxy enabled="true" ... />`;
|
||||
③ 放行服务器变量 HTTP_X_FORWARDED_FOR / HTTP_X_FORWARDED_PROTO / HTTP_X_REAL_IP /
|
||||
HTTP_X_FORWARDED_HOST,否则本文件的 `<serverVariables>` 会让站点报 500.19。
|
||||
|
||||
⚠️ 必须替换的占位值:
|
||||
- 后端地址 `http://192.168.100.232:6677` → 你内网 Linux 服务器的真实 IP:端口
|
||||
(本文件共 7 条反代规则 + 说明里的示例;用编辑器全局替换即可);
|
||||
- 域名 `mdmuse.kasugano.cn` → 你的 API 域名(站点绑定里也要一致);
|
||||
- `HTTP_X_FORWARDED_PROTO` 写死 `https`:只有本站点绑定了 HTTPS 证书才这样写;
|
||||
若站点同时有 HTTP 绑定,按文件末尾「HTTP 绑定」说明调整。
|
||||
|
||||
⚠️ 为什么这个站点里**一个文件下载路径都没有**:文件下载走另一个域名(另一个站点),
|
||||
这样"批量拉文件"的流量与"调接口"的流量在 IIS 层就分开了,互不影响;
|
||||
后端还会按 `X-Forwarded-Host` 再做一次宿主隔离(配置项见 `../README.md` 第 10 节),
|
||||
双重保险:谁也别想用 API 域名批量拖文件、也别想用下载域名调接口。
|
||||
|
||||
⚠️ 管理端口(**本文件里没有任何规则指向它**):公网因此访问不到管理界面 —— 这是
|
||||
"管理端口不对公网暴露"的第一道闸门(另外两道:IIS 里用 IP 限制 / 只绑内网地址,
|
||||
以及 Linux 侧防火墙;端口号与做法见 `../README.md` 的「管理端口」一节)。
|
||||
|
||||
⚠️ 查询串:URL Rewrite 默认 appendQueryString="true",会把原始 ?a=1&b=2 原样转给后端;
|
||||
这里显式写出来,避免以后有人改成 false 踩坑。{R:0} = 规则 pattern 匹配到的**整段路径**
|
||||
(不含开头的 /),所以路径原样透传。
|
||||
|
||||
⚠️ 请求方法:URL Rewrite 对**所有方法**(GET/HEAD/POST/PUT/DELETE…)都生效,
|
||||
不需要额外配置;本服务实际只用 GET/HEAD/POST。若站点装了 WebDAV 模块,它会在
|
||||
Rewrite 之前拦下 PUT/DELETE 并回 405(本项目用不到这两个方法)。
|
||||
-->
|
||||
<configuration>
|
||||
<system.webServer>
|
||||
|
||||
<!-- ══════════════ 反向代理规则(URL Rewrite + ARR)══════════════
|
||||
写法:<action type="Rewrite" url="http://后端/{R:0}" /> —— 目标写成绝对 URL 时,
|
||||
URL Rewrite 就会交给 ARR 做**反向代理**(不是 302 重定向)。
|
||||
每个规则都带 <serverVariables>,把真实客户端信息告诉后端:
|
||||
X-Forwarded-For = 客户端 IP(**有意只写 {REMOTE_ADDR}**:不拼接客户端自己传来的
|
||||
XFF,后端拿到的就是真实来源 IP,杜绝伪造链;
|
||||
后端还有 trusted_proxies 白名单兜底)
|
||||
X-Real-IP = 同上(有些反代/后端只认这一个)
|
||||
X-Forwarded-Proto = https(让后端把 /music/do.php 返回的下载地址拼成
|
||||
https://域名/…,而不是 http 内网地址)
|
||||
X-Forwarded-Host = 原始 Host(域名;既是后端拼绝对地址的依据,
|
||||
也是后端**宿主隔离**判定的依据 —— 媒体站靠它认出自己)
|
||||
-->
|
||||
<rewrite>
|
||||
<rules>
|
||||
|
||||
<!-- ① GET/POST /token(App 令牌:随机串,默认 10 分钟时效,可绑定客户端 IP)
|
||||
自有 App 的流程:先取令牌,再带请求头 X-App-Token 调 do.php / push_download。
|
||||
⚠️ 响应是 Cache-Control: no-store,中间层不要缓存它。 -->
|
||||
<rule name="ktv-app-token" stopProcessing="true">
|
||||
<match url="^token$" />
|
||||
<serverVariables>
|
||||
<set name="HTTP_X_FORWARDED_FOR" value="{REMOTE_ADDR}" />
|
||||
<set name="HTTP_X_REAL_IP" value="{REMOTE_ADDR}" />
|
||||
<set name="HTTP_X_FORWARDED_PROTO" value="https" />
|
||||
<set name="HTTP_X_FORWARDED_HOST" value="{HTTP_HOST}" />
|
||||
</serverVariables>
|
||||
<action type="Rewrite" url="http://192.168.100.232:6677/{R:0}" appendQueryString="true" />
|
||||
</rule>
|
||||
|
||||
<!-- ② POST /api.php(App 启动配置) -->
|
||||
<rule name="ktv-api-php" stopProcessing="true">
|
||||
<match url="^api\.php$" />
|
||||
<serverVariables>
|
||||
<set name="HTTP_X_FORWARDED_FOR" value="{REMOTE_ADDR}" />
|
||||
<set name="HTTP_X_REAL_IP" value="{REMOTE_ADDR}" />
|
||||
<set name="HTTP_X_FORWARDED_PROTO" value="https" />
|
||||
<set name="HTTP_X_FORWARDED_HOST" value="{HTTP_HOST}" />
|
||||
</serverVariables>
|
||||
<action type="Rewrite" url="http://192.168.100.232:6677/{R:0}" appendQueryString="true" />
|
||||
</rule>
|
||||
|
||||
<!-- ③ GET /i.php(老 App 取 token;**不带我们的 App 令牌**,后端默认不强制,
|
||||
但参数与签名算法一个字都没改,照常可用) -->
|
||||
<rule name="ktv-i-php" stopProcessing="true">
|
||||
<match url="^i\.php$" />
|
||||
<serverVariables>
|
||||
<set name="HTTP_X_FORWARDED_FOR" value="{REMOTE_ADDR}" />
|
||||
<set name="HTTP_X_REAL_IP" value="{REMOTE_ADDR}" />
|
||||
<set name="HTTP_X_FORWARDED_PROTO" value="https" />
|
||||
<set name="HTTP_X_FORWARDED_HOST" value="{HTTP_HOST}" />
|
||||
</serverVariables>
|
||||
<action type="Rewrite" url="http://192.168.100.232:6677/{R:0}" appendQueryString="true" />
|
||||
</rule>
|
||||
|
||||
<!-- ④ GET /music/do.php(点歌取址:返回的下载地址由后端按 X-Forwarded-* 拼成
|
||||
https 公网地址;老 App 带的 token 参数是它自己的老协议令牌,后端认得) -->
|
||||
<rule name="ktv-music-do-php" stopProcessing="true">
|
||||
<match url="^music/do\.php$" />
|
||||
<serverVariables>
|
||||
<set name="HTTP_X_FORWARDED_FOR" value="{REMOTE_ADDR}" />
|
||||
<set name="HTTP_X_REAL_IP" value="{REMOTE_ADDR}" />
|
||||
<set name="HTTP_X_FORWARDED_PROTO" value="https" />
|
||||
<set name="HTTP_X_FORWARDED_HOST" value="{HTTP_HOST}" />
|
||||
</serverVariables>
|
||||
<action type="Rewrite" url="http://192.168.100.232:6677/{R:0}" appendQueryString="true" />
|
||||
</rule>
|
||||
|
||||
<!-- ⑤ GET /push_download/status(推送计数,运维核对;注意放在 ⑥ 之前,
|
||||
虽然两条 pattern 互不重叠,但"长路径在前"是好习惯) -->
|
||||
<rule name="ktv-push-download-status" stopProcessing="true">
|
||||
<match url="^push_download/status$" />
|
||||
<serverVariables>
|
||||
<set name="HTTP_X_FORWARDED_FOR" value="{REMOTE_ADDR}" />
|
||||
<set name="HTTP_X_REAL_IP" value="{REMOTE_ADDR}" />
|
||||
<set name="HTTP_X_FORWARDED_PROTO" value="https" />
|
||||
<set name="HTTP_X_FORWARDED_HOST" value="{HTTP_HOST}" />
|
||||
</serverVariables>
|
||||
<action type="Rewrite" url="http://192.168.100.232:6677/{R:0}" appendQueryString="true" />
|
||||
</rule>
|
||||
|
||||
<!-- ⑥ POST /push_download(App 点歌推送:鉴权 = 合法 App 令牌 **或** push_token;
|
||||
限流按真实客户端 IP,靠上面的 XFF) -->
|
||||
<rule name="ktv-push-download" stopProcessing="true">
|
||||
<match url="^push_download$" />
|
||||
<serverVariables>
|
||||
<set name="HTTP_X_FORWARDED_FOR" value="{REMOTE_ADDR}" />
|
||||
<set name="HTTP_X_REAL_IP" value="{REMOTE_ADDR}" />
|
||||
<set name="HTTP_X_FORWARDED_PROTO" value="https" />
|
||||
<set name="HTTP_X_FORWARDED_HOST" value="{HTTP_HOST}" />
|
||||
</serverVariables>
|
||||
<action type="Rewrite" url="http://192.168.100.232:6677/{R:0}" appendQueryString="true" />
|
||||
</rule>
|
||||
|
||||
<!-- ⑦ GET /healthz(探活:给 IIS/负载均衡用;后端是纯内存快照,微秒级返回) -->
|
||||
<rule name="ktv-healthz" stopProcessing="true">
|
||||
<match url="^healthz$" />
|
||||
<serverVariables>
|
||||
<set name="HTTP_X_FORWARDED_FOR" value="{REMOTE_ADDR}" />
|
||||
<set name="HTTP_X_REAL_IP" value="{REMOTE_ADDR}" />
|
||||
<set name="HTTP_X_FORWARDED_PROTO" value="https" />
|
||||
<set name="HTTP_X_FORWARDED_HOST" value="{HTTP_HOST}" />
|
||||
</serverVariables>
|
||||
<action type="Rewrite" url="http://192.168.100.232:6677/{R:0}" appendQueryString="true" />
|
||||
</rule>
|
||||
|
||||
<!-- ⑧ 其余路径一律 404(不反代、不读本地文件):管理界面/管理接口/未列出的接口
|
||||
全部挡在外面。要临时放通别的路径,就在这条规则**之前**再复制一条上面的规则。 -->
|
||||
<rule name="ktv-block-everything-else" stopProcessing="true">
|
||||
<match url=".*" />
|
||||
<action type="CustomResponse" statusCode="404" statusReason="Not Found"
|
||||
statusDescription="404 Not Found" />
|
||||
</rule>
|
||||
|
||||
<!-- ⑨【可选·仅排查时启用】GET /api/client_ip:看服务端认定的客户端 IP / 是否来自
|
||||
可信代理 / 宿主判定结果(host、是否命中媒体宿主/API 宿主)。
|
||||
默认**故意不放通**(它会返回内网代理地址列表,属于内部信息);
|
||||
排查 X-Forwarded-For 是否生效时,把下面这段的注释去掉、排完再注释回去。
|
||||
另一种不改配置的做法:在 Linux 上直连服务端口自测(见 ../README.md「验证」)。
|
||||
<rule name="ktv-optional-client-ip" stopProcessing="true">
|
||||
<match url="^api/client_ip$" />
|
||||
<serverVariables>
|
||||
<set name="HTTP_X_FORWARDED_FOR" value="{REMOTE_ADDR}" />
|
||||
<set name="HTTP_X_REAL_IP" value="{REMOTE_ADDR}" />
|
||||
<set name="HTTP_X_FORWARDED_PROTO" value="https" />
|
||||
<set name="HTTP_X_FORWARDED_HOST" value="{HTTP_HOST}" />
|
||||
</serverVariables>
|
||||
<action type="Rewrite" url="http://192.168.100.232:6677/{R:0}" appendQueryString="true" />
|
||||
</rule>
|
||||
-->
|
||||
|
||||
<!-- ⑩【可选·仅运维时启用】GET /token/status:令牌计数(签发/校验失败/限流)。
|
||||
同样是内部信息,默认不放通。临时启用时用请求头 X-App-Token 调(开了强制的话)。
|
||||
<rule name="ktv-optional-token-status" stopProcessing="true">
|
||||
<match url="^token/status$" />
|
||||
<serverVariables>
|
||||
<set name="HTTP_X_FORWARDED_FOR" value="{REMOTE_ADDR}" />
|
||||
<set name="HTTP_X_REAL_IP" value="{REMOTE_ADDR}" />
|
||||
<set name="HTTP_X_FORWARDED_PROTO" value="https" />
|
||||
<set name="HTTP_X_FORWARDED_HOST" value="{HTTP_HOST}" />
|
||||
</serverVariables>
|
||||
<action type="Rewrite" url="http://192.168.100.232:6677/{R:0}" appendQueryString="true" />
|
||||
</rule>
|
||||
-->
|
||||
|
||||
</rules>
|
||||
</rewrite>
|
||||
|
||||
<!-- ══════════════ 上传/请求体限制 ══════════════
|
||||
maxAllowedContentLength 是 IIS 自己先卡的一道(单位**字节**,默认 30MB)。
|
||||
本站点公开的接口请求体都很小(推送是一条表单),这里仍给到 32MiB 作为余量。
|
||||
⚠️ 它必须 **≥ 后端配置 max_body_bytes(默认 8MiB)**,否则超限时是 IIS 先返回它自己的
|
||||
HTML 413,而不是后端的 JSON + 中文说明,两边行为就不一致了。 -->
|
||||
<security>
|
||||
<requestFiltering>
|
||||
<requestLimits maxAllowedContentLength="33554432" />
|
||||
</requestFiltering>
|
||||
</security>
|
||||
|
||||
<!-- 整站关掉静态/动态压缩:本站点没有可压缩的大响应,保持与另一个站点一致,
|
||||
避免以后误加规则时把分块/长度搞乱(压缩会让 Content-Length 与 Range 语义失真)。 -->
|
||||
<urlCompression doStaticCompression="false" doDynamicCompression="false" />
|
||||
|
||||
<!-- 站点若装了 WebDAV,它会在 URL Rewrite 之前拦下 PUT/DELETE 并回 405。
|
||||
本项目只用 GET/HEAD/POST,一般无需处理;确实遇到 405 就取消下面的注释。
|
||||
<modules>
|
||||
<remove name="WebDAVModule" />
|
||||
</modules>
|
||||
<handlers>
|
||||
<remove name="WebDAV" />
|
||||
</handlers>
|
||||
-->
|
||||
|
||||
</system.webServer>
|
||||
</configuration>
|
||||
|
||||
<!--
|
||||
══════════════ 若站点同时绑定了 HTTP(不带证书)══════════════
|
||||
上面每条规则的 HTTP_X_FORWARDED_PROTO 都写死了 "https"。如果这个站点**同时**有 http 和
|
||||
https 两个绑定,需要让 http 走 http、https 走 https。两种做法:
|
||||
|
||||
做法 A(推荐):把 proto 写成条件分支 —— 删掉每条规则里的
|
||||
<set name="HTTP_X_FORWARDED_PROTO" value="https" />
|
||||
改成在规则里加条件与两条设置(URL Rewrite 不支持条件赋值,所以要拆成两条规则):
|
||||
<conditions><add input="{HTTPS}" pattern="^ON$" /></conditions> → value="https"
|
||||
<conditions><add input="{HTTPS}" pattern="^ON$" negate="true" /></conditions> → value="http"
|
||||
|
||||
做法 B(更省事):干脆只保留 HTTPS 绑定(HTTP 绑定删掉或改成 301 跳转到 HTTPS),
|
||||
那么写死 "https" 就是对的。
|
||||
|
||||
⚠️ 忘记改的后果:HTTP 访问时会话里返回的下载地址是 https://…,或者 HTTPS 站点返回
|
||||
http://内网主机名(App 拒收)。定位办法:看后端的 `GET /api/client_ip`
|
||||
(默认不对外暴露;临时放通 / 内网直连两种方式见 ../README.md),
|
||||
它会给出服务端认定的真实客户端 IP / proto / host,一眼就能看出是谁给的。
|
||||
-->
|
||||
Reference in New Issue
Block a user