mirror of
https://github.com/Sebclem/hassio-nextcloud-backup.git
synced 2024-11-24 18:22:57 +01:00
Add cron
This commit is contained in:
parent
1f9d79d2ca
commit
5f3c59c572
@ -15,7 +15,7 @@
|
||||
"app-root-path": "3.1.0",
|
||||
"cookie-parser": "1.4.6",
|
||||
"cors": "^2.8.5",
|
||||
"cron": "3.1.6",
|
||||
"cron": "3.1.7",
|
||||
"debug": "4.3.4",
|
||||
"errorhandler": "^1.5.1",
|
||||
"express": "4.18.2",
|
||||
|
@ -15,8 +15,8 @@ dependencies:
|
||||
specifier: ^2.8.5
|
||||
version: 2.8.5
|
||||
cron:
|
||||
specifier: 3.1.6
|
||||
version: 3.1.6
|
||||
specifier: 3.1.7
|
||||
version: 3.1.7
|
||||
debug:
|
||||
specifier: 4.3.4
|
||||
version: 4.3.4(supports-color@5.5.0)
|
||||
@ -400,13 +400,8 @@ packages:
|
||||
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
||||
dev: true
|
||||
|
||||
/@types/luxon@3.3.8:
|
||||
resolution: {integrity: sha512-jYvz8UMLDgy3a5SkGJne8H7VA7zPV2Lwohjx0V8V31+SqAjNmurWMkk9cQhfvlcnXWudBpK9xPM1n4rljOcHYQ==}
|
||||
dev: false
|
||||
|
||||
/@types/luxon@3.4.2:
|
||||
resolution: {integrity: sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==}
|
||||
dev: true
|
||||
|
||||
/@types/mime@1.3.5:
|
||||
resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==}
|
||||
@ -928,10 +923,10 @@ packages:
|
||||
resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
|
||||
dev: true
|
||||
|
||||
/cron@3.1.6:
|
||||
resolution: {integrity: sha512-cvFiQCeVzsA+QPM6fhjBtlKGij7tLLISnTSvFxVdnFGLdz+ZdXN37kNe0i2gefmdD17XuZA6n2uPVwzl4FxW/w==}
|
||||
/cron@3.1.7:
|
||||
resolution: {integrity: sha512-tlBg7ARsAMQLzgwqVxy8AZl/qlTc5nibqYwtNGoCrd+cV+ugI+tvZC1oT/8dFH8W455YrywGykx/KMmAqOr7Jw==}
|
||||
dependencies:
|
||||
'@types/luxon': 3.3.8
|
||||
'@types/luxon': 3.4.2
|
||||
luxon: 3.4.4
|
||||
dev: false
|
||||
|
||||
|
@ -12,6 +12,8 @@ import {
|
||||
validateWebdavConfig,
|
||||
} from "./services/webdavConfigService.js";
|
||||
import messageManager from "./tools/messageManager.js";
|
||||
import { initCron } from "./services/cronService.js";
|
||||
import { getBackupConfig } from "./services/backupConfigService.js";
|
||||
|
||||
function postInit() {
|
||||
logger.info(`Log level: ${process.env.LOG_LEVEL}`);
|
||||
@ -39,34 +41,46 @@ function postInit() {
|
||||
);
|
||||
|
||||
const webdavConf = getWebdavConfig();
|
||||
validateWebdavConfig(webdavConf).then(
|
||||
() => {
|
||||
logger.info("Webdav config: " + kleur.green().bold("Go !"));
|
||||
checkWebdavLogin(webdavConf).then(
|
||||
() => {
|
||||
logger.info("Webdav : " + kleur.green().bold("Go !"));
|
||||
createBackupFolder(webdavConf).then(
|
||||
() => {
|
||||
logger.info("Webdav fodlers: " + kleur.green().bold("Go !"));
|
||||
},
|
||||
(reason) => {
|
||||
logger.error("Webdav folders: " + kleur.red().bold("FAIL !"));
|
||||
logger.error(reason);
|
||||
}
|
||||
);
|
||||
},
|
||||
(reason) => {
|
||||
logger.error("Webdav : " + kleur.red().bold("FAIL !"));
|
||||
logger.error(reason);
|
||||
}
|
||||
);
|
||||
},
|
||||
(reason: Error) => {
|
||||
logger.error("Webdav config: " + kleur.red().bold("FAIL !"));
|
||||
logger.error(reason);
|
||||
messageManager.error("Invalid webdav config", reason.message);
|
||||
}
|
||||
);
|
||||
validateWebdavConfig(webdavConf)
|
||||
.then(
|
||||
() => {
|
||||
logger.info("Webdav config: " + kleur.green().bold("Go !"));
|
||||
return checkWebdavLogin(webdavConf);
|
||||
},
|
||||
(reason: Error) => {
|
||||
logger.error("Webdav config: " + kleur.red().bold("FAIL !"));
|
||||
logger.error(reason);
|
||||
messageManager.error("Invalid webdav config", reason.message);
|
||||
}
|
||||
)
|
||||
.then(
|
||||
() => {
|
||||
logger.info("Webdav : " + kleur.green().bold("Go !"));
|
||||
return createBackupFolder(webdavConf);
|
||||
},
|
||||
(reason) => {
|
||||
logger.error("Webdav : " + kleur.red().bold("FAIL !"));
|
||||
logger.error(reason);
|
||||
}
|
||||
)
|
||||
.then(
|
||||
() => {
|
||||
logger.info("Webdav fodlers: " + kleur.green().bold("Go !"));
|
||||
return initCron(getBackupConfig());
|
||||
},
|
||||
(reason) => {
|
||||
logger.error("Webdav folders: " + kleur.red().bold("FAIL !"));
|
||||
logger.error(reason);
|
||||
}
|
||||
)
|
||||
.then(
|
||||
() => {
|
||||
logger.info("Cron: " + kleur.green().bold("Go !"));
|
||||
},
|
||||
(reason) => {
|
||||
logger.info("Cron: " + kleur.red().bold("FAIL !"));
|
||||
}
|
||||
);
|
||||
|
||||
// settingsTools.check(settingsTools.getSettings(), true);
|
||||
// cronTools.init();
|
||||
|
@ -22,13 +22,24 @@ configRouter.get("/backup", (req, res) => {
|
||||
|
||||
configRouter.put("/backup", (req, res) => {
|
||||
validateBackupConfig(req.body as BackupConfig)
|
||||
.then(() => {
|
||||
saveBackupConfig(req.body as BackupConfig);
|
||||
res.status(204).send();
|
||||
})
|
||||
.catch((error: ValidationError) => {
|
||||
res.status(400).json(error.details);
|
||||
});
|
||||
.then(
|
||||
() => {
|
||||
return saveBackupConfig(req.body as BackupConfig);
|
||||
},
|
||||
(error: ValidationError) => {
|
||||
res.status(400).json(error.details);
|
||||
}
|
||||
)
|
||||
.then(
|
||||
() => {
|
||||
res.status(204).send();
|
||||
},
|
||||
() => {
|
||||
res.status(400).json({
|
||||
message: "Fail to init cron, please check cron config",
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
configRouter.get("/webdav", (req, res) => {
|
||||
|
@ -8,6 +8,7 @@ import {
|
||||
import backupConfigValidation from "../types/services/backupConfigValidation.js";
|
||||
import { DateTime } from "luxon";
|
||||
import { WorkflowType } from "../types/services/orchecstrator.js";
|
||||
import { initCron } from "./cronService.js";
|
||||
|
||||
const backupConfigPath = "/data/backupConfigV2.json";
|
||||
|
||||
@ -20,13 +21,14 @@ export function validateBackupConfig(config: BackupConfig) {
|
||||
|
||||
export function saveBackupConfig(config: BackupConfig) {
|
||||
fs.writeFileSync(backupConfigPath, JSON.stringify(config, undefined, 2));
|
||||
return initCron(config);
|
||||
}
|
||||
|
||||
export function getBackupConfig(): BackupConfig {
|
||||
if (!fs.existsSync(backupConfigPath)) {
|
||||
logger.warn("Config file not found, creating default one !");
|
||||
const defaultConfig = getBackupDefaultConfig();
|
||||
saveBackupConfig(defaultConfig);
|
||||
saveBackupConfig(defaultConfig).catch(() => {});
|
||||
return defaultConfig;
|
||||
} else {
|
||||
return JSON.parse(
|
||||
|
119
nextcloud_backup/backend/src/services/cronService.ts
Normal file
119
nextcloud_backup/backend/src/services/cronService.ts
Normal file
@ -0,0 +1,119 @@
|
||||
import { CronJob } from "cron";
|
||||
import {
|
||||
CronMode,
|
||||
type BackupConfig,
|
||||
type CronConfig,
|
||||
} from "../types/services/backupConfig.js";
|
||||
import { WorkflowType } from "../types/services/orchecstrator.js";
|
||||
import { doBackupWorkflow } from "./orchestrator.js";
|
||||
import { DateTime } from "luxon";
|
||||
import { getStatus, setStatus } from "../tools/status.js";
|
||||
import logger from "../config/winston.js";
|
||||
|
||||
let cronList: Map<string, CronJob>;
|
||||
|
||||
export function initCron(backupConfig: BackupConfig) {
|
||||
return new Promise((res, rej) => {
|
||||
const fn = doBackupWorkflow;
|
||||
if (cronList) {
|
||||
stopAllCron(cronList);
|
||||
}
|
||||
cronList = new Map();
|
||||
for (const cronItem of backupConfig.cron) {
|
||||
try {
|
||||
if (cronItem.mode == CronMode.DAILY) {
|
||||
cronList.set(cronItem.id, getDailyCron(cronItem, fn));
|
||||
} else if (cronItem.mode == CronMode.WEEKLY) {
|
||||
cronList.set(cronItem.id, getWeeklyCron(cronItem, fn));
|
||||
} else if (cronItem.mode == CronMode.MONTHLY) {
|
||||
cronList.set(cronItem.id, getMonthlyCron(cronItem, fn));
|
||||
} else if (cronItem.mode == CronMode.CUSTOM) {
|
||||
cronList.set(cronItem.id, getCustomCron(cronItem, fn));
|
||||
}
|
||||
} catch {
|
||||
logger.error(`Fail to init CRON ${cronItem.id} (${cronItem.mode})`);
|
||||
stopAllCron(cronList);
|
||||
rej(Error(cronItem.id));
|
||||
}
|
||||
}
|
||||
const nextDate = getNextDate(cronList);
|
||||
const status = getStatus();
|
||||
status.next_backup = nextDate;
|
||||
setStatus(status);
|
||||
res(null);
|
||||
});
|
||||
}
|
||||
|
||||
function getNextDate(cronList: Map<string, CronJob>) {
|
||||
let nextDate: DateTime | undefined = undefined;
|
||||
for (const item of cronList) {
|
||||
const thisDate = item[1].nextDate();
|
||||
if (!nextDate) {
|
||||
nextDate = thisDate;
|
||||
}
|
||||
if (nextDate > thisDate) {
|
||||
nextDate = thisDate;
|
||||
}
|
||||
}
|
||||
return nextDate;
|
||||
}
|
||||
|
||||
function stopAllCron(cronList: Map<string, CronJob>) {
|
||||
for (const item of cronList) {
|
||||
item[1].stop();
|
||||
}
|
||||
}
|
||||
function getDailyCron(
|
||||
config: CronConfig,
|
||||
fn: (type: WorkflowType) => Promise<void>
|
||||
) {
|
||||
const splited = (config.hour as string).split(":");
|
||||
return new CronJob(
|
||||
`${splited[1]} ${splited[0]} * * *`,
|
||||
() => fn(WorkflowType.AUTO),
|
||||
null,
|
||||
true,
|
||||
Intl.DateTimeFormat().resolvedOptions().timeZone
|
||||
);
|
||||
}
|
||||
|
||||
function getWeeklyCron(
|
||||
config: CronConfig,
|
||||
fn: (type: WorkflowType) => Promise<void>
|
||||
) {
|
||||
const splited = (config.hour as string).split(":");
|
||||
return new CronJob(
|
||||
`${splited[1]} ${splited[0]} * * ${config.weekday}`,
|
||||
() => fn(WorkflowType.AUTO),
|
||||
null,
|
||||
true,
|
||||
Intl.DateTimeFormat().resolvedOptions().timeZone
|
||||
);
|
||||
}
|
||||
|
||||
function getMonthlyCron(
|
||||
config: CronConfig,
|
||||
fn: (type: WorkflowType) => Promise<void>
|
||||
) {
|
||||
const splited = (config.hour as string).split(":");
|
||||
return new CronJob(
|
||||
`${splited[1]} ${splited[0]} ${config.monthDay} * *`,
|
||||
() => fn(WorkflowType.AUTO),
|
||||
null,
|
||||
true,
|
||||
Intl.DateTimeFormat().resolvedOptions().timeZone
|
||||
);
|
||||
}
|
||||
|
||||
function getCustomCron(
|
||||
config: CronConfig,
|
||||
fn: (type: WorkflowType) => Promise<void>
|
||||
) {
|
||||
return new CronJob(
|
||||
config.custom as string,
|
||||
() => fn(WorkflowType.AUTO),
|
||||
null,
|
||||
true,
|
||||
Intl.DateTimeFormat().resolvedOptions().timeZone
|
||||
);
|
||||
}
|
@ -21,7 +21,7 @@ export interface Status {
|
||||
last_success?: DateTime;
|
||||
last_try?: DateTime;
|
||||
};
|
||||
next_backup?: string;
|
||||
next_backup?: DateTime;
|
||||
webdav: {
|
||||
logged_in: boolean;
|
||||
folder_created: boolean;
|
||||
|
@ -47,6 +47,19 @@
|
||||
<v-col xl="6" lg="12" sm="6" cols="12">
|
||||
<div class="h-100 d-flex align-center">
|
||||
<span class="me-auto">Next</span>
|
||||
<v-chip
|
||||
variant="elevated"
|
||||
color="success"
|
||||
prepend-icon="mdi-update"
|
||||
>
|
||||
{{
|
||||
status?.next_backup
|
||||
? DateTime.fromISO(status?.next_backup).toLocaleString(
|
||||
DateTime.DATETIME_MED
|
||||
)
|
||||
: "Unknown"
|
||||
}}
|
||||
</v-chip>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
Loading…
Reference in New Issue
Block a user