修改了密码的存储逻辑
This commit is contained in:
parent
71917122b6
commit
fe4acf5f0b
@ -4,7 +4,7 @@ from fastapi import APIRouter, Depends, Query
|
|||||||
|
|
||||||
from app.core.response import success_response
|
from app.core.response import success_response
|
||||||
from app.core.security import verify_api_token
|
from app.core.security import verify_api_token
|
||||||
from app.schemas.platform import AiAlarmSettingIn, ChannelConfigIn, DeviceConfigIn, LineAlarmSettingIn, NetConfigItem, SystemConfigIn, UartConfigItem
|
from app.schemas.platform import AiAlarmSettingIn, ChannelConfigIn, DeviceConfigIn, DevicePasswordUpdateIn, LineAlarmSettingIn, NetConfigItem, SystemConfigIn, UartConfigItem
|
||||||
from app.services.platform_service import platform_service
|
from app.services.platform_service import platform_service
|
||||||
|
|
||||||
|
|
||||||
@ -21,6 +21,11 @@ def save_device_config(payload: DeviceConfigIn) -> Dict[str, Any]:
|
|||||||
return success_response(platform_service.save_device_config(payload))
|
return success_response(platform_service.save_device_config(payload))
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/device/password", dependencies=[Depends(verify_api_token)])
|
||||||
|
def save_device_password(payload: DevicePasswordUpdateIn) -> Dict[str, Any]:
|
||||||
|
return success_response(platform_service.save_device_password(payload), msg="保存设备密码成功")
|
||||||
|
|
||||||
|
|
||||||
@router.get("/device/net", dependencies=[Depends(verify_api_token)])
|
@router.get("/device/net", dependencies=[Depends(verify_api_token)])
|
||||||
def get_net_config(nic: str = Query(..., min_length=1)) -> Dict[str, Any]:
|
def get_net_config(nic: str = Query(..., min_length=1)) -> Dict[str, Any]:
|
||||||
return success_response(platform_service.get_net_config(nic=nic), msg="获取网卡配置成功")
|
return success_response(platform_service.get_net_config(nic=nic), msg="获取网卡配置成功")
|
||||||
|
|||||||
@ -5,6 +5,7 @@ from typing import Optional
|
|||||||
from fastapi import Header, HTTPException, status
|
from fastapi import Header, HTTPException, status
|
||||||
|
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
|
from app.repositories.json_config_repo import JsonConfigRepository
|
||||||
|
|
||||||
|
|
||||||
def hash_password(password: str) -> str:
|
def hash_password(password: str) -> str:
|
||||||
@ -12,11 +13,20 @@ def hash_password(password: str) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def verify_password(password: str, expected_hash: str) -> bool:
|
def verify_password(password: str, expected_hash: str) -> bool:
|
||||||
return hash_password(password) == expected_hash
|
return secrets.compare_digest(hash_password(password), expected_hash)
|
||||||
|
|
||||||
|
|
||||||
|
def get_access_password_hash() -> str:
|
||||||
|
config_repo = JsonConfigRepository()
|
||||||
|
device_config = config_repo.read_device_config()
|
||||||
|
password_hash = device_config.get("password", "") if isinstance(device_config, dict) else ""
|
||||||
|
if isinstance(password_hash, str) and password_hash.strip():
|
||||||
|
return password_hash
|
||||||
|
return hash_password(settings.auth_password)
|
||||||
|
|
||||||
|
|
||||||
def verify_access_password(password: str) -> bool:
|
def verify_access_password(password: str) -> bool:
|
||||||
return secrets.compare_digest(password, settings.auth_password)
|
return verify_password(password, get_access_password_hash())
|
||||||
|
|
||||||
|
|
||||||
def verify_api_token(x_api_token: Optional[str] = Header(default=None)) -> None:
|
def verify_api_token(x_api_token: Optional[str] = Header(default=None)) -> None:
|
||||||
|
|||||||
@ -143,6 +143,10 @@ class PasswordVerifyIn(BaseModel):
|
|||||||
password: str
|
password: str
|
||||||
|
|
||||||
|
|
||||||
|
class DevicePasswordUpdateIn(BaseModel):
|
||||||
|
password: str = Field(min_length=1)
|
||||||
|
|
||||||
|
|
||||||
class AlarmEvent(BaseModel):
|
class AlarmEvent(BaseModel):
|
||||||
id: Optional[int] = None
|
id: Optional[int] = None
|
||||||
alarm_type: str
|
alarm_type: str
|
||||||
|
|||||||
@ -13,12 +13,15 @@ from app.schemas.platform import (
|
|||||||
AiAlarmSettingIn,
|
AiAlarmSettingIn,
|
||||||
ChannelConfigIn,
|
ChannelConfigIn,
|
||||||
DeviceConfigIn,
|
DeviceConfigIn,
|
||||||
|
DevicePasswordUpdateIn,
|
||||||
DeviceStatus,
|
DeviceStatus,
|
||||||
LineAlarmSettingIn,
|
LineAlarmSettingIn,
|
||||||
NetConfigItem,
|
NetConfigItem,
|
||||||
|
LineData,
|
||||||
RealtimeData,
|
RealtimeData,
|
||||||
SwitchControlIn,
|
SwitchControlIn,
|
||||||
SystemConfigIn,
|
SystemConfigIn,
|
||||||
|
ValueGroup,
|
||||||
UartConfigItem,
|
UartConfigItem,
|
||||||
)
|
)
|
||||||
from app.ws.manager import ws_manager
|
from app.ws.manager import ws_manager
|
||||||
@ -30,13 +33,135 @@ class PlatformService:
|
|||||||
self.alarm_repo = AlarmRepository()
|
self.alarm_repo = AlarmRepository()
|
||||||
self.device_client = MockDeviceClient() if settings.use_mock_device else CDeviceClient()
|
self.device_client = MockDeviceClient() if settings.use_mock_device else CDeviceClient()
|
||||||
|
|
||||||
|
def _safe_ratio(self, primary: float, secondary: float, default: float = 1.0) -> float:
|
||||||
|
if abs(float(secondary)) < 1e-9:
|
||||||
|
return default
|
||||||
|
return float(primary) / float(secondary)
|
||||||
|
|
||||||
|
def _get_line_transformer_ratios(self, line: LineData) -> Dict[str, float]:
|
||||||
|
pri = line.pri_val
|
||||||
|
sec = line.sec_val
|
||||||
|
|
||||||
|
pt_ratio_candidates = [
|
||||||
|
self._safe_ratio(pri.Ua, sec.Ua, 0.0),
|
||||||
|
self._safe_ratio(pri.Ub, sec.Ub, 0.0),
|
||||||
|
self._safe_ratio(pri.Uc, sec.Uc, 0.0),
|
||||||
|
self._safe_ratio(pri.Uab, sec.Uab, 0.0),
|
||||||
|
self._safe_ratio(pri.Ubc, sec.Ubc, 0.0),
|
||||||
|
self._safe_ratio(pri.Uca, sec.Uca, 0.0),
|
||||||
|
]
|
||||||
|
ct_ratio_candidates = [
|
||||||
|
self._safe_ratio(pri.Ia, sec.Ia, 0.0),
|
||||||
|
self._safe_ratio(pri.Ib, sec.Ib, 0.0),
|
||||||
|
self._safe_ratio(pri.Ic, sec.Ic, 0.0),
|
||||||
|
]
|
||||||
|
|
||||||
|
pt_ratio = next((ratio for ratio in pt_ratio_candidates if ratio > 0), 1.0)
|
||||||
|
ct_ratio = next((ratio for ratio in ct_ratio_candidates if ratio > 0), 1.0)
|
||||||
|
return {"pt_ratio": pt_ratio, "ct_ratio": ct_ratio}
|
||||||
|
|
||||||
|
def _build_primary_from_secondary(self, secondary: ValueGroup, pt_ratio: float, ct_ratio: float) -> ValueGroup:
|
||||||
|
power_ratio = pt_ratio * ct_ratio
|
||||||
|
return ValueGroup(
|
||||||
|
Ua=secondary.Ua * pt_ratio,
|
||||||
|
Ub=secondary.Ub * pt_ratio,
|
||||||
|
Uc=secondary.Uc * pt_ratio,
|
||||||
|
Ia=secondary.Ia * ct_ratio,
|
||||||
|
Ib=secondary.Ib * ct_ratio,
|
||||||
|
Ic=secondary.Ic * ct_ratio,
|
||||||
|
Pa=secondary.Pa * power_ratio,
|
||||||
|
Pb=secondary.Pb * power_ratio,
|
||||||
|
Pc=secondary.Pc * power_ratio,
|
||||||
|
Pt=secondary.Pt * power_ratio,
|
||||||
|
Qa=secondary.Qa * power_ratio,
|
||||||
|
Qb=secondary.Qb * power_ratio,
|
||||||
|
Qc=secondary.Qc * power_ratio,
|
||||||
|
Qt=secondary.Qt * power_ratio,
|
||||||
|
Sa=secondary.Sa * power_ratio,
|
||||||
|
Sb=secondary.Sb * power_ratio,
|
||||||
|
Sc=secondary.Sc * power_ratio,
|
||||||
|
St=secondary.St * power_ratio,
|
||||||
|
PFa=secondary.PFa,
|
||||||
|
PFb=secondary.PFb,
|
||||||
|
PFc=secondary.PFc,
|
||||||
|
PFt=secondary.PFt,
|
||||||
|
Uab=secondary.Uab * pt_ratio,
|
||||||
|
Ubc=secondary.Ubc * pt_ratio,
|
||||||
|
Uca=secondary.Uca * pt_ratio,
|
||||||
|
frq=secondary.frq,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _round_value_group(self, group: ValueGroup) -> ValueGroup:
|
||||||
|
group_data = group.model_dump()
|
||||||
|
precision_map = {
|
||||||
|
"Ua": 3,
|
||||||
|
"Ub": 3,
|
||||||
|
"Uc": 3,
|
||||||
|
"Ia": 3,
|
||||||
|
"Ib": 3,
|
||||||
|
"Ic": 3,
|
||||||
|
"Pa": 2,
|
||||||
|
"Pb": 2,
|
||||||
|
"Pc": 2,
|
||||||
|
"Pt": 2,
|
||||||
|
"Qa": 2,
|
||||||
|
"Qb": 2,
|
||||||
|
"Qc": 2,
|
||||||
|
"Qt": 2,
|
||||||
|
"Sa": 2,
|
||||||
|
"Sb": 2,
|
||||||
|
"Sc": 2,
|
||||||
|
"St": 2,
|
||||||
|
"PFa": 3,
|
||||||
|
"PFb": 3,
|
||||||
|
"PFc": 3,
|
||||||
|
"PFt": 3,
|
||||||
|
"Uab": 3,
|
||||||
|
"Ubc": 3,
|
||||||
|
"Uca": 3,
|
||||||
|
"frq": 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
for field_name, precision in precision_map.items():
|
||||||
|
value = group_data.get(field_name)
|
||||||
|
if isinstance(value, (int, float)):
|
||||||
|
group_data[field_name] = round(float(value), precision)
|
||||||
|
|
||||||
|
return ValueGroup(**group_data)
|
||||||
|
|
||||||
|
def _normalize_realtime_precision(self, realtime: RealtimeData) -> RealtimeData:
|
||||||
|
line_list = []
|
||||||
|
for line in realtime.line_list:
|
||||||
|
ratios = self._get_line_transformer_ratios(line)
|
||||||
|
line_list.append(
|
||||||
|
LineData(
|
||||||
|
line_no=line.line_no,
|
||||||
|
pri_val=self._round_value_group(
|
||||||
|
self._build_primary_from_secondary(
|
||||||
|
line.sec_val,
|
||||||
|
ratios["pt_ratio"],
|
||||||
|
ratios["ct_ratio"],
|
||||||
|
)
|
||||||
|
),
|
||||||
|
sec_val=self._round_value_group(line.sec_val),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
ai_collect = {
|
||||||
|
key: round(float(value), 2) if isinstance(value, (int, float)) else value
|
||||||
|
for key, value in realtime.ai_collect.items()
|
||||||
|
}
|
||||||
|
return RealtimeData(line_list=line_list, switch=dict(realtime.switch), ai_collect=ai_collect)
|
||||||
|
|
||||||
def get_realtime_data(self) -> RealtimeData:
|
def get_realtime_data(self) -> RealtimeData:
|
||||||
cached = memory_store.get_realtime()
|
cached = memory_store.get_realtime()
|
||||||
if cached is not None:
|
if cached is not None:
|
||||||
return cached
|
normalized = self._normalize_realtime_precision(cached)
|
||||||
|
memory_store.set_realtime(normalized)
|
||||||
|
return normalized
|
||||||
realtime = self.device_client.read_realtime_data()
|
realtime = self.device_client.read_realtime_data()
|
||||||
memory_store.set_realtime(realtime)
|
normalized = self._normalize_realtime_precision(realtime)
|
||||||
return realtime
|
memory_store.set_realtime(normalized)
|
||||||
|
return normalized
|
||||||
|
|
||||||
def get_device_status(self) -> DeviceStatus:
|
def get_device_status(self) -> DeviceStatus:
|
||||||
cached = memory_store.get_status()
|
cached = memory_store.get_status()
|
||||||
@ -144,6 +269,12 @@ class PlatformService:
|
|||||||
device_result = self.device_client.send_device_config(payload)
|
device_result = self.device_client.send_device_config(payload)
|
||||||
return {"save_path": f"/config/{path.name}", **device_result}
|
return {"save_path": f"/config/{path.name}", **device_result}
|
||||||
|
|
||||||
|
def save_device_password(self, payload: DevicePasswordUpdateIn) -> Dict[str, Any]:
|
||||||
|
data = self._load_device_config_with_defaults()
|
||||||
|
data["password"] = hash_password(payload.password)
|
||||||
|
path = self.config_repo.write_device_config(data)
|
||||||
|
return {"save_path": f"/config/{path.name}", "target": "password", "send_status": "成功"}
|
||||||
|
|
||||||
def get_device_config(self) -> Dict[str, Any]:
|
def get_device_config(self) -> Dict[str, Any]:
|
||||||
data = self._load_device_config_with_defaults()
|
data = self._load_device_config_with_defaults()
|
||||||
# 不返回已保存的哈希密码,避免前端把哈希串直接显示到设置界面
|
# 不返回已保存的哈希密码,避免前端把哈希串直接显示到设置界面
|
||||||
@ -300,7 +431,7 @@ class PlatformService:
|
|||||||
return self.device_client.send_switch_control(payload)
|
return self.device_client.send_switch_control(payload)
|
||||||
|
|
||||||
async def poll_device_once(self) -> None:
|
async def poll_device_once(self) -> None:
|
||||||
realtime = self.device_client.read_realtime_data()
|
realtime = self._normalize_realtime_precision(self.device_client.read_realtime_data())
|
||||||
status = self.device_client.read_device_status()
|
status = self.device_client.read_device_status()
|
||||||
alarms = self.device_client.read_alarm_events()
|
alarms = self.device_client.read_alarm_events()
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"password": "ac9689e2272427085e35b9d3e3e8bed88cb3434828b43b86fc0596cad4c6e270",
|
"password": "bcb15f821479b4d5772bd0ca866c00ad5f926e3580720659cc80d39c9d09802a",
|
||||||
"hardware_version": {
|
"hardware_version": {
|
||||||
"board_version": "B001.001.001",
|
"board_version": "B001.001.001",
|
||||||
"display_version": "S001.001.001",
|
"display_version": "S001.001.001",
|
||||||
@ -14,7 +14,7 @@
|
|||||||
{
|
{
|
||||||
"nic": "网卡一",
|
"nic": "网卡一",
|
||||||
"ip": "192.168.1.10",
|
"ip": "192.168.1.10",
|
||||||
"mask": "255.255.255.0",
|
"mask": "255.255.255.1",
|
||||||
"gateway": "192.168.1.1",
|
"gateway": "192.168.1.1",
|
||||||
"protocol": "Modbus TCP"
|
"protocol": "Modbus TCP"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -55,6 +55,62 @@
|
|||||||
"enabled": false
|
"enabled": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"line_no": 4,
|
||||||
|
"over_limit_alarm": [
|
||||||
|
{
|
||||||
|
"category": "电压",
|
||||||
|
"limit": 180.0,
|
||||||
|
"delay": 180,
|
||||||
|
"output_node": "开出1",
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "电流",
|
||||||
|
"limit": 180.0,
|
||||||
|
"delay": 180,
|
||||||
|
"output_node": "开出1",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "差流",
|
||||||
|
"limit": 180.0,
|
||||||
|
"delay": 180,
|
||||||
|
"output_node": "开出1",
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "功率",
|
||||||
|
"limit": 0.0,
|
||||||
|
"delay": 0,
|
||||||
|
"output_node": "",
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "频率",
|
||||||
|
"limit": 180.0,
|
||||||
|
"delay": 180,
|
||||||
|
"output_node": "开出1",
|
||||||
|
"enabled": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fault_alarm": [
|
||||||
|
{
|
||||||
|
"category": "PT断线",
|
||||||
|
"limit": 0.0,
|
||||||
|
"delay": 180,
|
||||||
|
"output_node": "开出1",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "CT断线",
|
||||||
|
"limit": 0.0,
|
||||||
|
"delay": 0,
|
||||||
|
"output_node": "",
|
||||||
|
"enabled": false
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"ai_alarm_setting": [
|
"ai_alarm_setting": [
|
||||||
@ -65,7 +121,7 @@
|
|||||||
"limit_high": 20.0,
|
"limit_high": 20.0,
|
||||||
"delay": 180,
|
"delay": 180,
|
||||||
"output_node": "开出1",
|
"output_node": "开出1",
|
||||||
"enabled": true
|
"enabled": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"channel_no": 2,
|
"channel_no": 2,
|
||||||
|
|||||||
@ -3,11 +3,12 @@ from datetime import datetime
|
|||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
|
from app.core.security import hash_password
|
||||||
from app.db.sqlite import get_connection, init_db
|
from app.db.sqlite import get_connection, init_db
|
||||||
from app.main import app
|
from app.main import app
|
||||||
from app.repositories.alarm_repo import AlarmRepository
|
from app.repositories.alarm_repo import AlarmRepository
|
||||||
from app.repositories.json_config_repo import JsonConfigRepository
|
from app.repositories.json_config_repo import JsonConfigRepository
|
||||||
from app.schemas.platform import AlarmEvent
|
from app.schemas.platform import AlarmEvent, LineData, RealtimeData, ValueGroup
|
||||||
from app.services.platform_service import platform_service
|
from app.services.platform_service import platform_service
|
||||||
|
|
||||||
|
|
||||||
@ -28,6 +29,55 @@ def test_realtime_endpoint() -> None:
|
|||||||
assert "line_list" in payload["data"]
|
assert "line_list" in payload["data"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_realtime_precision_normalization() -> None:
|
||||||
|
realtime = RealtimeData(
|
||||||
|
line_list=[
|
||||||
|
LineData(
|
||||||
|
line_no=1,
|
||||||
|
pri_val=ValueGroup(
|
||||||
|
Ua=5765.439,
|
||||||
|
Ia=61.728,
|
||||||
|
),
|
||||||
|
sec_val=ValueGroup(
|
||||||
|
Ua=57.65439,
|
||||||
|
Ia=1.23456,
|
||||||
|
Pa=68.245,
|
||||||
|
Qa=9.876,
|
||||||
|
Sa=69.995,
|
||||||
|
PFa=0.98123,
|
||||||
|
Uab=100.98765,
|
||||||
|
frq=49.99994,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
switch={"di1": 1},
|
||||||
|
ai_collect={"ai1": 4.5678},
|
||||||
|
)
|
||||||
|
|
||||||
|
normalized = platform_service._normalize_realtime_precision(realtime)
|
||||||
|
line = normalized.line_list[0]
|
||||||
|
|
||||||
|
assert line.pri_val.Ua == 5765.439
|
||||||
|
assert line.pri_val.Ia == 61.728
|
||||||
|
assert line.pri_val.Pa == 341225.0
|
||||||
|
assert line.pri_val.Qa == 49380.0
|
||||||
|
assert line.pri_val.Sa == 349975.0
|
||||||
|
assert line.pri_val.PFa == 0.981
|
||||||
|
assert line.pri_val.Uab == 10098.765
|
||||||
|
assert line.pri_val.frq == 50.0
|
||||||
|
|
||||||
|
assert line.sec_val.Ua == 57.654
|
||||||
|
assert line.sec_val.Ia == 1.235
|
||||||
|
assert line.sec_val.Pa == 68.25
|
||||||
|
assert line.sec_val.Qa == 9.88
|
||||||
|
assert line.sec_val.Sa == 70.0
|
||||||
|
assert line.sec_val.PFa == 0.981
|
||||||
|
assert line.sec_val.Uab == 100.988
|
||||||
|
assert line.sec_val.frq == 50.0
|
||||||
|
|
||||||
|
assert normalized.ai_collect["ai1"] == 4.57
|
||||||
|
|
||||||
|
|
||||||
def test_alarm_list_filters(tmp_path) -> None:
|
def test_alarm_list_filters(tmp_path) -> None:
|
||||||
settings.alarm_db_path = tmp_path / "alarm.db"
|
settings.alarm_db_path = tmp_path / "alarm.db"
|
||||||
init_db()
|
init_db()
|
||||||
@ -259,3 +309,84 @@ def test_save_device_net_and_uart_by_key(tmp_path) -> None:
|
|||||||
assert saved["password"] == "hashed-password"
|
assert saved["password"] == "hashed-password"
|
||||||
finally:
|
finally:
|
||||||
platform_service.config_repo = old_repo
|
platform_service.config_repo = old_repo
|
||||||
|
|
||||||
|
|
||||||
|
def test_save_device_password_only_updates_password(tmp_path) -> None:
|
||||||
|
old_repo = platform_service.config_repo
|
||||||
|
platform_service.config_repo = JsonConfigRepository(tmp_path)
|
||||||
|
|
||||||
|
try:
|
||||||
|
platform_service.config_repo.write_device_config(
|
||||||
|
{
|
||||||
|
"password": "old-hash",
|
||||||
|
"hardware_version": {
|
||||||
|
"board_version": "B001.001.001",
|
||||||
|
"display_version": "S001.001.001",
|
||||||
|
"other_version": "Y001.001.001",
|
||||||
|
},
|
||||||
|
"software_version": {
|
||||||
|
"display_program": "001.001.001",
|
||||||
|
"communication_program": "001.001.001",
|
||||||
|
"measurement_program": "001.001.001",
|
||||||
|
},
|
||||||
|
"net": [
|
||||||
|
{"nic": "网卡一", "ip": "192.168.1.10", "mask": "255.255.255.0", "gateway": "192.168.1.1", "protocol": "Modbus TCP"}
|
||||||
|
],
|
||||||
|
"uart": [
|
||||||
|
{"port": "COM1", "baud": 9600, "parity": "NONE", "data_bits": 8, "stop_bits": 1, "protocol": "Modbus RTU"}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
headers = {"X-API-Token": settings.auth_password}
|
||||||
|
response = client.post(
|
||||||
|
"/api/config/device/password",
|
||||||
|
headers=headers,
|
||||||
|
json={"password": "654321"},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
saved = platform_service.config_repo.read_device_config()
|
||||||
|
assert saved["password"] == hash_password("654321")
|
||||||
|
assert saved["net"][0]["nic"] == "网卡一"
|
||||||
|
assert saved["uart"][0]["port"] == "COM1"
|
||||||
|
assert saved["hardware_version"]["board_version"] == "B001.001.001"
|
||||||
|
finally:
|
||||||
|
platform_service.config_repo = old_repo
|
||||||
|
|
||||||
|
|
||||||
|
def test_verify_password_reads_device_config_hash(tmp_path) -> None:
|
||||||
|
old_repo = platform_service.config_repo
|
||||||
|
old_config_dir = settings.config_dir
|
||||||
|
settings.config_dir = tmp_path
|
||||||
|
platform_service.config_repo = JsonConfigRepository(tmp_path)
|
||||||
|
|
||||||
|
try:
|
||||||
|
platform_service.config_repo.write_device_config(
|
||||||
|
{
|
||||||
|
"password": hash_password("654321"),
|
||||||
|
"hardware_version": {
|
||||||
|
"board_version": "B001.001.001",
|
||||||
|
"display_version": "S001.001.001",
|
||||||
|
"other_version": "Y001.001.001",
|
||||||
|
},
|
||||||
|
"software_version": {
|
||||||
|
"display_program": "001.001.001",
|
||||||
|
"communication_program": "001.001.001",
|
||||||
|
"measurement_program": "001.001.001",
|
||||||
|
},
|
||||||
|
"net": [],
|
||||||
|
"uart": [],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
ok_response = client.post("/api/auth/verify-password", json={"password": "654321"})
|
||||||
|
fail_response = client.post("/api/auth/verify-password", json={"password": "admin123"})
|
||||||
|
|
||||||
|
assert ok_response.status_code == 200
|
||||||
|
assert ok_response.json()["data"] is True
|
||||||
|
assert fail_response.status_code == 200
|
||||||
|
assert fail_response.json()["data"] is False
|
||||||
|
finally:
|
||||||
|
settings.config_dir = old_config_dir
|
||||||
|
platform_service.config_repo = old_repo
|
||||||
|
|||||||
1544
frontend/package-lock.json
generated
1544
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -10,10 +10,13 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.7.2",
|
"axios": "^1.7.2",
|
||||||
"vue": "^3.5.13"
|
"element-plus": "^2.14.0",
|
||||||
|
"vue": "^3.5.13",
|
||||||
|
"vue-router": "^5.0.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^5.2.1",
|
"@vitejs/plugin-vue": "^5.2.1",
|
||||||
|
"sass-embedded": "^1.99.0",
|
||||||
"typescript": "^5.7.3",
|
"typescript": "^5.7.3",
|
||||||
"vite": "^6.0.5",
|
"vite": "^6.0.5",
|
||||||
"vue-tsc": "^2.2.0"
|
"vue-tsc": "^2.2.0"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user