maybe something to backup mealie data idk
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

185 lines
4.7 KiB

import fs from 'fs';
import path from 'path';
import { axiosWrapper } from './axios.js';
import { exportDatabaseValidation } from './validation.js';
import { logger, validationErrorCheck } from './utils.js';
import { envVars } from './config.js';
const getBackupsRequest = async () => {
try {
const { data } = await axiosWrapper({
method: 'get',
url: '/backups/available',
});
if (!data) throw 'failed to get a list of backups';
return data;
} catch (error) {
logger({ type: 'error', msg: error });
return false;
}
};
const exportDatabaseRequest = async (tag) => {
try {
const timestampedTag = `${tag}_${Date.now()}`;
const { value: validValue, error } = exportDatabaseValidation({
tag: timestampedTag,
});
validationErrorCheck(error);
const runBackup = async (attempt = 0) => {
/**
* mealie seems to get its knickers in a knot randomly
* noticing 500 internal server errors here
* and also with insomnia so thinken unrelated to axios
* so figured just try like 10 times
* but also for sure there's async/recussion issues here lmao
* eventually mealie would only return 500s when exporting
* and required a restart to fix
*/
try {
attempt++;
if (attempt <= envVars.EXPORT_ATTEMPTS) {
const result = await axiosWrapper({
method: 'post',
url: '/backups/export/database',
payload: {
tag: validValue.tag,
options: {
recipes: true,
settings: true,
pages: true,
themes: true,
groups: true,
users: true,
notifications: true,
},
templates: ['recipes.md'],
},
});
if (result?.status === 201) {
logger({
type: 'info',
msg: `db export was successful and saved at ${result?.data?.export_path}`,
});
return true;
}
if (result?.status !== 201) {
logger({
type: 'error',
msg: `export failed on attempt ${attempt}, will try again in 5 seconds`,
});
await new Promise((resolve) => setTimeout(resolve, 5000));
await runBackup(attempt);
}
}
if (attempt > envVars.EXPORT_ATTEMPTS) {
return false;
}
} catch (error) {
logger({
type: 'error',
msg: error,
});
return false;
}
};
return await runBackup();
} catch (error) {
logger({
type: 'error',
msg: 'failed to export the db',
});
return false;
}
};
const getFileTokenRequest = async (filename) => {
try {
const { data } = await axiosWrapper({
method: 'get',
url: `/backups/${filename}/download`,
});
return data;
} catch (error) {
logger({
type: 'error',
msg: 'failed to get a file token',
});
return false;
}
};
const downloadBackupRequest = async (fileToken, fileName) => {
try {
const subFolder = new Date().toLocaleDateString('en-CA', {
year: 'numeric',
month: 'numeric',
});
const base_path = path.resolve('backups/');
const subfolderExists = fs.existsSync(`${base_path}/${subFolder}`);
if (!subfolderExists)
fs.mkdirSync(`${base_path}/${subFolder}`, { recursive: false }, (err) => {
if (err) throw err;
});
const target_path = `${base_path}/${subFolder}/${fileName}`;
const writer = fs.createWriteStream(target_path, 'binary');
const streamResponse = await axiosWrapper({
method: 'get',
url: `/utils/download?token=${fileToken}`,
responseType: 'stream',
});
streamResponse.data.pipe(writer);
writer.on('finish', () =>
logger({
type: 'info',
msg: `Downloaded to ${target_path}`,
})
);
writer.on('error', () =>
logger({
type: 'error',
msg: `trouble while dowloading ${fileName}`,
})
);
return true;
} catch (error) {
logger({
type: 'error',
msg: 'failed to download a backup file',
});
return false;
}
};
const backupDeleteRequest = async (filename) => {
try {
await axiosWrapper({
method: 'delete',
url: `/backups/${filename}/delete`,
});
logger({
type: 'warn',
msg: `Deleted ${filename} from mealie`,
});
return true;
} catch (error) {
logger({
type: 'error',
msg: 'failed to delete a backup file',
});
return false;
}
};
export {
getBackupsRequest,
exportDatabaseRequest,
getFileTokenRequest,
downloadBackupRequest,
backupDeleteRequest,
};