de25dcee57
installer/:
- build_usb.py: dev tool — builds Flutter + AR-ECDIS, assembles USB pendrive
image with serial.key, autorun.inf, and START_INSTALLER.bat
- serial_generator.py: generates AR-XXXX-XXXX-XXXX serial numbers (48-bit
entropy), logs to CSV for CRM integration
- src/license.py: hardware fingerprint (Windows MachineGuid + primary MAC),
serial validation, online activation POST, local cache with 30-day offline
grace period
- src/sysconfig.py: HKCU autostart registry entries, .lnk shortcuts (desktop
+ Start Menu via WScript.Shell), firewall rules (netsh), COM port detection
- src/install.py: tkinter installer GUI — 9 sequential steps with per-step
progress indicators, threaded execution, error dialogs, and silent mode
license_server/ (FastAPI service — deploy to arelectronics.com VPS):
- POST /api/v1/activate: first activation accepted; same-HW re-activation
refreshes heartbeat; different-HW rejected with 409
- GET /api/v1/validate/{serial}: heartbeat endpoint to refresh offline cache
- Admin endpoints (X-Admin-Key): issue, list, revoke licenses
- SQLAlchemy models: License (serial registry) + Activation (per-machine rows)
- SQLite default, PostgreSQL-ready via DATABASE_URL env var
AR_electronics — AR-Autopilot Project
70 lines
2.0 KiB
Python
70 lines
2.0 KiB
Python
# =============================================================================
|
|
# license_server/schemas.py — Pydantic v2 request/response schemas
|
|
# =============================================================================
|
|
|
|
from datetime import datetime
|
|
from typing import Optional
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Activation
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class ActivationRequest(BaseModel):
|
|
serial: str = Field(..., pattern=r"^AR-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}$")
|
|
hardware_id: str = Field(..., min_length=16, max_length=64)
|
|
app_version: str = Field(default="", max_length=20)
|
|
platform: str = Field(default="", max_length=20)
|
|
hostname: str = Field(default="", max_length=120)
|
|
|
|
|
|
class ActivationResponse(BaseModel):
|
|
activation_id: str
|
|
vessel_slot: int
|
|
licensed_to: str
|
|
activated_at: datetime
|
|
expires_at: Optional[datetime] = None
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Validation
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class ValidateResponse(BaseModel):
|
|
serial: str
|
|
active: bool
|
|
hardware_id: str
|
|
activation_id: str
|
|
vessel_slot: int
|
|
licensed_to: str
|
|
activated_at: datetime
|
|
last_seen_at: datetime
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Admin list
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class LicenseAdminRecord(BaseModel):
|
|
serial: str
|
|
vessel: str
|
|
issued_at: datetime
|
|
is_active: bool
|
|
activations: int
|
|
|
|
|
|
class ActivationAdminRecord(BaseModel):
|
|
activation_id: str
|
|
serial: str
|
|
hardware_id: str
|
|
app_version: str
|
|
platform: str
|
|
hostname: str
|
|
activated_at: datetime
|
|
last_seen_at: datetime
|
|
vessel_slot: int
|
|
revoked: bool
|
|
licensed_to: str
|