Initial release v1.0.0 — Nextcloud Collabora Node-RED sidecar
This commit is contained in:
+155
@@ -0,0 +1,155 @@
|
||||
'use strict';
|
||||
|
||||
const { requestBuffer } = require('./http-client');
|
||||
const { normalizeNcPath, filenameFromPath } = require('./utils');
|
||||
|
||||
function authHeader(config) {
|
||||
const token = Buffer.from(`${config.username}:${config.password}`).toString('base64');
|
||||
return `Basic ${token}`;
|
||||
}
|
||||
|
||||
function webDavBase(config) {
|
||||
const base = config.nextcloudUrl.replace(/\/$/, '');
|
||||
return `${base}/remote.php/dav/files/${encodeURIComponent(config.username)}`;
|
||||
}
|
||||
|
||||
function encodePathSegments(ncPath) {
|
||||
const normalized = normalizeNcPath(ncPath);
|
||||
return normalized
|
||||
.split('/')
|
||||
.filter(Boolean)
|
||||
.map((segment) => encodeURIComponent(segment))
|
||||
.join('/');
|
||||
}
|
||||
|
||||
function webDavUrl(config, ncPath) {
|
||||
const rel = encodePathSegments(ncPath);
|
||||
return rel ? `${webDavBase(config)}/${rel}` : webDavBase(config);
|
||||
}
|
||||
|
||||
function tlsOptions(config) {
|
||||
return { rejectUnauthorized: config.verifyTls !== false && config.verifyTls !== 'false' };
|
||||
}
|
||||
|
||||
async function webdavGet(config, ncPath) {
|
||||
const url = webDavUrl(config, ncPath);
|
||||
const res = await requestBuffer({
|
||||
url,
|
||||
method: 'GET',
|
||||
headers: { Authorization: authHeader(config) },
|
||||
rejectUnauthorized: tlsOptions(config).rejectUnauthorized
|
||||
});
|
||||
|
||||
if (res.statusCode === 404) {
|
||||
throw new Error(`WebDAV 404: filen finns inte: ${normalizeNcPath(ncPath)}`);
|
||||
}
|
||||
if (res.statusCode === 401 || res.statusCode === 403) {
|
||||
throw new Error(`WebDAV ${res.statusCode}: kontrollera app-lösenord och användare`);
|
||||
}
|
||||
if (res.statusCode < 200 || res.statusCode >= 300) {
|
||||
throw new Error(`WebDAV GET misslyckades (${res.statusCode}) för ${normalizeNcPath(ncPath)}`);
|
||||
}
|
||||
|
||||
return {
|
||||
buffer: res.body,
|
||||
filename: filenameFromPath(ncPath),
|
||||
ncPath: normalizeNcPath(ncPath),
|
||||
contentType: res.headers['content-type']
|
||||
};
|
||||
}
|
||||
|
||||
async function ensureFolder(config, folderPath) {
|
||||
const normalized = normalizeNcPath(folderPath);
|
||||
if (!normalized || normalized === '/') {
|
||||
return;
|
||||
}
|
||||
|
||||
const parts = normalized.split('/').filter(Boolean);
|
||||
let current = '';
|
||||
for (const part of parts) {
|
||||
current += '/' + part;
|
||||
const url = webDavUrl(config, current);
|
||||
const res = await requestBuffer({
|
||||
url,
|
||||
method: 'MKCOL',
|
||||
headers: { Authorization: authHeader(config) },
|
||||
rejectUnauthorized: tlsOptions(config).rejectUnauthorized
|
||||
});
|
||||
if (res.statusCode !== 201 && res.statusCode !== 405 && res.statusCode !== 301) {
|
||||
if (res.statusCode >= 400) {
|
||||
throw new Error(`WebDAV MKCOL misslyckades (${res.statusCode}) för ${current}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function webdavPut(config, ncPath, buffer, contentType) {
|
||||
const dest = normalizeNcPath(ncPath);
|
||||
const folder = dest.substring(0, dest.lastIndexOf('/')) || '/';
|
||||
await ensureFolder(config, folder);
|
||||
|
||||
const url = webDavUrl(config, dest);
|
||||
const res = await requestBuffer({
|
||||
url,
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
Authorization: authHeader(config),
|
||||
'Content-Type': contentType || 'application/octet-stream',
|
||||
'Content-Length': buffer.length
|
||||
},
|
||||
body: buffer,
|
||||
rejectUnauthorized: tlsOptions(config).rejectUnauthorized
|
||||
});
|
||||
|
||||
if (res.statusCode === 401 || res.statusCode === 403) {
|
||||
throw new Error(`WebDAV ${res.statusCode}: kontrollera app-lösenord`);
|
||||
}
|
||||
if (res.statusCode < 200 || res.statusCode >= 300) {
|
||||
throw new Error(`WebDAV PUT misslyckades (${res.statusCode}) för ${dest}`);
|
||||
}
|
||||
|
||||
return {
|
||||
statusCode: res.statusCode,
|
||||
destNcPath: dest,
|
||||
filename: filenameFromPath(dest)
|
||||
};
|
||||
}
|
||||
|
||||
async function webdavPropfindFileId(config, ncPath) {
|
||||
const url = webDavUrl(config, ncPath);
|
||||
const body = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<d:propfind xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns" xmlns:nc="http://nextcloud.org/ns">
|
||||
<d:prop><oc:fileid/></d:prop>
|
||||
</d:propfind>`;
|
||||
|
||||
const res = await requestBuffer({
|
||||
url,
|
||||
method: 'PROPFIND',
|
||||
headers: {
|
||||
Authorization: authHeader(config),
|
||||
Depth: '0',
|
||||
'Content-Type': 'application/xml; charset=utf-8'
|
||||
},
|
||||
body,
|
||||
rejectUnauthorized: tlsOptions(config).rejectUnauthorized
|
||||
});
|
||||
|
||||
if (res.statusCode < 200 || res.statusCode >= 300) {
|
||||
throw new Error(`PROPFIND misslyckades (${res.statusCode}) för ${normalizeNcPath(ncPath)}`);
|
||||
}
|
||||
|
||||
const text = res.body.toString('utf8');
|
||||
const match = text.match(/<oc:fileid>(\d+)<\/oc:fileid>/i) || text.match(/<nc:fileid>(\d+)<\/nc:fileid>/i);
|
||||
if (!match) {
|
||||
throw new Error(`Kunde inte hitta fileId för ${normalizeNcPath(ncPath)}`);
|
||||
}
|
||||
return parseInt(match[1], 10);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
webDavUrl,
|
||||
webdavGet,
|
||||
webdavPut,
|
||||
webdavPropfindFileId,
|
||||
ensureFolder
|
||||
};
|
||||
Reference in New Issue
Block a user