节点在线、应用在线、配置在线使用令牌查询
|
# Deploy å®¢æˆ·ç«¯ä¸Šä¼ é‡è¯•与 401 釿–°è®¤è¯ Spec
## Why
DeployAgent ä¸Šä¼ ç¼–è¯‘äº§ç‰©åŒ…åˆ° `/Deploy/UploadBuildFile` 时,å¶å‘ `Connection reset by peer`ã€‚æ ¹å› ï¼šStarClient æŒæœ‰çš„ JWT 令牌过期或失效时,æœåŠ¡ç«¯ `[ApiFilter]` 返回 401 å¹¶ç«‹å³å…³é—连接;而客户端使用 `StreamContent` æµå¼ä¸Šä¼ ,body è¿˜åœ¨ä¼ è¾“ä¸ï¼Œè¿žæŽ¥è¢«æœåŠ¡ç«¯å…³é—åŽå†™å…¥å¤±è´¥ï¼Œè¡¨çŽ°ä¸º `System.Net.Sockets.SocketException (104): Connection reset by peer`。
ç›®å‰ `UploadPackageAsync` 没有任何é‡è¯•机制,一次失败就整个编译任务失败,用户体验差且浪费时间。需è¦å¢žåŠ å¤±è´¥é‡è¯•å’Œ 401 釿–°è®¤è¯çš„能力。
## What Changes
- 修改 `DeployAgent/DeployService.cs` çš„ `UploadPackageAsync` æ–¹æ³•ï¼Œå°†å•æ¬¡ä¸Šä¼ æ”¹é€ ä¸º"带é‡è¯• + 401 é‡è®¤è¯"çš„ä¸Šä¼ å¾ªçŽ¯
- æŠ½å–æ ¸å¿ƒ HTTP POST 逻辑为独立方法 `PostUploadAsync`,支æŒé‡è¯•å¤ç”¨
- 新增 `EnsureLoginAsync` 方法,在检测到 401 时主动调用 `_client.Login("ä¸Šä¼ è®¤è¯å¤±æ•ˆé‡è¯•")` 刷新 token
- é‡è¯•次数固定为 3 æ¬¡ï¼Œæ¯æ¬¡å¤±è´¥åŽå»¶è¿Ÿé€’增(1s → 2s → 4s)
- 401 触å‘釿–°ç™»å½•åŽç«‹å³é‡è¯•ï¼Œä¸æ¶ˆè€—é‡è¯•次数é…é¢
- æ¯æ¬¡é‡è¯•éƒ½æ‰“å°æ¸…æ™°çš„æ—¥å¿—ï¼ˆç¬¬å‡ æ¬¡é‡è¯•ã€å¤±è´¥åŽŸå› ã€ç‰å¾…时间)
## Impact
- Affected specs: æ— ï¼ˆæ–°èƒ½åŠ›ï¼Œä¸ä¿®æ”¹æ—¢æœ‰ spec)
- Affected code:
- `DeployAgent/DeployService.cs` — `UploadPackageAsync` æ–¹æ³•é‡æž„
- `Stardust.ServerTests/Controllers/DeployControllerTests.cs` — 新增é‡è¯•场景å•元测试
- å—影哿µç¨‹ï¼šDeployAgent 编译完æˆåŽçš„ä¸Šä¼ é˜¶æ®µ
- ä¸å—å½±å“:æœåŠ¡ç«¯ `DeployController.UploadBuildFile`ã€`Attachment.SaveFile`ã€Web 端 `PipelineService`
## ADDED Requirements
### Requirement: ä¸Šä¼ å¤±è´¥é‡è¯•
UploadPackageAsync SHALL åœ¨ä¸Šä¼ å¤±è´¥æ—¶è‡ªåŠ¨é‡è¯•,最多é‡è¯• 3 次。
#### Scenario: é¦–æ¬¡ä¸Šä¼ æˆåŠŸ
- **WHEN** DeployAgent 调用 UploadPackageAsync ä¸Šä¼ ç¼–è¯‘äº§ç‰©åŒ…
- **AND** æœåŠ¡ç«¯è¿”å›ž HTTP 200 且å“应体 `code == 0`
- **THEN** ä¸è§¦å‘é‡è¯•,记录"ä¸Šä¼ æˆåŠŸ"日志
#### Scenario: 网络异常é‡è¯•åŽæˆåŠŸ
- **WHEN** ä¸Šä¼ è¿‡ç¨‹ä¸å‘生 `HttpRequestException` 或 `IOException`(如 Connection reset by peer)
- **THEN** ç‰å¾… 1 ç§’åŽé‡è¯•
- **AND** 第二次失败ç‰å¾… 2 ç§’åŽé‡è¯•
- **AND** 第三次失败ç‰å¾… 4 ç§’åŽé‡è¯•
- **AND** 第四次ä»å¤±è´¥åˆ™æŠ›å‡ºå¼‚常,终æ¢ç¼–译任务
- **AND** æ¯æ¬¡é‡è¯•记录"第 N 次é‡è¯•ï¼ŒåŽŸå› ï¼šxxx,ç‰å¾… N ç§’"日志
#### Scenario: æœåŠ¡ç«¯è¿”å›ž 5xx é‡è¯•
- **WHEN** æœåŠ¡ç«¯è¿”å›ž HTTP 500/502/503/504
- **THEN** æŒ‰åŒæ ·çš„递增延迟ç–ç•¥é‡è¯•,最多 3 次
### Requirement: 401 认è¯å¤±æ•ˆé‡æ–°ç™»å½•
UploadPackageAsync SHALL 在检测到 401 认è¯å¤±æ•ˆæ—¶ï¼Œè‡ªåŠ¨è°ƒç”¨ `_client.Login` 刷新 token åŽé‡è¯•ä¸Šä¼ ã€‚
#### Scenario: Token 过期触å‘釿–°ç™»å½•
- **WHEN** æœåŠ¡ç«¯è¿”å›ž HTTP 401 或å“应体 `code == 401`
- **THEN** 调用 `_client.Login("ä¸Šä¼ è®¤è¯å¤±æ•ˆé‡è¯•")` èŽ·å–æ–° token
- **AND** 用新 token é‡å»º HttpClient çš„ `X-Token` 请求头
- **AND** ç«‹å³é‡è¯•ä¸Šä¼ ï¼Œä¸æ¶ˆè€—é‡è¯•次数é…é¢
- **AND** 记录"Token å¤±æ•ˆï¼Œé‡æ–°ç™»å½•åŽé‡è¯•ä¸Šä¼ "日志
#### Scenario: 釿–°ç™»å½•也失败
- **WHEN** 401 åŽè°ƒç”¨ `_client.Login` 失败(如æœåŠ¡ç«¯ä¸å¯è¾¾ã€è´¦å·ç¦ç”¨ï¼‰
- **THEN** 抛出异常,终æ¢ç¼–译任务
- **AND** 记录"釿–°ç™»å½•失败:xxx"日志
#### Scenario: 釿–°ç™»å½•åŽä¸Šä¼ ä»å¤±è´¥
- **WHEN** 釿–°ç™»å½•æˆåŠŸå¹¶é‡è¯•ä¸Šä¼ ï¼Œä½†è¿”å›žéž 401 错误
- **THEN** 按普通失败处ç†ï¼Œç»§ç»é‡è¯•次数é…é¢ï¼ˆæœ€å¤š 3 次)
### Requirement: é‡è¯•ä¸å½±å“文件æµ
UploadPackageAsync SHALL åœ¨æ¯æ¬¡é‡è¯•æ—¶é‡æ–°æ‰“开文件æµï¼Œç¡®ä¿æµå¼ä¸Šä¼ çš„å¯é‡å…¥æ€§ã€‚
#### Scenario: é‡è¯•æ—¶é‡æ–°æ‰“开文件æµ
- **WHEN** å› ä»»ä½•åŽŸå› è§¦å‘é‡è¯•
- **THEN** 釿–° `new FileStream(packagePath, ...)` 创建新的文件æµ
- **AND** 上一次失败的文件æµå·²è¢« `using`/`await using` æ£ç¡®é‡Šæ”¾
- **AND** 文件æµä½ç½®ä»Ž 0 开始,确ä¿å®Œæ•´é‡ä¼
## MODIFIED Requirements
### Requirement: UploadPackageAsync 方法ç¾å与调用方
UploadPackageAsync 的方法ç¾åä¿æŒä¸å˜ï¼ˆ`private async Task UploadPackageAsync(String server, String deployName, String packagePath, String? commitId, String? commitLog, String? commitTime)`),内部实现改为"é‡è¯• + é‡è®¤è¯"循环。调用方 `OnCompile` æ— éœ€ä¿®æ”¹ã€‚
#### Scenario: è°ƒç”¨æ–¹æ— æ„ŸçŸ¥
- **WHEN** `OnCompile` 调用 `await UploadPackageAsync(...)`
- **THEN** 方法ç¾åã€è¿”回值ã€å¼‚å¸¸è¡Œä¸ºä¿æŒä¸€è‡´
- **AND** é‡è¯•å’Œé‡è®¤è¯é€»è¾‘完全å°è£…在方法内部
|