Template
Add AWG/WireGuard connect-domain UI on server page and fix Traefik port for Dokploy.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -103,7 +103,7 @@ else:
|
||||
application_path = os.path.dirname(__file__)
|
||||
|
||||
DATA_FILE = os.path.join(application_path, 'data.json') # legacy JSON; used only for one-shot import / export
|
||||
CURRENT_VERSION = "v2.6.4"
|
||||
CURRENT_VERSION = "v2.6.5"
|
||||
RELEASES_REPO_URL = repo_url()
|
||||
RELEASES_API_LATEST = api_latest_url()
|
||||
BIN_DIR = os.environ.get('TUNNEL_BIN_DIR', os.path.join(application_path, 'bin'))
|
||||
@@ -199,12 +199,26 @@ def get_ssh(server):
|
||||
)
|
||||
|
||||
|
||||
def get_server_connect_host(server: dict) -> str:
|
||||
WG_AWG_PROTOCOLS = frozenset({'awg', 'awg2', 'awg_legacy', 'wireguard'})
|
||||
|
||||
|
||||
def get_server_connect_host(server: dict, protocol: Optional[str] = None) -> str:
|
||||
"""Hostname embedded in client VPN configs (Endpoint, vless://, etc.).
|
||||
|
||||
SSH still uses server['host']; this value can be a DNS name so clients
|
||||
keep working after the server IP changes — update the A-record only.
|
||||
Per-protocol connect_domain (AWG / WireGuard) overrides the server default.
|
||||
"""
|
||||
protocols = server.get('protocols') or {}
|
||||
if protocol:
|
||||
base = protocol_base(protocol)
|
||||
if base in WG_AWG_PROTOCOLS:
|
||||
for key in (protocol, base):
|
||||
pinfo = protocols.get(key) if key else None
|
||||
if isinstance(pinfo, dict):
|
||||
domain = (pinfo.get('connect_domain') or '').strip()
|
||||
if domain:
|
||||
return domain.lower().rstrip('.')
|
||||
info = server.get('server_info') or {}
|
||||
for key in ('connect_domain', 'ssl_domain'):
|
||||
val = (info.get(key) or '').strip()
|
||||
@@ -1446,7 +1460,7 @@ def _create_remote_client(manager, protocol: str, server: dict, name: str, sourc
|
||||
base = protocol_base(protocol)
|
||||
if base == 'telemt':
|
||||
user_data = (source_client or {}).get('userData') or {}
|
||||
connect_host = get_server_connect_host(server)
|
||||
connect_host = get_server_connect_host(server, protocol)
|
||||
return manager.add_client(
|
||||
protocol, name, connect_host, port,
|
||||
telemt_quota=user_data.get('quota'),
|
||||
@@ -1455,7 +1469,7 @@ def _create_remote_client(manager, protocol: str, server: dict, name: str, sourc
|
||||
user_ad_tag=user_data.get('user_ad_tag'),
|
||||
max_tcp_conns=user_data.get('max_tcp_conns'),
|
||||
)
|
||||
connect_host = get_server_connect_host(server)
|
||||
connect_host = get_server_connect_host(server, protocol)
|
||||
if base == 'wireguard':
|
||||
return manager.add_client(name, connect_host)
|
||||
return manager.add_client(protocol, name, connect_host, port)
|
||||
@@ -1663,9 +1677,9 @@ async def perform_mass_operations(delete_uids: List[str] = None, toggle_uids: Li
|
||||
manager = get_protocol_manager(ssh, c_req['protocol'])
|
||||
|
||||
if c_req['protocol'] == 'wireguard':
|
||||
res = await asyncio.to_thread(manager.add_client, c_req['name'], get_server_connect_host(srv))
|
||||
res = await asyncio.to_thread(manager.add_client, c_req['name'], get_server_connect_host(srv, 'wireguard'))
|
||||
else:
|
||||
res = await asyncio.to_thread(_manager_call, manager, 'add_client', c_req['protocol'], c_req['name'], get_server_connect_host(srv), port)
|
||||
res = await asyncio.to_thread(_manager_call, manager, 'add_client', c_req['protocol'], c_req['name'], get_server_connect_host(srv, c_req['protocol']), port)
|
||||
|
||||
if res.get('client_id'):
|
||||
new_conn = {
|
||||
@@ -2129,6 +2143,11 @@ class EditServerRequest(BaseModel):
|
||||
connect_domain: Optional[str] = None
|
||||
|
||||
|
||||
class ConnectDomainRequest(BaseModel):
|
||||
connect_domain: str = ''
|
||||
protocol: Optional[str] = None
|
||||
|
||||
|
||||
class ReorderServersRequest(BaseModel):
|
||||
# `order[i]` is the *old* server index now at position `i` in the new layout.
|
||||
order: List[int]
|
||||
@@ -3049,6 +3068,75 @@ async def api_edit_server(request: Request, server_id: int, req: EditServerReque
|
||||
return JSONResponse({'error': str(e)}, status_code=500)
|
||||
|
||||
|
||||
@app.get('/api/servers/{server_id}/connect-domain', tags=["Servers"])
|
||||
async def api_get_connect_domain(request: Request, server_id: int, protocol: Optional[str] = None):
|
||||
if not _check_admin(request):
|
||||
return JSONResponse({'error': 'Forbidden'}, status_code=403)
|
||||
data = load_data()
|
||||
if server_id >= len(data['servers']):
|
||||
return JSONResponse({'error': 'Server not found'}, status_code=404)
|
||||
server = data['servers'][server_id]
|
||||
proto = (protocol or '').strip()
|
||||
info = server.get('server_info') or {}
|
||||
proto_domain = ''
|
||||
if proto:
|
||||
pinfo = (server.get('protocols') or {}).get(proto) or {}
|
||||
proto_domain = (pinfo.get('connect_domain') or '').strip()
|
||||
return {
|
||||
'connect_domain': (info.get('connect_domain') or '').strip(),
|
||||
'protocol_connect_domain': proto_domain,
|
||||
'protocol': proto,
|
||||
'effective_host': get_server_connect_host(server, proto or None),
|
||||
'ssh_host': server.get('host') or '',
|
||||
}
|
||||
|
||||
|
||||
@app.post('/api/servers/{server_id}/connect-domain', tags=["Servers"])
|
||||
async def api_set_connect_domain(request: Request, server_id: int, req: ConnectDomainRequest):
|
||||
"""Set client Endpoint hostname for AWG / WireGuard without re-verifying SSH."""
|
||||
if not _check_admin(request):
|
||||
return JSONResponse({'error': 'Forbidden'}, status_code=403)
|
||||
try:
|
||||
data = load_data()
|
||||
if server_id >= len(data['servers']):
|
||||
return JSONResponse({'error': 'Server not found'}, status_code=404)
|
||||
server = data['servers'][server_id]
|
||||
domain = (req.connect_domain or '').strip().lower().rstrip('.')
|
||||
proto = (req.protocol or '').strip()
|
||||
|
||||
if proto:
|
||||
base = protocol_base(proto)
|
||||
if base not in WG_AWG_PROTOCOLS:
|
||||
return JSONResponse(
|
||||
{'error': 'Per-protocol domain is only supported for AmneziaWG / WireGuard'},
|
||||
status_code=400,
|
||||
)
|
||||
protocols = server.setdefault('protocols', {})
|
||||
proto_info = protocols.setdefault(proto, {})
|
||||
if domain:
|
||||
proto_info['connect_domain'] = domain
|
||||
else:
|
||||
proto_info.pop('connect_domain', None)
|
||||
else:
|
||||
info = dict(server.get('server_info') or {})
|
||||
if domain:
|
||||
info['connect_domain'] = domain
|
||||
else:
|
||||
info.pop('connect_domain', None)
|
||||
server['server_info'] = info
|
||||
|
||||
save_data(data)
|
||||
return {
|
||||
'status': 'success',
|
||||
'connect_domain': domain,
|
||||
'protocol': proto,
|
||||
'effective_host': get_server_connect_host(server, proto or None),
|
||||
}
|
||||
except Exception as e:
|
||||
logger.exception('Error saving connect domain')
|
||||
return JSONResponse({'error': str(e)}, status_code=500)
|
||||
|
||||
|
||||
@app.get('/api/servers/{server_id}/ping', tags=["Servers"])
|
||||
async def api_server_ping(request: Request, server_id: int):
|
||||
"""Cheap reachability check: opens a TCP connection to the SSH port,
|
||||
@@ -3983,7 +4071,7 @@ async def api_protocol_export_clients(request: Request, server_id: int, req: Pro
|
||||
continue
|
||||
try:
|
||||
config = _manager_call(
|
||||
manager, 'get_client_config', req.protocol, client_id, get_server_connect_host(server), port
|
||||
manager, 'get_client_config', req.protocol, client_id, get_server_connect_host(server, req.protocol), port
|
||||
)
|
||||
except Exception as exc:
|
||||
skipped.append(f'{client_name}: {exc}')
|
||||
@@ -4467,7 +4555,7 @@ async def api_add_connection(request: Request, server_id: int, req: AddConnectio
|
||||
|
||||
if protocol_base(req.protocol) == 'telemt':
|
||||
result = manager.add_client(
|
||||
req.protocol, req.name, get_server_connect_host(server), port,
|
||||
req.protocol, req.name, get_server_connect_host(server, req.protocol), port,
|
||||
telemt_quota=req.telemt_quota,
|
||||
telemt_max_ips=req.telemt_max_ips,
|
||||
telemt_expiry=req.telemt_expiry,
|
||||
@@ -4476,9 +4564,9 @@ async def api_add_connection(request: Request, server_id: int, req: AddConnectio
|
||||
max_tcp_conns=req.telemt_max_conns
|
||||
)
|
||||
elif protocol_base(req.protocol) == 'wireguard':
|
||||
result = manager.add_client(req.name, get_server_connect_host(server))
|
||||
result = manager.add_client(req.name, get_server_connect_host(server, req.protocol))
|
||||
else:
|
||||
result = manager.add_client(req.protocol, req.name, get_server_connect_host(server), port)
|
||||
result = manager.add_client(req.protocol, req.name, get_server_connect_host(server, req.protocol), port)
|
||||
ssh.disconnect()
|
||||
|
||||
if result.get('config'):
|
||||
@@ -4601,7 +4689,7 @@ async def api_get_connection_config(request: Request, server_id: int, req: Conne
|
||||
ssh = get_ssh(server)
|
||||
ssh.connect()
|
||||
manager = get_protocol_manager(ssh, req.protocol)
|
||||
config = _manager_call(manager, 'get_client_config', req.protocol, req.client_id, get_server_connect_host(server), port)
|
||||
config = _manager_call(manager, 'get_client_config', req.protocol, req.client_id, get_server_connect_host(server, req.protocol), port)
|
||||
ssh.disconnect()
|
||||
vpn_link = generate_vpn_link(config) if config else ''
|
||||
return {'config': config, 'vpn_link': vpn_link}
|
||||
@@ -4784,7 +4872,7 @@ async def api_add_user(request: Request, req: AddUserRequest):
|
||||
manager = get_protocol_manager(ssh, req.protocol)
|
||||
if protocol_base(req.protocol) == 'telemt':
|
||||
conn_result = manager.add_client(
|
||||
req.protocol, conn_name, get_server_connect_host(server), port,
|
||||
req.protocol, conn_name, get_server_connect_host(server, req.protocol), port,
|
||||
telemt_quota=req.telemt_quota,
|
||||
telemt_max_ips=req.telemt_max_ips,
|
||||
telemt_expiry=req.telemt_expiry,
|
||||
@@ -4793,7 +4881,7 @@ async def api_add_user(request: Request, req: AddUserRequest):
|
||||
max_tcp_conns=req.telemt_max_conns
|
||||
)
|
||||
else:
|
||||
conn_result = manager.add_client(req.protocol, conn_name, get_server_connect_host(server), port)
|
||||
conn_result = manager.add_client(req.protocol, conn_name, get_server_connect_host(server, req.protocol), port)
|
||||
ssh.disconnect()
|
||||
|
||||
if conn_result.get('client_id'):
|
||||
@@ -5001,12 +5089,12 @@ async def api_add_user_connection(request: Request, user_id: str, req: AddUserCo
|
||||
# Link existing client
|
||||
config = await asyncio.to_thread(
|
||||
_manager_call, manager, 'get_client_config',
|
||||
req.protocol, req.client_id, get_server_connect_host(server), port,
|
||||
req.protocol, req.client_id, get_server_connect_host(server, req.protocol), port,
|
||||
)
|
||||
result = {'client_id': req.client_id, 'config': config}
|
||||
elif protocol_base(req.protocol) == 'telemt':
|
||||
result = await asyncio.to_thread(
|
||||
manager.add_client, req.protocol, req.name, get_server_connect_host(server), port,
|
||||
manager.add_client, req.protocol, req.name, get_server_connect_host(server, req.protocol), port,
|
||||
telemt_quota=req.telemt_quota,
|
||||
telemt_max_ips=req.telemt_max_ips,
|
||||
telemt_expiry=req.telemt_expiry,
|
||||
@@ -5015,11 +5103,11 @@ async def api_add_user_connection(request: Request, user_id: str, req: AddUserCo
|
||||
max_tcp_conns=req.telemt_max_conns,
|
||||
)
|
||||
elif protocol_base(req.protocol) == 'wireguard':
|
||||
result = await asyncio.to_thread(manager.add_client, req.name, get_server_connect_host(server))
|
||||
result = await asyncio.to_thread(manager.add_client, req.name, get_server_connect_host(server, req.protocol))
|
||||
else:
|
||||
result = await asyncio.to_thread(
|
||||
_manager_call, manager, 'add_client',
|
||||
req.protocol, req.name, get_server_connect_host(server), port,
|
||||
req.protocol, req.name, get_server_connect_host(server, req.protocol), port,
|
||||
)
|
||||
finally:
|
||||
await asyncio.to_thread(ssh.disconnect)
|
||||
@@ -5210,7 +5298,7 @@ async def api_share_config(token: str, connection_id: str, request: Request):
|
||||
ssh.connect()
|
||||
# Use appropriate manager for the protocol
|
||||
manager = get_protocol_manager(ssh, conn['protocol'])
|
||||
config = _manager_call(manager, 'get_client_config', conn['protocol'], conn['client_id'], get_server_connect_host(server), port)
|
||||
config = _manager_call(manager, 'get_client_config', conn['protocol'], conn['client_id'], get_server_connect_host(server, conn['protocol']), port)
|
||||
ssh.disconnect()
|
||||
vpn_link = generate_vpn_link(config) if config else ''
|
||||
return {'config': config, 'vpn_link': vpn_link, 'expires_at': user.get('expiration_date')}
|
||||
@@ -5370,7 +5458,7 @@ async def api_guest_config(token: str, connection_id: str, request: Request):
|
||||
ssh = get_ssh(server)
|
||||
ssh.connect()
|
||||
manager = get_protocol_manager(ssh, conn['protocol'])
|
||||
config = _manager_call(manager, 'get_client_config', conn['protocol'], conn['client_id'], get_server_connect_host(server), port)
|
||||
config = _manager_call(manager, 'get_client_config', conn['protocol'], conn['client_id'], get_server_connect_host(server, conn['protocol']), port)
|
||||
ssh.disconnect()
|
||||
vpn_link = generate_vpn_link(config) if config else ''
|
||||
return {'config': config, 'vpn_link': vpn_link, 'expires_at': holder.get('expiration_date')}
|
||||
@@ -5434,11 +5522,11 @@ async def api_guest_create(token: str, req: GuestCreateRequest, request: Request
|
||||
try:
|
||||
manager = get_protocol_manager(ssh, protocol)
|
||||
if protocol_base(protocol) == 'wireguard':
|
||||
result = await asyncio.to_thread(manager.add_client, name, get_server_connect_host(server))
|
||||
result = await asyncio.to_thread(manager.add_client, name, get_server_connect_host(server, protocol))
|
||||
else:
|
||||
result = await asyncio.to_thread(
|
||||
_manager_call, manager, 'add_client',
|
||||
protocol, name, get_server_connect_host(server), port,
|
||||
protocol, name, get_server_connect_host(server, protocol), port,
|
||||
)
|
||||
finally:
|
||||
await asyncio.to_thread(ssh.disconnect)
|
||||
@@ -5596,11 +5684,11 @@ async def _create_config_for_protocol(
|
||||
try:
|
||||
manager = get_protocol_manager(ssh, protocol)
|
||||
if protocol_base(protocol) == 'wireguard':
|
||||
result = await asyncio.to_thread(manager.add_client, name, get_server_connect_host(server))
|
||||
result = await asyncio.to_thread(manager.add_client, name, get_server_connect_host(server, protocol))
|
||||
else:
|
||||
result = await asyncio.to_thread(
|
||||
_manager_call, manager, 'add_client',
|
||||
protocol, name, get_server_connect_host(server), port,
|
||||
protocol, name, get_server_connect_host(server, protocol), port,
|
||||
)
|
||||
finally:
|
||||
await asyncio.to_thread(ssh.disconnect)
|
||||
@@ -5971,7 +6059,7 @@ async def api_my_connection_config(request: Request, connection_id: str):
|
||||
ssh.connect()
|
||||
# Use appropriate manager for the protocol (fixes Telemt/Xray not working for users)
|
||||
manager = get_protocol_manager(ssh, conn['protocol'])
|
||||
config = _manager_call(manager, 'get_client_config', conn['protocol'], conn['client_id'], get_server_connect_host(server), port)
|
||||
config = _manager_call(manager, 'get_client_config', conn['protocol'], conn['client_id'], get_server_connect_host(server, conn['protocol']), port)
|
||||
ssh.disconnect()
|
||||
vpn_link = generate_vpn_link(config) if config else ''
|
||||
expires_at = None
|
||||
|
||||
Reference in New Issue
Block a user