Compare commits

..

No commits in common. "73d5213c4e2220c9a071a866dd2d75b565cbce2b" and "f8579b2a162a9d390ab438b5851af45a845ae969" have entirely different histories.

8 changed files with 20 additions and 277 deletions

View File

@ -19,7 +19,6 @@
"debug": "4.3.4", "debug": "4.3.4",
"errorhandler": "^1.5.1", "errorhandler": "^1.5.1",
"express": "4.18.1", "express": "4.18.1",
"fast-xml-parser": "^4.0.10",
"figlet": "^1.5.2", "figlet": "^1.5.2",
"form-data": "4.0.0", "form-data": "4.0.0",
"got": "12.3.0", "got": "12.3.0",

View File

@ -22,7 +22,6 @@ specifiers:
errorhandler: ^1.5.1 errorhandler: ^1.5.1
eslint: 7.19.0 eslint: 7.19.0
express: 4.18.1 express: 4.18.1
fast-xml-parser: ^4.0.10
figlet: ^1.5.2 figlet: ^1.5.2
form-data: 4.0.0 form-data: 4.0.0
got: 12.3.0 got: 12.3.0
@ -46,7 +45,6 @@ dependencies:
debug: 4.3.4 debug: 4.3.4
errorhandler: 1.5.1 errorhandler: 1.5.1
express: 4.18.1 express: 4.18.1
fast-xml-parser: 4.0.10
figlet: 1.5.2 figlet: 1.5.2
form-data: 4.0.0 form-data: 4.0.0
got: 12.3.0 got: 12.3.0
@ -1186,13 +1184,6 @@ packages:
strnum: 1.0.5 strnum: 1.0.5
dev: false 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: /fastq/1.13.0:
resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==} resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==}
dependencies: dependencies:

View File

@ -3,12 +3,8 @@ import logger from "./config/winston.js";
import * as homeAssistantService from "./services/homeAssistantService.js"; import * as homeAssistantService from "./services/homeAssistantService.js";
import * as settingsTools from "./tools/settingsTools.js"; import * as settingsTools from "./tools/settingsTools.js";
import * as statusTools from "./tools/status.js"; import * as statusTools from "./tools/status.js";
import kleur from "kleur"; import kleur from 'kleur';
import { checkWebdavLogin, createBackupFolder } from "./services/webdavService.js";
import {
getWebdavConfig,
validateWebdavConfig,
} from "./services/webdavConfigService.js";
function postInit() { function postInit() {
logger.info(`Log level: ${process.env.LOG_LEVEL}`); logger.info(`Log level: ${process.env.LOG_LEVEL}`);
@ -35,34 +31,15 @@ function postInit() {
} }
); );
const webdavConf = getWebdavConfig(); // webdav.confIsValid().then(
validateWebdavConfig(webdavConf).then( // () => {
() => { // newlog.info("Nextcloud connection : \x1b[32mGo !\x1b[0m");
logger.info("Webdav config: " + kleur.green().bold("Go !")); // },
checkWebdavLogin(webdavConf).then( // (err) => {
() => { // newlog.error("Nextcloud connection : \x1b[31;1mFAIL !\x1b[0m");
logger.info("Webdav : " + kleur.green().bold("Go !")); // newlog.error("... " + err);
createBackupFolder(webdavConf).then( // }
() => { // );
logger.info("Webdav fodlers: " + kleur.green().bold("Go !"));
},
(reason) => {
logger.error("Webdav folders: " + kleur.red().bold("FAIL !"));
logger.error(reason);
}
);
},
(reason) => {
logger.error("Webdav : " + kleur.red().bold("FAIL !"));
logger.error(reason);
}
);
},
(reason) => {
logger.error("Webdav config: " + kleur.red().bold("FAIL !"));
logger.error(reason);
}
);
settingsTools.check(settingsTools.getSettings(), true); settingsTools.check(settingsTools.getSettings(), true);
// cronTools.init(); // cronTools.init();

View File

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

View File

@ -1,51 +0,0 @@
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;

View File

