mm_os
Version:
MM_OS服务端架构,用于快速构建应用程序,支持网站建设、小程序后台、AI应用、物联网(IOT/AIOT)、游戏服务端等多种场景。
534 lines (485 loc) • 13.1 kB
JavaScript
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;