feat(agent-runtime): 接入多服务商安全配置

This commit is contained in:
bruce
2026-09-18 16:43:48 +08:00
parent 1e56a55399
commit f8c0c2d411
20 changed files with 1087 additions and 31 deletions
@@ -0,0 +1,57 @@
{% extends "base.html" %}
{% block title %}API 服务商 · JobRadar{% endblock %}
{% block content %}
<div class="provider-layout{% if selected_provider %} has-drawer{% endif %}">
<section class="provider-main">
<header class="page-heading">
<div><h1>API 服务商</h1><p>连接模型服务,为不同 Agent 选择合适的模型</p></div>
<a class="primary-button" href="?provider=custom">+ 自定义服务商</a>
</header>
<div class="provider-tools">
<form class="provider-search" method="get">
<label class="sr-only" for="provider-search">搜索服务商</label>
<input id="provider-search" name="q" value="{{ query }}" placeholder="⌕ 搜索服务商">
<input type="hidden" name="category" value="{{ active_category }}">
</form>
<nav class="filter-tabs" aria-label="服务商分类">
{% for code, label in categories.items %}<a href="?category={{ code }}{% if query %}&amp;q={{ query|urlencode }}{% endif %}" {% if active_category == code %}aria-current="page"{% endif %}>{{ label }}</a>{% endfor %}
</nav>
<span class="security-note">▣ 密钥仅在服务端加密保存</span>
</div>
<div class="provider-summary"><strong>共 {{ provider_count }} 个内置服务商</strong><span>{{ connected_count }} 个已连接</span></div>
<div class="provider-grid">
{% for card in cards %}
<article class="provider-card{% if selected_provider.code == card.provider.code %} selected{% endif %}">
<a class="card-link" href="?provider={{ card.provider.code }}&amp;category={{ active_category }}{% if query %}&amp;q={{ query|urlencode }}{% endif %}" aria-label="配置 {{ card.provider.name }}"></a>
<div class="provider-card-head"><span class="provider-mark mark-{{ card.provider.category }}">{{ card.provider.mark }}</span><span><strong>{{ card.provider.name }}</strong><small>{{ card.provider.vendor }}</small></span><span class="card-menu" aria-hidden="true">⋮</span></div>
<div class="provider-tags"><span>{{ card.provider.protocol_label }}</span><span>{% if card.provider.key_only %}仅 API Key{% else %}需额外配置{% endif %}</span></div>
<p class="connection-status {% if card.config %}connected{% endif %}"><span aria-hidden="true">●</span>{% if card.config %}{{ card.config.get_status_display }}{% else %}未配置{% endif %}{% if card.config.is_default %}<em>默认</em>{% endif %}</p>
</article>
{% empty %}<div class="empty-panel"><h2>没有匹配的服务商</h2><p>请调整搜索词或分类条件。</p></div>{% endfor %}
</div>
</section>
{% if selected_provider %}
<aside class="provider-drawer" aria-labelledby="drawer-title">
<div class="drawer-header"><div><h2 id="drawer-title">配置{{ selected_provider.name }}</h2><p>系统已预置接口地址和协议</p></div><a class="drawer-close" href="{% url 'agent_runtime:provider-list' %}" aria-label="关闭">×</a></div>
<dl class="preset-details"><div><dt>接口协议</dt><dd>{{ selected_provider.protocol_label }}</dd></div><div><dt>接口地址</dt><dd>{% if selected_provider.base_url %}由系统预置{% else %}需要额外配置{% endif %}</dd></div></dl>
{% if selected_provider.key_only %}
<form class="provider-form" method="post" action="{% url 'agent_runtime:provider-configure' selected_provider.code %}?provider={{ selected_provider.code }}">
{% csrf_token %}
{% if provider_form.non_field_errors %}<div class="form-alert">{{ provider_form.non_field_errors }}</div>{% endif %}
<div class="field-row"><label for="{{ provider_form.api_key.id_for_label }}">API Key</label>{{ provider_form.api_key }}{% if selected_config %}<small>当前密钥:{{ selected_config.key_hint }};重新保存将覆盖旧密钥。</small>{% endif %}{{ provider_form.api_key.errors }}</div>
<div class="key-safety">▣ 密钥加密存储,保存后不可查看</div>
{% if selected_config %}<div class="connection-ok"><strong>✓ 已通过连接验证</strong><span>最近验证:{{ selected_config.last_verified_at|date:"Y-m-d H:i" }}</span></div>{% endif %}
<div class="field-row"><label for="{{ provider_form.model_id.id_for_label }}">模型选择</label>{{ provider_form.model_id }}<small>留空时自动使用服务商推荐模型。</small>{{ provider_form.model_id.errors }}</div>
<label class="checkbox-row">{{ provider_form.use_as_default }} <span>设为 Agent 默认服务商</span></label>
<details class="advanced-settings"><summary>高级设置(可选)</summary><p>接口地址和协议由系统预设,当前版本不允许用户覆盖。</p></details>
<div class="drawer-actions">{% if selected_config %}<button class="danger-button" form="disconnect-form" type="submit">移除配置</button>{% endif %}<a class="secondary-button" href="{% url 'agent_runtime:provider-list' %}">取消</a><button class="primary-button" type="submit">测试并保存</button></div>
</form>
{% if selected_config %}<form id="disconnect-form" method="post" action="{% url 'agent_runtime:provider-disconnect' selected_provider.code %}">{% csrf_token %}</form>{% endif %}
{% else %}
<div class="coming-soon"><span aria-hidden="true">◇</span><h3>需要额外配置</h3><p>{{ selected_provider.note }}</p><p>后续将根据该平台的地域、部署或 IAM 要求提供专用表单。</p></div>
<div class="drawer-actions"><a class="secondary-button" href="{% url 'agent_runtime:provider-list' %}">关闭</a></div>
{% endif %}
</aside>
{% endif %}
</div>
{% endblock %}
+37 -2
View File
@@ -1,5 +1,40 @@
<!doctype html>
<html lang="zh-CN">
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>{% block title %}JobRadar{% endblock %}</title><link rel="stylesheet" href="/static/css/app.css"></head>
<body><a class="skip" href="#main">跳到主要内容</a><header><strong>JobRadar · 职途雷达</strong>{% if user.is_authenticated %}<nav><a href="{% url 'agent_runtime:run-list' %}">运行记录</a><a href="{% url 'accounts:profile' %}">个人资料</a><form method="post" action="{% url 'accounts:logout' %}">{% csrf_token %}<button>退出</button></form></nav>{% endif %}</header><main id="main">{% for message in messages %}<p class="message">{{ message }}</p>{% endfor %}{% block content %}{% endblock %}</main></body>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>{% block title %}JobRadar{% endblock %}</title>
<link rel="stylesheet" href="/static/css/app.css">
</head>
<body class="{% if user.is_authenticated %}app-shell{% else %}public-shell{% endif %}">
<a class="skip" href="#main">跳到主要内容</a>
{% if user.is_authenticated %}
<aside class="sidebar" aria-label="主导航">
<a class="brand" href="{% url 'agent_runtime:run-list' %}">
<span class="brand-mark" aria-hidden="true">◎</span>
<span><strong>JobRadar</strong><small>职途雷达</small></span>
</a>
<nav class="side-nav">
<p class="nav-group">工作台</p>
<a href="{% url 'agent_runtime:run-list' %}"><span aria-hidden="true">◫</span> Agent 运行记录</a>
<p class="nav-group">设置</p>
<a href="{% url 'agent_runtime:provider-list' %}"><span aria-hidden="true">⚙</span> API 服务商</a>
<a href="{% url 'accounts:profile' %}"><span aria-hidden="true">○</span> 个人资料</a>
</nav>
<div class="sidebar-user">
<span class="avatar">{{ user.username|first|upper }}</span>
<span><strong>{{ user.username }}</strong><small>个人工作区</small></span>
<form method="post" action="{% url 'accounts:logout' %}">
{% csrf_token %}<button class="icon-button" title="退出登录">↗</button>
</form>
</div>
</aside>
{% else %}
<header class="public-header"><strong>JobRadar · 职途雷达</strong></header>
{% endif %}
<main id="main" class="main-content">
{% for message in messages %}<p class="message">{{ message }}</p>{% endfor %}
{% block content %}{% endblock %}
</main>
</body>
</html>