immich/server/src/controllers/maintenance.controller.ts

77 lines
2.9 KiB
TypeScript
Raw Normal View History

feat: restore database backups (#23978) * feat: ProcessRepository#createSpawnDuplexStream * test: write tests for ProcessRepository#createSpawnDuplexStream * feat: StorageRepository#createGzip,createGunzip,createPlainReadStream * feat: backups util (args, create, restore, progress) * feat: wait on maintenance operation lock on boot * chore: use backup util from backup.service.ts test: update backup.service.ts tests with new util * feat: list/delete backups (maintenance services) * chore: open api fix: missing action in cli.service.ts * chore: add missing repositories to MaintenanceModule * refactor: move logSecret into module init * feat: initialise StorageCore in maintenance mode * feat: authenticate websocket requests in maintenance mode * test: add mock for new storage fns * feat: add MaintenanceEphemeralStateRepository refactor: cache the secret in memory * test: update service worker tests * feat: add external maintenance mode status * feat: synchronised status, restore db action * test: backup restore service tests * refactor: DRY end maintenance * feat: list and delete backup routes * feat: start action on boot * fix: should set status on restore end * refactor: add maintenanceStore to hold writables * feat: sync status to web app * feat: web impl. * test: various utils for testings * test: web e2e tests * test: e2e maintenance spec * test: update cli spec * chore: e2e lint * chore: lint fixes * chore: lint fixes * feat: start restore flow route * test: update e2e tests * chore: remove neon lights on maintenance action pages * fix: use 'startRestoreFlow' on onboarding page * chore: ignore any library folder in `docker/` * fix: load status on boot * feat: upload backups * refactor: permit any .sql(.gz) to be listed/restored * feat: download backups from list * fix: permit uploading just .sql files * feat: restore just .sql files * fix: don't show backups list if logged out * feat: system integrity check in restore flow * test: not providing failed backups in API anymore * test: util should also not try to use failedBackups * fix: actually assign inputStream * test: correct test backup prep. * fix: ensure task is defined to show error * test: fix docker cp command * test: update e2e web spec to select next button * test: update e2e api tests * test: refactor timeouts * chore: remove `showDelete` from maint. settings * chore: lint * chore: lint * fix: make sure backups are correctly sorted for clean up * test: update service spec * test: adjust e2e timeout * test: increase web timeouts for ci * chore: move gitignore changes * chore: additional filename validation * refactor: better typings for integrity API * feat: higher accuracy progress tracking * chore: delay lock retry * refactor: remove old maintenance settings * refactor: clean up tailwind classes * refactor: use while loop rather than recursive calls * test: update service specs * chore: check canParse too * chore: lint * fix: logic error causing infinite loop * refactor: use <ProgressBar /> from ui library * fix: create or overwrite file * chore: i18n pass, update progress bar * fix: wrong translation string * chore: update colour variables * test: update web test for new maint. page * chore: format, fix key * test: update tests to be more linter complaint & use new routines * chore: update onClick -> onAction, title -> breadcrumbs * fix: use wrench icon in admin settings sidebar * chore: add translation strings to accordion * chore: lint * refactor: move maintenance worker init into service * refactor: `maintenanceStatus` -> `getMaintenanceStatus` refactor: `integrityCheck` -> `detectPriorInstall` chore: add `v2.4.0` version refactor: `/backups/list` -> `/backups` refactor: use sendFile in download route refactor: use separate backups permissions chore: correct descriptions refactor: permit handler that doesn't return promise for sendfile * refactor: move status impl into service refactor: add active flag to maintenance status * refactor: split into database backup controller * test: split api e2e tests and passing * fix: move end button into authed default maint page * fix: also show in restore flow * fix: import getMaintenanceStatus * test: split web e2e tests * refactor: ensure detect install is consistently named * chore: ensure admin for detect install while out of maint. * refactor: remove state repository * test: update maint. worker service spec * test: split backup service spec * refactor: rename db backup routes * refactor: instead of param, allow bulk backup deletion * test: update sdk use in e2e test * test: correct deleteBackup call * fix: correct type for serverinstall response dto * chore: validate filename for deletion * test: wip * test: backups no longer take path param * refactor: scope util to database-backups instead of backups * fix: update worker controller with new route * chore: use new admin page actions * chore: remove stray comment * test: rename outdated test * refactor: getter pattern for maintenance secret * refactor: `createSpawnDuplexStream` -> `spawnDuplexStream` * refactor: prefer `Object.assign` * refactor: remove useless try {} block * refactor: prefer `type Props` refactor: prefer arrow function * refactor: use luxon API for minutesAgo * chore: remove change to gitignore * refactor: prefer `type Props` * refactor: remove async from onMount * refactor: use luxon toRelative for relative time * refactor: duplicate logic check * chore: open api * refactor: begin moving code into web//services * refactor: don't use template string with $t * test: use dialog role to match prompt * refactor: split actions into flow/restore * test: fix action value * refactor: move more service calls into web//services * chore: should void fn return * chore: bump 2.4.0 to 2.5.0 in controller * chore: bump 2.4.0 to 2.5.0 in controller * refactor: use events for web//services * chore: open api * chore: open api * refactor: don't await returned promise * refactor: remove redundant check * refactor: add `type: command` to actions * refactor: split backup entries into own component * refactor: split restore flow into separate components * refactor(web): split BackupDelete event * chore: stylings * chore: stylings * fix: don't log query failure on first boot * feat: support pg_dumpall backups * feat: display information about each backup * chore: i18n * feat: rollback to restore point on migrations failure * feat: health check after restore * chore: format * refactor: split health check into separate function * refactor: split health into repository test: write tests covering rollbacks * fix: omit 'health' requirement from createDbBackup * test(e2e): rollback test * fix: wrap text in backup entry * fix: don't shrink context menu button * fix: correct CREATE DB syntax for postgres * test: rename backups generated by test * feat: add filesize to backup response dto * feat: restore list * feat: ui work * fix: e2e test * fix: e2e test * pr feedback * pr feedback --------- Co-authored-by: Alex <alex.tran1502@gmail.com> Co-authored-by: Jason Rasmussen <jason@rasm.me>
2026-01-20 15:22:28 +00:00
import { BadRequestException, Body, Controller, Get, Post, Res } from '@nestjs/common';
feat: maintenance mode (#23431) * feat: add a `maintenance.enabled` config flag * feat: implement graceful restart feat: restart when maintenance config is toggled * feat: boot a stripped down maintenance api if enabled * feat: cli command to toggle maintenance mode * chore: fallback IMMICH_SERVER_URL environment variable in process * chore: add additional routes to maintenance controller * fix: don't wait for nest application to close to finish request response * chore: add a failsafe on restart to prevent other exit codes from preventing restart * feat: redirect into/from maintenance page * refactor: use system metadata for maintenance status * refactor: wait on WebSocket connection to refresh * feat: broadcast websocket event on server restart refactor: listen to WS instead of polling * refactor: bubble up maintenance information instead of hijacking in fetch function feat: show modal when server is restarting * chore: increase timeout for ungraceful restart * refactor: deduplicate code between api/maintenance workers * fix: skip config check if database is not initialised * fix: add `maintenanceMode` field to system config test * refactor: move maintenance resolution code to static method in service * chore: clean up linter issues * chore: generate dart openapi * refactor: use try{} block for maintenance mode check * fix: logic error in server redirect * chore: include `maintenanceMode` key in e2e test * chore: add i18n entries for maintenance screens * chore: remove negated condition from hook * fix: should set default value not override in service * fix: minor error in page * feat: initial draft of maintenance module, repo., worker controller, worker service * refactor: move broadcast code into notification service * chore: connect websocket on client if in maintenance * chore: set maintenance module app name * refactor: rename repository to include worker chore: configure websocket adapter * feat: reimplement maintenance mode exit with new module * refactor: add a constant enum for ExitCode * refactor: remove redundant route for maintenance * refactor: only spin up kysely on boot (rather than a Nest app) * refactor(web): move redirect logic into +layout file where modal is setup * feat: add Maintenance permission * refactor: merge common code between api/maintenance * fix: propagate changes from the CLI to servers * feat: maintenance authentication guard * refactor: unify maintenance code into repository feat: add a step to generate maintenance mode token * feat: jwt auth for maintenance * refactor: switch from nest jwt to just jsonwebtokens * feat: log into maintenance mode from CLI command * refactor: use `secret` instead of `token` in jwt terminology chore: log maintenance mode login URL on boot chore: don't make CLI actions reload if already in target state * docs: initial draft for maintenance mode page * refactor: always validate the maintenance auth on the server * feat: add a link to maintenance mode documentation * feat: redirect users back to the last page they were on when exiting maintenance * refactor: provide closeFn in both maintenance repos. * refactor: ensure the user is also redirected by the server * chore: swap jsonwebtoken for jose * refactor: introduce AppRestartEvent w/o secret passing * refactor: use navigation goto * refactor: use `continue` instead of `next` * chore: lint fixes for server * chore: lint fixes for web * test: add mock for maintenance repository * test: add base service dependency to maintenance * chore: remove @types/jsonwebtoken * refactor: close database connection after startup check * refactor: use `request#auth` key * refactor: use service instead of repository chore: read token from cookie if possible chore: rename client event to AppRestartV1 * refactor: more concise redirect logic on web * refactor: move redirect check into utils refactor: update translation strings to be more sensible * refactor: always validate login (i.e. check cookie) * refactor: lint, open-api, remove old dto * refactor: encode at point of usage * refactor: remove business logic from repositories * chore: fix server/web lints * refactor: remove repository mock * chore: fix formatting * test: write service mocks for maintenance mode * test: write cli service tests * fix: catch errors when closing app * fix: always report no maintenance when usual API is available * test: api e2e maintenance spec * chore: add response builder * chore: add helper to set maint. auth cookie * feat: add SSR to maintenance API * test(e2e): write web spec for maintenance * chore: clean up lint issues * chore: format files * feat: perform 302 redirect at server level during maintenance * fix: keep trying to stop immich until it succeeds (CLI issue) * chore: lint/format * refactor: annotate references to other services in worker service * chore: lint * refactor: remove unnecessary await Co-authored-by: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> * refactor: move static methods into util * refactor: assert secret exists in maintenance worker * refactor: remove assertion which isn't necessary anymore * refactor: remove assertion * refactor: remove outer try {} catch block from loadMaintenanceAuth * refactor: undo earlier change to vite.config.ts * chore: update tests due to refactors * revert: vite.config.ts * test: expect string jwt * chore: move blanket exceptions into controllers * test: update tests according with last change * refactor: use respondWithCookie refactor: merge start/end into one route refactor: rename MaintenanceRepository to AppRepository chore: use new ApiTag/Endpoint refactor: apply other requested changes * chore: regenerate openapi * chore: lint/format * chore: remove secureOnly for maint. cookie * refactor: move maintenance worker code into src/maintenance\nfix: various test fixes * refactor: use `action` property for setting maint. mode * refactor: remove Websocket#restartApp in favour of individual methods * chore: incomplete commit * chore: remove stray log * fix: call exitApp from maintenance worker on exit * fix: add app repository mock * fix: ensure maintenance cookies are secure * fix: run playwright tests over secure context (localhost) * test: update other references to 127.0.0.1 * refactor: use serverSideEmitWithAck * chore: correct the logic in tryTerminate * test: juggle cookies ourselves * chore: fix lint error for e2e spec * chore: format e2e test * fix: set cookie secure/non-secure depending on context * chore: format files --------- Co-authored-by: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com>
2025-11-17 17:15:44 +00:00
import { ApiTags } from '@nestjs/swagger';
import { Response } from 'express';
import { Endpoint, HistoryBuilder } from 'src/decorators';
import { AuthDto } from 'src/dtos/auth.dto';
feat: restore database backups (#23978) * feat: ProcessRepository#createSpawnDuplexStream * test: write tests for ProcessRepository#createSpawnDuplexStream * feat: StorageRepository#createGzip,createGunzip,createPlainReadStream * feat: backups util (args, create, restore, progress) * feat: wait on maintenance operation lock on boot * chore: use backup util from backup.service.ts test: update backup.service.ts tests with new util * feat: list/delete backups (maintenance services) * chore: open api fix: missing action in cli.service.ts * chore: add missing repositories to MaintenanceModule * refactor: move logSecret into module init * feat: initialise StorageCore in maintenance mode * feat: authenticate websocket requests in maintenance mode * test: add mock for new storage fns * feat: add MaintenanceEphemeralStateRepository refactor: cache the secret in memory * test: update service worker tests * feat: add external maintenance mode status * feat: synchronised status, restore db action * test: backup restore service tests * refactor: DRY end maintenance * feat: list and delete backup routes * feat: start action on boot * fix: should set status on restore end * refactor: add maintenanceStore to hold writables * feat: sync status to web app * feat: web impl. * test: various utils for testings * test: web e2e tests * test: e2e maintenance spec * test: update cli spec * chore: e2e lint * chore: lint fixes * chore: lint fixes * feat: start restore flow route * test: update e2e tests * chore: remove neon lights on maintenance action pages * fix: use 'startRestoreFlow' on onboarding page * chore: ignore any library folder in `docker/` * fix: load status on boot * feat: upload backups * refactor: permit any .sql(.gz) to be listed/restored * feat: download backups from list * fix: permit uploading just .sql files * feat: restore just .sql files * fix: don't show backups list if logged out * feat: system integrity check in restore flow * test: not providing failed backups in API anymore * test: util should also not try to use failedBackups * fix: actually assign inputStream * test: correct test backup prep. * fix: ensure task is defined to show error * test: fix docker cp command * test: update e2e web spec to select next button * test: update e2e api tests * test: refactor timeouts * chore: remove `showDelete` from maint. settings * chore: lint * chore: lint * fix: make sure backups are correctly sorted for clean up * test: update service spec * test: adjust e2e timeout * test: increase web timeouts for ci * chore: move gitignore changes * chore: additional filename validation * refactor: better typings for integrity API * feat: higher accuracy progress tracking * chore: delay lock retry * refactor: remove old maintenance settings * refactor: clean up tailwind classes * refactor: use while loop rather than recursive calls * test: update service specs * chore: check canParse too * chore: lint * fix: logic error causing infinite loop * refactor: use <ProgressBar /> from ui library * fix: create or overwrite file * chore: i18n pass, update progress bar * fix: wrong translation string * chore: update colour variables * test: update web test for new maint. page * chore: format, fix key * test: update tests to be more linter complaint & use new routines * chore: update onClick -> onAction, title -> breadcrumbs * fix: use wrench icon in admin settings sidebar * chore: add translation strings to accordion * chore: lint * refactor: move maintenance worker init into service * refactor: `maintenanceStatus` -> `getMaintenanceStatus` refactor: `integrityCheck` -> `detectPriorInstall` chore: add `v2.4.0` version refactor: `/backups/list` -> `/backups` refactor: use sendFile in download route refactor: use separate backups permissions chore: correct descriptions refactor: permit handler that doesn't return promise for sendfile * refactor: move status impl into service refactor: add active flag to maintenance status * refactor: split into database backup controller * test: split api e2e tests and passing * fix: move end button into authed default maint page * fix: also show in restore flow * fix: import getMaintenanceStatus * test: split web e2e tests * refactor: ensure detect install is consistently named * chore: ensure admin for detect install while out of maint. * refactor: remove state repository * test: update maint. worker service spec * test: split backup service spec * refactor: rename db backup routes * refactor: instead of param, allow bulk backup deletion * test: update sdk use in e2e test * test: correct deleteBackup call * fix: correct type for serverinstall response dto * chore: validate filename for deletion * test: wip * test: backups no longer take path param * refactor: scope util to database-backups instead of backups * fix: update worker controller with new route * chore: use new admin page actions * chore: remove stray comment * test: rename outdated test * refactor: getter pattern for maintenance secret * refactor: `createSpawnDuplexStream` -> `spawnDuplexStream` * refactor: prefer `Object.assign` * refactor: remove useless try {} block * refactor: prefer `type Props` refactor: prefer arrow function * refactor: use luxon API for minutesAgo * chore: remove change to gitignore * refactor: prefer `type Props` * refactor: remove async from onMount * refactor: use luxon toRelative for relative time * refactor: duplicate logic check * chore: open api * refactor: begin moving code into web//services * refactor: don't use template string with $t * test: use dialog role to match prompt * refactor: split actions into flow/restore * test: fix action value * refactor: move more service calls into web//services * chore: should void fn return * chore: bump 2.4.0 to 2.5.0 in controller * chore: bump 2.4.0 to 2.5.0 in controller * refactor: use events for web//services * chore: open api * chore: open api * refactor: don't await returned promise * refactor: remove redundant check * refactor: add `type: command` to actions * refactor: split backup entries into own component * refactor: split restore flow into separate components * refactor(web): split BackupDelete event * chore: stylings * chore: stylings * fix: don't log query failure on first boot * feat: support pg_dumpall backups * feat: display information about each backup * chore: i18n * feat: rollback to restore point on migrations failure * feat: health check after restore * chore: format * refactor: split health check into separate function * refactor: split health into repository test: write tests covering rollbacks * fix: omit 'health' requirement from createDbBackup * test(e2e): rollback test * fix: wrap text in backup entry * fix: don't shrink context menu button * fix: correct CREATE DB syntax for postgres * test: rename backups generated by test * feat: add filesize to backup response dto * feat: restore list * feat: ui work * fix: e2e test * fix: e2e test * pr feedback * pr feedback --------- Co-authored-by: Alex <alex.tran1502@gmail.com> Co-authored-by: Jason Rasmussen <jason@rasm.me>
2026-01-20 15:22:28 +00:00
import {
MaintenanceAuthDto,
MaintenanceDetectInstallResponseDto,
MaintenanceLoginDto,
MaintenanceStatusResponseDto,
SetMaintenanceModeDto,
} from 'src/dtos/maintenance.dto';
feat: maintenance mode (#23431) * feat: add a `maintenance.enabled` config flag * feat: implement graceful restart feat: restart when maintenance config is toggled * feat: boot a stripped down maintenance api if enabled * feat: cli command to toggle maintenance mode * chore: fallback IMMICH_SERVER_URL environment variable in process * chore: add additional routes to maintenance controller * fix: don't wait for nest application to close to finish request response * chore: add a failsafe on restart to prevent other exit codes from preventing restart * feat: redirect into/from maintenance page * refactor: use system metadata for maintenance status * refactor: wait on WebSocket connection to refresh * feat: broadcast websocket event on server restart refactor: listen to WS instead of polling * refactor: bubble up maintenance information instead of hijacking in fetch function feat: show modal when server is restarting * chore: increase timeout for ungraceful restart * refactor: deduplicate code between api/maintenance workers * fix: skip config check if database is not initialised * fix: add `maintenanceMode` field to system config test * refactor: move maintenance resolution code to static method in service * chore: clean up linter issues * chore: generate dart openapi * refactor: use try{} block for maintenance mode check * fix: logic error in server redirect * chore: include `maintenanceMode` key in e2e test * chore: add i18n entries for maintenance screens * chore: remove negated condition from hook * fix: should set default value not override in service * fix: minor error in page * feat: initial draft of maintenance module, repo., worker controller, worker service * refactor: move broadcast code into notification service * chore: connect websocket on client if in maintenance * chore: set maintenance module app name * refactor: rename repository to include worker chore: configure websocket adapter * feat: reimplement maintenance mode exit with new module * refactor: add a constant enum for ExitCode * refactor: remove redundant route for maintenance * refactor: only spin up kysely on boot (rather than a Nest app) * refactor(web): move redirect logic into +layout file where modal is setup * feat: add Maintenance permission * refactor: merge common code between api/maintenance * fix: propagate changes from the CLI to servers * feat: maintenance authentication guard * refactor: unify maintenance code into repository feat: add a step to generate maintenance mode token * feat: jwt auth for maintenance * refactor: switch from nest jwt to just jsonwebtokens * feat: log into maintenance mode from CLI command * refactor: use `secret` instead of `token` in jwt terminology chore: log maintenance mode login URL on boot chore: don't make CLI actions reload if already in target state * docs: initial draft for maintenance mode page * refactor: always validate the maintenance auth on the server * feat: add a link to maintenance mode documentation * feat: redirect users back to the last page they were on when exiting maintenance * refactor: provide closeFn in both maintenance repos. * refactor: ensure the user is also redirected by the server * chore: swap jsonwebtoken for jose * refactor: introduce AppRestartEvent w/o secret passing * refactor: use navigation goto * refactor: use `continue` instead of `next` * chore: lint fixes for server * chore: lint fixes for web * test: add mock for maintenance repository * test: add base service dependency to maintenance * chore: remove @types/jsonwebtoken * refactor: close database connection after startup check * refactor: use `request#auth` key * refactor: use service instead of repository chore: read token from cookie if possible chore: rename client event to AppRestartV1 * refactor: more concise redirect logic on web * refactor: move redirect check into utils refactor: update translation strings to be more sensible * refactor: always validate login (i.e. check cookie) * refactor: lint, open-api, remove old dto * refactor: encode at point of usage * refactor: remove business logic from repositories * chore: fix server/web lints * refactor: remove repository mock * chore: fix formatting * test: write service mocks for maintenance mode * test: write cli service tests * fix: catch errors when closing app * fix: always report no maintenance when usual API is available * test: api e2e maintenance spec * chore: add response builder * chore: add helper to set maint. auth cookie * feat: add SSR to maintenance API * test(e2e): write web spec for maintenance * chore: clean up lint issues * chore: format files * feat: perform 302 redirect at server level during maintenance * fix: keep trying to stop immich until it succeeds (CLI issue) * chore: lint/format * refactor: annotate references to other services in worker service * chore: lint * refactor: remove unnecessary await Co-authored-by: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> * refactor: move static methods into util * refactor: assert secret exists in maintenance worker * refactor: remove assertion which isn't necessary anymore * refactor: remove assertion * refactor: remove outer try {} catch block from loadMaintenanceAuth * refactor: undo earlier change to vite.config.ts * chore: update tests due to refactors * revert: vite.config.ts * test: expect string jwt * chore: move blanket exceptions into controllers * test: update tests according with last change * refactor: use respondWithCookie refactor: merge start/end into one route refactor: rename MaintenanceRepository to AppRepository chore: use new ApiTag/Endpoint refactor: apply other requested changes * chore: regenerate openapi * chore: lint/format * chore: remove secureOnly for maint. cookie * refactor: move maintenance worker code into src/maintenance\nfix: various test fixes * refactor: use `action` property for setting maint. mode * refactor: remove Websocket#restartApp in favour of individual methods * chore: incomplete commit * chore: remove stray log * fix: call exitApp from maintenance worker on exit * fix: add app repository mock * fix: ensure maintenance cookies are secure * fix: run playwright tests over secure context (localhost) * test: update other references to 127.0.0.1 * refactor: use serverSideEmitWithAck * chore: correct the logic in tryTerminate * test: juggle cookies ourselves * chore: fix lint error for e2e spec * chore: format e2e test * fix: set cookie secure/non-secure depending on context * chore: format files --------- Co-authored-by: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com>
2025-11-17 17:15:44 +00:00
import { ApiTag, ImmichCookie, MaintenanceAction, Permission } from 'src/enum';
import { Auth, Authenticated, GetLoginDetails } from 'src/middleware/auth.guard';
import { LoginDetails } from 'src/services/auth.service';
import { MaintenanceService } from 'src/services/maintenance.service';
import { respondWithCookie } from 'src/utils/response';
@ApiTags(ApiTag.Maintenance)
@Controller('admin/maintenance')
export class MaintenanceController {
constructor(private service: MaintenanceService) {}
feat: restore database backups (#23978) * feat: ProcessRepository#createSpawnDuplexStream * test: write tests for ProcessRepository#createSpawnDuplexStream * feat: StorageRepository#createGzip,createGunzip,createPlainReadStream * feat: backups util (args, create, restore, progress) * feat: wait on maintenance operation lock on boot * chore: use backup util from backup.service.ts test: update backup.service.ts tests with new util * feat: list/delete backups (maintenance services) * chore: open api fix: missing action in cli.service.ts * chore: add missing repositories to MaintenanceModule * refactor: move logSecret into module init * feat: initialise StorageCore in maintenance mode * feat: authenticate websocket requests in maintenance mode * test: add mock for new storage fns * feat: add MaintenanceEphemeralStateRepository refactor: cache the secret in memory * test: update service worker tests * feat: add external maintenance mode status * feat: synchronised status, restore db action * test: backup restore service tests * refactor: DRY end maintenance * feat: list and delete backup routes * feat: start action on boot * fix: should set status on restore end * refactor: add maintenanceStore to hold writables * feat: sync status to web app * feat: web impl. * test: various utils for testings * test: web e2e tests * test: e2e maintenance spec * test: update cli spec * chore: e2e lint * chore: lint fixes * chore: lint fixes * feat: start restore flow route * test: update e2e tests * chore: remove neon lights on maintenance action pages * fix: use 'startRestoreFlow' on onboarding page * chore: ignore any library folder in `docker/` * fix: load status on boot * feat: upload backups * refactor: permit any .sql(.gz) to be listed/restored * feat: download backups from list * fix: permit uploading just .sql files * feat: restore just .sql files * fix: don't show backups list if logged out * feat: system integrity check in restore flow * test: not providing failed backups in API anymore * test: util should also not try to use failedBackups * fix: actually assign inputStream * test: correct test backup prep. * fix: ensure task is defined to show error * test: fix docker cp command * test: update e2e web spec to select next button * test: update e2e api tests * test: refactor timeouts * chore: remove `showDelete` from maint. settings * chore: lint * chore: lint * fix: make sure backups are correctly sorted for clean up * test: update service spec * test: adjust e2e timeout * test: increase web timeouts for ci * chore: move gitignore changes * chore: additional filename validation * refactor: better typings for integrity API * feat: higher accuracy progress tracking * chore: delay lock retry * refactor: remove old maintenance settings * refactor: clean up tailwind classes * refactor: use while loop rather than recursive calls * test: update service specs * chore: check canParse too * chore: lint * fix: logic error causing infinite loop * refactor: use <ProgressBar /> from ui library * fix: create or overwrite file * chore: i18n pass, update progress bar * fix: wrong translation string * chore: update colour variables * test: update web test for new maint. page * chore: format, fix key * test: update tests to be more linter complaint & use new routines * chore: update onClick -> onAction, title -> breadcrumbs * fix: use wrench icon in admin settings sidebar * chore: add translation strings to accordion * chore: lint * refactor: move maintenance worker init into service * refactor: `maintenanceStatus` -> `getMaintenanceStatus` refactor: `integrityCheck` -> `detectPriorInstall` chore: add `v2.4.0` version refactor: `/backups/list` -> `/backups` refactor: use sendFile in download route refactor: use separate backups permissions chore: correct descriptions refactor: permit handler that doesn't return promise for sendfile * refactor: move status impl into service refactor: add active flag to maintenance status * refactor: split into database backup controller * test: split api e2e tests and passing * fix: move end button into authed default maint page * fix: also show in restore flow * fix: import getMaintenanceStatus * test: split web e2e tests * refactor: ensure detect install is consistently named * chore: ensure admin for detect install while out of maint. * refactor: remove state repository * test: update maint. worker service spec * test: split backup service spec * refactor: rename db backup routes * refactor: instead of param, allow bulk backup deletion * test: update sdk use in e2e test * test: correct deleteBackup call * fix: correct type for serverinstall response dto * chore: validate filename for deletion * test: wip * test: backups no longer take path param * refactor: scope util to database-backups instead of backups * fix: update worker controller with new route * chore: use new admin page actions * chore: remove stray comment * test: rename outdated test * refactor: getter pattern for maintenance secret * refactor: `createSpawnDuplexStream` -> `spawnDuplexStream` * refactor: prefer `Object.assign` * refactor: remove useless try {} block * refactor: prefer `type Props` refactor: prefer arrow function * refactor: use luxon API for minutesAgo * chore: remove change to gitignore * refactor: prefer `type Props` * refactor: remove async from onMount * refactor: use luxon toRelative for relative time * refactor: duplicate logic check * chore: open api * refactor: begin moving code into web//services * refactor: don't use template string with $t * test: use dialog role to match prompt * refactor: split actions into flow/restore * test: fix action value * refactor: move more service calls into web//services * chore: should void fn return * chore: bump 2.4.0 to 2.5.0 in controller * chore: bump 2.4.0 to 2.5.0 in controller * refactor: use events for web//services * chore: open api * chore: open api * refactor: don't await returned promise * refactor: remove redundant check * refactor: add `type: command` to actions * refactor: split backup entries into own component * refactor: split restore flow into separate components * refactor(web): split BackupDelete event * chore: stylings * chore: stylings * fix: don't log query failure on first boot * feat: support pg_dumpall backups * feat: display information about each backup * chore: i18n * feat: rollback to restore point on migrations failure * feat: health check after restore * chore: format * refactor: split health check into separate function * refactor: split health into repository test: write tests covering rollbacks * fix: omit 'health' requirement from createDbBackup * test(e2e): rollback test * fix: wrap text in backup entry * fix: don't shrink context menu button * fix: correct CREATE DB syntax for postgres * test: rename backups generated by test * feat: add filesize to backup response dto * feat: restore list * feat: ui work * fix: e2e test * fix: e2e test * pr feedback * pr feedback --------- Co-authored-by: Alex <alex.tran1502@gmail.com> Co-authored-by: Jason Rasmussen <jason@rasm.me>
2026-01-20 15:22:28 +00:00
@Get('status')
@Endpoint({
summary: 'Get maintenance mode status',
description: 'Fetch information about the currently running maintenance action.',
history: new HistoryBuilder().added('v2.5.0').alpha('v2.5.0'),
})
getMaintenanceStatus(): MaintenanceStatusResponseDto {
return this.service.getMaintenanceStatus();
}
@Get('detect-install')
@Endpoint({
summary: 'Detect existing install',
description: 'Collect integrity checks and other heuristics about local data.',
history: new HistoryBuilder().added('v2.5.0').alpha('v2.5.0'),
})
@Authenticated({ permission: Permission.Maintenance, admin: true })
detectPriorInstall(): Promise<MaintenanceDetectInstallResponseDto> {
return this.service.detectPriorInstall();
}
feat: maintenance mode (#23431) * feat: add a `maintenance.enabled` config flag * feat: implement graceful restart feat: restart when maintenance config is toggled * feat: boot a stripped down maintenance api if enabled * feat: cli command to toggle maintenance mode * chore: fallback IMMICH_SERVER_URL environment variable in process * chore: add additional routes to maintenance controller * fix: don't wait for nest application to close to finish request response * chore: add a failsafe on restart to prevent other exit codes from preventing restart * feat: redirect into/from maintenance page * refactor: use system metadata for maintenance status * refactor: wait on WebSocket connection to refresh * feat: broadcast websocket event on server restart refactor: listen to WS instead of polling * refactor: bubble up maintenance information instead of hijacking in fetch function feat: show modal when server is restarting * chore: increase timeout for ungraceful restart * refactor: deduplicate code between api/maintenance workers * fix: skip config check if database is not initialised * fix: add `maintenanceMode` field to system config test * refactor: move maintenance resolution code to static method in service * chore: clean up linter issues * chore: generate dart openapi * refactor: use try{} block for maintenance mode check * fix: logic error in server redirect * chore: include `maintenanceMode` key in e2e test * chore: add i18n entries for maintenance screens * chore: remove negated condition from hook * fix: should set default value not override in service * fix: minor error in page * feat: initial draft of maintenance module, repo., worker controller, worker service * refactor: move broadcast code into notification service * chore: connect websocket on client if in maintenance * chore: set maintenance module app name * refactor: rename repository to include worker chore: configure websocket adapter * feat: reimplement maintenance mode exit with new module * refactor: add a constant enum for ExitCode * refactor: remove redundant route for maintenance * refactor: only spin up kysely on boot (rather than a Nest app) * refactor(web): move redirect logic into +layout file where modal is setup * feat: add Maintenance permission * refactor: merge common code between api/maintenance * fix: propagate changes from the CLI to servers * feat: maintenance authentication guard * refactor: unify maintenance code into repository feat: add a step to generate maintenance mode token * feat: jwt auth for maintenance * refactor: switch from nest jwt to just jsonwebtokens * feat: log into maintenance mode from CLI command * refactor: use `secret` instead of `token` in jwt terminology chore: log maintenance mode login URL on boot chore: don't make CLI actions reload if already in target state * docs: initial draft for maintenance mode page * refactor: always validate the maintenance auth on the server * feat: add a link to maintenance mode documentation * feat: redirect users back to the last page they were on when exiting maintenance * refactor: provide closeFn in both maintenance repos. * refactor: ensure the user is also redirected by the server * chore: swap jsonwebtoken for jose * refactor: introduce AppRestartEvent w/o secret passing * refactor: use navigation goto * refactor: use `continue` instead of `next` * chore: lint fixes for server * chore: lint fixes for web * test: add mock for maintenance repository * test: add base service dependency to maintenance * chore: remove @types/jsonwebtoken * refactor: close database connection after startup check * refactor: use `request#auth` key * refactor: use service instead of repository chore: read token from cookie if possible chore: rename client event to AppRestartV1 * refactor: more concise redirect logic on web * refactor: move redirect check into utils refactor: update translation strings to be more sensible * refactor: always validate login (i.e. check cookie) * refactor: lint, open-api, remove old dto * refactor: encode at point of usage * refactor: remove business logic from repositories * chore: fix server/web lints * refactor: remove repository mock * chore: fix formatting * test: write service mocks for maintenance mode * test: write cli service tests * fix: catch errors when closing app * fix: always report no maintenance when usual API is available * test: api e2e maintenance spec * chore: add response builder * chore: add helper to set maint. auth cookie * feat: add SSR to maintenance API * test(e2e): write web spec for maintenance * chore: clean up lint issues * chore: format files * feat: perform 302 redirect at server level during maintenance * fix: keep trying to stop immich until it succeeds (CLI issue) * chore: lint/format * refactor: annotate references to other services in worker service * chore: lint * refactor: remove unnecessary await Co-authored-by: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> * refactor: move static methods into util * refactor: assert secret exists in maintenance worker * refactor: remove assertion which isn't necessary anymore * refactor: remove assertion * refactor: remove outer try {} catch block from loadMaintenanceAuth * refactor: undo earlier change to vite.config.ts * chore: update tests due to refactors * revert: vite.config.ts * test: expect string jwt * chore: move blanket exceptions into controllers * test: update tests according with last change * refactor: use respondWithCookie refactor: merge start/end into one route refactor: rename MaintenanceRepository to AppRepository chore: use new ApiTag/Endpoint refactor: apply other requested changes * chore: regenerate openapi * chore: lint/format * chore: remove secureOnly for maint. cookie * refactor: move maintenance worker code into src/maintenance\nfix: various test fixes * refactor: use `action` property for setting maint. mode * refactor: remove Websocket#restartApp in favour of individual methods * chore: incomplete commit * chore: remove stray log * fix: call exitApp from maintenance worker on exit * fix: add app repository mock * fix: ensure maintenance cookies are secure * fix: run playwright tests over secure context (localhost) * test: update other references to 127.0.0.1 * refactor: use serverSideEmitWithAck * chore: correct the logic in tryTerminate * test: juggle cookies ourselves * chore: fix lint error for e2e spec * chore: format e2e test * fix: set cookie secure/non-secure depending on context * chore: format files --------- Co-authored-by: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com>
2025-11-17 17:15:44 +00:00
@Post('login')
@Endpoint({
summary: 'Log into maintenance mode',
description: 'Login with maintenance token or cookie to receive current information and perform further actions.',
history: new HistoryBuilder().added('v2.3.0').alpha('v2.3.0'),
})
maintenanceLogin(@Body() _dto: MaintenanceLoginDto): MaintenanceAuthDto {
throw new BadRequestException('Not in maintenance mode');
}
@Post()
@Endpoint({
summary: 'Set maintenance mode',
description: 'Put Immich into or take it out of maintenance mode',
history: new HistoryBuilder().added('v2.3.0').alpha('v2.3.0'),
})
@Authenticated({ permission: Permission.Maintenance, admin: true })
async setMaintenanceMode(
@Auth() auth: AuthDto,
@Body() dto: SetMaintenanceModeDto,
@GetLoginDetails() loginDetails: LoginDetails,
@Res({ passthrough: true }) res: Response,
): Promise<void> {
feat: restore database backups (#23978) * feat: ProcessRepository#createSpawnDuplexStream * test: write tests for ProcessRepository#createSpawnDuplexStream * feat: StorageRepository#createGzip,createGunzip,createPlainReadStream * feat: backups util (args, create, restore, progress) * feat: wait on maintenance operation lock on boot * chore: use backup util from backup.service.ts test: update backup.service.ts tests with new util * feat: list/delete backups (maintenance services) * chore: open api fix: missing action in cli.service.ts * chore: add missing repositories to MaintenanceModule * refactor: move logSecret into module init * feat: initialise StorageCore in maintenance mode * feat: authenticate websocket requests in maintenance mode * test: add mock for new storage fns * feat: add MaintenanceEphemeralStateRepository refactor: cache the secret in memory * test: update service worker tests * feat: add external maintenance mode status * feat: synchronised status, restore db action * test: backup restore service tests * refactor: DRY end maintenance * feat: list and delete backup routes * feat: start action on boot * fix: should set status on restore end * refactor: add maintenanceStore to hold writables * feat: sync status to web app * feat: web impl. * test: various utils for testings * test: web e2e tests * test: e2e maintenance spec * test: update cli spec * chore: e2e lint * chore: lint fixes * chore: lint fixes * feat: start restore flow route * test: update e2e tests * chore: remove neon lights on maintenance action pages * fix: use 'startRestoreFlow' on onboarding page * chore: ignore any library folder in `docker/` * fix: load status on boot * feat: upload backups * refactor: permit any .sql(.gz) to be listed/restored * feat: download backups from list * fix: permit uploading just .sql files * feat: restore just .sql files * fix: don't show backups list if logged out * feat: system integrity check in restore flow * test: not providing failed backups in API anymore * test: util should also not try to use failedBackups * fix: actually assign inputStream * test: correct test backup prep. * fix: ensure task is defined to show error * test: fix docker cp command * test: update e2e web spec to select next button * test: update e2e api tests * test: refactor timeouts * chore: remove `showDelete` from maint. settings * chore: lint * chore: lint * fix: make sure backups are correctly sorted for clean up * test: update service spec * test: adjust e2e timeout * test: increase web timeouts for ci * chore: move gitignore changes * chore: additional filename validation * refactor: better typings for integrity API * feat: higher accuracy progress tracking * chore: delay lock retry * refactor: remove old maintenance settings * refactor: clean up tailwind classes * refactor: use while loop rather than recursive calls * test: update service specs * chore: check canParse too * chore: lint * fix: logic error causing infinite loop * refactor: use <ProgressBar /> from ui library * fix: create or overwrite file * chore: i18n pass, update progress bar * fix: wrong translation string * chore: update colour variables * test: update web test for new maint. page * chore: format, fix key * test: update tests to be more linter complaint & use new routines * chore: update onClick -> onAction, title -> breadcrumbs * fix: use wrench icon in admin settings sidebar * chore: add translation strings to accordion * chore: lint * refactor: move maintenance worker init into service * refactor: `maintenanceStatus` -> `getMaintenanceStatus` refactor: `integrityCheck` -> `detectPriorInstall` chore: add `v2.4.0` version refactor: `/backups/list` -> `/backups` refactor: use sendFile in download route refactor: use separate backups permissions chore: correct descriptions refactor: permit handler that doesn't return promise for sendfile * refactor: move status impl into service refactor: add active flag to maintenance status * refactor: split into database backup controller * test: split api e2e tests and passing * fix: move end button into authed default maint page * fix: also show in restore flow * fix: import getMaintenanceStatus * test: split web e2e tests * refactor: ensure detect install is consistently named * chore: ensure admin for detect install while out of maint. * refactor: remove state repository * test: update maint. worker service spec * test: split backup service spec * refactor: rename db backup routes * refactor: instead of param, allow bulk backup deletion * test: update sdk use in e2e test * test: correct deleteBackup call * fix: correct type for serverinstall response dto * chore: validate filename for deletion * test: wip * test: backups no longer take path param * refactor: scope util to database-backups instead of backups * fix: update worker controller with new route * chore: use new admin page actions * chore: remove stray comment * test: rename outdated test * refactor: getter pattern for maintenance secret * refactor: `createSpawnDuplexStream` -> `spawnDuplexStream` * refactor: prefer `Object.assign` * refactor: remove useless try {} block * refactor: prefer `type Props` refactor: prefer arrow function * refactor: use luxon API for minutesAgo * chore: remove change to gitignore * refactor: prefer `type Props` * refactor: remove async from onMount * refactor: use luxon toRelative for relative time * refactor: duplicate logic check * chore: open api * refactor: begin moving code into web//services * refactor: don't use template string with $t * test: use dialog role to match prompt * refactor: split actions into flow/restore * test: fix action value * refactor: move more service calls into web//services * chore: should void fn return * chore: bump 2.4.0 to 2.5.0 in controller * chore: bump 2.4.0 to 2.5.0 in controller * refactor: use events for web//services * chore: open api * chore: open api * refactor: don't await returned promise * refactor: remove redundant check * refactor: add `type: command` to actions * refactor: split backup entries into own component * refactor: split restore flow into separate components * refactor(web): split BackupDelete event * chore: stylings * chore: stylings * fix: don't log query failure on first boot * feat: support pg_dumpall backups * feat: display information about each backup * chore: i18n * feat: rollback to restore point on migrations failure * feat: health check after restore * chore: format * refactor: split health check into separate function * refactor: split health into repository test: write tests covering rollbacks * fix: omit 'health' requirement from createDbBackup * test(e2e): rollback test * fix: wrap text in backup entry * fix: don't shrink context menu button * fix: correct CREATE DB syntax for postgres * test: rename backups generated by test * feat: add filesize to backup response dto * feat: restore list * feat: ui work * fix: e2e test * fix: e2e test * pr feedback * pr feedback --------- Co-authored-by: Alex <alex.tran1502@gmail.com> Co-authored-by: Jason Rasmussen <jason@rasm.me>
2026-01-20 15:22:28 +00:00
if (dto.action !== MaintenanceAction.End) {
const { jwt } = await this.service.startMaintenance(dto, auth.user.name);
feat: maintenance mode (#23431) * feat: add a `maintenance.enabled` config flag * feat: implement graceful restart feat: restart when maintenance config is toggled * feat: boot a stripped down maintenance api if enabled * feat: cli command to toggle maintenance mode * chore: fallback IMMICH_SERVER_URL environment variable in process * chore: add additional routes to maintenance controller * fix: don't wait for nest application to close to finish request response * chore: add a failsafe on restart to prevent other exit codes from preventing restart * feat: redirect into/from maintenance page * refactor: use system metadata for maintenance status * refactor: wait on WebSocket connection to refresh * feat: broadcast websocket event on server restart refactor: listen to WS instead of polling * refactor: bubble up maintenance information instead of hijacking in fetch function feat: show modal when server is restarting * chore: increase timeout for ungraceful restart * refactor: deduplicate code between api/maintenance workers * fix: skip config check if database is not initialised * fix: add `maintenanceMode` field to system config test * refactor: move maintenance resolution code to static method in service * chore: clean up linter issues * chore: generate dart openapi * refactor: use try{} block for maintenance mode check * fix: logic error in server redirect * chore: include `maintenanceMode` key in e2e test * chore: add i18n entries for maintenance screens * chore: remove negated condition from hook * fix: should set default value not override in service * fix: minor error in page * feat: initial draft of maintenance module, repo., worker controller, worker service * refactor: move broadcast code into notification service * chore: connect websocket on client if in maintenance * chore: set maintenance module app name * refactor: rename repository to include worker chore: configure websocket adapter * feat: reimplement maintenance mode exit with new module * refactor: add a constant enum for ExitCode * refactor: remove redundant route for maintenance * refactor: only spin up kysely on boot (rather than a Nest app) * refactor(web): move redirect logic into +layout file where modal is setup * feat: add Maintenance permission * refactor: merge common code between api/maintenance * fix: propagate changes from the CLI to servers * feat: maintenance authentication guard * refactor: unify maintenance code into repository feat: add a step to generate maintenance mode token * feat: jwt auth for maintenance * refactor: switch from nest jwt to just jsonwebtokens * feat: log into maintenance mode from CLI command * refactor: use `secret` instead of `token` in jwt terminology chore: log maintenance mode login URL on boot chore: don't make CLI actions reload if already in target state * docs: initial draft for maintenance mode page * refactor: always validate the maintenance auth on the server * feat: add a link to maintenance mode documentation * feat: redirect users back to the last page they were on when exiting maintenance * refactor: provide closeFn in both maintenance repos. * refactor: ensure the user is also redirected by the server * chore: swap jsonwebtoken for jose * refactor: introduce AppRestartEvent w/o secret passing * refactor: use navigation goto * refactor: use `continue` instead of `next` * chore: lint fixes for server * chore: lint fixes for web * test: add mock for maintenance repository * test: add base service dependency to maintenance * chore: remove @types/jsonwebtoken * refactor: close database connection after startup check * refactor: use `request#auth` key * refactor: use service instead of repository chore: read token from cookie if possible chore: rename client event to AppRestartV1 * refactor: more concise redirect logic on web * refactor: move redirect check into utils refactor: update translation strings to be more sensible * refactor: always validate login (i.e. check cookie) * refactor: lint, open-api, remove old dto * refactor: encode at point of usage * refactor: remove business logic from repositories * chore: fix server/web lints * refactor: remove repository mock * chore: fix formatting * test: write service mocks for maintenance mode * test: write cli service tests * fix: catch errors when closing app * fix: always report no maintenance when usual API is available * test: api e2e maintenance spec * chore: add response builder * chore: add helper to set maint. auth cookie * feat: add SSR to maintenance API * test(e2e): write web spec for maintenance * chore: clean up lint issues * chore: format files * feat: perform 302 redirect at server level during maintenance * fix: keep trying to stop immich until it succeeds (CLI issue) * chore: lint/format * refactor: annotate references to other services in worker service * chore: lint * refactor: remove unnecessary await Co-authored-by: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> * refactor: move static methods into util * refactor: assert secret exists in maintenance worker * refactor: remove assertion which isn't necessary anymore * refactor: remove assertion * refactor: remove outer try {} catch block from loadMaintenanceAuth * refactor: undo earlier change to vite.config.ts * chore: update tests due to refactors * revert: vite.config.ts * test: expect string jwt * chore: move blanket exceptions into controllers * test: update tests according with last change * refactor: use respondWithCookie refactor: merge start/end into one route refactor: rename MaintenanceRepository to AppRepository chore: use new ApiTag/Endpoint refactor: apply other requested changes * chore: regenerate openapi * chore: lint/format * chore: remove secureOnly for maint. cookie * refactor: move maintenance worker code into src/maintenance\nfix: various test fixes * refactor: use `action` property for setting maint. mode * refactor: remove Websocket#restartApp in favour of individual methods * chore: incomplete commit * chore: remove stray log * fix: call exitApp from maintenance worker on exit * fix: add app repository mock * fix: ensure maintenance cookies are secure * fix: run playwright tests over secure context (localhost) * test: update other references to 127.0.0.1 * refactor: use serverSideEmitWithAck * chore: correct the logic in tryTerminate * test: juggle cookies ourselves * chore: fix lint error for e2e spec * chore: format e2e test * fix: set cookie secure/non-secure depending on context * chore: format files --------- Co-authored-by: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com>
2025-11-17 17:15:44 +00:00
return respondWithCookie(res, undefined, {
isSecure: loginDetails.isSecure,
values: [{ key: ImmichCookie.MaintenanceToken, value: jwt }],
});
}
}
}