UNPKG

mm_os

Version:

MM_OS服务端架构,用于快速构建应用程序,支持网站建设、小程序后台、AI应用、物联网(IOT/AIOT)、游戏服务端等多种场景。

534 lines (485 loc) 13.1 kB
const compressing = require('compressing'); const { Manager, Drive } = require('mm_machine'); const { Plugin } = require('../plugin/index.js'); /** * 应用 */ class App extends Drive { static config = { 'name': 'default', 'version': '1.0.0', 'title': '应用', 'description': '应用', 'author': 'qww', 'scope': 'server', 'dir': '', // // 游戏配置,非游戏模块不需要配置 // 'game': { // // 游戏类型 // 'type': 'card_game' // }, 'dependencies': [], // // 压缩目录 // 'zip_dir': './static/file/zip', // // 解压目录 // 'unzip_dir': './static/file/unzip', // 自定义数据库 - 1 开启 0 关闭 'diy_sql': 0, // 自定义缓存 - 1 开启 0 关闭 'diy_cache': 0, // sql 配置 - 可以没有,没有则使用默认数据库 'sql': { // 数据库方式 'way': 'mysql', 'mysql': { // 数据库主机 'host': '127.0.0.1', // 数据库端口 'port': 3306, // 数据库用户名 'username': 'root', // 数据库密码 'password': 'Asd159357', // 数据库名称 'database': 'mos' } }, // 缓存配置 - 可以没有,没有则使用默认缓存 'cache': { // 缓存方式方式 'way': 'redis', 'redis': { // 缓存主机 'host': '127.0.0.1', // 缓存端口 'port': 6379, // 缓存密码 'password': 'asd159357', // 缓存数据库 'database': 0 } } }; /** * 构造函数 * @param {object} config 配置参数 * @param {object} parent 父对象 */ constructor(config, parent) { super({ ...App.config, ...config || {} }, parent); // 管理器集合 this.manager = {}; // 插件,用于功能扩展 this.plugin = {}; } } /** * 预置 */ App.prototype._preset = function () { // 数据库,调用统一数据库接口,也允许重定义数据库 this.sql = null; // 缓存,调用统一缓存接口,也允许重定义缓存 this.cache = null; // 文件管理,调用统一文件管理接口,也允许重定义文件管理 this.filer = null; /** * 获取服务器实例 * @returns {object} 服务器实例对象 */ this.getServer = function () { return $.server; }; }; /** * 创建管理器实例 * @param {string} name 管理器名称 * @param {string} title 管理器标题 * @param {Function} cls 管理器类 * @returns {object} 管理器实例 */ App.prototype._createManager = function (name, title, cls) { let dir = this.getDir(); var manager = new Manager({ name: name, title: title, filename: name + '.json', tpl_dir: `../${name}/`.fullname(__dirname), base_dir: '', dir: `./${name}`.fullname(dir) }, this, this[name], cls); this.manager[name] = manager; return manager; }; /** * 初始化管理器集合 * @returns {Promise<void>} 初始化完成 */ App.prototype._initManager = async function () { // console.time('[TIMING] App._initManager'); // 管理器配置列表 var mgr_configs = [ { name: 'plugin', title: '插件', Module: Plugin } ]; // 管理器实例集合 var managers = []; // 使用for循环初始化所有管理器 for (var i = 0; i < mgr_configs.length; i++) { var config = mgr_configs[i]; var manager = this._createManager(config.name, config.title, config.Module); managers.push(manager); } // 并行执行所有管理器的初始化操作 var list = []; for (var j = 0; j < managers.length; j++) { list.push(managers[j].do('init')); } await Promise.all(list); // console.timeEnd('[TIMING] App._initManager'); }; /** * 加载资源 * @returns {Promise<void>} 加载完成 */ App.prototype._loadSources = async function () { await this.manager.plugin.runAll('load'); }; /** * 初始化资源 * @returns {Promise<void>} 初始化完成 */ App.prototype._initSources = async function () { await this.manager.plugin.runAll('init', this, this.getEventer(), this.getLogger()); }; /** * 初始化核心 * @param {object} server 服务器 * @param {object} eventer 事件总线 * @param {object} logger 日志管理器 * @private */ App.prototype._initCore = async function (server, eventer, logger) { if (logger) { this.setLogger(logger); } if (eventer) { this.getEventer = function () { return eventer; }; } if (server) { this.getServer = function () { return server; }; } // 初始化基础设施 await this._initBase(); // 初始化管理 await this._initManager(); // 加载资源 await this._loadSources(); // 验证依赖关系 this._validateDep(); }; /** * 初始化基础设施 * @private */ App.prototype._initBase = async function () { // 初始化文件处理器 this.filer = this.getServer().filer; // 初始化数据库 if (this.config.diy_sql) { var cg = this.config.sql || $.config.get('sql'); this.sql = sqlAdmin(this.config.name, cg); await this.sql.open(); } else { this.sql = this.getServer().sql; } // 初始化缓存 if (this.config.diy_cache) { var cg = this.config.cache || $.config.get('cache'); this.cache = cacheAdmin(this.config.name, cg); await this.cache.connect(); } else { this.cache = this.getServer().cache; } }; /** * 启动资源 */ App.prototype._startSources = async function () { await this.manager.plugin.runAll('start'); }; /** * 启动核心 * @private */ App.prototype._startCore = async function () { // 初始化资源 await this._initSources(); // 启动资源 await this._startSources(); }; /** * 验证依赖关系 * @private */ App.prototype._validateDep = function () { var required_managers = ['plugin']; for (var name of required_managers) { if (!this.manager[name]) { throw new Error(`缺少必要的管理器: ${name}`); } } }; /** * 压缩插件 * @param {string} name 插件名称 * @returns {string} 打包成功返回压缩包文件地址 */ App.prototype.zipPlugin = async function (name) { if (!name || typeof name !== 'string') { throw new TypeError('无效的插件名称'); } var plugin = this.plugin[name]; if (!plugin) { throw new Error(`插件 ${name} 不存在`); } var dir = plugin.dir; var file = ('./' + name + '.zip').fullname('/static/file/zip/'); return await this.zip(file, dir); }; /** * 压缩插件 * @param {string} file 插件压缩文件 * @param {string} dir 要压缩的目录 * @returns {string} 打包成功返回压缩包文件地址 */ App.prototype.zip = async function (file, dir = '/static/file/zip/') { file.addDir(); await compressing.zip.compressDir(dir, file); if (file.hasFile()) { return file; } return null; }; /** * 解压插件 * @param {object} file 插件压缩文件 * @param {string} unzip_dir 要解压的目录 * @returns {object} 返回解压结果 */ App.prototype.unzip = async function (file, unzip_dir = '/cache/unzip/') { if (!file) { throw new TypeError('无效的文件路径'); } var name = file.basename().left('.'); var dir = name.fullname(unzip_dir); try { // 确保目标目录存在 dir.addDir(); // 解压文件到目标目录 await compressing.zip.uncompress(file, dir); // 查找目标目录 var target_dir = this._findTargetDir(dir, name); // 如果找到目标目录,直接使用它作为解压结果 if (target_dir) { var config_file = './plugin.json'.fullname(target_dir); if (config_file.hasFile()) { return { file, config_file, dir: target_dir, unzip_dir: dir }; } } return { file, dir, unzip_dir: dir }; } catch (err) { this.log('error', '插件解压失败!', err); // 清理失败的解压目录 if (dir.hasDir()) { dir.delDir(); } return null; } }; /** * 查找目标目录 * @param {string} dir 解压目录 * @param {string} name 插件名称 * @returns {string|null} 目标目录路径 * @private */ App.prototype._findTargetDir = function (dir, name) { var dir_files = $.dir.get(dir); if (!dir_files || dir_files.length === 0) { return null; } for (var i = 0; i < dir_files.length; i++) { var item = dir_files[i]; if (item.hasDir() && item.basename() === name) { var plugin_json = './plugin.json'.fullname(item); if (plugin_json.hasFile()) { return item; } } } return null; }; /** * 卸载插件 * @param {string} name 插件名称 * @returns {string} 返回卸载路径 */ App.prototype.uninstallPlugin = async function (name) { if (!name || typeof name !== 'string') { this.log('error', '卸载失败:无效的插件名称'); return null; } var plugin = this.manager.plugin.get(name); if (!plugin) { this.log('error', '卸载失败:插件不存在'); return null; } var dir = plugin.dir; // 执行卸载脚本 if (plugin.call) { await plugin.call('uninstall'); } try { this.manager.plugin.del(name); dir.delDir(); } catch (err) { this.log('error', '卸载失败:', err); return null; } }; /** * 复制目录 * @param {string} dir_src 原路径 * @param {string} dir 新路径 * @returns {object} 返回复制结果 */ App.prototype.copyDir = function (dir_src, dir) { return new Promise((resolve, reject) => { if (!dir_src || !dir) { this.log('error', '复制目录失败:无效的目录路径'); reject(new Error('无效的目录路径')); return; } if (!dir_src.hasDir()) { this.log('error', '复制目录失败:源目录不存在'); reject(new Error('源目录不存在')); return; } try { // 确保目标目录存在 dir.addDir(); $.dir.copy(dir_src, dir, (err) => { if (err) { this.log('error', '复制目录失败:', err); reject(err); } else { resolve({ dir_src, dir }); } }); } catch (err) { this.log('error', '复制目录失败:', err); reject(err); } }); }; /** * 解压插件 * @param {object} file 插件压缩文件 * @returns {object} 返回解压结果 */ App.prototype._unzipPlugin = async function (file) { var ret = await this.unzip(file); if (!ret) { return null; } // 直接使用解压目录作为 dir_src var dir_src = ret.dir; var config_file = './plugin.json'.fullname(dir_src); if (!config_file.hasFile()) { throw new Error('安装失败:找不到配置文件'); } var config = config_file.loadJson(); if (!config) { throw new Error('安装失败:配置文件无效'); } var app = config.app || this.config.name; var name = config.name; if (!name) { throw new Error('安装失败:配置文件中缺少名称字段'); } var dir = `/app/${app}/plugin/${name}`; await this.copyDir(dir_src, dir); ret.app = app; ret.name = name; ret.dir = dir; return ret; }; /** * 安装插件 * @param {object} info 插件信息 * @returns {object} 返回安装结果 */ App.prototype._installPlugin = async function (info) { if (!info) { return null; } if (!info.config_file) { throw new Error('安装失败:找不到配置文件'); } var manager = this.manager.plugin; if (info.app !== this.config.name) { var app = this.getServer().app[info.app]; if (!app) { throw new Error(`安装失败:应用 ${info.app} 不存在`); } manager = app.manager.plugin; } // 只加载当前插件,而不是重载所有插件 await manager.load(info.config_file); // 执行安装方法 var plugin = manager.getMod(info.name); if (plugin && plugin.call) { await plugin.call('install'); } return true; }; /** * 安装插件 * @param {string} file 插件压缩文件路径 * @returns {object} 返回安装结果 */ App.prototype.installPlugin = async function (file) { if (!file) { throw new TypeError('无效的文件路径'); } // 解压插件 var info = await this._unzipPlugin(file); if (!info) { return null; } // 安装插件 await this._installPlugin(info); // 清理临时目录 setTimeout(() => { if (info.unzip_dir && info.unzip_dir.hasDir()) { info.unzip_dir.delDir(); } }, 3000); info.zip_file = file; return info; }; exports.App = App;