mm_os
Version:
这是超级美眉服务端框架,用于快速构建应用程序。
404 lines (373 loc) • 9.5 kB
JavaScript
const Item = require('mm_machine').Item;
// 提供一个全局方法容器
if (!$.methods) {
$.methods = {};
}
/**
* websocket驱动类
* @extends {Item}
* @class
*/
class Drive extends Item {
/**
* 构造函数
* @param {String} dir 当前目录
* @constructor
*/
constructor(dir) {
super(dir, __dirname);
this.default_file = "./socket.json";
/* 通用项 */
/**
* 配置参数
*/
this.config = {
// 名称, 由中英文和下“_”组成, 用于修改或卸载 例如: demo
"name": "",
// 状态 0未启用,1启用
"state": 1,
// socket 服务名称
"name": "",
// socket 服务标题
"title": "",
// socket 服务介绍
"description": "",
// 调用的脚本
"func_file": "./index.js",
// 同步消息循环发送的时间间隔
"interval": 1000
};
// 开放给前端调用的函数
this.methods = Object.assign({}, $.methods);
// 客户端集合
this.clients = {};
}
}
/**
* 新建脚本
* @param {String} 文件
*/
Drive.prototype.new_script = function(file) {
var fl = __dirname + "/script.js";
if (fl.hasFile()) {
var text = fl.loadText();
if (text) {
var l = $.slash;
if (file.indexOf('socket' + l) !== -1) {
var name = file.between('socket' + l, l);
text = text.replaceAll("{0}", name);
}
file.saveText(text);
}
}
};
/**
* 新建配置
* @param {String} 文件
*/
Drive.prototype.new_config = function(file) {
var fl = __dirname + "/config.tpl.json";
if (fl.hasFile()) {
var text = fl.loadText();
if (text) {
var l = $.slash;
if (file.indexOf('socket' + l) !== -1) {
var name = file.between('socket' + l, l);
text = text.replaceAll("{0}", name);
}
file.saveText(text);
}
}
};
/**
* 获取session ID
* @param {Object} ctx HTTP上下文
* @return {String} 返回用户的uuid
*/
Drive.prototype.getToken = async function(ctx) {
var uuid = await ctx.cookies.get("mm:uuid");
if (!uuid) {
var hd = ctx.request.header;
var agent = hd['user-agent'];
if (!agent) {
agent = "mm";
}
var start = agent.md5().substring(0, 32);
var stamp = Date.parse(new Date()) / 1000;
uuid = (ctx.ip + '_' + stamp).aes_encode(start);
}
return uuid;
};
/**
* 收到消息时处理函数
* @param {String} bodyStr 消息正文字符串
* @param {Object} ctx http上下文
* @param {String} token 临时访问牌
*/
Drive.prototype.onmessage = async function(bodyStr, ctx, token) {
var ret = await this.run(bodyStr, ctx, token);
if (ret) {
var ws = ctx.websocket;
if (typeof(ret) === "object") {
ws.send(JSON.stringify(ret));
} else {
ws.send(ret);
}
}
};
/**
* 状态变更通知
* @param {String} type 通知类型
* @param {String} bodyStr 消息正文字符串
* @param {Object} ctx http上下文
* @param {String} token 临时访问牌
* @return {Boolean} 返回true表示做状态修改, 例如关闭时为true, 会删除该客户端
*/
Drive.prototype.noticy = async function(type, bodyStr, ctx, token) {
// $.log.debug('通知:', '关闭了');
return true;
};
/**
* 关闭连接时处理函数
* @param {String} bodyStr 消息正文字符串
* @param {Object} ctx http上下文
* @param {String} token 临时访问牌
*/
Drive.prototype.onclose = async function(bodyStr, ctx, token) {
var del = await this.noticy("close", ctx, token);
if (del) {
var lt = this.clients[token];
var index = lt.indexOf(ctx);
lt.splice(index, 1);
}
};
/**
* 设置websocket
* @param {Object} ctx http上下文
* @param {String} token 临时访问牌
*/
Drive.prototype.set_socket = function(ctx, token) {
var ws = ctx.websocket;
// 增加消息队列
ws.list_msg = [];
/**
* 设置发送请求
* @param {String} method 方法名称
* @param {Object} params 请求参数
* @param {Function} func 回调函数
*/
var _this = this;
ws.req = async function(method, params, func) {
var key = _this.config.name + '';
var data = {
id: key + new Date().getTime() + Math.random(),
method: method,
params: params
};
this.send(JSON.stringify(data));
if (func) {
data.func = func;
this.list_msg.push(data);
}
};
// 设置事件 —— 获取消息时和socket关闭时
ws.on("message", async function(bodyStr) {
_this.onmessage(bodyStr, ctx, token)
});
ws.on("close", async function(bodyStr) {
_this.onclose(bodyStr, ctx, token)
});
}
/**
* 握手成功, 发送首条返回内容
* @param {Object} ctx http 上下文
* @param {String} token 临时访问牌
*/
Drive.prototype.success = function(ctx, token) {
var ret = $.ret.bl(true, 'connection succeeded');
// 首次响应加上身份牌
ret.result.token = token;
// ID为0表示连接成功
ret.id = 0;
ctx.websocket.send(JSON.stringify(ret));
};
/**
* 添加客户端
* @param {Object} ctx 请求上下文
* @param {Function} next 跳过当前, 然后继续执行函数
*/
Drive.prototype.add = async function(ctx) {
var token = await this.getToken(ctx);
if (!this.clients[token]) {
this.clients[token] = [];
}
this.set_socket(ctx, token);
this.success(ctx, token);
this.clients[token].push(ctx);
};
/**
* 发送消息 —— 会发送给所有目标, 如须过滤目标, 则须在渲染时过滤
* @param {String} body 消息正文
* @param {String} token 临时访问牌, 用于指定客户端发消息
*/
Drive.prototype.send = async function(body, token) {
if (token) {
var list = this.clients[token];
if (list) {
list.map(async (ctx) => {
ctx.websocket.send(body);
})
}
} else {
var dt = this.clients;
for (let k in dt) {
var list = dt[k];
list.map(async (ctx) => {
ctx.websocket.send(body);
});
}
}
};
/**
* 发送消息 —— 会发送给所有目标, 如须过滤目标, 则须在渲染时过滤
* @param {String} method 方法名称
* @param {Object} params 请求参数
* @param {Function} func 回调函数 可以为空
* @param {String} token 临时访问牌, 用于指定客户端发消息
*/
Drive.prototype.req = async function(method, params, func, token) {
if (token) {
var ctx = this.clients[token];
if (ctx) {
ctx.websocket.req(params, func);
}
} else {
var dt = this.clients;
for (let k in dt) {
ctx.websocket.req(params, func);
}
}
};
/**
* 执行
* @param {String} bodyStr 正文字符串
* @param {Object} ctx 请求上下文
* @param {String} token 临时访问牌
* @return {Object} 返回执行结果
*/
Drive.prototype.run = async function(bodyStr, ctx, token) {
try {
if(Object.prototype.toString.call(bodyStr) == "[object Uint8Array]"){
bodyStr = bodyStr.toString();
}
var ws = ctx.websocket;
var json = bodyStr.toJson();
var req = ctx.request;
var request = Object.assign({}, {
headers: req.headers,
query: req.query,
token: token
});
if (json) {
var {
id,
method
} = json;
if (json.result && id) {
var lt = ws.list_msg;
var len = lt.length;
var has = false;
for (var i = 0; i < len; i++) {
var o = lt[i];
if (id === o.id) {
o.func(json.result);
lt.splice(i, 1);
has = true;
break;
}
}
if (has) {
return;
}
} else if (method) {
if (this.methods[method]) {
var ret;
var result = await this.methods[method](json.params, ws, request);
if (result) {
if (typeof(result) == "object" && !Array.isArray(result)) {
ret = Object.assign({
id
}, result);
} else {
ret = {};
if (id) {
ret.id = id
}
ret.result = result;
}
}
return ret;
}
}
return await this.main(json, ws, request);
}
return await this.main(bodyStr, ws, request);
} catch (err) {
$.log.error("webscoket 错误", err);
return $.log.error(10000, "代码错误!原因:" + err.toString());
}
};
/**
* 非定义函数时执行
* @param {Object} body 请求正文
* @param {Object} params 参数
* @param {Object} ws Websocket服务
* @param {Object} request 请求协议头
* @return {Object} 返回执行结果
*/
Drive.prototype.main = async function(body, websocket, request) {
return null;
};
/**
* 初始化函数, 用于定义开放给前端的函数
*/
Drive.prototype.init = async function init() {
};
/**
* 加载完成时
*/
Drive.prototype.load_after = function() {
var m = this.methods;
/**
* 获取所有方法
* @param {Object} params 参数
* @param {Object} ws Websocket服务
* @param {Object} request 请求协议头
* @return {Object} 返回执行结果
*/
m.get_method = async function(params, ws, request) {
return Object.keys(m);
};
};
/**
* 获取插件
* @param {String} app 应用名称
* @param {String} name 插件名称
* @returns {Object} 返回获取到的插件
*/
Drive.prototype.plugin = function(app, name) {
var plus;
var l = $.slash;
if (!app) {
app = this.filename.between("app" + l, l);
}
if (!name) {
name = this.filename.between("plugin" + l, l);
}
var plugins = $.pool.plugin[app];
if (plugins) {
plus = plugins.get(name);
}
return plus
}
module.exports = Drive;