module.exports = function(RED) { var https = require('https'); var http = require('http'); var url = require('url'); // Operation definitions: operationId -> { method, pathTemplate, queryParams } var OPERATIONS = { // --- collective --- 'collective:list': { method: 'GET', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives' }, 'collective:create': { method: 'POST', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives', body: ['name','emoji'] }, 'collective:update': { method: 'PUT', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{id}', body: ['emoji'] }, 'collective:trash': { method: 'DELETE', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{id}' }, 'collective:editLevel': { method: 'PUT', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{id}/editLevel', body: ['level'] }, 'collective:shareLevel': { method: 'PUT', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{id}/shareLevel', body: ['level'] }, 'collective:pageMode': { method: 'PUT', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{id}/pageMode', body: ['mode'] }, // --- trash --- 'trash:list': { method: 'GET', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/trash' }, 'trash:restore': { method: 'PATCH', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/trash/{id}' }, 'trash:delete': { method: 'DELETE', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/trash/{id}', query: ['circle'] }, // --- share --- 'share:listCollectiveShares': { method: 'GET', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/shares' }, 'share:createCollectiveShare': { method: 'POST', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/shares', body: ['password'] }, 'share:updateCollectiveShare': { method: 'PUT', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/shares/{token}', body: ['editable','password'] }, 'share:deleteCollectiveShare': { method: 'DELETE', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/shares/{token}' }, 'share:createPageShare': { method: 'POST', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/pages/{pageId}/shares', body: ['password'] }, 'share:updatePageShare': { method: 'PUT', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/pages/{pageId}/shares/{token}', body: ['editable','password'] }, 'share:deletePageShare': { method: 'DELETE', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/pages/{pageId}/shares/{token}' }, // --- search --- 'search:recentPages': { method: 'GET', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/search/recent', query: ['query','limit'] }, 'search:content': { method: 'GET', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/search', query: ['searchString'] }, // --- page --- 'page:list': { method: 'GET', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/pages' }, 'page:get': { method: 'GET', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/pages/{id}' }, 'page:create': { method: 'POST', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/pages/{parentId}', body: ['title','templateId'] }, 'page:moveOrCopy': { method: 'PUT', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/pages/{id}', body: ['parentId','title','index','copy'] }, 'page:trash': { method: 'DELETE', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/pages/{id}' }, 'page:touch': { method: 'GET', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/pages/{id}/touch' }, 'page:fullWidth': { method: 'PUT', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/pages/{id}/fullWidth', body: ['fullWidth'] }, 'page:emoji': { method: 'PUT', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/pages/{id}/emoji', body: ['emoji'] }, 'page:subpageOrder': { method: 'PUT', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/pages/{id}/subpageOrder', body: ['subpageOrder'] }, 'page:moveToCollective': { method: 'PUT', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/pages/{id}/to/{newCollectiveId}', body: ['parentId','index','copy'] }, 'page:addTag': { method: 'PUT', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/pages/{id}/tags/{tagId}' }, 'page:removeTag': { method: 'DELETE', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/pages/{id}/tags/{tagId}' }, 'page:listAttachments': { method: 'GET', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/pages/{id}/attachments' }, 'page:uploadAttachment': { method: 'POST', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/pages/{id}/attachments' }, 'page:renameAttachment': { method: 'PUT', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/pages/{id}/attachments/{attachmentId}', body: ['name'] }, 'page:deleteAttachment': { method: 'DELETE', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/pages/{id}/attachments/{attachmentId}' }, 'page:restoreAttachment': { method: 'PATCH', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/pages/{id}/attachments/trash/{attachmentId}' }, // --- page_trash --- 'page_trash:list': { method: 'GET', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/pages/trash' }, 'page_trash:restore': { method: 'PATCH', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/pages/trash/{id}' }, 'page_trash:delete': { method: 'DELETE', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/pages/trash/{id}' }, // --- template --- 'template:list': { method: 'GET', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/pages/templates' }, 'template:create': { method: 'POST', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/pages/templates/{id}', body: ['title','parentId'] }, 'template:rename': { method: 'PUT', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/pages/templates/{id}', body: ['title'] }, 'template:delete': { method: 'DELETE', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/pages/templates/{id}' }, 'template:emoji': { method: 'PUT', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/pages/templates/{id}/emoji', body: ['emoji'] }, // --- tag --- 'tag:list': { method: 'GET', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/tags' }, 'tag:create': { method: 'POST', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/tags', body: ['name','color'] }, 'tag:update': { method: 'PUT', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/tags/{id}', body: ['name','color'] }, 'tag:delete': { method: 'DELETE', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/tags/{id}' }, // --- collective_user_settings --- 'userSettings:pageOrder': { method: 'PUT', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/userSettings/pageOrder', body: ['pageOrder'] }, 'userSettings:showMembers': { method: 'PUT', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/userSettings/showMembers', body: ['showMembers'] }, 'userSettings:showRecentPages':{ method: 'PUT', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/userSettings/showRecentPages', body: ['showRecentPages'] }, 'userSettings:favoritePages': { method: 'PUT', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/userSettings/favoritePages', body: ['favoritePages'] }, // --- session --- 'session:create': { method: 'POST', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/sessions' }, 'session:sync': { method: 'PUT', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/sessions', body: ['token'] }, 'session:close': { method: 'DELETE', path: '/ocs/v2.php/apps/collectives/api/v1.0/collectives/{collectiveId}/sessions', query: ['token'] }, // --- settings --- 'settings:get': { method: 'GET', path: '/ocs/v2.php/apps/collectives/api/v1.0/settings/user/{key}' }, 'settings:set': { method: 'POST', path: '/ocs/v2.php/apps/collectives/api/v1.0/settings/user', body: ['key','value'] }, // --- public_collective --- 'public:getCollective': { method: 'GET', path: '/ocs/v2.php/apps/collectives/api/v1.0/p/collectives/{token}' }, // --- public_page --- 'public:listPages': { method: 'GET', path: '/ocs/v2.php/apps/collectives/api/v1.0/p/collectives/{token}/pages' }, 'public:getPage': { method: 'GET', path: '/ocs/v2.php/apps/collectives/api/v1.0/p/collectives/{token}/pages/{id}' }, 'public:createPage': { method: 'POST', path: '/ocs/v2.php/apps/collectives/api/v1.0/p/collectives/{token}/pages/{parentId}', body: ['title','templateId'] }, 'public:moveOrCopyPage': { method: 'PUT', path: '/ocs/v2.php/apps/collectives/api/v1.0/p/collectives/{token}/pages/{id}', body: ['parentId','title','index','copy'] }, 'public:trashPage': { method: 'DELETE', path: '/ocs/v2.php/apps/collectives/api/v1.0/p/collectives/{token}/pages/{id}' }, 'public:touchPage': { method: 'GET', path: '/ocs/v2.php/apps/collectives/api/v1.0/p/collectives/{token}/pages/{id}/touch' }, 'public:fullWidth': { method: 'PUT', path: '/ocs/v2.php/apps/collectives/api/v1.0/p/collectives/{token}/pages/{id}/fullWidth', body: ['fullWidth'] }, 'public:emoji': { method: 'PUT', path: '/ocs/v2.php/apps/collectives/api/v1.0/p/collectives/{token}/pages/{id}/emoji', body: ['emoji'] }, 'public:subpageOrder': { method: 'PUT', path: '/ocs/v2.php/apps/collectives/api/v1.0/p/collectives/{token}/pages/{id}/subpageOrder', body: ['subpageOrder'] }, 'public:addTag': { method: 'PUT', path: '/ocs/v2.php/apps/collectives/api/v1.0/p/collectives/{token}/pages/{id}/tags/{tagId}' }, 'public:removeTag': { method: 'DELETE', path: '/ocs/v2.php/apps/collectives/api/v1.0/p/collectives/{token}/pages/{id}/tags/{tagId}' }, 'public:listAttachments': { method: 'GET', path: '/ocs/v2.php/apps/collectives/api/v1.0/p/collectives/{token}/pages/{id}/attachments' }, 'public:uploadAttachment': { method: 'POST', path: '/ocs/v2.php/apps/collectives/api/v1.0/p/collectives/{token}/pages/{id}/attachments' }, 'public:renameAttachment': { method: 'PUT', path: '/ocs/v2.php/apps/collectives/api/v1.0/p/collectives/{token}/pages/{id}/attachments/{attachmentId}', body: ['name'] }, 'public:deleteAttachment': { method: 'DELETE', path: '/ocs/v2.php/apps/collectives/api/v1.0/p/collectives/{token}/pages/{id}/attachments/{attachmentId}' }, 'public:restoreAttachment': { method: 'PATCH', path: '/ocs/v2.php/apps/collectives/api/v1.0/p/collectives/{token}/pages/{id}/attachments/trash/{attachmentId}' }, 'public:searchContent': { method: 'GET', path: '/ocs/v2.php/apps/collectives/api/v1.0/p/collectives/{token}/search', query: ['searchString'] }, // --- public_page_trash --- 'public:listTrash': { method: 'GET', path: '/ocs/v2.php/apps/collectives/api/v1.0/p/collectives/{token}/pages/trash' }, 'public:restoreTrash': { method: 'PATCH', path: '/ocs/v2.php/apps/collectives/api/v1.0/p/collectives/{token}/pages/trash/{id}' }, 'public:deleteTrash': { method: 'DELETE', path: '/ocs/v2.php/apps/collectives/api/v1.0/p/collectives/{token}/pages/trash/{id}' }, // --- public_template --- 'public:listTemplates': { method: 'GET', path: '/ocs/v2.php/apps/collectives/api/v1.0/p/collectives/{token}/pages/templates' }, // --- public_tag --- 'public:listTags': { method: 'GET', path: '/ocs/v2.php/apps/collectives/api/v1.0/p/collectives/{token}/tags' }, 'public:createTag': { method: 'POST', path: '/ocs/v2.php/apps/collectives/api/v1.0/p/collectives/{token}/tags', body: ['name','color'] }, 'public:updateTag': { method: 'PUT', path: '/ocs/v2.php/apps/collectives/api/v1.0/p/collectives/{token}/tags/{id}', body: ['name','color'] }, 'public:deleteTag': { method: 'DELETE', path: '/ocs/v2.php/apps/collectives/api/v1.0/p/collectives/{token}/tags/{id}' } }; function CollectivesNode(config) { RED.nodes.createNode(this, config); var node = this; this.configNode = RED.nodes.getNode(config.nextcloud); this.operation = config.operation || 'collective:list'; // Path parameter fields from config this.collectiveId = config.collectiveId; this.pageId = config.pageId; this.token = config.token; this.parentId = config.parentId; this.tagId = config.tagId; this.attachmentId = config.attachmentId; this.newCollectiveId = config.newCollectiveId; this.itemId = config.itemId; // generic {id} this.settingKey = config.settingKey; // Body fields this.bodyName = config.bodyName; this.bodyEmoji = config.bodyEmoji; this.bodyColor = config.bodyColor; this.bodyLevel = config.bodyLevel; this.bodyMode = config.bodyMode; this.bodyTitle = config.bodyTitle; this.bodyEditable = config.bodyEditable; this.bodyPassword = config.bodyPassword; this.bodyFullWidth = config.bodyFullWidth; this.bodyTemplateId = config.bodyTemplateId; this.bodyPageOrder = config.bodyPageOrder; this.bodyShowMembers = config.bodyShowMembers; this.bodyShowRecentPages = config.bodyShowRecentPages; this.bodyFavoritePages = config.bodyFavoritePages; this.bodyIndex = config.bodyIndex; this.bodyCopy = config.bodyCopy; this.bodySubpageOrder = config.bodySubpageOrder; this.bodyParentId = config.bodyParentId; this.bodyCircle = config.bodyCircle; this.bodySearchString = config.bodySearchString; this.bodyQuery = config.bodyQuery; this.bodyLimit = config.bodyLimit; this.bodyKey = config.bodyKey; this.bodyValue = config.bodyValue; this.bodySessionToken = config.bodySessionToken; 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; // Build path with substituted parameters (msg overrides config) var path = op.path; var subs = { '{id}': msg.itemId || node.itemId || msg.pageId || node.pageId || '', '{collectiveId}': msg.collectiveId || node.collectiveId || '', '{pageId}': msg.pageId || node.pageId || '', '{parentId}': msg.parentId || node.parentId || '', '{token}': msg.token || node.token || '', '{tagId}': msg.tagId || node.tagId || '', '{attachmentId}': msg.attachmentId || node.attachmentId || '', '{newCollectiveId}': msg.newCollectiveId || node.newCollectiveId || '', '{key}': msg.settingKey || node.settingKey || '' }; Object.keys(subs).forEach(function(k) { path = path.replace(k, subs[k]); }); // Query parameters var queryParts = []; var queryDefs = msg.query || {}; if (op.query) { op.query.forEach(function(q) { var val = msg[q] !== undefined ? msg[q] : getBodyField(node, q); if (val !== undefined && val !== '') { queryParts.push(encodeURIComponent(q) + '=' + encodeURIComponent(val)); } }); } // Also handle query overrides from msg if (typeof msg.query === 'object') { Object.keys(msg.query).forEach(function(k) { if (op.query && op.query.indexOf(k) === -1) { queryParts.push(encodeURIComponent(k) + '=' + encodeURIComponent(msg.query[k])); } }); } var fullUrl = baseUrl + path; if (queryParts.length) { fullUrl += '?' + queryParts.join('&'); } var parsedUrl = url.parse(fullUrl); var headers = { 'OCS-APIRequest': 'true', 'Accept': 'application/json', 'Authorization': 'Basic ' + Buffer.from(username + ':' + password).toString('base64') }; // Build request body var bodyObj = msg.body || {}; if (op.body) { op.body.forEach(function(b) { var val = msg[b] !== undefined ? msg[b] : getBodyField(node, b); if (val !== undefined && val !== '') { bodyObj[b] = val; } }); } var bodyStr = null; if (Object.keys(bodyObj).length > 0) { headers['Content-Type'] = 'application/json'; bodyStr = JSON.stringify(bodyObj); } // Allow raw body override if (msg.rawBody) { bodyStr = typeof msg.rawBody === 'string' ? msg.rawBody : JSON.stringify(msg.rawBody); if (!headers['Content-Type']) headers['Content-Type'] = 'application/json'; } if (msg.headers && typeof msg.headers === 'object') { Object.keys(msg.headers).forEach(function(k) { headers[k] = msg.headers[k]; }); } 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 = 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 getBodyField(node, name) { var map = { 'name': node.bodyName, 'emoji': node.bodyEmoji, 'color': node.bodyColor, 'level': node.bodyLevel, 'mode': node.bodyMode, 'title': node.bodyTitle, 'editable': node.bodyEditable, 'password': node.bodyPassword, 'fullWidth': node.bodyFullWidth, 'templateId': node.bodyTemplateId, 'pageOrder': node.bodyPageOrder, 'showMembers': node.bodyShowMembers, 'showRecentPages': node.bodyShowRecentPages, 'favoritePages': node.bodyFavoritePages, 'index': node.bodyIndex, 'copy': node.bodyCopy, 'subpageOrder': node.bodySubpageOrder, 'parentId': node.bodyParentId, 'circle': node.bodyCircle, 'searchString': node.bodySearchString, 'query': node.bodyQuery, 'limit': node.bodyLimit, 'key': node.bodyKey, 'value': node.bodyValue, 'token': node.bodySessionToken }; return map[name]; } RED.nodes.registerType("collectives", CollectivesNode); };