#!/bin/bash
========================================================
WordPress + Workerman WebSocket IM 一键部署脚本(Composer 版)
功能:自动创建插件目录、生成服务文件、前端测试页、Nginx 配置、systemd 服务
支持自动安装 Composer 和 workerman/workerman
使用:chmod +x install-wp-im.sh && ./install-wp-im.sh --domain yfw.szrengjing.com --site-root /www/wwwroot/yfw_szrengjing_com
========================================================
set -e # 出错即停止
============ 参数解析 ============
DOMAIN=“”
SITE_ROOT=“”
HELP=false
while [[ “$#” -gt 0 ]]; do
case $1 in
–domain) DOMAIN=“$2”; shift ;;
–site-root) SITE_ROOT=“$2”; shift ;;
-h|–help) HELP=true; break ;;
*) echo “未知参数: $1” >&2; exit 1 ;;
esac
shift
done
if [ “$HELP” = true ]; then
cat << EOF
用法: $0 --domain <域名> --site-root <网站根目录>
示例:
$0 --domain yfw.szrengjing.com --site-root /www/wwwroot/yfw_szrengjing_com
功能:
创建 WordPress 插件目录
自动生成 im-server.php (Workerman)
使用 Composer 安装 workerman/workerman
生成前端测试页面 websocket-test.html
生成 systemd 开机自启服务
提供 Nginx 反向代理配置片段
EOF
exit 0
fi
if [ -z “$DOMAIN” ] || [ -z “$SITE_ROOT” ]; then
echo “❌ 错误: 必须指定 --domain 和 --site-root”
echo “运行 $0 --help 查看帮助”
exit 1
fi
============ 变量定义 ============
PLUGIN_NAME=“wp-im-plugin”
PLUGIN_DIR=“$SITE_ROOT/wp-content/plugins/$PLUGIN_NAME”
IM_SERVER=“$PLUGIN_DIR/im-server.php”
WP_PLUGIN_MAIN=“$PLUGIN_DIR/$PLUGIN_NAME.php”
FRONTEND_TEST=“
�
�
�
�
�
�
�
�
/
�
�
�
�
�
�
�
�
�
−
�
�
�
�
.
ℎ
�
�
�
"
�
�
�
�
�
�
�
�
�
=
"
/
�
�
�
/
�
�
�
�
�
�
/
�
�
�
�
�
/
�
ℎ
�
�
�
/
�
�
�
�
�
/
SITE
R
OOT/websocket−test.html"NGINX
C
ONF="/www/server/panel/vhost/nginx/{DOMAIN}.conf”
SYSTEMD_SERVICE=“/etc/systemd/system/wp-im-server.service”
LOG_FILE=“/var/log/wp-im-server.log”
WORKER_USER=“www” # 宝塔默认 PHP 运行用户
echo “🚀 开始部署 WebSocket IM 系统”
echo “🌐 域名: $DOMAIN”
echo “📁 网站根目录: $SITE_ROOT”
echo “🔌 插件路径: $PLUGIN_DIR”
============ 1. 创建插件目录 ============
echo “📁 创建插件目录…”
mkdir -p “$PLUGIN_DIR”
============ 2. 生成 im-server.php (基于 Composer Autoload) ============
cat > “$IM_SERVER” << ‘EOF’
#!/usr/bin/env php
<?php /** * Workerman WebSocket Server for WordPress IM * 使用 Composer 自动加载 */ use Workerman\Worker; use Workerman\Connection\TcpConnection; // 引入 Composer 自动加载器 require_once __DIR__ . '/vendor/autoload.php'; $worker = new Worker('websocket://0.0.0.0:2121'); $worker->name = 'WP_IM_Server'; $worker->count = 1; $worker->user = 'www'; $worker->onWorkerStart = function () { file_put_contents('/tmp/workerman_stdout.log', "✅ IM Server started at " . date('Y-m-d H:i:s') . "\n", FILE_APPEND); }; $worker->onConnect = function (TcpConnection $conn) { $conn->userId = null; $conn->userName = null; file_put_contents('/tmp/workerman_stdout.log', "🔗 新连接来自 {$conn->getRemoteIp()}:{$conn->getRemotePort()}\n", FILE_APPEND); }; $worker->onMessage = function (TcpConnection $conn, $data) use ($worker) { try { $msg = json_decode($data, true, 512, JSON_THROW_ON_ERROR); if ($msg['type'] === 'register') { $userId = $msg['userId'] ?? ''; $userName = htmlspecialchars($msg['userName'] ?? ''); if (!is_numeric($userId) || intval($userId) <= 0) { $conn->send(json_encode(['type' => 'error', 'message' => '非法用户ID'])); $conn->close(); return; } $conn->userId = $userId; $conn->userName = $userName; // 广播上线消息(除自己外) $online_msg = json_encode([ 'type' => 'user_online', 'userId' => $userId, 'userName' => $userName, 'time' => date('H:i') ]); foreach ($worker->connections as $c) { if ($c !== $conn) { $c->send($online_msg); } } // 欢迎自己 $conn->send(json_encode([ 'type' => 'welcome', 'message' => "欢迎回来,{$userName}" ])); file_put_contents('/tmp/workerman_stdout.log', "👤 用户 {$userId}({$userName}) 上线\n", FILE_APPEND); } if ($msg['type'] === 'chat') { $content = htmlspecialchars($msg['content'] ?? ''); if (empty($content)) return; $fromName = $conn->userName ?: '匿名'; $fromId = $conn->userId ?: '0'; $chat_msg = json_encode([ 'type' => 'chat_message', 'fromUserId' => $fromId, 'fromUserName' => $fromName, 'content' => $content, 'time' => date('H:i') ]); foreach ($worker->connections as $c) { $c->send($chat_msg); } file_put_contents('/tmp/workerman_stdout.log', "💬 聊天: [{$fromName}] {$content}\n", FILE_APPEND); } } catch (Exception $e) { file_put_contents('/tmp/workerman_stdout.log', "⚠️ 消息解析失败: {$e->getMessage()}\n", FILE_APPEND); } }; $worker->onClose = function (TcpConnection $conn) use ($worker) { if ($conn->userId && $conn->userName) { $offline_msg = json_encode([ 'type' => 'user_offline', 'userId' => $conn->userId, 'userName' => $conn->userName, 'time' => date('H:i') ]); foreach ($worker->connections as $c) { if ($c !== $conn) { $c->send($offline_msg); } } file_put_contents('/tmp/workerman_stdout.log', "👋 用户 {$conn->userId}({$conn->userName}) 下线\n", FILE_APPEND); } }; // 启动服务 if (basename(__FILE__) == 'im-server.php') { Worker::runAll(); } EOF # 给 im-server.php 添加执行权限 chmod +x "$IM_SERVER" echo "✅ 已生成并授权 im-server.php" # ============ 3. 使用 Composer 安装 Workerman ============ echo "📦 初始化 Composer 并安装 workerman/workerman..." cd "$PLUGIN_DIR" # 如果没有 composer.json,则创建 if [ ! -f "composer.json" ]; then cat > composer.json << 'EOF' { "name": "your-company/wp-im-plugin", "description": "WebSocket IM plugin for WordPress", "type": "project", "require": { "workerman/workerman": "^4.1" }, "config": { "allow-plugins": true } } EOF echo "📄 已创建 composer.json" else echo "📄 已存在 composer.json,跳过创建" fi # 检查是否已安装 vendor if [ ! -d "vendor" ] || [ ! -f "vendor/autoload.php" ]; then # 检查 composer 是否存在 if ! command -v composer &> /dev/null; then echo "⬇️ Composer 未安装,正在全局安装..." curl -sS https://getcomposer.org/installer | php mv composer.phar /usr/local/bin/composer echo "✅ Composer 已安装到 /usr/local/bin/composer" fi echo "⬇️ 正在执行 composer install..." COMPOSER_PROCESS_TIMEOUT=300 composer install --no-dev --optimize-autoloader else echo "✅ vendor 目录已存在,跳过 Composer 安装" fi echo "✅ Workerman 依赖通过 Composer 成功安装" # ============ 4. 生成 WordPress 主插件文件 ============ cat > "$WP_PLUGIN_MAIN" << EOF <?php /** * Plugin Name: WP IM Plugin * Description: 实时聊天系统,集成 Workerman WebSocket * Version: 1.0 * Author: Admin */ // 阻止直接访问 if (!defined('ABSPATH')) exit; // 前端注入当前用户信息 function wp_im_get_current_user_json() { if (is_user_logged_in()) { \$user = wp_get_current_user(); echo '<script>window.WP_IM_USER = {id: "' . esc_js(\$user->ID) . '", name: "' . esc_js(\$user->display_name) . '"};</script>'; } else { echo '<script>window.WP_IM_USER = null;</script>'; } } add_action('wp_head', 'wp_im_get_current_user_json'); EOF echo "✅ 已生成 WordPress 插件主文件: $WP_PLUGIN_MAIN" # ============ 5. 生成前端测试页面 ============ cat > "$FRONTEND_TEST" << EOF <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8" /> <title>WebSocket IM 测试</title> <style> body { font-family: Arial, sans-serif; padding: 20px; } #logs { list-style: none; padding: 0; max-height: 400px; overflow-y: auto; border: 1px solid #ccc; padding: 10px; } li { margin: 5px 0; color: #333; } .sent { color: #0073aa; } .received { color: #51a351; } </style> </head> <body> <h1>WebSocket IM 测试</h1> <div id="status">状态:尝试连接...</div> <ul id="logs"></ul> <div style="margin-top: 20px;"> <input type="text" id="msgInput" placeholder="输入消息" style="width: 300px; padding: 5px;" /> <button onclick="sendMsg()">发送</button> </div> <script> const WS_URL = "wss://$DOMAIN/im-ws"; const ws = new WebSocket(WS_URL); let userId = "guest_" + Date.now(); let userName = "游客"; function log(msg, cls = "") { const li = document.createElement("li"); li.className = cls; li.textContent = new Date().toLocaleTimeString() + " - " + msg; document.getElementById("logs").appendChild(li); console.log(msg); } ws.onopen = () => { log("✅ 连接成功!"); document.getElementById("status").textContent = "✅ 已连接"; if (typeof WP_IM_USER !== "undefined" && WP_IM_USER) { userId = WP_IM_USER.id; userName = WP_IM_USER.name; } else { userId = "test_user_" + Date.now(); userName = "测试用户"; } ws.send(JSON.stringify({ type: "register", userId: userId, userName: userName })); }; ws.onmessage = (e) => { const data = JSON.parse(e.data); if (data.type === 'user_online') { log("🟢 " + data.userName + " 上线了", "received"); } else if (data.type === 'user_offline') { log("🔴 " + data.userName + " 下线了", "received"); } else if (data.type === 'chat_message') { log("💬 [" + data.time + "] " + data.fromUserName + ": " + data.content, "received"); } else if (data.type === 'welcome') { log("👋 " + data.message, "received"); } }; ws.onerror = (e) => { log("❌ 错误: " + JSON.stringify(e)); document.getElementById("status").textContent = "❌ 连接失败"; }; ws.onclose = (e) => { log(\`⚠️ 连接关闭: \${e.code} \${e.reason}\`); document.getElementById("status").textContent = "⚠️ 已断开"; }; function sendMsg() { const input = document.getElementById("msgInput"); const val = input.value.trim(); if (val) { ws.send(JSON.stringify({ type: "chat", content: val })); log("你: " + val, "sent"); input.value = ""; } } document.getElementById("msgInput").addEventListener("keypress", (e) => { if (e.key === "Enter") sendMsg(); }); </script> </body> </html> EOF echo "✅ 已生成前端测试页面: $FRONTEND_TEST" # ============ 6. 生成 Nginx 配置建议 ============ cat << 'EOF' 📌【重要】请将以下 Nginx 配置添加到你的站点配置中: server { # ... 其他配置 ... location /im-ws { proxy_pass http://127.0.0.1:2121; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_buffering off; proxy_read_timeout 86400s; proxy_send_timeout 86400s; } # ... 其他配置 ... } 📌 保存后重启 Nginx: systemctl restart nginx EOF # ============ 7. 生成 systemd 服务 ============ cat > "$SYSTEMD_SERVICE" << EOF [Unit] Description=WordPress IM WebSocket Server After=network.target [Service] Type=simple User=$WORKER_USER Group=$WORKER_USER WorkingDirectory=$PLUGIN_DIR ExecStart=/usr/bin/php im-server.php start ExecStop=/usr/bin/php im-server.php stop Restart=always StandardOutput=append:$LOG_FILE StandardError=append:$LOG_FILE [Install] WantedBy=multi-user.target EOF echo "✅ 已生成 systemd 服务: $SYSTEMD_SERVICE" echo "🔧 启用命令:" echo " sudo systemctl daemon-reexec" echo " sudo systemctl enable wp-im-server" echo " sudo systemctl start wp-im-server" # ============ 8. 创建日志文件并设置权限 ============ touch "$LOG_FILE" chown "$WORKER_USER:$WORKER_USER" "$LOG_FILE" chmod 644 "$LOG_FILE" echo "✅ 日志文件已创建: $LOG_FILE" # ============ 9. 设置插件目录权限 ============ chown -R "$WORKER_USER:$WORKER_USER" "$PLUGIN_DIR" echo "✅ 插件目录权限已设置为 $WORKER_USER" # ============ 10. 完成提示 ============ cat << EOF 🎉 ========================================= WebSocket IM 系统部署完成! 你可以进行下一步操作: ========================================== 1️⃣ 🔧 编辑 Nginx 配置: nano $NGINX_CONF 将上面的 location /im-ws {...} 块粘贴进 server {} 中 2️⃣ 🔄 重启 Nginx: systemctl restart nginx 3️⃣ ▶️ 启动 WebSocket 服务: sudo systemctl start wp-im-server 4️⃣ 🔍 查看服务状态: sudo systemctl status wp-im-server 5️⃣ 📂 查看运行日志: tail -f /tmp/workerman_stdout.log tail -f $LOG_FILE 6️⃣ 🌐 访问测试页面: https://$DOMAIN/websocket-test.html 💡 注意事项: - 确保服务器防火墙/安全组开放 443 和 2121 端口(2121 可不对外暴露) - 若修改了 im-server.php,需重启服务生效 - 生产环境建议增加 JWT 或 Cookie 校验身份 💪 你现在可以实现:用户登录 → 自动注册 WebSocket → 实时群聊! EOF 根据历史会话 脚本sh 全面升级im全部完整版功能源码配 套文件 前端后端一次性自动化部署配置
最新发布