docs: 补充独立站全自动复现与网络部署教程
This commit is contained in:
@@ -2,6 +2,14 @@
|
|||||||
|
|
||||||
## 2026-08-17
|
## 2026-08-17
|
||||||
|
|
||||||
|
### 全自动复现与网络环境教程
|
||||||
|
|
||||||
|
- 扩展 README,提供可直接复制的私有 GitHub 仓库 + GitHub Actions 更新、测试、数据提交、静态制品上传和公开快照校验流程。
|
||||||
|
- 说明通过 Cloudflare Pages 监听 Actions 数据提交复现主站自动发布效果,并明确 `SANSHENG_SITE_URL`、`PUBLIC_SITE_URL`、仓库权限、构建命令和输出目录。
|
||||||
|
- 分别给出有外网 Windows/Linux Server、Web Server 无外网但有更新节点、任何机器都无境外网络能力时的运行、制品传输、静态发布和回滚路径。
|
||||||
|
- 明确 GitHub/Gitea/计划任务只是调度层,核心代码无需为某个 CI 平台分叉;全网无可用出口时不能伪装为全量自动更新。
|
||||||
|
- 验证 README 中的 Actions YAML 可正常解析且包含 10 个必要步骤;Python 12 项、Node 14 项测试和独立站静态构建通过。
|
||||||
|
|
||||||
### 更新时间字段与自托管交接
|
### 更新时间字段与自托管交接
|
||||||
|
|
||||||
- 修正公开 JSON 快照把 `generated_at` 固定为 `2026-08-17T00:00:00Z` 的问题,恢复为真实 UTC 生成时刻;`as_of_date` 继续只表示北京时间业务日期。
|
- 修正公开 JSON 快照把 `generated_at` 固定为 `2026-08-17T00:00:00Z` 的问题,恢复为真实 UTC 生成时刻;`as_of_date` 继续只表示北京时间业务日期。
|
||||||
|
|||||||
@@ -244,24 +244,183 @@ C# 实现必须保留以下语义:稳定 ID、UTC `generated_at`、业务 `as_
|
|||||||
|
|
||||||
不要一次性重写全部抓取器,也不要先引入 LLM。先让 C# 后端稳定消费现有 JSON,再按源逐步替换,最容易定位数据差异和回滚。
|
不要一次性重写全部抓取器,也不要先引入 LLM。先让 C# 后端稳定消费现有 JSON,再按源逐步替换,最容易定位数据差异和回滚。
|
||||||
|
|
||||||
## 不依赖 GitHub Pages 的自动部署
|
## 复现主站的全自动更新与部署
|
||||||
|
|
||||||
公司 Gitea 没有 Pages 不影响部署。推荐由 Gitea Runner、Windows 计划任务或公司 CI 执行:
|
独立站不依赖 GitHub Actions 或 Pages,但现有命令可以直接接入 GitHub Actions、Gitea Runner、Windows 计划任务和 Linux `systemd`。研发人员应先按实际网络与服务器条件选择方案,不要在 Web 请求中执行抓取。
|
||||||
|
|
||||||
|
| 条件 | 推荐方案 | 更新节点 | Web 节点 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| 希望最快复现主站效果 | 私有 GitHub 仓库 + GitHub Actions + Cloudflare Pages | GitHub Hosted Runner | Cloudflare Pages |
|
||||||
|
| 公司有可访问全部源站的 Server | Gitea Runner、Windows 计划任务或 Linux `systemd` | 公司 Server | 同机 IIS/Nginx,或另一台内网 Web Server |
|
||||||
|
| Web Server 无外网,但有其他外网节点 | 外网更新节点生成 `dist/`,通过制品库、SFTP、SMB 或对象存储传入 | 外网更新节点 | 只接收并发布 `dist/` |
|
||||||
|
| 公司任何机器都不能访问部分境外源 | 合规代理、统一出口、GitHub Hosted Runner 或外部更新 Worker | 具备合法网络出口的节点 | 内网 Web Server |
|
||||||
|
|
||||||
|
### 最快复现:私有 GitHub 仓库 + GitHub Actions
|
||||||
|
|
||||||
|
该方案最接近个人主站:GitHub Actions 每天抓取、测试、构建并提交新数据;Cloudflare Pages 监听数据提交并重新发布 `dist/`。GitHub 仓库必须设为私有,避免把翻译缓存、信源状态和运行信息公开。
|
||||||
|
|
||||||
|
1. 在 GitHub 创建一个私有空仓库,把公司 Gitea 中的本项目推送到该仓库的 `main` 分支。这个 GitHub 仓库可以只是研发复现环境,不要求替代公司 Gitea 主仓库。
|
||||||
|
2. 打开 `Settings -> Actions -> General -> Workflow permissions`,选择 `Read and write permissions`,允许工作流提交更新后的数据快照。
|
||||||
|
3. 打开 `Settings -> Secrets and variables -> Actions -> Variables`,创建:
|
||||||
|
- `SANSHENG_SITE_URL`:独立站最终访问根地址,不带末尾 `/`。
|
||||||
|
- `PUBLIC_SITE_URL`:已经部署完成并可访问的站点根地址;尚未部署时可以暂不创建。
|
||||||
|
4. 在 GitHub 仓库创建 `.github/workflows/update-standalone.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: Update Sansheng Intelligence
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
# UTC 01:30,即北京时间 09:30。
|
||||||
|
- cron: "30 1 * * *"
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: update-sansheng-intelligence-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 35
|
||||||
|
env:
|
||||||
|
SANSHENG_SITE_URL: ${{ vars.SANSHENG_SITE_URL }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: Setup Python
|
||||||
|
uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: "3.11"
|
||||||
|
cache: pip
|
||||||
|
cache-dependency-path: requirements.txt
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: "24"
|
||||||
|
|
||||||
|
- name: Validate configuration
|
||||||
|
run: |
|
||||||
|
if [ -z "$SANSHENG_SITE_URL" ]; then
|
||||||
|
echo "Repository variable SANSHENG_SITE_URL is required"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
- name: Update data and build dist
|
||||||
|
run: python scripts/update_all.py --site-url "$SANSHENG_SITE_URL"
|
||||||
|
|
||||||
|
- name: Run contracts
|
||||||
|
run: |
|
||||||
|
python -m unittest discover -s tests -p "test_*.py"
|
||||||
|
node --test tests/test_frontend.mjs
|
||||||
|
|
||||||
|
- name: Commit data snapshots
|
||||||
|
run: |
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||||
|
git add \
|
||||||
|
data/textile-intelligence.json \
|
||||||
|
data/textile-trends.json \
|
||||||
|
data/textile-intelligence-source-status.json \
|
||||||
|
data/textile-zh-cache.json
|
||||||
|
if git diff --cached --quiet; then
|
||||||
|
echo "No data changes to commit"
|
||||||
|
else
|
||||||
|
git commit -m "chore: update sansheng intelligence snapshot"
|
||||||
|
git push
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Upload static artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: sansheng-intelligence-dist
|
||||||
|
path: dist/
|
||||||
|
if-no-files-found: error
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
|
- name: Verify public deployment
|
||||||
|
if: ${{ vars.PUBLIC_SITE_URL != '' }}
|
||||||
|
run: |
|
||||||
|
python scripts/verify_public_snapshot.py \
|
||||||
|
--base-url "${{ vars.PUBLIC_SITE_URL }}" \
|
||||||
|
--expected-file data/textile-intelligence.json \
|
||||||
|
--retries 12 \
|
||||||
|
--interval 30
|
||||||
|
```
|
||||||
|
|
||||||
|
5. 在 GitHub 的 `Actions` 页面手动运行一次 `Update Sansheng Intelligence`。首次验收应确认:抓取成功、测试通过、仓库出现数据提交、`sansheng-intelligence-dist` 制品可以下载并直接由静态服务器访问。
|
||||||
|
6. 若使用 Cloudflare Pages,在 Pages 项目中连接这个 GitHub 仓库并配置:
|
||||||
|
- 生产分支:`main`。
|
||||||
|
- 构建命令:`python scripts/build_site.py --site-url "$SANSHENG_SITE_URL"`。
|
||||||
|
- 输出目录:`dist`。
|
||||||
|
- 环境变量:`SANSHENG_SITE_URL` 使用与 GitHub 仓库变量相同的正式站点根地址。
|
||||||
|
7. Cloudflare Pages 会在 Actions 推送数据提交后自动构建。设置 `PUBLIC_SITE_URL` 后,Actions 最后一步会比较公开 JSON 与本次数据;公开站仍是旧快照时工作流会失败,不能把“构建成功”误当成“部署成功”。
|
||||||
|
|
||||||
|
GitHub Hosted Runner 的公网出口通常比内地 Server 更适合抓取境外官方源,但仍可能被个别源站按机房 IP、地区或反爬规则拒绝。项目的严格模式会保留旧版并拒绝发布不完整数据;不要为了让工作流变绿而删除 `--strict` 或绕过 `source_target == source_success`。
|
||||||
|
|
||||||
|
### 有外网访问能力的 Windows/Linux Server
|
||||||
|
|
||||||
|
这种环境不需要 GitHub,也不需要 Pages。Server 直接从公司 Gitea 检出代码,作为更新节点运行:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
检出仓库
|
检出或更新代码
|
||||||
-> 创建 Python 虚拟环境并安装 requirements.txt
|
-> 创建 Python 虚拟环境并安装 requirements.txt
|
||||||
|
-> 设置 SANSHENG_SITE_URL
|
||||||
|
-> 运行 update_all.py
|
||||||
-> 运行 Python 与 Node 契约测试
|
-> 运行 Python 与 Node 契约测试
|
||||||
-> 设置唯一 SANSHENG_SITE_URL
|
-> 得到不可变的 dist/ 制品
|
||||||
-> 运行 update_all.py(需要联网更新)或 build_site.py(只发布已提交快照)
|
-> 发布到 IIS/Nginx 新版本目录
|
||||||
-> 把 dist/ 作为构建制品
|
-> 请求公开 JSON 校验 generated_at 和 as_of_date
|
||||||
-> 同步到 IIS/Nginx 新版本目录
|
|
||||||
-> 健康检查通过后切换目录/发布槽
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Windows Server 可以复用 `update.bat` 和 `install-schedule.bat`,或由 Task Scheduler/Windows Service 定时运行:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
git pull --ff-only
|
||||||
|
$env:SANSHENG_SITE_URL='https://实际独立站域名'
|
||||||
|
.\.venv\Scripts\python.exe scripts\update_all.py --site-url $env:SANSHENG_SITE_URL
|
||||||
|
.\.venv\Scripts\python.exe -m unittest discover -s tests -p 'test_*.py'
|
||||||
|
node --test tests\test_frontend.mjs
|
||||||
|
```
|
||||||
|
|
||||||
|
Linux Server 使用同一组 Python 命令,由 `systemd timer`、Cron 或 Gitea Runner 调度;构建成功后把 `dist/` 同步到新的 Nginx 版本目录,再通过 `current` 软链接原子切换。完整的 Windows/IIS、Linux/Nginx、版本目录和回滚方式见 [Java/C# 自托管方案](docs/SELF_HOSTING_JAVA_CSHARP.md)。
|
||||||
|
|
||||||
|
### Web Server 没有外网,但存在外网更新节点
|
||||||
|
|
||||||
|
这是公司内网环境的推荐架构。外网更新节点可以是 GitHub Hosted Runner、公司代理区 Server、境外云主机或具备统一出口的 Gitea Runner;内网 Web Server 不运行抓取代码。
|
||||||
|
|
||||||
|
1. 外网更新节点运行 `update_all.py`,生成完整 `dist/`。
|
||||||
|
2. 将 `dist/` 压缩为带版本时间和提交 SHA 的制品,并生成 SHA-256。
|
||||||
|
3. 通过公司制品库、对象存储、SFTP、SMB 或人工审批通道传入内网。
|
||||||
|
4. Web Server 把制品解压到新版本目录,校验 SHA-256、JSON、Sitemap 和首页。
|
||||||
|
5. 验证通过后切换 IIS 物理路径或 Nginx `current` 软链接;失败则继续服务旧版本。
|
||||||
|
6. 发布完成后,在能访问公开站点的节点运行 `verify_public_snapshot.py`。
|
||||||
|
|
||||||
Web 节点只接收 `dist/`。不要把仓库、`.venv`、`data/textile-zh-cache.json`、`data/textile-intelligence-source-status.json`、`runtime/` 或抓取日志放进网站目录。IIS/Nginx 必须优先返回真实静态文件,不能让 `/data/*.json` 被 SPA fallback 替换成首页 HTML。
|
Web 节点只接收 `dist/`。不要把仓库、`.venv`、`data/textile-zh-cache.json`、`data/textile-intelligence-source-status.json`、`runtime/` 或抓取日志放进网站目录。IIS/Nginx 必须优先返回真实静态文件,不能让 `/data/*.json` 被 SPA fallback 替换成首页 HTML。
|
||||||
|
|
||||||
仓库没有预置绑定具体服务器账号的 Gitea workflow,因为 Runner 标签、发布协议、IIS 路径和密钥名尚未确定。确定这些公司环境参数后,只需把上面的命令接入现有流水线,不需要 Pages。
|
### 任何可用机器都没有境外网络能力
|
||||||
|
|
||||||
|
这种情况下不能复现“全部官方源自动更新”。代码无法替代网络出口,也不能安全地用空数据或旧时间伪装成功。只能选择以下路径之一:
|
||||||
|
|
||||||
|
- 由公司网络团队提供合规的 `HTTP_PROXY`、`HTTPS_PROXY`、统一出口或官方域名白名单。
|
||||||
|
- 使用 GitHub Hosted Runner、外部更新 Worker 或具有合法外网能力的云主机生成制品,再传入内网。
|
||||||
|
- 由研发人员在具备外网能力的工作站运行 `update.bat`,人工提交带 SHA-256 的 `dist/` 制品。
|
||||||
|
- 明确缩减信源范围,并同步调整数据契约、严格源数量和产品承诺;不能静默跳过不可访问源。
|
||||||
|
|
||||||
|
如果没有任何合规外网节点,也不允许人工制品交接,就只能基于仓库里最后一次已验证快照运行 `build_site.py`,可以持续提供静态站,但不能声称数据仍在自动更新。
|
||||||
|
|
||||||
|
仓库不预置绑定具体服务器账号的 Gitea 部署 workflow,因为 Runner 标签、发布协议、IIS/Nginx 路径、制品库和密钥名必须由公司环境决定。确定这些参数后,把本节相同命令接入现有流水线即可,不需要修改抓取、数据结构或前端代码。
|
||||||
|
|
||||||
## SEO/GEO 基线
|
## SEO/GEO 基线
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user