🔨 Manage backupConfig

This commit is contained in:
SebClem 2022-09-29 00:05:53 +02:00
parent 79f1cfdf09
commit b076f844a5
Signed by: sebclem
GPG Key ID: 5A4308F6A359EA50
8 changed files with 227 additions and 4 deletions

View File

@ -23,6 +23,7 @@
"form-data": "4.0.0", "form-data": "4.0.0",
"got": "12.3.0", "got": "12.3.0",
"http-errors": "2.0.0", "http-errors": "2.0.0",
"joi": "^17.6.1",
"jquery": "3.6.0", "jquery": "3.6.0",
"kleur": "^4.1.5", "kleur": "^4.1.5",
"luxon": "3.0.1", "luxon": "3.0.1",

View File

@ -26,6 +26,7 @@ specifiers:
form-data: 4.0.0 form-data: 4.0.0
got: 12.3.0 got: 12.3.0
http-errors: 2.0.0 http-errors: 2.0.0
joi: ^17.6.1
jquery: 3.6.0 jquery: 3.6.0
kleur: ^4.1.5 kleur: ^4.1.5
luxon: 3.0.1 luxon: 3.0.1
@ -48,6 +49,7 @@ dependencies:
form-data: 4.0.0 form-data: 4.0.0
got: 12.3.0 got: 12.3.0
http-errors: 2.0.0 http-errors: 2.0.0
joi: 17.6.1
jquery: 3.6.0 jquery: 3.6.0
kleur: 4.1.5 kleur: 4.1.5
luxon: 3.0.1 luxon: 3.0.1
@ -131,6 +133,16 @@ packages:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
/@hapi/hoek/9.3.0:
resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==}
dev: false
/@hapi/topo/5.1.0:
resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==}
dependencies:
'@hapi/hoek': 9.3.0
dev: false
/@jridgewell/resolve-uri/3.1.0: /@jridgewell/resolve-uri/3.1.0:
resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==}
engines: {node: '>=6.0.0'} engines: {node: '>=6.0.0'}
@ -165,6 +177,20 @@ packages:
'@nodelib/fs.scandir': 2.1.5 '@nodelib/fs.scandir': 2.1.5
fastq: 1.13.0 fastq: 1.13.0
/@sideway/address/4.1.4:
resolution: {integrity: sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==}
dependencies:
'@hapi/hoek': 9.3.0
dev: false
/@sideway/formula/3.0.0:
resolution: {integrity: sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==}
dev: false
/@sideway/pinpoint/2.0.0:
resolution: {integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==}
dev: false
/@sindresorhus/is/5.3.0: /@sindresorhus/is/5.3.0:
resolution: {integrity: sha512-CX6t4SYQ37lzxicAqsBtxA3OseeoVrh9cSJ5PFYam0GksYlupRfy1A+Q4aYD3zvcfECLc0zO2u+ZnR2UYKvCrw==} resolution: {integrity: sha512-CX6t4SYQ37lzxicAqsBtxA3OseeoVrh9cSJ5PFYam0GksYlupRfy1A+Q4aYD3zvcfECLc0zO2u+ZnR2UYKvCrw==}
engines: {node: '>=14.16'} engines: {node: '>=14.16'}
@ -1493,6 +1519,16 @@ packages:
/isexe/2.0.0: /isexe/2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
/joi/17.6.1:
resolution: {integrity: sha512-Hl7/iBklIX345OCM1TiFSCZRVaAOLDGlWCp0Df2vWYgBgjkezaR7Kvm3joBciBHQjZj5sxXs859r6eqsRSlG8w==}
dependencies:
'@hapi/hoek': 9.3.0
'@hapi/topo': 5.1.0
'@sideway/address': 4.1.4
'@sideway/formula': 3.0.0
'@sideway/pinpoint': 2.0.0
dev: false
/jquery/3.6.0: /jquery/3.6.0:
resolution: {integrity: sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==} resolution: {integrity: sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==}
dev: false dev: false

View File

@ -1,9 +1,11 @@
import express from "express" import express from "express"
import configRouter from "./config.js";
import homeAssistant from "./homeAssistant.js" import homeAssistant from "./homeAssistant.js"
const router = express.Router(); const router = express.Router();
router.use("/homeAssistant", homeAssistant) router.use("/homeAssistant", homeAssistant)
router.use("/config", configRouter);
export default router; export default router;

View File

@ -0,0 +1,20 @@
import { config } from "dotenv";
import express from "express";
import { saveBackupConfig, validateBackupConfig } from "../services/configService.js";
const configRouter = express.Router();
configRouter.put("/backup", (req, res, next) => {
validateBackupConfig(req.body)
.then(() => {
saveBackupConfig(req.body);
res.status(204);
res.send();
})
.catch((error) => {
res.status(400);
res.json(error.details);
});
});
export default configRouter;

View File

@ -1,9 +1,9 @@
import express from "express"; import express from "express";
import * as haOsService from "../services/homeAssistantService.js" import * as haOsService from "../services/homeAssistantService.js"
const router = express.Router(); const homeAssistantRouter = express.Router();
router.get("/backups/", (req, res, next) => { homeAssistantRouter.get("/backups/", (req, res, next) => {
haOsService.getBackups() haOsService.getBackups()
.then((value)=>{ .then((value)=>{
res.json(value.body.data.backups); res.json(value.body.data.backups);
@ -14,6 +14,5 @@ router.get("/backups/", (req, res, next) => {
}); });
router.get("")
export default router; export default homeAssistantRouter;

View File

@ -0,0 +1,52 @@
import fs from "fs";
import Joi from "joi"
import logger from "../config/winston.js";
import { backupConfigValidation } from "../types/services/backupConfigValidation.js"
import { BackupConfig } from "../types/services/backupConfig.js"
const backupConfigPath = "/data/backupConfigV2.json";
export function validateBackupConfig(config: BackupConfig){
const validator = Joi.object(backupConfigValidation);
return validator.validateAsync(config);
}
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 !")
const defaultConfig = getBackupDefaultConfig();
saveBackupConfig(defaultConfig);
return defaultConfig;
} else {
return JSON.parse(fs.readFileSync(backupConfigPath).toString());
}
}
export function getBackupDefaultConfig(): BackupConfig {
return {
nameTemplate: "{type}-{ha_version}-{date}_{hour}",
cron: [],
autoClean: {
homeAssistant: {
enabled: false,
},
webdav: {
enabled: false
},
},
exclude: {
addon: [],
folder: [],
},
autoStopAddon: [],
password: {
enabled: false,
}
}
}

View File

@ -0,0 +1,51 @@
export enum CronMode {
DAILY = "DAILY",
WEEKLY = "WEEKLY",
MONTHLY = "MONTHLY",
CUSTOM = "CUSTOM"
}
export enum Weekday {
SUNDAY,
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY
}
export interface BackupConfig {
nameTemplate: string;
cron: CronConfig[];
autoClean: {
homeAssistant: AutoCleanConfig;
webdav: AutoCleanConfig;
}
exclude: {
addon: string[];
folder: string[];
}
autoStopAddon: string[];
password: {
enabled: boolean;
value?: string;
}
}
export interface CronConfig {
id: string;
mode: CronMode;
hour?: string;
weekday?: Weekday;
monthDay: string;
custom?: string;
}
export interface AutoCleanConfig {
enabled: boolean;
nbrToKeep?: number;
}

View File

@ -0,0 +1,62 @@
import Joi from "joi";
import { CronMode } from "./backupConfig.js";
const CronConfigValidation = {
id: Joi.string().required().not().empty(),
mode: Joi.string()
.required()
.valid(CronMode.CUSTOM, CronMode.DAILY, CronMode.MONTHLY, CronMode.WEEKLY),
hour: Joi.alternatives().conditional("mode", {
is: CronMode.CUSTOM,
then: Joi.forbidden(),
otherwise: Joi.string()
.pattern(/^\d{2}:\d{2}$/)
.required(),
}),
weekday: Joi.alternatives().conditional("mode", {
is: CronMode.WEEKLY,
then: Joi.number().min(0).max(6).required(),
otherwise: Joi.forbidden(),
}),
monthDay: Joi.alternatives().conditional("mode", {
is: CronMode.MONTHLY,
then: Joi.number().min(1).max(28).required(),
otherwise: Joi.forbidden(),
}),
custom: Joi.alternatives().conditional("mode", {
is: CronMode.CUSTOM,
then: Joi.string().required(),
otherwise: Joi.forbidden(),
}),
};
const AutoCleanConfig = {
enabled: Joi.boolean().required(),
nbrToKeep: Joi.alternatives().conditional("enabled", {
is: true,
then: Joi.number().required().min(0),
otherwise: Joi.forbidden(),
}),
};
export const backupConfigValidation = {
nameTemplate: Joi.string().required().not().empty(),
cron: Joi.array().items(CronConfigValidation).required(),
autoClean: Joi.object({
homeAssistant: Joi.object(AutoCleanConfig).required(),
webdav: Joi.object(AutoCleanConfig).required(),
}).required(),
exclude: Joi.object({
addon: Joi.array().items(Joi.string().not().empty()),
folder: Joi.array().items(Joi.string().not().empty()),
}).required(),
autoStopAddon: Joi.array().items(Joi.string().not().empty()),
password: Joi.object({
enabled: Joi.boolean().required(),
value: Joi.alternatives().conditional("enabled", {
is: true,
then: Joi.string().required().not().empty(),
otherwise: Joi.forbidden(),
}),
}),
};