190 lines
6.1 KiB
Python
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", "")
|
|
)
|