mirror of
https://github.com/Sebclem/hassio-nextcloud-backup.git
synced 2024-08-18 05:44:54 +02:00
🔨 Refracto
This commit is contained in:
parent
98b91764cc
commit
79f1cfdf09
1
.gitignore
vendored
1
.gitignore
vendored
@ -103,7 +103,6 @@ dist
|
|||||||
|
|
||||||
# TernJS port file
|
# TernJS port file
|
||||||
.tern-port
|
.tern-port
|
||||||
.vscode
|
|
||||||
status.json
|
status.json
|
||||||
conf.json
|
conf.json
|
||||||
webdav_conf.json
|
webdav_conf.json
|
||||||
|
6
nextcloud_backup/backend/.gitignore
vendored
6
nextcloud_backup/backend/.gitignore
vendored
@ -1 +1,5 @@
|
|||||||
dist/**
|
dist/**
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/tasks.json
|
22
nextcloud_backup/backend/.vscode/launch.json
vendored
Normal file
22
nextcloud_backup/backend/.vscode/launch.json
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"console": "integratedTerminal",
|
||||||
|
"internalConsoleOptions": "neverOpen",
|
||||||
|
"name": "nodemon",
|
||||||
|
"program": "${workspaceFolder}/dist/server.js",
|
||||||
|
"request": "launch",
|
||||||
|
"restart": true,
|
||||||
|
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/nodemon",
|
||||||
|
"skipFiles": [
|
||||||
|
"<node_internals>/**"
|
||||||
|
],
|
||||||
|
"type": "node",
|
||||||
|
"preLaunchTask": "npm: build:watch"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
11
nextcloud_backup/backend/.vscode/settings.json
vendored
Normal file
11
nextcloud_backup/backend/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"typescript.inlayHints.enumMemberValues.enabled": true,
|
||||||
|
"typescript.inlayHints.propertyDeclarationTypes.enabled": true,
|
||||||
|
"typescript.inlayHints.parameterNames.suppressWhenArgumentMatchesName": true,
|
||||||
|
"typescript.inlayHints.parameterNames.enabled": "literals",
|
||||||
|
"typescript.inlayHints.functionLikeReturnTypes.enabled": true,
|
||||||
|
"javascript.inlayHints.enumMemberValues.enabled": true,
|
||||||
|
"javascript.inlayHints.propertyDeclarationTypes.enabled": true,
|
||||||
|
"javascript.inlayHints.parameterNames.suppressWhenArgumentMatchesName": true,
|
||||||
|
"javascript.inlayHints.parameterNames.enabled": "literals",
|
||||||
|
}
|
14
nextcloud_backup/backend/.vscode/tasks.json
vendored
Normal file
14
nextcloud_backup/backend/.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"type": "npm",
|
||||||
|
"script": "build:watch",
|
||||||
|
"group": "build",
|
||||||
|
"label": "npm: build:watch",
|
||||||
|
"detail": "tsc -w",
|
||||||
|
"isBackground": true,
|
||||||
|
"problemMatcher": "$tsc-watch"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -4,15 +4,12 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc -p .",
|
"build:watch": "tsc -w",
|
||||||
"debug": "pnpm run build && pnpm run watch-debug",
|
"build": "tsc --build --verbose",
|
||||||
|
"dev": "pnpm build && concurrently -n tsc,node \"pnpm build:watch\" \"pnpm serve:watch\"",
|
||||||
"lint": "tsc --noEmit && eslint \"**/*.{js,ts}\" --quiet --fix",
|
"lint": "tsc --noEmit && eslint \"**/*.{js,ts}\" --quiet --fix",
|
||||||
"serve-debug": "nodemon --inspect dist/server.js",
|
"serve:watch": "nodemon dist/server.js",
|
||||||
"serve": "node dist/server.js",
|
"serve": "node dist/server.js"
|
||||||
"watch-debug": "concurrently -k -p \"[{name}]\" -n \"TypeScript,Node\" -c \"cyan.bold,green.bold\" \"pnpm run watch-ts\" \"pnpm run serve-debug\"",
|
|
||||||
"watch-node": "nodemon dist/server.js",
|
|
||||||
"watch-ts": "tsc -w",
|
|
||||||
"watch": "concurrently -k -p \"[{name}]\" -n \"TypeScript,Node\" -c \"cyan.bold,green.bold\" \"pnpm run watch-ts\" \"pnpm run watch-node\""
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/eslint-plugin": "^5.38.0",
|
"@typescript-eslint/eslint-plugin": "^5.38.0",
|
||||||
@ -22,13 +19,14 @@
|
|||||||
"debug": "4.3.4",
|
"debug": "4.3.4",
|
||||||
"errorhandler": "^1.5.1",
|
"errorhandler": "^1.5.1",
|
||||||
"express": "4.18.1",
|
"express": "4.18.1",
|
||||||
|
"figlet": "^1.5.2",
|
||||||
"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",
|
||||||
"jquery": "3.6.0",
|
"jquery": "3.6.0",
|
||||||
|
"kleur": "^4.1.5",
|
||||||
"luxon": "3.0.1",
|
"luxon": "3.0.1",
|
||||||
"morgan": "1.10.0",
|
"morgan": "1.10.0",
|
||||||
"swagger-ui-express": "^4.5.0",
|
|
||||||
"webdav": "4.10.0",
|
"webdav": "4.10.0",
|
||||||
"winston": "3.8.1"
|
"winston": "3.8.1"
|
||||||
},
|
},
|
||||||
@ -39,14 +37,16 @@
|
|||||||
"@types/cron": "^2.0.0",
|
"@types/cron": "^2.0.0",
|
||||||
"@types/errorhandler": "^1.5.0",
|
"@types/errorhandler": "^1.5.0",
|
||||||
"@types/express": "^4.17.14",
|
"@types/express": "^4.17.14",
|
||||||
|
"@types/figlet": "^1.5.5",
|
||||||
"@types/http-errors": "^1.8.2",
|
"@types/http-errors": "^1.8.2",
|
||||||
"@types/luxon": "^3.0.1",
|
"@types/luxon": "^3.0.1",
|
||||||
"@types/morgan": "^1.9.3",
|
"@types/morgan": "^1.9.3",
|
||||||
"@types/node": "^18.7.18",
|
"@types/node": "^18.7.18",
|
||||||
"@typescript-eslint/parser": "^5.38.0",
|
"@typescript-eslint/parser": "^5.38.0",
|
||||||
"concurrently": "6.0.2",
|
"concurrently": "6.0.2",
|
||||||
|
"dotenv": "^16.0.2",
|
||||||
"eslint": "7.19.0",
|
"eslint": "7.19.0",
|
||||||
"nodemon": "^2.0.7",
|
"nodemon": "^2.0.20",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^4.8.3"
|
"typescript": "^4.8.3"
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
lockfileVersion: 5.4
|
lockfileVersion: 5.4
|
||||||
|
|
||||||
specifiers:
|
specifiers:
|
||||||
'@fortawesome/fontawesome-free': 6.1.2
|
|
||||||
'@tsconfig/recommended': ^1.0.1
|
'@tsconfig/recommended': ^1.0.1
|
||||||
'@types/cookie-parser': ^1.4.3
|
'@types/cookie-parser': ^1.4.3
|
||||||
'@types/cron': ^2.0.0
|
'@types/cron': ^2.0.0
|
||||||
'@types/errorhandler': ^1.5.0
|
'@types/errorhandler': ^1.5.0
|
||||||
'@types/express': ^4.17.14
|
'@types/express': ^4.17.14
|
||||||
|
'@types/figlet': ^1.5.5
|
||||||
'@types/http-errors': ^1.8.2
|
'@types/http-errors': ^1.8.2
|
||||||
'@types/luxon': ^3.0.1
|
'@types/luxon': ^3.0.1
|
||||||
'@types/morgan': ^1.9.3
|
'@types/morgan': ^1.9.3
|
||||||
@ -14,46 +14,44 @@ specifiers:
|
|||||||
'@typescript-eslint/eslint-plugin': ^5.38.0
|
'@typescript-eslint/eslint-plugin': ^5.38.0
|
||||||
'@typescript-eslint/parser': ^5.38.0
|
'@typescript-eslint/parser': ^5.38.0
|
||||||
app-root-path: 3.0.0
|
app-root-path: 3.0.0
|
||||||
bootstrap: 5.2.0
|
|
||||||
concurrently: 6.0.2
|
concurrently: 6.0.2
|
||||||
cookie-parser: 1.4.6
|
cookie-parser: 1.4.6
|
||||||
cron: 2.1.0
|
cron: 2.1.0
|
||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
ejs: 3.1.8
|
dotenv: ^16.0.2
|
||||||
errorhandler: ^1.5.1
|
errorhandler: ^1.5.1
|
||||||
eslint: 7.19.0
|
eslint: 7.19.0
|
||||||
express: 4.18.1
|
express: 4.18.1
|
||||||
|
figlet: ^1.5.2
|
||||||
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
|
||||||
jquery: 3.6.0
|
jquery: 3.6.0
|
||||||
|
kleur: ^4.1.5
|
||||||
luxon: 3.0.1
|
luxon: 3.0.1
|
||||||
morgan: 1.10.0
|
morgan: 1.10.0
|
||||||
nodemon: ^2.0.7
|
nodemon: ^2.0.20
|
||||||
swagger-ui-express: ^4.5.0
|
|
||||||
ts-node: ^10.9.1
|
ts-node: ^10.9.1
|
||||||
typescript: ^4.8.3
|
typescript: ^4.8.3
|
||||||
webdav: 4.10.0
|
webdav: 4.10.0
|
||||||
winston: 3.8.1
|
winston: 3.8.1
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
'@fortawesome/fontawesome-free': 6.1.2
|
|
||||||
'@typescript-eslint/eslint-plugin': 5.38.0_icxzmwhpfrmelnyzbhqbg2q4qi
|
'@typescript-eslint/eslint-plugin': 5.38.0_icxzmwhpfrmelnyzbhqbg2q4qi
|
||||||
app-root-path: 3.0.0
|
app-root-path: 3.0.0
|
||||||
bootstrap: 5.2.0
|
|
||||||
cookie-parser: 1.4.6
|
cookie-parser: 1.4.6
|
||||||
cron: 2.1.0
|
cron: 2.1.0
|
||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
ejs: 3.1.8
|
|
||||||
errorhandler: 1.5.1
|
errorhandler: 1.5.1
|
||||||
express: 4.18.1
|
express: 4.18.1
|
||||||
|
figlet: 1.5.2
|
||||||
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
|
||||||
jquery: 3.6.0
|
jquery: 3.6.0
|
||||||
|
kleur: 4.1.5
|
||||||
luxon: 3.0.1
|
luxon: 3.0.1
|
||||||
morgan: 1.10.0
|
morgan: 1.10.0
|
||||||
swagger-ui-express: 4.5.0_express@4.18.1
|
|
||||||
webdav: 4.10.0_debug@4.3.4
|
webdav: 4.10.0_debug@4.3.4
|
||||||
winston: 3.8.1
|
winston: 3.8.1
|
||||||
|
|
||||||
@ -63,12 +61,14 @@ devDependencies:
|
|||||||
'@types/cron': 2.0.0
|
'@types/cron': 2.0.0
|
||||||
'@types/errorhandler': 1.5.0
|
'@types/errorhandler': 1.5.0
|
||||||
'@types/express': 4.17.14
|
'@types/express': 4.17.14
|
||||||
|
'@types/figlet': 1.5.5
|
||||||
'@types/http-errors': 1.8.2
|
'@types/http-errors': 1.8.2
|
||||||
'@types/luxon': 3.0.1
|
'@types/luxon': 3.0.1
|
||||||
'@types/morgan': 1.9.3
|
'@types/morgan': 1.9.3
|
||||||
'@types/node': 18.7.18
|
'@types/node': 18.7.18
|
||||||
'@typescript-eslint/parser': 5.38.0_xkpbw63dclu5vuec6llpbh3ip4
|
'@typescript-eslint/parser': 5.38.0_xkpbw63dclu5vuec6llpbh3ip4
|
||||||
concurrently: 6.0.2
|
concurrently: 6.0.2
|
||||||
|
dotenv: 16.0.2
|
||||||
eslint: 7.19.0
|
eslint: 7.19.0
|
||||||
nodemon: 2.0.20
|
nodemon: 2.0.20
|
||||||
ts-node: 10.9.1_bidgzm5cq2du6gnjtweqqjrrn4
|
ts-node: 10.9.1_bidgzm5cq2du6gnjtweqqjrrn4
|
||||||
@ -131,12 +131,6 @@ packages:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
/@fortawesome/fontawesome-free/6.1.2:
|
|
||||||
resolution: {integrity: sha512-XwWADtfdSN73/udaFm+1mnGIj/ShDZNFMe/PRoqv3FhQ4GNI2PUN70yFTPsjq65Lw2C9i4TG5/hTbxXIXVCiqQ==}
|
|
||||||
engines: {node: '>=6'}
|
|
||||||
requiresBuild: true
|
|
||||||
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'}
|
||||||
@ -261,6 +255,10 @@ packages:
|
|||||||
'@types/serve-static': 1.15.0
|
'@types/serve-static': 1.15.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/figlet/1.5.5:
|
||||||
|
resolution: {integrity: sha512-0sMBeFoqdGgdXoR/hgKYSWMpFufSpToosNsI2VgmkPqZJgeEXsXNu2hGr0FN401dBro2tNO5y2D6uw3UxVaxbg==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/http-cache-semantics/4.0.1:
|
/@types/http-cache-semantics/4.0.1:
|
||||||
resolution: {integrity: sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==}
|
resolution: {integrity: sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==}
|
||||||
dev: false
|
dev: false
|
||||||
@ -603,12 +601,6 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/bootstrap/5.2.0:
|
|
||||||
resolution: {integrity: sha512-qlnS9GL6YZE6Wnef46GxGv1UpGGzAwO0aPL1yOjzDIJpeApeMvqV24iL+pjr2kU4dduoBA9fINKWKgMToobx9A==}
|
|
||||||
peerDependencies:
|
|
||||||
'@popperjs/core': ^2.11.5
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/brace-expansion/1.1.11:
|
/brace-expansion/1.1.11:
|
||||||
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
|
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -919,18 +911,15 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
esutils: 2.0.3
|
esutils: 2.0.3
|
||||||
|
|
||||||
|
/dotenv/16.0.2:
|
||||||
|
resolution: {integrity: sha512-JvpYKUmzQhYoIFgK2MOnF3bciIZoItIIoryihy0rIA+H4Jy0FmgyKYAHCTN98P5ybGSJcIFbh6QKeJdtZd1qhA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/ee-first/1.1.1:
|
/ee-first/1.1.1:
|
||||||
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
|
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/ejs/3.1.8:
|
|
||||||
resolution: {integrity: sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==}
|
|
||||||
engines: {node: '>=0.10.0'}
|
|
||||||
hasBin: true
|
|
||||||
dependencies:
|
|
||||||
jake: 10.8.5
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/emoji-regex/8.0.0:
|
/emoji-regex/8.0.0:
|
||||||
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||||
|
|
||||||
@ -1178,18 +1167,17 @@ packages:
|
|||||||
resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==}
|
resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/figlet/1.5.2:
|
||||||
|
resolution: {integrity: sha512-WOn21V8AhyE1QqVfPIVxe3tupJacq1xGkPTB4iagT6o+P2cAgEOOwIxMftr4+ZCTI6d551ij9j61DFr0nsP2uQ==}
|
||||||
|
engines: {node: '>= 0.4.0'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/file-entry-cache/6.0.1:
|
/file-entry-cache/6.0.1:
|
||||||
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
|
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
|
||||||
engines: {node: ^10.12.0 || >=12.0.0}
|
engines: {node: ^10.12.0 || >=12.0.0}
|
||||||
dependencies:
|
dependencies:
|
||||||
flat-cache: 3.0.4
|
flat-cache: 3.0.4
|
||||||
|
|
||||||
/filelist/1.0.4:
|
|
||||||
resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==}
|
|
||||||
dependencies:
|
|
||||||
minimatch: 5.1.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/fill-range/7.0.1:
|
/fill-range/7.0.1:
|
||||||
resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
|
resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@ -1505,17 +1493,6 @@ packages:
|
|||||||
/isexe/2.0.0:
|
/isexe/2.0.0:
|
||||||
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
||||||
|
|
||||||
/jake/10.8.5:
|
|
||||||
resolution: {integrity: sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
hasBin: true
|
|
||||||
dependencies:
|
|
||||||
async: 3.2.4
|
|
||||||
chalk: 4.1.2
|
|
||||||
filelist: 1.0.4
|
|
||||||
minimatch: 3.1.2
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/jquery/3.6.0:
|
/jquery/3.6.0:
|
||||||
resolution: {integrity: sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==}
|
resolution: {integrity: sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==}
|
||||||
dev: false
|
dev: false
|
||||||
@ -1553,6 +1530,11 @@ packages:
|
|||||||
json-buffer: 3.0.1
|
json-buffer: 3.0.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/kleur/4.1.5:
|
||||||
|
resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/kuler/2.0.0:
|
/kuler/2.0.0:
|
||||||
resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==}
|
resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==}
|
||||||
dev: false
|
dev: false
|
||||||
@ -2224,20 +2206,6 @@ packages:
|
|||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/swagger-ui-dist/4.14.0:
|
|
||||||
resolution: {integrity: sha512-TBzhheU15s+o54Cgk9qxuYcZMiqSm/SkvKnapoGHOF66kz0Y5aGjpzj5BT/vpBbn6rTPJ9tUYXQxuDWfsjiGMw==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/swagger-ui-express/4.5.0_express@4.18.1:
|
|
||||||
resolution: {integrity: sha512-DHk3zFvsxrkcnurGvQlAcLuTDacAVN1JHKDgcba/gr2NFRE4HGwP1YeHIXMiGznkWR4AeS7X5vEblNn4QljuNA==}
|
|
||||||
engines: {node: '>= v0.10.32'}
|
|
||||||
peerDependencies:
|
|
||||||
express: '>=4.0.0'
|
|
||||||
dependencies:
|
|
||||||
express: 4.18.1
|
|
||||||
swagger-ui-dist: 4.14.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/table/6.8.0:
|
/table/6.8.0:
|
||||||
resolution: {integrity: sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==}
|
resolution: {integrity: sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==}
|
||||||
engines: {node: '>=10.0.0'}
|
engines: {node: '>=10.0.0'}
|
||||||
|
@ -1,23 +1,19 @@
|
|||||||
import createError from "http-errors";
|
|
||||||
import express, { NextFunction, Request, Response } from "express";
|
|
||||||
import path from "path";
|
|
||||||
import cookieParser from "cookie-parser";
|
import cookieParser from "cookie-parser";
|
||||||
import fs from "fs"
|
import express, { NextFunction, Request, Response } from "express";
|
||||||
import newlog from "./config/winston.js"
|
import createError from "http-errors";
|
||||||
import * as statusTools from "./tools/status.js"
|
import morgan from "morgan";
|
||||||
import * as settingsTools from "./tools/settingsTools.js"
|
import path from "path";
|
||||||
import cronTools from "./tools/cronTools.js"
|
|
||||||
import webdav from "./services/webdavService.js";
|
|
||||||
|
|
||||||
import apiRouter from "./routes/api.js"
|
|
||||||
import { fileURLToPath } from "url";
|
import { fileURLToPath } from "url";
|
||||||
|
import logger from "./config/winston.js";
|
||||||
|
import apiV2Router from "./routes/apiV2.js";
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
|
app.set("port", process.env.PORT || 3000);
|
||||||
|
|
||||||
// app.use(
|
// app.use(
|
||||||
// logger("dev", {
|
// logger("dev", {
|
||||||
// skip: function (req, res) {
|
// skip: function (req, res) {
|
||||||
@ -25,13 +21,16 @@ const app = express();
|
|||||||
// },
|
// },
|
||||||
// })
|
// })
|
||||||
// );
|
// );
|
||||||
|
|
||||||
|
app.use(
|
||||||
|
morgan("dev", { stream: { write: (message) => logger.info(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());
|
||||||
app.use(express.static(path.join(__dirname, "public")));
|
app.use(express.static(path.join(__dirname, "public")));
|
||||||
|
|
||||||
app.use("/api", apiRouter);
|
app.use("/v2/api/", apiV2Router);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
-----------------------------------------------------------
|
-----------------------------------------------------------
|
||||||
Error handler
|
Error handler
|
||||||
@ -39,61 +38,19 @@ app.use("/api", apiRouter);
|
|||||||
*/
|
*/
|
||||||
// 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
|
// error handler
|
||||||
app.use((err: any, req: Request, res: Response, next: NextFunction) => {
|
app.use((err: any, req: Request, res: Response, next: NextFunction) => {
|
||||||
// set locals, only providing error in development
|
// set locals, only providing error in development
|
||||||
res.locals.message = err.message;
|
res.locals.message = err.message;
|
||||||
res.locals.error = req.app.get("env") === "development" ? err : {};
|
res.locals.error = req.app.get("env") === "development" ? err : {};
|
||||||
|
|
||||||
// render the error page
|
// render the error page
|
||||||
res.status(err.status || 500);
|
res.status(err.status || 500);
|
||||||
res.render("error");
|
res.render("error");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
-----------------------------------------------------------
|
|
||||||
Init app
|
|
||||||
----------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
newlog.info(`Log level: ${ process.env.LOG_LEVEL }`);
|
|
||||||
|
|
||||||
newlog.info(`Backup timeout: ${ (process.env.CREATE_BACKUP_TIMEOUT ? parseInt(process.env.CREATE_BACKUP_TIMEOUT) : false) || ( 90 * 60 * 1000 ) }`)
|
|
||||||
|
|
||||||
if (!fs.existsSync("/data")) fs.mkdirSync("/data");
|
|
||||||
statusTools.init();
|
|
||||||
newlog.info("Satus : \x1b[32mGo !\x1b[0m");
|
|
||||||
|
|
||||||
|
|
||||||
// TODO Change this
|
|
||||||
// hassioApiTools.getSnapshots().then(
|
|
||||||
// () => {
|
|
||||||
// newlog.info("Hassio API : \x1b[32mGo !\x1b[0m");
|
|
||||||
// },
|
|
||||||
// (err) => {
|
|
||||||
// newlog.error("Hassio API : \x1b[31;1mFAIL !\x1b[0m");
|
|
||||||
// newlog.error("... " + err);
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
|
|
||||||
webdav.confIsValid().then(
|
|
||||||
() => {
|
|
||||||
newlog.info("Nextcloud connection : \x1b[32mGo !\x1b[0m");
|
|
||||||
},
|
|
||||||
(err) => {
|
|
||||||
newlog.error("Nextcloud connection : \x1b[31;1mFAIL !\x1b[0m");
|
|
||||||
newlog.error("... " + err);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
settingsTools.check(settingsTools.getSettings(), true);
|
|
||||||
cronTools.init();
|
|
||||||
|
|
||||||
|
|
||||||
export default app;
|
export default app;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import winston from "winston";
|
import winston from "winston";
|
||||||
|
|
||||||
const logger = winston.createLogger({
|
const logger = winston.createLogger({
|
||||||
level: process.env.LOG_LEVEL || 'info',
|
level: process.env.LOG_LEVEL || 'info',
|
||||||
format: winston.format.combine(
|
format: winston.format.combine(
|
||||||
|
2
nextcloud_backup/backend/src/env.ts
Normal file
2
nextcloud_backup/backend/src/env.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import dotenv from "dotenv";
|
||||||
|
dotenv.config();
|
48
nextcloud_backup/backend/src/postInit.ts
Normal file
48
nextcloud_backup/backend/src/postInit.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { existsSync, mkdirSync } from "fs";
|
||||||
|
import logger from "./config/winston.js";
|
||||||
|
import * as homeAssistantService from "./services/homeAssistantService.js";
|
||||||
|
import * as settingsTools from "./tools/settingsTools.js";
|
||||||
|
import * as statusTools from "./tools/status.js";
|
||||||
|
import kleur from 'kleur';
|
||||||
|
|
||||||
|
|
||||||
|
function postInit() {
|
||||||
|
logger.info(`Log level: ${process.env.LOG_LEVEL}`);
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
`Backup timeout: ${
|
||||||
|
(process.env.CREATE_BACKUP_TIMEOUT
|
||||||
|
? parseInt(process.env.CREATE_BACKUP_TIMEOUT)
|
||||||
|
: false) || 90 * 60 * 1000
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!existsSync("/data")) mkdirSync("/data");
|
||||||
|
statusTools.init();
|
||||||
|
logger.info("Satus : " + kleur.green().bold("Go !"));
|
||||||
|
|
||||||
|
homeAssistantService.getBackups().then(
|
||||||
|
() => {
|
||||||
|
logger.info("Hassio API : " + kleur.green().bold("Go !"));
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
logger.error("Hassio API : " + kleur.red().bold("FAIL !"));
|
||||||
|
logger.error("... " + err);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// webdav.confIsValid().then(
|
||||||
|
// () => {
|
||||||
|
// newlog.info("Nextcloud connection : \x1b[32mGo !\x1b[0m");
|
||||||
|
// },
|
||||||
|
// (err) => {
|
||||||
|
// newlog.error("Nextcloud connection : \x1b[31;1mFAIL !\x1b[0m");
|
||||||
|
// newlog.error("... " + err);
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
|
||||||
|
settingsTools.check(settingsTools.getSettings(), true);
|
||||||
|
// cronTools.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default postInit;
|
@ -1,279 +1,279 @@
|
|||||||
import express from "express";
|
// import express from "express";
|
||||||
import * as statusTools from "../tools/status.js";
|
// import * as statusTools from "../tools/status.js";
|
||||||
import webdav from "../tools/webdavTools.js";
|
// import webdav from "../services/webdavService.js";
|
||||||
import * as settingsTools from "../tools/settingsTools.js";
|
// import * as settingsTools from "../tools/settingsTools.js";
|
||||||
import * as pathTools from "../tools/pathTools.js";
|
// import * as pathTools from "../tools/pathTools.js";
|
||||||
import * as hassioApiTools from "../tools/hassioApiTools.js";
|
// import * as hassioApiTools from "../tools/hassioApiTools.js";
|
||||||
import { humanFileSize } from "../tools/toolbox.js";
|
// import { humanFileSize } from "../tools/toolbox.js";
|
||||||
import cronTools from "../tools/cronTools.js";
|
// import cronTools from "../tools/cronTools.js";
|
||||||
import logger from "../config/winston.js";
|
// import logger from "../config/winston.js";
|
||||||
import { DateTime } from "luxon";
|
// import { DateTime } from "luxon";
|
||||||
|
|
||||||
const router = express.Router();
|
// const router = express.Router();
|
||||||
|
|
||||||
router.get("/status", (req, res, next) => {
|
// router.get("/status", (req, res, next) => {
|
||||||
cronTools.updateNextDate();
|
// cronTools.updateNextDate();
|
||||||
const status = statusTools.getStatus();
|
// const status = statusTools.getStatus();
|
||||||
res.json(status);
|
// res.json(status);
|
||||||
});
|
// });
|
||||||
|
|
||||||
router.get("/formated-local-snap", function (req, res, next) {
|
// router.get("/formated-local-snap", function (req, res, next) {
|
||||||
hassioApiTools
|
// hassioApiTools
|
||||||
.getSnapshots()
|
// .getSnapshots()
|
||||||
.then((snaps) => {
|
// .then((snaps) => {
|
||||||
snaps.sort((a, b) => {
|
// snaps.sort((a, b) => {
|
||||||
return a.date < b.date ? 1 : -1;
|
// return a.date < b.date ? 1 : -1;
|
||||||
});
|
// });
|
||||||
|
|
||||||
res.render("localSnaps", { snaps: snaps, DateTime: DateTime });
|
// res.render("localSnaps", { snaps: snaps, DateTime: DateTime });
|
||||||
})
|
// })
|
||||||
.catch((err) => {
|
// .catch((err) => {
|
||||||
logger.error(err);
|
// logger.error(err);
|
||||||
res.status(500);
|
// res.status(500);
|
||||||
res.send("");
|
// res.send("");
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
|
||||||
router.get("/formated-backup-manual", function (req, res, next) {
|
// router.get("/formated-backup-manual", function (req, res, next) {
|
||||||
if (webdav.getConf() == null) {
|
// if (webdav.getConf() == null) {
|
||||||
res.send("");
|
// res.send("");
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
webdav
|
// webdav
|
||||||
.getFolderContent(webdav.getConf()?.back_dir + pathTools.manual)
|
// .getFolderContent(webdav.getConf()?.back_dir + pathTools.manual)
|
||||||
.then((contents: any) => {
|
// .then((contents: any) => {
|
||||||
contents.sort((a: any, b: any) => {
|
// contents.sort((a: any, b: any) => {
|
||||||
return a.date < b.date ? 1 : -1;
|
// return a.date < b.date ? 1 : -1;
|
||||||
});
|
// });
|
||||||
//TODO Remove this when bug is fixed, etag contain '"' at start and end ?
|
// //TODO Remove this when bug is fixed, etag contain '"' at start and end ?
|
||||||
for (const backup of contents) {
|
// for (const backup of contents) {
|
||||||
backup.etag = backup.etag.replace(/"/g, "");
|
// backup.etag = backup.etag.replace(/"/g, "");
|
||||||
}
|
// }
|
||||||
res.render("backupSnaps", {
|
// res.render("backupSnaps", {
|
||||||
backups: contents,
|
// backups: contents,
|
||||||
DateTime: DateTime,
|
// DateTime: DateTime,
|
||||||
humanFileSize: humanFileSize,
|
// humanFileSize: humanFileSize,
|
||||||
});
|
// });
|
||||||
})
|
// })
|
||||||
.catch((err) => {
|
// .catch((err) => {
|
||||||
res.status(500);
|
// res.status(500);
|
||||||
res.send(err);
|
// res.send(err);
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
|
||||||
router.get("/formated-backup-auto", function (req, res, next) {
|
// router.get("/formated-backup-auto", function (req, res, next) {
|
||||||
if (webdav.getConf() == null) {
|
// if (webdav.getConf() == null) {
|
||||||
res.send("");
|
// res.send("");
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
const url = webdav.getConf()?.back_dir + pathTools.auto;
|
// const url = webdav.getConf()?.back_dir + pathTools.auto;
|
||||||
webdav
|
// webdav
|
||||||
.getFolderContent(url)
|
// .getFolderContent(url)
|
||||||
.then((contents: any) => {
|
// .then((contents: any) => {
|
||||||
contents.sort((a: any, b: any) => {
|
// contents.sort((a: any, b: any) => {
|
||||||
return a.date < b.date ? 1 : -1;
|
// return a.date < b.date ? 1 : -1;
|
||||||
});
|
// });
|
||||||
//TODO Remove this when bug is fixed, etag contain '"' at start and end ?
|
// //TODO Remove this when bug is fixed, etag contain '"' at start and end ?
|
||||||
for (const backup of contents) {
|
// for (const backup of contents) {
|
||||||
backup.etag = backup.etag.replace(/"/g, "");
|
// backup.etag = backup.etag.replace(/"/g, "");
|
||||||
}
|
// }
|
||||||
res.render("backupSnaps", {
|
// res.render("backupSnaps", {
|
||||||
backups: contents,
|
// backups: contents,
|
||||||
DateTime: DateTime,
|
// DateTime: DateTime,
|
||||||
humanFileSize: humanFileSize,
|
// humanFileSize: humanFileSize,
|
||||||
});
|
// });
|
||||||
})
|
// })
|
||||||
.catch((err) => {
|
// .catch((err) => {
|
||||||
res.status(500);
|
// res.status(500);
|
||||||
res.send(err);
|
// res.send(err);
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
|
||||||
router.post("/nextcloud-settings", function (req, res, next) {
|
// router.post("/nextcloud-settings", function (req, res, next) {
|
||||||
const settings = req.body;
|
// const settings = req.body;
|
||||||
if (
|
// if (
|
||||||
settings.ssl != null &&
|
// settings.ssl != null &&
|
||||||
settings.host != null &&
|
// settings.host != null &&
|
||||||
settings.host !== "" &&
|
// settings.host !== "" &&
|
||||||
settings.username != null &&
|
// settings.username != null &&
|
||||||
settings.password != null
|
// settings.password != null
|
||||||
) {
|
// ) {
|
||||||
webdav.setConf(settings);
|
// webdav.setConf(settings);
|
||||||
webdav
|
// webdav
|
||||||
.confIsValid()
|
// .confIsValid()
|
||||||
.then(() => {
|
// .then(() => {
|
||||||
res.status(201);
|
// res.status(201);
|
||||||
res.send();
|
// res.send();
|
||||||
})
|
// })
|
||||||
.catch((err) => {
|
// .catch((err) => {
|
||||||
res.status(406);
|
// res.status(406);
|
||||||
res.json({ message: err });
|
// res.json({ message: err });
|
||||||
});
|
// });
|
||||||
} else {
|
// } else {
|
||||||
res.status(400);
|
// res.status(400);
|
||||||
res.send();
|
// res.send();
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
|
|
||||||
router.get("/nextcloud-settings", function (req, res, next) {
|
// router.get("/nextcloud-settings", function (req, res, next) {
|
||||||
const conf = webdav.getConf();
|
// const conf = webdav.getConf();
|
||||||
if (conf == null) {
|
// if (conf == null) {
|
||||||
res.status(404);
|
// res.status(404);
|
||||||
res.send();
|
// res.send();
|
||||||
} else {
|
// } else {
|
||||||
res.json(conf);
|
// res.json(conf);
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
|
|
||||||
router.post("/manual-backup", function (req, res, next) {
|
// router.post("/manual-backup", function (req, res, next) {
|
||||||
const id = req.query.id;
|
// const id = req.query.id;
|
||||||
const name = req.query.name;
|
// const name = req.query.name;
|
||||||
const status = statusTools.getStatus();
|
// const status = statusTools.getStatus();
|
||||||
if (
|
// if (
|
||||||
status.status == "creating" ||
|
// status.status == "creating" ||
|
||||||
status.status == "upload" ||
|
// status.status == "upload" ||
|
||||||
status.status == "download"
|
// status.status == "download"
|
||||||
) {
|
// ) {
|
||||||
res.status(503);
|
// res.status(503);
|
||||||
res.send();
|
// res.send();
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
hassioApiTools
|
// hassioApiTools
|
||||||
.downloadSnapshot(id as string)
|
// .downloadSnapshot(id as string)
|
||||||
.then(() => {
|
// .then(() => {
|
||||||
webdav
|
// webdav
|
||||||
.uploadFile(
|
// .uploadFile(
|
||||||
id as string,
|
// id as string,
|
||||||
webdav.getConf()?.back_dir + pathTools.manual + name + ".tar"
|
// webdav.getConf()?.back_dir + pathTools.manual + name + ".tar"
|
||||||
)
|
// )
|
||||||
.then(() => {
|
// .then(() => {
|
||||||
res.status(201);
|
// res.status(201);
|
||||||
res.send();
|
// res.send();
|
||||||
})
|
// })
|
||||||
.catch(() => {
|
// .catch(() => {
|
||||||
res.status(500);
|
// res.status(500);
|
||||||
res.send();
|
// res.send();
|
||||||
});
|
// });
|
||||||
})
|
// })
|
||||||
.catch(() => {
|
// .catch(() => {
|
||||||
res.status(500);
|
// res.status(500);
|
||||||
res.send();
|
// res.send();
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
|
||||||
router.post("/new-backup", function (req, res, next) {
|
// router.post("/new-backup", function (req, res, next) {
|
||||||
const status = statusTools.getStatus();
|
// const status = statusTools.getStatus();
|
||||||
if (
|
// if (
|
||||||
status.status == "creating" ||
|
// status.status == "creating" ||
|
||||||
status.status == "upload" ||
|
// status.status == "upload" ||
|
||||||
status.status == "download" ||
|
// status.status == "download" ||
|
||||||
status.status == "stopping" ||
|
// status.status == "stopping" ||
|
||||||
status.status == "starting"
|
// status.status == "starting"
|
||||||
) {
|
// ) {
|
||||||
res.status(503);
|
// res.status(503);
|
||||||
res.send();
|
// res.send();
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
hassioApiTools
|
// hassioApiTools
|
||||||
.stopAddons()
|
// .stopAddons()
|
||||||
.then(() => {
|
// .then(() => {
|
||||||
hassioApiTools
|
// hassioApiTools
|
||||||
.getVersion()
|
// .getVersion()
|
||||||
.then((version) => {
|
// .then((version) => {
|
||||||
const name = settingsTools.getFormatedName(true, version);
|
// const name = settingsTools.getFormatedName(true, version);
|
||||||
hassioApiTools
|
// hassioApiTools
|
||||||
.createNewBackup(name)
|
// .createNewBackup(name)
|
||||||
.then((id) => {
|
// .then((id) => {
|
||||||
hassioApiTools
|
// hassioApiTools
|
||||||
.downloadSnapshot(id)
|
// .downloadSnapshot(id)
|
||||||
.then(() => {
|
// .then(() => {
|
||||||
webdav
|
// webdav
|
||||||
.uploadFile(
|
// .uploadFile(
|
||||||
id,
|
// id,
|
||||||
webdav.getConf()?.back_dir +
|
// webdav.getConf()?.back_dir +
|
||||||
pathTools.manual +
|
// pathTools.manual +
|
||||||
name +
|
// name +
|
||||||
".tar"
|
// ".tar"
|
||||||
)
|
// )
|
||||||
.then(() => {
|
// .then(() => {
|
||||||
hassioApiTools.startAddons().catch(() => {
|
// hassioApiTools.startAddons().catch(() => {
|
||||||
// Skip
|
// // Skip
|
||||||
});
|
// });
|
||||||
})
|
// })
|
||||||
.catch(() => {
|
// .catch(() => {
|
||||||
// Skip
|
// // Skip
|
||||||
});
|
// });
|
||||||
})
|
// })
|
||||||
.catch(() => {
|
// .catch(() => {
|
||||||
// Skip
|
// // Skip
|
||||||
});
|
// });
|
||||||
})
|
// })
|
||||||
.catch(() => {
|
// .catch(() => {
|
||||||
// Skip
|
// // Skip
|
||||||
});
|
// });
|
||||||
})
|
// })
|
||||||
.catch(() => {
|
// .catch(() => {
|
||||||
// Skip
|
// // Skip
|
||||||
});
|
// });
|
||||||
})
|
// })
|
||||||
.catch(() => {
|
// .catch(() => {
|
||||||
hassioApiTools.startAddons().catch(() => {
|
// hassioApiTools.startAddons().catch(() => {
|
||||||
// Skip
|
// // Skip
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
|
||||||
res.status(201);
|
// res.status(201);
|
||||||
res.send();
|
// res.send();
|
||||||
});
|
// });
|
||||||
|
|
||||||
router.get("/backup-settings", function (req, res, next) {
|
// router.get("/backup-settings", function (req, res, next) {
|
||||||
hassioApiTools.getAddonList().then((addonList) => {
|
// hassioApiTools.getAddonList().then((addonList) => {
|
||||||
const data = {
|
// const data = {
|
||||||
folder: hassioApiTools.getFolderList(),
|
// folder: hassioApiTools.getFolderList(),
|
||||||
addonList: addonList,
|
// addonList: addonList,
|
||||||
settings: settingsTools.getSettings()
|
// settings: settingsTools.getSettings()
|
||||||
};
|
// };
|
||||||
res.send(data);
|
// res.send(data);
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
|
||||||
router.post("/backup-settings", function (req, res, next) {
|
// router.post("/backup-settings", function (req, res, next) {
|
||||||
const [result, message] = settingsTools.check(req.body);
|
// const [result, message] = settingsTools.check(req.body);
|
||||||
if (result) {
|
// if (result) {
|
||||||
settingsTools.setSettings(req.body);
|
// settingsTools.setSettings(req.body);
|
||||||
cronTools.init();
|
// cronTools.init();
|
||||||
res.send();
|
// res.send();
|
||||||
} else {
|
// } else {
|
||||||
res.status(400);
|
// res.status(400);
|
||||||
res.send(message);
|
// res.send(message);
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
|
|
||||||
router.post("/clean-now", function (req, res, next) {
|
// router.post("/clean-now", function (req, res, next) {
|
||||||
webdav
|
// webdav
|
||||||
.clean()
|
// .clean()
|
||||||
.then(() => {
|
// .then(() => {
|
||||||
hassioApiTools.clean().catch();
|
// hassioApiTools.clean().catch();
|
||||||
})
|
// })
|
||||||
.catch(() => {
|
// .catch(() => {
|
||||||
hassioApiTools.clean().catch();
|
// hassioApiTools.clean().catch();
|
||||||
});
|
// });
|
||||||
res.status(201);
|
// res.status(201);
|
||||||
res.send();
|
// res.send();
|
||||||
});
|
// });
|
||||||
|
|
||||||
router.post("/restore", function (req, res, next) {
|
// router.post("/restore", function (req, res, next) {
|
||||||
if (req.body["path"] != null) {
|
// if (req.body["path"] != null) {
|
||||||
webdav.downloadFile(req.body["path"]).then((path) => {
|
// webdav.downloadFile(req.body["path"]).then((path) => {
|
||||||
hassioApiTools.uploadSnapshot(path).catch();
|
// hassioApiTools.uploadSnapshot(path).catch();
|
||||||
});
|
// });
|
||||||
res.status(200);
|
// res.status(200);
|
||||||
res.send();
|
// res.send();
|
||||||
} else {
|
// } else {
|
||||||
res.status(400);
|
// res.status(400);
|
||||||
res.send();
|
// res.send();
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
|
|
||||||
export default router;
|
// export default router;
|
||||||
|
9
nextcloud_backup/backend/src/routes/apiV2.ts
Normal file
9
nextcloud_backup/backend/src/routes/apiV2.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import express from "express"
|
||||||
|
import homeAssistant from "./homeAssistant.js"
|
||||||
|
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.use("/homeAssistant", homeAssistant)
|
||||||
|
|
||||||
|
export default router;
|
19
nextcloud_backup/backend/src/routes/homeAssistant.ts
Normal file
19
nextcloud_backup/backend/src/routes/homeAssistant.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import express from "express";
|
||||||
|
import * as haOsService from "../services/homeAssistantService.js"
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.get("/backups/", (req, res, next) => {
|
||||||
|
haOsService.getBackups()
|
||||||
|
.then((value)=>{
|
||||||
|
res.json(value.body.data.backups);
|
||||||
|
}).catch((reason)=>{
|
||||||
|
res.status(500);
|
||||||
|
res.json(reason);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
router.get("")
|
||||||
|
|
||||||
|
export default router;
|
@ -1,5 +1,10 @@
|
|||||||
import errorHandler from "errorhandler";
|
import errorHandler from "errorhandler";
|
||||||
|
import "./env.js";
|
||||||
import app from "./app.js";
|
import app from "./app.js";
|
||||||
|
import logger from "./config/winston.js";
|
||||||
|
import postInit from "./postInit.js";
|
||||||
|
import figlet from "figlet";
|
||||||
|
import kleur from 'kleur';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -14,12 +19,12 @@ if (process.env.NODE_ENV === "development") {
|
|||||||
* Start Express server.
|
* Start Express server.
|
||||||
*/
|
*/
|
||||||
const server = app.listen(app.get("port"), () => {
|
const server = app.listen(app.get("port"), () => {
|
||||||
console.log(
|
console.log(kleur.yellow().bold(figlet.textSync("NC Backup")))
|
||||||
" App is running at http://localhost:%d in %s mode",
|
logger.info(
|
||||||
app.get("port"),
|
`App is running at ` + kleur.green().bold(`http://localhost:${app.get("port")}`) + " in " + kleur.green().bold(app.get("env")) + " mode"
|
||||||
app.get("env")
|
|
||||||
);
|
);
|
||||||
console.log(" Press CTRL-C to stop\n");
|
logger.info(kleur.red().bold("Press CTRL-C to stop"));
|
||||||
|
postInit();
|
||||||
});
|
});
|
||||||
|
|
||||||
export default server;
|
export default server;
|
@ -10,10 +10,13 @@ import * as settingsTools from "../tools/settingsTools.js";
|
|||||||
import * as statusTools from "../tools/status.js";
|
import * as statusTools from "../tools/status.js";
|
||||||
import { NewPartialBackupPayload } from "../types/services/ha_os_payload.js";
|
import { NewPartialBackupPayload } from "../types/services/ha_os_payload.js";
|
||||||
import {
|
import {
|
||||||
|
AddonData,
|
||||||
AddonModel,
|
AddonModel,
|
||||||
|
BackupData,
|
||||||
BackupDetailModel,
|
BackupDetailModel,
|
||||||
BackupModel,
|
BackupModel,
|
||||||
CoreInfoBody
|
CoreInfoBody,
|
||||||
|
SupervisorResponse,
|
||||||
} from "../types/services/ha_os_response.js";
|
} from "../types/services/ha_os_response.js";
|
||||||
import { Status } from "../types/status.js";
|
import { Status } from "../types/status.js";
|
||||||
|
|
||||||
@ -26,8 +29,8 @@ const create_snap_timeout = process.env.CREATE_BACKUP_TIMEOUT
|
|||||||
? parseInt(process.env.CREATE_BACKUP_TIMEOUT)
|
? parseInt(process.env.CREATE_BACKUP_TIMEOUT)
|
||||||
: 90 * 60 * 1000;
|
: 90 * 60 * 1000;
|
||||||
|
|
||||||
function getVersion(): Promise<Response<CoreInfoBody>> {
|
function getVersion(): Promise<Response<SupervisorResponse<CoreInfoBody>>> {
|
||||||
return got<CoreInfoBody>("http://hassio/core/info", {
|
return got<SupervisorResponse<CoreInfoBody>>("http://hassio/core/info", {
|
||||||
headers: { authorization: `Bearer ${token}` },
|
headers: { authorization: `Bearer ${token}` },
|
||||||
responseType: "json",
|
responseType: "json",
|
||||||
}).then(
|
}).then(
|
||||||
@ -41,19 +44,22 @@ function getVersion(): Promise<Response<CoreInfoBody>> {
|
|||||||
);
|
);
|
||||||
logger.error(`Fail to fetch Home Assistant version`);
|
logger.error(`Fail to fetch Home Assistant version`);
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return error;
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAddonList(): Promise<Response<{ addons: AddonModel[] }>> {
|
function getAddonList(): Promise<Response<SupervisorResponse<AddonData>>> {
|
||||||
const option: OptionsOfJSONResponseBody = {
|
const option: OptionsOfJSONResponseBody = {
|
||||||
headers: { authorization: `Bearer ${token}` },
|
headers: { authorization: `Bearer ${token}` },
|
||||||
responseType: "json",
|
responseType: "json",
|
||||||
};
|
};
|
||||||
return got<{ addons: AddonModel[] }>("http://hassio/addons", option).then(
|
return got<SupervisorResponse<AddonData>>(
|
||||||
|
"http://hassio/addons",
|
||||||
|
option
|
||||||
|
).then(
|
||||||
(result) => {
|
(result) => {
|
||||||
result.body.addons.sort((a, b) => {
|
result.body.data.addons.sort((a, b) => {
|
||||||
const textA = a.name.toUpperCase();
|
const textA = a.name.toUpperCase();
|
||||||
const textB = b.name.toUpperCase();
|
const textB = b.name.toUpperCase();
|
||||||
return textA < textB ? -1 : textA > textB ? 1 : 0;
|
return textA < textB ? -1 : textA > textB ? 1 : 0;
|
||||||
@ -64,22 +70,20 @@ function getAddonList(): Promise<Response<{ addons: AddonModel[] }>> {
|
|||||||
messageManager.error("Fail to fetch addons list", error?.message);
|
messageManager.error("Fail to fetch addons list", error?.message);
|
||||||
logger.error(`Fail to fetch addons list (${error?.message})`);
|
logger.error(`Fail to fetch addons list (${error?.message})`);
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return error;
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAddonToBackup() {
|
function getAddonToBackup(addons: AddonModel[]) {
|
||||||
const excluded_addon = settingsTools.getSettings().exclude_addon;
|
const excluded_addon = settingsTools.getSettings().exclude_addon;
|
||||||
return getAddonList().then((response) => {
|
const slugs: string[] = [];
|
||||||
const slugs: string[] = [];
|
for (const addon of addons) {
|
||||||
for (const addon of response.body.addons) {
|
if (!excluded_addon.includes(addon.slug)) slugs.push(addon.slug);
|
||||||
if (!excluded_addon.includes(addon.slug)) slugs.push(addon.slug);
|
}
|
||||||
}
|
logger.debug("Addon to backup:");
|
||||||
logger.debug("Addon to backup:");
|
logger.debug(slugs);
|
||||||
logger.debug(slugs);
|
return slugs;
|
||||||
return slugs;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFolderList() {
|
function getFolderList() {
|
||||||
@ -119,18 +123,21 @@ function getFolderToBackup() {
|
|||||||
return slugs;
|
return slugs;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBackups(): Promise<Response<{ backups: BackupModel[] }>> {
|
function getBackups(): Promise<Response<SupervisorResponse<BackupData>>> {
|
||||||
const option: OptionsOfJSONResponseBody = {
|
const option: OptionsOfJSONResponseBody = {
|
||||||
headers: { authorization: `Bearer ${token}` },
|
headers: { authorization: `Bearer ${token}` },
|
||||||
responseType: "json",
|
responseType: "json",
|
||||||
};
|
};
|
||||||
return got<{ backups: BackupModel[] }>("http://hassio/backups", option).then(
|
return got<SupervisorResponse<BackupData>>(
|
||||||
|
"http://hassio/backups",
|
||||||
|
option
|
||||||
|
).then(
|
||||||
(result) => {
|
(result) => {
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
messageManager.error("Fail to fetch Hassio backups", error?.message);
|
messageManager.error("Fail to fetch Hassio backups", error?.message);
|
||||||
return error;
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -177,12 +184,12 @@ function downloadSnapshot(id: string): Promise<string> {
|
|||||||
"Fail to download Home Assistant backup",
|
"Fail to download Home Assistant backup",
|
||||||
reason.message
|
reason.message
|
||||||
);
|
);
|
||||||
return reason;
|
return Promise.reject(reason);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function delSnap(id: string): Promise<Response<string>> {
|
function delSnap(id: string) {
|
||||||
const option = {
|
const option = {
|
||||||
headers: { authorization: `Bearer ${token}` },
|
headers: { authorization: `Bearer ${token}` },
|
||||||
};
|
};
|
||||||
@ -197,23 +204,23 @@ function delSnap(id: string): Promise<Response<string>> {
|
|||||||
);
|
);
|
||||||
logger.error("Fail to retrive Homme assistant backup detail.");
|
logger.error("Fail to retrive Homme assistant backup detail.");
|
||||||
logger.error(reason);
|
logger.error(reason);
|
||||||
return reason;
|
return Promise.reject(reason);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkSnap(id: string): Promise<Response<BackupDetailModel>> {
|
function checkSnap(id: string) {
|
||||||
const option: OptionsOfJSONResponseBody = {
|
const option: OptionsOfJSONResponseBody = {
|
||||||
headers: { authorization: `Bearer ${token}` },
|
headers: { authorization: `Bearer ${token}` },
|
||||||
responseType: "json",
|
responseType: "json",
|
||||||
};
|
};
|
||||||
return got<BackupDetailModel>(
|
return got<SupervisorResponse<BackupDetailModel>>(
|
||||||
`http://hassio/backups/${id}/info`,
|
`http://hassio/backups/${id}/info`,
|
||||||
option
|
option
|
||||||
).then(
|
).then(
|
||||||
(result) => {
|
(result) => {
|
||||||
logger.info("Backup found !");
|
logger.info("Backup found !");
|
||||||
logger.debug(`Backup size: ${result.body.size}`);
|
logger.debug(`Backup size: ${result.body.data.size}`);
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
(reason) => {
|
(reason) => {
|
||||||
@ -223,7 +230,7 @@ function checkSnap(id: string): Promise<Response<BackupDetailModel>> {
|
|||||||
);
|
);
|
||||||
logger.error("Fail to retrive Homme assistant backup detail");
|
logger.error("Fail to retrive Homme assistant backup detail");
|
||||||
logger.error(reason);
|
logger.error(reason);
|
||||||
return reason;
|
return Promise.reject(reason);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -232,7 +239,7 @@ function createNewBackup(
|
|||||||
name: string,
|
name: string,
|
||||||
addonSlugs: string[],
|
addonSlugs: string[],
|
||||||
folders: string[]
|
folders: string[]
|
||||||
): Promise<Response<{ slug: string }>> {
|
) {
|
||||||
const status = statusTools.getStatus();
|
const status = statusTools.getStatus();
|
||||||
status.status = "creating";
|
status.status = "creating";
|
||||||
status.progress = -1;
|
status.progress = -1;
|
||||||
@ -258,17 +265,20 @@ function createNewBackup(
|
|||||||
json: body,
|
json: body,
|
||||||
};
|
};
|
||||||
return got
|
return got
|
||||||
.post<{ slug: string }>(`http://hassio/backups/new/partial`, option)
|
.post<SupervisorResponse<{ slug: string }>>(
|
||||||
|
`http://hassio/backups/new/partial`,
|
||||||
|
option
|
||||||
|
)
|
||||||
.then(
|
.then(
|
||||||
(result) => {
|
(result) => {
|
||||||
logger.info(`Snapshot created with id ${result.body.slug}`);
|
logger.info(`Snapshot created with id ${result.body.data.slug}`);
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
(reason) => {
|
(reason) => {
|
||||||
messageManager.error("Fail to create new backup.", reason.message);
|
messageManager.error("Fail to create new backup.", reason.message);
|
||||||
logger.error("Fail to create new backup");
|
logger.error("Fail to create new backup");
|
||||||
logger.error(reason);
|
logger.error(reason);
|
||||||
return reason;
|
return Promise.reject(reason);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
479
nextcloud_backup/backend/src/services/webdavService.ts
Normal file
479
nextcloud_backup/backend/src/services/webdavService.ts
Normal file
@ -0,0 +1,479 @@
|
|||||||
|
// import fs from "fs";
|
||||||
|
// import got from "got";
|
||||||
|
// import https from "https";
|
||||||
|
// import path from "path";
|
||||||
|
// import stream from "stream";
|
||||||
|
// import { promisify } from "util";
|
||||||
|
// import { createClient, WebDAVClient } from "webdav";
|
||||||
|
|
||||||
|
// import { DateTime } from "luxon";
|
||||||
|
// import logger from "../config/winston.js";
|
||||||
|
// import { WebdavSettings } from "../types/settings.js";
|
||||||
|
// import * as pathTools from "../tools/pathTools.js";
|
||||||
|
// import * as settingsTools from "../tools/settingsTools.js";
|
||||||
|
// import * as statusTools from "../tools/status.js";
|
||||||
|
|
||||||
|
// const endpoint = "/remote.php/webdav";
|
||||||
|
// const configPath = "/data/webdav_conf.json";
|
||||||
|
|
||||||
|
// const pipeline = promisify(stream.pipeline);
|
||||||
|
|
||||||
|
// class WebdavTools {
|
||||||
|
// host: string | undefined;
|
||||||
|
// client: WebDAVClient | undefined;
|
||||||
|
// baseUrl: string | undefined;
|
||||||
|
// username: string | undefined;
|
||||||
|
// password: string | undefined;
|
||||||
|
|
||||||
|
// init(
|
||||||
|
// ssl: boolean,
|
||||||
|
// host: string,
|
||||||
|
// username: string,
|
||||||
|
// password: string,
|
||||||
|
// accept_selfsigned_cert: boolean
|
||||||
|
// ) {
|
||||||
|
// return new Promise((resolve, reject) => {
|
||||||
|
// this.host = host;
|
||||||
|
// const status = statusTools.getStatus();
|
||||||
|
// logger.info("Initializing and checking webdav client...");
|
||||||
|
// this.baseUrl = (ssl ? "https" : "http") + "://" + host + endpoint;
|
||||||
|
// this.username = username;
|
||||||
|
// this.password = password;
|
||||||
|
// const agent_option = ssl
|
||||||
|
// ? { rejectUnauthorized: !accept_selfsigned_cert }
|
||||||
|
// : {};
|
||||||
|
// try {
|
||||||
|
// this.client = createClient(this.baseUrl, {
|
||||||
|
// username: username,
|
||||||
|
// password: password,
|
||||||
|
// httpsAgent: new https.Agent(agent_option),
|
||||||
|
// });
|
||||||
|
|
||||||
|
// this.client
|
||||||
|
// .getDirectoryContents("/")
|
||||||
|
// .then(() => {
|
||||||
|
// if (status.error_code === 3) {
|
||||||
|
// status.status = "idle";
|
||||||
|
// status.message = undefined;
|
||||||
|
// status.error_code = undefined;
|
||||||
|
// statusTools.setStatus(status);
|
||||||
|
// }
|
||||||
|
// logger.debug("Nextcloud connection: \x1b[32mSuccess !\x1b[0m");
|
||||||
|
// this.initFolder().then(() => {
|
||||||
|
// resolve(undefined);
|
||||||
|
// });
|
||||||
|
// })
|
||||||
|
// .catch((error) => {
|
||||||
|
// this.__cant_connect_status(error);
|
||||||
|
// this.client = undefined;
|
||||||
|
// reject("Can't connect to Nextcloud (" + error + ") !");
|
||||||
|
// });
|
||||||
|
// } catch (err) {
|
||||||
|
// this.__cant_connect_status(err);
|
||||||
|
// this.client = undefined;
|
||||||
|
// reject("Can't connect to Nextcloud (" + err + ") !");
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// __cant_connect_status(err: any) {
|
||||||
|
// statusTools.setError(`Can't connect to Nextcloud (${err})`, 3);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// async __createRoot() {
|
||||||
|
// const conf = this.getConf();
|
||||||
|
// if (this.client && conf) {
|
||||||
|
// const root_splited = conf.back_dir.split("/").splice(1);
|
||||||
|
// let path = "/";
|
||||||
|
// for (const elem of root_splited) {
|
||||||
|
// if (elem !== "") {
|
||||||
|
// path = path + elem + "/";
|
||||||
|
// try {
|
||||||
|
// await this.client.createDirectory(path);
|
||||||
|
// logger.debug(`Path ${path} created.`);
|
||||||
|
// } catch (error) {
|
||||||
|
// if ((error as any).status === 405)
|
||||||
|
// logger.debug(`Path ${path} already exist.`);
|
||||||
|
// else logger.error(error);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// initFolder() {
|
||||||
|
// return new Promise((resolve, reject) => {
|
||||||
|
// this.__createRoot()
|
||||||
|
// .catch((err) => {
|
||||||
|
// logger.error(err);
|
||||||
|
// })
|
||||||
|
// .then(() => {
|
||||||
|
// if (!this.client) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// this.client
|
||||||
|
// .createDirectory(this.getConf()?.back_dir + pathTools.auto)
|
||||||
|
// .catch(() => {
|
||||||
|
// // Ignore
|
||||||
|
// })
|
||||||
|
// .then(() => {
|
||||||
|
// if (!this.client) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// this.client
|
||||||
|
// .createDirectory(this.getConf()?.back_dir + pathTools.manual)
|
||||||
|
// .catch(() => {
|
||||||
|
// // Ignore
|
||||||
|
// })
|
||||||
|
// .then(() => {
|
||||||
|
// resolve(undefined);
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Check if theh webdav config is valid, if yes, start init of webdav client
|
||||||
|
// */
|
||||||
|
// confIsValid() {
|
||||||
|
// return new Promise((resolve, reject) => {
|
||||||
|
// const status = statusTools.getStatus();
|
||||||
|
// const conf = this.getConf();
|
||||||
|
// if (conf != undefined) {
|
||||||
|
// if (
|
||||||
|
// conf.ssl != undefined &&
|
||||||
|
// conf.host != undefined &&
|
||||||
|
// conf.username != undefined &&
|
||||||
|
// conf.password != undefined
|
||||||
|
// ) {
|
||||||
|
// if (status.error_code === 2) {
|
||||||
|
// status.status = "idle";
|
||||||
|
// status.message = undefined;
|
||||||
|
// status.error_code = undefined;
|
||||||
|
// statusTools.setStatus(status);
|
||||||
|
// }
|
||||||
|
// // Check if self_signed option exist
|
||||||
|
// if (conf.self_signed == undefined) {
|
||||||
|
// conf.self_signed = false;
|
||||||
|
// this.setConf(conf);
|
||||||
|
// }
|
||||||
|
// this.init(
|
||||||
|
// conf.ssl,
|
||||||
|
// conf.host,
|
||||||
|
// conf.username,
|
||||||
|
// conf.password,
|
||||||
|
// conf.self_signed
|
||||||
|
// )
|
||||||
|
// .then(() => {
|
||||||
|
// resolve(undefined);
|
||||||
|
// })
|
||||||
|
// .catch((err) => {
|
||||||
|
// reject(err);
|
||||||
|
// });
|
||||||
|
// } else {
|
||||||
|
// status.status = "error";
|
||||||
|
// status.error_code = 2;
|
||||||
|
// status.message = "Nextcloud config invalid !";
|
||||||
|
// statusTools.setStatus(status);
|
||||||
|
// logger.error(status.message);
|
||||||
|
// reject("Nextcloud config invalid !");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (conf.back_dir == null || conf.back_dir === "") {
|
||||||
|
// logger.info("Backup dir is null, initializing it.");
|
||||||
|
// conf.back_dir = pathTools.default_root;
|
||||||
|
// this.setConf(conf);
|
||||||
|
// } else {
|
||||||
|
// if (!conf.back_dir.startsWith("/")) {
|
||||||
|
// logger.warn("Backup dir not starting with '/', fixing this...");
|
||||||
|
// conf.back_dir = `/${conf.back_dir}`;
|
||||||
|
// this.setConf(conf);
|
||||||
|
// }
|
||||||
|
// if (!conf.back_dir.endsWith("/")) {
|
||||||
|
// logger.warn("Backup dir not ending with '/', fixing this...");
|
||||||
|
// conf.back_dir = `${conf.back_dir}/`;
|
||||||
|
// this.setConf(conf);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// status.status = "error";
|
||||||
|
// status.error_code = 2;
|
||||||
|
// status.message = "Nextcloud config not found !";
|
||||||
|
// statusTools.setStatus(status);
|
||||||
|
// logger.error(status.message);
|
||||||
|
// reject("Nextcloud config not found !");
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// getConf(): WebdavSettings | undefined {
|
||||||
|
// if (fs.existsSync(configPath)) {
|
||||||
|
// return JSON.parse(fs.readFileSync(configPath).toString());
|
||||||
|
// } else return undefined;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// setConf(conf: WebdavSettings) {
|
||||||
|
// fs.writeFileSync(configPath, JSON.stringify(conf));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// uploadFile(id: string, path: string) {
|
||||||
|
// return new Promise((resolve, reject) => {
|
||||||
|
// if (this.client == null) {
|
||||||
|
// this.confIsValid()
|
||||||
|
// .then(() => {
|
||||||
|
// this._startUpload(id, path)
|
||||||
|
// .then(() => resolve(undefined))
|
||||||
|
// .catch((err) => reject(err));
|
||||||
|
// })
|
||||||
|
// .catch((err) => {
|
||||||
|
// reject(err);
|
||||||
|
// });
|
||||||
|
// } else
|
||||||
|
// this._startUpload(id, path)
|
||||||
|
// .then(() => resolve(undefined))
|
||||||
|
// .catch((err) => reject(err));
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// _startUpload(id: string, path: string) {
|
||||||
|
// return new Promise((resolve, reject) => {
|
||||||
|
// const status = statusTools.getStatus();
|
||||||
|
// status.status = "upload";
|
||||||
|
// status.progress = 0;
|
||||||
|
// status.message = undefined;
|
||||||
|
// status.error_code = undefined;
|
||||||
|
// statusTools.setStatus(status);
|
||||||
|
// logger.info("Uploading snap...");
|
||||||
|
// const tmpFile = `./temp/${id}.tar`;
|
||||||
|
// const stats = fs.statSync(tmpFile);
|
||||||
|
// const stream = fs.createReadStream(tmpFile);
|
||||||
|
// const conf = this.getConf();
|
||||||
|
// const options = {
|
||||||
|
// body: stream,
|
||||||
|
// headers: {
|
||||||
|
// authorization:
|
||||||
|
// "Basic " +
|
||||||
|
// Buffer.from(this.username + ":" + this.password).toString("base64"),
|
||||||
|
// "content-length": String(stats.size),
|
||||||
|
// },
|
||||||
|
// https: undefined as any | undefined,
|
||||||
|
// };
|
||||||
|
// if (conf?.ssl) {
|
||||||
|
// options["https"] = { rejectUnauthorized: !conf.self_signed };
|
||||||
|
// }
|
||||||
|
// logger.debug(
|
||||||
|
// `...URI: ${encodeURI(
|
||||||
|
// this.baseUrl?.replace(this.host as string, "host.hiden") + path
|
||||||
|
// )}`
|
||||||
|
// );
|
||||||
|
// if (conf?.ssl)
|
||||||
|
// logger.debug(
|
||||||
|
// `...rejectUnauthorized: ${options.https?.rejectUnauthorized}`
|
||||||
|
// );
|
||||||
|
|
||||||
|
// got.stream
|
||||||
|
// .put(encodeURI(this.baseUrl + path), options)
|
||||||
|
// .on("uploadProgress", (e) => {
|
||||||
|
// const percent = e.percent;
|
||||||
|
// if (status.progress !== percent) {
|
||||||
|
// status.progress = percent;
|
||||||
|
// statusTools.setStatus(status);
|
||||||
|
// }
|
||||||
|
// if (percent >= 1) {
|
||||||
|
// logger.info("Upload done...");
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// .on("response", (res) => {
|
||||||
|
// if (res.statusCode !== 201 && res.statusCode !== 204) {
|
||||||
|
// status.status = "error";
|
||||||
|
// status.error_code = 4;
|
||||||
|
// status.message = `Fail to upload snapshot to nextcloud (Status code: ${res.statusCode})!`;
|
||||||
|
// statusTools.setStatus(status);
|
||||||
|
// logger.error(status.message);
|
||||||
|
// fs.unlinkSync(tmpFile);
|
||||||
|
// reject(status.message);
|
||||||
|
// } else {
|
||||||
|
// logger.info(`...Upload finish ! (status: ${res.statusCode})`);
|
||||||
|
// status.status = "idle";
|
||||||
|
// status.progress = -1;
|
||||||
|
// status.message = undefined;
|
||||||
|
// status.error_code = undefined;
|
||||||
|
// status.last_backup = DateTime.now().toFormat("dd MMM yyyy, HH:mm");
|
||||||
|
// statusTools.setStatus(status);
|
||||||
|
// cleanTempFolder();
|
||||||
|
// const autoCleanCloud =
|
||||||
|
// settingsTools.getSettings().auto_clean_backup;
|
||||||
|
// if (autoCleanCloud != null && autoCleanCloud === "true") {
|
||||||
|
// this.clean().catch();
|
||||||
|
// }
|
||||||
|
// const autoCleanlocal = settingsTools.getSettings().auto_clean_local;
|
||||||
|
// if (autoCleanlocal != null && autoCleanlocal === "true") {
|
||||||
|
// hassioApiTools.clean().catch();
|
||||||
|
// }
|
||||||
|
// resolve(undefined);
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// .on("error", (err) => {
|
||||||
|
// fs.unlinkSync(tmpFile);
|
||||||
|
// status.status = "error";
|
||||||
|
// status.error_code = 4;
|
||||||
|
// status.message = `Fail to upload snapshot to nextcloud (${err}) !`;
|
||||||
|
// statusTools.setStatus(status);
|
||||||
|
// logger.error(status.message);
|
||||||
|
// logger.error(err.stack);
|
||||||
|
// reject(status.message);
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// downloadFile(path: string) {
|
||||||
|
// return new Promise<string>((resolve, reject) => {
|
||||||
|
// if (this.client == null) {
|
||||||
|
// this.confIsValid()
|
||||||
|
// .then(() => {
|
||||||
|
// this._startDownload(path)
|
||||||
|
// .then((path) => resolve(path))
|
||||||
|
// .catch(() => reject());
|
||||||
|
// })
|
||||||
|
// .catch((err) => {
|
||||||
|
// reject(err);
|
||||||
|
// });
|
||||||
|
// } else
|
||||||
|
// this._startDownload(path)
|
||||||
|
// .then((path) => resolve(path))
|
||||||
|
// .catch(() => reject());
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// _startDownload(path: string) {
|
||||||
|
// return new Promise<string>((resolve, reject) => {
|
||||||
|
// const status = statusTools.getStatus();
|
||||||
|
// status.status = "download-b";
|
||||||
|
// status.progress = 0;
|
||||||
|
// status.message = undefined;
|
||||||
|
// status.error_code = undefined;
|
||||||
|
// statusTools.setStatus(status);
|
||||||
|
|
||||||
|
// logger.info("Downloading backup...");
|
||||||
|
// if (!fs.existsSync("./temp/")) fs.mkdirSync("./temp/");
|
||||||
|
// const tmpFile = `./temp/restore_${DateTime.now().toFormat(
|
||||||
|
// "MMM-dd-yyyy_HH_mm"
|
||||||
|
// )}.tar`;
|
||||||
|
// const stream = fs.createWriteStream(tmpFile);
|
||||||
|
// const conf = this.getConf();
|
||||||
|
// const options = {
|
||||||
|
// headers: {
|
||||||
|
// authorization:
|
||||||
|
// "Basic " +
|
||||||
|
// Buffer.from(this.username + ":" + this.password).toString("base64"),
|
||||||
|
// },
|
||||||
|
// https: undefined as any | undefined,
|
||||||
|
// };
|
||||||
|
// if (conf?.ssl) {
|
||||||
|
// options["https"] = { rejectUnauthorized: !conf?.self_signed };
|
||||||
|
// }
|
||||||
|
// logger.debug(
|
||||||
|
// `...URI: ${encodeURI(
|
||||||
|
// this.baseUrl?.replace(this.host as string, "host.hiden") + path
|
||||||
|
// )}`
|
||||||
|
// );
|
||||||
|
// if (conf?.ssl)
|
||||||
|
// logger.debug(
|
||||||
|
// `...rejectUnauthorized: ${options.https?.rejectUnauthorized}`
|
||||||
|
// );
|
||||||
|
// pipeline(
|
||||||
|
// got.stream
|
||||||
|
// .get(encodeURI(this.baseUrl + path), options)
|
||||||
|
// .on("downloadProgress", (e) => {
|
||||||
|
// const percent = Math.round(e.percent * 100) / 100;
|
||||||
|
// if (status.progress !== percent) {
|
||||||
|
// status.progress = percent;
|
||||||
|
// statusTools.setStatus(status);
|
||||||
|
// }
|
||||||
|
// }),
|
||||||
|
// stream
|
||||||
|
// )
|
||||||
|
// .then((res) => {
|
||||||
|
// logger.info("Download success !");
|
||||||
|
// status.progress = 1;
|
||||||
|
// statusTools.setStatus(status);
|
||||||
|
// logger.debug(
|
||||||
|
// "Backup dl size : " + fs.statSync(tmpFile).size / 1024 / 1024
|
||||||
|
// );
|
||||||
|
// resolve(tmpFile);
|
||||||
|
// })
|
||||||
|
// .catch((err) => {
|
||||||
|
// if (fs.existsSync(tmpFile)) fs.unlinkSync(tmpFile);
|
||||||
|
// status.status = "error";
|
||||||
|
// status.message =
|
||||||
|
// "Fail to download Hassio snapshot (" + err.message + ")";
|
||||||
|
// status.error_code = 7;
|
||||||
|
// statusTools.setStatus(status);
|
||||||
|
// logger.error(status.message);
|
||||||
|
// logger.error(err.stack);
|
||||||
|
// reject(err.message);
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// getFolderContent(path: string) {
|
||||||
|
// return new Promise((resolve, reject) => {
|
||||||
|
// if (this.client == null) {
|
||||||
|
// reject();
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// this.client
|
||||||
|
// .getDirectoryContents(path)
|
||||||
|
// .then((contents) => resolve(contents))
|
||||||
|
// .catch((error) => reject(error));
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// clean() {
|
||||||
|
// let limit = settingsTools.getSettings().auto_clean_backup_keep;
|
||||||
|
// if (limit == null) limit = 5;
|
||||||
|
// return new Promise((resolve, reject) => {
|
||||||
|
// this.getFolderContent(this.getConf()?.back_dir + pathTools.auto)
|
||||||
|
// .then(async (contents: any) => {
|
||||||
|
// if (contents.length < limit) {
|
||||||
|
// resolve(undefined);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// contents.sort((a: any, b: any) => {
|
||||||
|
// return a.date < b.date ? 1 : -1;
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const toDel = contents.slice(limit);
|
||||||
|
// for (const i in toDel) {
|
||||||
|
// await this.client?.deleteFile(toDel[i].filename);
|
||||||
|
// }
|
||||||
|
// logger.info("Cloud clean done.");
|
||||||
|
// resolve(undefined);
|
||||||
|
// })
|
||||||
|
// .catch((error) => {
|
||||||
|
// const status = statusTools.getStatus();
|
||||||
|
// status.status = "error";
|
||||||
|
// status.error_code = 6;
|
||||||
|
// status.message = "Fail to clean Nexcloud (" + error + ") !";
|
||||||
|
// statusTools.setStatus(status);
|
||||||
|
// logger.error(status.message);
|
||||||
|
// reject(status.message);
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// function cleanTempFolder() {
|
||||||
|
// fs.readdir("./temp/", (err, files) => {
|
||||||
|
// if (err) throw err;
|
||||||
|
|
||||||
|
// for (const file of files) {
|
||||||
|
// fs.unlink(path.join("./temp/", file), (err) => {
|
||||||
|
// if (err) throw err;
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const INSTANCE = new WebdavTools();
|
||||||
|
// export default INSTANCE;
|
@ -1,149 +1,149 @@
|
|||||||
import { CronJob } from "cron";
|
// import { CronJob } from "cron";
|
||||||
import * as settingsTools from "./settingsTools.js";
|
// import * as settingsTools from "./settingsTools.js";
|
||||||
import * as hassioApiTools from "./hassioApiTools.js";
|
// import * as hassioApiTools from "../services/haOsService.js";
|
||||||
import * as statusTools from "./status.js";
|
// import * as statusTools from "./status.js";
|
||||||
import * as pathTools from "./pathTools.js";
|
// import * as pathTools from "./pathTools.js";
|
||||||
import webdav from "../services/webdavService.js";
|
// // import webdav from "../services/webdavService.js";
|
||||||
|
|
||||||
import logger from "../config/winston.js";
|
// import logger from "../config/winston.js";
|
||||||
|
|
||||||
class CronContainer {
|
// class CronContainer {
|
||||||
cronJob: CronJob | undefined;
|
// cronJob: CronJob | undefined;
|
||||||
cronClean: CronJob | undefined;
|
// cronClean: CronJob | undefined;
|
||||||
|
|
||||||
init() {
|
// init() {
|
||||||
const settings = settingsTools.getSettings();
|
// const settings = settingsTools.getSettings();
|
||||||
let cronStr = "";
|
// let cronStr = "";
|
||||||
if (!this.cronClean) {
|
// if (!this.cronClean) {
|
||||||
logger.info("Starting auto clean cron...");
|
// logger.info("Starting auto clean cron...");
|
||||||
this.cronClean = new CronJob(
|
// this.cronClean = new CronJob(
|
||||||
"0 1 * * *",
|
// "0 1 * * *",
|
||||||
this._clean,
|
// this._clean,
|
||||||
null,
|
// null,
|
||||||
false,
|
// false,
|
||||||
Intl.DateTimeFormat().resolvedOptions().timeZone
|
// Intl.DateTimeFormat().resolvedOptions().timeZone
|
||||||
);
|
// );
|
||||||
this.cronClean.start();
|
// this.cronClean.start();
|
||||||
}
|
// }
|
||||||
if (this.cronJob) {
|
// if (this.cronJob) {
|
||||||
logger.info("Stopping Cron...");
|
// logger.info("Stopping Cron...");
|
||||||
this.cronJob.stop();
|
// this.cronJob.stop();
|
||||||
this.cronJob = undefined;
|
// this.cronJob = undefined;
|
||||||
}
|
// }
|
||||||
if (!settingsTools.check_cron(settingsTools.getSettings())) {
|
// if (!settingsTools.check_cron(settingsTools.getSettings())) {
|
||||||
logger.warn("No Cron settings available.");
|
// logger.warn("No Cron settings available.");
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
switch (settings.cron_base) {
|
// switch (settings.cron_base) {
|
||||||
case "0":
|
// case "0":
|
||||||
logger.warn("No Cron settings available.");
|
// logger.warn("No Cron settings available.");
|
||||||
return;
|
// return;
|
||||||
case "1": {
|
// case "1": {
|
||||||
const splited = settings.cron_hour.split(":");
|
// const splited = settings.cron_hour.split(":");
|
||||||
cronStr = "" + splited[1] + " " + splited[0] + " * * *";
|
// cronStr = "" + splited[1] + " " + splited[0] + " * * *";
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
|
|
||||||
case "2": {
|
// case "2": {
|
||||||
const splited = settings.cron_hour.split(":");
|
// const splited = settings.cron_hour.split(":");
|
||||||
cronStr =
|
// cronStr =
|
||||||
"" + splited[1] + " " + splited[0] + " * * " + settings.cron_weekday;
|
// "" + splited[1] + " " + splited[0] + " * * " + settings.cron_weekday;
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
|
|
||||||
case "3": {
|
// case "3": {
|
||||||
const splited = settings.cron_hour.split(":");
|
// const splited = settings.cron_hour.split(":");
|
||||||
cronStr =
|
// cronStr =
|
||||||
"" +
|
// "" +
|
||||||
splited[1] +
|
// splited[1] +
|
||||||
" " +
|
// " " +
|
||||||
splited[0] +
|
// splited[0] +
|
||||||
" " +
|
// " " +
|
||||||
settings.cron_month_day +
|
// settings.cron_month_day +
|
||||||
" * *";
|
// " * *";
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
case "4": {
|
// case "4": {
|
||||||
cronStr = settings.cron_custom;
|
// cronStr = settings.cron_custom;
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
logger.info("Starting Cron...");
|
// logger.info("Starting Cron...");
|
||||||
this.cronJob = new CronJob(
|
// this.cronJob = new CronJob(
|
||||||
cronStr,
|
// cronStr,
|
||||||
this._createBackup,
|
// this._createBackup,
|
||||||
null,
|
// null,
|
||||||
false,
|
// false,
|
||||||
Intl.DateTimeFormat().resolvedOptions().timeZone
|
// Intl.DateTimeFormat().resolvedOptions().timeZone
|
||||||
);
|
// );
|
||||||
this.cronJob.start();
|
// this.cronJob.start();
|
||||||
this.updateNextDate();
|
// this.updateNextDate();
|
||||||
}
|
// }
|
||||||
|
|
||||||
updateNextDate() {
|
// updateNextDate() {
|
||||||
let date;
|
// let date;
|
||||||
if (this.cronJob) {
|
// if (this.cronJob) {
|
||||||
date = this.cronJob
|
// date = this.cronJob
|
||||||
.nextDate()
|
// .nextDate()
|
||||||
.setLocale("en")
|
// .setLocale("en")
|
||||||
.toFormat("dd MMM yyyy, HH:mm");
|
// .toFormat("dd MMM yyyy, HH:mm");
|
||||||
}
|
// }
|
||||||
const status = statusTools.getStatus();
|
// const status = statusTools.getStatus();
|
||||||
status.next_backup = date;
|
// status.next_backup = date;
|
||||||
statusTools.setStatus(status);
|
// statusTools.setStatus(status);
|
||||||
}
|
// }
|
||||||
|
|
||||||
_createBackup() {
|
// _createBackup() {
|
||||||
logger.debug("Cron triggered !");
|
// logger.debug("Cron triggered !");
|
||||||
const status = statusTools.getStatus();
|
// const status = statusTools.getStatus();
|
||||||
if (
|
// if (
|
||||||
status.status === "creating" ||
|
// status.status === "creating" ||
|
||||||
status.status === "upload" ||
|
// status.status === "upload" ||
|
||||||
status.status === "download" ||
|
// status.status === "download" ||
|
||||||
status.status === "stopping" ||
|
// status.status === "stopping" ||
|
||||||
status.status === "starting"
|
// status.status === "starting"
|
||||||
)
|
// )
|
||||||
return;
|
// return;
|
||||||
hassioApiTools
|
// hassioApiTools
|
||||||
.stopAddons()
|
// .stopAddons()
|
||||||
.then(() => {
|
// .then(() => {
|
||||||
hassioApiTools.getVersion().then((version) => {
|
// hassioApiTools.getVersion().then((version) => {
|
||||||
const name = settingsTools.getFormatedName(false, version);
|
// const name = settingsTools.getFormatedName(false, version);
|
||||||
hassioApiTools.createNewBackup(name).then((id) => {
|
// hassioApiTools.createNewBackup(name).then((id) => {
|
||||||
hassioApiTools.downloadSnapshot(id).then(() => {
|
// hassioApiTools.downloadSnapshot(id).then(() => {
|
||||||
webdav
|
// webdav
|
||||||
.uploadFile(
|
// .uploadFile(
|
||||||
id,
|
// id,
|
||||||
webdav.getConf()?.back_dir + pathTools.auto + name + ".tar"
|
// webdav.getConf()?.back_dir + pathTools.auto + name + ".tar"
|
||||||
)
|
// )
|
||||||
.then(() => {
|
// .then(() => {
|
||||||
hassioApiTools.startAddons().catch(() => {
|
// hassioApiTools.startAddons().catch(() => {
|
||||||
// Skip
|
// // Skip
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
})
|
// })
|
||||||
.catch(() => {
|
// .catch(() => {
|
||||||
hassioApiTools.startAddons().catch(() => {
|
// hassioApiTools.startAddons().catch(() => {
|
||||||
// Skip
|
// // Skip
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
_clean() {
|
// _clean() {
|
||||||
const autoCleanlocal = settingsTools.getSettings().auto_clean_local;
|
// const autoCleanlocal = settingsTools.getSettings().auto_clean_local;
|
||||||
if (autoCleanlocal && autoCleanlocal == "true") {
|
// if (autoCleanlocal && autoCleanlocal == "true") {
|
||||||
hassioApiTools.clean().catch();
|
// hassioApiTools.clean().catch();
|
||||||
}
|
// }
|
||||||
const autoCleanCloud = settingsTools.getSettings().auto_clean_backup;
|
// const autoCleanCloud = settingsTools.getSettings().auto_clean_backup;
|
||||||
if (autoCleanCloud && autoCleanCloud == "true") {
|
// if (autoCleanCloud && autoCleanCloud == "true") {
|
||||||
webdav.clean().catch();
|
// webdav.clean().catch();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
const INSTANCE = new CronContainer();
|
// const INSTANCE = new CronContainer();
|
||||||
export default INSTANCE;
|
// export default INSTANCE;
|
||||||
|
@ -6,11 +6,12 @@ const maxMessageLength = 255;
|
|||||||
class MessageManager {
|
class MessageManager {
|
||||||
private messages: Message[] = [];
|
private messages: Message[] = [];
|
||||||
|
|
||||||
public addMessage(type: MessageType, message: string, detail?: string) {
|
public addMessage(type: MessageType, message: string, detail?: string, isImportant = false) {
|
||||||
this.messages.push({
|
this.messages.push({
|
||||||
message: message,
|
message: message,
|
||||||
type: type,
|
type: type,
|
||||||
time: DateTime.now(),
|
time: DateTime.now(),
|
||||||
|
viewed: !isImportant,
|
||||||
detail: detail
|
detail: detail
|
||||||
});
|
});
|
||||||
if (this.messages.length > maxMessageLength) {
|
if (this.messages.length > maxMessageLength) {
|
||||||
@ -19,7 +20,7 @@ class MessageManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public error(message: string, detail?: string) {
|
public error(message: string, detail?: string) {
|
||||||
this.addMessage(MessageType.ERROR, message, detail);
|
this.addMessage(MessageType.ERROR, message, detail, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public warn(message: string, detail?: string) {
|
public warn(message: string, detail?: string) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import * as hassioApiTools from "./hassioApiTools.js";
|
import { publish_state } from "../services/homeAssistantService.js";
|
||||||
import logger from "../config/winston.js"
|
import logger from "../config/winston.js"
|
||||||
import { type Status } from "../types/status.js";
|
import { type Status } from "../types/status.js";
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ export function setStatus(new_state: Status) {
|
|||||||
const old_state_str = JSON.stringify(status);
|
const old_state_str = JSON.stringify(status);
|
||||||
if(old_state_str !== JSON.stringify(new_state)){
|
if(old_state_str !== JSON.stringify(new_state)){
|
||||||
status = new_state;
|
status = new_state;
|
||||||
hassioApiTools.publish_state(status);
|
publish_state(status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,480 +0,0 @@
|
|||||||
import fs from "fs";
|
|
||||||
import got from "got";
|
|
||||||
import https from "https";
|
|
||||||
import path from "path";
|
|
||||||
import stream from "stream";
|
|
||||||
import { promisify } from "util";
|
|
||||||
import { createClient, WebDAVClient } from "webdav";
|
|
||||||
|
|
||||||
import { DateTime } from "luxon";
|
|
||||||
import logger from "../config/winston.js";
|
|
||||||
import { WebdavSettings } from "../types/settings.js";
|
|
||||||
import * as hassioApiTools from "./hassioApiTools.js";
|
|
||||||
import * as pathTools from "./pathTools.js";
|
|
||||||
import * as settingsTools from "./settingsTools.js";
|
|
||||||
import * as statusTools from "./status.js";
|
|
||||||
|
|
||||||
const endpoint = "/remote.php/webdav";
|
|
||||||
const configPath = "/data/webdav_conf.json";
|
|
||||||
|
|
||||||
const pipeline = promisify(stream.pipeline);
|
|
||||||
|
|
||||||
class WebdavTools {
|
|
||||||
host: string | undefined;
|
|
||||||
client: WebDAVClient | undefined;
|
|
||||||
baseUrl: string | undefined;
|
|
||||||
username: string | undefined;
|
|
||||||
password: string | undefined;
|
|
||||||
|
|
||||||
init(
|
|
||||||
ssl: boolean,
|
|
||||||
host: string,
|
|
||||||
username: string,
|
|
||||||
password: string,
|
|
||||||
accept_selfsigned_cert: boolean
|
|
||||||
) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
this.host = host;
|
|
||||||
const status = statusTools.getStatus();
|
|
||||||
logger.info("Initializing and checking webdav client...");
|
|
||||||
this.baseUrl = (ssl ? "https" : "http") + "://" + host + endpoint;
|
|
||||||
this.username = username;
|
|
||||||
this.password = password;
|
|
||||||
const agent_option = ssl
|
|
||||||
? { rejectUnauthorized: !accept_selfsigned_cert }
|
|
||||||
: {};
|
|
||||||
try {
|
|
||||||
this.client = createClient(this.baseUrl, {
|
|
||||||
username: username,
|
|
||||||
password: password,
|
|
||||||
httpsAgent: new https.Agent(agent_option),
|
|
||||||
});
|
|
||||||
|
|
||||||
this.client
|
|
||||||
.getDirectoryContents("/")
|
|
||||||
.then(() => {
|
|
||||||
if (status.error_code === 3) {
|
|
||||||
status.status = "idle";
|
|
||||||
status.message = undefined;
|
|
||||||
status.error_code = undefined;
|
|
||||||
statusTools.setStatus(status);
|
|
||||||
}
|
|
||||||
logger.debug("Nextcloud connection: \x1b[32mSuccess !\x1b[0m");
|
|
||||||
this.initFolder().then(() => {
|
|
||||||
resolve(undefined);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
this.__cant_connect_status(error);
|
|
||||||
this.client = undefined;
|
|
||||||
reject("Can't connect to Nextcloud (" + error + ") !");
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
this.__cant_connect_status(err);
|
|
||||||
this.client = undefined;
|
|
||||||
reject("Can't connect to Nextcloud (" + err + ") !");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
__cant_connect_status(err: any) {
|
|
||||||
statusTools.setError(`Can't connect to Nextcloud (${err})`, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
async __createRoot() {
|
|
||||||
const conf = this.getConf();
|
|
||||||
if (this.client && conf) {
|
|
||||||
const root_splited = conf.back_dir.split("/").splice(1);
|
|
||||||
let path = "/";
|
|
||||||
for (const elem of root_splited) {
|
|
||||||
if (elem !== "") {
|
|
||||||
path = path + elem + "/";
|
|
||||||
try {
|
|
||||||
await this.client.createDirectory(path);
|
|
||||||
logger.debug(`Path ${path} created.`);
|
|
||||||
} catch (error) {
|
|
||||||
if ((error as any).status === 405)
|
|
||||||
logger.debug(`Path ${path} already exist.`);
|
|
||||||
else logger.error(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
initFolder() {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
this.__createRoot()
|
|
||||||
.catch((err) => {
|
|
||||||
logger.error(err);
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
if (!this.client) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.client
|
|
||||||
.createDirectory(this.getConf()?.back_dir + pathTools.auto)
|
|
||||||
.catch(() => {
|
|
||||||
// Ignore
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
if (!this.client) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.client
|
|
||||||
.createDirectory(this.getConf()?.back_dir + pathTools.manual)
|
|
||||||
.catch(() => {
|
|
||||||
// Ignore
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
resolve(undefined);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if theh webdav config is valid, if yes, start init of webdav client
|
|
||||||
*/
|
|
||||||
confIsValid() {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const status = statusTools.getStatus();
|
|
||||||
const conf = this.getConf();
|
|
||||||
if (conf != undefined) {
|
|
||||||
if (
|
|
||||||
conf.ssl != undefined &&
|
|
||||||
conf.host != undefined &&
|
|
||||||
conf.username != undefined &&
|
|
||||||
conf.password != undefined
|
|
||||||
) {
|
|
||||||
if (status.error_code === 2) {
|
|
||||||
status.status = "idle";
|
|
||||||
status.message = undefined;
|
|
||||||
status.error_code = undefined;
|
|
||||||
statusTools.setStatus(status);
|
|
||||||
}
|
|
||||||
// Check if self_signed option exist
|
|
||||||
if (conf.self_signed == undefined) {
|
|
||||||
conf.self_signed = false;
|
|
||||||
this.setConf(conf);
|
|
||||||
}
|
|
||||||
this.init(
|
|
||||||
conf.ssl,
|
|
||||||
conf.host,
|
|
||||||
conf.username,
|
|
||||||
conf.password,
|
|
||||||
conf.self_signed
|
|
||||||
)
|
|
||||||
.then(() => {
|
|
||||||
resolve(undefined);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
status.status = "error";
|
|
||||||
status.error_code = 2;
|
|
||||||
status.message = "Nextcloud config invalid !";
|
|
||||||
statusTools.setStatus(status);
|
|
||||||
logger.error(status.message);
|
|
||||||
reject("Nextcloud config invalid !");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (conf.back_dir == null || conf.back_dir === "") {
|
|
||||||
logger.info("Backup dir is null, initializing it.");
|
|
||||||
conf.back_dir = pathTools.default_root;
|
|
||||||
this.setConf(conf);
|
|
||||||
} else {
|
|
||||||
if (!conf.back_dir.startsWith("/")) {
|
|
||||||
logger.warn("Backup dir not starting with '/', fixing this...");
|
|
||||||
conf.back_dir = `/${conf.back_dir}`;
|
|
||||||
this.setConf(conf);
|
|
||||||
}
|
|
||||||
if (!conf.back_dir.endsWith("/")) {
|
|
||||||
logger.warn("Backup dir not ending with '/', fixing this...");
|
|
||||||
conf.back_dir = `${conf.back_dir}/`;
|
|
||||||
this.setConf(conf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
status.status = "error";
|
|
||||||
status.error_code = 2;
|
|
||||||
status.message = "Nextcloud config not found !";
|
|
||||||
statusTools.setStatus(status);
|
|
||||||
logger.error(status.message);
|
|
||||||
reject("Nextcloud config not found !");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getConf(): WebdavSettings | undefined {
|
|
||||||
if (fs.existsSync(configPath)) {
|
|
||||||
return JSON.parse(fs.readFileSync(configPath).toString());
|
|
||||||
} else return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
setConf(conf: WebdavSettings) {
|
|
||||||
fs.writeFileSync(configPath, JSON.stringify(conf));
|
|
||||||
}
|
|
||||||
|
|
||||||
uploadFile(id: string, path: string) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
if (this.client == null) {
|
|
||||||
this.confIsValid()
|
|
||||||
.then(() => {
|
|
||||||
this._startUpload(id, path)
|
|
||||||
.then(() => resolve(undefined))
|
|
||||||
.catch((err) => reject(err));
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
} else
|
|
||||||
this._startUpload(id, path)
|
|
||||||
.then(() => resolve(undefined))
|
|
||||||
.catch((err) => reject(err));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_startUpload(id: string, path: string) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const status = statusTools.getStatus();
|
|
||||||
status.status = "upload";
|
|
||||||
status.progress = 0;
|
|
||||||
status.message = undefined;
|
|
||||||
status.error_code = undefined;
|
|
||||||
statusTools.setStatus(status);
|
|
||||||
logger.info("Uploading snap...");
|
|
||||||
const tmpFile = `./temp/${id}.tar`;
|
|
||||||
const stats = fs.statSync(tmpFile);
|
|
||||||
const stream = fs.createReadStream(tmpFile);
|
|
||||||
const conf = this.getConf();
|
|
||||||
const options = {
|
|
||||||
body: stream,
|
|
||||||
headers: {
|
|
||||||
authorization:
|
|
||||||
"Basic " +
|
|
||||||
Buffer.from(this.username + ":" + this.password).toString("base64"),
|
|
||||||
"content-length": String(stats.size),
|
|
||||||
},
|
|
||||||
https: undefined as any | undefined,
|
|
||||||
};
|
|
||||||
if (conf?.ssl) {
|
|
||||||
options["https"] = { rejectUnauthorized: !conf.self_signed };
|
|
||||||
}
|
|
||||||
logger.debug(
|
|
||||||
`...URI: ${encodeURI(
|
|
||||||
this.baseUrl?.replace(this.host as string, "host.hiden") + path
|
|
||||||
)}`
|
|
||||||
);
|
|
||||||
if (conf?.ssl)
|
|
||||||
logger.debug(
|
|
||||||
`...rejectUnauthorized: ${options.https?.rejectUnauthorized}`
|
|
||||||
);
|
|
||||||
|
|
||||||
got.stream
|
|
||||||
.put(encodeURI(this.baseUrl + path), options)
|
|
||||||
.on("uploadProgress", (e) => {
|
|
||||||
const percent = e.percent;
|
|
||||||
if (status.progress !== percent) {
|
|
||||||
status.progress = percent;
|
|
||||||
statusTools.setStatus(status);
|
|
||||||
}
|
|
||||||
if (percent >= 1) {
|
|
||||||
logger.info("Upload done...");
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.on("response", (res) => {
|
|
||||||
if (res.statusCode !== 201 && res.statusCode !== 204) {
|
|
||||||
status.status = "error";
|
|
||||||
status.error_code = 4;
|
|
||||||
status.message = `Fail to upload snapshot to nextcloud (Status code: ${res.statusCode})!`;
|
|
||||||
statusTools.setStatus(status);
|
|
||||||
logger.error(status.message);
|
|
||||||
fs.unlinkSync(tmpFile);
|
|
||||||
reject(status.message);
|
|
||||||
} else {
|
|
||||||
logger.info(`...Upload finish ! (status: ${res.statusCode})`);
|
|
||||||
status.status = "idle";
|
|
||||||
status.progress = -1;
|
|
||||||
status.message = undefined;
|
|
||||||
status.error_code = undefined;
|
|
||||||
status.last_backup = DateTime.now().toFormat("dd MMM yyyy, HH:mm");
|
|
||||||
statusTools.setStatus(status);
|
|
||||||
cleanTempFolder();
|
|
||||||
const autoCleanCloud =
|
|
||||||
settingsTools.getSettings().auto_clean_backup;
|
|
||||||
if (autoCleanCloud != null && autoCleanCloud === "true") {
|
|
||||||
this.clean().catch();
|
|
||||||
}
|
|
||||||
const autoCleanlocal = settingsTools.getSettings().auto_clean_local;
|
|
||||||
if (autoCleanlocal != null && autoCleanlocal === "true") {
|
|
||||||
hassioApiTools.clean().catch();
|
|
||||||
}
|
|
||||||
resolve(undefined);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.on("error", (err) => {
|
|
||||||
fs.unlinkSync(tmpFile);
|
|
||||||
status.status = "error";
|
|
||||||
status.error_code = 4;
|
|
||||||
status.message = `Fail to upload snapshot to nextcloud (${err}) !`;
|
|
||||||
statusTools.setStatus(status);
|
|
||||||
logger.error(status.message);
|
|
||||||
logger.error(err.stack);
|
|
||||||
reject(status.message);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
downloadFile(path: string) {
|
|
||||||
return new Promise<string>((resolve, reject) => {
|
|
||||||
if (this.client == null) {
|
|
||||||
this.confIsValid()
|
|
||||||
.then(() => {
|
|
||||||
this._startDownload(path)
|
|
||||||
.then((path) => resolve(path))
|
|
||||||
.catch(() => reject());
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
} else
|
|
||||||
this._startDownload(path)
|
|
||||||
.then((path) => resolve(path))
|
|
||||||
.catch(() => reject());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_startDownload(path: string) {
|
|
||||||
return new Promise<string>((resolve, reject) => {
|
|
||||||
const status = statusTools.getStatus();
|
|
||||||
status.status = "download-b";
|
|
||||||
status.progress = 0;
|
|
||||||
status.message = undefined;
|
|
||||||
status.error_code = undefined;
|
|
||||||
statusTools.setStatus(status);
|
|
||||||
|
|
||||||
logger.info("Downloading backup...");
|
|
||||||
if (!fs.existsSync("./temp/")) fs.mkdirSync("./temp/");
|
|
||||||
const tmpFile = `./temp/restore_${DateTime.now().toFormat(
|
|
||||||
"MMM-dd-yyyy_HH_mm"
|
|
||||||
)}.tar`;
|
|
||||||
const stream = fs.createWriteStream(tmpFile);
|
|
||||||
const conf = this.getConf();
|
|
||||||
const options = {
|
|
||||||
headers: {
|
|
||||||
authorization:
|
|
||||||
"Basic " +
|
|
||||||
Buffer.from(this.username + ":" + this.password).toString("base64"),
|
|
||||||
},
|
|
||||||
https: undefined as any | undefined,
|
|
||||||
};
|
|
||||||
if (conf?.ssl) {
|
|
||||||
options["https"] = { rejectUnauthorized: !conf?.self_signed };
|
|
||||||
}
|
|
||||||
logger.debug(
|
|
||||||
`...URI: ${encodeURI(
|
|
||||||
this.baseUrl?.replace(this.host as string, "host.hiden") + path
|
|
||||||
)}`
|
|
||||||
);
|
|
||||||
if (conf?.ssl)
|
|
||||||
logger.debug(
|
|
||||||
`...rejectUnauthorized: ${options.https?.rejectUnauthorized}`
|
|
||||||
);
|
|
||||||
pipeline(
|
|
||||||
got.stream
|
|
||||||
.get(encodeURI(this.baseUrl + path), options)
|
|
||||||
.on("downloadProgress", (e) => {
|
|
||||||
const percent = Math.round(e.percent * 100) / 100;
|
|
||||||
if (status.progress !== percent) {
|
|
||||||
status.progress = percent;
|
|
||||||
statusTools.setStatus(status);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
stream
|
|
||||||
)
|
|
||||||
.then((res) => {
|
|
||||||
logger.info("Download success !");
|
|
||||||
status.progress = 1;
|
|
||||||
statusTools.setStatus(status);
|
|
||||||
logger.debug(
|
|
||||||
"Backup dl size : " + fs.statSync(tmpFile).size / 1024 / 1024
|
|
||||||
);
|
|
||||||
resolve(tmpFile);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
if (fs.existsSync(tmpFile)) fs.unlinkSync(tmpFile);
|
|
||||||
status.status = "error";
|
|
||||||
status.message =
|
|
||||||
"Fail to download Hassio snapshot (" + err.message + ")";
|
|
||||||
status.error_code = 7;
|
|
||||||
statusTools.setStatus(status);
|
|
||||||
logger.error(status.message);
|
|
||||||
logger.error(err.stack);
|
|
||||||
reject(err.message);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getFolderContent(path: string) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
if (this.client == null) {
|
|
||||||
reject();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.client
|
|
||||||
.getDirectoryContents(path)
|
|
||||||
.then((contents) => resolve(contents))
|
|
||||||
.catch((error) => reject(error));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
clean() {
|
|
||||||
let limit = settingsTools.getSettings().auto_clean_backup_keep;
|
|
||||||
if (limit == null) limit = 5;
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
this.getFolderContent(this.getConf()?.back_dir + pathTools.auto)
|
|
||||||
.then(async (contents: any) => {
|
|
||||||
if (contents.length < limit) {
|
|
||||||
resolve(undefined);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
contents.sort((a: any, b: any) => {
|
|
||||||
return a.date < b.date ? 1 : -1;
|
|
||||||
});
|
|
||||||
|
|
||||||
const toDel = contents.slice(limit);
|
|
||||||
for (const i in toDel) {
|
|
||||||
await this.client?.deleteFile(toDel[i].filename);
|
|
||||||
}
|
|
||||||
logger.info("Cloud clean done.");
|
|
||||||
resolve(undefined);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
const status = statusTools.getStatus();
|
|
||||||
status.status = "error";
|
|
||||||
status.error_code = 6;
|
|
||||||
status.message = "Fail to clean Nexcloud (" + error + ") !";
|
|
||||||
statusTools.setStatus(status);
|
|
||||||
logger.error(status.message);
|
|
||||||
reject(status.message);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function cleanTempFolder() {
|
|
||||||
fs.readdir("./temp/", (err, files) => {
|
|
||||||
if (err) throw err;
|
|
||||||
|
|
||||||
for (const file of files) {
|
|
||||||
fs.unlink(path.join("./temp/", file), (err) => {
|
|
||||||
if (err) throw err;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const INSTANCE = new WebdavTools();
|
|
||||||
export default INSTANCE;
|
|
@ -1,10 +1,10 @@
|
|||||||
import { DateTime } from "luxon";
|
import { DateTime } from "luxon";
|
||||||
|
|
||||||
export enum MessageType {
|
export enum MessageType {
|
||||||
ERROR,
|
ERROR = "ERROR",
|
||||||
WARN,
|
WARN = "WARN",
|
||||||
INFO,
|
INFO = "INFO",
|
||||||
SUCCESS
|
SUCCESS = "SUCCESS"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -12,5 +12,6 @@ export interface Message {
|
|||||||
time: DateTime;
|
time: DateTime;
|
||||||
type: MessageType;
|
type: MessageType;
|
||||||
message: string;
|
message: string;
|
||||||
|
viewed: boolean;
|
||||||
detail?: string;
|
detail?: string;
|
||||||
}
|
}
|
@ -1,3 +1,9 @@
|
|||||||
|
|
||||||
|
export interface SupervisorResponse<T> {
|
||||||
|
result: string;
|
||||||
|
data: T;
|
||||||
|
}
|
||||||
|
|
||||||
export interface CoreInfoBody {
|
export interface CoreInfoBody {
|
||||||
version: string;
|
version: string;
|
||||||
version_latest: string;
|
version_latest: string;
|
||||||
@ -15,6 +21,11 @@ export interface CoreInfoBody {
|
|||||||
audio_output: string;
|
audio_output: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AddonData {
|
||||||
|
addons: AddonModel[];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export interface AddonModel {
|
export interface AddonModel {
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
@ -31,6 +42,11 @@ export interface AddonModel {
|
|||||||
state: string;
|
state: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BackupData {
|
||||||
|
backups: BackupModel[]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export interface BackupModel {
|
export interface BackupModel {
|
||||||
slug: string;
|
slug: string;
|
||||||
date: string;
|
date: string;
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
"module": "ES2022",
|
"module": "ES2022",
|
||||||
"moduleResolution": "NodeNext",
|
"moduleResolution": "NodeNext",
|
||||||
"target": "es6",
|
"target": "es6",
|
||||||
|
"sourceMap": true,
|
||||||
},
|
},
|
||||||
"include": ["src/**/*"]
|
"include": ["src/**/*"]
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user