GameServerManager/server/data/plugins/example-plugin/gsm3-api.js
2025-07-12 11:02:22 +08:00

607 lines
No EOL
15 KiB
JavaScript
Raw Permalink 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.

/**
* GSM3 插件API客户端
* 提供插件与主面板通信的接口
*/
class GSM3API {
constructor() {
this.baseURL = '/api/plugin-api'
this.token = null
this.initializeToken()
}
/**
* 初始化token获取机制
*/
initializeToken() {
try {
// 检查是否已经通过脚本注入设置了全局token
if (window.gsm3Token) {
this.token = window.gsm3Token
console.log('Token已从全局变量获取:', this.token)
return
}
// 检查是否已经通过脚本注入设置了token
if (window.gsm3 && window.gsm3.token) {
this.token = window.gsm3.token
console.log('Token已从注入脚本获取')
return
}
// 尝试从父窗口获取token
if (window.parent && window.parent !== window) {
window.parent.postMessage({ type: 'gsm3-get-token' }, '*')
}
console.log('正在初始化token...')
} catch (error) {
console.warn('Token初始化失败:', error)
}
// 监听token更新
window.addEventListener('message', (event) => {
if (event.data && event.data.type === 'gsm3-token-update') {
this.token = event.data.token
console.log('通过消息更新Token:', this.token)
}
})
// 定期检查全局token变量
const checkGlobalToken = () => {
if (!this.token && window.gsm3Token) {
this.token = window.gsm3Token
console.log('从全局变量延迟获取Token:', this.token)
}
}
// 每100ms检查一次最多检查50次5秒
let checkCount = 0
const tokenChecker = setInterval(() => {
checkGlobalToken()
checkCount++
if (this.token || checkCount >= 50) {
clearInterval(tokenChecker)
}
}, 100)
}
/**
* 发送HTTP请求
*/
async request(endpoint, options = {}) {
const url = `${this.baseURL}${endpoint}`
const config = {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'X-Plugin-Request': 'true', // 添加插件标识
...(this.token && { 'Authorization': `Bearer ${this.token}` })
},
...options
}
if (config.body && typeof config.body === 'object') {
config.body = JSON.stringify(config.body)
}
try {
const response = await fetch(url, config)
const data = await response.json()
if (!response.ok) {
throw new Error(data.message || `HTTP ${response.status}`)
}
return data
} catch (error) {
console.error('API请求失败:', error)
throw error
}
}
// ==================== 系统信息API ====================
/**
* 获取系统状态
*/
async getSystemStatus() {
return await this.request('/system/status')
}
/**
* 获取系统信息
*/
async getSystemInfo() {
return await this.request('/system/info')
}
// ==================== 实例管理API ====================
/**
* 获取所有实例列表
*/
async getInstances() {
return await this.request('/instances')
}
/**
* 获取单个实例信息
* @param {string} instanceId 实例ID
*/
async getInstance(instanceId) {
return await this.request(`/instances/${instanceId}`)
}
/**
* 获取实例状态
* @param {string} instanceId 实例ID
*/
async getInstanceStatus(instanceId) {
return await this.request(`/instances/${instanceId}/status`)
}
/**
* 创建新实例
* @param {Object} instanceData 实例数据
* @param {string} instanceData.name 实例名称
* @param {string} instanceData.description 实例描述
* @param {string} instanceData.workingDirectory 工作目录
* @param {string} instanceData.startCommand 启动命令
* @param {boolean} instanceData.autoStart 是否自动启动
* @param {string} instanceData.stopCommand 停止命令
*/
async createInstance(instanceData) {
return await this.request('/instances', {
method: 'POST',
body: instanceData
})
}
/**
* 更新实例
* @param {string} instanceId 实例ID
* @param {Object} instanceData 实例数据
*/
async updateInstance(instanceId, instanceData) {
return await this.request(`/instances/${instanceId}`, {
method: 'PUT',
body: instanceData
})
}
/**
* 删除实例
* @param {string} instanceId 实例ID
*/
async deleteInstance(instanceId) {
return await this.request(`/instances/${instanceId}`, {
method: 'DELETE'
})
}
/**
* 启动实例
* @param {string} instanceId 实例ID
*/
async startInstance(instanceId) {
return await this.request(`/instances/${instanceId}/start`, {
method: 'POST'
})
}
/**
* 停止实例
* @param {string} instanceId 实例ID
*/
async stopInstance(instanceId) {
return await this.request(`/instances/${instanceId}/stop`, {
method: 'POST'
})
}
/**
* 重启实例
* @param {string} instanceId 实例ID
*/
async restartInstance(instanceId) {
return await this.request(`/instances/${instanceId}/restart`, {
method: 'POST'
})
}
/**
* 获取实例市场列表
*/
async getMarketInstances() {
return await this.request('/instances/market')
}
// ==================== 终端管理API ====================
/**
* 获取终端会话列表
*/
async getTerminals() {
return await this.request('/terminals')
}
/**
* 获取终端会话统计信息
*/
async getTerminalStats() {
return await this.request('/terminals/stats', {
method: 'GET'
})
}
/**
* 获取终端会话详细信息
*/
async getTerminalSessions() {
return await this.request('/terminals/sessions', {
method: 'GET'
})
}
/**
* 获取活跃终端进程信息
*/
async getActiveTerminalProcesses() {
return await this.request('/terminals/active-processes', {
method: 'GET'
})
}
/**
* 更新终端会话名称
* @param {string} sessionId 会话ID
* @param {string} name 新的会话名称
*/
async updateTerminalSessionName(sessionId, name) {
return await this.request(`/terminals/sessions/${sessionId}/name`, {
method: 'PUT',
body: { name }
})
}
/**
* 验证终端配置
* @param {string} workingDirectory 工作目录
* @param {string} shell Shell程序路径
*/
async validateTerminalConfig(workingDirectory, shell) {
return await this.request('/terminals/validate-config', {
method: 'POST',
body: { workingDirectory, shell }
})
}
/**
* 获取系统默认Shell信息
*/
async getDefaultShell() {
return await this.request('/terminals/default-shell', {
method: 'GET'
})
}
/**
* 获取终端主题配置
*/
async getTerminalThemes() {
return await this.request('/terminals/themes', {
method: 'GET'
})
}
/**
* 获取终端字体配置
*/
async getTerminalFonts() {
return await this.request('/terminals/fonts', {
method: 'GET'
})
}
/**
* 测试终端连接
* @param {string} workingDirectory 工作目录
*/
async testTerminalConnection(workingDirectory) {
return await this.request('/terminals/test-connection', {
method: 'POST',
body: { workingDirectory }
})
}
// ==================== 游戏管理API ====================
/**
* 获取游戏列表
*/
async getGames() {
return await this.request('/games')
}
// ==================== 文件操作API ====================
/**
* 读取文件内容
* @param {string} filePath 文件路径相对于服务器data目录
* @param {string} encoding 文件编码,默认为'utf-8',二进制文件使用'binary'
*/
async readFile(filePath, encoding = 'utf-8') {
return await this.request('/files/read', {
method: 'POST',
body: { filePath, encoding }
})
}
/**
* 写入文件内容
* @param {string} filePath 文件路径相对于服务器data目录
* @param {string} content 文件内容
* @param {string} encoding 文件编码,默认为'utf-8'
*/
async writeFile(filePath, content, encoding = 'utf-8') {
return await this.request('/files/write', {
method: 'POST',
body: { filePath, content, encoding }
})
}
/**
* 删除文件
* @param {string} filePath 文件路径相对于服务器data目录
*/
async deleteFile(filePath) {
return await this.request('/files/delete', {
method: 'DELETE',
body: { filePath }
})
}
/**
* 创建目录
* @param {string} dirPath 目录路径相对于服务器data目录
* @param {boolean} recursive 是否递归创建父目录默认为true
*/
async createDirectory(dirPath, recursive = true) {
return await this.request('/files/mkdir', {
method: 'POST',
body: { dirPath, recursive }
})
}
/**
* 删除目录
* @param {string} dirPath 目录路径相对于服务器data目录
* @param {boolean} recursive 是否递归删除默认为false
*/
async deleteDirectory(dirPath, recursive = false) {
return await this.request('/files/rmdir', {
method: 'DELETE',
body: { dirPath, recursive }
})
}
/**
* 列出目录内容
* @param {string} dirPath 目录路径相对于服务器data目录默认为根目录
* @param {boolean} includeHidden 是否包含隐藏文件默认为false
*/
async listDirectory(dirPath = '', includeHidden = false) {
return await this.request('/files/list', {
method: 'POST',
body: { dirPath, includeHidden }
})
}
/**
* 获取文件或目录信息
* @param {string} path 文件或目录路径相对于服务器data目录
*/
async getFileInfo(path) {
return await this.request('/files/info', {
method: 'POST',
body: { path }
})
}
/**
* 检查文件或目录是否存在
* @param {string} path 文件或目录路径相对于服务器data目录
*/
async exists(path) {
return await this.request('/files/exists', {
method: 'POST',
body: { path }
})
}
/**
* 复制文件或目录
* @param {string} sourcePath 源路径相对于服务器data目录
* @param {string} destPath 目标路径相对于服务器data目录
* @param {boolean} overwrite 是否覆盖已存在的文件默认为false
*/
async copy(sourcePath, destPath, overwrite = false) {
return await this.request('/files/copy', {
method: 'POST',
body: { sourcePath, destPath, overwrite }
})
}
/**
* 移动/重命名文件或目录
* @param {string} sourcePath 源路径相对于服务器data目录
* @param {string} destPath 目标路径相对于服务器data目录
* @param {boolean} overwrite 是否覆盖已存在的文件默认为false
*/
async move(sourcePath, destPath, overwrite = false) {
return await this.request('/files/move', {
method: 'POST',
body: { sourcePath, destPath, overwrite }
})
}
/**
* 搜索文件
* @param {string} pattern 搜索模式(支持通配符)
* @param {string} searchPath 搜索路径相对于服务器data目录默认为根目录
* @param {boolean} recursive 是否递归搜索子目录默认为true
*/
async searchFiles(pattern, searchPath = '', recursive = true) {
return await this.request('/files/search', {
method: 'POST',
body: { pattern, searchPath, recursive }
})
}
// ==================== 通用API ====================
/**
* 获取API版本信息
*/
async getVersion() {
return await this.request('/version')
}
/**
* 健康检查
*/
async healthCheck() {
return await this.request('/health')
}
// ==================== 工具方法 ====================
/**
* 显示通知消息(如果主面板支持)
* @param {string} type 消息类型: 'info', 'success', 'warning', 'error'
* @param {string} message 消息内容
*/
showNotification(type, message) {
try {
// 尝试向父窗口发送通知消息
if (window.parent && window.parent !== window) {
window.parent.postMessage({
type: 'gsm3-notification',
data: { type, message }
}, '*')
} else {
// 如果无法发送到父窗口,使用浏览器原生通知
console.log(`[${type.toUpperCase()}] ${message}`)
}
} catch (error) {
console.warn('发送通知失败:', error)
}
}
/**
* 格式化字节大小
* @param {number} bytes 字节数
* @param {number} decimals 小数位数
*/
formatBytes(bytes, decimals = 2) {
if (bytes === 0) return '0 Bytes'
const k = 1024
const dm = decimals < 0 ? 0 : decimals
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
const i = Math.floor(Math.log(bytes) / Math.log(k))
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]
}
/**
* 格式化时间戳
* @param {string|number|Date} timestamp 时间戳
*/
formatTime(timestamp) {
const date = new Date(timestamp)
return date.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
})
}
/**
* 格式化运行时间
* @param {number} seconds 秒数
*/
formatUptime(seconds) {
const days = Math.floor(seconds / 86400)
const hours = Math.floor((seconds % 86400) / 3600)
const minutes = Math.floor((seconds % 3600) / 60)
const secs = Math.floor(seconds % 60)
const parts = []
if (days > 0) parts.push(`${days}`)
if (hours > 0) parts.push(`${hours}小时`)
if (minutes > 0) parts.push(`${minutes}分钟`)
if (secs > 0 || parts.length === 0) parts.push(`${secs}`)
return parts.join(' ')
}
}
// 创建全局实例
window.gsm3 = new GSM3API()
// 添加初始化状态标记
window.gsm3.isInitialized = false
window.gsm3.initPromise = null
// 初始化API的Promise
window.gsm3.initialize = function() {
if (this.initPromise) {
return this.initPromise
}
this.initPromise = new Promise((resolve) => {
const checkReady = () => {
if (this.token) {
this.isInitialized = true
console.log('GSM3 API初始化完成Token:', this.token)
resolve(true)
} else {
setTimeout(checkReady, 100)
}
}
checkReady()
})
return this.initPromise
}
// 监听来自父窗口的消息
window.addEventListener('message', (event) => {
if (event.data && event.data.type === 'gsm3-token-update') {
window.gsm3.token = event.data.token
console.log('Token已更新:', event.data.token)
}
})
// 插件加载完成后的初始化
document.addEventListener('DOMContentLoaded', () => {
console.log('GSM3 插件API已加载')
// 启动初始化过程
window.gsm3.initialize().then(() => {
console.log('GSM3 API准备就绪')
// 向父窗口发送插件加载完成的消息
if (window.parent && window.parent !== window) {
window.parent.postMessage({
type: 'gsm3-plugin-loaded',
data: { timestamp: new Date().toISOString() }
}, '*')
}
})
})