first commit
This commit is contained in:
118
frontend/app/admin/page.tsx
Normal file
118
frontend/app/admin/page.tsx
Normal file
@@ -0,0 +1,118 @@
|
||||
"use client";
|
||||
|
||||
import Shell from "@/components/Shell";
|
||||
import { apiFetch, getCsrfFromCookie } from "@/lib/api";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export default function AdminPage() {
|
||||
const [routers, setRouters] = useState<any[]>([]);
|
||||
const [err, setErr] = useState<string | null>(null);
|
||||
|
||||
// add router
|
||||
const [name, setName] = useState("r1");
|
||||
const [host, setHost] = useState("192.168.88.1");
|
||||
const [verify, setVerify] = useState(false);
|
||||
|
||||
// add credential
|
||||
const [routerId, setRouterId] = useState<number>(1);
|
||||
const [method, setMethod] = useState("rest");
|
||||
const [username, setUsername] = useState("admin");
|
||||
const [secret, setSecret] = useState("");
|
||||
|
||||
async function load() {
|
||||
setErr(null);
|
||||
try {
|
||||
const r = await apiFetch("/api/routers");
|
||||
setRouters(r);
|
||||
if (r?.length) setRouterId(r[0].id);
|
||||
} catch (e:any) { setErr(e.message || "error"); }
|
||||
}
|
||||
|
||||
useEffect(() => { load(); }, []);
|
||||
|
||||
async function createRouter() {
|
||||
setErr(null);
|
||||
try {
|
||||
const csrf = getCsrfFromCookie();
|
||||
await apiFetch("/api/routers", {
|
||||
method: "POST",
|
||||
headers: { "X-CSRF-Token": csrf || "" },
|
||||
body: JSON.stringify({
|
||||
name, host, verify_ssl: verify, preferred_method: "auto",
|
||||
port_rest: 443, port_ssh: 22, port_api: 8728, tags: ""
|
||||
})
|
||||
});
|
||||
await load();
|
||||
} catch (e:any) { setErr(e.message || "error"); }
|
||||
}
|
||||
|
||||
async function addCred() {
|
||||
setErr(null);
|
||||
try {
|
||||
const csrf = getCsrfFromCookie();
|
||||
await apiFetch(`/api/routers/${routerId}/credentials`, {
|
||||
method: "POST",
|
||||
headers: { "X-CSRF-Token": csrf || "" },
|
||||
body: JSON.stringify({ method, username, secret, extra_json: { scheme: "https" } })
|
||||
});
|
||||
setSecret("");
|
||||
await load();
|
||||
} catch (e:any) { setErr(e.message || "error"); }
|
||||
}
|
||||
|
||||
return (
|
||||
<Shell>
|
||||
<div className="text-2xl font-semibold">Admin</div>
|
||||
<div className="text-sm text-zinc-400 mt-1">Routery i poświadczenia (wymaga roli admin)</div>
|
||||
{err && <div className="mt-4 text-sm text-red-400">{err}</div>}
|
||||
|
||||
<div className="mt-6 grid grid-cols-1 xl:grid-cols-2 gap-3">
|
||||
<div className="p-4 rounded-xl border border-zinc-800 bg-zinc-950">
|
||||
<div className="font-medium">Add router</div>
|
||||
<div className="mt-3 space-y-2">
|
||||
<input className="w-full px-3 py-2 rounded-lg bg-zinc-900 border border-zinc-700" value={name} onChange={e=>setName(e.target.value)} placeholder="name" />
|
||||
<input className="w-full px-3 py-2 rounded-lg bg-zinc-900 border border-zinc-700" value={host} onChange={e=>setHost(e.target.value)} placeholder="host/ip" />
|
||||
<label className="text-sm text-zinc-300 flex items-center gap-2">
|
||||
<input type="checkbox" checked={verify} onChange={e=>setVerify(e.target.checked)} />
|
||||
verify SSL
|
||||
</label>
|
||||
<button onClick={createRouter} className="px-3 py-2 rounded-lg bg-zinc-100 text-zinc-900">Create router</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-4 rounded-xl border border-zinc-800 bg-zinc-950">
|
||||
<div className="font-medium">Add credentials</div>
|
||||
<div className="mt-3 space-y-2">
|
||||
<select className="w-full px-3 py-2 rounded-lg bg-zinc-900 border border-zinc-700" value={routerId} onChange={e=>setRouterId(Number(e.target.value))}>
|
||||
{routers.map(r => <option key={r.id} value={r.id}>{r.name}</option>)}
|
||||
</select>
|
||||
<select className="w-full px-3 py-2 rounded-lg bg-zinc-900 border border-zinc-700" value={method} onChange={e=>setMethod(e.target.value)}>
|
||||
<option value="rest">rest</option>
|
||||
<option value="ssh">ssh</option>
|
||||
<option value="api">api</option>
|
||||
</select>
|
||||
<input className="w-full px-3 py-2 rounded-lg bg-zinc-900 border border-zinc-700" value={username} onChange={e=>setUsername(e.target.value)} placeholder="username" />
|
||||
<input className="w-full px-3 py-2 rounded-lg bg-zinc-900 border border-zinc-700" value={secret} onChange={e=>setSecret(e.target.value)} placeholder="password/token" />
|
||||
<button onClick={addCred} className="px-3 py-2 rounded-lg bg-zinc-100 text-zinc-900">Save credentials</button>
|
||||
<div className="text-xs text-zinc-400">REST: ustaw scheme w extra_json (domyślnie https). SSH/API to szkielety.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 p-4 rounded-xl border border-zinc-800 bg-zinc-950">
|
||||
<div className="font-medium">Routers</div>
|
||||
<div className="mt-3 space-y-2 text-sm">
|
||||
{routers.map(r => (
|
||||
<div key={r.id} className="flex items-center justify-between border border-zinc-800 rounded-lg p-3">
|
||||
<div>
|
||||
<div className="font-medium">{r.name}</div>
|
||||
<div className="text-xs text-zinc-400">{r.host} • pref: {r.preferred_method}</div>
|
||||
</div>
|
||||
<div className="text-xs text-zinc-400">id: {r.id}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Shell>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user