Refactor folder structure
This commit is contained in:
34
systemd/recurring-tasks/homeassistant-backup/.gitignore
vendored
Normal file
34
systemd/recurring-tasks/homeassistant-backup/.gitignore
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
# dependencies (bun install)
|
||||
node_modules
|
||||
|
||||
# output
|
||||
out
|
||||
dist
|
||||
*.tgz
|
||||
|
||||
# code coverage
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# logs
|
||||
logs
|
||||
_.log
|
||||
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# caches
|
||||
.eslintcache
|
||||
.cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# IntelliJ based IDEs
|
||||
.idea
|
||||
|
||||
# Finder (MacOS) folder config
|
||||
.DS_Store
|
||||
15
systemd/recurring-tasks/homeassistant-backup/README.md
Normal file
15
systemd/recurring-tasks/homeassistant-backup/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# homeassistant-backup
|
||||
|
||||
To install dependencies:
|
||||
|
||||
```bash
|
||||
bun install
|
||||
```
|
||||
|
||||
To run:
|
||||
|
||||
```bash
|
||||
bun run index.ts
|
||||
```
|
||||
|
||||
This project was created using `bun init` in bun v1.2.15. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.
|
||||
57
systemd/recurring-tasks/homeassistant-backup/bun.lock
Normal file
57
systemd/recurring-tasks/homeassistant-backup/bun.lock
Normal file
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "homeassistant-backup",
|
||||
"dependencies": {
|
||||
"moment": "^2.30.1",
|
||||
"pino": "^9.7.0",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@types/bun": ["@types/bun@1.2.15", "", { "dependencies": { "bun-types": "1.2.15" } }, "sha512-U1ljPdBEphF0nw1MIk0hI7kPg7dFdPyM7EenHsp6W5loNHl7zqy6JQf/RKCgnUn2KDzUpkBwHPnEJEjII594bA=="],
|
||||
|
||||
"@types/node": ["@types/node@22.15.30", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-6Q7lr06bEHdlfplU6YRbgG1SFBdlsfNC4/lX+SkhiTs0cpJkOElmWls8PxDFv4yY/xKb8Y6SO0OmSX4wgqTZbA=="],
|
||||
|
||||
"atomic-sleep": ["atomic-sleep@1.0.0", "", {}, "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ=="],
|
||||
|
||||
"bun-types": ["bun-types@1.2.15", "", { "dependencies": { "@types/node": "*" } }, "sha512-NarRIaS+iOaQU1JPfyKhZm4AsUOrwUOqRNHY0XxI8GI8jYxiLXLcdjYMG9UKS+fwWasc1uw1htV9AX24dD+p4w=="],
|
||||
|
||||
"fast-redact": ["fast-redact@3.5.0", "", {}, "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A=="],
|
||||
|
||||
"moment": ["moment@2.30.1", "", {}, "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how=="],
|
||||
|
||||
"on-exit-leak-free": ["on-exit-leak-free@2.1.2", "", {}, "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA=="],
|
||||
|
||||
"pino": ["pino@9.7.0", "", { "dependencies": { "atomic-sleep": "^1.0.0", "fast-redact": "^3.1.1", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^2.0.0", "pino-std-serializers": "^7.0.0", "process-warning": "^5.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", "sonic-boom": "^4.0.1", "thread-stream": "^3.0.0" }, "bin": { "pino": "bin.js" } }, "sha512-vnMCM6xZTb1WDmLvtG2lE/2p+t9hDEIvTWJsu6FejkE62vB7gDhvzrpFR4Cw2to+9JNQxVnkAKVPA1KPB98vWg=="],
|
||||
|
||||
"pino-abstract-transport": ["pino-abstract-transport@2.0.0", "", { "dependencies": { "split2": "^4.0.0" } }, "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw=="],
|
||||
|
||||
"pino-std-serializers": ["pino-std-serializers@7.0.0", "", {}, "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA=="],
|
||||
|
||||
"process-warning": ["process-warning@5.0.0", "", {}, "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA=="],
|
||||
|
||||
"quick-format-unescaped": ["quick-format-unescaped@4.0.4", "", {}, "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="],
|
||||
|
||||
"real-require": ["real-require@0.2.0", "", {}, "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg=="],
|
||||
|
||||
"safe-stable-stringify": ["safe-stable-stringify@2.5.0", "", {}, "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA=="],
|
||||
|
||||
"sonic-boom": ["sonic-boom@4.2.0", "", { "dependencies": { "atomic-sleep": "^1.0.0" } }, "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww=="],
|
||||
|
||||
"split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="],
|
||||
|
||||
"thread-stream": ["thread-stream@3.1.0", "", { "dependencies": { "real-require": "^0.2.0" } }, "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A=="],
|
||||
|
||||
"typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
|
||||
|
||||
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
|
||||
}
|
||||
}
|
||||
BIN
systemd/recurring-tasks/homeassistant-backup/homeassistant-backup
Executable file
BIN
systemd/recurring-tasks/homeassistant-backup/homeassistant-backup
Executable file
Binary file not shown.
89
systemd/recurring-tasks/homeassistant-backup/index.ts
Normal file
89
systemd/recurring-tasks/homeassistant-backup/index.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import { $ } from "bun";
|
||||
import moment from "moment";
|
||||
import logger from "pino";
|
||||
import { parseArgs } from "util";
|
||||
|
||||
const { values } = parseArgs({
|
||||
args: Bun.argv,
|
||||
options: {
|
||||
mode: {
|
||||
type: "string",
|
||||
required: true,
|
||||
},
|
||||
sourceFolder: {
|
||||
type: "string",
|
||||
required: true,
|
||||
},
|
||||
targetFolder: {
|
||||
type: "string",
|
||||
required: true,
|
||||
},
|
||||
backupName: {
|
||||
type: "string",
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
strict: true,
|
||||
allowPositionals: true,
|
||||
});
|
||||
const log = logger();
|
||||
|
||||
switch (values.mode) {
|
||||
case "hot":
|
||||
await hotCopy(
|
||||
values.sourceFolder!,
|
||||
values.targetFolder!,
|
||||
values.backupName!,
|
||||
);
|
||||
break;
|
||||
case "cold":
|
||||
await coldCopy(
|
||||
values.sourceFolder!,
|
||||
values.targetFolder!,
|
||||
values.backupName!,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
// 3 cold copies are stored
|
||||
async function hotCopy(
|
||||
sourceFolder: string,
|
||||
targetFolder: string,
|
||||
backupName: string,
|
||||
) {
|
||||
const timestamp = moment().format("YYYY-MM-DD_HH-mm-ss"); // Return today's date time
|
||||
|
||||
// Make sure user has access to all folders
|
||||
await $`sudo chown -R willem:willem /etc/homeassistant`;
|
||||
|
||||
log.info("Creating new copy");
|
||||
await $`mkdir -p ${targetFolder}`;
|
||||
// Sync new copy
|
||||
await $`rsync -a ${sourceFolder} ${targetFolder}/backup_${backupName}_${timestamp}`;
|
||||
|
||||
log.info("Only keep last 3 copies");
|
||||
// Only keep last 3 copies
|
||||
await $`cd ${targetFolder} && ls | grep ${backupName} | sort -r | tail -n +4 | xargs rm -rf`;
|
||||
}
|
||||
|
||||
// Only 1 cold copy is retained
|
||||
async function coldCopy(
|
||||
sourceFolder: string,
|
||||
targetFolder: string,
|
||||
backupName: string,
|
||||
) {
|
||||
const timestamp = moment().format("YYYY-MM-DD_HH-mm-ss"); // Return today's date time
|
||||
|
||||
log.info("Creating new copy");
|
||||
|
||||
// find latest backup
|
||||
const sourceFolderToBackup =
|
||||
await $`cd ${sourceFolder} && ls | grep ${backupName} | sort | tail -n 1`.text().then(resp => resp.replace('\n',''));
|
||||
log.info(`Folder to back up: ${sourceFolderToBackup}`);
|
||||
await $`mkdir -p ${targetFolder}`;
|
||||
await $`zip -r ${targetFolder}/${backupName}_${timestamp}.zip ${sourceFolder}/${sourceFolderToBackup}`;
|
||||
|
||||
log.info("Only keep last copy");
|
||||
// Only keep last copy
|
||||
await $`cd ${targetFolder} && ls | grep ${backupName} | sort -r | tail -n +2 | xargs rm -rf`;
|
||||
}
|
||||
16
systemd/recurring-tasks/homeassistant-backup/package.json
Normal file
16
systemd/recurring-tasks/homeassistant-backup/package.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "homeassistant-backup",
|
||||
"module": "index.ts",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5"
|
||||
},
|
||||
"dependencies": {
|
||||
"moment": "^2.30.1",
|
||||
"pino": "^9.7.0"
|
||||
}
|
||||
}
|
||||
29
systemd/recurring-tasks/homeassistant-backup/tsconfig.json
Normal file
29
systemd/recurring-tasks/homeassistant-backup/tsconfig.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
// Environment setup & latest features
|
||||
"lib": ["ESNext"],
|
||||
"target": "ESNext",
|
||||
"module": "Preserve",
|
||||
"moduleDetection": "force",
|
||||
"jsx": "react-jsx",
|
||||
"allowJs": true,
|
||||
|
||||
// Bundler mode
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"noEmit": true,
|
||||
|
||||
// Best practices
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noImplicitOverride": true,
|
||||
|
||||
// Some stricter flags (disabled by default)
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noPropertyAccessFromIndexSignature": false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user