Files
movies-diary/crates/presentation/src/openapi/mod.rs
Gabriel Kaszewski 020fbb891e
Some checks failed
CI / Check / Test (push) Failing after 9m16s
feat: always-on Insights — correlation analysis over viewing habits
Conservative, live insights (ADR-0010) derived from each user's diary:
rating lift by genre (first-time watches only), genre seasonality vs a
uniform 1/12 month baseline, medium weekend-lift, and watch-time rhythm.

- domain: WatchRow model, InsightsQuery port, pure insights_analyzer with
  all honesty thresholds (sample minimums, strength cutoffs, suppressions
  for import noise), unit-tested
- adapters: thin Sqlite/Postgres fetch twins, reusing WrapUp's genre fetch
- application: get_insights use case, InsightsGroup in Deps
- composition/server: wiring through Repositories/DatabaseOutput
- presentation: GET /api/v1/insights handler, typed serde-tagged DTOs,
  openapi doc
- spa: /insights route (shadcn cards), insights feature hook, profile
  entry link, en locale strings
- server test: end-to-end seed sqlite → HTTP → rating lift + rhythm

Also records ADR-0010, defines "Insight" in the glossary, and includes
pre-existing worktree comment-stripping cleanups.
2026-10-12 19:29:54 +02:00

64 lines
1.8 KiB
Rust

mod auth;
mod diary;
mod goals;
mod import;
mod insights;
mod movies;
mod search;
mod social;
mod users;
mod watchlist;
mod webhook;
mod wrapup;
use axum::Router;
use utoipa::{
Modify, OpenApi,
openapi::security::{Http, HttpAuthScheme, SecurityScheme},
};
use utoipa_scalar::{Scalar, Servable};
use utoipa_swagger_ui::SwaggerUi;
struct SecurityAddon;
impl Modify for SecurityAddon {
fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
let components = openapi.components.get_or_insert_with(Default::default);
components.add_security_scheme(
"bearer_auth",
SecurityScheme::Http(Http::new(HttpAuthScheme::Bearer)),
);
}
}
fn build() -> utoipa::openapi::OpenApi {
let mut api = auth::AuthDoc::openapi();
api.info = utoipa::openapi::InfoBuilder::new()
.title("Movies Diary API")
.version("1.0.0")
.description(Some("REST API for Movies Diary. Authenticate with `POST /api/v1/auth/login` to get a Bearer token."))
.build();
api.merge(diary::DiaryDoc::openapi());
api.merge(movies::MoviesDoc::openapi());
api.merge(users::UsersDoc::openapi());
api.merge(import::ImportDoc::openapi());
api.merge(search::SearchDoc::openapi());
api.merge(watchlist::WatchlistDoc::openapi());
api.merge(goals::GoalsDoc::openapi());
api.merge(webhook::WebhookDoc::openapi());
api.merge(wrapup::WrapUpDoc::openapi());
api.merge(insights::InsightsDoc::openapi());
#[cfg(feature = "federation")]
api.merge(social::SocialDoc::openapi());
SecurityAddon.modify(&mut api);
api
}
pub fn serve(router: Router) -> Router {
tracing::info!("API docs at /docs (Swagger) and /scalar");
let spec = build();
router
.merge(SwaggerUi::new("/docs").url("/openapi.json", spec.clone()))
.merge(Scalar::with_url("/scalar", spec))
}