feat: 信标设备绑定 + 蓝牙模式管理 + 系统管理增强 + 数据导出

- 新增 DeviceBeaconBinding 模型,信标-设备多对多绑定 CRUD
- 蓝牙打卡模式批量配置/恢复正常模式 API
- 反向同步: 查询设备 BTMACSET 配置更新数据库绑定 (独立 session 解决事务隔离)
- 设备列表快捷操作弹窗修复 (fire-and-forget IIFE 替代阻塞轮询)
- 保存按钮防抖: 围栏/信标绑定保存点击后 disabled + 转圈防重复提交
- 审计日志中间件 + 系统配置/备份/固件 API
- 设备分组管理 + 告警规则配置
- 5个数据导出 API (CSV UTF-8 BOM)
- 位置热力图 + 告警条件删除 + 位置清理

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>
This commit is contained in:
2026-04-01 07:06:37 +00:00
parent 9daa81621c
commit 9cd9dd9d76
19 changed files with 3403 additions and 100 deletions

View File

@@ -8,7 +8,7 @@ import hashlib
import secrets
import time
from fastapi import Depends, HTTPException, Security
from fastapi import Depends, HTTPException, Request, Security
from fastapi.security import APIKeyHeader
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
@@ -32,6 +32,7 @@ def _hash_key(key: str) -> str:
async def verify_api_key(
request: Request,
api_key: str | None = Security(_api_key_header),
db: AsyncSession = Depends(get_db),
) -> dict | None:
@@ -48,7 +49,9 @@ async def verify_api_key(
# Check master key
if secrets.compare_digest(api_key, settings.API_KEY):
return {"permissions": "admin", "key_id": None, "name": "master"}
info = {"permissions": "admin", "key_id": None, "name": "master"}
request.state.key_info = info
return info
# Check in-memory cache first
key_hash = _hash_key(api_key)
@@ -77,6 +80,7 @@ async def verify_api_key(
key_info = {"permissions": db_key.permissions, "key_id": db_key.id, "name": db_key.name}
_AUTH_CACHE[key_hash] = (key_info, now + _AUTH_CACHE_TTL)
request.state.key_info = key_info
return key_info