diff --git a/.spec/specs/2026-10-24_add-blog-infra/tasks.md b/.spec/specs/2026-10-24_add-blog-infra/tasks.md index b0ddda9..845e927 100644 --- a/.spec/specs/2026-10-24_add-blog-infra/tasks.md +++ b/.spec/specs/2026-10-24_add-blog-infra/tasks.md @@ -19,7 +19,7 @@ - boundary: 只读调用 127.0.0.1:3000 的模型列表与一次 chat 调用;证据写 `evidence/new-api-check.json`;不改 new-api 配置 - verify: `test -s .spec/specs/2026-10-24_add-blog-infra/evidence/new-api-check.json` -- [ ] T3 gitea 部署(容器 13020,nginx conf 草稿不安装) +- [x] T3 gitea 部署(容器 13020,nginx conf 草稿不安装) - claim: op-gitea 2026-10-24 - boundary: 新建 `~/docker_apps/gitea/`(compose+数据卷);容器仅绑 127.0.0.1:13020;初始化管理员与 blog 空仓;nginx conf 草稿与 app.ini 上传限制调整(MAX_FILES=20)写进 `infra/gitea/`(仓库内);不安装任何 nginx 配置、不 reload、不碰 DNS - verify: `curl -sf http://127.0.0.1:13020/api/v1/version && docker inspect -f {{.State.Status}} gitea | grep -q running` diff --git a/infra/gitea/README.md b/infra/gitea/README.md new file mode 100644 index 0000000..bfcf5c7 --- /dev/null +++ b/infra/gitea/README.md @@ -0,0 +1,62 @@ +# gitea 部署(博客代码托管) + +博客重建方案中的自建 git 托管后端:容器只绑 `127.0.0.1:13020`,公网经 nginx 反代为 +`https://git.wait4x.online/`。本目录是入库快照,宿主实际运行位置为 `~/docker_apps/gitea/`。 + +## 文件与同步方向 + +| 文件 | 作用 | +|------|------| +| `docker-compose.yml` | 容器编排(gitea/gitea:28.2.0,UID/GID 1000,restart unless-stopped) | +| `app.ini` | 首启种子配置;密钥(SECRET_KEY/INTERNAL_TOKEN 等)由首启自动生成 | +| `nginx-git.wait4x.online.conf` | nginx 接入**草稿,未安装**(proxy 头写法对齐主站 halo vhost) | + +改动流程:先改本目录 → 拷贝到 `~/docker_apps/gitea/` → `docker compose up -d`。 +运行时 app.ini 在宿主 `data/gitea/conf/app.ini`,含首启生成的密钥, +**严禁**用本目录快照回覆盖(密钥丢失 = 加密数据不可解密)。 + +## 初始化凭据(仅初始化用) + +- 管理员:`wait4x`;初始密码与 API token 一并存宿主 `~/docker_apps/gitea/init-credentials.txt` + (0600,不入库——本仓库将公开,凭据不进 git 历史),首登修改后删除该文件敏感行 +- API token(scopes=all)存宿主 `~/docker_apps/gitea/init-credentials.txt`(0600,不入库), + 供后续 webhook/CI 用,可随时在 gitea UI 撤销重建 + +## 管理命令速查(均在宿主 `~/docker_apps/gitea/` 下执行) + +```bash +# 建用户(关注册实例,账号只能这样建) +docker compose exec -u git gitea gitea admin user create --username --email @wait4x.online --random-password +# 建私有空仓(token 从 init-credentials.txt 取) +curl -X POST http://127.0.0.1:13020/api/v1/user/repos \ + -H "Authorization: token " -H 'Content-Type: application/json' \ + -d '{"name":"","private":true,"auto_init":false}' +# 生成新 token +docker compose exec -u git gitea gitea admin user generate-access-token --username --scopes all --raw +# 改密(含管理员自救) +docker compose exec -u git gitea gitea admin user change-password -u -p <新密码> +``` + +## 数据备份 + +全部状态在宿主 `~/docker_apps/gitea/data/`:sqlite(`gitea/gitea.db`)、 +裸仓(`git/repositories/`)、LFS 与附件(`gitea/data/`)。 +sqlite 直接热拷贝会撕裂,备份前 `docker compose down` 或用 `sqlite3 .backup`; +升级前必须先备份。 + +## 升级 + +改 compose 里镜像 tag → `docker compose pull` → `up -d`(首启自动迁移 schema,不可降级)。 + +## 已知坑 + +- **不要开 `REQUIRE_SIGNIN_VIEW`**:它会把匿名 `/api/v1/version` 一并 403, + 而验收与健康检查都裸 curl 该端点;私有性靠 private 仓 + `DISABLE_REGISTRATION` 保证。 +- nginx 的 `client_max_body_size 100M`(git push 大 pack)与 app.ini + `FILE_MAX_SIZE=50`(网页上传单文件)是两条独立限制,别混为一谈。 +- vhost 草稿未带 websocket Upgrade 头(与 halo 写法一致);若日后 Actions 实时日志 + 异常,参照 dpanel vhost 补 Upgrade 头——全局 `map $connection_upgrade` 已在 + default 里定义,新 vhost 直接引用即可,勿重复定义。 +- SSH 通道整体关闭(不映射 22 + `DISABLE_SSH=true`),克隆统一走 https。 +- DNS/ESA 解析 `git.wait4x.online` 尚未配置,nginx 接入由运维会话统一执行, + 本目录草稿不自行生效。 diff --git a/infra/gitea/app.ini b/infra/gitea/app.ini new file mode 100644 index 0000000..9a4c47a --- /dev/null +++ b/infra/gitea/app.ini @@ -0,0 +1,49 @@ +; 首启种子配置(入库快照)。运行时文件在宿主部署目录 data/gitea/conf/app.ini, +; 首启由 gitea 追加 SECRET_KEY / INTERNAL_TOKEN / LFS_JWT_SECRET 等密钥, +; 严禁用本快照回覆盖运行时文件(密钥丢失 = 已加密数据不可再解密)。 +APP_NAME = wait4x git +RUN_MODE = prod +RUN_USER = git +WORK_PATH = /data/gitea + +[database] +; 个人规模 sqlite 足够,免掉独立 DB 容器;将来迁 PG 需停机导换 +DB_TYPE = sqlite3 +PATH = /data/gitea/gitea.db + +[repository] +ROOT = /data/git/repositories +; 防误公开:新仓一律私有 +DEFAULT_PRIVATE = private + +[repository.upload] +; 网页端上传上限:单次 20 个文件、单文件 50MB。 +; git push 大 pack 不走这项,走 nginx client_max_body_size(见 nginx-git.wait4x.online.conf) +MAX_FILES = 20 +FILE_MAX_SIZE = 50 + +[server] +DOMAIN = git.wait4x.online +ROOT_URL = https://git.wait4x.online/ +HTTP_ADDR = 0.0.0.0 +HTTP_PORT = 3000 +; 容器不映射 22 端口、公网只过 nginx,SSH 通道整体关闭,克隆统一 https +DISABLE_SSH = true + +[security] +; 跳过网页安装向导;密钥留空由首启生成并写回 +INSTALL_LOCK = true + +[service] +; 公网可达的私有实例:无自助注册,账号只能由管理员 CLI 建 +DISABLE_REGISTRATION = true +SHOW_REGISTRATION_BUTTON = false +; 注意不要开 REQUIRE_SIGNIN_VIEW:它会把匿名 /api/v1/version 一并 403, +; 而验收与健康检查都依赖裸 curl 该端点;私有性靠 private 仓 + 关注册保证 + +[i18n] +LANG = zh-CN + +[log] +MODE = console +LEVEL = Info diff --git a/infra/gitea/docker-compose.yml b/infra/gitea/docker-compose.yml new file mode 100644 index 0000000..859c513 --- /dev/null +++ b/infra/gitea/docker-compose.yml @@ -0,0 +1,20 @@ +# 入库快照:宿主运行位置 ~/docker_apps/gitea/,内容与本文件一致;改动先改这里再同步过去 +services: + gitea: + # 官方 rootful 镜像,锁补丁版本保证可重现;升级=改 tag + pull + up -d + image: gitea/gitea:28.2.0 + container_name: gitea + restart: unless-stopped + environment: + # 容器内 git 用户映射到宿主 1000,./data 下不产生 root 属主文件 + USER_UID: "1000" + USER_GID: "1000" + TZ: Asia/Shanghai + volumes: + - ./data:/data + # 时区注入双保险(文件+TZ env),避免日志与提交时间戳漂移 + - /etc/localtime:/etc/localtime:ro + - /etc/timezone:/etc/timezone:ro + ports: + # 仅绑回环,公网入口由 nginx 反代(见 nginx-git.wait4x.online.conf,接入前不生效) + - "127.0.0.1:13020:3000" diff --git a/infra/gitea/nginx-git.wait4x.online.conf b/infra/gitea/nginx-git.wait4x.online.conf new file mode 100644 index 0000000..51293d9 --- /dev/null +++ b/infra/gitea/nginx-git.wait4x.online.conf @@ -0,0 +1,28 @@ +# gitea 接入草稿(git.wait4x.online → 127.0.0.1:13020):仅入库,未安装; +# proxy 头写法与主站 halo vhost(/etc/nginx/sites-available/default)保持一致, +# 接入时由运维侧复制到 sites-available 并 reload,本文件不自行进 sites-enabled +server { + include snippets/esa-origin-ssl.conf; + server_name git.wait4x.online; + + # git push 大 pack 走 http body;与 app.ini 网页上传上限(单文件 50MB)是两条独立限制 + client_max_body_size 100M; + + location / { + proxy_pass http://127.0.0.1:13020; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + proxy_hide_header X-Powered-By; + proxy_hide_header Server; + + proxy_connect_timeout 60s; + proxy_send_timeout 120s; + proxy_read_timeout 120s; + } + + access_log /var/log/nginx/git-access.log; + error_log /var/log/nginx/git-error.log; +}