Files
tutti/services/tuttid/api/daemon_mobile_remote_access.go
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

215 lines
9.2 KiB
Go

package api
import (
"context"
"errors"
"math"
"strings"
tuttigenerated "github.com/tutti-os/tutti/services/tuttid/api/generated"
"github.com/tutti-os/tutti/services/tuttid/apierrors"
mobileremotebiz "github.com/tutti-os/tutti/services/tuttid/biz/mobileremote"
mobileremoteservice "github.com/tutti-os/tutti/services/tuttid/service/mobileremote"
)
type MobileRemoteService interface {
StartPairing(context.Context) (mobileremoteservice.StartPairingResult, error)
GetChallenge(context.Context, string) (mobileremotebiz.PairingChallenge, error)
ConfirmPairing(context.Context, string) (mobileremoteservice.ConfirmChallengeResult, error)
ListPairings(context.Context) ([]mobileremotebiz.DevicePairing, error)
RevokePairing(context.Context, string) (mobileremotebiz.DevicePairing, error)
}
func (api DaemonAPI) StartMobileRemotePairing(ctx context.Context, _ tuttigenerated.StartMobileRemotePairingRequestObject) (tuttigenerated.StartMobileRemotePairingResponseObject, error) {
if api.MobileRemoteService == nil {
return startMobileRemoteUnavailable("mobile remote access service is unavailable", nil), nil
}
started, err := api.MobileRemoteService.StartPairing(ctx)
if err != nil {
return startMobileRemoteUnavailable("mobile remote pairing could not be started", err), nil
}
challenge, err := generatedMobileRemoteChallenge(started.Challenge)
if err != nil {
return startMobileRemoteUnavailable("mobile remote pairing challenge is invalid", err), nil
}
return tuttigenerated.StartMobileRemotePairing200JSONResponse{
Challenge: challenge,
QrPayload: started.QRPayload,
}, nil
}
func (api DaemonAPI) GetMobileRemotePairingChallenge(ctx context.Context, request tuttigenerated.GetMobileRemotePairingChallengeRequestObject) (tuttigenerated.GetMobileRemotePairingChallengeResponseObject, error) {
if strings.TrimSpace(request.ChallengeID) == "" {
return tuttigenerated.GetMobileRemotePairingChallenge400JSONResponse{
InvalidRequestErrorJSONResponse: mobileRemoteInvalidRequest("mobile_remote_challenge_id_required"),
}, nil
}
if api.MobileRemoteService == nil {
return getMobileRemoteUnavailable("mobile remote access service is unavailable", nil), nil
}
challenge, err := api.MobileRemoteService.GetChallenge(ctx, request.ChallengeID)
if err != nil {
return getMobileRemoteUnavailable("mobile remote pairing challenge could not be read", err), nil
}
generated, err := generatedMobileRemoteChallenge(challenge)
if err != nil {
return getMobileRemoteUnavailable("mobile remote pairing challenge is invalid", err), nil
}
return tuttigenerated.GetMobileRemotePairingChallenge200JSONResponse{Challenge: generated}, nil
}
func (api DaemonAPI) ConfirmMobileRemotePairing(ctx context.Context, request tuttigenerated.ConfirmMobileRemotePairingRequestObject) (tuttigenerated.ConfirmMobileRemotePairingResponseObject, error) {
if strings.TrimSpace(request.ChallengeID) == "" {
return tuttigenerated.ConfirmMobileRemotePairing400JSONResponse{
InvalidRequestErrorJSONResponse: mobileRemoteInvalidRequest("mobile_remote_challenge_id_required"),
}, nil
}
if api.MobileRemoteService == nil {
return confirmMobileRemoteUnavailable("mobile remote access service is unavailable", nil), nil
}
confirmed, err := api.MobileRemoteService.ConfirmPairing(ctx, request.ChallengeID)
if err != nil {
return confirmMobileRemoteUnavailable("mobile remote pairing could not be confirmed", err), nil
}
challenge, err := generatedMobileRemoteChallenge(confirmed.Challenge)
if err != nil {
return confirmMobileRemoteUnavailable("mobile remote pairing challenge is invalid", err), nil
}
pairing, err := generatedMobileRemotePairing(confirmed.Pairing)
if err != nil {
return confirmMobileRemoteUnavailable("mobile remote pairing is invalid", err), nil
}
return tuttigenerated.ConfirmMobileRemotePairing200JSONResponse{
Challenge: challenge,
Pairing: pairing,
}, nil
}
func (api DaemonAPI) ListMobileRemotePairings(ctx context.Context, _ tuttigenerated.ListMobileRemotePairingsRequestObject) (tuttigenerated.ListMobileRemotePairingsResponseObject, error) {
if api.MobileRemoteService == nil {
return listMobileRemoteUnavailable("mobile remote access service is unavailable", nil), nil
}
pairings, err := api.MobileRemoteService.ListPairings(ctx)
if err != nil {
return listMobileRemoteUnavailable("mobile remote pairings could not be read", err), nil
}
response := make([]tuttigenerated.MobileRemoteDevicePairing, 0, len(pairings))
for _, pairing := range pairings {
generated, err := generatedMobileRemotePairing(pairing)
if err != nil {
return listMobileRemoteUnavailable("mobile remote pairing is invalid", err), nil
}
response = append(response, generated)
}
return tuttigenerated.ListMobileRemotePairings200JSONResponse{Pairings: response}, nil
}
func (api DaemonAPI) RevokeMobileRemotePairing(ctx context.Context, request tuttigenerated.RevokeMobileRemotePairingRequestObject) (tuttigenerated.RevokeMobileRemotePairingResponseObject, error) {
if strings.TrimSpace(request.PairingID) == "" {
return tuttigenerated.RevokeMobileRemotePairing400JSONResponse{
InvalidRequestErrorJSONResponse: mobileRemoteInvalidRequest("mobile_remote_pairing_id_required"),
}, nil
}
if api.MobileRemoteService == nil {
return revokeMobileRemoteUnavailable("mobile remote access service is unavailable", nil), nil
}
pairing, err := api.MobileRemoteService.RevokePairing(ctx, request.PairingID)
if err != nil {
return revokeMobileRemoteUnavailable("mobile remote pairing could not be revoked", err), nil
}
generated, err := generatedMobileRemotePairing(pairing)
if err != nil {
return revokeMobileRemoteUnavailable("mobile remote pairing is invalid", err), nil
}
return tuttigenerated.RevokeMobileRemotePairing200JSONResponse{Pairing: generated}, nil
}
func generatedMobileRemoteChallenge(challenge mobileremotebiz.PairingChallenge) (tuttigenerated.MobileRemotePairingChallenge, error) {
revision, err := mobileRemoteRevision(challenge.Revision)
if err != nil {
return tuttigenerated.MobileRemotePairingChallenge{}, err
}
return tuttigenerated.MobileRemotePairingChallenge{
ChallengeId: challenge.ChallengeID,
TargetUserDeviceId: challenge.TargetUserDeviceID,
ControllerUserDeviceId: mobileRemoteOptionalString(challenge.ControllerUserDeviceID),
State: challenge.State,
PairingId: mobileRemoteOptionalString(challenge.PairingID),
Revision: revision,
ExpiresAt: challenge.ExpiresAt,
}, nil
}
func generatedMobileRemotePairing(pairing mobileremotebiz.DevicePairing) (tuttigenerated.MobileRemoteDevicePairing, error) {
revision, err := mobileRemoteRevision(pairing.Revision)
if err != nil {
return tuttigenerated.MobileRemoteDevicePairing{}, err
}
return tuttigenerated.MobileRemoteDevicePairing{
PairingId: pairing.PairingID,
ControllerUserDeviceId: pairing.ControllerUserDeviceID,
TargetUserDeviceId: pairing.TargetUserDeviceID,
State: pairing.State,
Revision: revision,
ConfirmedAt: pairing.ConfirmedAt,
RevokedAt: pairing.RevokedAt,
}, nil
}
func mobileRemoteRevision(revision uint64) (int64, error) {
if revision > math.MaxInt64 {
return 0, errors.New("mobile remote revision exceeds local API range")
}
return int64(revision), nil
}
func mobileRemoteOptionalString(value string) *string {
value = strings.TrimSpace(value)
if value == "" {
return nil
}
return &value
}
func mobileRemoteInvalidRequest(reason string) tuttigenerated.InvalidRequestErrorJSONResponse {
return invalidRequestError(apierrors.InvalidRequest(reason))
}
func mobileRemoteUnavailable(reason string, cause error) tuttigenerated.ServiceUnavailableErrorJSONResponse {
options := []apierrors.Option{apierrors.WithDeveloperMessage(reason)}
if cause != nil {
options = append(options, apierrors.WithCause(cause))
}
return serviceUnavailableError(apierrors.ServiceUnavailable("mobile_remote_access_unavailable", options...))
}
func startMobileRemoteUnavailable(reason string, cause error) tuttigenerated.StartMobileRemotePairing503JSONResponse {
return tuttigenerated.StartMobileRemotePairing503JSONResponse{
ServiceUnavailableErrorJSONResponse: mobileRemoteUnavailable(reason, cause),
}
}
func getMobileRemoteUnavailable(reason string, cause error) tuttigenerated.GetMobileRemotePairingChallenge503JSONResponse {
return tuttigenerated.GetMobileRemotePairingChallenge503JSONResponse{
ServiceUnavailableErrorJSONResponse: mobileRemoteUnavailable(reason, cause),
}
}
func confirmMobileRemoteUnavailable(reason string, cause error) tuttigenerated.ConfirmMobileRemotePairing503JSONResponse {
return tuttigenerated.ConfirmMobileRemotePairing503JSONResponse{
ServiceUnavailableErrorJSONResponse: mobileRemoteUnavailable(reason, cause),
}
}
func listMobileRemoteUnavailable(reason string, cause error) tuttigenerated.ListMobileRemotePairings503JSONResponse {
return tuttigenerated.ListMobileRemotePairings503JSONResponse{
ServiceUnavailableErrorJSONResponse: mobileRemoteUnavailable(reason, cause),
}
}
func revokeMobileRemoteUnavailable(reason string, cause error) tuttigenerated.RevokeMobileRemotePairing503JSONResponse {
return tuttigenerated.RevokeMobileRemotePairing503JSONResponse{
ServiceUnavailableErrorJSONResponse: mobileRemoteUnavailable(reason, cause),
}
}