Files
license-manager-sdk-python/license_manager/models.py
T
2026-02-27 14:29:48 +08:00

190 lines
6.1 KiB
Python

import base64
import json
from dataclasses import dataclass, field
from datetime import datetime
from typing import Any, Dict, Optional
@dataclass
class LicensePayload:
license_key: str
authorization_code: str
hardware_fingerprint: str
status: str
start_date: datetime
end_date: datetime
expires_at: datetime
product: Optional[str] = None
version: Optional[str] = None
authorization_code_id: Optional[str] = None
deployment_type: Optional[str] = None
max_activations: Optional[int] = None
custom_parameters: Dict[str, Any] = field(default_factory=dict)
feature_config: Dict[str, Any] = field(default_factory=dict)
usage_limits: Dict[str, Any] = field(default_factory=dict)
extras: Dict[str, Any] = field(default_factory=dict)
activated_at: Optional[datetime] = None
generated_at: Optional[datetime] = None
config_updated_at: Optional[datetime] = None
@classmethod
def from_dict(cls, data: dict) -> "LicensePayload":
def parse_time(val):
if not val:
return None
# Go's time.RFC3339 / ISO 8601 parsing approximation
try:
# Handle Z timezone
if isinstance(val, str) and val.endswith("Z"):
val = val[:-1] + "+00:00"
return datetime.fromisoformat(val)
except Exception:
return None
# expires_at is not explicitly in JSON sometimes, handled differently in Go?
# In Go, ExpiresAt is populated by the validator based on EndDate or similar logic,
# or from a different field. We'll set it to end_date as default parser.
args = {
"license_key": data.get("license_key", ""),
"authorization_code": data.get("authorization_code", ""),
"hardware_fingerprint": data.get("hardware_fingerprint", ""),
"status": data.get("status", ""),
"start_date": parse_time(data.get("start_date")),
"end_date": parse_time(data.get("end_date")),
"product": data.get("product"),
"version": data.get("version"),
"authorization_code_id": data.get("authorization_code_id"),
"deployment_type": data.get("deployment_type"),
"max_activations": data.get("max_activations"),
"custom_parameters": data.get("custom_parameters", {}),
"feature_config": data.get("feature_config", {}),
"usage_limits": data.get("usage_limits", {}),
"extras": data.get("extras", {}),
"activated_at": parse_time(data.get("activated_at")),
"generated_at": parse_time(data.get("generated_at")),
"config_updated_at": parse_time(data.get("config_updated_at")),
}
# In Go ExpiresAt has json:"-" meaning it's internal.
args["expires_at"] = args["end_date"]
return cls(**args)
@dataclass
class LicenseEnvelope:
algorithm: str
data: str
signature: str
@classmethod
def from_dict(cls, data: dict) -> "LicenseEnvelope":
return cls(
algorithm=data.get("algorithm", ""),
data=data.get("data", ""),
signature=data.get("signature", "")
)
@dataclass
class ActivateRequest:
authorization_code: str
product: str
version: str
hardware_fingerprint: str
software_version: Optional[str] = None
device_info: Dict[str, Any] = field(default_factory=dict)
metadata: Dict[str, Any] = field(default_factory=dict)
def to_dict(self) -> dict:
return {
"authorization_code": self.authorization_code,
"product": self.product,
"version": self.version,
"hardware_fingerprint": self.hardware_fingerprint,
"software_version": self.software_version,
"device_info": self.device_info,
"metadata": self.metadata,
}
@dataclass
class ActivateResponse:
license_key: str
license_file: str
heartbeat_interval: int
payload: Optional[LicensePayload] = None
@classmethod
def from_dict(cls, data: dict) -> "ActivateResponse":
payload_data = data.get("payload")
payload = LicensePayload.from_dict(payload_data) if payload_data else None
return cls(
license_key=data.get("license_key", ""),
license_file=data.get("license_file", ""),
heartbeat_interval=data.get("heartbeat_interval", 0),
payload=payload
)
@dataclass
class HeartbeatRequest:
license_key: str
hardware_fingerprint: str
software_version: Optional[str] = None
usage_data: Dict[str, Any] = field(default_factory=dict)
config_updated_at: Optional[datetime] = None
def to_dict(self) -> dict:
d = {
"license_key": self.license_key,
"hardware_fingerprint": self.hardware_fingerprint,
}
if self.software_version is not None:
d["software_version"] = self.software_version
if self.usage_data:
d["usage_data"] = self.usage_data
if self.config_updated_at:
d["config_updated_at"] = self.config_updated_at.isoformat()
return d
@dataclass
class HeartbeatResponse:
status: str
license_file: Optional[str] = None
heartbeat_interval: Optional[int] = None
config_updated: Optional[bool] = None
payload: Optional[LicensePayload] = None
@classmethod
def from_dict(cls, data: dict) -> "HeartbeatResponse":
payload_data = data.get("payload")
payload = LicensePayload.from_dict(payload_data) if payload_data else None
return cls(
status=data.get("status", ""),
license_file=data.get("license_file"),
heartbeat_interval=data.get("heartbeat_interval"),
config_updated=data.get("config_updated"),
payload=payload
)
@dataclass
class APIError:
code: str
message: str
timestamp: str
@classmethod
def from_dict(cls, data: dict) -> "APIError":
return cls(
code=data.get("code", ""),
message=data.get("message", ""),
timestamp=data.get("timestamp", "")
)