Use client connect domain in VPN configs and fix legacy JSON import to PostgreSQL.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
orohi
2026-07-28 13:00:37 +03:00
co-authored by Cursor
parent 78e69d899a
commit 9b4ef0f9f0
9 changed files with 248 additions and 36 deletions
+62 -27
View File
@@ -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.2"
CURRENT_VERSION = "v2.6.4"
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'))
@@ -116,8 +116,10 @@ from db import ( # noqa: E402
ensure_db_ready,
export_data_dict,
export_database_sql,
invalidate_data_cache,
load_data,
load_tunnel_state,
normalize_import_data,
restore_database_sql,
save_data,
save_tunnel_state,
@@ -197,6 +199,20 @@ def get_ssh(server):
)
def get_server_connect_host(server: dict) -> 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.
"""
info = server.get('server_info') or {}
for key in ('connect_domain', 'ssl_domain'):
val = (info.get(key) or '').strip()
if val:
return val.lower().rstrip('.')
return (server.get('host') or '').strip()
def get_current_user(request: Request, data: Optional[dict] = None):
user_id = request.session.get('user_id')
if not user_id:
@@ -1430,17 +1446,19 @@ 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)
return manager.add_client(
protocol, name, server['host'], port,
protocol, name, connect_host, port,
telemt_quota=user_data.get('quota'),
telemt_expiry=user_data.get('expiry'),
secret=user_data.get('token'),
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)
if base == 'wireguard':
return manager.add_client(name, server['host'])
return manager.add_client(protocol, name, server['host'], port)
return manager.add_client(name, connect_host)
return manager.add_client(protocol, name, connect_host, port)
def _move_connections_sync(
@@ -1645,9 +1663,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'], srv['host'])
res = await asyncio.to_thread(manager.add_client, c_req['name'], get_server_connect_host(srv))
else:
res = await asyncio.to_thread(_manager_call, manager, 'add_client', c_req['protocol'], c_req['name'], srv['host'], port)
res = await asyncio.to_thread(_manager_call, manager, 'add_client', c_req['protocol'], c_req['name'], get_server_connect_host(srv), port)
if res.get('client_id'):
new_conn = {
@@ -2093,6 +2111,7 @@ class AddServerRequest(BaseModel):
name: str = ''
ssl_domain: str = ''
ssl_email: str = ''
connect_domain: str = ''
class EditServerRequest(BaseModel):
@@ -2107,6 +2126,7 @@ class EditServerRequest(BaseModel):
private_key: Optional[str] = None
ssl_domain: Optional[str] = None
ssl_email: Optional[str] = None
connect_domain: Optional[str] = None
class ReorderServersRequest(BaseModel):
@@ -2929,10 +2949,13 @@ async def api_add_server(request: Request, req: AddServerRequest):
}
ssl_domain = (req.ssl_domain or '').strip().lower()
ssl_email = (req.ssl_email or '').strip()
connect_domain = (req.connect_domain or '').strip().lower()
if ssl_domain:
server_info['ssl_domain'] = ssl_domain
if ssl_email:
server_info['ssl_email'] = ssl_email
if connect_domain:
server_info['connect_domain'] = connect_domain
server['server_info'] = server_info
data = load_data()
data['servers'].append(server)
@@ -2998,7 +3021,7 @@ async def api_edit_server(request: Request, server_id: int, req: EditServerReque
info = dict(server.get('server_info') or {})
if isinstance(server_info, dict):
for k, v in server_info.items():
if k not in ('ssl_domain', 'ssl_email'):
if k not in ('ssl_domain', 'ssl_email', 'connect_domain'):
info[k] = v
if req.ssl_domain is not None:
domain = (req.ssl_domain or '').strip().lower()
@@ -3012,6 +3035,12 @@ async def api_edit_server(request: Request, server_id: int, req: EditServerReque
info['ssl_email'] = email
else:
info.pop('ssl_email', None)
if req.connect_domain is not None:
domain = (req.connect_domain or '').strip().lower()
if domain:
info['connect_domain'] = domain
else:
info.pop('connect_domain', None)
server['server_info'] = info
save_data(data)
return {'status': 'success', 'server_info': info}
@@ -3954,7 +3983,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, server['host'], port
manager, 'get_client_config', req.protocol, client_id, get_server_connect_host(server), port
)
except Exception as exc:
skipped.append(f'{client_name}: {exc}')
@@ -4438,7 +4467,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, server['host'], port,
req.protocol, req.name, get_server_connect_host(server), port,
telemt_quota=req.telemt_quota,
telemt_max_ips=req.telemt_max_ips,
telemt_expiry=req.telemt_expiry,
@@ -4447,9 +4476,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, server['host'])
result = manager.add_client(req.name, get_server_connect_host(server))
else:
result = manager.add_client(req.protocol, req.name, server['host'], port)
result = manager.add_client(req.protocol, req.name, get_server_connect_host(server), port)
ssh.disconnect()
if result.get('config'):
@@ -4572,7 +4601,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, server['host'], port)
config = _manager_call(manager, 'get_client_config', req.protocol, req.client_id, get_server_connect_host(server), port)
ssh.disconnect()
vpn_link = generate_vpn_link(config) if config else ''
return {'config': config, 'vpn_link': vpn_link}
@@ -4755,7 +4784,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, server['host'], port,
req.protocol, conn_name, get_server_connect_host(server), port,
telemt_quota=req.telemt_quota,
telemt_max_ips=req.telemt_max_ips,
telemt_expiry=req.telemt_expiry,
@@ -4764,7 +4793,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, server['host'], port)
conn_result = manager.add_client(req.protocol, conn_name, get_server_connect_host(server), port)
ssh.disconnect()
if conn_result.get('client_id'):
@@ -4972,12 +5001,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, server['host'], port,
req.protocol, req.client_id, get_server_connect_host(server), 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, server['host'], port,
manager.add_client, req.protocol, req.name, get_server_connect_host(server), port,
telemt_quota=req.telemt_quota,
telemt_max_ips=req.telemt_max_ips,
telemt_expiry=req.telemt_expiry,
@@ -4986,11 +5015,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, server['host'])
result = await asyncio.to_thread(manager.add_client, req.name, get_server_connect_host(server))
else:
result = await asyncio.to_thread(
_manager_call, manager, 'add_client',
req.protocol, req.name, server['host'], port,
req.protocol, req.name, get_server_connect_host(server), port,
)
finally:
await asyncio.to_thread(ssh.disconnect)
@@ -5181,7 +5210,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'], server['host'], port)
config = _manager_call(manager, 'get_client_config', conn['protocol'], conn['client_id'], get_server_connect_host(server), 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')}
@@ -5341,7 +5370,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'], server['host'], port)
config = _manager_call(manager, 'get_client_config', conn['protocol'], conn['client_id'], get_server_connect_host(server), 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')}
@@ -5405,11 +5434,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, server['host'])
result = await asyncio.to_thread(manager.add_client, name, get_server_connect_host(server))
else:
result = await asyncio.to_thread(
_manager_call, manager, 'add_client',
protocol, name, server['host'], port,
protocol, name, get_server_connect_host(server), port,
)
finally:
await asyncio.to_thread(ssh.disconnect)
@@ -5567,11 +5596,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, server['host'])
result = await asyncio.to_thread(manager.add_client, name, get_server_connect_host(server))
else:
result = await asyncio.to_thread(
_manager_call, manager, 'add_client',
protocol, name, server['host'], port,
protocol, name, get_server_connect_host(server), port,
)
finally:
await asyncio.to_thread(ssh.disconnect)
@@ -5942,7 +5971,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'], server['host'], port)
config = _manager_call(manager, 'get_client_config', conn['protocol'], conn['client_id'], get_server_connect_host(server), port)
ssh.disconnect()
vpn_link = generate_vpn_link(config) if config else ''
expires_at = None
@@ -6654,8 +6683,14 @@ async def api_backup_restore(request: Request, file: UploadFile = File(...)):
backup_data.setdefault('invite_links', [])
backup_data.setdefault('settings', {})
async with DATA_LOCK:
save_data(backup_data)
try:
backup_data = normalize_import_data(backup_data)
async with DATA_LOCK:
save_data(backup_data)
except Exception as e:
invalidate_data_cache()
logger.exception('JSON backup restore failed')
return JSONResponse({'error': f'Import failed: {e}'}, status_code=400)
return {'status': 'success', 'format': 'json'}
try: