ML-180: remove backup download feature

Delete ArchiveController, its routes, the maintenance UI backup
button, and the test file. Management scripts and Litestream
supersede this functionality.
This commit is contained in:
Claudio Ortolina
2026-05-15 07:13:33 +01:00
parent 93e414cf15
commit 8391cf275a
8 changed files with 30 additions and 57 deletions
@@ -1,14 +1,20 @@
--- ---
id: ML-180 id: ML-180
title: Remove backup function from application title: Remove backup function from application
status: To Do status: Done
assignee: [] assignee: []
created_date: "2026-05-12 04:57" created_date: "2026-05-12 04:57"
updated_date: "2026-05-12 10:25" updated_date: "2026-05-15 06:11"
labels: labels:
- api - api
- ui - ui
dependencies: [] dependencies: []
modified_files:
- lib/music_library_web/router.ex
- lib/music_library_web/controllers/archive_controller.ex
- lib/music_library_web/live/maintenance_live/index.ex
- test/music_library_web/controllers/archive_controller_test.exs
- docs/architecture.md
--- ---
## Description ## Description
@@ -23,12 +29,12 @@ Management scripts and tools superseded this functionality. It can be removed fr
<!-- AC:BEGIN --> <!-- AC:BEGIN -->
- [ ] #1 The /backup and /api/v1/backup routes return 404 - [x] #1 The /backup and /api/v1/backup routes return 404
- [ ] #2 The Maintenance page no longer shows a Backup button in the Database section - [x] #2 The Maintenance page no longer shows a Backup button in the Database section
- [ ] #3 `mix compile --warnings-as-errors` passes with no ArchiveController references - [x] #3 `mix compile --warnings-as-errors` passes with no ArchiveController references
- [ ] #4 `mix test` passes with no ArchiveControllerTest references - [x] #4 `mix test` passes with no ArchiveControllerTest references
- [ ] #5 The record format `:backup` type still works (record_component format labels still show "Backup" for backup-format records) - [x] #5 The record format `:backup` type still works (record_component format labels still show "Backup" for backup-format records)
- [ ] #6 `docs/architecture.md` no longer lists ArchiveController in the routes table - [x] #6 `docs/architecture.md` no longer lists ArchiveController in the routes table
<!-- AC:END --> <!-- AC:END -->
## Implementation Plan ## Implementation Plan
@@ -136,3 +142,19 @@ None required. No migrations, no environment variables, no service provisioning.
- **`docs/architecture.md`**: Remove the `ArchiveController` row from the routes table. No other architecture docs need changes. - **`docs/architecture.md`**: Remove the `ArchiveController` row from the routes table. No other architecture docs need changes.
- No changes to `docs/project-conventions.md`, `docs/production-infrastructure.md`, `docs/available-tasks.md`, or README needed. - No changes to `docs/project-conventions.md`, `docs/production-infrastructure.md`, `docs/available-tasks.md`, or README needed.
<!-- SECTION:PLAN:END --> <!-- SECTION:PLAN:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Removed the in-app database backup download feature:
1. **Router** (`lib/music_library_web/router.ex`): Removed `get "/backup", ArchiveController, :backup` from both the `:logged_in` pipeline and the `:api` scope.
2. **Controller** (`lib/music_library_web/controllers/archive_controller.ex`): Deleted — module only contained `backup/2` and a private `database_path/0` helper.
3. **Maintenance UI** (`lib/music_library_web/live/maintenance_live/index.ex`): Removed the `<.button href={~p"/backup"}>` element from the Database section.
4. **Tests** (`test/music_library_web/controllers/archive_controller_test.exs`): Deleted.
5. **Docs** (`docs/architecture.md`): Removed the `ArchiveController` row from the controller routes table.
All 982 tests pass. No ArchiveController references remain. The `:backup` record format atom and `format_label(:backup)` are intentionally untouched (they represent a record format type, not the backup download feature).
<!-- SECTION:FINAL_SUMMARY:END -->
-1
View File
@@ -345,7 +345,6 @@ All authenticated routes live inside a single `live_session` with three `on_moun
| `SessionController` | `/login`, `/sessions/create` | Login/logout | | `SessionController` | `/login`, `/sessions/create` | Login/logout |
| `HealthController` | `/health` | Health check | | `HealthController` | `/health` | Health check |
| `LastFmController` | `/auth/last_fm/callback` | Last.fm OAuth | | `LastFmController` | `/auth/last_fm/callback` | Last.fm OAuth |
| `ArchiveController` | `/backup`, `/api/v1/backup` | Database backup download (API route requires token) |
| `AssetController` | `/assets/:transform_payload`, `/public/assets/:transform_payload`, `/api/v1/assets/:transform_payload` | Serve images with transforms (public route for emails, API route requires token) | | `AssetController` | `/assets/:transform_payload`, `/public/assets/:transform_payload`, `/api/v1/assets/:transform_payload` | Serve images with transforms (public route for emails, API route requires token) |
| `CollectionController` | `/api/v1/collection/*`, `POST /api/v1/collection/:record_id/scrobble` | JSON API for collection queries (search with `?q=`, returns 4 cover sizes), and scrobbling records from Presto | | `CollectionController` | `/api/v1/collection/*`, `POST /api/v1/collection/:record_id/scrobble` | JSON API for collection queries (search with `?q=`, returns 4 cover sizes), and scrobbling records from Presto |
| `ErrorController` | `/api/v1/errors`, `/api/v1/errors/:id`, `/api/v1/errors/:id/mute`, `/api/v1/errors/:id/unmute`, `/api/v1/errors/:id/resolve`, `/api/v1/errors/:id/unresolve` | JSON API for production error queries and mutations (requires Bearer token); POST endpoints for mute/unmute/resolve/unresolve | | `ErrorController` | `/api/v1/errors`, `/api/v1/errors/:id`, `/api/v1/errors/:id/mute`, `/api/v1/errors/:id/unmute`, `/api/v1/errors/:id/resolve`, `/api/v1/errors/:id/unresolve` | JSON API for production error queries and mutations (requires Bearer token); POST endpoints for mute/unmute/resolve/unresolve |
@@ -1,23 +0,0 @@
defmodule MusicLibraryWeb.ArchiveController do
use MusicLibraryWeb, :controller
# The database path is configuration controlled, and not derived from any
# user input, so we can safely skip the check.
# sobelow_skip ["Traversal.SendDownload"]
def backup(conn, _params) do
MusicLibrary.Repo.vacuum()
database_path = database_path()
current_time = DateTime.utc_now()
file_name = "music_library_#{DateTime.to_unix(current_time)}.db"
send_download(conn, {:file, database_path},
filename: file_name,
content_type: "application/x-sqlite3"
)
end
defp database_path do
Application.get_env(:music_library, MusicLibrary.Repo)[:database]
end
end
@@ -153,9 +153,6 @@ defmodule MusicLibraryWeb.MaintenanceLive.Index do
> >
{gettext("Optimize")} {gettext("Optimize")}
</.button> </.button>
<.button href={~p"/backup"}>
{gettext("Backup")}
</.button>
</.button_group> </.button_group>
</li> </li>
</ul> </ul>
-3
View File
@@ -63,8 +63,6 @@ defmodule MusicLibraryWeb.Router do
scope "/" do scope "/" do
pipe_through :logged_in pipe_through :logged_in
get "/backup", ArchiveController, :backup
get "/assets/:transform_payload", AssetController, :show get "/assets/:transform_payload", AssetController, :show
live_session :default, live_session :default,
@@ -138,7 +136,6 @@ defmodule MusicLibraryWeb.Router do
post "/errors/:id/resolve", ErrorController, :resolve post "/errors/:id/resolve", ErrorController, :resolve
post "/errors/:id/unresolve", ErrorController, :unresolve post "/errors/:id/unresolve", ErrorController, :unresolve
get "/assets/:transform_payload", AssetController, :show get "/assets/:transform_payload", AssetController, :show
get "/backup", ArchiveController, :backup
end end
if Application.compile_env(:music_library, :monitoring_routes) do if Application.compile_env(:music_library, :monitoring_routes) do
-1
View File
@@ -362,7 +362,6 @@ msgid "Album"
msgstr "" msgstr ""
#: lib/music_library_web/components/record_components.ex #: lib/music_library_web/components/record_components.ex
#: lib/music_library_web/live/maintenance_live/index.ex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Backup" msgid "Backup"
msgstr "" msgstr ""
-1
View File
@@ -362,7 +362,6 @@ msgid "Album"
msgstr "" msgstr ""
#: lib/music_library_web/components/record_components.ex #: lib/music_library_web/components/record_components.ex
#: lib/music_library_web/live/maintenance_live/index.ex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Backup" msgid "Backup"
msgstr "" msgstr ""
@@ -1,17 +0,0 @@
defmodule MusicLibraryWeb.ArchiveControllerTest do
use MusicLibraryWeb.ConnCase
describe "GET /backup" do
test "returns a sqlite database file", %{conn: conn} do
conn = get(conn, "/backup")
assert response(conn, 200)
assert get_resp_header(conn, "content-type") |> hd() =~ "application/x-sqlite3"
[content_disposition] = get_resp_header(conn, "content-disposition")
assert content_disposition =~ "attachment"
assert content_disposition =~ "music_library_"
assert content_disposition =~ ".db"
end
end
end