Guanghu Domestic Migration a27e87cb99 chore: import sanitized domestic snapshot for REPO-007
Source snapshot: 97d7f0fae96dc04b7ddad56fc1db6a108ed662cc

[SEC-CLEAN] · pre-push-clean v1.0 · 109处敏感信息已自动转乱码
2026-07-17 15:59:55 +08:00

185 lines
5.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# GHCS v0.2 Step2 · deploy_router.py · 一键部署代码
## 文件1 · `backend/deploy_router.py`(新建)
```python
"""
GHCS v0.2 - deploy router
一键部署git pull → install deps → restart service
"""
import os
import logging
from datetime import datetime
from fastapi import APIRouter, Depends, HTTPException
from auth import verify_token
logger = logging.getLogger("ghcs.deploy")
router = APIRouter(prefix="/api/deploy", tags=["deploy"])
SSH_KEY_PATH = os.getenv("GHCS_SSH_KEY", os.path.expanduser("~/.ssh/id_rsa"))
GHCS_SERVER = {
"host": "43.139.207.172",
"port": 22,
"username": "root",
"key_filename": SSH_KEY_PATH,
}
# 可部署的服务白名单
DEPLOY_WHITELIST = {
"console-api": {
"name": "GHCS console-api",
"server": GHCS_SERVER,
"repo_dir": "/root/guanghu-dev/ghcs-mono",
"steps": [
"cd /root/guanghu-dev/ghcs-mono && git pull origin main",
"cd /root/guanghu-dev/ghcs-mono/backend && source venv/bin/activate && pip install -r requirements.txt -q",
"systemctl restart console-api",
],
"verify_cmd": "systemctl is-active console-api",
},
}
def _ssh_exec(server_config, command, timeout=60):
"""SSH远程执行命令"""
import paramiko
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
client.connect(
hostname=server_config["host"],
port=server_config["port"],
username=server_config["username"],
key_filename=server_config["key_filename"],
timeout=10,
)
stdin, stdout, stderr = client.exec_command(command, timeout=timeout)
exit_code = stdout.channel.recv_exit_status()
return {
"stdout": stdout.read().decode("utf-8").strip(),
"stderr": stderr.read().decode("utf-8").strip(),
"exit_code": exit_code,
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"SSH error: {str(e)}")
finally:
client.close()
@router.post("/{service_key}")
async def deploy_service(service_key: str, username: str = Depends(verify_token)):
"""一键部署git pull → install → restart"""
if service_key not in DEPLOY_WHITELIST:
raise HTTPException(status_code=404, detail=f"unknown service: {service_key}")
svc = DEPLOY_WHITELIST[service_key]
logger.info(f"[DEPLOY] user={username} service={service_key} time={datetime.now().isoformat()}")
results = []
for i, step in enumerate(svc["steps"]):
logger.info(f"[DEPLOY STEP {i+1}] {step[:80]}")
result = _ssh_exec(svc["server"], step, timeout=120)
results.append({
"step": i + 1,
"command": step[:80],
"stdout": result["stdout"][-500:] if result["stdout"] else "",
"stderr": result["stderr"][-500:] if result["stderr"] else "",
"exit_code": result["exit_code"],
})
if result["exit_code"] != 0:
logger.error(f"[DEPLOY FAILED] step={i+1} stderr={result['stderr']}")
return {
"service": service_key,
"success": False,
"failed_step": i + 1,
"steps": results,
"timestamp": datetime.now().isoformat(),
}
# 验证服务状态
verify = _ssh_exec(svc["server"], svc["verify_cmd"])
status = verify["stdout"]
logger.info(f"[DEPLOY OK] service={service_key} status={status}")
return {
"service": service_key,
"success": True,
"status_after": status,
"steps": results,
"timestamp": datetime.now().isoformat(),
}
@router.get("/{service_key}/check")
async def check_deploy_status(service_key: str, username: str = Depends(verify_token)):
"""检查服务器代码是否落后于 Gitea"""
if service_key not in DEPLOY_WHITELIST:
raise HTTPException(status_code=404, detail=f"unknown service: {service_key}")
svc = DEPLOY_WHITELIST[service_key]
repo_dir = svc["repo_dir"]
result = _ssh_exec(
svc["server"],
f"cd {repo_dir} && git fetch origin main -q 2>&1 && echo 'LOCAL:' && git log --oneline -1 HEAD && echo 'REMOTE:' && git log --oneline -1 origin/main",
)
lines = result["stdout"].split("\n")
local_commit = ""
remote_commit = ""
for i, line in enumerate(lines):
if line.startswith("LOCAL:") and i + 1 < len(lines):
local_commit = lines[i + 1]
if line.startswith("REMOTE:") and i + 1 < len(lines):
remote_commit = lines[i + 1]
return {
"service": service_key,
"server_commit": local_commit,
"gitea_commit": remote_commit,
"needs_deploy": local_commit != remote_commit,
}
```
---
## 文件2 · `backend/main.py`(改动两处)
### 改动 A · 顶部加一行 import
`from servers_router import router as servers_router` 后面加一行:
```python
from deploy_router import router as deploy_router
```
### 改动 B · 挂载 router
`app.include_router(servers_router)` 后面加一行:
```python
app.include_router(deploy_router)
```
---
## 接口说明
| 接口 | 方法 | 作用 |
| --- | --- | --- |
| `POST /api/deploy/console-api` | POST | 一键部署 GHCS · git pull → pip install → restart |
| `GET /api/deploy/console-api/check` | GET | 检查服务器代码是否落后于 Gitea |
---
## 部署流程
1. Mac 本地改代码 → `git push origin main`
2.`POST /api/deploy/console-api`
3. 服务器自动执行:`git pull``pip install``systemctl restart`
4. 返回每一步的执行结果 + 最终服务状态
---
## ⚠️ 注意
- venv 路径假设在 `/root/guanghu-dev/ghcs-mono/backend/venv/`,如果实际路径不同需要改 steps 里的 source 路径
- 当前只支持部署 console-apiGHCS 自身),后续可以扩展 DEPLOY_WHITELIST 加入其他服务