refac
This commit is contained in:
@@ -269,6 +269,51 @@ async def set_terminal_servers_config(
|
||||
}
|
||||
|
||||
|
||||
@router.post('/terminal_servers/verify')
|
||||
async def verify_terminal_server_connection(
|
||||
request: Request, form_data: TerminalServerConnection, user=Depends(get_admin_user)
|
||||
):
|
||||
"""
|
||||
Verify the connection to a terminal server by detecting its type.
|
||||
|
||||
Tries GET {url}/api/v1/policies (orchestrator) then GET {url}/api/config
|
||||
(plain terminal). Returns ``{status: true, type: "orchestrator"|"terminal"}``.
|
||||
"""
|
||||
base_url = (form_data.url or '').rstrip('/')
|
||||
if not base_url:
|
||||
raise HTTPException(status_code=400, detail='Terminal server URL is required')
|
||||
|
||||
headers = {}
|
||||
if form_data.auth_type == 'bearer' and form_data.key:
|
||||
headers['Authorization'] = f'Bearer {form_data.key}'
|
||||
|
||||
try:
|
||||
async with aiohttp.ClientSession(
|
||||
trust_env=True,
|
||||
timeout=aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT),
|
||||
) as session:
|
||||
# Orchestrators expose a policies API; plain terminals don't.
|
||||
try:
|
||||
async with session.get(f'{base_url}/api/v1/policies', headers=headers) as resp:
|
||||
if resp.ok:
|
||||
return {'status': True, 'type': 'orchestrator'}
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Fall back to open-terminal config endpoint.
|
||||
try:
|
||||
async with session.get(f'{base_url}/api/config', headers=headers) as resp:
|
||||
if resp.ok:
|
||||
return {'status': True, 'type': 'terminal'}
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
except Exception as e:
|
||||
log.debug(f'Failed to connect to the terminal server: {e}')
|
||||
|
||||
raise HTTPException(status_code=400, detail='Failed to connect to the terminal server')
|
||||
|
||||
|
||||
@router.post('/tool_servers/verify')
|
||||
async def verify_tool_servers_config(request: Request, form_data: ToolServerConnection, user=Depends(get_admin_user)):
|
||||
"""
|
||||
|
||||
@@ -308,6 +308,40 @@ export const putOrchestratorPolicy = async (
|
||||
return res;
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify a terminal server connection via the backend proxy.
|
||||
* Used for system/admin connections to avoid CORS issues and API key exposure.
|
||||
*/
|
||||
export const verifyTerminalServerConnection = async (token: string, connection: object) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${WEBUI_API_BASE_URL}/configs/terminal_servers/verify`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
...connection
|
||||
})
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw await res.json();
|
||||
return res.json();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
error = err.detail;
|
||||
return null;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
export const verifyToolServerConnection = async (token: string, connection: object) => {
|
||||
let error = null;
|
||||
|
||||
|
||||
@@ -12,12 +12,12 @@
|
||||
import LockClosed from '$lib/components/icons/LockClosed.svelte';
|
||||
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
||||
import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
|
||||
import { detectTerminalServerType, putOrchestratorPolicy } from '$lib/apis/configs';
|
||||
import { detectTerminalServerType, verifyTerminalServerConnection, putOrchestratorPolicy } from '$lib/apis/configs';
|
||||
import { getTerminalConfig } from '$lib/apis/terminal';
|
||||
|
||||
export let show = false;
|
||||
export let edit = false;
|
||||
export let admin = false;
|
||||
export let direct = false;
|
||||
export let connection = null;
|
||||
|
||||
export let onSubmit: Function = () => {};
|
||||
@@ -110,9 +110,13 @@
|
||||
|
||||
verifying = true;
|
||||
try {
|
||||
if (admin) {
|
||||
// Admin: detect orchestrator vs terminal
|
||||
const type = await detectTerminalServerType(_url, key);
|
||||
if (!direct) {
|
||||
// System connection: proxy through backend to avoid CORS / key exposure
|
||||
const result = await verifyTerminalServerConnection(
|
||||
localStorage.token,
|
||||
{ url: _url, key, auth_type }
|
||||
);
|
||||
const type = result?.type ?? null;
|
||||
|
||||
if (type) {
|
||||
serverType = type;
|
||||
@@ -137,7 +141,7 @@
|
||||
toast.error($i18n.t('Server connection failed'));
|
||||
}
|
||||
} else {
|
||||
// Non-admin: simple terminal verification
|
||||
// Direct connection: verify from browser
|
||||
const res = await getTerminalConfig(_url, key);
|
||||
if (res) {
|
||||
toast.success($i18n.t('Server connection verified'));
|
||||
@@ -192,7 +196,7 @@
|
||||
url = url.replace(/\/$/, '');
|
||||
|
||||
// Save policy to orchestrator if applicable
|
||||
if (serverType === 'orchestrator' && admin && policyId) {
|
||||
if (serverType === 'orchestrator' && !direct && policyId) {
|
||||
try {
|
||||
await putOrchestratorPolicy(url, key, policyId, buildPolicyData());
|
||||
} catch (err) {
|
||||
@@ -202,7 +206,7 @@
|
||||
}
|
||||
|
||||
const result = {
|
||||
...(admin && id.trim() ? { id: id.trim() } : {}),
|
||||
...(!direct && id.trim() ? { id: id.trim() } : {}),
|
||||
url,
|
||||
key,
|
||||
name,
|
||||
@@ -210,7 +214,7 @@
|
||||
auth_type,
|
||||
enabled: enabled,
|
||||
config: {
|
||||
...(admin ? { access_grants: accessGrants } : {})
|
||||
...(!direct ? { access_grants: accessGrants } : {})
|
||||
},
|
||||
// Policy fields
|
||||
...(serverType ? { server_type: serverType } : {}),
|
||||
@@ -270,7 +274,7 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{#if admin}
|
||||
{#if !direct}
|
||||
<div class="flex flex-col flex-1">
|
||||
<div class="flex justify-between mb-0.5">
|
||||
<label
|
||||
@@ -368,7 +372,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Policy section (orchestrator only, admin only) -->
|
||||
{#if serverType === 'orchestrator' && admin}
|
||||
{#if serverType === 'orchestrator' && !direct}
|
||||
<div class="flex gap-2 mt-2">
|
||||
<div class="flex flex-col w-full">
|
||||
<div class="flex justify-between mb-0.5">
|
||||
@@ -580,7 +584,7 @@
|
||||
{$i18n.t('Advanced')}
|
||||
</button>
|
||||
|
||||
{#if admin}
|
||||
{#if !direct}
|
||||
<button
|
||||
class="bg-gray-50 hover:bg-gray-100 text-black dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-white transition px-2 py-1 object-cover rounded-full flex gap-1 items-center mt-2"
|
||||
type="button"
|
||||
@@ -662,7 +666,7 @@
|
||||
>
|
||||
<option value="none">{$i18n.t('None')}</option>
|
||||
<option value="bearer">{$i18n.t('Bearer')}</option>
|
||||
{#if admin}
|
||||
{#if !direct}
|
||||
<option value="session">{$i18n.t('Session')}</option>
|
||||
<option value="system_oauth">{$i18n.t('OAuth')}</option>
|
||||
{/if}
|
||||
|
||||
@@ -122,7 +122,6 @@
|
||||
<AddToolServerModal bind:show={showConnectionModal} onSubmit={addConnectionHandler} />
|
||||
|
||||
<AddTerminalServerModal
|
||||
admin
|
||||
bind:show={showAddTerminalModal}
|
||||
edit={editTerminalIdx !== null}
|
||||
connection={editTerminalIdx !== null ? terminalConnections[editTerminalIdx] : null}
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
};
|
||||
</script>
|
||||
|
||||
<AddTerminalServerModal bind:show={showAddModal} onSubmit={(server) => addServer(server)} />
|
||||
<AddTerminalServerModal direct bind:show={showAddModal} onSubmit={(server) => addServer(server)} />
|
||||
|
||||
<div>
|
||||
<div class="flex justify-between items-center mb-1">
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
</script>
|
||||
|
||||
<AddTerminalServerModal
|
||||
direct
|
||||
edit
|
||||
bind:show={showConfigModal}
|
||||
{connection}
|
||||
|
||||
Reference in New Issue
Block a user