From d7e0f0e70eb80b2018ec2113d175ef4ca994941d Mon Sep 17 00:00:00 2001 From: Lorenzo Montanari Date: Tue, 11 Mar 2025 12:30:43 +0100 Subject: [PATCH] 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 Co-authored-by: Zack Pollard --- .../docs/administration/backup-and-restore.md | 7 +++++++ mobile/openapi/lib/model/manual_job_name.dart | Bin 3131 -> 3290 bytes open-api/immich-openapi-specs.json | 3 ++- open-api/typescript-sdk/src/fetch-client.ts | 3 ++- server/src/enum.ts | 1 + server/src/services/job.service.spec.ts | 8 ++++++++ server/src/services/job.service.ts | 4 ++++ web/src/routes/admin/jobs-status/+page.svelte | 1 + 8 files changed, 25 insertions(+), 2 deletions(-) diff --git a/docs/docs/administration/backup-and-restore.md b/docs/docs/administration/backup-and-restore.md index 1b5cdba19..817a7dca6 100644 --- a/docs/docs/administration/backup-and-restore.md +++ b/docs/docs/administration/backup-and-restore.md @@ -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. diff --git a/mobile/openapi/lib/model/manual_job_name.dart b/mobile/openapi/lib/model/manual_job_name.dart index 71c60d8e6489f67e7d7319cc17a9d7fb79575a87..311215ad9e3df768bea061a9fc90bf1966195e7c 100644 GIT binary patch delta 114 zcmdljaZ7ST2J_^lO#Hk_iOJce1ulstiAjmYlO37G_#h&>DNvzg=F|KN3Mk4pYqGGj ZizX))rz#YwqbX { 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 }); diff --git a/server/src/services/job.service.ts b/server/src/services/job.service.ts index 342aec7a7..2f180edd4 100644 --- a/server/src/services/job.service.ts +++ b/server/src/services/job.service.ts @@ -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'); } diff --git a/web/src/routes/admin/jobs-status/+page.svelte b/web/src/routes/admin/jobs-status/+page.svelte index a3e3d6eb0..21381081e 100644 --- a/web/src/routes/admin/jobs-status/+page.svelte +++ b/web/src/routes/admin/jobs-status/+page.svelte @@ -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);