mirror of
https://github.com/immich-app/immich.git
synced 2025-03-18 02:31:28 -05:00
feat(web): exposed a job to manually trigger database backup procedures (#16622)
* feat(web): exposed a new job to create a manual database backup * chore(server): added a new test case * chore(server): moved job to backup db into the create job popup * remove irrelevant change * openapi * chore: formatting * docs: trigger backup documentation --------- Co-authored-by: Lorenzo Montanari <13736036+l0ll098@users.noreply.github.com> Co-authored-by: Alex Tran <alex.tran1502@gmail.com> Co-authored-by: Zack Pollard <zack@futo.org>
This commit is contained in:
parent
decc878267
commit
d7e0f0e70e
8 changed files with 28 additions and 2 deletions
|
@ -30,6 +30,13 @@ As mentioned above, you should make your own backup of these together with the a
|
|||
You can adjust the schedule and amount of kept backups in the [admin settings](http://my.immich.app/admin/system-settings?isOpen=backup).
|
||||
By default, Immich will keep the last 14 backups and create a new backup every day at 2:00 AM.
|
||||
|
||||
#### Trigger Backup
|
||||
|
||||
You are able to trigger a backup in the [admin job status page](http://my.immich.app/admin/jobs-status).
|
||||
Visit the page, open the "Create job" modal from the top right, select "Backup Database" and click "Confirm".
|
||||
A job will run and trigger a backup, you can verify this worked correctly by checking the logs or the backup folder.
|
||||
This backup will count towards the last X backups that will be kept based on your settings.
|
||||
|
||||
#### Restoring
|
||||
|
||||
We hope to make restoring simpler in future versions, for now you can find the backups in the `UPLOAD_LOCATION/backups` folder on your host.
|
||||
|
|
3
mobile/openapi/lib/model/manual_job_name.dart
generated
3
mobile/openapi/lib/model/manual_job_name.dart
generated
|
@ -28,6 +28,7 @@ class ManualJobName {
|
|||
static const userCleanup = ManualJobName._(r'user-cleanup');
|
||||
static const memoryCleanup = ManualJobName._(r'memory-cleanup');
|
||||
static const memoryCreate = ManualJobName._(r'memory-create');
|
||||
static const backupDatabase = ManualJobName._(r'backup-database');
|
||||
|
||||
/// List of all possible values in this [enum][ManualJobName].
|
||||
static const values = <ManualJobName>[
|
||||
|
@ -36,6 +37,7 @@ class ManualJobName {
|
|||
userCleanup,
|
||||
memoryCleanup,
|
||||
memoryCreate,
|
||||
backupDatabase,
|
||||
];
|
||||
|
||||
static ManualJobName? fromJson(dynamic value) => ManualJobNameTypeTransformer().decode(value);
|
||||
|
@ -79,6 +81,7 @@ class ManualJobNameTypeTransformer {
|
|||
case r'user-cleanup': return ManualJobName.userCleanup;
|
||||
case r'memory-cleanup': return ManualJobName.memoryCleanup;
|
||||
case r'memory-create': return ManualJobName.memoryCreate;
|
||||
case r'backup-database': return ManualJobName.backupDatabase;
|
||||
default:
|
||||
if (!allowNull) {
|
||||
throw ArgumentError('Unknown enum value to decode: $data');
|
||||
|
|
|
@ -9918,7 +9918,8 @@
|
|||
"tag-cleanup",
|
||||
"user-cleanup",
|
||||
"memory-cleanup",
|
||||
"memory-create"
|
||||
"memory-create",
|
||||
"backup-database"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
|
|
@ -3580,7 +3580,8 @@ export enum ManualJobName {
|
|||
TagCleanup = "tag-cleanup",
|
||||
UserCleanup = "user-cleanup",
|
||||
MemoryCleanup = "memory-cleanup",
|
||||
MemoryCreate = "memory-create"
|
||||
MemoryCreate = "memory-create",
|
||||
BackupDatabase = "backup-database"
|
||||
}
|
||||
export enum JobName {
|
||||
ThumbnailGeneration = "thumbnailGeneration",
|
||||
|
|
|
@ -237,6 +237,7 @@ export enum ManualJobName {
|
|||
USER_CLEANUP = 'user-cleanup',
|
||||
MEMORY_CLEANUP = 'memory-cleanup',
|
||||
MEMORY_CREATE = 'memory-create',
|
||||
BACKUP_DATABASE = 'backup-database',
|
||||
}
|
||||
|
||||
export enum AssetPathType {
|
||||
|
|
|
@ -195,6 +195,14 @@ describe(JobService.name, () => {
|
|||
expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.QUEUE_FACIAL_RECOGNITION, data: { force: false } });
|
||||
});
|
||||
|
||||
it('should handle a start backup database command', async () => {
|
||||
mocks.job.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false });
|
||||
|
||||
await sut.handleCommand(QueueName.BACKUP_DATABASE, { command: JobCommand.START, force: false });
|
||||
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.BACKUP_DATABASE, data: { force: false } });
|
||||
});
|
||||
|
||||
it('should throw a bad request when an invalid queue is used', async () => {
|
||||
mocks.job.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false });
|
||||
|
||||
|
|
|
@ -39,6 +39,10 @@ const asJobItem = (dto: JobCreateDto): JobItem => {
|
|||
return { name: JobName.MEMORIES_CREATE };
|
||||
}
|
||||
|
||||
case ManualJobName.BACKUP_DATABASE: {
|
||||
return { name: JobName.BACKUP_DATABASE };
|
||||
}
|
||||
|
||||
default: {
|
||||
throw new BadRequestException('Invalid job name');
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
{ title: $t('admin.user_cleanup_job'), value: ManualJobName.UserCleanup },
|
||||
{ title: $t('admin.memory_cleanup_job'), value: ManualJobName.MemoryCleanup },
|
||||
{ title: $t('admin.memory_generate_job'), value: ManualJobName.MemoryCreate },
|
||||
{ title: $t('admin.backup_database'), value: ManualJobName.BackupDatabase },
|
||||
].map(({ value, title }) => ({ id: value, label: title, value }));
|
||||
|
||||
const handleCancel = () => (isOpen = false);
|
||||
|
|
Loading…
Add table
Reference in a new issue