@ones-open/node-host
Version:
ONES Open Platform Node.js plugin host
488 lines (487 loc) • 21.5 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.HttpHandler = void 0;
const config_1 = require("../config");
const logger_1 = require("../logger");
const plugin_1 = require("../plugin");
const protocol_1 = require("../protocol");
const node_utils_1 = require("@ones-op/node-utils");
const node_utils_2 = require("@ones-op/node-utils");
const utils_1 = require("../utils");
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const plugin_func_type = 'ones-plugin-function-type';
const plugin_instance_id = 'ones-plugin-instance-id';
class HttpHandler {
static async handlePBPluginCall(ctx) {
logger_1.logger.debug(`[plugin-func]request body length ${ctx.request.body.length}`); // as Buffer
let requestMesage;
try {
//requestMesage = JSON.parse(ctx.request.body as string)
requestMesage = protocol_1.protocol.PlatformMessage.decode(ctx.request.body);
}
catch (err) {
logger_1.logger.error('decode error', err);
logger_1.logger.error(`decode error bodylen: ${ctx.request.body.length} body: [${ctx.request.body}]`);
}
const instanceId = ctx.headers[plugin_instance_id] || '';
try {
const funcType = ctx.headers[plugin_func_type] || '';
if (requestMesage) {
switch (funcType) {
case 'plugin-func':
{
const pluginHttpResponse = await HttpHandler.callPluginFunc(instanceId, requestMesage.Plugin?.Http?.Request);
const responseMessage = {
Plugin: {
Http: {
Response: pluginHttpResponse,
},
},
};
const msg = protocol_1.protocol.PlatformMessage.fromObject(responseMessage);
ctx.response.body = protocol_1.protocol.PlatformMessage.encode(msg).finish(); //JSON.stringify(responseMessage)
}
break;
case 'lifecycle':
{
const controlMessage = requestMesage.Control;
controlMessage.Headers = controlMessage.Headers || {};
const response = await HttpHandler.callPluginLifecycle(instanceId, controlMessage.Headers || {}, controlMessage.LifeCycleRequest);
const responseMessage = {
Control: {
LifeCycleResponse: response,
},
};
const msg = protocol_1.protocol.PlatformMessage.fromObject(responseMessage);
ctx.response.body = protocol_1.protocol.PlatformMessage.encode(msg).finish(); //JSON.stringify(responseMessage)
}
break;
case 'readfile':
{
const response = await HttpHandler.callPluginReadFile(instanceId, requestMesage.Plugin?.ReadFile);
const responseMessage = {
Plugin: {
ReadFile: response,
},
};
const msg = protocol_1.protocol.PlatformMessage.fromObject(responseMessage);
ctx.response.body = protocol_1.protocol.PlatformMessage.encode(msg).finish(); //JSON.stringify(responseMessage)
}
break;
case 'timer-task':
{
const result = await HttpHandler.callPluginTimerTask(instanceId, requestMesage.Ability?.TimerTask);
const responseMessage = {
Ability: {
TimerTask: {
Response: {
Result: result,
},
},
},
};
const msg = protocol_1.protocol.PlatformMessage.fromObject(responseMessage);
ctx.response.body = protocol_1.protocol.PlatformMessage.encode(msg).finish(); //JSON.stringify(responseMessage)
}
break;
default:
throw new Error(`Not found plugin call type: [${funcType}]`);
}
logger_1.logger.debug(`[${funcType}] execute success`);
}
}
catch (err) {
logger_1.logger.error('handlePBPluginCall error', err, err.stack);
}
}
static async callPluginFunc(instanceId, message) {
HttpHandler.debugRequest(message);
const { abilityFunc: functionName, ...request } = (0, node_utils_1.parseRequest)(message);
const instance = plugin_1.pluginManager.getPlugin();
if (!instance) {
throw new Error(`Not found plugin instance: ${instanceId}`);
}
if (!functionName) {
throw new Error(`Not found AbilityFunc!`);
}
let response = {};
try {
if (!message.IsEvent) {
const result = (await instance.runMethod(functionName, request));
if (!result) {
response = {
StatusCode: 500,
Headers: null,
Body: null,
Error: {
Code: 500,
Error: `Plugin function [${functionName}] execute empty response!`,
},
};
}
else {
response = {
StatusCode: result.statusCode || 200,
Headers: result.headers ? (0, node_utils_1.formatHeaders)(result.headers) : {},
Body: result.body ? (0, node_utils_1.formatBody)(result.body) : null,
};
}
}
else {
instance.runMethod(functionName, request);
response = {
StatusCode: 200,
Headers: null,
Body: null,
};
}
}
catch (error) {
const errorType = error?.errType || '';
let statusCode;
let Err;
if (errorType == 'pluginError') {
statusCode = error?.code || 500;
Err = HttpHandler.handlePluginErr(error);
}
else if (errorType == 'abilityError') {
statusCode = error?.statusCode || 500;
Err = HttpHandler.handleAbilityErr(error);
}
else {
if (error?.message === 'Reference is not a function') {
statusCode = 404;
Err = {
Code: 404,
Msg: `Can not find function[${functionName}]!`,
Error: error?.stack,
};
}
else {
statusCode = 500;
Err = {
Code: 500,
Msg: `Plugin function [${functionName}] execute failed!`,
Error: error?.stack,
};
}
}
response = {
StatusCode: statusCode,
Headers: null,
Body: null,
Error: Err,
};
logger_1.logger.error(`Plugin function [${functionName}] execute error: `, response);
}
HttpHandler.debugResponse(response);
return response;
}
static handlePluginErr(pluginErr) {
return {
Code: pluginErr?.code || 500,
Msg: pluginErr?.reason || '',
Level: pluginErr?.type || 'error',
Model: 'plugin.Err',
};
}
static handleAbilityErr(abilityErr) {
const err = {
Code: abilityErr?.statusCode || 500,
Msg: abilityErr?.reason || '',
Level: abilityErr?.level || 'error',
Model: 'plugin.Err',
};
if (err.Code == 500) {
err.Msg = 'Service unavailable. Try again later.';
}
return err;
}
static debugRequest(request) {
if (config_1.config.host.debug_mode) {
logger_1.logger.info(`[debug RequestMessage]: `, request);
}
}
static debugResponse(response) {
if (config_1.config.host.debug_mode) {
logger_1.logger.info(`[debug ResponseMessage]: `, response);
}
}
static async callPluginLifecycle(instanceId, headers, requestMessage) {
HttpHandler.debugRequest(requestMessage);
const Action = requestMessage.Action || 0;
const Forced = requestMessage.Forced || false;
const teamUUIDList = requestMessage.TeamUUID || [];
const orgUUID = requestMessage.OrgUUID || '';
const firstInstall = requestMessage.FirstInstall || false;
const responseMessage = {
Result: true,
Error: null,
FailedTeamUUID: [],
};
try {
const pluginRequest = {
headers: {},
parsedHeaders: (0, protocol_1.parseHeaders)(headers),
method: '',
body: {},
};
const instance = plugin_1.pluginManager.findPlugin(instanceId);
if (!instance) {
throw new Error(`Not found plugin instance: ${instanceId}`);
}
switch (Action) {
case protocol_1.protocol.ControlMessage.PluginActionType['Install']: {
await instance.Install(pluginRequest);
break;
}
case protocol_1.protocol.ControlMessage.PluginActionType['OrgInstall']: {
if (config_1.config.host.serve_mode == config_1.serveModeSaas) {
await instance.SaasOrgInstall(pluginRequest);
}
else {
await instance.OrgInstall(pluginRequest, teamUUIDList, firstInstall);
}
break;
}
case protocol_1.protocol.ControlMessage.PluginActionType['UnInstall']: {
await instance.Uninstall(pluginRequest);
break;
}
case protocol_1.protocol.ControlMessage.PluginActionType['OrgUnInstall']: {
if (config_1.config.host.serve_mode == config_1.serveModeSaas) {
await instance.SaasOrgUninstall(pluginRequest);
}
else {
await instance.OrgUninstall(pluginRequest, teamUUIDList);
}
break;
}
case protocol_1.protocol.ControlMessage.PluginActionType['Enable']: {
if (config_1.config.host.serve_mode == config_1.serveModeSaas) {
await instance.SaasEnable(pluginRequest, teamUUIDList);
}
else {
await instance.Enable(pluginRequest);
}
break;
}
case protocol_1.protocol.ControlMessage.PluginActionType['OrgEnable']: {
if (config_1.config.host.serve_mode == config_1.serveModeSaas) {
const resp = await instance.SaasOrgEnable(pluginRequest, orgUUID, teamUUIDList);
responseMessage.FailedTeamUUID = resp;
}
else {
const resp = await instance.OrgEnable(pluginRequest, teamUUIDList);
responseMessage.FailedTeamUUID = resp;
}
break;
}
case protocol_1.protocol.ControlMessage.PluginActionType['Disable']: {
if (config_1.config.host.serve_mode == config_1.serveModeSaas) {
await instance.SaasDisable(pluginRequest, teamUUIDList, Forced);
}
else {
await instance.Disable(pluginRequest, Forced);
}
break;
}
case protocol_1.protocol.ControlMessage.PluginActionType['OrgDisable']: {
if (config_1.config.host.serve_mode == config_1.serveModeSaas) {
const resp = await instance.SaasOrgDisable(pluginRequest, orgUUID, teamUUIDList, Forced);
responseMessage.FailedTeamUUID = resp;
}
else {
const resp = await instance.OrgDisable(pluginRequest, teamUUIDList, Forced);
responseMessage.FailedTeamUUID = resp;
}
break;
}
case protocol_1.protocol.ControlMessage.PluginActionType['Upgrade']: {
const oldPluginInfo = {
version: (0, node_utils_1.parseVersion)(requestMessage.OldVersion),
};
await instance?.Upgrade(oldPluginInfo, pluginRequest);
break;
}
case protocol_1.protocol.ControlMessage.PluginActionType['OrgUpgrade']: {
const oldPluginInfo = {
version: (0, node_utils_1.parseVersion)(requestMessage.OldVersion),
};
if (config_1.config.host.serve_mode == config_1.serveModeSaas) {
await instance?.SaasOrgUpgrade(oldPluginInfo, pluginRequest);
}
else {
await instance?.OrgUpgrade(oldPluginInfo, pluginRequest, teamUUIDList);
}
break;
}
default:
logger_1.logger.error(`Lifecycle action not found: ${Action}`);
break;
}
}
catch (error) {
responseMessage.Result = false;
const ErrorMessage = (0, node_utils_2.formatError)(error, 500);
responseMessage.Error = ErrorMessage;
logger_1.logger.error(`Lifecycle execute error: `, error);
}
HttpHandler.debugResponse(responseMessage);
return responseMessage;
}
static async callPluginReadFile(instanceId, message) {
const request = message?.Request;
if (!instanceId || !request || !request.Path || !request.Name) {
return;
}
request.Type = request.Type || 0;
const response = {
Error: null,
Md5Response: null,
ContentResponse: null,
};
try {
const instance = plugin_1.pluginManager.findPlugin(instanceId);
if (!instance) {
throw new Error(`Not found plugin instance: ${instanceId}`);
}
let file = path_1.default.join(request.Path, request.Name);
if (config_1.config.cli.debug_mode) {
file = path_1.default.join(config_1.config.plugin.workspace_path, request.Name);
}
if (request.Type == protocol_1.protocol.ReadFileMessageRequest.MessageType.Md5) {
//const md5 = await instance.runMethod(getFileMd5,request.Name)
const stat = await fs_1.default.promises.stat(file);
const md5 = await (0, protocol_1.getFileMd5)(file);
response.Md5Response = {
Name: request.Name,
Md5: md5,
Size: stat.size,
};
}
else if (request.Type == protocol_1.protocol.ReadFileMessageRequest.MessageType.Content) {
request.StartPos = request.StartPos || 0;
if (!request.EndPos) {
throw new Error(`Invalid EndPos!`);
}
//proto定义为int64,通过protbuf到node.js这边变成了string。。。
const startPos = (0, utils_1.toNumber)(request.StartPos);
const endPos = (0, utils_1.toNumber)(request.EndPos);
const len = endPos - startPos;
if (len < 0) {
throw new Error(`Invalid StartPos:[${request.StartPos}] and EndPos:[${request.EndPos}!`);
}
const content = await (0, utils_1.readPartialFile)(file, startPos, len);
response.ContentResponse = {
Name: request.Name,
Content: content,
};
}
else {
throw new Error(`Invalid requestType: [${request.Type}]`);
}
}
catch (err) {
logger_1.logger.error(`read file handler err: `, err);
const ErrorMessage = (0, node_utils_2.formatError)(err, 500);
response.Error = ErrorMessage;
}
return {
Response: response,
};
}
static async callPluginTimerTask(instanceId, message) {
const request = message?.Request;
let result = true;
try {
const instance = plugin_1.pluginManager.findPlugin(instanceId);
if (!instance) {
throw new Error(`[callPluginTimerTask] Not found plugin instance: ${instanceId}`);
}
// 异步调用插件方法
const functionName = request?.AbilityFunc || '';
if (functionName == '' || !functionName) {
throw new Error(`[callPluginTimerTask] Not found AbilityFunc!`);
}
instance.runMethod(functionName);
}
catch (err) {
logger_1.logger.error(`Ability timer-task handle error: `, err);
result = false;
}
// 发送收到确认消息
return result;
}
static async extensionFuncHandler(ctx, extensionFuncPath, funcName) {
try {
logger_1.logger.debug(`Received request at ${extensionFuncPath}`);
const instance = plugin_1.pluginManager.getPlugin();
const instanceID = instance?.plugin.InstanceID;
if (!instance) {
throw new Error(`Not found plugin instance: ${instanceID}`);
}
const result = (await instance.runMethod(funcName, ctx.request.body));
if (!result) {
ctx.response.body = {
StatusCode: 500,
Headers: null,
Body: null,
Error: {
Code: 500,
Error: `Plugin function [${funcName}] execute empty response!`,
},
};
}
else {
ctx.response.body = {
StatusCode: result.statusCode || 200,
Headers: result.headers ? (0, node_utils_1.formatHeaders)(result.headers) : {},
Body: JSON.stringify(result.body),
};
}
}
catch (error) {
const errorType = error?.errType || '';
let statusCode;
let Err;
switch (true) {
case errorType === 'pluginError':
statusCode = error?.code || 500;
Err = HttpHandler.handlePluginErr(error);
break;
case errorType === 'abilityError':
statusCode = error?.statusCode || 500;
Err = HttpHandler.handleAbilityErr(error);
break;
case error?.message === 'Reference is not a function':
statusCode = 404;
Err = {
Code: 404,
Msg: `Can not find function[${funcName}]!`,
Error: error?.stack,
};
break;
default:
statusCode = 500;
Err = {
Code: 500,
Msg: `Plugin function [${funcName}] execute failed!`,
Error: error?.stack,
};
}
ctx.response.body = {
StatusCode: statusCode,
Headers: null,
Body: null,
Error: Err,
};
logger_1.logger.error(`Plugin function [${funcName}] execute error: `, ctx.response.body);
}
}
}
exports.HttpHandler = HttpHandler;