diff --git a/src/presentation/provider_controller.py b/src/presentation/provider_controller.py index 8026bdf..a94ee60 100644 --- a/src/presentation/provider_controller.py +++ b/src/presentation/provider_controller.py @@ -31,6 +31,7 @@ class ProviderController: self._app.route("/api/providers", methods=["GET"])(auth(self.get_providers)) self._app.route("/api/providers", methods=["POST"])(auth(self.create_provider)) self._app.route("/api/providers/fetch-models", methods=["GET"])(auth(self.fetch_models)) + self._app.route("/api/providers/chat-whitelist", methods=["PUT"])(auth(self.update_chat_whitelist)) self._app.route("/api/providers/", methods=["GET"])(auth(self.get_provider)) self._app.route("/api/providers/", methods=["PUT"])(auth(self.update_provider)) self._app.route("/api/providers/", methods=["DELETE"])(auth(self.delete_provider)) @@ -113,3 +114,15 @@ class ProviderController: except Exception as exc: self._logger.error(f"Error fetching provider models: {exc}") return jsonify({"error": str(exc)}), 500 + + def update_chat_whitelist(self) -> Response: + try: + payload = request.get_json(silent=True) or {} + enabled = self._provider_service.update_chat_whitelist_enabled(payload.get("enabled")) + self._logger.info("Chat whitelist updated: enabled=%s", enabled) + return jsonify({"enabled": enabled}) + except ValueError as exc: + return jsonify({"error": str(exc)}), 400 + except Exception as exc: + self._logger.error(f"Error updating chat whitelist: {exc}") + return jsonify({"error": str(exc)}), 500 diff --git a/src/presentation/static/css/providers.css b/src/presentation/static/css/providers.css index c99bedc..d251961 100644 --- a/src/presentation/static/css/providers.css +++ b/src/presentation/static/css/providers.css @@ -26,6 +26,67 @@ gap: 10px; } +.providers-page .toolbar-setting-card { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + padding: 12px 14px; + border: 1px solid var(--line); + border-radius: var(--radius-md); + background: rgba(255, 255, 255, 0.72); +} + +.providers-page .toolbar-setting-card + .toolbar-card { + margin-top: 16px; +} + +.providers-page .toolbar-setting-copy { + min-width: 0; + display: flex; + flex-direction: column; + gap: 3px; +} + +.providers-page .toolbar-setting-label { + color: var(--text-primary); + font-size: 13px; + font-weight: 700; +} + +.providers-page .toolbar-setting-hint { + color: var(--text-secondary); + font-size: 12px; + line-height: 1.45; +} + +.providers-page .toolbar-setting-action { + display: inline-flex; + align-items: center; + gap: 10px; + flex-shrink: 0; +} + +.providers-page .toolbar-setting-status { + color: var(--text-secondary); + font-size: 12px; + font-weight: 700; + white-space: nowrap; +} + +.providers-page .toolbar-setting-card[data-enabled="true"] .toolbar-setting-status { + color: var(--success); +} + +.providers-page .toolbar-setting-card[data-enabled="true"] { + border-color: rgba(28, 164, 113, 0.22); + background: rgba(28, 164, 113, 0.06); +} + +.providers-page .toolbar-setting-card[data-saving="true"] .toolbar-setting-status { + color: var(--primary-active); +} + .providers-page .toolbar-pill { display: inline-flex; align-items: center; @@ -128,6 +189,20 @@ color: currentColor; } +:root[data-theme="dark"] .providers-page .toolbar-setting-card { + border-color: #313b47; + background: rgba(255, 255, 255, 0.03); +} + +:root[data-theme="dark"] .providers-page .toolbar-setting-card[data-enabled="true"] { + border-color: rgba(46, 160, 104, 0.42); + background: rgba(46, 160, 104, 0.1); +} + +:root[data-theme="dark"] .providers-page .toolbar-setting-card[data-saving="true"] .toolbar-setting-status { + color: #9bc7ff; +} + .providers-page .providers-table tbody tr:nth-child(even) td { background: #fbfdff; } @@ -226,6 +301,58 @@ white-space: nowrap; } +.providers-page .switch { + position: relative; + display: inline-block; + width: 44px; + height: 24px; + vertical-align: middle; +} + +.providers-page .switch input { + opacity: 0; + width: 0; + height: 0; +} + +.providers-page .slider { + position: absolute; + inset: 0; + border-radius: 999px; + border: 1px solid #c8d7e6; + background: #e7eef6; + cursor: pointer; + transition: background-color var(--transition), border-color var(--transition), opacity var(--transition); +} + +.providers-page .slider::before { + content: ""; + position: absolute; + left: 2px; + top: 2px; + width: 18px; + height: 18px; + border-radius: 50%; + background: #fff; + box-shadow: 0 1px 2px rgba(15, 23, 42, 0.16); + transition: transform var(--transition), background-color var(--transition); +} + +.providers-page .switch input:checked + .slider { + background: #e7f5ef; + border-color: #b7dccf; +} + +.providers-page .switch input:checked + .slider::before { + transform: translateX(20px); + background: var(--success); +} + +.providers-page .switch input:disabled + .slider { + opacity: 0.72; + cursor: wait; +} + .providers-page .model-summary-bar { margin-top: 10px; display: flex; @@ -317,6 +444,15 @@ background: #0d1117; } +:root[data-theme="dark"] .providers-page .slider { + background: #222b36; + border-color: #30363d; +} + +:root[data-theme="dark"] .providers-page .slider::before { + background: #e8eef8; +} + :root[data-theme="dark"] .providers-page .btn-edit { background: #1b2230; border-color: #30363d; @@ -621,6 +757,16 @@ white-space: normal; } + .providers-page .toolbar-setting-card { + align-items: flex-start; + flex-direction: column; + } + + .providers-page .toolbar-setting-action { + width: 100%; + justify-content: space-between; + } + .providers-page .provider-form-grid { grid-template-columns: 1fr; } diff --git a/src/presentation/templates/providers.html b/src/presentation/templates/providers.html index 8ac1977..6174191 100644 --- a/src/presentation/templates/providers.html +++ b/src/presentation/templates/providers.html @@ -8,6 +8,19 @@ {% block content %}
+
+
+ Chat 白名单 + 控制 Provider Chat 接口是否启用白名单限制,启用后仅允许白名单访问的用户可访问Chat +
+
+ 已禁用 + +
+
共 0 个 Provider @@ -124,11 +137,14 @@ {% block page_scripts %}