module.exports = function(RED) { var https = require('https'); var http = require('http'); var url = require('url'); var OPERATIONS = { // Tables (api v2 OCS) 'table:list': { method:'GET', path:'/ocs/v2.php/apps/tables/api/2/tables' }, 'table:get': { method:'GET', path:'/ocs/v2.php/apps/tables/api/2/tables/{id}' }, 'table:create': { method:'POST', path:'/ocs/v2.php/apps/tables/api/2/tables', body:['title','emoji','description','template'] }, 'table:update': { method:'PUT', path:'/ocs/v2.php/apps/tables/api/2/tables/{id}', body:['title','emoji','description','archived'] }, 'table:delete': { method:'DELETE', path:'/ocs/v2.php/apps/tables/api/2/tables/{id}' }, 'table:scheme': { method:'GET', path:'/ocs/v2.php/apps/tables/api/2/tables/scheme/{id}' }, 'table:transfer': { method:'PUT', path:'/ocs/v2.php/apps/tables/api/2/tables/{id}/transfer', body:['newOwnerUserId'] }, // Init 'init:index': { method:'GET', path:'/ocs/v2.php/apps/tables/api/2/init' }, // Columns (api v2) 'column:list': { method:'GET', path:'/ocs/v2.php/apps/tables/api/2/columns/{nodeType}/{nodeId}' }, 'column:get': { method:'GET', path:'/ocs/v2.php/apps/tables/api/2/columns/{id}' }, 'column:text': { method:'POST', path:'/ocs/v2.php/apps/tables/api/2/columns/text', body:['baseNodeId','title','textDefault','textMaxLength','textUnique','mandatory','baseNodeType','selectedViewIds'] }, 'column:number': { method:'POST', path:'/ocs/v2.php/apps/tables/api/2/columns/number', body:['baseNodeId','title','numberDefault','numberDecimals','numberMin','numberMax','mandatory','baseNodeType'] }, 'column:selection': { method:'POST', path:'/ocs/v2.php/apps/tables/api/2/columns/selection', body:['baseNodeId','title','selectionOptions','selectionDefault','mandatory','baseNodeType'] }, 'column:datetime': { method:'POST', path:'/ocs/v2.php/apps/tables/api/2/columns/datetime', body:['baseNodeId','title','datetimeDefault','mandatory','baseNodeType'] }, 'column:usergroup': { method:'POST', path:'/ocs/v2.php/apps/tables/api/2/columns/usergroup', body:['baseNodeId','title','usergroupDefault','usergroupMultipleItems','usergroupSelectUsers','usergroupSelectGroups','mandatory','baseNodeType'] }, // Rows (api v2) 'row:list': { method:'GET', path:'/ocs/v2.php/apps/tables/api/2/{nodeCollection}/{nodeId}/rows', query:['limit','offset'] }, 'row:create': { method:'POST', path:'/ocs/v2.php/apps/tables/api/2/{nodeCollection}/{nodeId}/rows', body:['data'] }, // Generic row ops (api v1) 'row:get': { method:'GET', path:'/index.php/apps/tables/api/1/rows/{rowId}' }, 'row:update': { method:'PUT', path:'/index.php/apps/tables/api/1/rows/{rowId}', body:['data'] }, 'row:delete': { method:'DELETE', path:'/index.php/apps/tables/api/1/rows/{rowId}' }, // Views 'view:list': { method:'GET', path:'/index.php/apps/tables/api/1/tables/{tableId}/views' }, 'view:get': { method:'GET', path:'/index.php/apps/tables/api/1/views/{viewId}' }, 'view:create': { method:'POST', path:'/index.php/apps/tables/api/1/tables/{tableId}/views', body:['title','emoji'] }, 'view:update': { method:'PUT', path:'/index.php/apps/tables/api/1/views/{viewId}', body:['data'] }, 'view:delete': { method:'DELETE', path:'/index.php/apps/tables/api/1/views/{viewId}' }, // Shares 'share:listTable': { method:'GET', path:'/index.php/apps/tables/api/1/tables/{tableId}/shares' }, 'share:listView': { method:'GET', path:'/index.php/apps/tables/api/1/views/{viewId}/shares' }, 'share:create': { method:'POST', path:'/index.php/apps/tables/api/1/shares', body:['nodeId','nodeType','receiver','receiverType','permissionRead','permissionCreate','permissionUpdate','permissionDelete','permissionManage'] }, 'share:update': { method:'PUT', path:'/index.php/apps/tables/api/1/shares/{shareId}', body:['permissionType','permissionValue'] }, 'share:delete': { method:'DELETE', path:'/index.php/apps/tables/api/1/shares/{shareId}' }, 'share:linkCreate': { method:'POST', path:'/ocs/v2.php/apps/tables/api/2/{nodeCollection}/{nodeId}/share', body:['password'] }, // Contexts 'context:list': { method:'GET', path:'/ocs/v2.php/apps/tables/api/2/contexts' }, 'context:get': { method:'GET', path:'/ocs/v2.php/apps/tables/api/2/contexts/{contextId}' }, 'context:create': { method:'POST', path:'/ocs/v2.php/apps/tables/api/2/contexts', body:['name','iconName','description'] }, 'context:update': { method:'PUT', path:'/ocs/v2.php/apps/tables/api/2/contexts/{contextId}', body:['name','iconName','description'] }, 'context:delete': { method:'DELETE', path:'/ocs/v2.php/apps/tables/api/2/contexts/{contextId}' }, // Favorites 'favorite:add': { method:'POST', path:'/ocs/v2.php/apps/tables/api/2/favorites/{nodeType}/{nodeId}' }, 'favorite:remove': { method:'DELETE', path:'/ocs/v2.php/apps/tables/api/2/favorites/{nodeType}/{nodeId}' }, // Import 'import:table': { method:'POST', path:'/index.php/apps/tables/api/1/import/table/{tableId}', body:['path','createMissingColumns'] }, // Public rows 'public:rows': { method:'GET', path:'/ocs/v2.php/apps/tables/api/2/public/{token}/rows', query:['limit','offset'] }, 'public:columns': { method:'GET', path:'/ocs/v2.php/apps/tables/api/2/public/{token}/columns' } }; function TablesNode(config) { RED.nodes.createNode(this, config); var node = this; this.configNode = RED.nodes.getNode(config.nextcloud); this.operation = config.operation || 'table:list'; this.tableId = config.tableId; this.viewId = config.viewId; this.rowId = config.rowId; this.shareId = config.shareId; this.nodeId = config.nodeId; this.nodeType = config.nodeType; this.nodeCollection = config.nodeCollection; this.token = config.token; this.contextId = config.contextId; this.bodyTitle = config.bodyTitle; this.bodyEmoji = config.bodyEmoji; this.bodyData = config.bodyData; this.bodyName = config.bodyName; this.bodyReceiver = config.bodyReceiver; this.bodyReceiverType = config.bodyReceiverType; this.bodyBaseNodeId = config.bodyBaseNodeId; this.bodyBaseNodeType = config.bodyBaseNodeType; 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 path = op.path .replace(/\{id\}/g, msg.id || node.nodeId || '') .replace(/\{tableId\}/g, msg.tableId || node.tableId || '') .replace(/\{viewId\}/g, msg.viewId || node.viewId || '') .replace(/\{rowId\}/g, msg.rowId || node.rowId || '') .replace(/\{shareId\}/g, msg.shareId || node.shareId || '') .replace(/\{nodeId\}/g, msg.nodeId || node.nodeId || '') .replace(/\{nodeType\}/g, msg.nodeType || node.nodeType || 'table') .replace(/\{nodeCollection\}/g, msg.nodeCollection || node.nodeCollection || 'tables') .replace(/\{token\}/g, msg.token || node.token || '') .replace(/\{contextId\}/g, msg.contextId || node.contextId || ''); var queryParts = []; if (op.query) { op.query.forEach(function(q) { var v = msg[q] !== undefined ? msg[q] : getField(node, q); if (v !== undefined && v !== '') queryParts.push(encodeURIComponent(q) + '=' + encodeURIComponent(v)); }); } var fullUrl = baseUrl + path + (queryParts.length ? '?' + queryParts.join('&') : ''); var parsedUrl = url.parse(fullUrl); var headers = { 'Accept': 'application/json', 'Authorization': 'Basic ' + Buffer.from(username + ':' + password).toString('base64') }; if (path.indexOf('/ocs/') === 0) headers['OCS-APIRequest'] = 'true'; var bodyObj = {}; if (op.body) { op.body.forEach(function(b) { var v = msg[b] !== undefined ? msg[b] : getField(node, b); if (v !== undefined && v !== '') bodyObj[b] = v; }); } if (msg.data !== undefined) bodyObj.data = msg.data; if (msg.body && typeof msg.body === 'object') Object.assign(bodyObj, msg.body); var bodyStr = null; if (Object.keys(bodyObj).length > 0) { headers['Content-Type'] = 'application/json'; bodyStr = JSON.stringify(bodyObj); } var opts = { hostname: parsedUrl.hostname, port: parsedUrl.port || (parsedUrl.protocol === 'https:' ? 443 : 80), path: parsedUrl.path, method: op.method, headers: headers, rejectUnauthorized: false }; node.log(op.method + ' ' + fullUrl); var transport = parsedUrl.protocol === 'https:' ? https : http; 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 { msg.payload = JSON.parse(body); } catch(e) { 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 getField(node, name) { var m = { 'title': node.bodyTitle, 'emoji': node.bodyEmoji, 'name': node.bodyName, 'receiver': node.bodyReceiver, 'receiverType': node.bodyReceiverType, 'baseNodeId': node.bodyBaseNodeId, 'baseNodeType': node.bodyBaseNodeType, 'data': node.bodyData, 'limit': node.bodyLimit, 'offset': node.bodyOffset, 'password': node.bodyPassword, 'description': node.bodyDescription, 'template': node.bodyTemplate, 'nodeId': node.nodeId, 'nodeType': node.nodeType, 'newOwnerUserId': node.bodyNewOwnerId }; return m[name]; } RED.nodes.registerType("tables", TablesNode); };