diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..9b0333f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,24 @@ +FROM vnc-base + + +ENV DEBIAN_FRONTEND=noninteractive \ + VNC_PASSWORD=123456 + +# 安装 Snap 版 Chromium +RUN snap install chromium \ + && ln -s /snap/bin/chromium /usr/local/bin/chromium-browser + +RUN git clone https://github.com/novnc/noVNC.git /opt/novnc \ + && git clone https://github.com/novnc/websockify.git /opt/novnc/utils/websockify + +RUN mkdir -p /root/.vnc && echo "$VNC_PASSWORD" | vncpasswd -f > /root/.vnc/passwd && chmod 600 /root/.vnc/passwd + +EXPOSE 6080 9222 + +CMD Xvfb :1 -screen 0 1280x1024x24 & \ + export DISPLAY=:1 && \ + fluxbox & \ + vncserver :1 -geometry 1280x1024 -depth 24 -localhost no && \ + /opt/novnc/utils/novnc_proxy --vnc localhost:5901 --listen 6080 & \ + chromium-browser --no-sandbox --disable-gpu --remote-debugging-port=9222 --remote-debugging-address=0.0.0.0 --window-size=1280,1024 && \ + tail -f /dev/null \ No newline at end of file diff --git a/Dockerfile_base b/Dockerfile_base new file mode 100644 index 0000000..6cce700 --- /dev/null +++ b/Dockerfile_base @@ -0,0 +1,14 @@ +# FROM ubuntu:22.04 +FROM harbor.hk.crabapples.cn:22580/library/ubuntu:24.04 + +ENV DEBIAN_FRONTEND=noninteractive \ + VNC_PASSWORD=123456 + +RUN sed -i 's@//.*archive.ubuntu.com@//mirrors.aliyun.com@g' /etc/apt/sources.list.d/ubuntu.sources &&\ + sed -i 's@//security.ubuntu.com@//mirrors.aliyun.com@g' /etc/apt/sources.list.d/ubuntu.sources &&\ + sed -i 's@//ports.ubuntu.com@//mirrors.aliyun.com@g' /etc/apt/sources.list.d/ubuntu.sources + + +RUN apt-get update + +RUN apt-get install -y wget curl gnupg x11vnc xvfb fluxbox git python3 snapd && rm -rf /var/lib/apt/lists/* \ No newline at end of file diff --git a/app.py b/app.py index 8e035b4..cbae76f 100755 --- a/app.py +++ b/app.py @@ -58,7 +58,7 @@ async def start_xvnc_server(instance: int = 1, password: str = "123456") -> str: try: # 调用 browser-vnc.sh 脚本 process = await asyncio.create_subprocess_exec( - f"{SHELL_DIR}/browser-vnc.sh", + f"{SHELL_DIR}/start-vnc.sh", "start", str(instance), password, @@ -139,154 +139,6 @@ async def stop_xvnc_server(instance: int = 1, keep_data: bool = False) -> str: data["message"] = str(e) return json.dumps(data, ensure_ascii=False) - -@mcp.tool() -async def stop_all_xvnc_servers(keep_data: bool = False) -> str: - """停止所有 VNC 实例 - - Args: - keep_data: 是否保留用户数据,默认 False(删除所有数据) - - Returns: - 操作结果 - """ - data = { - "status": 200, - "action": "stop_all", - "keep_data": keep_data - } - - try: - # 选择命令:stop-all 或 stop-all-keep-data - cmd = "stop-all-keep-data" if keep_data else "stop-all" - - process = await asyncio.create_subprocess_exec( - f"{SHELL_DIR}/browser-vnc.sh", - cmd, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - - stdout, stderr = await process.communicate() - output = stdout.decode() - - if process.returncode == 0: - data["message"] = output - else: - data["status"] = 500 - data["message"] = stderr.decode() or output or "Unknown error" - - return json.dumps(data, ensure_ascii=False) - - except Exception as e: - data["status"] = 500 - data["message"] = str(e) - return json.dumps(data, ensure_ascii=False) - - -@mcp.tool() -async def list_xvnc_servers() -> str: - """列出所有运行中的 VNC 实例""" - data = { - "status": 200, - "action": "list", - "instances": [] - } - - try: - process = await asyncio.create_subprocess_exec( - f"{SHELL_DIR}/browser-vnc.sh", - "status", - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - - stdout, stderr = await process.communicate() - output = stdout.decode() - - # 解析输出,提取运行中的实例 - for line in output.split('\n'): - if "✅ Running" in line: - # 提取实例编号,例如 "Instance 1: ✅ Running" - import re - match = re.search(r'Instance (\d+):', line) - if match: - instance = int(match.group(1)) - data["instances"].append({ - "instance": instance, - "vnc_port": 5900 + (instance - 1), - "display": 98 + instance, - "vnc_url": f"{VNC_SERVER_HOST}/vnc_lite.html?path=?token=user{instance}" - }) - - data["count"] = len(data["instances"]) - data["raw_output"] = output - - return json.dumps(data, ensure_ascii=False) - - except Exception as e: - data["status"] = 500 - data["message"] = str(e) - return json.dumps(data, ensure_ascii=False) - - -@mcp.tool() -async def get_vnc_connection_info(instance: int = 1) -> str: - """获取指定实例的连接信息 - - Args: - instance: 实例编号 (1-10) - - Returns: - 包含连接信息的 JSON 字符串 - """ - data = { - "instance": instance, - "status": 200 - } - - try: - # 检查实例是否在运行 - process = await asyncio.create_subprocess_exec( - f"{SHELL_DIR}/browser-vnc.sh", - "status", - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - - stdout, _ = await process.communicate() - output = stdout.decode() - - # 检查实例状态 - instance_pattern = f"Instance {instance}:" - is_running = False - for line in output.split('\n'): - if instance_pattern in line: - if "✅ Running" in line: - is_running = True - break - - public_ip = subprocess.getoutput("hostname -I | awk '{print $1}'").strip() - vnc_port = 5900 + (instance - 1) - display_num = 98 + instance - - data["is_running"] = is_running - data["vnc_url"] = f"{VNC_SERVER_HOST}/vnc_lite.html?path=?token=user{instance}" - data["vnc_port"] = vnc_port - data["display"] = display_num - data["public_ip"] = public_ip - - if not is_running: - data["warning"] = "Instance is not running, start it with start_xvnc_server" - - return json.dumps(data, ensure_ascii=False) - - except Exception as e: - data["status"] = 500 - data["message"] = str(e) - return json.dumps(data, ensure_ascii=False) - - @mcp.tool() async def cleanup_xvnc_data(instance: Optional[int] = None) -> str: """清理 VNC 实例的用户数据 diff --git a/bin/start-vnc.sh b/bin/start-vnc.sh new file mode 100755 index 0000000..c52a93f --- /dev/null +++ b/bin/start-vnc.sh @@ -0,0 +1,77 @@ +#!/bin/bash +# 浏览器 VNC 守护进程 + +set -euo pipefail + +TARGET_URL="https://www.baidu.com" +VNC_PWD=${1:-"123456"} # VNC 密码 +VNC_PORT=${2:-5900} # VNC 端口 +DISPLAY_NUM=${3:-99} # 显示编号 +DATA_DIR=${4:-"/tmp/chrome-profile-${DISPLAY_NUM}"} # 用户数据目录 +PID_FILE="/tmp/chrome-browser-${DISPLAY_NUM}.pid" # 进程 ID 文件 +LOG_FILE="/tmp/chrome-browser-${DISPLAY_NUM}.log" # 日志文件 +USER_COUNT="${5:-10}" + +GREEN='\033[0;32m'; RED='\033[0;31m'; NC='\033[0m' +log() { echo -e "${GREEN}[$(date '+%H:%M:%S')]${NC} $*"; } +err() { echo -e "${RED}[$(date '+%H:%M:%S')]${NC} $*" >&2; } + +# 检查是否已在运行 +if [ -f "$PID_FILE" ] && kill -0 $(cat "$PID_FILE") 2>/dev/null; then + err "实例 :${DISPLAY_NUM} 已在运行中" + exit 1 +fi + +cleanup() { + log "正在清理实例 :${DISPLAY_NUM}..." + pkill -P $$ 2>/dev/null || true + pkill -f "Xvfb :${DISPLAY_NUM}" 2>/dev/null || true + pkill -f "x11vnc.*${DISPLAY_NUM}" 2>/dev/null || true + rm -f "$PID_FILE" 2>/dev/null || true +} +trap cleanup EXIT INT TERM + +# 记录当前进程 ID +echo $$ > "$PID_FILE" + +log "正在启动 Xvfb :${DISPLAY_NUM}" +Xvfb :${DISPLAY_NUM} -screen 0 1280x1024x24 -nolisten tcp & +sleep 2 +export DISPLAY=:${DISPLAY_NUM} + +log "正在启动 Chromium (数据目录: ${DATA_DIR})" +mkdir -p "$DATA_DIR" + +# 查找 Chromium 可执行文件 +CHROME_BIN=$(ls $HOME/.cache/ms-playwright/chromium-*/chrome-linux/chrome 2>/dev/null | head -1) +if [ -z "$CHROME_BIN" ]; then + CHROME_BIN=$(which chromium-browser 2>/dev/null || which google-chrome-stable 2>/dev/null || true) +fi +[ -z "$CHROME_BIN" ] && { err "未找到 Chromium"; exit 1; } + +# 启动 Chromium(非 headless 模式,可见界面) +"$CHROME_BIN" \ + --no-sandbox \ + --disable-gpu \ + --disable-dev-shm-usage \ + --user-data-dir="${DATA_DIR}" \ + --window-size=1280,1024 \ + "${TARGET_URL}" & + +CHROME_PID=$! + +log "正在启动 x11vnc,端口: ${VNC_PORT}..." +x11vnc -display :${DISPLAY_NUM} -forever -shared -passwd ${VNC_PWD} -rfbport ${VNC_PORT} -bg & + +# 获取服务器公网 IP +PUBLIC_HOST=$(curl -sf ifconfig.me 2>/dev/null || hostname -I | awk '{print $1}') +log "==========================================" +log "✅ 浏览器已启动 (仅 VNC,无 CDP)" +log "🖥️ VNC 地址: ${PUBLIC_HOST}:${VNC_PORT}" +log "🔑 VNC 密码: ${VNC_PWD}" +log "📁 数据目录: ${DATA_DIR}" +log "🛑 停止命令: ./browser-vnc.sh stop ${DISPLAY_NUM}" +log "==========================================" + +# 等待 Chromium 进程结束 +wait $CHROME_PID 2>/dev/null || true \ No newline at end of file diff --git a/bin/vnc_manager.sh b/bin/vnc_manager.sh new file mode 100644 index 0000000..5280166 --- /dev/null +++ b/bin/vnc_manager.sh @@ -0,0 +1,137 @@ +#!/bin/bash +# 浏览器 VNC 管理器 + +DAEMON_SCRIPT="$(dirname $0)/start-browser-vnc.sh" +BASE_VNC_PORT=5900 +BASE_DATA_DIR="/tmp/chrome-profiles" # 用户数据目录 + +start() { + local instance=${1:-1} + local vnc_pwd=${2} # 密码必须传入,不再有默认值 + local display_num=$((98 + instance)) + local vnc_port=$((BASE_VNC_PORT + instance - 1)) + local data_dir="${BASE_DATA_DIR}/instance-${instance}" + + # 检查密码是否提供 + if [ -z "$vnc_pwd" ]; then + echo "❌ 错误: 启动实例时必须提供密码" + echo "用法: $0 start <实例编号> <密码>" + return 1 + fi + + if [ -f "/tmp/chrome-browser-${display_num}.pid" ] && kill -0 $(cat "/tmp/chrome-browser-${display_num}.pid") 2>/dev/null; then + echo "实例 ${instance} 已在运行中" + return 1 + fi + + echo "正在启动实例 ${instance} (显示编号 :${display_num}, VNC 端口: ${vnc_port})..." + echo "数据目录: ${data_dir}" + + # 确保数据目录存在 + mkdir -p "$data_dir" + + nohup "$DAEMON_SCRIPT" "$vnc_pwd" "$vnc_port" "$display_num" "$data_dir" >> "/tmp/chrome-browser-${display_num}.log" 2>&1 & + sleep 3 + + local public_ip=$(hostname -I | awk '{print $1}') + echo "✅ 实例 ${instance} 已启动" + echo " VNC: ${public_ip}:${vnc_port}" + echo " 密码: ${vnc_pwd}" + echo " 数据: ${data_dir} (停止时将删除)" +} + +stop() { + local instance=${1:-1} + local display_num=$((98 + instance)) + local pid_file="/tmp/chrome-browser-${display_num}.pid" + local data_dir="${BASE_DATA_DIR}/instance-${instance}" + + if [ -f "$pid_file" ]; then + local pid=$(cat "$pid_file") + echo "正在停止实例 ${instance} (PID: $pid)..." + + # 先发送 TERM 信号,让浏览器优雅关闭 + kill -TERM $pid 2>/dev/null + sleep 3 + + # 如果还在运行,强制杀死 + if kill -0 $pid 2>/dev/null; then + echo "正在强制终止..." + kill -9 $pid 2>/dev/null + fi + + # 删除 PID 文件 + rm -f "$pid_file" + + # 删除用户数据目录 + if [ -d "$data_dir" ]; then + echo "正在删除用户数据: ${data_dir}" + rm -rf "$data_dir" + fi + + echo "✅ 实例 ${instance} 已停止" + else + echo "实例 ${instance} 未运行,正在清理数据..." + # 即使进程不在运行,也清理数据目录 + if [ -d "$data_dir" ]; then + rm -rf "$data_dir" + echo "✅ 数据已清理: ${data_dir}" + fi + fi + + # 额外清理:杀掉可能残留的 Xvfb 和 x11vnc 进程 + pkill -f "Xvfb :${display_num}" 2>/dev/null + pkill -f "x11vnc.*${display_num}" 2>/dev/null + + # 清理临时文件 + rm -f "/tmp/.X${display_num}-lock" 2>/dev/null + rm -f "/tmp/.X11-unix/X${display_num}" 2>/dev/null +} + +cleanup_data() { + local instance=${1:-1} + local data_dir="${BASE_DATA_DIR}/instance-${instance}" + + # 先停止实例 + stop $instance + + # 如果还有残留数据,强制删除 + if [ -d "$data_dir" ]; then + echo "正在强制清理数据: ${data_dir}" + rm -rf "$data_dir" + fi + + echo "✅ 实例 ${instance} 的数据已清理" +} + +case "${1:-}" in + start) + if [ $# -lt 3 ]; then + echo "❌ 错误: start 命令需要实例编号和密码" + echo "用法: $0 start <实例编号> <密码>" + echo "示例: $0 start 1 mypassword" + exit 1 + fi + start "$2" "$3" + ;; + stop) + stop "${2:-1}" + ;; + cleanup) + cleanup_data "${2:-1}" + ;; + *) + echo "用法: $0 {start|stop|cleanup} [参数]" + echo "" + echo "命令说明:" + echo " start <编号> <密码> - 启动实例 (必须提供密码)" + echo " stop [编号] - 停止实例并删除用户数据 (默认: 1)" + echo " cleanup [编号] - 强制删除指定实例的用户数据 (默认: 1)" + echo "" + echo "使用示例:" + echo " $0 start 1 mypass # 启动实例 1,密码 mypass" + echo " $0 start 2 123456 # 启动实例 2,密码 123456" + echo " $0 stop 1 # 停止实例 1 并删除其数据" + exit 1 + ;; +esac \ No newline at end of file diff --git a/bin_bak/auto_install.sh b/bin_bak/auto_install.sh new file mode 100644 index 0000000..a7f8669 --- /dev/null +++ b/bin_bak/auto_install.sh @@ -0,0 +1,46 @@ +#!/bin/bash +#wget -O- -q --show-progress https://git.nps.crabapples.cn/crabapples/learn-spider/raw/branch/main/bin/auto_install.sh | bash +# wget -q --show-progress https://git.nps.crabapples.cn/crabapples/learn-spider/raw/branch/main/bin/close_vnc.sh && chmod +x close_vnc.sh +SECOND=5 +GIT_DOMAIN=https://git.nps.crabapples.cn +echo "清理旧脚本..." + +rm -rf ./install.sh ./start_vnc_server.sh ./close_vnc_server.sh ./start_spider.sh ./update.sh ./uninstall.sh +echo "开始下载..." + +echo "下载安装脚本..." +wget -q --show-progress $GIT_DOMAIN/crabapples/learn-spider/raw/branch/main/bin/install.sh && chmod +x install.sh + +echo "下载启动VNC脚本..." +wget -q --show-progress $GIT_DOMAIN/crabapples/learn-spider/raw/branch/main/bin/start_vnc_server.sh && chmod +x start_vnc_server.sh + +echo "下载关闭VNC脚本..." +wget -q --show-progress $GIT_DOMAIN/crabapples/learn-spider/raw/branch/main/bin/close_vnc_server.sh && chmod +x close_vnc_server.sh + +echo "下载启动爬虫脚本..." +wget -q --show-progress $GIT_DOMAIN/crabapples/learn-spider/raw/branch/main/bin/start_spider.sh && chmod +x start_spider.sh + +echo "下载更新脚本..." +wget -q --show-progress $GIT_DOMAIN/crabapples/learn-spider/raw/branch/main/bin/update.sh && chmod +x update.sh + +echo "下载卸载脚本..." +wget -q --show-progress $GIT_DOMAIN/crabapples/learn-spider/raw/branch/main/bin/uninstall.sh && chmod +x uninstall.sh + +for i in $(seq 1 $SECOND); do + clear + echo "脚本下载完成$(($SECOND-$i))S后开始自动安装...." + sleep 1 +done + +USER_COUNT=10 +# 公共的工作目录 +PUBLIC_DIR="/shared" +# git仓库加速地址 +GIT_NO_VNC="$GIT_DOMAIN/crabapples/noVNC.git" +GIT_WEBSOCKIFY="$GIT_DOMAIN/crabapples/websockify.git" +GIT_PY_SPIDER="$GIT_DOMAIN/crabapples/learn-spider.git" + +./install.sh $USER_COUNT $PUBLIC_DIR $GIT_NO_VNC $GIT_WEBSOCKIFY $GIT_PY_SPIDER +echo "安装完成" + + diff --git a/bin/browser-vnc.sh b/bin_bak/browser-vnc.sh similarity index 58% rename from bin/browser-vnc.sh rename to bin_bak/browser-vnc.sh index c48edc9..bae44e5 100644 --- a/bin/browser-vnc.sh +++ b/bin_bak/browser-vnc.sh @@ -1,6 +1,5 @@ #!/bin/bash -# Browser VNC Manager - +# 浏览器 VNC 管理器 DAEMON_SCRIPT="$(dirname $0)/start-browser-vnc.sh" BASE_VNC_PORT=5900 @@ -14,12 +13,12 @@ start() { local data_dir="${BASE_DATA_DIR}/instance-${instance}" if [ -f "/tmp/chrome-browser-${display_num}.pid" ] && kill -0 $(cat "/tmp/chrome-browser-${display_num}.pid") 2>/dev/null; then - echo "Instance ${instance} already running" + echo "实例 ${instance} 已在运行中" return 1 fi - echo "Starting instance ${instance} (display :${display_num}, VNC port: ${vnc_port})..." - echo "Data directory: ${data_dir}" + echo "正在启动实例 ${instance} (显示编号 :${display_num}, VNC 端口: ${vnc_port})..." + echo "数据目录: ${data_dir}" # 确保数据目录存在 mkdir -p "$data_dir" @@ -28,10 +27,10 @@ start() { sleep 3 local public_ip=$(hostname -I | awk '{print $1}') - echo "✅ Instance ${instance} started" + echo "✅ 实例 ${instance} 已启动" echo " VNC: ${public_ip}:${vnc_port}" - echo " Password: ${vnc_pwd}" - echo " Data: ${data_dir} (will be deleted on stop)" + echo " 密码: ${vnc_pwd}" + echo " 数据: ${data_dir} (停止时将删除)" } stop() { @@ -43,7 +42,7 @@ stop() { if [ -f "$pid_file" ]; then local pid=$(cat "$pid_file") - echo "Stopping instance ${instance} (PID: $pid)..." + echo "正在停止实例 ${instance} (PID: $pid)..." # 先发送 TERM 信号,让浏览器优雅关闭 kill -TERM $pid 2>/dev/null @@ -51,7 +50,7 @@ stop() { # 如果还在运行,强制杀死 if kill -0 $pid 2>/dev/null; then - echo "Force killing..." + echo "正在强制终止..." kill -9 $pid 2>/dev/null fi @@ -60,17 +59,17 @@ stop() { # 删除用户数据目录 if [ "$delete_data" = "true" ] && [ -d "$data_dir" ]; then - echo "Deleting user data: ${data_dir}" + echo "正在删除用户数据: ${data_dir}" rm -rf "$data_dir" fi - echo "✅ Instance ${instance} stopped" + echo "✅ 实例 ${instance} 已停止" else - echo "Instance ${instance} not running, cleaning up data..." + echo "实例 ${instance} 未运行,正在清理数据..." # 即使进程不在运行,也清理数据目录 if [ -d "$data_dir" ] && [ "$delete_data" = "true" ]; then rm -rf "$data_dir" - echo "✅ Data cleaned: ${data_dir}" + echo "✅ 数据已清理: ${data_dir}" fi fi @@ -84,7 +83,7 @@ stop() { } status() { - echo "=== Browser Instances (VNC only) ===" + echo "=== 浏览器实例状态 (仅 VNC) ===" for i in {1..10}; do local display_num=$((98 + i)) local pid_file="/tmp/chrome-browser-${display_num}.pid" @@ -92,19 +91,19 @@ status() { if [ -f "$pid_file" ] && kill -0 $(cat "$pid_file") 2>/dev/null; then local pid=$(cat "$pid_file") - echo "Instance ${i}: ✅ Running (VNC: $((5900 + i - 1)), PID: ${pid}, Data: ${data_dir})" + echo "实例 ${i}: ✅ 运行中 (VNC 端口: $((5900 + i - 1)), PID: ${pid}, 数据: ${data_dir})" else - echo "Instance ${i}: ❌ Stopped" + echo "实例 ${i}: ❌ 已停止" fi done } stop_all() { - echo "Stopping all instances..." + echo "正在停止所有实例..." for i in {1..10}; do stop $i "${1:-true}" done - echo "✅ All instances stopped" + echo "✅ 所有实例已停止" } cleanup_data() { @@ -116,23 +115,23 @@ cleanup_data() { # 如果还有残留数据,强制删除 if [ -d "$data_dir" ]; then - echo "Force cleaning data: ${data_dir}" + echo "正在强制清理数据: ${data_dir}" rm -rf "$data_dir" fi - echo "✅ Data for instance ${instance} cleaned" + echo "✅ 实例 ${instance} 的数据已清理" } cleanup_all_data() { - echo "Cleaning all instance data..." + echo "正在清理所有实例数据..." for i in {1..10}; do local data_dir="${BASE_DATA_DIR}/instance-${i}" if [ -d "$data_dir" ]; then rm -rf "$data_dir" - echo " Removed: ${data_dir}" + echo " 已删除: ${data_dir}" fi done - echo "✅ All data cleaned" + echo "✅ 所有数据已清理" } case "${1:-}" in @@ -161,25 +160,25 @@ case "${1:-}" in cleanup_all_data ;; *) - echo "Usage: $0 {start|stop|stop-keep-data|stop-all|stop-all-keep-data|status|cleanup|cleanup-all} [instance_num] [password]" + echo "用法: $0 {start|stop|stop-keep-data|stop-all|stop-all-keep-data|status|cleanup|cleanup-all} [实例编号] [密码]" echo "" - echo "Commands:" - echo " start [num] [pwd] - Start instance (default: 1, 123456)" - echo " stop [num] - Stop and DELETE user data" - echo " stop-keep-data [num] - Stop but KEEP user data" - echo " stop-all - Stop all and DELETE all user data" - echo " stop-all-keep-data - Stop all but KEEP user data" - echo " status - Show all instances status" - echo " cleanup [num] - Force delete user data for instance" - echo " cleanup-all - Force delete all user data" + echo "命令说明:" + echo " start [编号] [密码] - 启动实例 (默认: 1, 123456)" + echo " stop [编号] - 停止并删除用户数据" + echo " stop-keep-data [编号] - 停止但保留用户数据" + echo " stop-all - 停止所有实例并删除所有数据" + echo " stop-all-keep-data - 停止所有实例但保留所有数据" + echo " status - 显示所有实例状态" + echo " cleanup [编号] - 强制删除指定实例的用户数据" + echo " cleanup-all - 强制删除所有实例的用户数据" echo "" - echo "Examples:" - echo " $0 start 1 # Start instance 1 (VNC:5900, password:123456)" - echo " $0 start 2 mypass # Start instance 2 (VNC:5901, password:mypass)" - echo " $0 stop 1 # Stop instance 1 and DELETE its data" - echo " $0 stop-keep-data 1 # Stop instance 1 but KEEP its data" - echo " $0 status # Show all instances" - echo " $0 cleanup-all # Delete all instance data" + echo "使用示例:" + echo " $0 start 1 # 启动实例 1 (VNC:5900, 密码:123456)" + echo " $0 start 2 mypass # 启动实例 2 (VNC:5901, 密码:mypass)" + echo " $0 stop 1 # 停止实例 1 并删除其数据" + echo " $0 stop-keep-data 1 # 停止实例 1 但保留其数据" + echo " $0 status # 显示所有实例状态" + echo " $0 cleanup-all # 删除所有实例数据" exit 1 ;; esac \ No newline at end of file diff --git a/bin/close_vnc_server.sh b/bin_bak/close_vnc_server.sh similarity index 100% rename from bin/close_vnc_server.sh rename to bin_bak/close_vnc_server.sh diff --git a/bin_bak/install.sh b/bin_bak/install.sh new file mode 100644 index 0000000..aa5d498 --- /dev/null +++ b/bin_bak/install.sh @@ -0,0 +1,244 @@ +#!/bin/bash + +# ==================== 配置变量 ==================== +# 需要创建的用户数量 +USER_COUNT=${1:-10} +# 公共的工作目录 +PUBLIC_DIR="${2:-/shared}" +# git仓库地址 +GIT_NO_VNC="${3:-https://github.com/novnc/noVNC.git}" +GIT_WEBSOCKIFY="${4:-https://github.com/novnc/websockify.git}" +GIT_PY_SPIDER="${5:-https://git.nps.crabapples.cn/crabapples/learn-spider.git}" + +echo "USER_COUNT: $USER_COUNT" +echo "PUBLIC_DIR: $PUBLIC_DIR" +echo "GIT_NO_VNC: $GIT_NO_VNC" +echo "GIT_WEBSOCKIFY: $GIT_WEBSOCKIFY" +echo "GIT_PY_SPIDER: $GIT_PY_SPIDER" +echo "" +sleep 3 +# ==================== 函数定义 ==================== + +# 1. 预安装软件 +pre_install(){ + echo "当前进度:01.预安装软件" + sudo apt update + sudo apt install -y xfce4 xfce4-goodies + sudo apt install -y tigervnc-standalone-server tigervnc-common + sudo apt install -y git python3 openssl + echo "✅ [完成] 预安装软件" + echo "" +} + +# 2. 开放防火墙 +open_firewall(){ + echo "当前进度:02.开放防火墙" + sudo ufw allow 6080/tcp + echo "✅ [完成] 开放防火墙端口 6080" + echo "" +} + +# 3. 批量创建用户 +batch_create_user(){ + echo "当前进度:03.创建用户" + for i in $(seq 1 $USER_COUNT); do + username="user$i" + sudo useradd -m -s /bin/bash "$username" + echo "$username:$username" | sudo chpasswd + echo " ✓ 用户 $username 创建完成,密码: $username" + done + echo "✅ [完成] 共创建 $USER_COUNT 个用户" + echo "" +} + +# 4. 批量设置VNC密码 +batch_set_vncpdw(){ + echo "当前进度:04.设置VNC密码" + for i in $(seq 1 $USER_COUNT); do + username="user$i" + sudo su - "$username" -c "mkdir -p ~/.vnc && echo '$username' | vncpasswd -f > ~/.vnc/passwd && chmod 600 ~/.vnc/passwd" + echo " ✓ 正在设置 $username 的VNC密码" + done + echo "✅ [完成] 所有VNC密码设置完成" + echo "" +} + +# 5. 创建 VNC 启动配置 +create_xstartup(){ + echo "当前进度:05.创建VNC启动配置" + for i in $(seq 1 ${USER_COUNT}); do + username="user$i" + sudo bash -c "cat > /home/$username/.vnc/xstartup << 'EOF' +#!/bin/sh +unset SESSION_MANAGER +unset DBUS_SESSION_BUS_ADDRESS +startxfce4 & +wait +EOF" + sudo chmod +x "/home/$username/.vnc/xstartup" + echo " ✓ 已创建 $username 的 xstartup" + done + echo "✅ [完成] 所有用户的 VNC 启动配置创建完成" + echo "" +} + +# 6. 创建公共目录 +cerate_public_dir(){ + echo "当前进度:06.创建公共工作目录" + sudo mkdir -p "$PUBLIC_DIR" + sudo chmod 777 "$PUBLIC_DIR" + sudo chmod +t "$PUBLIC_DIR" + sudo chown root:root "$PUBLIC_DIR" + echo " 公共文件夹已创建: $PUBLIC_DIR" + echo " 权限: $(ls -ld $PUBLIC_DIR)" + echo "✅ [完成] 公共工作目录创建完成" + echo "" +} + +# 7. 在每个用户home目录创建软连接 +create_shortcut_for_users(){ + echo "当前进度:07.创建公共工作目录软连接" + for i in $(seq 1 $USER_COUNT); do + username="user$i" + sudo su - "$username" -c "ln -sf $PUBLIC_DIR ~/workspace" + echo " ✓ 正在创建 $username 的快捷方式" + done + echo "✅ [完成] 所有用户的软连接创建完成" + echo "" +} + +# 8. 准备novnc文件 +install_vnc_server(){ + echo "当前进度:08.下载NoVNC源码" + cd $PUBLIC_DIR + sudo git clone $GIT_NO_VNC + sudo git clone $GIT_WEBSOCKIFY + echo "✅ [完成] NoVNC 源码下载完成" + echo "" +} + +# 9. 生成ssl证书 +create_ssl_cert(){ + echo "当前进度:09.生成SSL证书,有效期:10年" + cd $PUBLIC_DIR/noVNC + sudo openssl req -new -x509 -days 3650 -nodes \ + -out self.pem \ + -keyout self.pem \ + -subj "/C=CN/ST=Beijing/L=Beijing/O=Test/CN=localhost" + echo "✅ [完成] SSL证书生成完成" + echo "" +} + +# 10. 预安装爬虫 +pre_install_spider(){ + echo "当前进度:10.下载爬虫源码" + cd $PUBLIC_DIR + if [ -d "learn-spider" ]; then + cd learn-spider + sudo git pull + else + sudo git clone $GIT_PY_SPIDER + cd learn-spider + fi + sudo chmod +x ./pre_install_spider.sh + sudo chmod +x ./start_spider.sh + sudo ./pre_install_spider.sh + echo "✅ [完成] 爬虫环境安装完成" + echo "" +} + +# 11. 创建 token.conf 配置文件 +create_token_conf(){ + echo "当前进度:11.创建token.conf配置文件" + sudo tee "$PUBLIC_DIR/websockify/token.conf" > /dev/null << EOF +# noVNC Token Configuration +$(for i in $(seq 1 $USER_COUNT); do echo "user$i: 127.0.0.1:$((5900 + i))"; done) +EOF + sudo chmod 644 "$PUBLIC_DIR/websockify/token.conf" + echo " token.conf 内容预览:" + sudo cat "$PUBLIC_DIR/websockify/token.conf" | head -5 + echo "✅ [完成] token.conf 已创建" + echo "" +} + +# 12. 创建novnc服务文件 +create_novnc_service(){ + echo "当前进度:12.创建novnc.service服务配置文件" + WEB_ROOT="$PUBLIC_DIR/noVNC" + WORKSPACE="$PUBLIC_DIR/websockify" + TOKEN_FILE="$WORKSPACE/token.conf" + CERT_PATH="$WEB_ROOT/self.pem" + SERVICE_FILE="/etc/systemd/system/novnc.service" + + sudo tee "$SERVICE_FILE" > /dev/null << EOF +[Unit] +Description=noVNC WebSocket Proxy +After=network.target + +[Service] +Type=simple +User=root +WorkingDirectory=$WORKSPACE +ExecStart=$WORKSPACE/run \ + --web $WEB_ROOT \ + --target-config $TOKEN_FILE \ + --cert $CERT_PATH \ + 6080 +Restart=always +RestartSec=10 + +[Install] +WantedBy=multi-user.target +EOF + + sudo systemctl daemon-reload + sudo systemctl stop novnc 2>/dev/null + sudo systemctl enable novnc.service --now + + echo " 服务状态检查:" + sudo systemctl status novnc --no-pager | head -5 + echo "✅ [完成] noVNC 服务已创建并启动" + echo " 查看日志: sudo journalctl -u novnc -f" + echo " 访问地址: https://$(hostname -I | awk '{print $1}'):6080/vnc.html" + echo "" +} + +# 13. 显示部署总结 +show_summary(){ + echo "==================== 部署总结 ====================" + echo "✅ 用户数量: $USER_COUNT" + echo "✅ 公共目录: $PUBLIC_DIR" + echo "✅ VNC 端口范围: 5901-$((5900 + $USER_COUNT))" + echo "✅ noVNC 访问地址: https://$(hostname -I | awk '{print $1}'):6080/vnc.html" + echo "✅ Token 配置: user1-user$USER_COUNT 使用对应密码" + echo "" + echo "==================== 部署完成 ====================" +} + +# ==================== 主执行流程 ==================== + +main(){ + echo "" + echo "========== 开始部署 VNC 环境 ==========" + echo "开始时间: $(date '+%Y-%m-%d %H:%M:%S')" + echo "" + + pre_install + open_firewall + batch_create_user + batch_set_vncpdw + create_xstartup + cerate_public_dir + create_shortcut_for_users + install_vnc_server + create_ssl_cert + pre_install_spider + create_token_conf + create_novnc_service + show_summary + + echo "结束时间: $(date '+%Y-%m-%d %H:%M:%S')" + echo "" +} + +main \ No newline at end of file diff --git a/bin/start-browser-vnc.sh b/bin_bak/start-browser-vnc.sh similarity index 58% rename from bin/start-browser-vnc.sh rename to bin_bak/start-browser-vnc.sh index 56e76b2..2dbbdd5 100755 --- a/bin/start-browser-vnc.sh +++ b/bin_bak/start-browser-vnc.sh @@ -1,15 +1,15 @@ #!/bin/bash -# Browser VNC Daemon +# 浏览器 VNC 守护进程 set -euo pipefail TARGET_URL="https://www.baidu.com" -VNC_PWD=${1:-"123456"} -VNC_PORT=${2:-5900} -DISPLAY_NUM=${3:-99} +VNC_PWD=${1:-"123456"} # VNC 密码 +VNC_PORT=${2:-5900} # VNC 端口 +DISPLAY_NUM=${3:-99} # 显示编号 DATA_DIR=${4:-"/tmp/chrome-profile-${DISPLAY_NUM}"} # 用户数据目录 -PID_FILE="/tmp/chrome-browser-${DISPLAY_NUM}.pid" -LOG_FILE="/tmp/chrome-browser-${DISPLAY_NUM}.log" +PID_FILE="/tmp/chrome-browser-${DISPLAY_NUM}.pid" # 进程 ID 文件 +LOG_FILE="/tmp/chrome-browser-${DISPLAY_NUM}.log" # 日志文件 GREEN='\033[0;32m'; RED='\033[0;31m'; NC='\033[0m' log() { echo -e "${GREEN}[$(date '+%H:%M:%S')]${NC} $*"; } @@ -17,12 +17,12 @@ err() { echo -e "${RED}[$(date '+%H:%M:%S')]${NC} $*" >&2; } # 检查是否已在运行 if [ -f "$PID_FILE" ] && kill -0 $(cat "$PID_FILE") 2>/dev/null; then - err "Instance :${DISPLAY_NUM} already running" + err "实例 :${DISPLAY_NUM} 已在运行中" exit 1 fi cleanup() { - log "Cleaning up instance :${DISPLAY_NUM}..." + log "正在清理实例 :${DISPLAY_NUM}..." pkill -P $$ 2>/dev/null || true pkill -f "Xvfb :${DISPLAY_NUM}" 2>/dev/null || true pkill -f "x11vnc.*${DISPLAY_NUM}" 2>/dev/null || true @@ -30,21 +30,25 @@ cleanup() { } trap cleanup EXIT INT TERM +# 记录当前进程 ID echo $$ > "$PID_FILE" -log "Starting Xvfb :${DISPLAY_NUM}" +log "正在启动 Xvfb :${DISPLAY_NUM}" Xvfb :${DISPLAY_NUM} -screen 0 1280x1024x24 -nolisten tcp & sleep 2 export DISPLAY=:${DISPLAY_NUM} -log "Starting Chromium (data dir: ${DATA_DIR})" +log "正在启动 Chromium (数据目录: ${DATA_DIR})" mkdir -p "$DATA_DIR" + +# 查找 Chromium 可执行文件 CHROME_BIN=$(ls $HOME/.cache/ms-playwright/chromium-*/chrome-linux/chrome 2>/dev/null | head -1) if [ -z "$CHROME_BIN" ]; then CHROME_BIN=$(which chromium-browser 2>/dev/null || which google-chrome-stable 2>/dev/null || true) fi -[ -z "$CHROME_BIN" ] && { err "Chromium not found"; exit 1; } +[ -z "$CHROME_BIN" ] && { err "未找到 Chromium"; exit 1; } +# 启动 Chromium(非 headless 模式,可见界面) "$CHROME_BIN" \ --no-sandbox \ --disable-gpu \ @@ -55,16 +59,18 @@ fi CHROME_PID=$! -log "Starting x11vnc on port ${VNC_PORT}..." +log "正在启动 x11vnc,端口: ${VNC_PORT}..." x11vnc -display :${DISPLAY_NUM} -forever -shared -passwd ${VNC_PWD} -rfbport ${VNC_PORT} -bg & +# 获取服务器公网 IP PUBLIC_HOST=$(curl -sf ifconfig.me 2>/dev/null || hostname -I | awk '{print $1}') log "==========================================" -log "✅ Browser started (VNC only, no CDP)" -log "🖥️ VNC: ${PUBLIC_HOST}:${VNC_PORT}" -log "🔑 Password: ${VNC_PWD}" -log "📁 Data: ${DATA_DIR}" -log "🛑 Stop: ./browser-vnc.sh stop ${DISPLAY_NUM}" +log "✅ 浏览器已启动 (仅 VNC,无 CDP)" +log "🖥️ VNC 地址: ${PUBLIC_HOST}:${VNC_PORT}" +log "🔑 VNC 密码: ${VNC_PWD}" +log "📁 数据目录: ${DATA_DIR}" +log "🛑 停止命令: ./browser-vnc.sh stop ${DISPLAY_NUM}" log "==========================================" +# 等待 Chromium 进程结束 wait $CHROME_PID 2>/dev/null || true \ No newline at end of file diff --git a/bin_bak/start_spider.sh b/bin_bak/start_spider.sh new file mode 100644 index 0000000..96178d7 --- /dev/null +++ b/bin_bak/start_spider.sh @@ -0,0 +1,24 @@ +#!/bin/bash +spider_code=$1 +index=$2 +PUBLIC_DIR="${2:-/shared}" +export DISPLAY=:$index +echo "当前显示桌面:$DISPLAY" +start_spider(){ + echo "正在启动爬虫$spider_code" + if [ "$spider_code" = "01" ]; then + spider_name="china_net.py" + elif [ "$spider_code" = "02" ]; then + spider_name="mail_qq.py" + else + echo "爬虫名称错误" + exit -1 + fi + echo "爬虫名称:$spider_name" + .venv/bin/python3.12 "spider/$spider_name" +} + +echo "启动爬虫" +cd $PUBLIC_DIR/learn-spider +echo "$PUBLIC_DIR/learn-spider" +start_spider \ No newline at end of file diff --git a/bin/start_vnc_server.sh b/bin_bak/start_vnc_server.sh similarity index 100% rename from bin/start_vnc_server.sh rename to bin_bak/start_vnc_server.sh diff --git a/bin_bak/uninstall.sh b/bin_bak/uninstall.sh new file mode 100644 index 0000000..c18e2e9 --- /dev/null +++ b/bin_bak/uninstall.sh @@ -0,0 +1,40 @@ +#!/bin/bash +USER_COUNT="${1:-10}" +PUBLIC_DIR="${2:-/shared}" +SECOND="${3:-5}" + +batch_kill_process(){ + echo "正在结束进程" + for i in $(seq 1 $USER_COUNT); do + username="user$i" + # 杀死用户所有进程 + sudo pkill -u "$username" 2>/dev/null + done + echo "进程结束完成" +} + +batch_delete_user(){ + for i in $(seq 1 $USER_COUNT); do + username="user$i" + # 杀死用户所有进程 + sudo userdel -r "$username" 2>/dev/null + if [ $? -eq 0 ]; then + echo "已删除用户: $username" + else + echo "用户 $username 不存在,跳过" + fi + done + echo "批量删除完成!" +} + +for i in $(seq 1 $SECOND); do + clear + echo "$(($SECOND-$i))S后开始数据清理...." + sleep 1 +done + +batch_kill_process +sleep 5 +batch_delete_user +sudo rm -rf $PUBLIC_DIR +echo "数据清理完成" \ No newline at end of file diff --git a/bin_bak/update.sh b/bin_bak/update.sh new file mode 100644 index 0000000..88f257c --- /dev/null +++ b/bin_bak/update.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +PUBLIC_DIR="${1:-/shared}" +GIT_PY_SPIDER="${2:-https://git.nps.crabapples.cn}" +echo "开始更新爬虫" +cd $PUBLIC_DIR +if [ -d "learn-spider" ]; then + cd learn-spider + sudo git pull +else + sudo git clone $GIT_PY_SPIDER + cd learn-spider +fi + +sudo chmod +x ./pre_install_spider.sh ./start_spider.sh +sudo ./pre_install_spider.sh +echo "✅ [完成] 爬虫更新完成" \ No newline at end of file