Skip to content

Commit 26cc5c4

Browse files
authored
feat: detect stale local server and notify developer to restart (#1517)
1 parent 199b013 commit 26cc5c4

11 files changed

Lines changed: 358 additions & 33 deletions

File tree

server/Dockerfile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ RUN sed -i 's/\r$//' /app/start.sh && chmod +x /app/start.sh
6161
RUN sed -i 's/\r$//' /app/celery/worker/start && chmod +x /app/celery/worker/start
6262
RUN sed -i 's/\r$//' /app/celery/beat/start && chmod +x /app/celery/beat/start
6363

64+
# Bake the latest server/ commit into the image for stale-server detection.
65+
# Uses --mount=type=bind to access .git without adding it to a layer.
66+
RUN --mount=type=bind,source=.git,target=/tmp/.git \
67+
echo "EIGENT_SERVER_GIT_COMMIT=$(git --git-dir=/tmp/.git log -1 --format=%H -- server/ 2>/dev/null || echo unknown)" > /app/.image_env
68+
6469
# Reset the entrypoint, don't invoke `uv`
6570
ENTRYPOINT []
6671

server/README_CN.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
- 配置中心 Config(保存各类工具/能力所需的密钥或参数)
1515
- `GET /configs``POST /configs``PUT /configs/{id}``DELETE /configs/{id}``GET /config/info`
1616
- 聊天与数据
17-
- 历史、快照、分享等接口位于 `app/controller/chat/`,数据全部落在本地数据库
17+
- 历史、快照、分享等接口位于 `app/domains/chat/api/`,数据全部落在本地数据库
1818
- MCP 服务管理(导入本地/远程 MCP 服务器)
1919
- `GET /mcps``POST /mcp/install``POST /mcp/import/{Local|Remote}`
2020

server/README_EN.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
- Config Center (store secrets/params required by tools/capabilities)
1515
- `GET /configs`, `POST /configs`, `PUT /configs/{id}`, `DELETE /configs/{id}`, `GET /config/info`
1616
- Chat & Data
17-
- History, snapshots, sharing, etc. in `app/controller/chat/`, all persisted to local DB
17+
- History, snapshots, sharing, etc. in `app/domains/chat/api/`, all persisted to local DB
1818
- MCP Management (import local/remote MCP servers)
1919
- `GET /mcps`, `POST /mcp/install`, `POST /mcp/import/{Local|Remote}`, etc.
2020

server/doc/server-refactor-v1.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Server Refactor v1 - Upgrade Guide
2+
3+
> Applies to: v0.0.89+
4+
> PR: #1509
5+
6+
## What Changed
7+
8+
The server codebase has been restructured from a flat layout to a **domain-driven architecture**. No API endpoints or database schemas were changed — this is a code organization refactor only.
9+
10+
### Directory Mapping
11+
12+
| Before | After | Description |
13+
|---|---|---|
14+
| `app/component/` | `app/core/` | Infrastructure utilities (database, encryption, celery, etc.) |
15+
| `app/controller/` | `app/domains/*/api/` | API controllers, grouped by domain |
16+
| `app/service/` | `app/domains/*/service/` | Business logic, grouped by domain |
17+
| `app/exception/` | `app/shared/exception/` | Exception handling |
18+
| `app/type/` | `app/shared/types/` | Shared type definitions |
19+
| _(new)_ | `app/shared/auth/` | Authentication & authorization |
20+
| _(new)_ | `app/shared/middleware/` | CORS, rate limiting, trace ID |
21+
| _(new)_ | `app/shared/http/` | HTTP client utilities |
22+
| _(new)_ | `app/shared/logging/` | Logging & sensitive data filtering |
23+
24+
### Domain Structure
25+
26+
Each domain (`chat`, `config`, `mcp`, `model_provider`, `oauth`, `trigger`, `user`) follows the same layout:
27+
28+
```
29+
app/domains/<domain>/
30+
api/ # Controllers (route handlers)
31+
service/ # Business logic
32+
schema/ # Request/response schemas
33+
```
34+
35+
## Upgrade Action Required
36+
37+
**This is a breaking change for local deployments.** The old server code will fail to start due to changed import paths.
38+
39+
### Docker Users
40+
41+
```bash
42+
cd server
43+
docker-compose up --build -d
44+
```
45+
46+
You **must** include `--build` to rebuild the image. Running `docker-compose up -d` without `--build` will use the stale old image and fail.
47+
48+
### Non-Docker Users (Local Development)
49+
50+
If you are running the server directly (via `start_server.sh` or `uv run uvicorn`):
51+
52+
1. Stop the running server process
53+
2. Pull the latest code
54+
3. Restart the server
55+
56+
```bash
57+
# If using start_server.sh
58+
cd server
59+
./start_server.sh
60+
61+
# If running uvicorn directly
62+
cd server
63+
uv run uvicorn main:api --reload --port 3001 --host 0.0.0.0
64+
```
65+
66+
### Electron App Users
67+
68+
If you are running Eigent as a desktop app, simply restart the application. The server will be restarted automatically.
69+
70+
## FAQ
71+
72+
**Q: Will I lose my data?**
73+
A: No. Database volumes and schemas are not affected. Only the Python source code layout changed.
74+
75+
**Q: Do I need to re-run database migrations?**
76+
A: No. There are no new migrations in this change.
77+
78+
**Q: I see import errors like `ModuleNotFoundError: No module named 'app.component'`**
79+
A: This means you are running an old server binary/image. Follow the upgrade steps above.
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Server 重构 v1 - 升级指南
2+
3+
> 适用版本: v0.0.89+
4+
> PR: #1509
5+
6+
## 改动概述
7+
8+
Server 代码从扁平结构重构为**领域驱动架构 (Domain-Driven)**。API 接口和数据库结构均未变更,这是一次纯代码组织层面的重构。
9+
10+
### 目录变更对照
11+
12+
| 重构前 | 重构后 | 说明 |
13+
|---|---|---|
14+
| `app/component/` | `app/core/` | 基础设施(数据库、加密、celery 等) |
15+
| `app/controller/` | `app/domains/*/api/` | 按领域分组的 API 控制器 |
16+
| `app/service/` | `app/domains/*/service/` | 按领域分组的业务逻辑 |
17+
| `app/exception/` | `app/shared/exception/` | 异常处理 |
18+
| `app/type/` | `app/shared/types/` | 共享类型定义 |
19+
| _(新增)_ | `app/shared/auth/` | 认证与授权 |
20+
| _(新增)_ | `app/shared/middleware/` | CORS、限流、Trace ID |
21+
| _(新增)_ | `app/shared/http/` | HTTP 客户端工具 |
22+
| _(新增)_ | `app/shared/logging/` | 日志与敏感信息过滤 |
23+
24+
### 领域结构
25+
26+
每个领域(`chat``config``mcp``model_provider``oauth``trigger``user`)遵循统一结构:
27+
28+
```
29+
app/domains/<领域>/
30+
api/ # 控制器(路由处理)
31+
service/ # 业务逻辑
32+
schema/ # 请求/响应模型
33+
```
34+
35+
## 升级操作(必须)
36+
37+
**此改动对本地部署是 breaking change。** 旧版 server 代码因 import 路径变更将无法启动。
38+
39+
### Docker 用户
40+
41+
```bash
42+
cd server
43+
docker-compose up --build -d
44+
```
45+
46+
**必须**`--build` 参数重新构建镜像。直接 `docker-compose up -d` 会使用旧镜像导致启动失败。
47+
48+
### 非 Docker 用户(本地开发)
49+
50+
如果你通过 `start_server.sh``uv run uvicorn` 直接运行 server:
51+
52+
1. 停止正在运行的 server 进程
53+
2. 拉取最新代码
54+
3. 重新启动 server
55+
56+
```bash
57+
# 使用 start_server.sh
58+
cd server
59+
./start_server.sh
60+
61+
# 直接运行 uvicorn
62+
cd server
63+
uv run uvicorn main:api --reload --port 3001 --host 0.0.0.0
64+
```
65+
66+
### Electron 桌面应用用户
67+
68+
重启应用即可,server 会自动重启。
69+
70+
## 常见问题
71+
72+
**Q: 数据会丢失吗?**
73+
A: 不会。数据库卷和表结构未受影响,仅 Python 源码目录结构发生了变化。
74+
75+
**Q: 需要重新执行数据库迁移吗?**
76+
A: 不需要。此次改动没有新增数据库迁移。
77+
78+
**Q: 出现 `ModuleNotFoundError: No module named 'app.component'`**
79+
A: 说明正在运行旧版 server。请按上述升级步骤操作。

server/docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ services:
107107
test:
108108
[
109109
'CMD-SHELL',
110-
'celery -A app.component.celery inspect ping -d celery@$$HOSTNAME',
110+
'celery -A app.core.celery inspect ping -d celery@$$HOSTNAME',
111111
]
112112
interval: 30s
113113
timeout: 10s

server/main.py

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
sys.path.insert(0, str(_project_root))
2323

2424
import logging
25-
import sys
25+
import subprocess
26+
from importlib.metadata import version as pkg_version
2627

2728
from fastapi.staticfiles import StaticFiles
2829
from fastapi_pagination import add_pagination
@@ -50,10 +51,51 @@
5051
auto_include_routers(router, "", "app/api")
5152
api.include_router(router, prefix=f"{prefix}/v1")
5253

54+
# Server version — read once at import time so it reflects the running code
55+
try:
56+
SERVER_VERSION = pkg_version("Eigent")
57+
except Exception:
58+
SERVER_VERSION = "unknown"
59+
60+
# Git hash of the last commit that touched server/ — used for stale-server detection.
61+
# Captured once at startup; stays constant while the process lives.
62+
# 1) Try git directly (works in local dev)
63+
# 2) Fall back to .image_env baked by Dockerfile (works in Docker)
64+
def _read_server_code_hash() -> str:
65+
# Try git first (local dev)
66+
try:
67+
h = subprocess.check_output(
68+
["git", "log", "-1", "--format=%H", "--", "server/"],
69+
cwd=str(_project_root), text=True, stderr=subprocess.DEVNULL,
70+
).strip()
71+
if h:
72+
return h
73+
except Exception:
74+
pass
75+
# Fallback: read from Docker-baked .image_env
76+
try:
77+
env_file = pathlib.Path(__file__).parent / ".image_env"
78+
for line in env_file.read_text().splitlines():
79+
if line.startswith("EIGENT_SERVER_GIT_COMMIT="):
80+
v = line.split("=", 1)[1].strip()
81+
if v:
82+
return v
83+
except Exception:
84+
pass
85+
return "unknown"
86+
87+
SERVER_CODE_HASH = _read_server_code_hash()
88+
89+
5390
# Health check at root level for Docker healthcheck (GET /health)
5491
@api.get("/health", tags=["Health"])
5592
async def health_check():
56-
return {"status": "ok", "service": "eigent-server"}
93+
return {
94+
"status": "ok",
95+
"service": "eigent-server",
96+
"version": SERVER_VERSION,
97+
"server_hash": SERVER_CODE_HASH,
98+
}
5799

58100
# Backward-compatible webhook route (/api/webhook/...)
59101
from app.domains.trigger.api.webhook_controller import router as webhook_router

server/uv.lock

Lines changed: 25 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)