Files
gitlab-instance-0a899031_cn…/scripts/dev-tunnel.py
2026-06-08 06:05:31 -05:00

134 lines
3.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
Windows / macOS 本地开发SSH 隧道把线上 MinIO/PocketBase 映射到本机。
默认映射:
localhost:9100 -> 服务器 127.0.0.1:9100 (MinIO S3 API)
localhost:8090 -> 服务器 127.0.0.1:8090 (PocketBase可选)
用法:
python scripts/dev-tunnel.py
环境变量(推荐写在 cursor.env也可写在 .env.local
DEV_SSH_HOST=82.157.112.245
DEV_SSH_USER=ubuntu
DEV_SSH_PASSWORD=你的密码
DEV_TUNNEL_MINIO_PORT=9100
DEV_TUNNEL_POCKETBASE=0 # 设为 1 时同时映射 8090
"""
from __future__ import annotations
import os
import socket
import sys
import threading
import time
from pathlib import Path
try:
import paramiko
except ImportError:
print("请先安装 paramiko: pip install paramiko", file=sys.stderr)
raise
ROOT = Path(__file__).resolve().parent.parent
from load_dev_env import load_dev_env as load_env # noqa: E402
def forward_tunnel(local_port: int, remote_host: str, remote_port: int, transport) -> None:
def handle(client: socket.socket, addr) -> None:
try:
chan = transport.open_channel("direct-tcpip", (remote_host, remote_port), addr)
except Exception as exc:
print(f"[tunnel] open_channel failed {local_port}->{remote_host}:{remote_port}: {exc}")
client.close()
return
def relay(src, dst) -> None:
try:
while True:
data = src.recv(1024 * 64)
if not data:
break
dst.sendall(data)
except Exception:
pass
finally:
try:
src.close()
except Exception:
pass
try:
dst.close()
except Exception:
pass
threading.Thread(target=relay, args=(client, chan), daemon=True).start()
threading.Thread(target=relay, args=(chan, client), daemon=True).start()
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(("127.0.0.1", local_port))
server.listen(32)
print(f"[tunnel] listening 127.0.0.1:{local_port} -> {remote_host}:{remote_port}")
while True:
client_sock, addr = server.accept()
threading.Thread(
target=handle,
args=(client_sock, addr),
daemon=True,
).start()
def main() -> None:
load_env()
host = os.getenv("DEV_SSH_HOST", "82.157.112.245")
user = os.getenv("DEV_SSH_USER", "ubuntu")
password = os.getenv("DEV_SSH_PASSWORD", "")
if not password:
print("缺少 DEV_SSH_PASSWORD请在 .env.local 中配置", file=sys.stderr)
sys.exit(1)
minio_local = int(os.getenv("DEV_TUNNEL_MINIO_PORT", "9100"))
pb_enabled = os.getenv("DEV_TUNNEL_POCKETBASE", "0").lower() in {"1", "true", "yes"}
pb_local = int(os.getenv("DEV_TUNNEL_POCKETBASE_PORT", "8090"))
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
print(f"[tunnel] connecting {user}@{host} ...")
client.connect(host, username=user, password=password, timeout=20)
transport = client.get_transport()
if transport is None:
print("SSH transport unavailable", file=sys.stderr)
sys.exit(1)
threading.Thread(
target=forward_tunnel,
args=(minio_local, "127.0.0.1", 9100, transport),
daemon=True,
).start()
if pb_enabled:
threading.Thread(
target=forward_tunnel,
args=(pb_local, "127.0.0.1", 8090, transport),
daemon=True,
).start()
print("[tunnel] ready. Ctrl+C to stop.")
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print("\n[tunnel] stopping")
finally:
client.close()
if __name__ == "__main__":
main()