Skip to main content

cairn_mod/server/admin/
mod.rs

1//! `tools.cairn.admin.*` handlers (§F12).
2//!
3//! Each endpoint lives in its own file; [`admin_router`] wires them
4//! together under one `Extension<AdminState>` so sub-modules don't
5//! need to duplicate state plumbing. Per-request auth + role + CORS
6//! gating is centralized in the shared `common::verify_and_authorize`
7//! helper inside this module.
8
9use std::sync::Arc;
10
11use axum::Extension;
12use axum::Router;
13use axum::routing::{get, post};
14use sqlx::{Pool, Sqlite};
15
16use crate::auth::AuthContext;
17use crate::writer::WriterHandle;
18
19mod apply_label;
20mod audit_view;
21mod common;
22mod flag_reporter;
23mod get_report;
24mod list_audit_log;
25mod list_labels;
26mod list_reports;
27mod negate_label;
28mod report_view;
29mod resolve_report;
30
31/// Operator configuration for admin endpoints. Kept separate from the
32/// subscribe/query configs so operators can tune label-value policy
33/// without touching read-side knobs.
34#[derive(Debug, Clone, Default)]
35pub struct AdminConfig {
36    /// Operator-declared label values (§F12 `InvalidLabelValue`).
37    /// When `Some`, `applyLabel` rejects values not in this set.
38    /// When `None`, any val ≤128 bytes is accepted — the §F11
39    /// anti-leak principle applies: the error message on reject
40    /// does NOT enumerate the allowed values.
41    ///
42    /// Future: a #9 service-record update may derive this from the
43    /// published `app.bsky.labeler.service` record so the lexicon set
44    /// and the runtime policy stay in lockstep.
45    pub label_values: Option<Vec<String>>,
46}
47
48/// Build a Router exposing the tools.cairn.admin.* endpoints
49/// registered so far. Compose with subscribe/query/createReport
50/// routers via `Router::merge`.
51pub fn admin_router(
52    pool: Pool<Sqlite>,
53    writer: WriterHandle,
54    auth: Arc<AuthContext>,
55    config: AdminConfig,
56) -> Router {
57    let state = common::AdminState {
58        pool,
59        writer,
60        auth,
61        config: Arc::new(config),
62    };
63    Router::new()
64        .route(
65            "/xrpc/tools.cairn.admin.applyLabel",
66            post(apply_label::handler),
67        )
68        .route(
69            "/xrpc/tools.cairn.admin.negateLabel",
70            post(negate_label::handler),
71        )
72        .route(
73            "/xrpc/tools.cairn.admin.listLabels",
74            get(list_labels::handler),
75        )
76        .route(
77            "/xrpc/tools.cairn.admin.listReports",
78            get(list_reports::handler),
79        )
80        .route(
81            "/xrpc/tools.cairn.admin.getReport",
82            get(get_report::handler),
83        )
84        .route(
85            "/xrpc/tools.cairn.admin.resolveReport",
86            post(resolve_report::handler),
87        )
88        .route(
89            "/xrpc/tools.cairn.admin.flagReporter",
90            post(flag_reporter::handler),
91        )
92        .route(
93            "/xrpc/tools.cairn.admin.listAuditLog",
94            get(list_audit_log::handler),
95        )
96        .layer(Extension(state))
97}