Add WebSocket, multi API key, geocoding proxy, beacon map picker, and comprehensive bug fixes
- Multi API Key + permission system (read/write/admin) with SHA-256 hash - WebSocket real-time push (location, alarm, device_status, attendance, bluetooth) - Geocoding proxy endpoints for Amap POI search and reverse geocode - Beacon modal map-based location picker with search and click-to-select - GCJ-02 ↔ WGS-84 bidirectional coordinate conversion - Data cleanup scheduler (configurable retention days) - Fix GPS longitude sign inversion (course_status bit 11: 0=East, 1=West) - Fix 2G CellID 2→3 bytes across all protocols (0x28, 0x2C, parser.py) - Fix parser loop guards, alarm_source field length, CommandLog.sent_at - Fix geocoding IMEI parameterization, require_admin import - Improve API error messages for 422 validation errors - Remove beacon floor/area fields (consolidated into name) via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run>
This commit is contained in:
@@ -55,7 +55,6 @@ class DeviceCreate(DeviceBase):
|
||||
|
||||
class DeviceUpdate(BaseModel):
|
||||
name: str | None = Field(None, max_length=100)
|
||||
status: Literal["online", "offline"] | None = Field(None, description="Device status")
|
||||
iccid: str | None = Field(None, max_length=30)
|
||||
imsi: str | None = Field(None, max_length=20)
|
||||
timezone: str | None = Field(None, max_length=30)
|
||||
@@ -144,7 +143,7 @@ class LocationListResponse(APIResponse[PaginatedList[LocationRecordResponse]]):
|
||||
class AlarmRecordBase(BaseModel):
|
||||
device_id: int
|
||||
alarm_type: str = Field(..., max_length=30)
|
||||
alarm_source: str | None = Field(None, max_length=10)
|
||||
alarm_source: str | None = Field(None, max_length=20)
|
||||
protocol_number: int
|
||||
latitude: float | None = Field(None, ge=-90, le=90)
|
||||
longitude: float | None = Field(None, ge=-180, le=180)
|
||||
@@ -320,7 +319,7 @@ class BluetoothListResponse(APIResponse[PaginatedList[BluetoothRecordResponse]])
|
||||
|
||||
class BeaconConfigBase(BaseModel):
|
||||
beacon_mac: str = Field(..., max_length=20, pattern=r"^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$", description="信标MAC地址 (AA:BB:CC:DD:EE:FF)")
|
||||
beacon_uuid: str | None = Field(None, max_length=36, pattern=r"^[0-9A-Fa-f]{8}-([0-9A-Fa-f]{4}-){3}[0-9A-Fa-f]{12}$", description="iBeacon UUID")
|
||||
beacon_uuid: str | None = Field(None, max_length=36, description="iBeacon UUID")
|
||||
beacon_major: int | None = Field(None, ge=0, le=65535, description="iBeacon Major")
|
||||
beacon_minor: int | None = Field(None, ge=0, le=65535, description="iBeacon Minor")
|
||||
name: str = Field(..., max_length=100, description="信标名称")
|
||||
@@ -337,7 +336,7 @@ class BeaconConfigCreate(BeaconConfigBase):
|
||||
|
||||
|
||||
class BeaconConfigUpdate(BaseModel):
|
||||
beacon_uuid: str | None = Field(None, max_length=36, pattern=r"^[0-9A-Fa-f]{8}-([0-9A-Fa-f]{4}-){3}[0-9A-Fa-f]{12}$")
|
||||
beacon_uuid: str | None = Field(None, max_length=36)
|
||||
beacon_major: int | None = Field(None, ge=0, le=65535)
|
||||
beacon_minor: int | None = Field(None, ge=0, le=65535)
|
||||
name: str | None = Field(None, max_length=100)
|
||||
@@ -482,3 +481,35 @@ class CommandResponse(BaseModel):
|
||||
|
||||
class CommandListResponse(APIResponse[PaginatedList[CommandResponse]]):
|
||||
pass
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# API Key schemas
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class ApiKeyCreate(BaseModel):
|
||||
name: str = Field(..., min_length=1, max_length=100, description="Key name / 名称")
|
||||
permissions: Literal["read", "write", "admin"] = Field(default="read", description="Permission level")
|
||||
|
||||
|
||||
class ApiKeyUpdate(BaseModel):
|
||||
name: str | None = Field(None, max_length=100)
|
||||
permissions: Literal["read", "write", "admin"] | None = None
|
||||
is_active: bool | None = None
|
||||
|
||||
|
||||
class ApiKeyResponse(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
name: str
|
||||
permissions: str
|
||||
is_active: bool
|
||||
last_used_at: datetime | None = None
|
||||
created_at: datetime
|
||||
|
||||
|
||||
class ApiKeyCreateResponse(ApiKeyResponse):
|
||||
"""Returned only on creation — includes the plaintext key (shown once)."""
|
||||
key: str
|
||||
|
||||
Reference in New Issue
Block a user