feat: add legacy device migration flow
This commit is contained in:
@@ -66,12 +66,15 @@ from apps.billing.services import (
|
||||
from apps.portal.models import DownloadRelease
|
||||
from apps.licensing.authentication import DeviceSessionAuthentication
|
||||
from apps.licensing.services import (
|
||||
LicensingError,
|
||||
DeviceRegistrationError,
|
||||
DeviceSessionValidationError,
|
||||
create_migration_request,
|
||||
record_device_heartbeat,
|
||||
register_device,
|
||||
resolve_optional_device_session,
|
||||
)
|
||||
from apps.licensing.models import MigrationRequest
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -478,6 +481,88 @@ class DeviceHeartbeatView(DeviceSessionApiView):
|
||||
)
|
||||
|
||||
|
||||
def _migration_request_response(migration_request, *, credential_token=None, confirmation_url=""):
|
||||
data = {
|
||||
"request_id": str(migration_request.request_id),
|
||||
"status": migration_request.status,
|
||||
"product_code": migration_request.device.product_code,
|
||||
"expires_at": migration_request.expires_at.isoformat(),
|
||||
"confirmed_at": (
|
||||
migration_request.confirmed_at.isoformat()
|
||||
if migration_request.confirmed_at
|
||||
else None
|
||||
),
|
||||
"confirmation_url": confirmation_url,
|
||||
}
|
||||
if credential_token is not None:
|
||||
data["device_credential_token"] = credential_token
|
||||
return data
|
||||
|
||||
|
||||
class MigrationRequestCreateView(ExternalApiView):
|
||||
def post(self, request):
|
||||
try:
|
||||
device = self.optional_client_device(request)
|
||||
except ApiRequestError as exc:
|
||||
return Response(exc.as_response_data(), status=exc.http_status)
|
||||
if device is None:
|
||||
return Response(
|
||||
api_error("device_session_required", "必须提供当前设备会话"),
|
||||
status=status.HTTP_401_UNAUTHORIZED,
|
||||
)
|
||||
try:
|
||||
migration_request, credential_token = create_migration_request(
|
||||
user=request.user,
|
||||
device=device,
|
||||
)
|
||||
except LicensingError as exc:
|
||||
response_status = (
|
||||
status.HTTP_403_FORBIDDEN
|
||||
if exc.code in {"migration_not_eligible", "license_expired"}
|
||||
else status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
return Response(api_error(exc.code, exc.message), status=response_status)
|
||||
|
||||
confirmation_url = request.build_absolute_uri(
|
||||
f"/migration/confirm/{migration_request.request_id}"
|
||||
)
|
||||
return Response(
|
||||
_migration_request_response(
|
||||
migration_request,
|
||||
credential_token=credential_token,
|
||||
confirmation_url=confirmation_url,
|
||||
),
|
||||
status=status.HTTP_201_CREATED,
|
||||
)
|
||||
|
||||
|
||||
class MigrationRequestDetailView(ExternalApiView):
|
||||
def get(self, request, request_id):
|
||||
try:
|
||||
device = self.optional_client_device(request)
|
||||
except ApiRequestError as exc:
|
||||
return Response(exc.as_response_data(), status=exc.http_status)
|
||||
if device is None:
|
||||
return Response(
|
||||
api_error("device_session_required", "必须提供当前设备会话"),
|
||||
status=status.HTTP_401_UNAUTHORIZED,
|
||||
)
|
||||
migration_request = (
|
||||
MigrationRequest.objects.select_related("device")
|
||||
.filter(request_id=request_id, user=request.user, device=device)
|
||||
.first()
|
||||
)
|
||||
if migration_request is None:
|
||||
return Response(
|
||||
api_error("migration_request_not_found", "迁移请求不存在"),
|
||||
status=status.HTTP_404_NOT_FOUND,
|
||||
)
|
||||
return Response(
|
||||
_migration_request_response(migration_request),
|
||||
status=status.HTTP_200_OK,
|
||||
)
|
||||
|
||||
|
||||
def _release_unpublished_response(platform: str) -> dict:
|
||||
return {
|
||||
"platform": platform,
|
||||
|
||||
Reference in New Issue
Block a user