Initial commit: Nextcloud Node-RED Docker image and custom nodes
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
module.exports = function(RED) {
|
||||
var https = require('https');
|
||||
var http = require('http');
|
||||
var url = require('url');
|
||||
|
||||
var OPERATIONS = {
|
||||
// Read/List
|
||||
'list': { method: 'PROPFIND', path: '/remote.php/dav/files/{user}/{folder}' },
|
||||
'get': { method: 'GET', path: '/remote.php/dav/files/{user}/{path}' },
|
||||
// Write
|
||||
'upload': { method: 'PUT', path: '/remote.php/dav/files/{user}/{path}' },
|
||||
'mkdir': { method: 'MKCOL', path: '/remote.php/dav/files/{user}/{folder}' },
|
||||
// Modify
|
||||
'move': { method: 'MOVE', path: '/remote.php/dav/files/{user}/{path}', destHeader: true },
|
||||
'copy': { method: 'COPY', path: '/remote.php/dav/files/{user}/{path}', destHeader: true },
|
||||
// Delete
|
||||
'delete': { method: 'DELETE', path: '/remote.php/dav/files/{user}/{path}' },
|
||||
// OCS sharing
|
||||
'listShares': { method: 'GET', path: '/ocs/v2.php/apps/files_sharing/api/v1/shares' },
|
||||
'createShare': { method: 'POST', path: '/ocs/v2.php/apps/files_sharing/api/v1/shares', body: ['path','shareType','permissions','shareWith'] },
|
||||
'deleteShare': { method: 'DELETE', path: '/ocs/v2.php/apps/files_sharing/api/v1/shares/{id}' },
|
||||
// File info
|
||||
'fileInfo': { method: 'GET', path: '/ocs/v2.php/apps/files/api/v1/stats' },
|
||||
'favorites': { method: 'GET', path: '/ocs/v2.php/apps/files/api/v1/files' }
|
||||
};
|
||||
|
||||
function FileOperationsNode(config) {
|
||||
RED.nodes.createNode(this, config);
|
||||
var node = this;
|
||||
this.configNode = RED.nodes.getNode(config.nextcloud);
|
||||
this.operation = config.operation || 'list';
|
||||
this.ncUser = config.ncUser || '';
|
||||
this.ncPath = config.ncPath || '';
|
||||
this.ncFolder = config.ncFolder || '';
|
||||
this.destination = config.destination || '';
|
||||
this.bodySharePath = config.bodySharePath;
|
||||
this.bodyShareType = config.bodyShareType;
|
||||
this.bodyPermissions = config.bodyPermissions;
|
||||
this.bodyShareWith = config.bodyShareWith;
|
||||
|
||||
if (!this.configNode) {
|
||||
node.error("No Nextcloud configuration node selected");
|
||||
return;
|
||||
}
|
||||
|
||||
node.on('input', function(msg) {
|
||||
var op = OPERATIONS[msg.operation || node.operation];
|
||||
if (!op) {
|
||||
node.error("Unknown operation: " + (msg.operation || node.operation), msg);
|
||||
return;
|
||||
}
|
||||
|
||||
var baseUrl = (node.configNode.baseUrl || '').replace(/\/+$/, '');
|
||||
var username = node.configNode.credentials.username;
|
||||
var password = node.configNode.credentials.password;
|
||||
|
||||
var ncUser = msg.ncUser || node.ncUser || username;
|
||||
var ncPath = msg.ncPath || node.ncPath || '';
|
||||
var ncFolder = msg.ncFolder || node.ncFolder || '';
|
||||
var dest = msg.destination || node.destination || '';
|
||||
|
||||
var path = op.path
|
||||
.replace('{user}', encodeURIComponent(ncUser))
|
||||
.replace('{path}', ncPath)
|
||||
.replace('{folder}', ncFolder)
|
||||
.replace('{id}', msg.id || '');
|
||||
|
||||
var fullUrl = baseUrl + path;
|
||||
var parsedUrl = url.parse(fullUrl);
|
||||
|
||||
var headers = {
|
||||
'Authorization': 'Basic ' + Buffer.from(username + ':' + password).toString('base64')
|
||||
};
|
||||
|
||||
// WebDAV requests need depth header for PROPFIND
|
||||
if (op.method === 'PROPFIND') {
|
||||
headers['Depth'] = msg.depth || '1';
|
||||
headers['Content-Type'] = 'application/xml';
|
||||
}
|
||||
|
||||
// MOVE/COPY use Destination header
|
||||
if (op.destHeader && dest) {
|
||||
headers['Destination'] = baseUrl + '/remote.php/dav/files/' + encodeURIComponent(ncUser) + '/' + dest;
|
||||
}
|
||||
|
||||
// OCS sharing headers
|
||||
if (op.path.indexOf('/ocs/') === 0) {
|
||||
headers['OCS-APIRequest'] = 'true';
|
||||
headers['Accept'] = 'application/json';
|
||||
}
|
||||
|
||||
if (msg.headers && typeof msg.headers === 'object') {
|
||||
Object.keys(msg.headers).forEach(function(k) {
|
||||
headers[k] = msg.headers[k];
|
||||
});
|
||||
}
|
||||
|
||||
var bodyStr = null;
|
||||
if (op.body) {
|
||||
var bodyObj = {};
|
||||
op.body.forEach(function(b) {
|
||||
var val = msg[b] !== undefined ? msg[b] : getBodyField(node, b);
|
||||
if (val !== undefined && val !== '') {
|
||||
bodyObj[b] = val;
|
||||
}
|
||||
});
|
||||
if (Object.keys(bodyObj).length > 0) {
|
||||
headers['Content-Type'] = 'application/json';
|
||||
bodyStr = JSON.stringify(bodyObj);
|
||||
}
|
||||
}
|
||||
if (msg.body) {
|
||||
headers['Content-Type'] = msg.contentType || 'application/json';
|
||||
bodyStr = typeof msg.body === 'string' ? msg.body : JSON.stringify(msg.body);
|
||||
}
|
||||
if (msg.payload && op.method === 'PUT') {
|
||||
bodyStr = typeof msg.payload === 'string' ? msg.payload : JSON.stringify(msg.payload);
|
||||
}
|
||||
|
||||
var opts = {
|
||||
hostname: parsedUrl.hostname,
|
||||
port: parsedUrl.port || (parsedUrl.protocol === 'https:' ? 443 : 80),
|
||||
path: parsedUrl.path,
|
||||
method: op.method,
|
||||
headers: headers,
|
||||
rejectUnauthorized: false
|
||||
};
|
||||
|
||||
var transport = parsedUrl.protocol === 'https:' ? https : http;
|
||||
|
||||
node.log(op.method + ' ' + fullUrl);
|
||||
|
||||
var req = transport.request(opts, function(res) {
|
||||
var body = '';
|
||||
res.on('data', function(chunk) { body += chunk; });
|
||||
res.on('end', function() {
|
||||
msg.statusCode = res.statusCode;
|
||||
msg.operation = msg.operation || node.operation;
|
||||
// Try JSON, fallback to XML/text
|
||||
try { msg.payload = JSON.parse(body); }
|
||||
catch(e) {
|
||||
// PROPFIND returns XML
|
||||
msg.payload = body;
|
||||
}
|
||||
node.send(msg);
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', function(err) {
|
||||
msg.error = err.message;
|
||||
node.error(op.method + ' ' + fullUrl + ' — ' + err.message, msg);
|
||||
});
|
||||
|
||||
if (bodyStr) {
|
||||
req.write(bodyStr);
|
||||
}
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
function getBodyField(node, name) {
|
||||
var map = {
|
||||
'path': node.bodySharePath, 'shareType': node.bodyShareType,
|
||||
'permissions': node.bodyPermissions, 'shareWith': node.bodyShareWith
|
||||
};
|
||||
return map[name];
|
||||
}
|
||||
|
||||
RED.nodes.registerType("file-operations", FileOperationsNode);
|
||||
};
|
||||
Reference in New Issue
Block a user