Require node names and preserve disk monitoring defaults
This commit is contained in:
parent
a279e6720e
commit
cb37d0de01
1 changed files with 59 additions and 7 deletions
|
|
@ -26,6 +26,25 @@ interface NodeModalProps {
|
||||||
securityStatus?: Partial<SecurityStatus>;
|
securityStatus?: Partial<SecurityStatus>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const deriveNameFromHost = (host: string): string => {
|
||||||
|
let value = host.trim();
|
||||||
|
if (!value) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const url = value.includes('://') ? new URL(value) : new URL(`https://${value}`);
|
||||||
|
value = url.hostname || value;
|
||||||
|
} catch {
|
||||||
|
value = value.replace(/^https?:\/\//, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
value = value.replace(/\/.*$/, '').replace(/^\[(.*)\]$/, '$1');
|
||||||
|
value = value.replace(/\s+/g, '-');
|
||||||
|
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
export const NodeModal: Component<NodeModalProps> = (props) => {
|
export const NodeModal: Component<NodeModalProps> = (props) => {
|
||||||
const [testResult, setTestResult] = createSignal<{
|
const [testResult, setTestResult] = createSignal<{
|
||||||
status: string;
|
status: string;
|
||||||
|
|
@ -136,7 +155,7 @@ export const NodeModal: Component<NodeModalProps> = (props) => {
|
||||||
verifySSL: node.verifySSL ?? true,
|
verifySSL: node.verifySSL ?? true,
|
||||||
monitorPhysicalDisks:
|
monitorPhysicalDisks:
|
||||||
node.type === 'pve'
|
node.type === 'pve'
|
||||||
? (node as NodeConfig & { monitorPhysicalDisks?: boolean }).monitorPhysicalDisks ?? false
|
? (node as NodeConfig & { monitorPhysicalDisks?: boolean }).monitorPhysicalDisks ?? true
|
||||||
: false,
|
: false,
|
||||||
monitorMailStats: pmgConfig?.monitorMailStats ?? true,
|
monitorMailStats: pmgConfig?.monitorMailStats ?? true,
|
||||||
monitorQueues: pmgConfig?.monitorQueues ?? true,
|
monitorQueues: pmgConfig?.monitorQueues ?? true,
|
||||||
|
|
@ -150,10 +169,20 @@ export const NodeModal: Component<NodeModalProps> = (props) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const data = formData();
|
const data = formData();
|
||||||
|
|
||||||
|
const normalizedName = data.name.trim() || deriveNameFromHost(data.host);
|
||||||
|
if (!normalizedName) {
|
||||||
|
showError('Node name is required');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalizedName !== data.name) {
|
||||||
|
setFormData((prev) => ({ ...prev, name: normalizedName }));
|
||||||
|
}
|
||||||
|
|
||||||
// Prepare data based on auth type
|
// Prepare data based on auth type
|
||||||
const nodeData: Partial<NodeConfig> = {
|
const nodeData: Partial<NodeConfig> = {
|
||||||
type: props.nodeType,
|
type: props.nodeType,
|
||||||
name: data.name || '', // Will be auto-generated by backend if empty
|
name: normalizedName,
|
||||||
host: data.host,
|
host: data.host,
|
||||||
fingerprint: data.fingerprint,
|
fingerprint: data.fingerprint,
|
||||||
verifySSL: data.verifySSL,
|
verifySSL: data.verifySSL,
|
||||||
|
|
@ -202,10 +231,24 @@ export const NodeModal: Component<NodeModalProps> = (props) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateField = (field: string, value: string | boolean) => {
|
const updateField = (field: string, value: string | boolean) => {
|
||||||
setFormData((prev) => ({ ...prev, [field]: value }));
|
if (field === 'host' && typeof value === 'string') {
|
||||||
if (field === 'host') {
|
setFormData((prev) => {
|
||||||
|
const next = { ...prev, host: value };
|
||||||
|
const derivedName = deriveNameFromHost(value);
|
||||||
|
const previousDerivedName = deriveNameFromHost(prev.host || '');
|
||||||
|
const shouldAutoUpdate =
|
||||||
|
!prev.name.trim() || (previousDerivedName && prev.name === previousDerivedName);
|
||||||
|
if (derivedName && shouldAutoUpdate) {
|
||||||
|
next.name = derivedName;
|
||||||
|
}
|
||||||
|
return next;
|
||||||
|
});
|
||||||
setQuickSetupCommand('');
|
setQuickSetupCommand('');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setFormData((prev) => ({ ...prev, [field]: value }));
|
||||||
|
|
||||||
if (field === 'setupMode' && value !== 'auto') {
|
if (field === 'setupMode' && value !== 'auto') {
|
||||||
setQuickSetupCommand('');
|
setQuickSetupCommand('');
|
||||||
}
|
}
|
||||||
|
|
@ -213,6 +256,11 @@ export const NodeModal: Component<NodeModalProps> = (props) => {
|
||||||
|
|
||||||
const handleTestConnection = async () => {
|
const handleTestConnection = async () => {
|
||||||
const data = formData();
|
const data = formData();
|
||||||
|
const normalizedName = data.name.trim() || deriveNameFromHost(data.host);
|
||||||
|
|
||||||
|
if (!data.name.trim() && normalizedName) {
|
||||||
|
setFormData((prev) => ({ ...prev, name: normalizedName }));
|
||||||
|
}
|
||||||
|
|
||||||
// If editing an existing node and no new credentials provided, use stored credentials
|
// If editing an existing node and no new credentials provided, use stored credentials
|
||||||
if (props.editingNode) {
|
if (props.editingNode) {
|
||||||
|
|
@ -267,7 +315,7 @@ export const NodeModal: Component<NodeModalProps> = (props) => {
|
||||||
// Prepare test data
|
// Prepare test data
|
||||||
const testData: Partial<NodeConfig> = {
|
const testData: Partial<NodeConfig> = {
|
||||||
type: props.nodeType,
|
type: props.nodeType,
|
||||||
name: data.name || '', // Will be auto-generated by backend if empty
|
name: normalizedName || '',
|
||||||
host: data.host,
|
host: data.host,
|
||||||
fingerprint: data.fingerprint,
|
fingerprint: data.fingerprint,
|
||||||
verifySSL: data.verifySSL,
|
verifySSL: data.verifySSL,
|
||||||
|
|
@ -369,15 +417,19 @@ export const NodeModal: Component<NodeModalProps> = (props) => {
|
||||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||||
<div class={formField}>
|
<div class={formField}>
|
||||||
<label class={labelClass('flex items-center gap-2')}>
|
<label class={labelClass('flex items-center gap-2')}>
|
||||||
Node Name <span class="text-xs text-gray-500">(optional)</span>
|
Node Name <span class="text-red-500">*</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={formData().name}
|
value={formData().name}
|
||||||
onInput={(e) => updateField('name', e.currentTarget.value)}
|
onInput={(e) => updateField('name', e.currentTarget.value)}
|
||||||
placeholder="Will auto-detect from hostname"
|
placeholder="Pulse uses this label across dashboards"
|
||||||
|
required
|
||||||
class={controlClass()}
|
class={controlClass()}
|
||||||
/>
|
/>
|
||||||
|
<p class={formHelpText}>
|
||||||
|
Required and must be unique. We can auto-fill it from the Host URL if you leave it blank.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class={formField}>
|
<div class={formField}>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue