split uploaded and generated backups
This commit is contained in:
parent
542a77fbef
commit
0d6103170a
6 changed files with 122 additions and 15 deletions
|
@ -12,7 +12,9 @@ import InternalException from "../../../exceptions/internalException";
|
|||
export async function getGeneratedBackups(req: Request, res: Response): Promise<any> {
|
||||
let filesInFolder = FileSystemHelper.getFilesInDirectory(`backup`);
|
||||
|
||||
res.json(filesInFolder);
|
||||
let sorted = filesInFolder.sort((a, b) => new Date(b.split(".")[0]).getTime() - new Date(a.split(".")[0]).getTime());
|
||||
|
||||
res.json(sorted);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -33,6 +35,38 @@ export async function downloadBackupFile(req: Request, res: Response): Promise<a
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description get uploaded backups
|
||||
* @param req {Request} Express req object
|
||||
* @param res {Response} Express res object
|
||||
* @returns {Promise<*>}
|
||||
*/
|
||||
export async function getUploadedBackups(req: Request, res: Response): Promise<any> {
|
||||
let filesInFolder = FileSystemHelper.getFilesInDirectory("uploaded-backup");
|
||||
|
||||
let sorted = filesInFolder.sort((a, b) => new Date(b.split("_")[0]).getTime() - new Date(a.split("_")[0]).getTime());
|
||||
|
||||
res.json(sorted);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description download uploaded backup file
|
||||
* @param req {Request} Express req object
|
||||
* @param res {Response} Express res object
|
||||
* @returns {Promise<*>}
|
||||
*/
|
||||
export async function downloadUploadedBackupFile(req: Request, res: Response): Promise<any> {
|
||||
let filename = req.params.filename;
|
||||
|
||||
let filepath = FileSystemHelper.formatPath("uploaded-backup", filename);
|
||||
|
||||
res.sendFile(filepath, {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description create backup manually
|
||||
* @param req {Request} Express req object
|
||||
|
@ -55,8 +89,25 @@ export async function restoreBackupByLocalFile(req: Request, res: Response): Pro
|
|||
let filename = req.body.filename;
|
||||
let partial = req.body.partial;
|
||||
let include = req.body.include;
|
||||
let overwrite = req.body.overwrite;
|
||||
|
||||
await BackupHelper.loadBackup({ filename, include, partial });
|
||||
await BackupHelper.loadBackup({ filename, include, partial, overwrite });
|
||||
|
||||
res.sendStatus(204);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description restore uploaded backup by selected
|
||||
* @param req {Request} Express req object
|
||||
* @param res {Response} Express res object
|
||||
* @returns {Promise<*>}
|
||||
*/
|
||||
export async function restoreBackupByUploadedFile(req: Request, res: Response): Promise<any> {
|
||||
let filename = req.body.filename;
|
||||
let partial = req.body.partial;
|
||||
let include = req.body.include;
|
||||
|
||||
await BackupHelper.loadBackup({ filename, path: "uploaded-backup", include, partial });
|
||||
|
||||
res.sendStatus(204);
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ export default abstract class MembershipFactory {
|
|||
return {
|
||||
durationInDays: record.durationInDays,
|
||||
durationInYears: record.durationInYears,
|
||||
exactDuration: record.exactDuration,
|
||||
exactDuration: record.exactDuration.toString(),
|
||||
status: record.status,
|
||||
statusId: record.statusId,
|
||||
memberId: record.memberId,
|
||||
|
|
|
@ -4,6 +4,8 @@ import { EntityManager } from "typeorm";
|
|||
import uniqBy from "lodash.uniqby";
|
||||
import InternalException from "../exceptions/internalException";
|
||||
import UserService from "../service/user/userService";
|
||||
import { BACKUP_COPIES, BACKUP_INTERVAL } from "../env.defaults";
|
||||
import DatabaseActionException from "../exceptions/databaseActionException";
|
||||
|
||||
export type BackupSection =
|
||||
| "member"
|
||||
|
@ -97,7 +99,26 @@ export default abstract class BackupHelper {
|
|||
|
||||
FileSystemHelper.writeFile(path, filename + ".json", JSON.stringify(json, null, 2));
|
||||
|
||||
// TODO: delete older backups by copies env
|
||||
let files = FileSystemHelper.getFilesInDirectory("backup", ".json");
|
||||
let sorted = files.sort((a, b) => new Date(b.split(".")[0]).getTime() - new Date(a.split(".")[0]).getTime());
|
||||
|
||||
const filesToDelete = sorted.slice(BACKUP_COPIES);
|
||||
for (const file of filesToDelete) {
|
||||
FileSystemHelper.deleteFile("backup", file);
|
||||
}
|
||||
}
|
||||
|
||||
static async createBackupOnInterval() {
|
||||
let files = FileSystemHelper.getFilesInDirectory("backup", ".json");
|
||||
let newestFile = files.sort((a, b) => new Date(b.split(".")[0]).getTime() - new Date(a.split(".")[0]).getTime())[0];
|
||||
|
||||
let lastBackup = new Date(newestFile.split(".")[0]);
|
||||
let diffInMs = new Date().getTime() - lastBackup.getTime();
|
||||
let diffInDays = diffInMs / (1000 * 60 * 60 * 24);
|
||||
|
||||
if (diffInDays >= BACKUP_INTERVAL) {
|
||||
await this.createBackup({});
|
||||
}
|
||||
}
|
||||
|
||||
static async loadBackup({
|
||||
|
@ -105,11 +126,13 @@ export default abstract class BackupHelper {
|
|||
path = "/backup",
|
||||
include = [],
|
||||
partial = false,
|
||||
overwrite = false,
|
||||
}: {
|
||||
filename: string;
|
||||
path?: string;
|
||||
partial?: boolean;
|
||||
include?: Array<BackupSection>;
|
||||
overwrite?: boolean;
|
||||
}): Promise<void> {
|
||||
this.transactionManager = undefined;
|
||||
|
||||
|
@ -127,10 +150,12 @@ export default abstract class BackupHelper {
|
|||
const sections = this.backupSection
|
||||
.filter((bs) => (partial ? include.includes(bs.type) : true))
|
||||
.sort((a, b) => a.orderOnClear - b.orderOnClear);
|
||||
for (const section of sections.filter((s) => Object.keys(backup).includes(s.type))) {
|
||||
let refered = this.backupSectionRefered[section.type];
|
||||
for (const ref of refered) {
|
||||
await this.transactionManager.getRepository(ref).delete({});
|
||||
if (!overwrite) {
|
||||
for (const section of sections.filter((s) => Object.keys(backup).includes(s.type))) {
|
||||
let refered = this.backupSectionRefered[section.type];
|
||||
for (const ref of refered) {
|
||||
await this.transactionManager.getRepository(ref).delete({});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -144,7 +169,7 @@ export default abstract class BackupHelper {
|
|||
})
|
||||
.catch((err) => {
|
||||
this.transactionManager = undefined;
|
||||
throw new InternalException("failed to restore backup - rolling back actions", err);
|
||||
throw new DatabaseActionException("BACKUP RESTORE", include.join(", "), err);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -11,14 +11,17 @@ export abstract class FileSystemHelper {
|
|||
}
|
||||
|
||||
static readFile(...filePath: string[]) {
|
||||
this.createFolder(...filePath);
|
||||
return readFileSync(this.formatPath(...filePath), "utf8");
|
||||
}
|
||||
|
||||
static readFileasBase64(...filePath: string[]) {
|
||||
this.createFolder(...filePath);
|
||||
return readFileSync(this.formatPath(...filePath), "base64");
|
||||
}
|
||||
|
||||
static readTemplateFile(filePath: string) {
|
||||
this.createFolder(filePath);
|
||||
return readFileSync(process.cwd() + filePath, "utf8");
|
||||
}
|
||||
|
||||
|
@ -28,6 +31,13 @@ export abstract class FileSystemHelper {
|
|||
writeFileSync(path, file);
|
||||
}
|
||||
|
||||
static deleteFile(...filePath: string[]) {
|
||||
const path = this.formatPath(...filePath);
|
||||
if (existsSync(path)) {
|
||||
unlinkSync(path);
|
||||
}
|
||||
}
|
||||
|
||||
static formatPath(...args: string[]) {
|
||||
return join(process.cwd(), "files", ...args);
|
||||
}
|
||||
|
|
|
@ -40,5 +40,5 @@ import RefreshCommandHandler from "./command/refreshCommandHandler";
|
|||
const job = schedule.scheduleJob("0 0 * * *", async () => {
|
||||
console.log(`${new Date().toISOString()}: running Cron`);
|
||||
await RefreshCommandHandler.deleteExpired();
|
||||
// TODO: create backup by interval env
|
||||
await BackupHelper.createBackupOnInterval();
|
||||
});
|
||||
|
|
|
@ -4,17 +4,22 @@ import multer from "multer";
|
|||
import {
|
||||
createManualBackup,
|
||||
downloadBackupFile,
|
||||
downloadUploadedBackupFile,
|
||||
getGeneratedBackups,
|
||||
getUploadedBackups,
|
||||
restoreBackupByLocalFile,
|
||||
restoreBackupByUploadedFile,
|
||||
uploadBackupFile,
|
||||
} from "../../../controller/admin/user/backupController";
|
||||
import { FileSystemHelper } from "../../../helpers/fileSystemHelper";
|
||||
|
||||
const storage = multer.diskStorage({
|
||||
destination: (req, file, cb) => {
|
||||
cb(null, "files/backup/");
|
||||
FileSystemHelper.createFolder("uploaded-backup");
|
||||
cb(null, "files/uploaded-backup/");
|
||||
},
|
||||
filename: (req, file, cb) => {
|
||||
cb(null, `${new Date().toISOString().split("T")[0]}-uploaded-${file.originalname}`);
|
||||
cb(null, `${new Date().toISOString().split("T")[0]}_${file.originalname}`);
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -31,14 +36,22 @@ const upload = multer({
|
|||
|
||||
var router = express.Router({ mergeParams: true });
|
||||
|
||||
router.get("/", async (req: Request, res: Response) => {
|
||||
router.get("/generated", async (req: Request, res: Response) => {
|
||||
await getGeneratedBackups(req, res);
|
||||
});
|
||||
|
||||
router.get("/:filename", async (req: Request, res: Response) => {
|
||||
router.get("/generated/:filename", async (req: Request, res: Response) => {
|
||||
await downloadBackupFile(req, res);
|
||||
});
|
||||
|
||||
router.get("/uploaded", async (req: Request, res: Response) => {
|
||||
await getUploadedBackups(req, res);
|
||||
});
|
||||
|
||||
router.get("/uploaded/:filename", async (req: Request, res: Response) => {
|
||||
await downloadUploadedBackupFile(req, res);
|
||||
});
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
PermissionHelper.passCheckMiddleware("create", "user", "backup"),
|
||||
|
@ -48,13 +61,21 @@ router.post(
|
|||
);
|
||||
|
||||
router.post(
|
||||
"/restore",
|
||||
"/generated/restore",
|
||||
PermissionHelper.passCheckMiddleware("admin", "user", "backup"),
|
||||
async (req: Request, res: Response) => {
|
||||
await restoreBackupByLocalFile(req, res);
|
||||
}
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/uploaded/restore",
|
||||
PermissionHelper.passCheckMiddleware("admin", "user", "backup"),
|
||||
async (req: Request, res: Response) => {
|
||||
await restoreBackupByUploadedFile(req, res);
|
||||
}
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/upload",
|
||||
PermissionHelper.passCheckMiddleware("create", "user", "backup"),
|
||||
|
|
Loading…
Add table
Reference in a new issue