@ -1,29 +1,26 @@
import fs from "fs"; import fs from "fs";
import Joi from "joi"; import Joi from "joi"
import logger from "../config/winston.js"; import logger from "../config/winston.js";
import { default_root } from "../tools/pathTools.js"; import { default_root } from "../tools/pathTools.js";
import WebdavConfigValidation from "../types/services/webdavConfigValidation.js"; import WebdavConfigValidation from "../types/services/webdavConfigValidation.js";
import { import { WebdavConfig, WebdavEndpointType } from "../types/services/webdavConfig.js"
WebdavConfig,
WebdavEndpointType,
} from "../types/services/webdavConfig.js";
const webdavConfigPath = "/data/webdavConfigV2.json"; const webdavConfigPath = "/data/webdavConfigV2.json";
const NEXTCLOUD_ENDPOINT = "/remote.php/dav/files/$username";
export function validateWebdavConfig(config: WebdavConfig) { export function validateWebdavConfig(config: WebdavConfig){
const validator = Joi.object(WebdavConfigValidation); const validator = Joi.object(WebdavConfigValidation);
return validator.validateAsync(config); return validator.validateAsync(config);
} }
export function saveWebdavConfig(config: WebdavConfig) { export function saveWebdavConfig(config: WebdavConfig){
fs.writeFileSync(webdavConfigPath, JSON.stringify(config, undefined, 2)); fs.writeFileSync(webdavConfigPath, JSON.stringify(config, undefined, 2));
} }
export function getWebdavConfig(): WebdavConfig { export function getWebdavConfig(): WebdavConfig {
if (!fs.existsSync(webdavConfigPath)) { if (!fs.existsSync(webdavConfigPath)) {
logger.warn("Webdav Config file not found, creating default one !"); logger.warn("Webdav Config file not found, creating default one !")
const defaultConfig = getWebdavDefaultConfig(); const defaultConfig = getWebdavDefaultConfig();
saveWebdavConfig(defaultConfig); saveWebdavConfig(defaultConfig);
return defaultConfig; return defaultConfig;
@ -32,19 +29,6 @@ export function getWebdavConfig(): WebdavConfig {
} }
} }
export function getEndpoint(config: WebdavConfig) {
if (config.webdavEndpoint.type == WebdavEndpointType.NEXTCLOUD) {
return NEXTCLOUD_ENDPOINT.replace("$username", config.username);
} else if (config.webdavEndpoint.customEndpoint) {
return config.webdavEndpoint.customEndpoint.replace(
"$username",
config.username
);
} else {
return "";
}
}
export function getWebdavDefaultConfig(): WebdavConfig { export function getWebdavDefaultConfig(): WebdavConfig {
return { return {
url: "", url: "",
@ -54,6 +38,6 @@ export function getWebdavDefaultConfig(): WebdavConfig {
allowSelfSignedCerts: false, allowSelfSignedCerts: false,
webdavEndpoint: { webdavEndpoint: {
type: WebdavEndpointType.NEXTCLOUD, type: WebdavEndpointType.NEXTCLOUD,
}, }
}; }
} }

View File

@ -1,150 +1,3 @@
import got, { HTTPError, Method } from "got";
import logger from "../config/winston.js";
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 =
'<?xml version="1.0" encoding="utf-8" ?>\
<d:propfind xmlns:d="DAV:">\
<d:prop>\
<d:getlastmodified />\
<d:getetag />\
<d:getcontenttype />\
<d:resourcetype />\
<d:getcontentlength />\
</d:prop>\
</d:propfind>';
export function checkWebdavLogin(config: WebdavConfig) {
const endpoint = getEndpoint(config);
return got(config.url + endpoint, {
method: "OPTIONS",
headers: {
authorization:
"Basic " +
Buffer.from(config.username + ":" + config.password).toString("base64"),
},
}).then(
(response) => {
return response;
},
(reason) => {
messageManager.error("Fail to connect to Webdav", reason?.message);
logger.error(`Fail to connect to Webdav`);
logger.error(reason);
return Promise.reject(reason);
}
);
}
export async function createBackupFolder(conf: WebdavConfig) {
const root_splited = conf.backupDir.split("/").splice(1);
let path = "/";
for (const elem of root_splited) {
if (elem != "") {
path = path + elem + "/";
try {
await createDirectory(path, conf);
logger.debug(`Path ${path} created.`);
} catch (error) {
if (error instanceof HTTPError && error.response.statusCode == 405)
logger.debug(`Path ${path} already exist.`);
else {
messageManager.error("Fail to create webdav root folder");
logger.error("Fail to create webdav root folder");
logger.error(error);
return Promise.reject(error);
}
}
}
}
for (const elem of [pathTools.auto, pathTools.manual]) {
try {
await createDirectory(conf.backupDir + elem, conf);
logger.debug(`Path ${conf.backupDir + elem} created.`);
} catch (error) {
if (error instanceof HTTPError && error.response.statusCode == 405) {
logger.debug(`Path ${conf.backupDir + elem} already exist.`);
} else {
messageManager.error("Fail to create webdav root folder");
logger.error("Fail to create webdav root folder");
logger.error(error);
return Promise.reject(error);
}
}
}
}
function createDirectory(path: string, config: WebdavConfig) {
const endpoint = getEndpoint(config);
return got(config.url + endpoint + path, {
method: "MKCOL" as Method,
headers: {
authorization:
"Basic " +
Buffer.from(config.username + ":" + config.password).toString("base64"),
},
});
}
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 fs from "fs";
// import got from "got"; // import got from "got";
// import https from "https"; // import https from "https";

View File

@ -1,8 +0,0 @@
import { DateTime } from "luxon";
export interface WebdavBackup {
id: string;
name: string;
size: number;
lastEdit: DateTime;
}