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.