Compare commits

...

7 Commits

Author SHA1 Message Date
ace8fe7caa
Format 2024-07-12 14:37:34 +02:00
9a62814c38
Reset state afecter chunked upload 2024-07-12 14:33:11 +02:00
6f20b97488
Fix backup name 2024-07-12 14:32:53 +02:00
7abd6bab8b
Add start addon in orchestrator 2024-07-12 14:32:36 +02:00
2b639a0df5
Add custom chunk url to front 2024-07-12 14:31:55 +02:00
f6db3499a4
Reddirect 404erro only on dev 2024-07-12 14:31:24 +02:00
bd444ad8d6
Remove old api 2024-07-12 14:31:02 +02:00
8 changed files with 81 additions and 328 deletions

View File

@ -12,9 +12,7 @@
"request": "launch", "request": "launch",
"restart": true, "restart": true,
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/nodemon", "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/nodemon",
"skipFiles": [ "skipFiles": ["<node_internals>/**"],
"<node_internals>/**"
],
"type": "node", "type": "node",
"preLaunchTask": "npm: build:watch" "preLaunchTask": "npm: build:watch"
} }

View File

@ -21,9 +21,9 @@ app.use(
app.set("port", process.env.PORT || 3000); app.set("port", process.env.PORT || 3000);
app.use( // app.use(
morgan("dev", { stream: { write: (message) => logger.debug(message) } }) // morgan("dev", { stream: { write: (message) => logger.debug(message) } })
); // );
app.use(express.json()); app.use(express.json());
app.use(express.urlencoded({ extended: false })); app.use(express.urlencoded({ extended: false }));
app.use(cookieParser()); app.use(cookieParser());
@ -35,13 +35,14 @@ app.use("/v2/api/", apiV2Router);
Error handler Error handler
---------------------------------------------------------- ----------------------------------------------------------
*/ */
// error handler
if (app.get("env") == "development") {
// catch 404 and forward to error handler // catch 404 and forward to error handler
app.use((req, res, next) => { app.use((req, res, next) => {
next(createError(404)); next(createError(404));
}); });
// error handler
if (process.env.NODE_ENV === "development") {
// only use in development // only use in development
app.use(errorHandler()); app.use(errorHandler());
} }

View File

@ -1,279 +0,0 @@
// import express from "express";
// import * as statusTools from "../tools/status.js";
// import webdav from "../services/webdavService.js";
// import * as settingsTools from "../tools/settingsTools.js";
// import * as pathTools from "../tools/pathTools.js";
// import * as hassioApiTools from "../tools/hassioApiTools.js";
// import { humanFileSize } from "../tools/toolbox.js";
// import cronTools from "../tools/cronTools.js";
// import logger from "../config/winston.js";
// import { DateTime } from "luxon";
// const router = express.Router();
// router.get("/status", (req, res, next) => {
// cronTools.updateNextDate();
// const status = statusTools.getStatus();
// res.json(status);
// });
// router.get("/formated-local-snap", function (req, res, next) {
// hassioApiTools
// .getSnapshots()
// .then((snaps) => {
// snaps.sort((a, b) => {
// return a.date < b.date ? 1 : -1;
// });
// res.render("localSnaps", { snaps: snaps, DateTime: DateTime });
// })
// .catch((err) => {
// logger.error(err);
// res.status(500);
// res.send("");
// });
// });
// router.get("/formated-backup-manual", function (req, res, next) {
// if (webdav.getConf() == null) {
// res.send("");
// return;
// }
// webdav
// .getFolderContent(webdav.getConf()?.back_dir + pathTools.manual)
// .then((contents: any) => {
// contents.sort((a: any, b: any) => {
// return a.date < b.date ? 1 : -1;
// });
// //TODO Remove this when bug is fixed, etag contain '&quot;' at start and end ?
// for (const backup of contents) {
// backup.etag = backup.etag.replace(/&quot;/g, "");
// }
// res.render("backupSnaps", {
// backups: contents,
// DateTime: DateTime,
// humanFileSize: humanFileSize,
// });
// })
// .catch((err) => {
// res.status(500);
// res.send(err);
// });
// });
// router.get("/formated-backup-auto", function (req, res, next) {
// if (webdav.getConf() == null) {
// res.send("");
// return;
// }
// const url = webdav.getConf()?.back_dir + pathTools.auto;
// webdav
// .getFolderContent(url)
// .then((contents: any) => {
// contents.sort((a: any, b: any) => {
// return a.date < b.date ? 1 : -1;
// });
// //TODO Remove this when bug is fixed, etag contain '&quot;' at start and end ?
// for (const backup of contents) {
// backup.etag = backup.etag.replace(/&quot;/g, "");
// }
// res.render("backupSnaps", {
// backups: contents,
// DateTime: DateTime,
// humanFileSize: humanFileSize,
// });
// })
// .catch((err) => {
// res.status(500);
// res.send(err);
// });
// });
// router.post("/nextcloud-settings", function (req, res, next) {
// const settings = req.body;
// if (
// settings.ssl != null &&
// settings.host != null &&
// settings.host !== "" &&
// settings.username != null &&
// settings.password != null
// ) {
// webdav.setConf(settings);
// webdav
// .confIsValid()
// .then(() => {
// res.status(201);
// res.send();
// })
// .catch((err) => {
// res.status(406);
// res.json({ message: err });
// });
// } else {
// res.status(400);
// res.send();
// }
// });
// router.get("/nextcloud-settings", function (req, res, next) {
// const conf = webdav.getConf();
// if (conf == null) {
// res.status(404);
// res.send();
// } else {
// res.json(conf);
// }
// });
// router.post("/manual-backup", function (req, res, next) {
// const id = req.query.id;
// const name = req.query.name;
// const status = statusTools.getStatus();
// if (
// status.status == "creating" ||
// status.status == "upload" ||
// status.status == "download"
// ) {
// res.status(503);
// res.send();
// return;
// }
// hassioApiTools
// .downloadSnapshot(id as string)
// .then(() => {
// webdav
// .uploadFile(
// id as string,
// webdav.getConf()?.back_dir + pathTools.manual + name + ".tar"
// )
// .then(() => {
// res.status(201);
// res.send();
// })
// .catch(() => {
// res.status(500);
// res.send();
// });
// })
// .catch(() => {
// res.status(500);
// res.send();
// });
// });
// router.post("/new-backup", function (req, res, next) {
// const status = statusTools.getStatus();
// if (
// status.status == "creating" ||
// status.status == "upload" ||
// status.status == "download" ||
// status.status == "stopping" ||
// status.status == "starting"
// ) {
// res.status(503);
// res.send();
// return;
// }
// hassioApiTools
// .stopAddons()
// .then(() => {
// hassioApiTools
// .getVersion()
// .then((version) => {
// const name = settingsTools.getFormatedName(true, version);
// hassioApiTools
// .createNewBackup(name)
// .then((id) => {
// hassioApiTools
// .downloadSnapshot(id)
// .then(() => {
// webdav
// .uploadFile(
// id,
// webdav.getConf()?.back_dir +
// pathTools.manual +
// name +
// ".tar"
// )
// .then(() => {
// hassioApiTools.startAddons().catch(() => {
// // Skip
// });
// })
// .catch(() => {
// // Skip
// });
// })
// .catch(() => {
// // Skip
// });
// })
// .catch(() => {
// // Skip
// });
// })
// .catch(() => {
// // Skip
// });
// })
// .catch(() => {
// hassioApiTools.startAddons().catch(() => {
// // Skip
// });
// });
// res.status(201);
// res.send();
// });
// router.get("/backup-settings", function (req, res, next) {
// hassioApiTools.getAddonList().then((addonList) => {
// const data = {
// folder: hassioApiTools.getFolderList(),
// addonList: addonList,
// settings: settingsTools.getSettings()
// };
// res.send(data);
// });
// });
// router.post("/backup-settings", function (req, res, next) {
// const [result, message] = settingsTools.check(req.body);
// if (result) {
// settingsTools.setSettings(req.body);
// cronTools.init();
// res.send();
// } else {
// res.status(400);
// res.send(message);
// }
// });
// router.post("/clean-now", function (req, res, next) {
// webdav
// .clean()
// .then(() => {
// hassioApiTools.clean().catch();
// })
// .catch(() => {
// hassioApiTools.clean().catch();
// });
// res.status(201);
// res.send();
// });
// router.post("/restore", function (req, res, next) {
// if (req.body["path"] != null) {
// webdav.downloadFile(req.body["path"]).then((path) => {
// hassioApiTools.uploadSnapshot(path).catch();
// });
// res.status(200);
// res.send();
// } else {
// res.status(400);
// res.send();
// }
// });
// export default router;

View File

@ -73,17 +73,20 @@ export function doBackupWorkflow(type: WorkflowType) {
if (webdavConfig.chunckedUpload) { if (webdavConfig.chunckedUpload) {
return webDavService.chunkedUpload( return webDavService.chunkedUpload(
tmpFile, tmpFile,
getBackupFolder(type, webdavConfig) + name, getBackupFolder(type, webdavConfig) + name + ".tar",
webdavConfig webdavConfig
); );
} else { } else {
return webDavService.webdavUploadFile( return webDavService.webdavUploadFile(
tmpFile, tmpFile,
getBackupFolder(type, webdavConfig) + name, getBackupFolder(type, webdavConfig) + name + ".tar",
webdavConfig webdavConfig
); );
} }
}) })
.then(() => {
return homeAssistantService.startAddons(addonsToStartStop);
})
.then(() => { .then(() => {
logger.info("Backup workflow finished successfully !"); logger.info("Backup workflow finished successfully !");
messageManager.info("Backup workflow finished successfully !"); messageManager.info("Backup workflow finished successfully !");
@ -104,8 +107,7 @@ export function doBackupWorkflow(type: WorkflowType) {
// This methods remove addon that are no installed in HA from the conf array // This methods remove addon that are no installed in HA from the conf array
function sanitizeAddonList(addonInConf: string[], addonInHA: AddonModel[]) { function sanitizeAddonList(addonInConf: string[], addonInHA: AddonModel[]) {
addonInConf.filter((value) => addonInHA.some((v) => v.slug == value)); return addonInConf.filter((value) => addonInHA.some((v) => v.slug == value));
return addonInConf;
} }
function getAddonToBackup(excludedAddon: string[], addonInHA: AddonModel[]) { function getAddonToBackup(excludedAddon: string[], addonInHA: AddonModel[]) {

View File

@ -414,6 +414,12 @@ export async function chunkedUpload(
logger.debug("Chunked upload funished, assembling chunks."); logger.debug("Chunked upload funished, assembling chunks.");
try { try {
await assembleChunkedUpload(chunkedUrl, finalDestination, fileSize, config); await assembleChunkedUpload(chunkedUrl, finalDestination, fileSize, config);
const status = statusTools.getStatus();
status.status = States.IDLE;
status.progress = undefined;
statusTools.setStatus(status);
logger.info(`...Upload finish !`);
fs.unlinkSync(localPath);
} catch (err) { } catch (err) {
if (err instanceof RequestError) { if (err instanceof RequestError) {
messageManager.error( messageManager.error(
@ -482,7 +488,7 @@ export function initChunkedUpload(
finalDestination: string, finalDestination: string,
config: WebdavConfig config: WebdavConfig
) { ) {
logger.debug(`Init chuncked upload.`); logger.info(`Init chuncked upload.`);
logger.debug(`...URI: ${encodeURI(url)}`); logger.debug(`...URI: ${encodeURI(url)}`);
logger.debug(`...Final destination: ${encodeURI(finalDestination)}`); logger.debug(`...Final destination: ${encodeURI(finalDestination)}`);
return got(encodeURI(url), { return got(encodeURI(url), {
@ -504,7 +510,7 @@ export function assembleChunkedUpload(
config: WebdavConfig config: WebdavConfig
) { ) {
const chunckFile = `${url}/.file`; const chunckFile = `${url}/.file`;
logger.debug(`Assemble chuncked upload.`); logger.info(`Assemble chuncked upload.`);
logger.debug(`...URI: ${encodeURI(chunckFile)}`); logger.debug(`...URI: ${encodeURI(chunckFile)}`);
logger.debug(`...Final destination: ${encodeURI(finalDestination)}`); logger.debug(`...Final destination: ${encodeURI(finalDestination)}`);
return got(encodeURI(chunckFile), { return got(encodeURI(chunckFile), {

View File

@ -32,9 +32,12 @@
</v-select> </v-select>
</v-col> </v-col>
</v-row> </v-row>
<v-row v-if="data.webdavEndpoint.type == WebdavEndpointType.CUSTOM"> <div v-if="data.webdavEndpoint.type == WebdavEndpointType.CUSTOM">
<v-row>
<v-col> <v-col>
<div class="text-subtitle-1 text-medium-emphasis">Custom Endpoint</div> <div class="text-subtitle-1 text-medium-emphasis">
Custom endpoint
</div>
<v-text-field <v-text-field
placeholder="/remote.php/dav/files/$username" placeholder="/remote.php/dav/files/$username"
hint="You can use the $username variable" hint="You can use the $username variable"
@ -48,10 +51,30 @@
></v-text-field> ></v-text-field>
</v-col> </v-col>
</v-row> </v-row>
<v-row>
<v-col>
<div class="text-subtitle-1 text-medium-emphasis">
Custom chunk endpoint
</div>
<v-text-field
placeholder="/remote.php/dav/uploads/$username"
hint="You can use the $username variable"
variant="outlined"
density="compact"
hide-details="auto"
v-model="data.webdavEndpoint.customChunkEndpoint"
:error-messages="errors.customChunkEndpoint"
:loading="loading"
color="orange"
></v-text-field>
</v-col>
</v-row>
</div>
<v-row class="mt-0"> <v-row class="mt-0">
<v-col class="d-flex align-content-end"> <v-col class="d-flex align-content-end">
<v-switch <v-switch
label="Allow Self Signed Certificate" label="Allow self signed certificate"
v-model="data.allowSelfSignedCerts" v-model="data.allowSelfSignedCerts"
hide-details="auto" hide-details="auto"
density="compact" density="compact"
@ -65,7 +88,7 @@
<v-row class="mt-0"> <v-row class="mt-0">
<v-col class="d-flex align-content-end"> <v-col class="d-flex align-content-end">
<v-switch <v-switch
label="Chunked Upload (Beta)" label="Chunked upload (Beta)"
v-model="data.chunckedUpload" v-model="data.chunckedUpload"
hide-details="auto" hide-details="auto"
density="compact" density="compact"
@ -171,6 +194,7 @@ const errors = ref({
allowSelfSignedCerts: [], allowSelfSignedCerts: [],
type: [], type: [],
customEndpoint: [], customEndpoint: [],
customChunkEndpoint: [],
chunckedUpload: [], chunckedUpload: [],
}); });

View File

@ -13,5 +13,6 @@ export interface WebdavConfig {
webdavEndpoint: { webdavEndpoint: {
type: WebdavEndpointType; type: WebdavEndpointType;
customEndpoint?: string; customEndpoint?: string;
customChunkEndpoint?: string;
}; };
} }