feat(server,web,desktop): admin "use system HTTP proxy" switch (#225)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e8499befd4
commit
91f3292ab5
29 files changed
+710
-19
No files matched your search
@@ -50,6 +50,8 @@ import type {
|
||||
ScheduleItem,
|
||||
SchedulesResponse,
|
||||
ScheduleUpsertRequest,
|
||||
ServerSettingsResponse,
|
||||
ServerSettingsUpdateRequest,
|
||||
SessionCategory,
|
||||
SessionCreateRequest,
|
||||
SessionCreateResponse,
|
||||
@@ -114,6 +116,13 @@ export const adminResetPassword = (userId: string, body: AdminPasswordResetReque
|
||||
export const adminDeleteUser = (userId: string) =>
|
||||
apiFetch<void>(`/api/admin/users/${encodeURIComponent(userId)}`, { method: "DELETE" });
|
||||
|
||||
/** Server-global settings (admin only): currently the "use system HTTP proxy" switch. */
|
||||
export const adminGetSettings = () => apiFetch<ServerSettingsResponse>("/api/admin/settings");
|
||||
|
||||
/** Omitted fields keep their current value; applies immediately (no restart). */
|
||||
export const adminPutSettings = (body: ServerSettingsUpdateRequest) =>
|
||||
apiFetch<ServerSettingsResponse>("/api/admin/settings", { method: "PUT", body });
|
||||
|
||||
// Project & members --------------------------------------------------------------
|
||||
|
||||
export const listProjects = () => apiFetch<ProjectsResponse>("/api/projects");
|
||||
|
||||
@@ -234,6 +234,35 @@ export function Sidebar({
|
||||
setUpdateChecking(false);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Admin-only "use system HTTP proxy" switch (server-global, design § "出网与系统代理"):
|
||||
* null = not hydrated yet. Fetched lazily the first time an ADMIN opens the dropdown —
|
||||
* same laziness as the version info above, and non-admins never call the endpoint (the
|
||||
* row is not rendered for them either). A failed hydration stays null (row disabled)
|
||||
* and is retried on the next open.
|
||||
*/
|
||||
const [useSystemProxy, setUseSystemProxyState] = useState<boolean | null>(null);
|
||||
useEffect(() => {
|
||||
if (!userOpen || user?.isAdmin !== true || useSystemProxy !== null) return;
|
||||
let cancelled = false;
|
||||
void api
|
||||
.adminGetSettings()
|
||||
.then((res) => {
|
||||
if (!cancelled) setUseSystemProxyState(res.settings.useSystemProxy);
|
||||
})
|
||||
.catch(() => undefined);
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [userOpen, user?.isAdmin, useSystemProxy]);
|
||||
/** Saved immediately on toggle (the user-menu quick-control convention): optimistic flip, reverted with a toast on failure. */
|
||||
const setUseSystemProxy = (value: boolean) => {
|
||||
setUseSystemProxyState(value);
|
||||
void api.adminPutSettings({ useSystemProxy: value }).catch((e: unknown) => {
|
||||
setUseSystemProxyState(!value);
|
||||
toastError(apiErrorText(e));
|
||||
});
|
||||
};
|
||||
const currentProjectId = currentProject?.projectId ?? null;
|
||||
const collapseStoreKey = currentProjectId === null ? null : collapsedGroupsKey(currentProjectId);
|
||||
const pinStoreKey = currentProjectId === null ? null : pinnedGroupsKey(currentProjectId);
|
||||
@@ -1005,6 +1034,19 @@ export function Sidebar({
|
||||
<SettingRow label={S.settings.showCliSessions}>
|
||||
<Switch checked={showCliSessions} onChange={setShowCliSessions} />
|
||||
</SettingRow>
|
||||
{/* Admin-only, server-global (all users), saved immediately on toggle; the
|
||||
tooltip spells out the scope and the loopback exemption. Disabled (showing
|
||||
the default: on) until the stored value has hydrated, so a click can never
|
||||
write a value the admin was not looking at. */}
|
||||
{user?.isAdmin && (
|
||||
<SettingRow label={S.settings.useSystemProxy} title={S.settings.useSystemProxyHint}>
|
||||
<Switch
|
||||
checked={useSystemProxy ?? true}
|
||||
onChange={setUseSystemProxy}
|
||||
disabled={useSystemProxy === null}
|
||||
/>
|
||||
</SettingRow>
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-1 border-t border-gray-100 pt-1 dark:border-gray-800">
|
||||
<button
|
||||
@@ -1332,9 +1374,18 @@ function SessionRow({
|
||||
);
|
||||
}
|
||||
|
||||
function SettingRow({ label, children }: { label: string; children: ReactNode }) {
|
||||
function SettingRow({
|
||||
label,
|
||||
title,
|
||||
children,
|
||||
}: {
|
||||
label: string;
|
||||
/** Optional row tooltip (e.g. the system-proxy row's scope + loopback-exemption hint). */
|
||||
title?: string;
|
||||
children: ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<div>
|
||||
<div {...(title !== undefined ? { title } : {})}>
|
||||
<p className="mb-1 text-[11px] font-medium text-gray-500 dark:text-gray-400">{label}</p>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@@ -33,6 +33,11 @@ export const en: Strings = {
|
||||
language: "Language",
|
||||
/** Sidebar Session list: also show CLI-created Sessions (default off — the list then never scans the Trace directories). */
|
||||
showCliSessions: "Show CLI sessions",
|
||||
/** Admin-only server-global switch (design § "出网与系统代理"): saved immediately on toggle. */
|
||||
useSystemProxy: "Use system HTTP proxy",
|
||||
/** Row tooltip: scope (server + its child processes, server-wide) and the loopback exemption. */
|
||||
useSystemProxyHint:
|
||||
"Whether the server and its child processes (update checks, LLM requests, agent commands) reach the internet through the proxy named by HTTP_PROXY / HTTPS_PROXY. Applies server-wide; loopback addresses (localhost, 127.0.0.1, ::1) always connect directly. When off, the server connects directly and the proxy variables are removed from agent command subprocess environments.",
|
||||
theme: "Theme",
|
||||
themeLight: "Light",
|
||||
themeDark: "Dark",
|
||||
|
||||
@@ -35,6 +35,11 @@ export const zh = {
|
||||
language: "语言",
|
||||
/** Sidebar Session list: also show CLI-created Sessions (default off — the list then never scans the Trace directories). */
|
||||
showCliSessions: "显示 CLI 会话",
|
||||
/** Admin-only server-global switch (design § "出网与系统代理"): saved immediately on toggle. */
|
||||
useSystemProxy: "使用系统 HTTP 代理",
|
||||
/** Row tooltip: scope (server + its child processes, server-wide) and the loopback exemption. */
|
||||
useSystemProxyHint:
|
||||
"服务端及其子进程(更新检查、LLM 请求、Agent 命令)出网时是否使用 HTTP_PROXY / HTTPS_PROXY 环境变量指定的代理,对整个服务端全局生效;回环地址(localhost、127.0.0.1、::1)始终直连。关闭后服务端一律直连,并从 Agent 命令子进程环境中移除代理变量。",
|
||||
theme: "主题",
|
||||
themeLight: "浅色",
|
||||
themeDark: "深色",
|
||||
|
||||
Reference in new issue
Block a user