67 lines
3.0 KiB
Go
67 lines
3.0 KiB
Go
package onboarding
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"data-terminal-cloud/internal/devicebinding"
|
||
|
|
"data-terminal-cloud/internal/devicereset"
|
||
|
|
"data-terminal-cloud/internal/idempotency"
|
||
|
|
"errors"
|
||
|
|
)
|
||
|
|
|
||
|
|
// This is a DEVREQ1 adapter for the existing reset transaction, not an owner
|
||
|
|
// authorization shortcut. The account must release ownership before reset.
|
||
|
|
func (s *Service) DeviceResetState(ctx context.Context, d Device) (map[string]any, error) {
|
||
|
|
if !deviceAllowed(d) || d.Mode != "physical_confirm" {
|
||
|
|
return nil, errAccess
|
||
|
|
}
|
||
|
|
if err := devicebinding.RequireUnboundForReset(ctx, s.pool, d.ID); err != nil {
|
||
|
|
return nil, resetError(err)
|
||
|
|
}
|
||
|
|
var counter int64
|
||
|
|
if err := s.pool.QueryRow(ctx, `SELECT factory_reset_counter FROM device WHERE id=$1 AND tenant_id=$2`, d.ID, d.TenantID).Scan(&counter); err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
return map[string]any{"reset_counter": counter, "binding_generation": d.Generation}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *Service) DeviceFactoryReset(ctx context.Context, d Device, requestID string, raw []byte, in devicereset.Input) (devicereset.Response, error) {
|
||
|
|
if !deviceAllowed(d) || d.Mode != "physical_confirm" {
|
||
|
|
return devicereset.Response{}, errAccess
|
||
|
|
}
|
||
|
|
service, err := devicereset.NewService(s.pool, idempotency.NewExecutor(s.pool), s.audit)
|
||
|
|
if err != nil {
|
||
|
|
return devicereset.Response{}, err
|
||
|
|
}
|
||
|
|
// The reset ID lives in the signed and persisted body. HTTP requests may use
|
||
|
|
// fresh nonce/request IDs while retrying an uncertain result after a reboot.
|
||
|
|
response, _, err := service.Reset(ctx, controlIdentity(d), "factory-reset:"+in.ResetID, requestID, raw, in)
|
||
|
|
return response, resetError(err)
|
||
|
|
}
|
||
|
|
|
||
|
|
func resetError(err error) error {
|
||
|
|
switch {
|
||
|
|
case err == nil:
|
||
|
|
return nil
|
||
|
|
case errors.Is(err, devicebinding.ErrConfirmationRequired):
|
||
|
|
return fail(409, "DEVICE_RESET_AUTHORIZATION_REQUIRED", "请先由当前账号授权解绑,再恢复出厂")
|
||
|
|
case errors.Is(err, devicereset.ErrCallInProgress):
|
||
|
|
return fail(409, "DEVICE_RESET_CALL_IN_PROGRESS", "设备正在通话,请结束后再恢复出厂")
|
||
|
|
case errors.Is(err, devicereset.ErrFirmwareInProgress):
|
||
|
|
return fail(409, "DEVICE_RESET_FIRMWARE_IN_PROGRESS", "设备正在升级,暂时不能恢复出厂")
|
||
|
|
case errors.Is(err, devicereset.ErrPendingTranscripts):
|
||
|
|
return fail(409, "DEVICE_RESET_PENDING_TRANSCRIPTS", "设备仍有未上传的聊天记录")
|
||
|
|
case errors.Is(err, devicereset.ErrResetCounterConflict):
|
||
|
|
return fail(409, "DEVICE_RESET_COUNTER_CONFLICT", "设备重置计数器已变化")
|
||
|
|
case errors.Is(err, devicereset.ErrStateConflict):
|
||
|
|
return fail(409, "DEVICE_RESET_STATE_CONFLICT", "设备状态已变化,请重新确认")
|
||
|
|
case errors.Is(err, devicereset.ErrInvalid):
|
||
|
|
return fail(400, "INVALID_RESET_REQUEST", "设备重置参数不正确")
|
||
|
|
case errors.Is(err, idempotency.ErrKeyConflict):
|
||
|
|
return fail(409, "IDEMPOTENCY_KEY_CONFLICT", "同一重置请求不能修改内容")
|
||
|
|
case errors.Is(err, idempotency.ErrInProgress):
|
||
|
|
return fail(409, "IDEMPOTENCY_IN_PROGRESS", "设备重置请求仍在处理中")
|
||
|
|
default:
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
}
|