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
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, |
|
};
|
|
|