import React, { useEffect, useState } from "react"; import { apiFetch } from "../api"; import { useAuth } from "../state"; const TEMPLATE_VARIABLES = [ "target_name", "target_id", "alert_name", "severity", "category", "description", "message", "value", "warning_threshold", "alert_threshold", "checked_at", "alert_key", ]; export function AdminUsersPage() { const { tokens, refresh, me } = useAuth(); const emptyCreateForm = { email: "", first_name: "", last_name: "", password: "", role: "viewer" }; const [users, setUsers] = useState([]); const [form, setForm] = useState(emptyCreateForm); const [editingUserId, setEditingUserId] = useState(null); const [editForm, setEditForm] = useState({ email: "", first_name: "", last_name: "", password: "", role: "viewer" }); const [emailSettings, setEmailSettings] = useState({ enabled: false, smtp_host: "", smtp_port: 587, smtp_username: "", smtp_password: "", clear_smtp_password: false, from_name: "", from_email: "", use_starttls: true, use_ssl: false, warning_subject_template: "", alert_subject_template: "", warning_body_template: "", alert_body_template: "", }); const [smtpState, setSmtpState] = useState({ has_password: false, updated_at: null }); const [testRecipient, setTestRecipient] = useState(""); const [smtpInfo, setSmtpInfo] = useState(""); const [error, setError] = useState(""); const load = async () => { const [userRows, smtp] = await Promise.all([ apiFetch("/admin/users", {}, tokens, refresh), apiFetch("/admin/settings/email", {}, tokens, refresh), ]); setUsers(userRows); setEmailSettings((prev) => ({ ...prev, enabled: !!smtp.enabled, smtp_host: smtp.smtp_host || "", smtp_port: smtp.smtp_port || 587, smtp_username: smtp.smtp_username || "", smtp_password: "", clear_smtp_password: false, from_name: smtp.from_name || "", from_email: smtp.from_email || "", use_starttls: !!smtp.use_starttls, use_ssl: !!smtp.use_ssl, warning_subject_template: smtp.warning_subject_template || "", alert_subject_template: smtp.alert_subject_template || "", warning_body_template: smtp.warning_body_template || "", alert_body_template: smtp.alert_body_template || "", })); setSmtpState({ has_password: !!smtp.has_password, updated_at: smtp.updated_at }); setTestRecipient(smtp.from_email || ""); }; useEffect(() => { if (me?.role === "admin") load().catch((e) => setError(String(e.message || e))); }, [me]); if (me?.role !== "admin") return
Admins only.
; const create = async (e) => { e.preventDefault(); try { await apiFetch("/admin/users", { method: "POST", body: JSON.stringify(form) }, tokens, refresh); setForm(emptyCreateForm); await load(); } catch (e) { setError(String(e.message || e)); } }; const remove = async (id) => { try { await apiFetch(`/admin/users/${id}`, { method: "DELETE" }, tokens, refresh); await load(); } catch (e) { setError(String(e.message || e)); } }; const startEdit = (user) => { setEditingUserId(user.id); setEditForm({ email: user.email || "", first_name: user.first_name || "", last_name: user.last_name || "", password: "", role: user.role || "viewer", }); }; const cancelEdit = () => { setEditingUserId(null); setEditForm({ email: "", first_name: "", last_name: "", password: "", role: "viewer" }); }; const saveEdit = async (userId) => { try { const payload = { email: editForm.email, first_name: editForm.first_name.trim() || null, last_name: editForm.last_name.trim() || null, role: editForm.role, }; if (editForm.password.trim()) payload.password = editForm.password; await apiFetch(`/admin/users/${userId}`, { method: "PUT", body: JSON.stringify(payload) }, tokens, refresh); cancelEdit(); await load(); } catch (e) { setError(String(e.message || e)); } }; const saveSmtp = async (e) => { e.preventDefault(); setError(""); setSmtpInfo(""); try { const payload = { ...emailSettings, smtp_host: emailSettings.smtp_host.trim() || null, smtp_username: emailSettings.smtp_username.trim() || null, from_name: emailSettings.from_name.trim() || null, from_email: emailSettings.from_email.trim() || null, smtp_password: emailSettings.smtp_password || null, warning_subject_template: emailSettings.warning_subject_template.trim() || null, alert_subject_template: emailSettings.alert_subject_template.trim() || null, warning_body_template: emailSettings.warning_body_template.trim() || null, alert_body_template: emailSettings.alert_body_template.trim() || null, }; await apiFetch("/admin/settings/email", { method: "PUT", body: JSON.stringify(payload) }, tokens, refresh); setSmtpInfo("SMTP settings saved."); await load(); } catch (e) { setError(String(e.message || e)); } }; const sendTestMail = async () => { setError(""); setSmtpInfo(""); try { const recipient = testRecipient.trim(); if (!recipient) { throw new Error("Please enter a test recipient email."); } await apiFetch( "/admin/settings/email/test", { method: "POST", body: JSON.stringify({ recipient }) }, tokens, refresh ); setSmtpInfo(`Test email sent to ${recipient}.`); } catch (e) { setError(String(e.message || e)); } }; const protocolMode = emailSettings.use_ssl ? "ssl" : emailSettings.use_starttls ? "starttls" : "plain"; const setProtocolMode = (mode) => { if (mode === "ssl") { setEmailSettings({ ...emailSettings, use_ssl: true, use_starttls: false }); return; } if (mode === "starttls") { setEmailSettings({ ...emailSettings, use_ssl: false, use_starttls: true }); return; } setEmailSettings({ ...emailSettings, use_ssl: false, use_starttls: false }); }; return (

Admin Settings

Manage users and outgoing notifications for this NexaPG instance.

{error &&
{error}
}

User Management

Create accounts and manage access roles.

setForm({ ...form, first_name: e.target.value })} />
setForm({ ...form, last_name: e.target.value })} />
setForm({ ...form, email: e.target.value })} />
setForm({ ...form, password: e.target.value })} />
{users.map((u) => ( ))}
ID Name Email Role Action
{u.id} {editingUserId === u.id ? (
setEditForm({ ...editForm, first_name: e.target.value })} /> setEditForm({ ...editForm, last_name: e.target.value })} />
) : ( {[u.first_name, u.last_name].filter(Boolean).join(" ") || "-"} )}
{editingUserId === u.id ? ( setEditForm({ ...editForm, email: e.target.value })} /> ) : ( u.email )} {editingUserId === u.id ? ( ) : ( {u.role} )} {editingUserId === u.id && ( setEditForm({ ...editForm, password: e.target.value })} /> )} {editingUserId === u.id ? ( <> ) : ( )} {u.id !== me.id && ( )}

Alert Email Notifications (SMTP)

Configure send-only SMTP for warning and alert notifications.

{smtpInfo &&
{smtpInfo}
}

SMTP Settings

setEmailSettings({ ...emailSettings, smtp_host: e.target.value })} />
setEmailSettings({ ...emailSettings, smtp_port: Number(e.target.value || 587) })} />
setEmailSettings({ ...emailSettings, smtp_username: e.target.value })} />
setEmailSettings({ ...emailSettings, smtp_password: e.target.value, clear_smtp_password: false })} />
setEmailSettings({ ...emailSettings, from_name: e.target.value })} />
setEmailSettings({ ...emailSettings, from_email: e.target.value })} />
Select exactly one mode to avoid STARTTLS/SSL conflicts.

Template Settings

If a template field is left empty, NexaPG automatically uses the built-in default template.

{TEMPLATE_VARIABLES.map((item) => ( {"{" + item + "}"} ))}
setEmailSettings({ ...emailSettings, warning_subject_template: e.target.value })} />
setEmailSettings({ ...emailSettings, alert_subject_template: e.target.value })} />