mirror of
https://github.com/andrey271192/domen_hydra.git
synced 2026-09-20 14:42:00 +00:00
routers: add optional SSH port
Add an optional SSH port field in the routers UI and use it for push_all SSH connections. Made-with: Cursor
This commit is contained in:
@@ -65,6 +65,13 @@ async def _ssh_on_router(rcfg: dict, remote_cmd: str, timeout: int = 45) -> tupl
|
|||||||
return 1, "", "нет IP и нет тоннеля (добавь IP или настрой тоннель)"
|
return 1, "", "нет IP и нет тоннеля (добавь IP или настрой тоннель)"
|
||||||
ssh_host = ip
|
ssh_host = ip
|
||||||
extra_args = []
|
extra_args = []
|
||||||
|
# Optional direct SSH port (default: 22). Tunnel uses its own port args above.
|
||||||
|
try:
|
||||||
|
p = int(rcfg.get("ssh_port") or 22)
|
||||||
|
if p and p != 22:
|
||||||
|
extra_args = ["-p", str(p)]
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
user = rcfg.get("user") or config.SSH_USER
|
user = rcfg.get("user") or config.SSH_USER
|
||||||
pwd = rcfg.get("password") or config.SSH_PASS
|
pwd = rcfg.get("password") or config.SSH_PASS
|
||||||
try:
|
try:
|
||||||
@@ -103,8 +110,16 @@ async def _push_one_router(server_url: str, router_key: str, rcfg: dict) -> dict
|
|||||||
"-o", "StrictHostKeyChecking=no", "-o", "ConnectTimeout=10",
|
"-o", "StrictHostKeyChecking=no", "-o", "ConnectTimeout=10",
|
||||||
"-p", str(int(tunnel_port)), f"{user}@127.0.0.1", cmd]
|
"-p", str(int(tunnel_port)), f"{user}@127.0.0.1", cmd]
|
||||||
else:
|
else:
|
||||||
|
port_args: list[str] = []
|
||||||
|
try:
|
||||||
|
p = int(rcfg.get("ssh_port") or 22)
|
||||||
|
if p and p != 22:
|
||||||
|
port_args = ["-p", str(p)]
|
||||||
|
except Exception:
|
||||||
|
port_args = []
|
||||||
ssh_cmd = ["sshpass", "-p", pwd, "ssh",
|
ssh_cmd = ["sshpass", "-p", pwd, "ssh",
|
||||||
"-o", "StrictHostKeyChecking=no", "-o", "ConnectTimeout=10",
|
"-o", "StrictHostKeyChecking=no", "-o", "ConnectTimeout=10",
|
||||||
|
*port_args,
|
||||||
f"{user}@{ip}", cmd]
|
f"{user}@{ip}", cmd]
|
||||||
try:
|
try:
|
||||||
r = await asyncio.to_thread(subprocess.run, ssh_cmd, capture_output=True, text=True, timeout=60)
|
r = await asyncio.to_thread(subprocess.run, ssh_cmd, capture_output=True, text=True, timeout=60)
|
||||||
|
|||||||
@@ -207,6 +207,7 @@ input[type=text]:focus,input[type=password]:focus{border-color:var(--accent)}
|
|||||||
<input type="text" id="r-name" placeholder="Имя (andrey)" style="width:130px">
|
<input type="text" id="r-name" placeholder="Имя (andrey)" style="width:130px">
|
||||||
<input type="text" id="r-ip" placeholder="IP или hostname SSH" style="min-width:160px;flex:1;max-width:240px">
|
<input type="text" id="r-ip" placeholder="IP или hostname SSH" style="min-width:160px;flex:1;max-width:240px">
|
||||||
<input type="text" id="r-user" value="root" style="width:80px">
|
<input type="text" id="r-user" value="root" style="width:80px">
|
||||||
|
<input type="text" id="r-port" placeholder="SSH порт (опц.)" inputmode="numeric" style="width:120px">
|
||||||
<input type="password" id="r-pass" placeholder="SSH пароль" style="width:130px">
|
<input type="password" id="r-pass" placeholder="SSH пароль" style="width:130px">
|
||||||
<button class="btn btn-b" onclick="addRouter()">+ Добавить</button>
|
<button class="btn btn-b" onclick="addRouter()">+ Добавить</button>
|
||||||
<button class="btn" style="background:var(--orange);color:#000" onclick="addAndTunnel()" title="Добавить роутер без IP и сразу открыть настройку тоннеля">⇄ Тоннель</button>
|
<button class="btn" style="background:var(--orange);color:#000" onclick="addAndTunnel()" title="Добавить роутер без IP и сразу открыть настройку тоннеля">⇄ Тоннель</button>
|
||||||
@@ -430,10 +431,17 @@ async function loadRouters(){
|
|||||||
const R=await(await fetch('/api/routers')).json();
|
const R=await(await fetch('/api/routers')).json();
|
||||||
const el=document.getElementById('routers-list');
|
const el=document.getElementById('routers-list');
|
||||||
if(!Object.keys(R).length){ el.innerHTML='<span style="color:var(--muted)">Роутеры не добавлены</span>'; return; }
|
if(!Object.keys(R).length){ el.innerHTML='<span style="color:var(--muted)">Роутеры не добавлены</span>'; return; }
|
||||||
|
const _hostWithPort = (c) => {
|
||||||
|
const host=(c.ip||'').toString().trim();
|
||||||
|
const p=(c.ssh_port!=null && c.ssh_port!=='' && !Number.isNaN(Number(c.ssh_port))) ? Number(c.ssh_port) : null;
|
||||||
|
if(!host) return '';
|
||||||
|
if(!p) return host;
|
||||||
|
return `${host}:${p}`;
|
||||||
|
};
|
||||||
el.innerHTML=Object.entries(R).map(([n,c])=>{
|
el.innerHTML=Object.entries(R).map(([n,c])=>{
|
||||||
const enc=encodeURIComponent(n);
|
const enc=encodeURIComponent(n);
|
||||||
return `<div class="ri" style="align-items:flex-start;gap:10px">
|
return `<div class="ri" style="align-items:flex-start;gap:10px">
|
||||||
<div style="flex:1;min-width:0"><span class="n">${_escHtml(n)}</span> <span style="font-size:12px">${c.tunnel_port?`<span style="color:var(--green)">⇄ тоннель :${c.tunnel_port}</span>`:`<span style="color:var(--muted)">— ${_escHtml(c.ip||'—')}</span>`}</span></div>
|
<div style="flex:1;min-width:0"><span class="n">${_escHtml(n)}</span> <span style="font-size:12px">${c.tunnel_port?`<span style="color:var(--green)">⇄ тоннель :${c.tunnel_port}</span>`:`<span style="color:var(--muted)">— ${_escHtml(_hostWithPort(c)||'—')}</span>`}</span></div>
|
||||||
<div style="display:flex;flex-wrap:wrap;gap:6px;justify-content:flex-end">
|
<div style="display:flex;flex-wrap:wrap;gap:6px;justify-content:flex-end">
|
||||||
<button type="button" class="btn btn-ghost" style="font-size:11px;padding:5px 10px" data-r-act="test" data-router="${enc}">🔌 Тест</button>
|
<button type="button" class="btn btn-ghost" style="font-size:11px;padding:5px 10px" data-r-act="test" data-router="${enc}">🔌 Тест</button>
|
||||||
<button type="button" class="btn btn-b" style="font-size:11px;padding:5px 10px" data-r-act="pull" data-router="${enc}">⬇ С роутера</button>
|
<button type="button" class="btn btn-b" style="font-size:11px;padding:5px 10px" data-r-act="pull" data-router="${enc}">⬇ С роутера</button>
|
||||||
@@ -443,7 +451,13 @@ async function loadRouters(){
|
|||||||
</div></div>`;
|
</div></div>`;
|
||||||
}).join('');
|
}).join('');
|
||||||
}
|
}
|
||||||
async function addRouter(){ const n=document.getElementById('r-name').value.trim(); if(!n)return; const b={ip:document.getElementById('r-ip').value.trim(),user:document.getElementById('r-user').value||'root',password:document.getElementById('r-pass').value}; try{ await fetch('/api/routers/'+encodeURIComponent(n),{method:'POST',headers:authHdr(),body:JSON.stringify(b)}); sm('rm','ok','✅ '+n); loadRouters(); }catch(e){sm('rm','err','❌ '+e)} }
|
async function addRouter(){
|
||||||
|
const n=document.getElementById('r-name').value.trim(); if(!n)return;
|
||||||
|
const portRaw=(document.getElementById('r-port')?.value||'').trim();
|
||||||
|
const p = portRaw ? Number(portRaw) : null;
|
||||||
|
const ssh_port = (p && Number.isFinite(p) && p > 0) ? Math.trunc(p) : null;
|
||||||
|
const b={ip:document.getElementById('r-ip').value.trim(),user:document.getElementById('r-user').value||'root',ssh_port:ssh_port,password:document.getElementById('r-pass').value};
|
||||||
|
try{ await fetch('/api/routers/'+encodeURIComponent(n),{method:'POST',headers:authHdr(),body:JSON.stringify(b)}); sm('rm','ok','✅ '+n); loadRouters(); }catch(e){sm('rm','err','❌ '+e)} }
|
||||||
async function addAndTunnel(){
|
async function addAndTunnel(){
|
||||||
const n=document.getElementById('r-name').value.trim();
|
const n=document.getElementById('r-name').value.trim();
|
||||||
if(!n){sm('rm','err','❌ Введи имя роутера');return;}
|
if(!n){sm('rm','err','❌ Введи имя роутера');return;}
|
||||||
|
|||||||
Reference in New Issue
Block a user