feat(MikrotikConnection): add MikroTik connection testing functionality in ServerModal; update API routes and README for new encryption key and connection endpoint
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 2m13s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 2m13s
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import { IconPlus, IconEdit, IconTrash, IconServer, IconWorld, IconMapPin } from '@tabler/icons-react';
|
||||
import { IconPlus, IconEdit, IconTrash, IconServer, IconWorld, IconMapPin, IconRouter, IconPlug } from '@tabler/icons-react';
|
||||
import api from '../../lib/api.js';
|
||||
import {
|
||||
makeGateway,
|
||||
normalizeGateways,
|
||||
@@ -76,6 +77,8 @@ function ServerModal({ show, mode = 'add', server, onSave, onClose, error: exter
|
||||
const [localServer, setLocalServer] = useState(getEmptyServer());
|
||||
const [customProvider, setCustomProvider] = useState('');
|
||||
const [internalError, setInternalError] = useState('');
|
||||
const [mikrotikTesting, setMikrotikTesting] = useState(false);
|
||||
const [mikrotikTestResult, setMikrotikTestResult] = useState(null);
|
||||
|
||||
const isEditMode = mode === 'edit';
|
||||
const error = externalError || internalError;
|
||||
@@ -119,6 +122,7 @@ function ServerModal({ show, mode = 'add', server, onSave, onClose, error: exter
|
||||
}
|
||||
if (!show) {
|
||||
initializedRef.current = false;
|
||||
setMikrotikTestResult(null);
|
||||
}
|
||||
}, [show, server, isEditMode]);
|
||||
|
||||
@@ -560,6 +564,103 @@ function ServerModal({ show, mode = 'add', server, onSave, onClose, error: exter
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* MikroTik — только для jumphost */}
|
||||
{localServer.type === 'jumphost' && (
|
||||
<div className="mt-4 pt-3 border-top">
|
||||
<label className="form-label d-flex align-items-center gap-2">
|
||||
<IconRouter size={18} />
|
||||
MikroTik RouterOS API
|
||||
</label>
|
||||
<div className="row g-3">
|
||||
<div className="col-md-4">
|
||||
<label className="form-label small">Хост</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control form-control-sm"
|
||||
value={localServer.mikrotikHost || ''}
|
||||
onChange={(e) => handleFieldChange('mikrotikHost', e.target.value)}
|
||||
placeholder={localServer.ip || 'IP или DNS'}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-md-2">
|
||||
<label className="form-label small">Порт</label>
|
||||
<input
|
||||
type="number"
|
||||
className="form-control form-control-sm"
|
||||
value={localServer.mikrotikPort || '8728'}
|
||||
onChange={(e) => handleFieldChange('mikrotikPort', e.target.value)}
|
||||
placeholder="8728"
|
||||
/>
|
||||
</div>
|
||||
<div className="col-md-3">
|
||||
<label className="form-label small">Пользователь</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control form-control-sm"
|
||||
value={localServer.mikrotikUser || 'admin'}
|
||||
onChange={(e) => handleFieldChange('mikrotikUser', e.target.value)}
|
||||
placeholder="admin"
|
||||
/>
|
||||
</div>
|
||||
<div className="col-md-3">
|
||||
<label className="form-label small">Пароль</label>
|
||||
<input
|
||||
type="password"
|
||||
className="form-control form-control-sm"
|
||||
value={localServer.mikrotikPassword ?? ''}
|
||||
onChange={(e) => handleFieldChange('mikrotikPassword', e.target.value)}
|
||||
placeholder={localServer.hasMikrotikPassword ? '••••••' : 'Введите пароль'}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-2 d-flex align-items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-outline-primary btn-sm d-flex align-items-center gap-1"
|
||||
disabled={mikrotikTesting || !(localServer.mikrotikHost || localServer.ip)}
|
||||
onClick={async () => {
|
||||
setMikrotikTestResult(null);
|
||||
setMikrotikTesting(true);
|
||||
try {
|
||||
const hasPassword = localServer.mikrotikPassword && String(localServer.mikrotikPassword).trim() !== '';
|
||||
const payload = hasPassword
|
||||
? {
|
||||
host: localServer.mikrotikHost || localServer.ip,
|
||||
port: localServer.mikrotikPort || 8728,
|
||||
user: localServer.mikrotikUser || 'admin',
|
||||
password: localServer.mikrotikPassword,
|
||||
}
|
||||
: (localServer.hasMikrotikPassword && (localServer.id || localServer.dns || localServer.ip))
|
||||
? { serverId: localServer.id || localServer.dns || localServer.ip }
|
||||
: null;
|
||||
if (!payload) {
|
||||
setMikrotikTestResult({ ok: false, message: 'Введите пароль или сохраните сервер с паролем' });
|
||||
return;
|
||||
}
|
||||
const { data } = await api.post('/mikrotik/test-connection', payload);
|
||||
setMikrotikTestResult(data);
|
||||
} catch (err) {
|
||||
setMikrotikTestResult({
|
||||
ok: false,
|
||||
message: err.response?.data?.message || err.message || 'Ошибка соединения',
|
||||
});
|
||||
} finally {
|
||||
setMikrotikTesting(false);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<IconPlug size={14} />
|
||||
{mikrotikTesting ? 'Проверка...' : 'Проверить соединение'}
|
||||
</button>
|
||||
{mikrotikTestResult && (
|
||||
<span className={mikrotikTestResult.ok ? 'text-success' : 'text-danger'}>
|
||||
{mikrotikTestResult.ok ? '✓ ' + (mikrotikTestResult.message || 'OK') : '✗ ' + mikrotikTestResult.message}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="modal-footer">
|
||||
|
||||
Reference in New Issue
Block a user