206 lines
7.3 KiB
Markdown
206 lines
7.3 KiB
Markdown
# AGENTS 指南(ssh-manager)
|
||
|
||
本文件面向在本仓库工作的自动化 coding agents。
|
||
目标:快速、安全、风格一致地完成改动。
|
||
|
||
## 1) 仓库概览
|
||
|
||
- Monorepo:`backend`(Spring Boot 2.7 + Java 8)+ `frontend`(Vue 3 + TS + Vite)
|
||
- 默认后端端口:`48080`(见 `backend/src/main/resources/application.yml`)
|
||
- 前端开发端口:`5173`,通过 Vite 代理 `/api` 和 `/ws`
|
||
- 数据库:H2 文件库(`./data/sshmanager`)
|
||
- 认证:JWT(HTTP Header Bearer + WebSocket query token)
|
||
|
||
## 2) 环境与依赖
|
||
|
||
- JDK:8+
|
||
- Maven:3.6+
|
||
- Node.js:18+
|
||
- npm:与 `frontend/package-lock.json` 配套
|
||
|
||
## 3) 常用命令(构建 / 检查 / 测试)
|
||
|
||
### 3.1 后端(在 `backend/` 目录)
|
||
|
||
- 启动开发服务:`mvn spring-boot:run`
|
||
- 打包:`mvn package`
|
||
- 仅运行测试:`mvn test`
|
||
- 跳过测试打包:`mvn -DskipTests package`
|
||
- 运行单个测试类:`mvn -Dtest=ConnectionServiceTest test`
|
||
- 运行单个测试方法:`mvn -Dtest=ConnectionServiceTest#shouldCreateConnection test`
|
||
|
||
说明:
|
||
- 当前仓库可能暂无 `src/test` 用例;上述命令是 Maven 标准入口。
|
||
- 新增测试时优先使用 Surefire 默认命名约定(`*Test`、`*Tests`)。
|
||
|
||
### 3.2 前端(在 `frontend/` 目录)
|
||
|
||
- 安装依赖:`npm install`
|
||
- 启动开发服务:`npm run dev`
|
||
- 生产构建:`npm run build`
|
||
- 本地预览构建产物:`npm run preview`
|
||
|
||
说明:
|
||
- 当前 `package.json` 未配置独立 lint/test script。
|
||
- `npm run build` 会先执行 `vue-tsc -b`,可视为类型检查关卡。
|
||
|
||
### 3.3 Docker(在仓库根目录)
|
||
|
||
- 构建镜像:`docker compose -f docker/docker-compose.yml build`
|
||
- 前台运行:`docker compose -f docker/docker-compose.yml up`
|
||
- 后台运行:`docker compose -f docker/docker-compose.yml up -d`
|
||
|
||
## 4) 单测执行策略(重点)
|
||
|
||
- 后端单测首选:`mvn -Dtest=类名#方法名 test`
|
||
- 多方法可用逗号:`mvn -Dtest=ClassTest#testA,testB test`
|
||
- 若测试依赖 Spring 上下文,优先在类级隔离,避免全量启动过慢
|
||
- 修改 Service/Controller 后,至少补一条对应单元或集成测试(若仓库开始建设测试)
|
||
- 前端若后续引入 Vitest,建议约定:
|
||
- 全量:`npm run test`
|
||
- 单文件:`npm run test -- src/views/ConnectionsView.test.ts`
|
||
- 单用例:`npm run test -- -t "case name"`
|
||
|
||
## 5) 代码风格总则
|
||
|
||
- 小步提交:改动聚焦,避免“顺手重构”无关文件
|
||
- 保持现有技术栈,不随意引入新框架/重依赖
|
||
- 默认不修改对外 API 语义;若必须修改,同步更新调用侧
|
||
- 安全优先:严禁提交真实密钥、密码、token、私钥
|
||
- 可观测性:失败路径要返回可诊断信息(但不能泄露敏感数据)
|
||
|
||
## 6) 后端风格(Java / Spring)
|
||
|
||
### 6.1 导入与结构
|
||
|
||
- 包名固定前缀:`com.sshmanager`
|
||
- import 分组遵循现有习惯:
|
||
- 先项目内 `com.sshmanager...`
|
||
- 再第三方/框架 `org...` / `javax...`
|
||
- 最后 `java...`
|
||
- 避免通配符 import(`*`)
|
||
|
||
### 6.2 格式与命名
|
||
|
||
- 4 空格缩进,左花括号同行
|
||
- 类名 `PascalCase`,方法/变量 `camelCase`
|
||
- Controller 以 `*Controller`,Service 以 `*Service`,Repository 以 `*Repository`
|
||
- DTO 放 `dto` 包,实体放 `entity` 包
|
||
- 常量使用 `UPPER_SNAKE_CASE`
|
||
|
||
### 6.3 类型与注解
|
||
|
||
- Java 8 语法兼容(不要引入更高版本语法特性)
|
||
- 优先构造器注入(本仓库已有统一做法)
|
||
- 合理使用 Lombok(当前主要在 Entity 使用)
|
||
- 对外接口返回 `ResponseEntity<...>` 保持一致
|
||
|
||
### 6.4 异常与错误处理
|
||
|
||
- 与现有代码一致:业务错误返回 JSON(`message` 或 `error` 字段)
|
||
- 鉴权失败返回 401;参数错误返回 400;服务异常返回 500
|
||
- 记录日志时避免输出明文凭据(password/privateKey/passphrase/token)
|
||
- SFTP 相关改动需考虑并发安全:`ChannelSftp` 非线程安全
|
||
|
||
### 6.5 安全相关
|
||
|
||
- JWT 校验逻辑位于过滤器与握手流程,改动需覆盖 HTTP + WS 场景
|
||
- 允许跨域来源在 `SecurityConfig` 中集中维护
|
||
- 加密逻辑统一走 `EncryptionService`,禁止绕过直接落库明文
|
||
|
||
## 7) 前端风格(Vue 3 / TS / Tailwind)
|
||
|
||
### 7.1 导入与模块组织
|
||
|
||
- 使用 `<script setup lang="ts">`
|
||
- import 优先级参考现有文件:
|
||
- Vue 核心库
|
||
- 路由 / store
|
||
- 本地 API / type / 组件
|
||
- 图标或其他第三方
|
||
- 相对路径保持简洁,避免无必要跨层引用
|
||
|
||
### 7.2 格式与命名
|
||
|
||
- 2 空格缩进
|
||
- TypeScript 与 Vue 脚本保持“无分号 + 单引号”风格
|
||
- 组件文件名 `PascalCase.vue`
|
||
- store 命名:`useXxxStore`
|
||
- 方法变量 `camelCase`,常量 `UPPER_SNAKE_CASE`
|
||
|
||
### 7.3 类型与状态
|
||
|
||
- API 类型定义集中在 `frontend/src/api/*.ts`
|
||
- 对可空值显式标注(如 `Connection | null`)
|
||
- Pinia store 负责状态与数据同步,视图层尽量避免重复请求逻辑
|
||
- 新增接口先补类型,再写调用
|
||
|
||
### 7.4 UI 与交互
|
||
|
||
- 延续 Tailwind 工具类写法,不引入额外 CSS 框架
|
||
- 保持现有深色主题语言(`slate/cyan`)与可访问性属性(如 `aria-label`)
|
||
- 交互失败要有可感知反馈(至少日志或错误提示)
|
||
- 注意终端/SFTP页面的响应性能,避免不必要重渲染
|
||
|
||
## 8) 提交前自检清单
|
||
|
||
- 后端改动:至少运行 `mvn test`(若存在测试)或最小可行编译验证
|
||
- 前端改动:运行 `npm run build`(包含 `vue-tsc`)
|
||
- 手工验证登录流程、连接列表、终端或 SFTP 关键路径(按改动范围)
|
||
- 检查未提交敏感信息与本地配置
|
||
- 仅提交与需求直接相关的文件
|
||
|
||
## 9) 文档与规则文件检查结果
|
||
|
||
- `AGENTS.md`:本文件为新建(仓库根目录)
|
||
- Cursor 规则:未发现 `.cursor/rules/` 或 `.cursorrules`
|
||
- Copilot 规则:未发现 `.github/copilot-instructions.md`
|
||
|
||
若未来新增上述规则文件,agents 必须先读取并将其视为高优先级约束。
|
||
|
||
## 10) 近期修复记录(2026-03-11)
|
||
|
||
### 10.1 Docker 启动失败修复
|
||
|
||
**问题现象**
|
||
```text
|
||
Could not resolve placeholder 'SSHMANAGER_JWT_SECRET'
|
||
Encryption key must be 32 bytes (256 bits)
|
||
No qualifying bean of type 'ExecutorService' available: expected single matching bean but found 2
|
||
```
|
||
|
||
**修复措施**
|
||
1. **`application.yml`** - 为安全配置添加空字符串默认值
|
||
```yaml
|
||
sshmanager:
|
||
encryption-key: ${SSHMANAGER_ENCRYPTION_KEY ""}
|
||
jwt-secret: ${SSHMANAGER_JWT_SECRET ""}
|
||
```
|
||
|
||
2. **`docker-compose.yml`** - 提供有效的默认密钥(仅用于开发/测试)
|
||
```yaml
|
||
environment:
|
||
- SSHMANAGER_JWT_SECRET=ssh-manager-prod-jwt-secret-20240311
|
||
- SSHMANAGER_ENCRYPTION_KEY=MLVt7pE35KULIppEiit0doUMvSjozZJ037oNGeXjhVA=
|
||
```
|
||
> 注:`MLVt7pE35KULIppEiit0doUMvSjozZJ037oNGeXjhVA=` 是通过 `openssl rand -base64 32` 生成的有效 32 字节 AES-256 密钥
|
||
|
||
3. **`TerminalWebSocketHandler.java`** - 解决依赖注入歧义
|
||
```java
|
||
import org.springframework.beans.factory.annotation.Qualifier;
|
||
|
||
public TerminalWebSocketHandler(
|
||
// ... 其他参数
|
||
@Qualifier("terminalWebSocketExecutor") ExecutorService executor) {
|
||
}
|
||
```
|
||
|
||
**验证结果**
|
||
```
|
||
Started SshManagerApplication in 3.469 seconds (JVM running for 3.836)
|
||
```
|
||
|
||
**注意事项**
|
||
- **生产环境部署时必须修改** `SSHMANAGER_JWT_SECRET` 和 `SSHMANAGER_ENCRYPTION_KEY`
|
||
- 建议取消 `docker-compose.yml` 中 `volumes` 注释以持久化 H2 数据库文件
|