Add test connection feature for database targets

This commit introduces a new endpoint to test database connection. The frontend now includes a button to test the connection before creating a target, with real-time feedback on success or failure. Related styles and components were updated for better user experience.
This commit is contained in:
2026-02-12 11:56:32 +01:00
parent 2f5529a93a
commit 3e025bcf1b
4 changed files with 117 additions and 5 deletions

View File

@@ -20,6 +20,7 @@ export function TargetsPage() {
const [form, setForm] = useState(emptyForm);
const [error, setError] = useState("");
const [loading, setLoading] = useState(true);
const [testState, setTestState] = useState({ loading: false, message: "", ok: null });
const canManage = me?.role === "admin" || me?.role === "operator";
@@ -50,6 +51,31 @@ export function TargetsPage() {
}
};
const testConnection = async () => {
setTestState({ loading: true, message: "", ok: null });
try {
const result = await apiFetch(
"/targets/test-connection",
{
method: "POST",
body: JSON.stringify({
host: form.host,
port: form.port,
dbname: form.dbname,
username: form.username,
password: form.password,
sslmode: form.sslmode,
}),
},
tokens,
refresh
);
setTestState({ loading: false, message: `${result.message} (PostgreSQL ${result.server_version})`, ok: true });
} catch (e) {
setTestState({ loading: false, message: String(e.message || e), ok: false });
}
};
const deleteTarget = async (id) => {
if (!confirm("Delete target?")) return;
try {
@@ -66,7 +92,7 @@ export function TargetsPage() {
{error && <div className="card error">{error}</div>}
{canManage && (
<details className="card collapsible" open>
<details className="card collapsible">
<summary className="collapse-head">
<div>
<h3>New Target</h3>
@@ -111,9 +137,17 @@ export function TargetsPage() {
</small>
</div>
<div className="field submit-field">
<button className="primary-btn">Create target</button>
<div className="target-actions">
<button type="button" className="secondary-btn" onClick={testConnection} disabled={testState.loading}>
{testState.loading ? "Testing..." : "Test connection"}
</button>
<button className="primary-btn">Create target</button>
</div>
</div>
</form>
{testState.message && (
<div className={`test-connection-result ${testState.ok ? "ok" : "fail"}`}>{testState.message}</div>
)}
</details>
)}