搭建个人项目中枢:Gitea + Halo + Nginx + HTTPS + Demo 站点全过程记录
这篇文章记录一次个人服务器建站与项目中枢搭建过程。目标是搭一个能长期管理项目的代码仓库、技术文档、演示页面和自动化部署流程的个人开发基础设施。
1. 搭建目标
最开始的需求很简单:我有多个长期项目,分散在不同电脑和目录里,希望统一管理。
最后确定的整体方案是:
云服务器:
- Gitea:私有 Git 仓库中枢
- Halo:个人博客发布系统
- Nginx:统一反向代理
- Certbot:HTTPS 证书
- 定时备份:每天备份代码服务、博客服务和数据库
本地电脑:
- ai_project_center:总控仓库
- 每个项目独立 Git 仓库
- 每个项目维护 STATUS.md / 日志 / 阻塞点 / 下一步计划
核心原则是:
总控仓库只记录状态,不存真实源码;
真实项目独立仓库管理;
博客只发布整理后的公开内容;
Demo 站只展示 mock 数据,不连接真实后台和真实设备。
2. 服务器基础准备
服务器使用 Linux 系统,先安装 Docker、Docker Compose、Nginx、Certbot 等基础组件。
目录规划如下:
/opt/stacks/
├── gitea/
├── halo/
├── demos/
├── backup/
└── scripts/
这样做的好处是:
- 每个服务独立目录,便于维护
- Docker Compose 文件集中管理
- 备份脚本和备份文件有固定位置
- 后续新增服务时不会混乱
Docker 配置了镜像加速和日志限制,避免日志无限增长。
示例配置:
{
"registry-mirrors": [
"https://<docker-mirror>"
],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
}
3. 部署 Gitea 私有 Git 服务
Gitea 用来管理所有私有代码仓库。
部署方式采用 Docker Compose:
gitea/
├── docker-compose.yml
└── data/
服务组成:
Gitea Web 服务
PostgreSQL 数据库
端口设计:
127.0.0.1:3000 -> Gitea Web
0.0.0.0:2222 -> Gitea SSH
这里 Web 端口只监听本地,由 Nginx 反向代理出去;SSH 端口对外开放,用于 Git push / clone。
Gitea 对外访问方式:
https://git.<your-domain>
Git SSH 地址格式:
ssh://git@git.<your-domain>:2222/<owner>/<repo>.git
遇到的问题:Docker 权限不足
一开始执行 Docker 命令时遇到权限问题:
permission denied while trying to connect to the Docker daemon socket
解决方式是使用 sudo,或者把当前用户加入 Docker 用户组。
临时方案:
sudo docker ps
sudo docker compose up -d
长期方案:
sudo usermod -aG docker <your-user>
重新登录后生效。
4. 部署 Halo 博客
Halo 用作博客和项目记录发布系统。
同样使用 Docker Compose 部署:
halo/
├── docker-compose.yml
└── data/
服务组成:
Halo
PostgreSQL
Halo 内部监听:
127.0.0.1:8090
外部通过 Nginx 暴露为:
https://blog.<your-domain>
Halo 配置里需要设置外部访问地址:
--halo.external-url=https://blog.<your-domain>/
遇到的问题:curl -I 返回 404
部署后,浏览器访问正常,但使用:
curl -I https://blog.<your-domain>
返回了 404。
后来确认这不是备案、Nginx 或 Halo 部署失败的问题,而是 Halo 对 HEAD 请求返回不符合预期。用 GET 请求验证是正常的:
curl -L -o /dev/null -s -w "%{http_code}\n" https://blog.<your-domain>/
curl -L -o /dev/null -s -w "%{http_code}\n" https://blog.<your-domain>/console
如果返回 200,就说明网页访问正常。
5. Nginx 反向代理
Nginx 负责把不同子域名转发到不同服务。
规划如下:
git.<your-domain> -> Gitea
blog.<your-domain> -> Halo
demo.<your-domain> -> 静态 Demo 站
Gitea 示例配置:
server {
server_name git.<your-domain>;
client_max_body_size 512m;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
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;
}
}
Halo 示例配置:
server {
server_name blog.<your-domain>;
client_max_body_size 512m;
location / {
proxy_pass http://127.0.0.1:8090;
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;
}
}
每次修改 Nginx 后,都先检查配置:
sudo nginx -t
确认没问题再 reload:
sudo systemctl reload nginx
6. HTTPS 证书
使用 Certbot 给子域名申请 HTTPS 证书:
sudo certbot --nginx -d git.<your-domain> -d blog.<your-domain>
后续增加 Demo 站时,再单独申请:
sudo certbot --nginx -d demo.<your-domain>
证书申请成功后,Certbot 会自动改写 Nginx 配置,添加 listen 443 ssl 和证书路径。
验证:
sudo nginx -t
sudo systemctl reload nginx
浏览器访问:
https://git.<your-domain>
https://blog.<your-domain>
https://demo.<your-domain>
7. 备案和站点信息
因为服务器在国内云厂商,域名要长期公开访问,需要完成:
ICP备案
公安联网备案
备案完成后,把备案号放到博客页脚。
这里不记录真实备案号,只保留结论:
ICP备案完成
公安联网备案完成
博客页脚备案信息已配置
8. 自动备份
为了防止服务或数据库损坏,写了统一备份脚本。
备份内容包括:
Gitea 数据目录
Gitea PostgreSQL 数据库
Halo 数据目录
Halo PostgreSQL 数据库
Docker Compose 配置
备份清单 MANIFEST
备份目录:
/opt/stacks/backup/
备份脚本:
/opt/stacks/scripts/backup_all.sh
定时任务:
0 3 * * * /opt/stacks/scripts/backup_all.sh >> /opt/stacks/backup/backup.log 2>&1
每天凌晨自动备份一次。
遇到的问题:系统自带 crontab 项
服务器里原本有云厂商的系统任务,不需要删除。只追加自己的备份任务即可。
9. 建立 AI 项目总控仓库
为了让 AI 能长期管理项目状态,创建了一个总控仓库:
ai_project_center
它不是源码仓库,而是项目管理仓库。
结构设计:
ai_project_center/
├── DASHBOARD.md
├── RULES.md
├── DAILY_LOG/
├── WEEKLY_REVIEW/
├── INFRA/
└── PROJECTS/
├── novel_skill/
├── iec61850/
├── web_om_a300/
└── blog/
核心规则:
每天最多一个主线任务
不允许无状态切换项目
每个项目必须维护 STATUS.md
博客和 Git 分离
每天结束必须复盘
推送到 Gitea:
git remote add origin ssh://git@git.<your-domain>:2222/<owner>/ai_project_center.git
git push -u origin main
以后所有 Git 操作都先确认目录。
10. 接入 IEC 61850 项目仓库
IEC 61850 项目是完整工程,不只是代码目录,还包含:
code
knowledge
standards
tools
logs
README.md
AGENTS.md
questions.md
但参考源码目录 source/ 不纳入 Git,避免许可证边界问题。
.gitignore 里忽略:
source/
_repo_scan/
tmp/
build/
build_*/
*.exe
*.obj
*.pdb
*.ilk
*.lib
*.dll
推送到 Gitea 后,做 clone 测试:
git clone ssh://git@git.<your-domain>:2222/<owner>/iec61850_project.git
cd iec61850_project
git status
dir
验证结果:
能正常 clone
工作区 clean
主目录完整
构建产物未误提交
11. 接入 Web 前端项目
Web 项目路径类似:
F:\codex\web\om_a300_view
项目已经是 Git 仓库,但远程地址是旧服务器,需要替换为新的 Gitea 地址。
检查:
git status
git remote -v
替换远程:
git remote set-url origin ssh://git@git.<your-domain>:2222/<owner>/web_om_a300_view.git
git branch -M main
git push -u origin main
clone 测试:
git clone ssh://git@git.<your-domain>:2222/<owner>/web_om_a300_view.git
cd web_om_a300_view
git status
验证结果:
clone 成功
工作区 clean
src / public / scripts / package.json 等文件完整
12. Web 项目依赖安装问题
clone 下来的项目执行:
npm install
npm run build
一开始失败。
问题 1:旧 npm 镜像地址证书过期
报错:
CERT_HAS_EXPIRED
registry.npm.taobao.org
后来发现 package-lock.json 里锁定了大量旧地址:
registry.npm.taobao.org
registry.nlark.com
解决方式:
node -e "const fs=require('fs'); const p='package-lock.json'; let s=fs.readFileSync(p,'utf8'); s=s.replace(/registry\.nlark\.com/g,'registry.npmmirror.com').replace(/registry\.npm\.taobao\.org/g,'registry.npmmirror.com'); fs.writeFileSync(p,s);"
确认清理干净:
findstr /I "registry.nlark.com registry.npm.taobao.org" package-lock.json
没有输出说明清理完成。
重新安装:
npm cache clean --force
npm install --legacy-peer-deps
构建:
npm run build
成功标志:
DONE Compiled successfully
DONE Build complete
13. 修复 Web 构建输出目录
原项目的 vue.config.js 写死了旧服务器路径:
outputDir: '/home/xxx/web/om_a300_view'
这导致 Windows 本地构建时输出到奇怪路径。
改成环境变量控制:
const outputDir = process.env.OM_A300_OUTPUT_DIR || 'dist'
module.exports = {
outputDir
}
这样普通构建输出到:
dist
Demo 构建可以输出到:
dist-demo
PowerShell 构建 Demo:
$env:OM_A300_OUTPUT_DIR="dist-demo"
npm run build
Remove-Item Env:\OM_A300_OUTPUT_DIR
验证结果:
普通构建成功,输出 dist
Demo 构建成功,输出 dist-demo
14. 部署 demo 静态站
Demo 站点目标:
https://demo.<your-domain>
服务器目录:
/opt/stacks/demos/om_a300_view
本地构建:
$env:OM_A300_OUTPUT_DIR="dist-demo"
npm run build
Remove-Item Env:\OM_A300_OUTPUT_DIR
打包:
Compress-Archive -Path .\dist-demo\* -DestinationPath .\om_a300_view_demo.zip -Force
上传:
scp .\om_a300_view_demo.zip <server-user>@<server-ip>:/tmp/
服务器解压:
sudo mkdir -p /opt/stacks/demos/om_a300_view
sudo rm -rf /opt/stacks/demos/om_a300_view/*
sudo unzip -o /tmp/om_a300_view_demo.zip -d /opt/stacks/demos/om_a300_view
sudo chown -R www-data:www-data /opt/stacks/demos/om_a300_view
Nginx 配置:
server {
listen 80;
server_name demo.<your-domain>;
root /opt/stacks/demos/om_a300_view;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location ~ ^/(ajax|rabbitmq|login|file|upload|websocket) {
return 503;
}
}
启用:
sudo ln -sf /etc/nginx/sites-available/demo.<your-domain> /etc/nginx/sites-enabled/demo.<your-domain>
sudo nginx -t
sudo systemctl reload nginx
申请 HTTPS:
sudo certbot --nginx -d demo.<your-domain>
验证:
https://demo.<your-domain>
页面可以正常打开。
15. Demo 站无法登录的问题
静态站部署成功后,发现页面能打开,但无法登录。
这是正常的,因为它只是前端静态资源,没有后端服务。
如果直接把 Demo 站接到真实后台,会有安全风险,所以最后采用了前端 Demo Mock 模式。
目标:
Demo 站可以登录
不连接真实后台
不连接真实设备
不连接真实数据库
Demo 登录账号:
账号:admin
密码:admin123
16. 设计 Demo Mock 模式
先让本地 Codex 扫描项目登录链路,不直接修改代码。
扫描内容包括:
登录页面
登录 API
axios/request 封装
token 存储
用户信息存储
权限存储
router guard
Vuex/store 初始化
菜单来源
首页初始化接口
websocket 初始化位置
最终确认:只 mock 登录不够。
因为登录成功后,项目会继续触发:
store.init -> /ajax
WebsocketBridge -> /websocket
部分隐藏组件 -> p_query_ms_svg
所以 Demo Mock 第一阶段需要处理:
登录
store 初始化
websocket 禁用
真实请求阻断
登出清理
首页隐藏组件侧漏
17. 实现 Demo Mock 模式
新增文件:
src/demo/mode.js
src/demo/auth.js
src/demo/store-data.js
用途:
mode.js:统一读取 VUE_APP_DEMO_MODE
auth.js:处理 demo 账号校验和 sessionStorage 登录态
store-data.js:提供本地 mock 初始化数据
修改文件:
LoginView.vue:demo 模式下本地登录
stores/index.js:demo 模式下本地初始化 store
WebsocketBridge.vue:demo 模式下不连接 websocket
request.js:demo 模式下阻断真实请求
utils/index.js:登出时清理完整登录态
StationSvgView.vue:避免首页隐藏 SVG 面板触发真实请求
SvgEditDialog.vue:避免常驻对话框触发真实请求
.gitignore:忽略 dist、dist-demo、node_modules、zip
codex_notes:记录任务日志
Demo 构建方式:
$env:VUE_APP_DEMO_MODE="true"
$env:OM_A300_OUTPUT_DIR="dist-demo"
npm run build
Remove-Item Env:\VUE_APP_DEMO_MODE
Remove-Item Env:\OM_A300_OUTPUT_DIR
验证结果:
普通构建 npm run build 通过
Demo 构建通过
admin / admin123 可登录
错误账号密码会提示失败
登录后进入 /home
刷新后仍保持登录
没有真实 /login 请求
没有真实 /ajax 请求
没有真实 /file 请求
没有真实 /rabbitmq 请求
没有真实 /websocket 请求
18. 部署新的 Demo Mock 构建
重新构建:
$env:VUE_APP_DEMO_MODE="true"
$env:OM_A300_OUTPUT_DIR="dist-demo"
npm run build
Remove-Item Env:\VUE_APP_DEMO_MODE
Remove-Item Env:\OM_A300_OUTPUT_DIR
重新打包:
Compress-Archive -Path .\dist-demo\* -DestinationPath .\om_a300_view_demo.zip -Force
上传服务器:
scp .\om_a300_view_demo.zip <server-user>@<server-ip>:/tmp/
服务器覆盖部署:
sudo tar -czf /opt/stacks/demos/backup/om_a300_view_$(date +%Y%m%d_%H%M%S).tar.gz -C /opt/stacks/demos om_a300_view
sudo rm -rf /opt/stacks/demos/om_a300_view/*
sudo unzip -o /tmp/om_a300_view_demo.zip -d /opt/stacks/demos/om_a300_view
sudo chown -R www-data:www-data /opt/stacks/demos/om_a300_view
sudo nginx -t
sudo systemctl reload nginx
浏览器验证:
https://demo.<your-domain>
测试:
admin / admin123
预期结果:
登录成功
进入首页
刷新后不退出
没有真实后台请求
19. 常见误操作和解决办法
19.1 .git/index.lock 锁文件
如果遇到:
unable to unlink '.git/index.lock'
先确认没有 Git 进程正在运行,再删除锁:
Remove-Item -Force .git/index.lock -ErrorAction SilentlyContinue
然后重新:
git status -sb
19.2 不要盲目 git add .
对于大型项目,尤其是前端项目,容易误提交:
node_modules
dist
dist-demo
zip 包
日志文件
缓存
推荐精确添加:
git add package-lock.json vue.config.js
或者:
git add src/demo src/views/login/LoginView.vue src/stores/index.js
20. 当前完成情况
到这里,整个个人项目中枢基本完成第一阶段闭环:
Gitea 私有 Git 服务:完成
Halo 博客:完成
Nginx 反代:完成
HTTPS:完成
自动备份:完成
ai_project_center 总控仓库:完成
IEC 61850 项目接入:完成
Web 项目接入:完成
Web 项目构建验证:完成
demo.<your-domain> 静态站:完成
Demo Mock 登录:完成
当前 Demo 站能力:
可以打开页面
可以使用固定账号登录
可以进入首页
刷新后保持登录态
不连接真实后台
不连接真实设备
不连接真实数据库
21. 后续计划
1. Demo 菜单收敛,只展示已 mock 的页面
2. 历史数据页面增加 mock 数据
3. 文件页面增加占位提示
4. 控制类页面隐藏或禁用
5. 首页数据做更真实的模拟
6. Gitea 仓库和 Halo 博客联动
7. 项目日报自动生成
8. Demo 发布脚本自动化
9. 备份恢复演练
10. 多项目状态看板网页化
22. 总结
这次建站搭建了一套个人项目基础设施。
最终形成了:
代码管理:Gitea
内容发布:Halo
项目管理:ai_project_center
在线展示:demo 站
安全边界:mock 模式,不接真实后台
运维保障:HTTPS + 自动备份
最重要的经验是:
服务要分层
仓库要分离
构建产物不要进 Git
真实后台不要暴露给 Demo
每一步都要 clone 验证
每个问题都要记录解决方法
这样后续无论是继续开发项目、写博客、展示 Demo,都有了一个比较稳定的基础。
从零搭建个人 AI 项目中枢:Gitea + Halo + Nginx + HTTPS + Demo 站点全过程记录
本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。