diff --git a/nextcloud_backup/backend/package.json b/nextcloud_backup/backend/package.json index 4cc590a..df16bcc 100644 --- a/nextcloud_backup/backend/package.json +++ b/nextcloud_backup/backend/package.json @@ -19,6 +19,7 @@ "debug": "4.3.4", "errorhandler": "^1.5.1", "express": "4.18.1", + "fast-xml-parser": "^4.0.10", "figlet": "^1.5.2", "form-data": "4.0.0", "got": "12.3.0", diff --git a/nextcloud_backup/backend/pnpm-lock.yaml b/nextcloud_backup/backend/pnpm-lock.yaml index 7afc773..d2ecad6 100644 --- a/nextcloud_backup/backend/pnpm-lock.yaml +++ b/nextcloud_backup/backend/pnpm-lock.yaml @@ -22,6 +22,7 @@ specifiers: errorhandler: ^1.5.1 eslint: 7.19.0 express: 4.18.1 + fast-xml-parser: ^4.0.10 figlet: ^1.5.2 form-data: 4.0.0 got: 12.3.0 @@ -45,6 +46,7 @@ dependencies: debug: 4.3.4 errorhandler: 1.5.1 express: 4.18.1 + fast-xml-parser: 4.0.10 figlet: 1.5.2 form-data: 4.0.0 got: 12.3.0 @@ -1184,6 +1186,13 @@ packages: strnum: 1.0.5 dev: false + /fast-xml-parser/4.0.10: + resolution: {integrity: sha512-mYMMIk7Ho1QOiedyvafdyPamn1Vlda+5n95lcn0g79UiCQoLQ2xfPQ8m3pcxBMpVaftYXtoIE2wrNTjmLQnnkg==} + hasBin: true + dependencies: + strnum: 1.0.5 + dev: false + /fastq/1.13.0: resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==} dependencies: diff --git a/nextcloud_backup/backend/src/routes/apiV2.ts b/nextcloud_backup/backend/src/routes/apiV2.ts index 736df57..91e3e4e 100644 --- a/nextcloud_backup/backend/src/routes/apiV2.ts +++ b/nextcloud_backup/backend/src/routes/apiV2.ts @@ -1,11 +1,13 @@ import express from "express" import configRouter from "./config.js"; import homeAssistant from "./homeAssistant.js" +import webdavRouter from "./webdav.js"; const router = express.Router(); router.use("/homeAssistant", homeAssistant) router.use("/config", configRouter); +router.use("/webdav", webdavRouter); export default router; \ No newline at end of file diff --git a/nextcloud_backup/backend/src/routes/webdav.ts b/nextcloud_backup/backend/src/routes/webdav.ts new file mode 100644 index 0000000..c261b2e --- /dev/null +++ b/nextcloud_backup/backend/src/routes/webdav.ts @@ -0,0 +1,51 @@ +import express from "express"; +import { + getWebdavConfig, + validateWebdavConfig, +} from "../services/webdavConfigService.js"; +import * as webdavService from "../services/webdavService.js"; +import * as pathTools from "../tools/pathTools.js"; + +const webdavRouter = express.Router(); + +webdavRouter.get("/backup/auto", (req, res, next) => { + const config = getWebdavConfig(); + validateWebdavConfig(config) + .then(() => { + webdavService + .getBackups(pathTools.auto, config) + .then((value) => { + res.json(value); + }) + .catch((reason) => { + res.status(500); + res.json(reason); + }); + }) + .catch((reason) => { + res.status(500); + res.json(reason); + }); +}); + +webdavRouter.get("/backup/manual", (req, res, next) => { + const config = getWebdavConfig(); + validateWebdavConfig(config) + .then(() => { + webdavService + .getBackups(pathTools.manual, config) + .then((value) => { + res.json(value); + }) + .catch((reason) => { + res.status(500); + res.json(reason); + }); + }) + .catch((reason) => { + res.status(500); + res.json(reason); + }); +}); + +export default webdavRouter; diff --git a/nextcloud_backup/backend/src/services/webdavService.ts b/nextcloud_backup/backend/src/services/webdavService.ts index cc30c21..6564be3 100644 --- a/nextcloud_backup/backend/src/services/webdavService.ts +++ b/nextcloud_backup/backend/src/services/webdavService.ts @@ -4,6 +4,21 @@ import messageManager from "../tools/messageManager.js"; import { WebdavConfig } from "../types/services/webdavConfig.js"; import { getEndpoint } from "./webdavConfigService.js"; import * as pathTools from "../tools/pathTools.js"; +import { XMLParser } from "fast-xml-parser"; +import { WebdavBackup } from "../types/services/webdav.js"; +import { DateTime } from "luxon"; + +const PROPFIND_BODY = +'\ +\ + \ + \ + \ + \ + \ + \ + \ +'; export function checkWebdavLogin(config: WebdavConfig) { const endpoint = getEndpoint(config); @@ -77,6 +92,59 @@ function createDirectory(path: string, config: WebdavConfig) { }); } +export function getBackups(folder: string, config: WebdavConfig) { + const endpoint = getEndpoint(config); + return got(config.url + endpoint + config.backupDir + folder, { + method: "PROPFIND" as Method, + headers: { + authorization: + "Basic " + + Buffer.from(config.username + ":" + config.password).toString("base64"), + Depth: "1", + }, + body: PROPFIND_BODY, + }).then( + (value) => { + return parseXmlBackupData(value.body); + }, + (reason) => { + messageManager.error( + `Fail to retrive webdav backups in ${folder} folder` + ); + logger.error(`Fail to retrive webdav backups in ${folder} folder`); + logger.error(reason); + return Promise.reject(reason); + } + ); +} + + +function parseXmlBackupData(body: string){ + const parser = new XMLParser(); + const data = parser.parse(body); + const multistatus = data["d:multistatus"]; + const backups: WebdavBackup[] = []; + if(Array.isArray(multistatus["d:response"])){ + for(const elem of multistatus["d:response"]){ + // If array -> base folder, ignoring it + if(!Array.isArray(elem["d:propstat"])){ + const propstat = elem["d:propstat"]; + const id = propstat["d:prop"]["d:getetag"].replaceAll("\"", ""); + const href = decodeURI(elem["d:href"]); + const name = href.split("/").slice(-1)[0]; + const lastEdit = DateTime.fromHTTP(propstat["d:prop"]["d:getlastmodified"]); + backups.push({ + id: id, + lastEdit: lastEdit, + size: propstat["d:prop"]["d:getcontentlenght"], + name: name + }) + } + } + } + return backups; +} + // import fs from "fs"; // import got from "got"; // import https from "https"; diff --git a/nextcloud_backup/backend/src/types/services/webdav.ts b/nextcloud_backup/backend/src/types/services/webdav.ts new file mode 100644 index 0000000..47c3fe9 --- /dev/null +++ b/nextcloud_backup/backend/src/types/services/webdav.ts @@ -0,0 +1,8 @@ +import { DateTime } from "luxon"; + +export interface WebdavBackup { + id: string; + name: string; + size: number; + lastEdit: DateTime; +} \ No newline at end of file