| .scratch/server-testenv/README.md | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| .scratch/server-testenv/nginx-talent-card.conf | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| rencaika/config/api.uts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| rencaika_merchant/config/api.uts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
.scratch/server-testenv/README.md
@@ -55,14 +55,19 @@ > 命令通道单次只能带约 4KB,所以 dist(4.4MB)走不了命令通道——这是当初需要人工 SFTP 传包的原因, > 现在用「服务器自建」绕过去了。 ## HTTPS(2026-09-29 已上线) ## HTTPS(2026-09-29 已上线;2026-10-08 域名变更) > **2026-10-08 域名变更**:甲方把 `www.dqrcfwpt.com` 与退域名 `dqrcfwpt.com` 解析到了他们自己的机器(公司官网), > 那两个名字**已不在我们手上**。测试环境改用新建子域 **`rencaika.dqrcfwpt.com`**,证书换成 > **Let's Encrypt(certbot 自动续期)**。本节里凡提到 `www` / `:8081` 的表述都是**变更前**的历史; > 当前口径以下面「端口」与「证书」两行为准。另:旧证书是**腾讯云**签发(不是华为云)。 | 项 | 值 | |---|---| | 证书 | 华为云签发的 DV(TrustAsia 签发)。`/etc/nginx/ssl/talent-card/server.crt` 是**含中间链的 bundle**(4451 字节),`server.key` 权限 `600` | | 覆盖域名(SAN) | `dqrcfwpt.com` + `www.dqrcfwpt.com` | | **到期** | **2027-02-11** ⚠️ **不会自动续到本机** —— 到期前在华为云重新下载再传一次(或那时改用 Let's Encrypt:80 已通,certbot 一条命令且自带自动续期) | | 端口 | **8081 = 测试环境(TLS)**:`https://www.dqrcfwpt.com:8081/`;**8082 = 测试环境(明文兜底)**:`http://117.172.73.106:8082/`;**443 = 生产预留,现在是占位页**(`/data/talent-card/placeholder/index.html`,正文「本域名对应的生产环境尚未部署」);80 只做 301 跳转到 https | | 证书(两套并存,nginx 按 SNI 选) | ① **`rencaika.dqrcfwpt.com`** → **Let's Encrypt**:`/etc/letsencrypt/live/rencaika.dqrcfwpt.com/fullchain.pem` + `privkey.pem`;② `www`/退域名 → **腾讯云**签发的 DV:`/etc/nginx/ssl/talent-card/server.crt`(**含中间链的 bundle**,4451 字节)+ `server.key`(`600`)。**之后换腾讯云证书到新子域:只改 443/8081 那两块里的两行路径再 reload,不需要动别的。** | | 覆盖域名(SAN) | ① `rencaika.dqrcfwpt.com`(LE 那张)② `dqrcfwpt.com` + `www.dqrcfwpt.com`(腾讯云那张) | | **到期** | ① **2027-01-06**(LE,**`certbot.timer` 自动续期,不用人管** —— 前提是 80 上的 `/.well-known/acme-challenge/` 直读例外别删)② **2027-02-11**(腾讯云那张,**人工**:到期前重新下载再传一次) | | 端口 | **`rencaika.dqrcfwpt.com` 就是测试环境**:**443**(`https://rencaika.dqrcfwpt.com`,正式入口、**不带端口**)与 **8081**(同机兼容)都提供完整功能;**8082 = 明文兜底**(`http://117.172.73.106:8082/`,给 IP 访问;HSTS 管不到 IP,这条永远可用);**80 只做 301**,且给 `/.well-known/acme-challenge/` 留了直读例外(**删了证书就自动续期不了,且是静默失败**)。旧名字 `www`/退域名的 443 仍挂着生产占位页,但甲方已把那两个名字指走,**那段实际不生效、可删**。 | | ⚠️ 为什么 8081 必须是 TLS、明文要另开 8082 | 443 上发了 HSTS,而 **HSTS 按「主机」生效、覆盖该主机的所有端口**(RFC 6797 §8.3),且升级时**显式端口会保留**(`http://host:8081` → `https://host:8081`)。所以 8081 若只说纯 HTTP,浏览器(只要访问过一次域名)一律打不开。**也不能让 nginx 用两个 server 块在 8081 上分别 listen 明文与 ssl** —— 同一个 address:port 的 ssl 标志必须一致,混用直接校验失败,报 `no "ssl_certificate" is defined for the "listen ... ssl" directive`,而且**错误行号指向那个明文块**,极易看错地方(2026-09-29 就在这里绕了几圈)。所以拆成 8081(TLS) + 8082(明文)。 | | 生产上线时 | 把 443 块里的 `root` 与 `location` 换成 `include snippets/talent-card-locations.conf;`,80 的 301 不用动。该域名上的 `/api` 目前返回 503 JSON,避免请求落到静态页被误解为可用 | | HSTS | `max-age=86400`(1 天),**没有** `includeSubDomains` / `preload` | @@ -77,11 +82,11 @@ - **汇付的五个回调地址**现在可以是真实公网 HTTPS 了,形如 `https://www.dqrcfwpt.com/api/payments/huifu/notify`——这解掉了 `.scratch/pending-order-payment-flow/README.md` 里 F1 那个长期项。**要在汇付控台逐条注册**,且与本地 yml 的 `HUIFU_*_NOTIFY_URL` 保持一致。 - **微信小程序的服务器域名**可以填 `https://www.dqrcfwpt.com`(request / uploadFile / downloadFile 都要)。 - **微信小程序的服务器域名**填 **`https://rencaika.dqrcfwpt.com`**(request / uploadFile / downloadFile 都要,**不带端口**)。 **还没做的:** - 退域名 `dqrcfwpt.com` **没有 A 记录**(公网解析实测 `ENODATA`)→ `https://dqrcfwpt.com` 用不了。 在腾讯云补一条 `@` 的 A 记录即可(**证书已覆盖它,不用重签**)。 - ~~退域名 `dqrcfwpt.com` 没有 A 记录~~ **已作废**:2026-10-08 起 `dqrcfwpt.com` 与 `www` 都解析到甲方的机器 (公司官网),我们不再持有这两个名字,原来那条「补 A 记录」的待办随之取消。 - **8081 仍是全公网可达的明文端口**,上面挂着后台 API。HTTPS 已稳定,建议请云管理方关掉它; 在那之前它仍是唯一的明文入口。 - `CORS_ALLOWED_ORIGINS` 里没有 `http://www.dqrcfwpt.com`(80 那条)——**已无关紧要**,因为 80 现在 301 到 https。 @@ -92,8 +97,8 @@ | 位置 | 值 | |---|---| | 代码 | `rencaika/config/api.uts` 与 `rencaika_merchant/config/api.uts`:`apiEnvironment = 'staging'` + `stagingBaseUrl = 'https://www.dqrcfwpt.com:8081'` | | 微信后台 → 开发管理 → 服务器域名 | request / uploadFile / downloadFile **都填** `https://www.dqrcfwpt.com:8081`;socket / udp / tcp **留空**(两个小程序零 WebSocket 用法,已 grep 确认) | | 代码 | `rencaika/config/api.uts` 与 `rencaika_merchant/config/api.uts`:`apiEnvironment = 'staging'` + `stagingBaseUrl = 'https://rencaika.dqrcfwpt.com'` | | 微信后台 → 开发管理 → 服务器域名 | request / uploadFile / downloadFile **都填** `https://rencaika.dqrcfwpt.com`(**不带端口**,与代码里的 `stagingBaseUrl` 一致);socket / udp / tcp **留空**(两个小程序零 WebSocket 用法,已 grep 确认) | | 体验成员 | 「成员管理」里逐个加微信号;配额以后台实时提示为准 | - **两个 appid 各配一遍**:人才端 `wxcfee477acb54d179`(后端 `app.wechat.mini-program.app-id` 与它一致)、商户端 `wx550e520ad19458ea`。 .scratch/server-testenv/nginx-talent-card.conf
@@ -1,34 +1,71 @@ # 东区人才卡 —— 站点(2026-09-29) # 东区人才卡 —— 站点(2026-10-08) # # 部署位置:/etc/nginx/sites-available/talent-card (软链到 sites-enabled/) # # 【域名现状】甲方把 `www.dqrcfwpt.com` 与退域名 `dqrcfwpt.com` 指向了他们自己的机器 # (公司官网,腾讯云 IP),所以那两个名字**已经不在我们手上**。测试环境改用新建的 # 子域 **`rencaika.dqrcfwpt.com`**(解析到本机)。 # # 端口分工: # 8081 → **测试环境(TLS 入口)**:https://www.dqrcfwpt.com:8081/ # 8082 → **测试环境(明文兜底)**:http://117.172.73.106:8082/ (用 IP 访问时走它) # 80 → 只做 301 跳到 https # 443 → 这个域名留给**生产环境**,目前**尚未部署**,所以只显示占位页 # rencaika.dqrcfwpt.com:443 → **测试环境(正式入口,不带端口)** # rencaika.dqrcfwpt.com:8081 → 测试环境(同机兼容,TLS;小程序白名单也可以填它) # 8082(明文,server_name _) → 测试环境兜底,给 IP 访问用(HSTS 管不到 IP,这条永远可用) # 80 → 301 到 https(但给 ACME 校验留例外,见下) # www/退域名 的 443 → 生产占位页(那两个名字已不在我们手上,这段是残留,可删) # # ⚠️ 为什么 8081 必须是 TLS、明文入口要另开一个端口: # 443 上发了 HSTS(max-age=86400),而 **HSTS 按“主机”生效、覆盖该主机的所有端口** # (RFC 6797 §8.3:「the HSTS Policy applies to HTTP over any TCP port of an HSTS Host」), # 且升级时**显式端口会被保留**(`http://host:8081` → `https://host:8081`)。 # 所以浏览器一旦收过 HSTS,`http://www.dqrcfwpt.com:8081` 就会被强制升级成 https —— # 8081 若只说纯 HTTP,用户看到的就是“打不开”。 # 【证书】两套并存,nginx 按 SNI 选: # * `rencaika.dqrcfwpt.com` → Let's Encrypt(certbot 自动续期,2027-01-06 到期, # 续期由 certbot.timer 负责)。**换腾讯云证书时只需把下面两行路径指过去,reload 即可。** # * `www`/退域名 → 腾讯云签发的 DV(SAN: dqrcfwpt.com + www.dqrcfwpt.com,人工续期) # # 那能不能让 8081 同时说明文和 TLS?**不能让 nginx 用两个 server 块分别 listen**: # nginx 要求同一个 address:port 的 ssl 标志一致,混用会直接校验失败 # (报 `no "ssl_certificate" is defined for the "listen ... ssl" directive`, # 而且错误会指向那个**明文**块的行号,很容易看错地方)。所以明文入口拆到 8082。 # ⚠️ 80 上的 ACME 例外不能删:Let's Encrypt 用 HTTP-01 校验续期,它要能读到 # http://<域名>/.well-known/acme-challenge/<token>。加 301 时必须给这个前缀留出直读路径, # 否则**自动续期会静默失败**,到期那天才发现。 # # 生产上线时怎么切:把 443 那个 server 块里的 `root` 与两个 location 换成 # `root <生产的 dist 目录>;` + `include snippets/talent-card-locations.conf;`,80 的 301 不用动。 # # 证书:华为云 DV(TrustAsia 签发),SAN 覆盖 `dqrcfwpt.com` 与 `www.dqrcfwpt.com`。 # `/etc/nginx/ssl/talent-card/server.crt` **必须含中间链**(bundle,4451 字节)。 # **2027-02-11 到期,且不会自动续到本机** —— 到期前重新下载再传一次。 # HSTS 仍是第一步:max-age=86400,无 includeSubDomains / preload。 # 测试环境 · TLS 入口(域名) # ── 测试环境 · 新子域(TLS,443 = 正式入口)────────────────────────────── server { listen 443 ssl; http2 on; server_name rencaika.dqrcfwpt.com; ssl_certificate /etc/letsencrypt/live/rencaika.dqrcfwpt.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/rencaika.dqrcfwpt.com/privkey.pem; ssl_trusted_certificate /etc/letsencrypt/live/rencaika.dqrcfwpt.com/chain.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers off; ssl_session_cache shared:SSL:10m; ssl_session_timeout 1d; # 刻意不配 ssl_stapling:Let's Encrypt 的证书已不再包含 OCSP 响应者地址, # 配了只会每次 reload 打一条 "ssl_stapling ignored" 警告,留一行永不生效的配置更容易误导人。 add_header Strict-Transport-Security "max-age=86400" always; include snippets/talent-card-locations.conf; } # ── 测试环境 · 新子域的 8081(与 443 同一套内容,小程序白名单填哪个都行)── server { listen 8081 ssl; http2 on; server_name rencaika.dqrcfwpt.com; ssl_certificate /etc/letsencrypt/live/rencaika.dqrcfwpt.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/rencaika.dqrcfwpt.com/privkey.pem; ssl_trusted_certificate /etc/letsencrypt/live/rencaika.dqrcfwpt.com/chain.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers off; ssl_session_cache shared:SSL:10m; ssl_session_timeout 1d; include snippets/talent-card-locations.conf; } # ── 旧名字(www / 退域名)的 8081:那两个名字已不在我们手上,保留只为兼容 ── server { listen 8081 ssl; http2 on; @@ -50,19 +87,28 @@ include snippets/talent-card-locations.conf; } # 测试环境 · 明文兜底(IP 访问用;HSTS 管不到 IP,所以这条永远可用) # ── 明文兜底(IP 访问用;HSTS 管不到 IP,所以这条永远可用)────────────── server { listen 8082; server_name _; include snippets/talent-card-locations.conf; } # ── 80:跳转到 https,但 ACME 校验直读 ──────────────────────────────── server { listen 80; server_name www.dqrcfwpt.com dqrcfwpt.com; return 301 https://$host$request_uri; server_name rencaika.dqrcfwpt.com; location ^~ /.well-known/acme-challenge/ { root /var/www/html; } location / { return 301 https://$host$request_uri; } } # ── 生产预留占位页(旧名字;那两个名字已指向甲方机器,这段实际已不生效)──── server { listen 443 ssl; http2 on; rencaika/config/api.uts
@@ -11,8 +11,8 @@ // 后台提供合法域名后仅在此处填写,业务页面不得硬编码域名。 // 开发环境地址由 development-server.uts 在启动时自动探测本机局域网 IP,换网后不需要改代码; // 本机 IP 会变(见过 192.168.110.26 与 192.168.110.15),写死会表现成"登录不上"。 // 必须与微信后台「开发管理 → 服务器域名」里配的完全一致(含端口)。 const stagingBaseUrl = 'https://www.dqrcfwpt.com:8081' // 必须与微信后台「开发管理 → 服务器域名」里配的完全一致(不带端口时,请求 URL 也不能带端口)。 const stagingBaseUrl = 'https://rencaika.dqrcfwpt.com' const productionBaseUrl = '' export const getApiBaseUrl = (): string => { rencaika_merchant/config/api.uts
@@ -21,8 +21,8 @@ // 后台提供微信小程序合法 HTTPS 域名后,在对应环境填写完整基础地址。 // 开发环境地址由 development-server.uts 在启动时自动探测本机局域网 IP,换网后不需要改代码; // 本机 IP 会变(见过 192.168.110.26 与 192.168.110.15),写死会表现成"登录不上"。 // 必须与微信后台「开发管理 → 服务器域名」里配的完全一致(含端口)。 const stagingBaseUrl = 'https://www.dqrcfwpt.com:8081' // 必须与微信后台「开发管理 → 服务器域名」里配的完全一致(不带端口时,请求 URL 也不能带端口)。 const stagingBaseUrl = 'https://rencaika.dqrcfwpt.com' const productionBaseUrl = '' export const getApiBaseUrl = (): string => {