1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
//! # modo::tenant
//!
//! Multi-tenant request routing.
//!
//! A two-step pipeline resolves a tenant on every request: a [`TenantStrategy`]
//! extracts a raw [`TenantId`] from `http::request::Parts`, and a
//! [`TenantResolver`] maps that identifier to an app-defined tenant type. The
//! resolved tenant is inserted into request extensions and surfaced to handlers
//! via the [`Tenant<T>`] axum extractor.
//!
//! Provides:
//! - [`TenantId`] — raw identifier extracted from the request (`Slug`, `Domain`, `Id`, `ApiKey`)
//! - [`TenantStrategy`] — trait for extracting a [`TenantId`] from request parts
//! - [`TenantResolver`] — trait for mapping a [`TenantId`] to an app-defined tenant type (not object-safe; uses RPITIT)
//! - [`HasTenantId`] — required bound on the resolved tenant; provides the tracing field value
//! - [`Tenant<T>`] — axum extractor for the resolved tenant
//! - [`TenantLayer`] — Tower layer produced by [`middleware()`]
//! - [`TenantMiddleware`] — Tower service that resolves the tenant on every request
//! - [`middleware()`] — primary entry point that builds a [`TenantLayer`] from a strategy and resolver
//!
//! The [`domain`] submodule provides [`DomainService`](domain::DomainService)
//! for registering and DNS-verifying custom per-tenant domains.
//!
//! # How it works
//!
//! 1. A [`TenantStrategy`] extracts a [`TenantId`] from `http::request::Parts`.
//! 2. A [`TenantResolver`] maps that `TenantId` to the app's concrete tenant type.
//! 3. [`middleware()`] combines the two into a Tower [`TenantLayer`] that inserts the
//! resolved tenant into request extensions and records `tenant_id` in the
//! current tracing span.
//! 4. Handler functions use the [`Tenant<T>`] extractor to access the resolved value.
//!
//! # Strategies
//!
//! | Constructor | Struct | Produces |
//! |---|---|---|
//! | [`subdomain()`] | [`SubdomainStrategy`] | `TenantId::Slug` |
//! | [`domain()`] | [`DomainStrategy`] | `TenantId::Domain` |
//! | [`subdomain_or_domain()`] | [`SubdomainOrDomainStrategy`] | `TenantId::Slug` or `TenantId::Domain` |
//! | [`header()`] | [`HeaderStrategy`] | `TenantId::Id` |
//! | [`api_key_header()`] | [`ApiKeyHeaderStrategy`] | `TenantId::ApiKey` (redacted in `Display`/`Debug`) |
//! | [`path_prefix()`] | [`PathPrefixStrategy`] | `TenantId::Slug` (rewrites URI) |
//! | [`path_param()`] | [`PathParamStrategy`] | `TenantId::Slug` (requires `.route_layer()`) |
//!
//! # Quick start
//!
//! ```rust,ignore
//! use modo::tenant::{HasTenantId, Tenant, TenantId, TenantResolver, middleware, subdomain};
//!
//! #[derive(Clone)]
//! struct MyTenant { id: String }
//!
//! impl HasTenantId for MyTenant {
//! fn tenant_id(&self) -> &str { &self.id }
//! }
//!
//! struct MyResolver;
//! impl TenantResolver for MyResolver {
//! type Tenant = MyTenant;
//! async fn resolve(&self, id: &TenantId) -> modo::Result<MyTenant> {
//! Ok(MyTenant { id: id.as_str().to_string() })
//! }
//! }
//!
//! let app = axum::Router::new()
//! .route("/dashboard", axum::routing::get(|t: Tenant<MyTenant>| async move {
//! format!("tenant: {}", t.id)
//! }))
//! .layer(middleware(subdomain("example.com"), MyResolver));
//! ```
pub use Tenant;
pub use TenantId;
pub use ;
pub use ;
pub use ;