Files
story/internal/httpapi/device_factory_reset_handler.go
T
Codex Local bf1233d5df
ci / verify (push) Canceled after 0s
feat: expand companion platform, device controls, and memoir workflows
Add customer and operator login, BLE provisioning, device fleet controls, call memory, memoir generation, and refreshed family and maintenance portals. Include API contracts, migrations, firmware sources, tests, assets, and deployment documentation.
2026-10-08 10:06:44 +08:00

80 lines
3.8 KiB
Go

package httpapi
import (
"bytes"
"context"
"encoding/json"
"errors"
"io"
"net/http"
"strings"
"data-terminal-cloud/internal/deviceauth"
"data-terminal-cloud/internal/devicebinding"
"data-terminal-cloud/internal/devicereset"
"data-terminal-cloud/internal/idempotency"
)
type DeviceFactoryResetService interface {
Reset(context.Context, deviceauth.Identity, string, string, []byte, devicereset.Input) (devicereset.Response, bool, error)
}
func DeviceFactoryResetHandler(service DeviceFactoryResetService) http.Handler {
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
writer.Header().Set("Cache-Control", "no-store")
writer.Header().Set("Pragma", "no-cache")
identity, ok := deviceauth.FromContext(request.Context())
if !ok {
WriteError(writer, request, http.StatusUnauthorized, "DEVICE_AUTHENTICATION_FAILED", "设备身份验证失败")
return
}
key := strings.TrimSpace(request.Header.Get("Idempotency-Key"))
if len(key) < 16 || len(key) > 128 {
WriteError(writer, request, http.StatusBadRequest, "VALIDATION_FAILED", "缺少有效的重复请求识别码")
return
}
rawBody, err := io.ReadAll(request.Body)
if err != nil {
WriteError(writer, request, http.StatusBadRequest, "INVALID_REQUEST_BODY", "请求正文无法读取")
return
}
decoder := json.NewDecoder(bytes.NewReader(rawBody))
decoder.DisallowUnknownFields()
var input devicereset.Input
if err := decoder.Decode(&input); err != nil || ensureJSONEOF(decoder) != nil || devicereset.Validate(input) != nil {
WriteError(writer, request, http.StatusBadRequest, "VALIDATION_FAILED", "设备恢复出厂参数不正确")
return
}
response, replayed, err := service.Reset(request.Context(), identity, key, RequestID(request.Context()), rawBody, input)
if replayed {
writer.Header().Set("Idempotency-Replayed", "true")
}
if err != nil {
switch {
case errors.Is(err, devicebinding.ErrConfirmationRequired):
WriteError(writer, request, http.StatusConflict, "DEVICE_RESET_AUTHORIZATION_REQUIRED", "请先由当前家庭授权解绑,随后再执行设备重置")
case errors.Is(err, devicereset.ErrPendingTranscripts):
WriteError(writer, request, http.StatusConflict, "DEVICE_RESET_PENDING_TRANSCRIPTS", "设备仍有未上传的聊天记录")
case errors.Is(err, devicereset.ErrCallInProgress):
WriteError(writer, request, http.StatusConflict, "DEVICE_RESET_CALL_IN_PROGRESS", "设备正在通话,请结束后再恢复出厂")
case errors.Is(err, devicereset.ErrFirmwareInProgress):
WriteError(writer, request, http.StatusConflict, "DEVICE_RESET_FIRMWARE_IN_PROGRESS", "设备正在升级固件,暂时不能恢复出厂")
case errors.Is(err, devicereset.ErrResetCounterConflict):
WriteError(writer, request, http.StatusConflict, "DEVICE_RESET_COUNTER_CONFLICT", "设备重置计数器不是递增值")
case errors.Is(err, devicereset.ErrStateConflict):
WriteError(writer, request, http.StatusConflict, "DEVICE_RESET_STATE_CONFLICT", "当前设备状态不允许恢复出厂")
case errors.Is(err, idempotency.ErrKeyConflict):
WriteError(writer, request, http.StatusConflict, "IDEMPOTENCY_KEY_CONFLICT", "重复请求识别码已用于不同的请求")
case errors.Is(err, idempotency.ErrInProgress):
WriteAPIError(writer, request, http.StatusConflict, Error{Code: "IDEMPOTENCY_IN_PROGRESS", Message: "相同恢复出厂请求仍在处理中", Retryable: true})
case errors.Is(err, devicereset.ErrInvalid):
WriteError(writer, request, http.StatusBadRequest, "VALIDATION_FAILED", "设备恢复出厂参数不正确")
default:
WriteAPIError(writer, request, http.StatusServiceUnavailable, Error{Code: "DEVICE_RESET_FAILED", Message: "设备恢复出厂暂时失败", Retryable: true})
}
return
}
WriteJSON(writer, request, http.StatusOK, response)
})
}