Files
alro65 de25dcee57 feat(installer): J6412 USB installer + AR Electronics license activation server
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
2026-05-24 01:36:24 -04:00

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