feat(NetworkConfigManager, SettingsModal): add global PTR zone settings management, allowing users to define DNS replacements in both global and interface-specific contexts for enhanced configuration flexibility
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m47s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m47s
This commit is contained in:
@@ -232,13 +232,30 @@ function NetworkConfigManager() {
|
|||||||
const [mikrotikCodeModalOpen, setMikrotikCodeModalOpen] = useState(false);
|
const [mikrotikCodeModalOpen, setMikrotikCodeModalOpen] = useState(false);
|
||||||
const [generatedMikrotikCode, setGeneratedMikrotikCode] = useState([]);
|
const [generatedMikrotikCode, setGeneratedMikrotikCode] = useState([]);
|
||||||
|
|
||||||
|
// === Глобальные настройки PTR зоны ===
|
||||||
|
const [globalPtrZoneReplaceFrom, setGlobalPtrZoneReplaceFrom] = useState('');
|
||||||
|
const [globalPtrZoneReplaceTo, setGlobalPtrZoneReplaceTo] = useState('');
|
||||||
|
|
||||||
// === Загрузка данных ===
|
// === Загрузка данных ===
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchConfig();
|
fetchConfig();
|
||||||
fetchServers();
|
fetchServers();
|
||||||
fetchIpsecPasswords();
|
fetchIpsecPasswords();
|
||||||
|
fetchUiSettings();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const fetchUiSettings = async () => {
|
||||||
|
try {
|
||||||
|
const response = await api.get('/ui-settings');
|
||||||
|
const data = response.data || {};
|
||||||
|
setGlobalPtrZoneReplaceFrom(String(data?.ptrZoneReplaceFrom || ''));
|
||||||
|
setGlobalPtrZoneReplaceTo(String(data?.ptrZoneReplaceTo || ''));
|
||||||
|
} catch (error) {
|
||||||
|
// Мягко игнорируем ошибку, используем значения по умолчанию
|
||||||
|
console.warn('Не удалось загрузить глобальные настройки PTR зоны:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const fetchConfig = async () => {
|
const fetchConfig = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
@@ -1494,14 +1511,20 @@ function NetworkConfigManager() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Функция для генерации PTR зоны с учетом замены
|
// Функция для генерации PTR зоны с учетом замены
|
||||||
|
// Приоритет: настройки интерфейса > глобальные настройки
|
||||||
const generatePtrZone = (serverDns, iface) => {
|
const generatePtrZone = (serverDns, iface) => {
|
||||||
if (!serverDns) return '—';
|
if (!serverDns) return '—';
|
||||||
|
|
||||||
// Если указана замена PTR зоны, применяем её
|
// Сначала проверяем настройки интерфейса
|
||||||
if (iface?.ptrZoneReplaceFrom && iface?.ptrZoneReplaceTo) {
|
if (iface?.ptrZoneReplaceFrom && iface?.ptrZoneReplaceTo) {
|
||||||
return serverDns.replace(iface.ptrZoneReplaceFrom, iface.ptrZoneReplaceTo);
|
return serverDns.replace(iface.ptrZoneReplaceFrom, iface.ptrZoneReplaceTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Если настройки интерфейса не заданы, используем глобальные настройки
|
||||||
|
if (globalPtrZoneReplaceFrom && globalPtrZoneReplaceTo) {
|
||||||
|
return serverDns.replace(globalPtrZoneReplaceFrom, globalPtrZoneReplaceTo);
|
||||||
|
}
|
||||||
|
|
||||||
return serverDns;
|
return serverDns;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1560,7 +1583,7 @@ function NetworkConfigManager() {
|
|||||||
}
|
}
|
||||||
return a.type.localeCompare(b.type);
|
return a.type.localeCompare(b.type);
|
||||||
});
|
});
|
||||||
}, [config.tunnelInterfaces, servers]);
|
}, [config.tunnelInterfaces, servers, globalPtrZoneReplaceFrom, globalPtrZoneReplaceTo]);
|
||||||
|
|
||||||
// === Фильтрация реестра IP ===
|
// === Фильтрация реестра IP ===
|
||||||
const filteredIpRegistry = useMemo(() => {
|
const filteredIpRegistry = useMemo(() => {
|
||||||
@@ -4254,10 +4277,24 @@ function NetworkConfigManager() {
|
|||||||
) : null;
|
) : null;
|
||||||
})()}
|
})()}
|
||||||
|
|
||||||
{/* Поля для замены PTR зоны */}
|
{/* Поля для замены PTR зоны (опционально, переопределяют глобальные настройки) */}
|
||||||
<div className="col-12">
|
<div className="col-12">
|
||||||
<hr />
|
<hr />
|
||||||
<h6 className="mb-3">Настройка PTR зоны</h6>
|
<h6 className="mb-3">Настройка PTR зоны (опционально)</h6>
|
||||||
|
<div className="alert alert-info alert-dismissible mb-3" role="alert">
|
||||||
|
<div className="small">
|
||||||
|
{globalPtrZoneReplaceFrom && globalPtrZoneReplaceTo ? (
|
||||||
|
<>
|
||||||
|
<strong>Глобальные настройки:</strong> "{globalPtrZoneReplaceFrom}" → "{globalPtrZoneReplaceTo}"<br />
|
||||||
|
Оставьте поля пустыми, чтобы использовать глобальные настройки, или укажите свои значения для переопределения.
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
Глобальные настройки PTR зоны не заданы. Укажите значения здесь или в разделе "Настройки интерфейса".
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className="row g-3">
|
<div className="row g-3">
|
||||||
<div className="col-md-6">
|
<div className="col-md-6">
|
||||||
<FormField
|
<FormField
|
||||||
@@ -4265,10 +4302,10 @@ function NetworkConfigManager() {
|
|||||||
name="ptrZoneReplaceFrom"
|
name="ptrZoneReplaceFrom"
|
||||||
value={editingInterface.ptrZoneReplaceFrom || ''}
|
value={editingInterface.ptrZoneReplaceFrom || ''}
|
||||||
onChange={(val) => setEditingInterface({ ...editingInterface, ptrZoneReplaceFrom: val })}
|
onChange={(val) => setEditingInterface({ ...editingInterface, ptrZoneReplaceFrom: val })}
|
||||||
placeholder="rt.shx"
|
placeholder={globalPtrZoneReplaceFrom || "rt.shx"}
|
||||||
/>
|
/>
|
||||||
<div className="form-text">
|
<div className="form-text">
|
||||||
Часть DNS домена, которую нужно заменить (например, "rt.shx")
|
Часть DNS домена, которую нужно заменить (например, "rt.shx"). Оставьте пустым для использования глобальных настроек.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-md-6">
|
<div className="col-md-6">
|
||||||
@@ -4277,13 +4314,13 @@ function NetworkConfigManager() {
|
|||||||
name="ptrZoneReplaceTo"
|
name="ptrZoneReplaceTo"
|
||||||
value={editingInterface.ptrZoneReplaceTo || ''}
|
value={editingInterface.ptrZoneReplaceTo || ''}
|
||||||
onChange={(val) => setEditingInterface({ ...editingInterface, ptrZoneReplaceTo: val })}
|
onChange={(val) => setEditingInterface({ ...editingInterface, ptrZoneReplaceTo: val })}
|
||||||
placeholder="shrt"
|
placeholder={globalPtrZoneReplaceTo || "shrt"}
|
||||||
/>
|
/>
|
||||||
<div className="form-text">
|
<div className="form-text">
|
||||||
На что заменить (например, "shrt"). Результат: DNS "selectel.msk.rt.shx.su" → PTR "selectel.msk.shrt.su"
|
На что заменить (например, "shrt"). Оставьте пустым для использования глобальных настроек.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{editingInterface.serverId && editingInterface.ptrZoneReplaceFrom && editingInterface.ptrZoneReplaceTo && (() => {
|
{editingInterface.serverId && (editingInterface.ptrZoneReplaceFrom && editingInterface.ptrZoneReplaceTo || globalPtrZoneReplaceFrom && globalPtrZoneReplaceTo) && (() => {
|
||||||
const server = getServerInfo(editingInterface.serverId);
|
const server = getServerInfo(editingInterface.serverId);
|
||||||
const originalDns = server?.dns || '';
|
const originalDns = server?.dns || '';
|
||||||
const ptrZone = generatePtrZone(originalDns, editingInterface);
|
const ptrZone = generatePtrZone(originalDns, editingInterface);
|
||||||
@@ -4298,6 +4335,15 @@ function NetworkConfigManager() {
|
|||||||
<div>
|
<div>
|
||||||
<strong>PTR зона:</strong> <code className="text-info">{ptrZone}</code>
|
<strong>PTR зона:</strong> <code className="text-info">{ptrZone}</code>
|
||||||
</div>
|
</div>
|
||||||
|
{editingInterface.ptrZoneReplaceFrom && editingInterface.ptrZoneReplaceTo ? (
|
||||||
|
<div className="text-muted mt-1" style={{ fontSize: '0.75rem' }}>
|
||||||
|
Используются настройки интерфейса
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="text-muted mt-1" style={{ fontSize: '0.75rem' }}>
|
||||||
|
Используются глобальные настройки
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ export default function SettingsModal({ open, onClose }) {
|
|||||||
const [dohServer, setDohServer] = useState('');
|
const [dohServer, setDohServer] = useState('');
|
||||||
const [wsUrl, setWsUrl] = useState('');
|
const [wsUrl, setWsUrl] = useState('');
|
||||||
const [baseAS, setBaseAS] = useState('65001');
|
const [baseAS, setBaseAS] = useState('65001');
|
||||||
|
const [ptrZoneReplaceFrom, setPtrZoneReplaceFrom] = useState('');
|
||||||
|
const [ptrZoneReplaceTo, setPtrZoneReplaceTo] = useState('');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!open) return;
|
if (!open) return;
|
||||||
@@ -31,6 +33,8 @@ export default function SettingsModal({ open, onClose }) {
|
|||||||
setDohServer(String(data?.dohServer || ''));
|
setDohServer(String(data?.dohServer || ''));
|
||||||
setWsUrl(String(data?.wsUpdateUrl || ''));
|
setWsUrl(String(data?.wsUpdateUrl || ''));
|
||||||
setBaseAS(String(data?.baseAS || '65001'));
|
setBaseAS(String(data?.baseAS || '65001'));
|
||||||
|
setPtrZoneReplaceFrom(String(data?.ptrZoneReplaceFrom || ''));
|
||||||
|
setPtrZoneReplaceTo(String(data?.ptrZoneReplaceTo || ''));
|
||||||
const e = res?.headers?.etag || res?.headers?.ETag || '';
|
const e = res?.headers?.etag || res?.headers?.ETag || '';
|
||||||
setEtag(e ? String(e) : '');
|
setEtag(e ? String(e) : '');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -90,7 +94,9 @@ export default function SettingsModal({ open, onClose }) {
|
|||||||
settings: {
|
settings: {
|
||||||
dohServer: String(dohServer || '').trim(),
|
dohServer: String(dohServer || '').trim(),
|
||||||
wsUpdateUrl: String(wsUrl || '').trim(),
|
wsUpdateUrl: String(wsUrl || '').trim(),
|
||||||
baseAS: String(baseAS || '65001').trim()
|
baseAS: String(baseAS || '65001').trim(),
|
||||||
|
ptrZoneReplaceFrom: String(ptrZoneReplaceFrom || '').trim(),
|
||||||
|
ptrZoneReplaceTo: String(ptrZoneReplaceTo || '').trim()
|
||||||
},
|
},
|
||||||
etag
|
etag
|
||||||
};
|
};
|
||||||
@@ -186,6 +192,36 @@ export default function SettingsModal({ open, onClose }) {
|
|||||||
helpText="AS по умолчанию для community (например, 65001). Используется при поиске и нормализации community."
|
helpText="AS по умолчанию для community (например, 65001). Используется при поиске и нормализации community."
|
||||||
disabled={loading || saving}
|
disabled={loading || saving}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div className="mt-4 pt-3 border-top">
|
||||||
|
<h6 className="mb-3">Настройка PTR зоны</h6>
|
||||||
|
<div className="row g-3">
|
||||||
|
<div className="col-md-6">
|
||||||
|
<FormField
|
||||||
|
label="Заменить в DNS домене"
|
||||||
|
name="ptrZoneReplaceFrom"
|
||||||
|
type="text"
|
||||||
|
value={ptrZoneReplaceFrom}
|
||||||
|
onChange={setPtrZoneReplaceFrom}
|
||||||
|
placeholder="rt.shx"
|
||||||
|
helpText='Часть DNS домена, которую нужно заменить (например, "rt.shx")'
|
||||||
|
disabled={loading || saving}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="col-md-6">
|
||||||
|
<FormField
|
||||||
|
label="Заменить на"
|
||||||
|
name="ptrZoneReplaceTo"
|
||||||
|
type="text"
|
||||||
|
value={ptrZoneReplaceTo}
|
||||||
|
onChange={setPtrZoneReplaceTo}
|
||||||
|
placeholder="shrt"
|
||||||
|
helpText='На что заменить (например, "shrt"). Результат: DNS "selectel.msk.rt.shx.su" → PTR "selectel.msk.shrt.su"'
|
||||||
|
disabled={loading || saving}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="modal-footer">
|
<div className="modal-footer">
|
||||||
|
|||||||
Reference in New Issue
Block a user