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
52 lines
2.2 KiB
Python
52 lines
2.2 KiB
Python
# =============================================================================
|
|
# license_server/models.py — SQLAlchemy ORM models
|
|
# =============================================================================
|
|
|
|
import uuid
|
|
from datetime import datetime, timezone
|
|
|
|
from sqlalchemy import Boolean, DateTime, Integer, String
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
from .database import Base
|
|
|
|
|
|
def _now() -> datetime:
|
|
return datetime.now(timezone.utc)
|
|
|
|
|
|
def _uuid() -> str:
|
|
return str(uuid.uuid4())
|
|
|
|
|
|
class License(Base):
|
|
"""One row per serial number issued by AR Electronics."""
|
|
|
|
__tablename__ = "licenses"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
serial: Mapped[str] = mapped_column(String(20), unique=True, nullable=False, index=True)
|
|
vessel: Mapped[str] = mapped_column(String(120), default="")
|
|
issued_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_now)
|
|
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
|
|
notes: Mapped[str] = mapped_column(String(500), default="")
|
|
|
|
|
|
class Activation(Base):
|
|
"""One row per successful hardware activation of a serial number."""
|
|
|
|
__tablename__ = "activations"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
activation_id: Mapped[str] = mapped_column(String(36), unique=True, default=_uuid, index=True)
|
|
serial: Mapped[str] = mapped_column(String(20), nullable=False, index=True)
|
|
hardware_id: Mapped[str] = mapped_column(String(64), nullable=False)
|
|
app_version: Mapped[str] = mapped_column(String(20), default="")
|
|
platform: Mapped[str] = mapped_column(String(20), default="")
|
|
hostname: Mapped[str] = mapped_column(String(120), default="")
|
|
activated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_now)
|
|
last_seen_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_now)
|
|
vessel_slot: Mapped[int] = mapped_column(Integer, default=1)
|
|
revoked: Mapped[bool] = mapped_column(Boolean, default=False)
|
|
licensed_to: Mapped[str] = mapped_column(String(120), default="")
|