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:
@@ -13,7 +13,7 @@ from slowapi.errors import RateLimitExceeded
|
||||
from app.database import init_db, async_session, engine
|
||||
from app.tcp_server import tcp_manager
|
||||
from app.config import settings
|
||||
from app.routers import devices, locations, alarms, attendance, commands, bluetooth, beacons, fences, heartbeats, api_keys, ws, geocoding
|
||||
from app.routers import devices, locations, alarms, attendance, commands, bluetooth, beacons, fences, heartbeats, api_keys, ws, geocoding, device_groups, alert_rules, system
|
||||
from app.dependencies import verify_api_key, require_write, require_admin
|
||||
|
||||
import asyncio
|
||||
@@ -159,6 +159,10 @@ app.add_middleware(
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# Audit logging middleware (records POST/PUT/DELETE to audit_logs table)
|
||||
from app.middleware import AuditMiddleware
|
||||
app.add_middleware(AuditMiddleware)
|
||||
|
||||
|
||||
# Global exception handler — prevent stack trace leaks
|
||||
@app.exception_handler(Exception)
|
||||
@@ -190,6 +194,9 @@ app.include_router(heartbeats.router, dependencies=[*_api_deps])
|
||||
app.include_router(api_keys.router, dependencies=[*_api_deps])
|
||||
app.include_router(ws.router) # WebSocket handles auth internally
|
||||
app.include_router(geocoding.router, dependencies=[*_api_deps])
|
||||
app.include_router(device_groups.router, dependencies=[*_api_deps])
|
||||
app.include_router(alert_rules.router, dependencies=[*_api_deps])
|
||||
app.include_router(system.router, dependencies=[*_api_deps])
|
||||
|
||||
_STATIC_DIR = Path(__file__).parent / "static"
|
||||
app.mount("/static", StaticFiles(directory=str(_STATIC_DIR)), name="static")
|
||||
|
||||
Reference in New Issue
Block a user