Skip to content

plugin_registerApi

注册 HTTP API 端点,允许外部程序通过 HTTP 请求调用插件功能。

函数签名

javascript
plugin_registerApi(method: string, path: string, handler: (req, res) => void): void

参数说明

参数类型必填说明
methodstringHTTP 方法(GET、POST、PUT、DELETE 等)
pathstringAPI 路径(如 /api/myplugin
handlerfunction请求处理函数

参数详解

  • method:HTTP 请求方法,支持 GETPOSTPUTDELETEPATCH 等,不区分大小写

  • path:API 路径,必须以 / 开头,建议使用 /api/ 前缀以区分插件 API 和内置 API

  • handler:请求处理函数,接收两个参数:

    • req:Node.js 的 http.IncomingMessage 对象,包含请求信息
    • res:Node.js 的 http.ServerResponse 对象,用于发送响应

返回值

无返回值。

功能描述

plugin_registerApi 允许插件注册自定义的 HTTP API 端点。外部程序可以通过 HTTP 请求调用这些 API,实现远程控制、数据查询、Webhook 接收等功能。

所有插件注册的 API 都需要通过 Bearer Token 进行身份验证。Token 在 config.jsapi.token 中配置,请求时需要在 Authorization 头中携带。

使用示例

基本用法

javascript
// 注册 GET 接口
plugin_registerApi("GET", "/api/hello", (req, res) => {
    res.writeHead(200, { "Content-Type": "application/json" });
    res.end(JSON.stringify({
        success: true,
        message: "Hello from MSL Plugin!"
    }));
});

// 注册 POST 接口
plugin_registerApi("POST", "/api/broadcast", (req, res) => {
    let body = "";
    req.on("data", chunk => body += chunk);
    req.on("end", () => {
        try {
            const data = JSON.parse(body);
            plugin_executeCommand(`say ${data.message}`);
            res.writeHead(200, { "Content-Type": "application/json" });
            res.end(JSON.stringify({ success: true }));
        } catch (e) {
            res.writeHead(400, { "Content-Type": "application/json" });
            res.end(JSON.stringify({ success: false, error: "Invalid JSON" }));
        }
    });
});

查询在线玩家

javascript
// GET /api/players - 查询在线玩家
plugin_registerApi("GET", "/api/players", (req, res) => {
    plugin_executeCommand("list", (lines) => {
        let playerInfo = null;
        lines.forEach(line => {
            const match = line.match(/There are (\d+) of a max of (\d+) players online: (.*)/);
            if (match) {
                playerInfo = {
                    online: parseInt(match[1]),
                    max: parseInt(match[2]),
                    players: match[3] ? match[3].split(", ") : []
                };
            }
        });
        
        res.writeHead(200, { "Content-Type": "application/json" });
        res.end(JSON.stringify({
            success: true,
            data: playerInfo
        }));
    });
});

执行服务器指令

javascript
// POST /api/execute - 执行服务器指令
plugin_registerApi("POST", "/api/execute", (req, res) => {
    let body = "";
    req.on("data", chunk => body += chunk);
    req.on("end", () => {
        try {
            const data = JSON.parse(body);
            
            if (!data.command) {
                res.writeHead(400, { "Content-Type": "application/json" });
                res.end(JSON.stringify({ success: false, error: "Missing command" }));
                return;
            }
            
            plugin_executeCommand(data.command, (lines) => {
                res.writeHead(200, { "Content-Type": "application/json" });
                res.end(JSON.stringify({
                    success: true,
                    response: lines
                }));
            });
        } catch (e) {
            res.writeHead(400, { "Content-Type": "application/json" });
            res.end(JSON.stringify({ success: false, error: "Invalid JSON" }));
        }
    });
});

玩家管理 API

javascript
// GET /api/player/:name - 查询玩家信息
plugin_registerApi("GET", "/api/player/info", (req, res) => {
    const url = new URL(req.url, `http://${req.headers.host}`);
    const player = url.searchParams.get("name");
    
    if (!player) {
        res.writeHead(400, { "Content-Type": "application/json" });
        res.end(JSON.stringify({ success: false, error: "Missing name parameter" }));
        return;
    }
    
    const uuid = plugin_generateOfflineUUID(player);
    const playerData = plugin_pull("playerData") || {};
    const data = playerData[uuid];
    
    res.writeHead(200, { "Content-Type": "application/json" });
    res.end(JSON.stringify({
        success: true,
        data: {
            name: player,
            uuid: uuid,
            stats: data || null
        }
    }));
});

// POST /api/player/kick - 踢出玩家
plugin_registerApi("POST", "/api/player/kick", (req, res) => {
    let body = "";
    req.on("data", chunk => body += chunk);
    req.on("end", () => {
        try {
            const data = JSON.parse(body);
            
            if (!data.player) {
                res.writeHead(400, { "Content-Type": "application/json" });
                res.end(JSON.stringify({ success: false, error: "Missing player" }));
                return;
            }
            
            plugin_executeCommand(`kick ${data.player} ${data.reason || "Kicked via API"}`);
            
            res.writeHead(200, { "Content-Type": "application/json" });
            res.end(JSON.stringify({
                success: true,
                message: `Player ${data.player} kicked`
            }));
        } catch (e) {
            res.writeHead(400, { "Content-Type": "application/json" });
            res.end(JSON.stringify({ success: false, error: "Invalid JSON" }));
        }
    });
});

Webhook 接收

javascript
// POST /api/webhook - 接收外部 Webhook
plugin_registerApi("POST", "/api/webhook", (req, res) => {
    let body = "";
    req.on("data", chunk => body += chunk);
    req.on("end", () => {
        try {
            const data = JSON.parse(body);
            
            plugin_log("INFO", `收到 Webhook: ${JSON.stringify(data)}`);
            
            // 处理 Webhook 数据
            if (data.type === "notification") {
                plugin_executeCommand(`say [通知] ${data.message}`);
            }
            
            res.writeHead(200, { "Content-Type": "application/json" });
            res.end(JSON.stringify({ success: true }));
        } catch (e) {
            res.writeHead(400, { "Content-Type": "application/json" });
            res.end(JSON.stringify({ success: false, error: "Invalid JSON" }));
        }
    });
});

服务器状态 API

javascript
// GET /api/status - 服务器状态
plugin_registerApi("GET", "/api/status", (req, res) => {
    const plugins = plugin_getPluginsList();
    const uptime = process.uptime();
    
    res.writeHead(200, { "Content-Type": "application/json" });
    res.end(JSON.stringify({
        success: true,
        data: {
            uptime: Math.floor(uptime),
            uptimeFormatted: formatUptime(uptime),
            plugins: {
                total: plugins.all.length,
                loaded: plugins.loaded.length
            }
        }
    }));
});

function formatUptime(seconds) {
    const days = Math.floor(seconds / 86400);
    const hours = Math.floor((seconds % 86400) / 3600);
    const minutes = Math.floor((seconds % 3600) / 60);
    return `${days}d ${hours}h ${minutes}m`;
}

身份验证

所有插件 API 都需要 Bearer Token 认证:

bash
# 请求示例
curl -H "Authorization: Bearer your-api-token" http://localhost:30600/api/hello

如果 Token 无效或缺失,会返回 401 错误:

json
{
    "success": false,
    "error": "Unauthorized: invalid or missing token"
}

错误处理

MSL 会自动捕获 API 处理函数中的错误:

javascript
plugin_registerApi("GET", "/api/test", (req, res) => {
    // 如果这里抛出错误,MSL 会捕获并返回 500 错误
    throw new Error("Test error");
});

返回:

json
{
    "success": false,
    "error": "Internal plugin error"
}

注意事项

  1. 路径唯一性:相同方法和路径的 API 只能注册一次
  2. 异步响应:如果需要异步操作,确保在操作完成后再调用 res.end()
  3. 请求体大小:注意处理大请求体,避免内存溢出
  4. CORS:如需跨域访问,需要手动设置 CORS 头

相关 API

相关指南