mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-09-19 09:40:12 +00:00
129 lines
6.3 KiB
Go
129 lines
6.3 KiB
Go
package model
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// ServerTransferStatus represents the lifecycle state of a server ownership
|
|
// transfer. A transfer's life starts at Pending (server.user_id has been
|
|
// flipped to the new owner; agent still authenticates with the old owner's
|
|
// AgentSecret) and ends in exactly one of the terminal states.
|
|
type ServerTransferStatus uint8
|
|
|
|
const (
|
|
// ServerTransferStatusPending means the dashboard has flipped Server.UserID
|
|
// to the new owner and queued an ApplyConfig task to swap the agent's
|
|
// client_secret. Auth still accepts the old owner's AgentSecret for this
|
|
// UUID until verification arrives or the transfer times out.
|
|
ServerTransferStatusPending ServerTransferStatus = iota
|
|
// ServerTransferStatusVerified means the agent successfully reconnected
|
|
// using the new owner's AgentSecret. Auth no longer tolerates the old
|
|
// owner's secret on this UUID.
|
|
ServerTransferStatusVerified
|
|
// ServerTransferStatusFailed means the agent explicitly reported the
|
|
// ApplyConfig task as unsuccessful (e.g. DisableCommandExecute). The
|
|
// dashboard has rolled Server.UserID back to FromUserID.
|
|
ServerTransferStatusFailed
|
|
// ServerTransferStatusTimeout means the verification window expired
|
|
// without the agent reconnecting under the new secret. The dashboard has
|
|
// rolled Server.UserID back to FromUserID.
|
|
ServerTransferStatusTimeout
|
|
// ServerTransferStatusCancelled means an administrator cancelled the
|
|
// transfer before any verification event was observed. The dashboard has
|
|
// rolled Server.UserID back to FromUserID.
|
|
ServerTransferStatusCancelled
|
|
)
|
|
|
|
// IsTerminal reports whether the status represents a settled transfer. Only
|
|
// terminal transfers are eligible for retry and they will never be in the
|
|
// pending index.
|
|
func (s ServerTransferStatus) IsTerminal() bool {
|
|
return s != ServerTransferStatusPending
|
|
}
|
|
|
|
// ServerTransfer records a single attempt to transfer ownership of one server
|
|
// to another user. It is the source of truth for the auth-tolerance window
|
|
// during a transfer — service/rpc.authorizeAgentForUUID consults the pending
|
|
// index built from this table to decide whether to accept the old owner's
|
|
// AgentSecret on the affected UUID.
|
|
//
|
|
// Naming note: the existing model.Transfer records hourly traffic snapshots
|
|
// and is unrelated. This entity is named ServerTransfer to disambiguate.
|
|
type ServerTransfer struct {
|
|
Common
|
|
ServerID uint64 `json:"server_id" gorm:"index"`
|
|
FromUserID uint64 `json:"from_user_id"`
|
|
ToUserID uint64 `json:"to_user_id"`
|
|
InitiatorID uint64 `json:"initiator_id"`
|
|
Status ServerTransferStatus `json:"status" gorm:"index"`
|
|
LastError string `json:"last_error,omitempty"`
|
|
AckedAt *time.Time `json:"acked_at,omitempty"`
|
|
// HandshakeSecret is a per-transfer random credential that PushIfOnline
|
|
// delivers in place of the destination user's global AgentSecret. The
|
|
// agent treats it as a temporary handshake token: it rotates to this
|
|
// secret on the 10s reload, reconnects, and the dashboard's auth path
|
|
// recognises it as proof of transfer delivery (MarkVerified). It is
|
|
// scoped to this single transfer and to this single UUID — leaking it
|
|
// to the previous owner who hijacks the stream still does NOT expose
|
|
// the destination user's other agents. Never returned to API clients.
|
|
HandshakeSecret string `json:"-" gorm:"type:char(32)"`
|
|
// RevertHandshakeSecret is the same idea for the rollback path: when
|
|
// the dashboard pushes a revert ApplyConfig over a stream now held by
|
|
// the destination user, we must not embed the source user's global
|
|
// AgentSecret. Instead the agent rotates back through this token, which
|
|
// is recognised by the auth path during the revert window only.
|
|
RevertHandshakeSecret string `json:"-" gorm:"type:char(32)"`
|
|
}
|
|
|
|
// HasPermission overrides Common.HasPermission so a transfer is visible to
|
|
// admins, the source user, the destination user, and the initiator. Listing
|
|
// uses this to filter what the caller can see; mutating endpoints (cancel,
|
|
// retry) layer additional checks on top.
|
|
func (t *ServerTransfer) HasPermission(ctx *gin.Context) bool {
|
|
auth, ok := ctx.Get(CtxKeyAuthorizedUser)
|
|
if !ok {
|
|
return false
|
|
}
|
|
user := *auth.(*User)
|
|
if user.Role == RoleAdmin {
|
|
return true
|
|
}
|
|
return user.ID == t.FromUserID || user.ID == t.ToUserID || user.ID == t.InitiatorID
|
|
}
|
|
|
|
// BatchMoveServerResultStatus is the per-server outcome returned by the
|
|
// batch-move endpoint. It maps to TransferStatus for transfers that were
|
|
// successfully created, plus extra synchronous-failure modes (permission,
|
|
// duplicate active transfer, missing server) that never produce a row.
|
|
type BatchMoveServerResultStatus string
|
|
|
|
const (
|
|
// BatchMoveServerResultPending: ServerTransfer row created, agent push
|
|
// in progress. Callers should watch the WS for terminal status.
|
|
BatchMoveServerResultPending BatchMoveServerResultStatus = "pending"
|
|
// BatchMoveServerResultPermissionDenied: caller cannot move this server.
|
|
BatchMoveServerResultPermissionDenied BatchMoveServerResultStatus = "permission_denied"
|
|
// BatchMoveServerResultAlreadyTransferring: server already has an in-flight
|
|
// ServerTransfer row, cancel or wait first.
|
|
BatchMoveServerResultAlreadyTransferring BatchMoveServerResultStatus = "already_transferring"
|
|
// BatchMoveServerResultServerNotFound: server id does not exist.
|
|
BatchMoveServerResultServerNotFound BatchMoveServerResultStatus = "server_not_found"
|
|
// BatchMoveServerResultSameOwner: target user already owns this server.
|
|
BatchMoveServerResultSameOwner BatchMoveServerResultStatus = "same_owner"
|
|
// BatchMoveServerResultAgentTooOld: agent build does not understand
|
|
// TaskTypeServerTransferApply, so the rotation would never complete and
|
|
// dashboard refuses to start it. Operator must upgrade the agent.
|
|
BatchMoveServerResultAgentTooOld BatchMoveServerResultStatus = "agent_too_old"
|
|
)
|
|
|
|
// BatchMoveServerResult is one entry in the batchMoveServer response, one
|
|
// per requested server id, in the same order.
|
|
type BatchMoveServerResult struct {
|
|
ServerID uint64 `json:"server_id"`
|
|
Status BatchMoveServerResultStatus `json:"status"`
|
|
TransferID uint64 `json:"transfer_id,omitempty"`
|
|
Error string `json:"error,omitempty"`
|
|
}
|