From 5be8c9e42605a9cbac0fe8800f8df703fdd9ae0d Mon Sep 17 00:00:00 2001 From: SebClem Date: Mon, 14 Nov 2022 16:12:05 +0100 Subject: [PATCH] :hammer: Extract data from cloud file name --- nextcloud_backup/backend/src/routes/webdav.ts | 7 +- .../src/services/backupConfigService.ts | 34 +++++--- .../backend/src/services/webdavService.ts | 81 ++++++++++++++----- .../src/types/services/backupConfig.ts | 2 +- .../backend/src/types/services/webdav.ts | 2 + .../src/components/cloud/CloudListItem.vue | 76 ++++++++++++++--- nextcloud_backup/frontend/src/types/webdav.ts | 4 +- 7 files changed, 155 insertions(+), 51 deletions(-) diff --git a/nextcloud_backup/backend/src/routes/webdav.ts b/nextcloud_backup/backend/src/routes/webdav.ts index 8f55d3f..b31734c 100644 --- a/nextcloud_backup/backend/src/routes/webdav.ts +++ b/nextcloud_backup/backend/src/routes/webdav.ts @@ -1,5 +1,6 @@ import express from "express"; import Joi from "joi"; +import { getBackupConfig } from "../services/backupConfigService.js"; import { getWebdavConfig, validateWebdavConfig, @@ -13,10 +14,11 @@ const webdavRouter = express.Router(); webdavRouter.get("/backup/auto", (req, res, next) => { const config = getWebdavConfig(); + const backupConf = getBackupConfig(); validateWebdavConfig(config) .then(async () => { const value = await webdavService - .getBackups(pathTools.auto, config); + .getBackups(pathTools.auto, config, backupConf.nameTemplate); res.json(value); }) .catch((reason) => { @@ -27,10 +29,11 @@ webdavRouter.get("/backup/auto", (req, res, next) => { webdavRouter.get("/backup/manual", (req, res, next) => { const config = getWebdavConfig(); + const backupConf = getBackupConfig(); validateWebdavConfig(config) .then(async () => { const value = await webdavService - .getBackups(pathTools.manual, config); + .getBackups(pathTools.manual, config, backupConf.nameTemplate); res.json(value); }) .catch((reason) => { diff --git a/nextcloud_backup/backend/src/services/backupConfigService.ts b/nextcloud_backup/backend/src/services/backupConfigService.ts index 3c8de59..014ed4c 100644 --- a/nextcloud_backup/backend/src/services/backupConfigService.ts +++ b/nextcloud_backup/backend/src/services/backupConfigService.ts @@ -1,27 +1,25 @@ import fs from "fs"; -import Joi from "joi" +import Joi from "joi"; import logger from "../config/winston.js"; -import type { BackupConfig } from "../types/services/backupConfig.js" +import type { BackupConfig } from "../types/services/backupConfig.js"; import backupConfigValidation from "../types/services/backupConfigValidation.js"; - const backupConfigPath = "/data/backupConfigV2.json"; - -export function validateBackupConfig(config: BackupConfig){ +export function validateBackupConfig(config: BackupConfig) { const validator = Joi.object(backupConfigValidation); return validator.validateAsync(config, { - abortEarly: false + abortEarly: false, }); } -export function saveBackupConfig(config: BackupConfig){ +export function saveBackupConfig(config: BackupConfig) { fs.writeFileSync(backupConfigPath, JSON.stringify(config, undefined, 2)); } export function getBackupConfig(): BackupConfig { if (!fs.existsSync(backupConfigPath)) { - logger.warn("Config file not found, creating default one !") + logger.warn("Config file not found, creating default one !"); const defaultConfig = getBackupDefaultConfig(); saveBackupConfig(defaultConfig); return defaultConfig; @@ -39,7 +37,7 @@ export function getBackupDefaultConfig(): BackupConfig { enabled: false, }, webdav: { - enabled: false + enabled: false, }, }, exclude: { @@ -49,6 +47,18 @@ export function getBackupDefaultConfig(): BackupConfig { autoStopAddon: [], password: { enabled: false, - } - } -} \ No newline at end of file + }, + }; +} + +export function templateToRegexp(template: string) { + let regexp = template.replace("{date}", "(?\\d{4}-\\d{2}-\\d{2})"); + regexp = regexp.replace("{hour}", "(?\\d{4})"); + regexp = regexp.replace("{hour_12}", "(?\\d{4}(AM|PM))"); + regexp = regexp.replace("{type}", "(?Auto|Manual|)") + regexp = regexp.replace("{type_low}", "(?auto|manual|)") + return regexp.replace( + "{ha_version}", + "(?\\d+\\.\\d+\\.\\d+(b\\d+)?)" + ); +} diff --git a/nextcloud_backup/backend/src/services/webdavService.ts b/nextcloud_backup/backend/src/services/webdavService.ts index 8c7d7f6..eaabda2 100644 --- a/nextcloud_backup/backend/src/services/webdavService.ts +++ b/nextcloud_backup/backend/src/services/webdavService.ts @@ -8,6 +8,7 @@ import * as pathTools from "../tools/pathTools.js"; import * as statusTools from "../tools/status.js"; import type { WebdavBackup } from "../types/services/webdav.js"; import type { WebdavConfig } from "../types/services/webdavConfig.js"; +import { templateToRegexp } from "./backupConfigService.js"; import { getEndpoint } from "./webdavConfigService.js"; const PROPFIND_BODY = @@ -94,7 +95,11 @@ function createDirectory(path: string, config: WebdavConfig) { }); } -export function getBackups(folder: string, config: WebdavConfig) { +export function getBackups( + folder: string, + config: WebdavConfig, + nameTemplate: string +) { const endpoint = getEndpoint(config); return got(config.url + endpoint + config.backupDir + folder, { method: "PROPFIND" as Method, @@ -107,7 +112,10 @@ export function getBackups(folder: string, config: WebdavConfig) { body: PROPFIND_BODY, }).then( (value) => { - return parseXmlBackupData(value.body, config); + const data = parseXmlBackupData(value.body, config).sort( + (a, b) => b.lastEdit.toMillis() - a.lastEdit.toMillis() + ); + return extractBackupInfo(data, nameTemplate); }, (reason) => { messageManager.error( @@ -120,25 +128,56 @@ export function getBackups(folder: string, config: WebdavConfig) { ); } -export function deleteBackup(path: string, config: WebdavConfig){ +function extractBackupInfo(backups: WebdavBackup[], template: string) { + const regex = new RegExp(templateToRegexp(template)); + for (const elem of backups) { + const match = elem.name.match(regex); + if (match?.groups?.date) { + let format = "yyyy-LL-dd"; + let date = match.groups.date; + if (match.groups.hour) { + format += "+HHmm"; + date += `+${match.groups.hour}`; + } else if (match.groups.hour_12) { + format += "+hhmma"; + date += `+${match.groups.hour_12}`; + } + + elem.creationDate = DateTime.fromFormat(date, format); + } + if(match?.groups?.version){ + elem.haVersion = match.groups.version + } + } + return backups; +} + +export function deleteBackup(path: string, config: WebdavConfig) { const endpoint = getEndpoint(config); - return got.delete(config.url + endpoint + path, { - headers: { - authorization: - "Basic " + - Buffer.from(config.username + ":" + config.password).toString("base64") - } - }).then( - (response) => { - return response; - }, - (reason) => { - messageManager.error("Fail to delete backup in webdav", reason?.message); - logger.error(`Fail to delete backup in Cloud`); - logger.error(reason); - return Promise.reject(reason); - } - ); + return got + .delete(config.url + endpoint + path, { + headers: { + authorization: + "Basic " + + Buffer.from(config.username + ":" + config.password).toString( + "base64" + ), + }, + }) + .then( + (response) => { + return response; + }, + (reason) => { + messageManager.error( + "Fail to delete backup in webdav", + reason?.message + ); + logger.error(`Fail to delete backup in Cloud`); + logger.error(reason); + return Promise.reject(reason); + } + ); } function parseXmlBackupData(body: string, config: WebdavConfig) { @@ -162,7 +201,7 @@ function parseXmlBackupData(body: string, config: WebdavConfig) { lastEdit: lastEdit, size: propstat["d:prop"]["d:getcontentlength"], name: name, - path: href.replace(getEndpoint(config), "") + path: href.replace(getEndpoint(config), ""), }); } } diff --git a/nextcloud_backup/backend/src/types/services/backupConfig.ts b/nextcloud_backup/backend/src/types/services/backupConfig.ts index 8705e59..9e17ddf 100644 --- a/nextcloud_backup/backend/src/types/services/backupConfig.ts +++ b/nextcloud_backup/backend/src/types/services/backupConfig.ts @@ -48,4 +48,4 @@ export interface CronConfig { export interface AutoCleanConfig { enabled: boolean; nbrToKeep?: number; -} \ No newline at end of file +} diff --git a/nextcloud_backup/backend/src/types/services/webdav.ts b/nextcloud_backup/backend/src/types/services/webdav.ts index e837a3e..de3a773 100644 --- a/nextcloud_backup/backend/src/types/services/webdav.ts +++ b/nextcloud_backup/backend/src/types/services/webdav.ts @@ -6,6 +6,8 @@ export interface WebdavBackup { size: number; lastEdit: DateTime; path: string; + haVersion?: string; + creationDate?: DateTime; } diff --git a/nextcloud_backup/frontend/src/components/cloud/CloudListItem.vue b/nextcloud_backup/frontend/src/components/cloud/CloudListItem.vue index f15ba5e..10b0dd0 100644 --- a/nextcloud_backup/frontend/src/components/cloud/CloudListItem.vue +++ b/nextcloud_backup/frontend/src/components/cloud/CloudListItem.vue @@ -26,19 +26,69 @@ - - - - {{ - DateTime.fromISO(item.lastEdit).toLocaleString( - DateTime.DATETIME_MED - ) - }} - - - - {{ prettyBytes(item.size) }} - + + + + + + + + + + + + + + + + + + + + + diff --git a/nextcloud_backup/frontend/src/types/webdav.ts b/nextcloud_backup/frontend/src/types/webdav.ts index e946cc1..934f04a 100644 --- a/nextcloud_backup/frontend/src/types/webdav.ts +++ b/nextcloud_backup/frontend/src/types/webdav.ts @@ -1,11 +1,11 @@ -import type { DateTime } from "luxon"; - export interface WebdavBackup { id: string; name: string; size: number; lastEdit: string; path: string; + haVersion?: string; + creationDate?: string; } export interface WebdavDeletePayload {