docs: 补充独立站全自动复现与网络部署教程
This commit is contained in:
@@ -244,24 +244,183 @@ C# 实现必须保留以下语义:稳定 ID、UTC `generated_at`、业务 `as_
|
||||
|
||||
不要一次性重写全部抓取器,也不要先引入 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
|
||||
检出仓库
|
||||
检出或更新代码
|
||||
-> 创建 Python 虚拟环境并安装 requirements.txt
|
||||
-> 设置 SANSHENG_SITE_URL
|
||||
-> 运行 update_all.py
|
||||
-> 运行 Python 与 Node 契约测试
|
||||
-> 设置唯一 SANSHENG_SITE_URL
|
||||
-> 运行 update_all.py(需要联网更新)或 build_site.py(只发布已提交快照)
|
||||
-> 把 dist/ 作为构建制品
|
||||
-> 同步到 IIS/Nginx 新版本目录
|
||||
-> 健康检查通过后切换目录/发布槽
|
||||
-> 得到不可变的 dist/ 制品
|
||||
-> 发布到 IIS/Nginx 新版本目录
|
||||
-> 请求公开 JSON 校验 generated_at 和 as_of_date
|
||||
```
|
||||
|
||||
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。
|
||||
|
||||
仓库没有预置绑定具体服务器账号的 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 基线
|
||||
|
||||
|
||||
Reference in New Issue
Block a user