Files
story/internal/httpapi/device_factory_reset_handler.go
T
Codex Local 0c2151ace9
ci / verify (push) Canceled after 0s
chore: checkpoint 第一版
2026-09-06 17:04:11 +08:00

75 lines
3.4 KiB
Go

package httpapi
import (
"bytes"
"context"
"encoding/json"
"errors"
"io"
"net/http"
"strings"
"data-terminal-cloud/internal/deviceauth"
"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, devicereset.ErrPendingTranscripts):
WriteError(writer, request, http.StatusConflict, "DEVICE_RESET_PENDING_TRANSCRIPTS", "设备仍有未上传的聊天记录")
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)
})
}