Feature: System settings in DB - configurable backend URLs and API key
This commit is contained in:
@@ -9,9 +9,10 @@ const PLATFORMS = [
|
||||
{ id: 'windows', label: 'Windows', icon: Cpu },
|
||||
]
|
||||
|
||||
import { BACKEND_URL } from '../config'
|
||||
import { useSettingsStore } from '../stores/settings'
|
||||
|
||||
export default function Firmware() {
|
||||
const BACKEND_URL = useSettingsStore((s) => s.settings.backend_url_external || s.settings.backend_url_internal || '')
|
||||
const [firmwareData, setFirmwareData] = useState(null)
|
||||
const [installerData, setInstallerData] = useState(null)
|
||||
const [agents, setAgents] = useState([])
|
||||
|
||||
@@ -2,8 +2,8 @@ import { useEffect, useState } from 'react'
|
||||
import api from '../api/client'
|
||||
import { useAuthStore } from '../stores/auth'
|
||||
import { copyToClipboard } from '../utils/clipboard'
|
||||
import { Plus, Trash2, KeyRound, ShieldAlert, Copy, Check, Key } from 'lucide-react'
|
||||
import { BACKEND_HOST } from '../config'
|
||||
import { Plus, Trash2, KeyRound, ShieldAlert, Copy, Check, Key, Save, Settings } from 'lucide-react'
|
||||
import { useSettingsStore } from '../stores/settings'
|
||||
|
||||
export default function SettingsPage() {
|
||||
const [users, setUsers] = useState([])
|
||||
@@ -204,22 +204,8 @@ export default function SettingsPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Info */}
|
||||
<div className="bg-gray-900 rounded-lg border border-gray-800 overflow-hidden">
|
||||
<div className="px-4 py-3 border-b border-gray-800">
|
||||
<span className="text-sm font-medium text-gray-300">System</span>
|
||||
</div>
|
||||
<div className="px-4 py-3 space-y-1 text-sm">
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-500">Backend</span>
|
||||
<span className="text-gray-400">{BACKEND_HOST + ':8443'}</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-500">Authentifizierung</span>
|
||||
<span className="text-gray-400">JWT (8h Token) + API-Keys</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* System Settings */}
|
||||
<SystemSettingsSection />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -375,3 +361,111 @@ function APIKeysSection() {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function SystemSettingsSection() {
|
||||
const { settings, fetchSettings } = useSettingsStore()
|
||||
const [form, setForm] = useState({})
|
||||
const [saving, setSaving] = useState(false)
|
||||
const [msg, setMsg] = useState('')
|
||||
const [copied, setCopied] = useState(null)
|
||||
|
||||
useEffect(() => {
|
||||
fetchSettings()
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
setForm({
|
||||
backend_url_internal: settings.backend_url_internal || '',
|
||||
backend_url_external: settings.backend_url_external || '',
|
||||
api_key: settings.api_key || '',
|
||||
})
|
||||
}, [settings])
|
||||
|
||||
const handleSave = async () => {
|
||||
setSaving(true)
|
||||
setMsg('')
|
||||
try {
|
||||
await api.updateSettings(form)
|
||||
await fetchSettings()
|
||||
setMsg('Gespeichert')
|
||||
setTimeout(() => setMsg(''), 3000)
|
||||
} catch (e) {
|
||||
setMsg('Fehler: ' + e.message)
|
||||
} finally {
|
||||
setSaving(false)
|
||||
}
|
||||
}
|
||||
|
||||
const copyText = (text, id) => {
|
||||
copyToClipboard(text)
|
||||
setCopied(id)
|
||||
setTimeout(() => setCopied(null), 2000)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-gray-900 rounded-lg border border-gray-800 overflow-hidden">
|
||||
<div className="px-4 py-3 border-b border-gray-800 flex items-center gap-2">
|
||||
<Settings className="w-4 h-4 text-gray-500" />
|
||||
<span className="text-sm font-medium text-gray-300">System-Einstellungen</span>
|
||||
</div>
|
||||
<div className="px-4 py-4 space-y-3">
|
||||
<div>
|
||||
<label className="text-xs text-gray-500">Backend URL (intern)</label>
|
||||
<input
|
||||
type="text"
|
||||
value={form.backend_url_internal || ''}
|
||||
onChange={(e) => setForm({ ...form, backend_url_internal: e.target.value })}
|
||||
placeholder="https://192.168.85.13:8443"
|
||||
className="w-full bg-gray-800 border border-gray-700 rounded px-3 py-2 text-white text-sm focus:outline-none focus:border-orange-500 mt-1"
|
||||
/>
|
||||
<div className="text-[10px] text-gray-600 mt-0.5">Fuer Tunnel-Zugriff und lokale Verbindungen</div>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-gray-500">Backend URL (extern)</label>
|
||||
<input
|
||||
type="text"
|
||||
value={form.backend_url_external || ''}
|
||||
onChange={(e) => setForm({ ...form, backend_url_external: e.target.value })}
|
||||
placeholder="https://example.dyndns.org:8443"
|
||||
className="w-full bg-gray-800 border border-gray-700 rounded px-3 py-2 text-white text-sm focus:outline-none focus:border-orange-500 mt-1"
|
||||
/>
|
||||
<div className="text-[10px] text-gray-600 mt-0.5">Fuer Remote-Firewalls in der Installationsanleitung</div>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-gray-500">API-Key</label>
|
||||
<div className="flex gap-2 mt-1">
|
||||
<input
|
||||
type="text"
|
||||
value={form.api_key || ''}
|
||||
onChange={(e) => setForm({ ...form, api_key: e.target.value })}
|
||||
className="flex-1 bg-gray-800 border border-gray-700 rounded px-3 py-2 text-white text-sm font-mono focus:outline-none focus:border-orange-500"
|
||||
/>
|
||||
<button
|
||||
onClick={() => copyText(form.api_key, 'sys-apikey')}
|
||||
className="px-3 py-2 bg-gray-800 hover:bg-gray-700 rounded text-gray-400 transition-colors"
|
||||
title="Kopieren"
|
||||
>
|
||||
{copied === 'sys-apikey' ? <Check className="w-4 h-4 text-green-400" /> : <Copy className="w-4 h-4" />}
|
||||
</button>
|
||||
</div>
|
||||
<div className="text-[10px] text-gray-600 mt-0.5">Wird in Installationsanleitungen angezeigt</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 pt-2">
|
||||
<button
|
||||
onClick={handleSave}
|
||||
disabled={saving}
|
||||
className="flex items-center gap-2 px-4 py-2 bg-orange-600 hover:bg-orange-700 disabled:bg-gray-700 disabled:text-gray-500 rounded text-sm text-white transition-colors"
|
||||
>
|
||||
<Save className="w-4 h-4" />
|
||||
{saving ? 'Speichere...' : 'Speichern'}
|
||||
</button>
|
||||
{msg && (
|
||||
<span className={`text-sm ${msg.startsWith('Fehler') ? 'text-red-400' : 'text-green-400'}`}>
|
||||
{msg}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user