feat(api): policy and root CRUD endpoints

Policies and roots were editable only via SQL inside the container.
Full CRUD over both, validated against the vocabulary the policy
engine knows — unknown resolutions or sources answer 422 naming the
field, malformed JSON answers 422, deleting a referenced policy or an
occupied root answers 409. Closes #116 (API half).
This commit is contained in:
Miguel Palhas
2026-08-23 14:56:51 +01:00
parent 951669749b
commit 9fa1f0cc51
17 changed files with 1450 additions and 12 deletions
+15 -2
View File
@@ -9,6 +9,7 @@
mod health;
mod movies;
mod owners;
mod policies;
mod roots;
mod search;
mod series;
@@ -26,7 +27,10 @@ pub use movies::{
Accepted, AttentionQueues, CreateMovie, ErrorBody, Movie, MovieFile, Release, UpdateMovie,
};
pub use owners::{CreateOwner, Owner, UpdateOwner};
pub use roots::Root;
pub use policies::{
HdrRulesSpec, Policy, PolicyInput, RequiredAudioSpec, ScoreWeightsSpec, SizeBandSpec,
};
pub use roots::{Root, RootInput};
pub use search::{ClassifiedRelease, SearchResponse};
pub use series::{
CreateEpisode, CreateSeason, CreateSeries, Episode, Season, Series, UpdateEpisode,
@@ -55,6 +59,7 @@ pub const DOCS_PATH: &str = "/api/docs";
(name = "movies", description = "Movie library and actions"),
(name = "series", description = "Series, seasons and episodes (DESIGN.md §4.1, §4.2)"),
(name = "owners", description = "Owner tags and filtered views (DESIGN.md §4.3)"),
(name = "policies", description = "Quality policies (DESIGN.md §5)"),
(name = "search", description = "Unified title and release search"),
(name = "roots", description = "Root folders and their policies")
),
@@ -88,7 +93,10 @@ fn api_router() -> OpenApiRouter<AppState> {
.routes(routes!(owners::get, owners::update, owners::delete))
.routes(routes!(search::search))
.routes(routes!(search::releases))
.routes(routes!(roots::list))
.routes(routes!(roots::list, roots::create))
.routes(routes!(roots::get, roots::update, roots::delete))
.routes(routes!(policies::list, policies::create))
.routes(routes!(policies::get, policies::update, policies::delete))
}
/// The generated `OpenAPI` document.
@@ -301,6 +309,11 @@ mod tests {
("/api/movies/{movie_id}/releases/{release_id}/grab", "post"),
("/api/queues/attention", "get"),
("/api/series", "get"),
("/api/policies", "get"),
("/api/policies", "post"),
("/api/policies/{policy_id}", "put"),
("/api/policies/{policy_id}", "delete"),
("/api/roots", "post"),
] {
assert!(
json["paths"][path][method].is_object(),