PageSourceSearch

https://www.hamienet.com/assets/AiSettings-CXu_UOCT.js

js hamienet.com collected 2026-10-02 05:01:27 UTC 35,285 bytes, 550 lines download raw bytes

1import { r as reactExports, j as jsxRuntimeExports } from "./vendor-react-Bay9Y7Lb.js";
2import { a as api } from "./index-w5lXls15.js";
3import { D as DevDebugInfo } from "./AdminShared-BP_vxG_A.js";
4import { e4 as Bot, cM as RefreshCw, cd as Save, cF as Key, bz as EyeOff, bA as Eye, cs as Network, a$ as Shield, cj as Cpu, b5 as Trash2, cf as Plus, bn as Globe, dZ as Server, e7 as Lock } from "./vendor-D0P4XgNj.js";
5import "./vendor-i18n-BC7ZMXWQ.js";
6const DEFAULT_AI_CONFIG = {
7  provider: "gemini",
8  apiKey: "",
9  model: "gemini-2.5-flash-latest",
10  imageModel: "gemini-2.5-flash-image",
11  openRouterApiKey: "",
12  openRouterModel: "google/gemini-2.5-flash",
13  customModels: [],
14  routingMode: "direct",
15  proxyUrl: "",
16  vpnConfig: {
17    l2tp: { server: "", user: "", pass: "", psk: "" },
18    wireguard: { privateKey: "", address: "", peerPublicKey: "", endpoint: "", allowedIps: "0.0.0.0/0" }
19  }
20};
21const AVAILABLE_MODELS = [
22  { id: "gemini-3.1-pro-preview", name: "Gemini 3.1 Pro (Preview)" },
23  { id: "gemini-3-pro-preview", name: "Gemini 3 Pro (Preview)" },
24  { id: "gemini-3-flash-preview", name: "Gemini 3 Flash (Preview)" },
25  { id: "gemini-2.5-flash-latest", name: "Gemini 2.5 Flash" },
26  { id: "gemini-flash-latest", name: "Gemini Flash" },
27  { id: "gemini-flash-lite-latest", name: "Gemini Flash Lite" }
28];
29const AVAILABLE_IMAGE_MODELS = [
30  { id: "gemini-2.5-flash-image", name: "Gemini 2.5 Flash Image" },
31  { id: "imagen-3.0-generate-001", name: "Imagen 3" }
32];
33const AiSettings = ({ activeRole }) => {
34  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
35  const [config, setConfig] = reactExports.useState(DEFAULT_AI_CONFIG);
36  const [loading, setLoading] = reactExports.useState(true);
37  const [saving, setSaving] = reactExports.useState(false);
38  const [testing, setTesting] = reactExports.useState(false);
39  const [testResult, setTestResult] = reactExports.useState(null);
40  const [showKey, setShowKey] = reactExports.useState(false);
41  const [showVpnSecret, setShowVpnSecret] = reactExports.useState(false);
42  const [newModelId, setNewModelId] = reactExports.useState("");
43  const [newModelName, setNewModelName] = reactExports.useState("");
44  reactExports.useEffect(() => {
45    loadConfig();
46  }, []);
47  const loadConfig = async () => {
48    var _a2, _b2;
49    setLoading(true);
50    const saved = await api.getSystemSetting("ai_config");
51    if (saved && saved.value) {
52      const merged = { ...DEFAULT_AI_CONFIG, ...saved.value };
53      if (!merged.model) merged.model = DEFAULT_AI_CONFIG.model;
54      if (!merged.customModels) merged.customModels = [];
55      if (!merged.vpnConfig) merged.vpnConfig = DEFAULT_AI_CONFIG.vpnConfig;
56      if (!merged.vpnConfig.l2tp) merged.vpnConfig.l2tp = (_a2 = DEFAULT_AI_CONFIG.vpnConfig) == null ? void 0 : _a2.l2tp;
57      if (!merged.vpnConfig.wireguard) merged.vpnConfig.wireguard = (_b2 = DEFAULT_AI_CONFIG.vpnConfig) == null ? void 0 : _b2.wireguard;
58      setConfig(merged);
59      localStorage.setItem("hamie_ai_config", JSON.stringify(merged));
60    }
61    setLoading(false);
62  };
63  const handleSave = async () => {
64    setSaving(true);
65    try {
66      await api.saveSystemSetting("ai_config", config);
67      localStorage.setItem("hamie_ai_config", JSON.stringify(config));
68      alert("AI Settings saved successfully. Client updated.");
69    } catch (e) {
70      alert("Failed to save configuration.");
71    } finally {
72      setSaving(false);
73    }
74  };
75  const handleTestConnection = async () => {
76    setTesting(true);
77    setTestResult(null);
78    try {
79      const isOpenRouter = config.provider === "openrouter";
80      const apiKey = isOpenRouter ? config.openRouterApiKey : config.apiKey;
81      const model = isOpenRouter ? config.openRouterModel : config.model;
82      if (!apiKey) {
83        throw new Error("No API Key configured");
84      }
85      const response = await fetch("/api/admin/ai/test", {
86        method: "POST",
87        headers: { "Content-Type": "application/json" },
88        body: JSON.stringify({
89          provider: config.provider,
90          apiKey,
91          model,
92          routingMode: config.routingMode,
93          proxyUrl: config.proxyUrl,
94          vpnConfig: config.vpnConfig
95        })
96      });
97      const contentType = response.headers.get("content-type");
98      let data;
99      if (contentType && contentType.indexOf("application/json") !== -1) {
100        data = await response.json();
101      } else {
102        const text = await response.text();
103        throw new Error(`Server returned non-JSON response (${response.status}): ${text.substring(0, 100)}`);
104      }
105      if (!response.ok) {
106        throw new Error(data.message || `Server Error: ${response.status}`);
107      }
108      if (!data.success) {
109        throw new Error(data.message || "Test failed");
110      }
111      setTestResult({ success: true, message: data.message });
112    } catch (e) {
113      console.error("AI Test Failed", e);
114      setTestResult({ success: false, message: e.message || "Connection failed. Check API Key and Model." });
115    } finally {
116      setTesting(false);
117    }
118  };
119  const handleAddModel = () => {
120    if (!newModelId || !newModelName) return;
121    const newModel = { id: newModelId, name: newModelName };
122    setConfig((prev) => ({
123      ...prev,
124      customModels: [...prev.customModels || [], newModel]
125    }));
126    setNewModelId("");
127    setNewModelName("");
128  };
129  const handleRemoveModel = (id) => {
130    if (confirm("Remove this custom model?")) {
131      setConfig((prev) => ({
132        ...prev,
133        customModels: (prev.customModels || []).filter((m) => m.id !== id)
134      }));
135    }
136  };
137  const updateVpnConfig = (type, field, value) => {
138    setConfig((prev) => ({
139      ...prev,
140      vpnConfig: {
141        ...prev.vpnConfig,
142        [type]: {
143          ...prev.vpnConfig[type],
144          [field]: value
145        }
146      }
147    }));
148  };
149  if (loading) return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-10 text-center text-zinc-500", children: "Loading Configuration..." });
150  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-6 max-w-4xl mx-auto animate-in fade-in", children: [
151    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-xl p-6 shadow-sm", children: [
152      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between mb-6 pb-6 border-b border-zinc-100 dark:border-zinc-800", children: [
153        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [
154          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-12 h-12 bg-indigo-50 dark:bg-indigo-900/20 rounded-xl flex items-center justify-center text-indigo-600 dark:text-indigo-400", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Bot, { size: 24 }) }),
155          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
156            /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-xl font-bold text-zinc-900 dark:text-white", children: "AI Configuration" }),
157            /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-zinc-500", children: "Manage Gemini API keys, models, and routing infrastructure." })
158          ] })
159        ] }),
160        /* @__PURE__ */ jsxRuntimeExports.jsxs(
161          "button",
162          {
163            onClick: handleSave,
164            disabled: saving,
165            className: "px-6 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg font-bold text-sm flex items-center gap-2 shadow-lg shadow-indigo-500/20 disabled:opacity-50",
166            children: [
167              saving ? /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "animate-spin", size: 16 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Save, { size: 16 }),
168              "Save Config"
169            ]
170          }
171        )
172      ] }),
173      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-8", children: [
174        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
175          /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "font-bold text-sm text-zinc-900 dark:text-white flex items-center gap-2 mb-4", children: [
176            /* @__PURE__ */ jsxRuntimeExports.jsx(Key, { size: 16, className: "text-amber-500" }),
177            " API Credentials"
178          ] }),
179          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-50 dark:bg-zinc-800/50 p-4 rounded-xl border border-zinc-200 dark:border-zinc-800 space-y-4", children: [
180            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
181              /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-500 uppercase mb-2", children: "AI Provider" }),
182              /* @__PURE__ */ jsxRuntimeExports.jsxs(
183                "select",
184                {
185                  value: config.provider || "gemini",
186                  onChange: (e) => setConfig({ ...config, provider: e.target.value }),
187                  className: "w-full bg-white dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-700 rounded-lg p-3 text-sm outline-none focus:border-indigo-500 text-zinc-900 dark:text-white",
188                  children: [
189                    /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "gemini", children: "Google Gemini" }),
190                    /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "openrouter", children: "OpenRouter" })
191                  ]
192                }
193              )
194            ] }),
195            (!config.provider || config.provider === "gemini") && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
196              /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-500 uppercase mb-2", children: "Gemini API Key" }),
197              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative", children: [
198                /* @__PURE__ */ jsxRuntimeExports.jsx(
199                  "input",
200                  {
201                    type: showKey ? "text" : "password",
202                    value: config.apiKey,
203                    onChange: (e) => setConfig({ ...config, apiKey: e.target.value }),
204                    placeholder: "AIzaSy...",
205                    className: "w-full bg-white dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-700 rounded-lg pl-3 pr-10 py-2 text-sm outline-none focus:border-indigo-500 text-zinc-900 dark:text-white font-mono"
206                  }
207                ),
208                /* @__PURE__ */ jsxRuntimeExports.jsx(
209                  "button",
210                  {
211                    onClick: () => setShowKey(!showKey),
212                    className: "absolute right-3 top-1/2 -translate-y-1/2 text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-200",
213                    children: showKey ? /* @__PURE__ */ jsxRuntimeExports.jsx(EyeOff, { size: 16 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Eye, { size: 16 })
214                  }
215                )
216              ] }),
217              /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs text-zinc-500 mt-2", children: [
218                "Leave blank to use system environment variable ",
219                /* @__PURE__ */ jsxRuntimeExports.jsx("code", { children: "API_KEY" }),
220                "."
221              ] })
222            ] }),
223            config.provider === "openrouter" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
224              /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-500 uppercase mb-2", children: "OpenRouter API Key" }),
225              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative", children: [
226                /* @__PURE__ */ jsxRuntimeExports.jsx(
227                  "input",
228                  {
229                    type: showKey ? "text" : "password",
230                    value: config.openRouterApiKey || "",
231                    onChange: (e) => setConfig({ ...config, openRouterApiKey: e.target.value }),
232                    placeholder: "sk-or-v1-...",
233                    className: "w-full bg-white dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-700 rounded-lg pl-3 pr-10 py-2 text-sm outline-none focus:border-indigo-500 text-zinc-900 dark:text-white font-mono"
234                  }
235                ),
236                /* @__PURE__ */ jsxRuntimeExports.jsx(
237                  "button",
238                  {
239                    onClick: () => setShowKey(!showKey),
240                    className: "absolute right-3 top-1/2 -translate-y-1/2 text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-200",
241                    children: showKey ? /* @__PURE__ */ jsxRuntimeExports.jsx(EyeOff, { size: 16 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Eye, { size: 16 })
242                  }
243                )
244              ] })
245            ] }),
246            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-4 flex items-center gap-4", children: [
247              /* @__PURE__ */ jsxRuntimeExports.jsxs(
248                "button",
249                {
250                  onClick: handleTestConnection,
251                  disabled: testing,
252                  className: `px-4 py-2 rounded-lg text-xs font-bold flex items-center gap-2 transition-colors ${testing ? "bg-zinc-100 text-zinc-400" : "bg-zinc-100 hover:bg-zinc-200 dark:bg-zinc-700 dark:hover:bg-zinc-600 text-zinc-700 dark:text-zinc-200"}`,
253                  children: [
254                    testing ? /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "animate-spin", size: 14 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Network, { size: 14 }),
255                    testing ? "Testing..." : "Test Connection"
256                  ]
257                }
258              ),
259              testResult && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `text-xs font-bold flex items-center gap-2 ${testResult.success ? "text-emerald-600 dark:text-emerald-400" : "text-red-600 dark:text-red-400"}`, children: [
260                testResult.success ? /* @__PURE__ */ jsxRuntimeExports.jsx(Shield, { size: 14 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Bot, { size: 14 }),
261                testResult.message
262              ] })
263            ] })
264          ] })
265        ] }),
266        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
267          /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "font-bold text-sm text-zinc-900 dark:text-white flex items-center gap-2 mb-4", children: [
268            /* @__PURE__ */ jsxRuntimeExports.jsx(Cpu, { size: 16, className: "text-blue-500" }),
269            " Model Selection"
270          ] }),
271          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-50 dark:bg-zinc-800/50 p-4 rounded-xl border border-zinc-200 dark:border-zinc-800", children: [
272            config.provider === "openrouter" ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
273              /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-500 uppercase mb-2", children: "OpenRouter Model ID" }),
274              /* @__PURE__ */ jsxRuntimeExports.jsx(
275                "input",
276                {
277                  value: config.openRouterModel || "",
278                  onChange: (e) => setConfig({ ...config, openRouterModel: e.target.value }),
279                  placeholder: "e.g. google/gemini-2.5-flash",
280                  className: "w-full bg-white dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-700 rounded-lg p-3 text-sm outline-none focus:border-indigo-500 text-zinc-900 dark:text-white font-mono"
281                }
282              ),
283              /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs text-zinc-500 mt-2", children: [
284                "Enter the exact model ID from OpenRouter (e.g. ",
285                /* @__PURE__ */ jsxRuntimeExports.jsx("code", { children: "google/gemini-2.5-flash" }),
286                ", ",
287                /* @__PURE__ */ jsxRuntimeExports.jsx("code", { children: "anthropic/claude-3-haiku" }),
288                ")."
289              ] })
290            ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
291              /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-500 uppercase mb-2", children: "Default Generation Model" }),
292              /* @__PURE__ */ jsxRuntimeExports.jsx(
293                "select",
294                {
295                  value: config.model,
296                  onChange: (e) => setConfig({ ...config, model: e.target.value }),
297                  className: "w-full bg-white dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-700 rounded-lg p-3 text-sm outline-none focus:border-indigo-500 text-zinc-900 dark:text-white",
298                  children: AVAILABLE_MODELS.concat(config.customModels || []).map((m) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: m.id, children: m.name }, m.id))
299                }
300              ),
301              /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs text-zinc-500 mt-2", children: [
302                "Select the model used for standard generation tasks. Default is ",
303                /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Gemini 2.5 Flash" }),
304                "."
305              ] })
306            ] }),
307            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-4 pt-4 border-t border-zinc-200 dark:border-zinc-700", children: [
308              /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-500 uppercase mb-2", children: "Image Generation Model" }),
309              /* @__PURE__ */ jsxRuntimeExports.jsx(
310                "select",
311                {
312                  value: config.imageModel || "gemini-2.5-flash-image",
313                  onChange: (e) => setConfig({ ...config, imageModel: e.target.value }),
314                  className: "w-full bg-white dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-700 rounded-lg p-3 text-sm outline-none focus:border-indigo-500 text-zinc-900 dark:text-white",
315                  children: AVAILABLE_IMAGE_MODELS.map((m) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: m.id, children: m.name }, m.id))
316                }
317              ),
318              /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-500 mt-2", children: "Select the model used for image generation tasks (e.g. Graphic Manager)." })
319            ] }),
320            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-6 pt-6 border-t border-zinc-200 dark:border-zinc-700", children: [
321              /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-500 uppercase mb-3", children: "Custom Models" }),
322              config.customModels && config.customModels.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-2 mb-4", children: config.customModels.map((m) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between bg-white dark:bg-zinc-900 p-2 rounded border border-zinc-200 dark:border-zinc-700", children: [
323                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col", children: [
324                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm font-bold text-zinc-700 dark:text-zinc-300", children: m.name }),
325                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-mono text-zinc-400", children: m.id })
326                ] }),
327                /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => handleRemoveModel(m.id), className: "p-1.5 hover:bg-red-50 dark:hover:bg-red-900/20 text-zinc-400 hover:text-red-500 rounded transition-colors", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { size: 14 }) })
328              ] }, m.id)) }),
329              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row gap-2", children: [
330                /* @__PURE__ */ jsxRuntimeExports.jsx(
331                  "input",
332                  {
333                    value: newModelName,
334                    onChange: (e) => setNewModelName(e.target.value),
335                    placeholder: "Display Name (e.g. Gemini Ultra)",
336                    className: "flex-1 bg-white dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-700 rounded-lg p-2 text-sm outline-none focus:border-indigo-500 text-zinc-900 dark:text-white"
337                  }
338                ),
339                /* @__PURE__ */ jsxRuntimeExports.jsx(
340                  "input",
341                  {
342                    value: newModelId,
343                    onChange: (e) => setNewModelId(e.target.value),
344                    placeholder: "Model ID (e.g. gemini-4-ultra)",
345                    className: "flex-1 bg-white dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-700 rounded-lg p-2 text-sm outline-none focus:border-indigo-500 text-zinc-900 dark:text-white font-mono"
346                  }
347                ),
348                /* @__PURE__ */ jsxRuntimeExports.jsxs(
349                  "button",
350                  {
351                    onClick: handleAddModel,
352                    disabled: !newModelId || !newModelName,
353                    className: "px-4 py-2 bg-zinc-900 dark:bg-white text-white dark:text-black hover:opacity-90 rounded-lg font-bold text-xs disabled:opacity-50 flex items-center gap-2 whitespace-nowrap",
354                    children: [
355                      /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { size: 14 }),
356                      " Add Model"
357                    ]
358                  }
359                )
360              ] })
361            ] })
362          ] })
363        ] }),
364        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
365          /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "font-bold text-sm text-zinc-900 dark:text-white flex items-center gap-2 mb-4", children: [
366            /* @__PURE__ */ jsxRuntimeExports.jsx(Globe, { size: 16, className: "text-emerald-500" }),
367            " Global Routing & VPN"
368          ] }),
369          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-2 md:grid-cols-4 gap-4 mb-6", children: [
370            { id: "direct", label: "Direct", icon: Network },
371            { id: "proxy", label: "HTTP Proxy", icon: Server },
372            { id: "vpn_l2tp", label: "L2TP VPN", icon: Shield },
373            { id: "vpn_wireguard", label: "WireGuard", icon: Lock }
374          ].map((mode) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
375            "button",
376            {
377              onClick: () => setConfig({ ...config, routingMode: mode.id }),
378              className: `flex flex-col items-center justify-center p-4 rounded-xl border transition-all ${config.routingMode === mode.id ? "bg-indigo-50 dark:bg-indigo-900/20 border-indigo-500 text-indigo-700 dark:text-indigo-400" : "bg-white dark:bg-zinc-900 border-zinc-200 dark:border-zinc-800 text-zinc-500 hover:border-zinc-300 dark:hover:border-zinc-700"}`,
379              children: [
380                /* @__PURE__ */ jsxRuntimeExports.jsx(mode.icon, { size: 24, className: "mb-2" }),
381                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-bold", children: mode.label })
382              ]
383            },
384            mode.id
385          )) }),
386          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-50 dark:bg-zinc-900/50 p-6 rounded-xl border border-zinc-200 dark:border-zinc-800", children: [
387            config.routingMode === "direct" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center py-8 text-zinc-500", children: [
388              /* @__PURE__ */ jsxRuntimeExports.jsx(Globe, { size: 48, className: "mx-auto mb-4 opacity-20" }),
389              /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: "Standard connection. No routing or proxy applied." })
390            ] }),
391            config.routingMode === "proxy" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
392              /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-500 uppercase mb-2", children: "Proxy Endpoint URL" }),
393              /* @__PURE__ */ jsxRuntimeExports.jsx(
394                "input",
395                {
396                  value: config.proxyUrl,
397                  onChange: (e) => setConfig({ ...config, proxyUrl: e.target.value }),
398                  placeholder: "http://proxy.example.com:8080",
399                  className: "w-full bg-white dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-700 rounded-lg p-3 text-sm outline-none focus:border-indigo-500 text-zinc-900 dark:text-white font-mono"
400                }
401              ),
402              /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-500 mt-2", children: "Requests will be forwarded through this Japan server endpoint." })
403            ] }),
404            config.routingMode === "vpn_l2tp" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
405              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
406                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
407                  /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-500 uppercase mb-1", children: "Server Address" }),
408                  /* @__PURE__ */ jsxRuntimeExports.jsx(
409                    "input",
410                    {
411                      value: (_b = (_a = config.vpnConfig) == null ? void 0 : _a.l2tp) == null ? void 0 : _b.server,
412                      onChange: (e) => updateVpnConfig("l2tp", "server", e.target.value),
413                      className: "w-full bg-white dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-700 rounded-lg p-2 text-sm outline-none focus:border-indigo-500 text-zinc-900 dark:text-white"
414                    }
415                  )
416                ] }),
417                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
418                  /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-500 uppercase mb-1", children: "PSK (Pre-Shared Key)" }),
419                  /* @__PURE__ */ jsxRuntimeExports.jsx(
420                    "input",
421                    {
422                      type: showVpnSecret ? "text" : "password",
423                      value: (_d = (_c = config.vpnConfig) == null ? void 0 : _c.l2tp) == null ? void 0 : _d.psk,
424                      onChange: (e) => updateVpnConfig("l2tp", "psk", e.target.value),
425                      className: "w-full bg-white dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-700 rounded-lg p-2 text-sm outline-none focus:border-indigo-500 text-zinc-900 dark:text-white"
426                    }
427                  )
428                ] })
429              ] }),
430              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
431                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
432                  /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-500 uppercase mb-1", children: "Username" }),
433                  /* @__PURE__ */ jsxRuntimeExports.jsx(
434                    "input",
435                    {
436                      value: (_f = (_e = config.vpnConfig) == null ? void 0 : _e.l2tp) == null ? void 0 : _f.user,
437                      onChange: (e) => updateVpnConfig("l2tp", "user", e.target.value),
438                      className: "w-full bg-white dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-700 rounded-lg p-2 text-sm outline-none focus:border-indigo-500 text-zinc-900 dark:text-white"
439                    }
440                  )
441                ] }),
442                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
443                  /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-500 uppercase mb-1", children: "Password" }),
444                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative", children: [
445                    /* @__PURE__ */ jsxRuntimeExports.jsx(
446                      "input",
447                      {
448                        type: showVpnSecret ? "text" : "password",
449                        value: (_h = (_g = config.vpnConfig) == null ? void 0 : _g.l2tp) == null ? void 0 : _h.pass,
450                        onChange: (e) => updateVpnConfig("l2tp", "pass", e.target.value),
451                        className: "w-full bg-white dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-700 rounded-lg p-2 text-sm outline-none focus:border-indigo-500 text-zinc-900 dark:text-white"
452                      }
453                    ),
454                    /* @__PURE__ */ jsxRuntimeExports.jsx(
455                      "button",
456                      {
457                        onClick: () => setShowVpnSecret(!showVpnSecret),
458                        className: "absolute right-3 top-1/2 -translate-y-1/2 text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-200",
459                        children: showVpnSecret ? /* @__PURE__ */ jsxRuntimeExports.jsx(EyeOff, { size: 16 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Eye, { size: 16 })
460                      }
461                    )
462                  ] })
463                ] })
464              ] })
465            ] }),
466            config.routingMode === "vpn_wireguard" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
467              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
468                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
469                  /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-500 uppercase mb-1", children: "Interface Address" }),
470                  /* @__PURE__ */ jsxRuntimeExports.jsx(
471                    "input",
472                    {
473                      value: (_j = (_i = config.vpnConfig) == null ? void 0 : _i.wireguard) == null ? void 0 : _j.address,
474                      onChange: (e) => updateVpnConfig("wireguard", "address", e.target.value),
475                      placeholder: "10.0.0.2/32",
476                      className: "w-full bg-white dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-700 rounded-lg p-2 text-sm outline-none focus:border-indigo-500 text-zinc-900 dark:text-white font-mono"
477                    }
478                  )
479                ] }),
480                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
481                  /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-500 uppercase mb-1", children: "Private Key" }),
482                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative", children: [
483                    /* @__PURE__ */ jsxRuntimeExports.jsx(
484                      "input",
485                      {
486                        type: showVpnSecret ? "text" : "password",
487                        value: (_l = (_k = config.vpnConfig) == null ? void 0 : _k.wireguard) == null ? void 0 : _l.privateKey,
488                        onChange: (e) => updateVpnConfig("wireguard", "privateKey", e.target.value),
489                        className: "w-full bg-white dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-700 rounded-lg p-2 text-sm outline-none focus:border-indigo-500 text-zinc-900 dark:text-white font-mono"
490                      }
491                    ),
492                    /* @__PURE__ */ jsxRuntimeExports.jsx(
493                      "button",
494                      {
495                        onClick: () => setShowVpnSecret(!showVpnSecret),
496                        className: "absolute right-3 top-1/2 -translate-y-1/2 text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-200",
497                        children: showVpnSecret ? /* @__PURE__ */ jsxRuntimeExports.jsx(EyeOff, { size: 16 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Eye, { size: 16 })
498                      }
499                    )
500                  ] })
501                ] })
502              ] }),
503              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
504                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
505                  /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-500 uppercase mb-1", children: "Peer Public Key" }),
506                  /* @__PURE__ */ jsxRuntimeExports.jsx(
507                    "input",
508                    {
509                      value: (_n = (_m = config.vpnConfig) == null ? void 0 : _m.wireguard) == null ? void 0 : _n.peerPublicKey,
510                      onChange: (e) => updateVpnConfig("wireguard", "peerPublicKey", e.target.value),
511                      className: "w-full bg-white dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-700 rounded-lg p-2 text-sm outline-none focus:border-indigo-500 text-zinc-900 dark:text-white font-mono"
512                    }
513                  )
514                ] }),
515                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
516                  /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-500 uppercase mb-1", children: "Endpoint (IP:Port)" }),
517                  /* @__PURE__ */ jsxRuntimeExports.jsx(
518                    "input",
519                    {
520                      value: (_p = (_o = config.vpnConfig) == null ? void 0 : _o.wireguard) == null ? void 0 : _p.endpoint,
521                      onChange: (e) => updateVpnConfig("wireguard", "endpoint", e.target.value),
522                      placeholder: "203.0.113.1:51820",
523                      className: "w-full bg-white dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-700 rounded-lg p-2 text-sm outline-none focus:border-indigo-500 text-zinc-900 dark:text-white font-mono"
524                    }
525                  )
526                ] })
527              ] }),
528              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
529                /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-500 uppercase mb-1", children: "Allowed IPs" }),
530                /* @__PURE__ */ jsxRuntimeExports.jsx(
531                  "input",
532                  {
533                    value: (_r = (_q = config.vpnConfig) == null ? void 0 : _q.wireguard) == null ? void 0 : _r.allowedIps,
534                    onChange: (e) => updateVpnConfig("wireguard", "allowedIps", e.target.value),
535                    placeholder: "0.0.0.0/0",
536                    className: "w-full bg-white dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-700 rounded-lg p-2 text-sm outline-none focus:border-indigo-500 text-zinc-900 dark:text-white font-mono"
537                  }
538                )
539              ] })
540            ] })
541          ] })
542        ] })
543      ] })
544    ] }),
545    /* @__PURE__ */ jsxRuntimeExports.jsx(DevDebugInfo, { table: "SystemSetting", keyName: "ai_config", value: config, isSuperAdmin: activeRole == null ? void 0 : activeRole.isSuperAdmin })
546  ] });
547};
548export {
549  AiSettings as default
550};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.