132 lines
4.3 KiB
JavaScript
132 lines
4.3 KiB
JavaScript
import { useEffect, useState } from "react";
|
|
import { Server } from "lucide-react";
|
|
import Modal from "../ui/Modal";
|
|
import Field from "../ui/Field";
|
|
import Button from "../ui/Button";
|
|
import { emptyServer } from "../../lib/constants";
|
|
|
|
export default function ServerFormModal({ open, onClose, initial, onSave }) {
|
|
const [form, setForm] = useState(emptyServer);
|
|
const [saving, setSaving] = useState(false);
|
|
const [error, setError] = useState("");
|
|
|
|
const isEditing = Boolean(initial?.id);
|
|
|
|
useEffect(() => {
|
|
if (open) {
|
|
setForm(
|
|
initial
|
|
? {
|
|
name: initial.name || "",
|
|
ipAddress: initial.ipAddress || "",
|
|
description: initial.description || "",
|
|
proxyPort: initial.proxyPort ?? "",
|
|
proxyPath: initial.proxyPath || "",
|
|
}
|
|
: emptyServer,
|
|
);
|
|
setError("");
|
|
}
|
|
}, [open, initial]);
|
|
|
|
async function submit(event) {
|
|
event.preventDefault();
|
|
setSaving(true);
|
|
setError("");
|
|
try {
|
|
await onSave({
|
|
...form,
|
|
proxyPort: form.proxyPort === "" ? null : Number(form.proxyPort),
|
|
proxyPath: form.proxyPath.trim() === "" ? null : form.proxyPath.trim(),
|
|
});
|
|
onClose();
|
|
} catch (err) {
|
|
setError(err.message);
|
|
} finally {
|
|
setSaving(false);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Modal
|
|
open={open}
|
|
onClose={onClose}
|
|
title={isEditing ? "Edit Server" : "Add Server"}
|
|
subtitle={isEditing ? `Editing ${initial.ipAddress}` : "Register a new internal server."}
|
|
icon={Server}
|
|
size="sm"
|
|
footer={
|
|
<>
|
|
<Button variant="ghost" onClick={onClose} disabled={saving}>
|
|
Cancel
|
|
</Button>
|
|
<Button variant="primary" type="submit" form="server-form" loading={saving}>
|
|
{isEditing ? "Save Changes" : "Add Server"}
|
|
</Button>
|
|
</>
|
|
}
|
|
>
|
|
<form id="server-form" className="grid gap-4" onSubmit={submit}>
|
|
<Field label="Server name">
|
|
<input
|
|
className="input"
|
|
required
|
|
value={form.name}
|
|
onChange={(event) => setForm({ ...form, name: event.target.value })}
|
|
placeholder="e.g. Billing Server"
|
|
/>
|
|
</Field>
|
|
<Field label="IP address" hint="or hostname">
|
|
<input
|
|
className="input mono"
|
|
required
|
|
value={form.ipAddress}
|
|
onChange={(event) => setForm({ ...form, ipAddress: event.target.value })}
|
|
placeholder="192.168.1.50"
|
|
/>
|
|
</Field>
|
|
<Field label="Description" hint="optional">
|
|
<textarea
|
|
className="input min-h-20"
|
|
value={form.description}
|
|
onChange={(event) => setForm({ ...form, description: event.target.value })}
|
|
placeholder="What is this server responsible for?"
|
|
/>
|
|
</Field>
|
|
<div className="grid gap-4 sm:grid-cols-2">
|
|
<Field label="Gateway port" hint="optional — common proxy port">
|
|
<input
|
|
className="input mono"
|
|
min="1"
|
|
max="65535"
|
|
type="number"
|
|
value={form.proxyPort}
|
|
onChange={(event) => setForm({ ...form, proxyPort: event.target.value })}
|
|
placeholder="9999"
|
|
/>
|
|
</Field>
|
|
<Field label="Gateway path" hint="optional — common proxy endpoint">
|
|
<input
|
|
className="input mono"
|
|
value={form.proxyPath}
|
|
onChange={(event) => setForm({ ...form, proxyPath: event.target.value })}
|
|
placeholder="/api/nginx/app"
|
|
/>
|
|
</Field>
|
|
</div>
|
|
{form.proxyPort && (
|
|
<p className="rounded-lg border border-line bg-surface-2 px-3 py-2 text-[11px] text-ink-muted">
|
|
All services on this server will proxy to{" "}
|
|
<span className="mono text-ink-soft">
|
|
http://{form.ipAddress || "…"}:{form.proxyPort}
|
|
{form.proxyPath || "/api/nginx/app"}
|
|
</span>{" "}
|
|
with the service domain & port in the body.
|
|
</p>
|
|
)}
|
|
{error && <p className="rounded-lg border border-rose-500/30 bg-rose-500/10 px-3 py-2 text-xs font-medium text-rose-400">{error}</p>}
|
|
</form>
|
|
</Modal>
|
|
);
|
|
}
|