mirror of
https://github.com/Awuqing/BackupX.git
synced 2026-09-04 23:16:41 +08:00
feat(cluster): 支持远程源服务器集中备份
为 Master 本地磁盘增加可选流式中转,远程 Agent 可直接把产物写入中央存储并通过反向通道恢复。 持久化并校验传输模式,兼容既有 Agent 本机磁盘目标,补齐鉴权、完整性、配额、访问保护、前端配置及双向链路测试。 Closes #101
This commit is contained in:
@@ -156,6 +156,44 @@ func (h *AgentHandler) UpdateRecord(c *gin.Context) {
|
||||
response.Success(c, gin.H{"status": "ok"})
|
||||
}
|
||||
|
||||
// UploadArtifact streams a remote source artifact into storage mounted only on
|
||||
// the Master. The request body is never buffered as a whole in memory or disk.
|
||||
func (h *AgentHandler) UploadArtifact(c *gin.Context) {
|
||||
node, err := h.agentService.AuthenticatedNode(c.Request.Context(), extractToken(c))
|
||||
if err != nil {
|
||||
response.Error(c, err)
|
||||
return
|
||||
}
|
||||
recordID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
response.Error(c, err)
|
||||
return
|
||||
}
|
||||
targetID, err := strconv.ParseUint(c.Param("targetId"), 10, 32)
|
||||
if err != nil {
|
||||
response.Error(c, err)
|
||||
return
|
||||
}
|
||||
if c.Request.ContentLength < 0 {
|
||||
c.JSON(stdhttp.StatusLengthRequired, gin.H{"code": "CONTENT_LENGTH_REQUIRED", "message": "artifact content length is required"})
|
||||
return
|
||||
}
|
||||
if err := h.agentService.UploadArtifact(
|
||||
c.Request.Context(),
|
||||
node,
|
||||
uint(recordID),
|
||||
uint(targetID),
|
||||
c.GetHeader("X-BackupX-Object-Key"),
|
||||
c.Request.ContentLength,
|
||||
c.GetHeader("X-BackupX-SHA256"),
|
||||
c.Request.Body,
|
||||
); err != nil {
|
||||
response.Error(c, err)
|
||||
return
|
||||
}
|
||||
response.Success(c, gin.H{"status": "ok"})
|
||||
}
|
||||
|
||||
// GetRestoreSpec Agent 拉取恢复规格。
|
||||
func (h *AgentHandler) GetRestoreSpec(c *gin.Context) {
|
||||
if h.restoreService == nil {
|
||||
@@ -208,6 +246,34 @@ func (h *AgentHandler) UpdateRestore(c *gin.Context) {
|
||||
response.Success(c, gin.H{"status": "ok"})
|
||||
}
|
||||
|
||||
// DownloadRestoreArtifact streams a Master-local backup back to its source
|
||||
// Agent for restore without exposing the local storage configuration.
|
||||
func (h *AgentHandler) DownloadRestoreArtifact(c *gin.Context) {
|
||||
if h.restoreService == nil {
|
||||
c.JSON(stdhttp.StatusServiceUnavailable, gin.H{"code": "RESTORE_SERVICE_DISABLED", "message": "restore service is not enabled"})
|
||||
return
|
||||
}
|
||||
node, err := h.agentService.AuthenticatedNode(c.Request.Context(), extractToken(c))
|
||||
if err != nil {
|
||||
response.Error(c, err)
|
||||
return
|
||||
}
|
||||
restoreID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
response.Error(c, err)
|
||||
return
|
||||
}
|
||||
artifact, err := h.restoreService.DownloadAgentArtifact(c.Request.Context(), node, uint(restoreID))
|
||||
if err != nil {
|
||||
response.Error(c, err)
|
||||
return
|
||||
}
|
||||
c.DataFromReader(stdhttp.StatusOK, artifact.Size, "application/octet-stream", artifact.Reader, nil)
|
||||
if err := artifact.Reader.Close(); err != nil {
|
||||
_ = c.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Self 返回当前 Agent token 所属节点的状态,供安装脚本末尾探活。
|
||||
func (h *AgentHandler) Self(c *gin.Context) {
|
||||
node, err := h.agentService.AuthenticatedNode(c.Request.Context(), extractToken(c))
|
||||
|
||||
Reference in New Issue
Block a user