mirror of
https://github.com/Sebclem/hassio-nextcloud-backup.git
synced 2024-09-11 16:29:07 +02:00
🔨 Extract data from cloud file name
This commit is contained in:
parent
bff494252b
commit
5be8c9e426
@ -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) => {
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function templateToRegexp(template: string) {
|
||||
let regexp = template.replace("{date}", "(?<date>\\d{4}-\\d{2}-\\d{2})");
|
||||
regexp = regexp.replace("{hour}", "(?<hour>\\d{4})");
|
||||
regexp = regexp.replace("{hour_12}", "(?<hour12>\\d{4}(AM|PM))");
|
||||
regexp = regexp.replace("{type}", "(?<type>Auto|Manual|)")
|
||||
regexp = regexp.replace("{type_low}", "(?<type>auto|manual|)")
|
||||
return regexp.replace(
|
||||
"{ha_version}",
|
||||
"(?<version>\\d+\\.\\d+\\.\\d+(b\\d+)?)"
|
||||
);
|
||||
}
|
||||
|
@ -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), ""),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -48,4 +48,4 @@ export interface CronConfig {
|
||||
export interface AutoCleanConfig {
|
||||
enabled: boolean;
|
||||
nbrToKeep?: number;
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ export interface WebdavBackup {
|
||||
size: number;
|
||||
lastEdit: DateTime;
|
||||
path: string;
|
||||
haVersion?: string;
|
||||
creationDate?: DateTime;
|
||||
}
|
||||
|
||||
|
||||
|
@ -26,19 +26,69 @@
|
||||
</v-list-item>
|
||||
<v-expand-transition>
|
||||
<v-card v-show="detail" variant="tonal" color="secondary" rounded="0">
|
||||
<v-card-text class="d-flex justify-center">
|
||||
<v-chip color="primary" variant="flat" class="mr-2" label>
|
||||
<v-icon start icon="mdi-pencil"></v-icon>
|
||||
{{
|
||||
DateTime.fromISO(item.lastEdit).toLocaleString(
|
||||
DateTime.DATETIME_MED
|
||||
)
|
||||
}}
|
||||
</v-chip>
|
||||
<v-chip color="success" variant="flat" label>
|
||||
<v-icon start icon="mdi-database"></v-icon>
|
||||
{{ prettyBytes(item.size) }}
|
||||
</v-chip>
|
||||
<v-card-text>
|
||||
<v-row>
|
||||
<v-col class="d-flex justify-center">
|
||||
<v-tooltip text="Creation" location="top">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-chip
|
||||
color="primary"
|
||||
variant="flat"
|
||||
class="mr-2"
|
||||
label
|
||||
v-bind="props"
|
||||
>
|
||||
<v-icon start icon="mdi-folder-plus"></v-icon>
|
||||
{{
|
||||
item.creationDate
|
||||
? DateTime.fromISO(item.creationDate).toLocaleString(
|
||||
DateTime.DATETIME_MED
|
||||
)
|
||||
: "UNKNOWN"
|
||||
}}
|
||||
</v-chip>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
<v-tooltip text="Home Assistant Version" location="top">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-chip color="success" variant="flat" label v-bind="props">
|
||||
<v-icon start icon="mdi-home-assistant"></v-icon>
|
||||
{{ item.haVersion ? item.haVersion : "UNKNOWN" }}
|
||||
</v-chip>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col class="d-flex justify-center">
|
||||
<v-tooltip text="Last edit" location="top">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-chip
|
||||
color="primary"
|
||||
variant="flat"
|
||||
class="mr-2"
|
||||
label
|
||||
v-bind="props"
|
||||
>
|
||||
<v-icon start icon="mdi-pencil"></v-icon>
|
||||
{{
|
||||
DateTime.fromISO(item.lastEdit).toLocaleString(
|
||||
DateTime.DATETIME_MED
|
||||
)
|
||||
}}
|
||||
</v-chip>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
<v-tooltip text="Size" location="top">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-chip color="success" variant="flat" label v-bind="props">
|
||||
<v-icon start icon="mdi-database"></v-icon>
|
||||
{{ prettyBytes(item.size) }}
|
||||
</v-chip>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
<v-divider class="mx-4"></v-divider>
|
||||
<v-card-actions class="justify-center">
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user