mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Merge branch 'dev' into function-modules
This commit is contained in:
@@ -28,6 +28,7 @@ var api = module.exports = {
|
||||
api.library.init(runtime);
|
||||
api.projects.init(runtime);
|
||||
api.context.init(runtime);
|
||||
api.plugins.init(runtime);
|
||||
},
|
||||
|
||||
comms: require("./comms"),
|
||||
@@ -37,6 +38,7 @@ var api = module.exports = {
|
||||
settings: require("./settings"),
|
||||
projects: require("./projects"),
|
||||
context: require("./context"),
|
||||
plugins: require("./plugins"),
|
||||
|
||||
isStarted: async function(opts) {
|
||||
return runtime.isStarted();
|
||||
|
@@ -94,6 +94,10 @@ var api = module.exports = {
|
||||
getNodeConfig: async function(opts) {
|
||||
var id = opts.id;
|
||||
var lang = opts.lang;
|
||||
if (/[^a-z\-\*]/i.test(opts.lang)) {
|
||||
reject(new Error("Invalid language: "+opts.lang));
|
||||
return
|
||||
}
|
||||
var result = runtime.nodes.getNodeConfig(id,lang);
|
||||
if (result) {
|
||||
runtime.log.audit({event: "nodes.config.get",id:id}, opts.req);
|
||||
@@ -116,6 +120,10 @@ var api = module.exports = {
|
||||
* @memberof @node-red/runtime_nodes
|
||||
*/
|
||||
getNodeConfigs: async function(opts) {
|
||||
if (/[^a-z\-\*]/i.test(opts.lang)) {
|
||||
reject(new Error("Invalid language: "+opts.lang));
|
||||
return
|
||||
}
|
||||
runtime.log.audit({event: "nodes.configs.get"}, opts.req);
|
||||
return runtime.nodes.getNodeConfigs(opts.lang);
|
||||
},
|
||||
@@ -374,6 +382,10 @@ var api = module.exports = {
|
||||
getModuleCatalogs: async function(opts) {
|
||||
var namespace = opts.module;
|
||||
var lang = opts.lang;
|
||||
if (/[^a-z\-\*]/i.test(lang)) {
|
||||
reject(new Error("Invalid language: "+lang));
|
||||
return
|
||||
}
|
||||
var prevLang = runtime.i18n.i.language;
|
||||
// Trigger a load from disk of the language if it is not the default
|
||||
return new Promise( (resolve,reject) => {
|
||||
@@ -404,6 +416,10 @@ var api = module.exports = {
|
||||
getModuleCatalog: async function(opts) {
|
||||
var namespace = opts.module;
|
||||
var lang = opts.lang;
|
||||
if (/[^a-z\-\*]/i.test(lang)) {
|
||||
reject(new Error("Invalid language: "+lang));
|
||||
return
|
||||
}
|
||||
var prevLang = runtime.i18n.i.language;
|
||||
// Trigger a load from disk of the language if it is not the default
|
||||
return new Promise(resolve => {
|
||||
|
95
packages/node_modules/@node-red/runtime/lib/api/plugins.js
vendored
Normal file
95
packages/node_modules/@node-red/runtime/lib/api/plugins.js
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* @mixin @node-red/runtime_plugins
|
||||
*/
|
||||
|
||||
var runtime;
|
||||
|
||||
var api = module.exports = {
|
||||
init: function(_runtime) {
|
||||
runtime = _runtime;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets a plugin definition from the registry
|
||||
* @param {Object} opts
|
||||
* @param {String} opts.id - the id of the plugin to get
|
||||
* @param {User} opts.user - the user calling the api
|
||||
* @param {Object} opts.req - the request to log (optional)
|
||||
* @return {Promise<PluginDefinition>} - the plugin definition
|
||||
* @memberof @node-red/runtime_plugins
|
||||
*/
|
||||
getPlugin: async function(opts) {
|
||||
return runtime.plugins.getPlugin(opts.id);
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets all plugin definitions of a given type
|
||||
* @param {Object} opts
|
||||
* @param {String} opts.type - the type of the plugins to get
|
||||
* @param {User} opts.user - the user calling the api
|
||||
* @param {Object} opts.req - the request to log (optional)
|
||||
* @return {Promise<Array>} - the plugin definitions
|
||||
* @memberof @node-red/runtime_plugins
|
||||
*/
|
||||
getPluginsByType: async function(opts) {
|
||||
return runtime.plugins.getPluginsByType(opts.type);
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the editor content for an individual plugin
|
||||
* @param {String} opts.lang - the locale language to return
|
||||
* @param {User} opts.user - the user calling the api
|
||||
* @param {Object} opts.req - the request to log (optional)
|
||||
* @return {Promise<NodeInfo>} - the node information
|
||||
* @memberof @node-red/runtime_plugins
|
||||
*/
|
||||
getPluginList: async function(opts) {
|
||||
runtime.log.audit({event: "plugins.list.get"}, opts.req);
|
||||
return runtime.plugins.getPluginList();
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the editor content for all registered plugins
|
||||
* @param {Object} opts
|
||||
* @param {User} opts.user - the user calling the api
|
||||
* @param {User} opts.user - the user calling the api
|
||||
* @param {Object} opts.req - the request to log (optional)
|
||||
* @return {Promise<NodeInfo>} - the node information
|
||||
* @memberof @node-red/runtime_plugins
|
||||
*/
|
||||
getPluginConfigs: async function(opts) {
|
||||
if (/[^a-z\-]/i.test(opts.lang)) {
|
||||
throw new Error("Invalid language: "+opts.lang)
|
||||
return;
|
||||
}
|
||||
runtime.log.audit({event: "plugins.configs.get"}, opts.req);
|
||||
return runtime.plugins.getPluginConfigs(opts.lang);
|
||||
},
|
||||
/**
|
||||
* Gets all registered module message catalogs
|
||||
* @param {Object} opts
|
||||
* @param {User} opts.user - the user calling the api
|
||||
* @param {User} opts.lang - the i18n language to return. If not set, uses runtime default (en-US)
|
||||
* @param {Object} opts.req - the request to log (optional)
|
||||
* @return {Promise<Object>} - the message catalogs
|
||||
* @memberof @node-red/runtime_plugins
|
||||
*/
|
||||
getPluginCatalogs: async function(opts) {
|
||||
var lang = opts.lang;
|
||||
var prevLang = runtime.i18n.i.language;
|
||||
// Trigger a load from disk of the language if it is not the default
|
||||
return new Promise( (resolve,reject) => {
|
||||
runtime.i18n.i.changeLanguage(lang, function(){
|
||||
var nodeList = runtime.plugins.getPluginList();
|
||||
var result = {};
|
||||
nodeList.forEach(function(n) {
|
||||
if (n.module !== "node-red") {
|
||||
result[n.id] = runtime.i18n.i.getResourceBundle(lang, n.id)||{};
|
||||
}
|
||||
});
|
||||
runtime.i18n.i.changeLanguage(prevLang);
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
},
|
||||
}
|
@@ -21,6 +21,7 @@ var flows = require("./flows");
|
||||
var storage = require("./storage");
|
||||
var library = require("./library");
|
||||
var hooks = require("./hooks");
|
||||
var plugins = require("./plugins");
|
||||
var settings = require("./settings");
|
||||
|
||||
var express = require("express");
|
||||
@@ -281,6 +282,7 @@ var runtime = {
|
||||
storage: storage,
|
||||
hooks: hooks,
|
||||
nodes: redNodes,
|
||||
plugins: plugins,
|
||||
flows: flows,
|
||||
library: library,
|
||||
exec: exec,
|
||||
@@ -342,6 +344,12 @@ module.exports = {
|
||||
*/
|
||||
context: externalAPI.context,
|
||||
|
||||
/**
|
||||
* @memberof @node-red/runtime
|
||||
* @mixes @node-red/runtime_plugins
|
||||
*/
|
||||
plugins: externalAPI.plugins,
|
||||
|
||||
/**
|
||||
* Returns whether the runtime is started
|
||||
* @param {Object} opts
|
||||
|
10
packages/node_modules/@node-red/runtime/lib/plugins.js
vendored
Normal file
10
packages/node_modules/@node-red/runtime/lib/plugins.js
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
const registry = require("@node-red/registry");
|
||||
|
||||
module.exports = {
|
||||
init: function() {},
|
||||
registerPlugin: registry.registerPlugin,
|
||||
getPlugin: registry.getPlugin,
|
||||
getPluginsByType: registry.getPluginsByType,
|
||||
getPluginList: registry.getPluginList,
|
||||
getPluginConfigs: registry.getPluginConfigs,
|
||||
}
|
@@ -305,6 +305,9 @@ Project.prototype.update = async function (user, data) {
|
||||
return new Error("Invalid package file: "+data.files.package)
|
||||
}
|
||||
var root = data.files.package.substring(0,data.files.package.length-12);
|
||||
if (/^\.\./.test(fspath.relative(this.path,fspath.join(this.path,data.files.package)))) {
|
||||
return Promise.reject("Invalid package file: "+data.files.package)
|
||||
}
|
||||
this.paths.root = root;
|
||||
this.paths['package.json'] = data.files.package;
|
||||
globalProjectSettings.projects[this.name].rootPath = root;
|
||||
@@ -322,12 +325,18 @@ Project.prototype.update = async function (user, data) {
|
||||
}
|
||||
|
||||
if (data.files.hasOwnProperty('flow') && this.package['node-red'].settings.flowFile !== data.files.flow.substring(this.paths.root.length)) {
|
||||
if (/^\.\./.test(fspath.relative(this.path,fspath.join(this.path,data.files.flow)))) {
|
||||
return Promise.reject("Invalid flow file: "+data.files.flow)
|
||||
}
|
||||
this.paths.flowFile = data.files.flow;
|
||||
this.package['node-red'].settings.flowFile = data.files.flow.substring(this.paths.root.length);
|
||||
savePackage = true;
|
||||
flowFilesChanged = true;
|
||||
}
|
||||
if (data.files.hasOwnProperty('credentials') && this.package['node-red'].settings.credentialsFile !== data.files.credentials.substring(this.paths.root.length)) {
|
||||
if (/^\.\./.test(fspath.relative(this.path,fspath.join(this.path,data.files.credentials)))) {
|
||||
return Promise.reject("Invalid credentials file: "+data.files.credentials)
|
||||
}
|
||||
this.paths.credentialsFile = data.files.credentials;
|
||||
this.package['node-red'].settings.credentialsFile = data.files.credentials.substring(this.paths.root.length);
|
||||
// Don't know if the credSecret is invalid or not so clear the flag
|
||||
@@ -490,6 +499,10 @@ Project.prototype.getFile = function (filePath,treeish) {
|
||||
if (treeish !== "_") {
|
||||
return gitTools.getFile(this.path, filePath, treeish);
|
||||
} else {
|
||||
let fullPath = fspath.join(this.path,filePath);
|
||||
if (/^\.\./.test(fspath.relative(this.path,fullPath))) {
|
||||
throw new Error("Invalid file name")
|
||||
}
|
||||
return fs.readFile(fspath.join(this.path,filePath),"utf8");
|
||||
}
|
||||
};
|
||||
@@ -639,6 +652,11 @@ Project.prototype.pull = function (user,remoteBranchName,setRemote,allowUnrelate
|
||||
|
||||
Project.prototype.resolveMerge = function (file,resolutions) {
|
||||
var filePath = fspath.join(this.path,file);
|
||||
|
||||
if (/^\.\./.test(fspath.relative(this.path,filePath))) {
|
||||
throw new Error("Invalid file name")
|
||||
}
|
||||
|
||||
var self = this;
|
||||
if (typeof resolutions === 'string') {
|
||||
return util.writeFile(filePath, resolutions).then(function() {
|
||||
@@ -1047,7 +1065,10 @@ function loadProject(projectPath) {
|
||||
function init(_settings, _runtime) {
|
||||
settings = _settings;
|
||||
runtime = _runtime;
|
||||
projectsDir = fspath.join(settings.userDir,"projects");
|
||||
projectsDir = fspath.resolve(fspath.join(settings.userDir,"projects"));
|
||||
if(settings.editorTheme.projects.path) {
|
||||
projectsDir = settings.editorTheme.projects.path;
|
||||
}
|
||||
authCache.init();
|
||||
}
|
||||
|
||||
|
@@ -113,7 +113,13 @@ function init(_settings, _runtime) {
|
||||
globalGitUser = gitConfig.user;
|
||||
Projects.init(settings,runtime);
|
||||
sshTools.init(settings);
|
||||
projectsDir = fspath.join(settings.userDir,"projects");
|
||||
|
||||
projectsDir = fspath.resolve(fspath.join(settings.userDir,"projects"));
|
||||
|
||||
if(settings.editorTheme.projects.path) {
|
||||
projectsDir = settings.editorTheme.projects.path;
|
||||
}
|
||||
|
||||
if (!settings.readOnly) {
|
||||
return fs.ensureDir(projectsDir)
|
||||
//TODO: this is accessing settings from storage directly as settings
|
||||
@@ -210,9 +216,16 @@ function getBackupFilename(filename) {
|
||||
}
|
||||
|
||||
function loadProject(name) {
|
||||
let fullPath = fspath.resolve(fspath.join(projectsDir,name));
|
||||
var projectPath = name;
|
||||
if (projectPath.indexOf(fspath.sep) === -1) {
|
||||
projectPath = fspath.join(projectsDir,name);
|
||||
projectPath = fullPath;
|
||||
} else {
|
||||
// Ensure this project dir is under projectsDir;
|
||||
let relativePath = fspath.relative(projectsDir,fullPath);
|
||||
if (/^\.\./.test(relativePath)) {
|
||||
throw new Error("Invalid project name")
|
||||
}
|
||||
}
|
||||
return Projects.load(projectPath).then(function(project) {
|
||||
activeProject = project;
|
||||
@@ -236,6 +249,10 @@ function deleteProject(user, name) {
|
||||
throw e;
|
||||
}
|
||||
var projectPath = fspath.join(projectsDir,name);
|
||||
let relativePath = fspath.relative(projectsDir,projectPath);
|
||||
if (/^\.\./.test(relativePath)) {
|
||||
throw new Error("Invalid project name")
|
||||
}
|
||||
return Projects.delete(user, projectPath);
|
||||
}
|
||||
|
||||
@@ -394,6 +411,10 @@ function createProject(user, metadata) {
|
||||
metadata.files.credentialSecret = currentEncryptionKey;
|
||||
}
|
||||
metadata.path = fspath.join(projectsDir,metadata.name);
|
||||
if (/^\.\./.test(fspath.relative(projectsDir,metadata.path))) {
|
||||
throw new Error("Invalid project name")
|
||||
}
|
||||
|
||||
return Projects.create(user, metadata).then(function(p) {
|
||||
return setActiveProject(user, p.name);
|
||||
}).then(function() {
|
||||
@@ -501,6 +522,11 @@ async function getFlows() {
|
||||
if (!initialFlowLoadComplete) {
|
||||
initialFlowLoadComplete = true;
|
||||
log.info(log._("storage.localfilesystem.user-dir",{path:settings.userDir}));
|
||||
|
||||
if (projectsEnabled) {
|
||||
log.info(log._("storage.localfilesystem.projects.projects-directory", {projectsDirectory: projectsDir}));
|
||||
}
|
||||
|
||||
if (activeProject) {
|
||||
// At this point activeProject will be a string, so go load it and
|
||||
// swap in an instance of Project
|
||||
|
@@ -156,6 +156,7 @@
|
||||
"projects": {
|
||||
"changing-project": "Setting active project : __project__",
|
||||
"active-project": "Active project : __project__",
|
||||
"projects-directory": "Projects directory: __projectsDirectory__",
|
||||
"project-not-found": "Project not found : __project__",
|
||||
"no-active-project": "No active project : using default flows file",
|
||||
"disabled": "Projects disabled : editorTheme.projects.enabled=false",
|
||||
|
Reference in New Issue
Block a user