//! `GET /api/v1/admin/dev/emails` — dev-only inspection of mail captured //! by the in-memory email sink (G5). //! //! Mounted **only** when the email service is running in dev-capture mode //! (`PICLOUD_DEV_MODE=true` and no SMTP relay configured). In every other //! configuration the route does not exist, so there is no production //! surface here. Capture is instance-wide (the SMTP transport seam can't //! see a script's `app_id`), so the endpoint is instance-wide too and is //! restricted to instance Owners/Admins. use std::sync::Arc; use axum::extract::State; use axum::http::StatusCode; use axum::response::Json; use axum::routing::get; use axum::{Extension, Router}; use picloud_shared::{InstanceRole, Principal}; use crate::email_service::{CapturedEmail, DevEmailSink}; #[derive(Clone)] pub struct DevEmailState { pub sink: Arc, } /// Build the dev-email router. Callers mount this only when dev-capture /// mode is active (i.e. they hold a `Some(sink)`). pub fn dev_emails_router(state: DevEmailState) -> Router { Router::new() .route("/dev/emails", get(list_dev_emails)) .with_state(state) } async fn list_dev_emails( Extension(principal): Extension, State(state): State, ) -> Result>, StatusCode> { // Instance-wide data → require an instance Owner/Admin. A Member // (app-scoped) principal has no business reading every app's mail. if !matches!( principal.instance_role, InstanceRole::Owner | InstanceRole::Admin ) { return Err(StatusCode::FORBIDDEN); } Ok(Json(state.sink.snapshot())) }