diff --git a/app/config/settings.py b/app/config/settings.py index 17fad3b..e71d33e 100644 --- a/app/config/settings.py +++ b/app/config/settings.py @@ -104,15 +104,20 @@ class ConfigManager: logger.info(f"已创建默认配置文件: {self.config_file}") def save_config(self) -> None: - """保存配置到文件(API 密钥不写入文件)""" - # 保存前临时清空 API 密钥,避免写入文件 + """保存配置到文件(敏感字段不写入文件)""" + # 保存前临时清空敏感字段,避免写入文件 saved_keys = {} - for option in ('api_key', 'secret_key'): + sensitive_fields = [ + ('API', 'api_key'), + ('API', 'secret_key'), + ('Gitea', 'token'), + ] + for section, option in sensitive_fields: try: - saved_keys[option] = self.config.get('API', option, fallback='') + saved_keys[(section, option)] = self.config.get(section, option, fallback='') except Exception: - saved_keys[option] = '' - self.config.set('API', option, '') + saved_keys[(section, option)] = '' + self.config.set(section, option, '') try: with open(self.config_file, 'w', encoding='utf-8') as f: @@ -120,9 +125,9 @@ class ConfigManager: logger.info(f"配置已保存到: {self.config_file}") finally: # 恢复内存中的值(即使写入失败也恢复) - for option, val in saved_keys.items(): + for (section, option), val in saved_keys.items(): if val: - self.config.set('API', option, val) + self.config.set(section, option, val) def get(self, section: str, option: str, fallback: Any = None) -> Any: """获取配置值""" diff --git a/config/config.ini b/config/config.ini index 34be985..2fb9649 100644 --- a/config/config.ini +++ b/config/config.ini @@ -37,5 +37,5 @@ item_data = 商品资料.xlsx base_url = https://gitea.94kan.cn owner = houhuan repo = yixuan-sync-data -token = +token = 50b61e43a141d606ae2529cd1755bc666d800e08 diff --git a/web/backend/routers/config_api.py b/web/backend/routers/config_api.py index 2aeecdb..cea01dd 100644 --- a/web/backend/routers/config_api.py +++ b/web/backend/routers/config_api.py @@ -64,6 +64,11 @@ async def get_config( return result +def _is_masked(key: str, value: str) -> bool: + """Check if a value looks like a masked sensitive field (contains asterisks).""" + return any(s in key.lower() for s in _SENSITIVE_KEYS) and '*' in value + + @router.put("") async def update_config( body: ConfigUpdate, @@ -72,6 +77,9 @@ async def update_config( if body.section not in _ALLOWED_SECTIONS: raise HTTPException(403, f"不允许修改配置节: {body.section}") + if _is_masked(body.key, body.value): + raise HTTPException(400, "敏感字段不能直接提交掩码值,请先清除输入框再输入真实值") + cfg = _get_config() try: cfg.update(body.section, body.key, body.value) @@ -88,11 +96,19 @@ async def bulk_update_config( ): cfg = _get_config() updated = [] + skipped = [] for item in body.updates: if item.section not in _ALLOWED_SECTIONS: continue + # Skip masked sensitive values to prevent destroying real credentials + if _is_masked(item.key, item.value): + skipped.append(f"[{item.section}] {item.key}") + continue cfg.update(item.section, item.key, item.value) updated.append(f"[{item.section}] {item.key}") cfg.save_config() - return {"message": f"已更新 {len(updated)} 项", "updated": updated} + msg = f"已更新 {len(updated)} 项" + if skipped: + msg += f",跳过 {len(skipped)} 项掩码值" + return {"message": msg, "updated": updated, "skipped": skipped} diff --git a/web/backend/routers/sync.py b/web/backend/routers/sync.py index 4b5c449..13e027f 100644 --- a/web/backend/routers/sync.py +++ b/web/backend/routers/sync.py @@ -78,6 +78,7 @@ async def sync_status( ): try: from app.config.settings import ConfigManager + import httpx as _httpx cfg = ConfigManager() base_url = cfg.get("Gitea", "base_url", fallback="").strip() owner = cfg.get("Gitea", "owner", fallback="").strip() @@ -85,6 +86,22 @@ async def sync_status( token = cfg.get("Gitea", "token", fallback="").strip() enabled = bool(base_url and owner and repo and token) repo_url = f"{base_url}/{owner}/{repo}" if enabled else "" - return {"enabled": enabled, "repo_url": repo_url} - except Exception: - return {"enabled": False, "repo_url": ""} + + connected = False + error = "" + if enabled: + try: + async with _httpx.AsyncClient(timeout=10) as client: + resp = await client.get( + f"{base_url}/api/v1/repos/{owner}/{repo}", + headers={"Authorization": f"token {token}"}, + ) + connected = resp.status_code == 200 + if not connected: + error = f"Gitea 返回 {resp.status_code}" + except Exception as e: + error = str(e) + + return {"enabled": enabled, "connected": connected, "repo_url": repo_url, "error": error} + except Exception as e: + return {"enabled": False, "connected": False, "repo_url": "", "error": str(e)} diff --git a/web/frontend/src/views/Sync.vue b/web/frontend/src/views/Sync.vue index 074f493..dcc1903 100644 --- a/web/frontend/src/views/Sync.vue +++ b/web/frontend/src/views/Sync.vue @@ -10,16 +10,24 @@