update rustwarden-CLI
EBO <eric.bouhana@softalys.com> committé le 2026-05-12 08:48
2ed307e73b04b144e4ec92422ff575fcda57ec84
1 parent(s)
4 fichiers modifiés
+301
-18
M
README.md
+80
-2
@@ -18,6 +18,7 @@ Permet de creer rapidement des applications web avec authentification JWT, RBAC
| 18 | 18 | - Middleware d'authentification et d'autorisation |
| 19 | 19 | - Conformite ANSSI (complexite des mots de passe, HttpOnly cookies, etc.) |
| 20 | 20 | - OAuth2 / OpenID Connect (Google, GitHub, Discord, Microsoft) avec architecture extensible |
| 21 | +- Federation ActivityPub optionnelle (RFC 0001+) : WebFinger, NodeInfo, login AP-native, invitations d'equipe cross-instance, modele de confiance allowlist/blocklist — fournie par le crate `rustwarden-fediverse` | |
| 21 | 22 | - Variable `APP_NAME` pour personnaliser le branding partout dans l'UI |
| 22 | 23 | |
| 23 | 24 | ## Architecture |
@@ -25,8 +26,9 @@ Permet de creer rapidement des applications web avec authentification JWT, RBAC
| 25 | 26 | ``` |
| 26 | 27 | rustwarden/ |
| 27 | 28 | ├── crates/ |
| 28 | -│ ├── rustwarden-core/ # Library : auth, RBAC, services, middleware, i18n, hooks | |
| 29 | -│ └── rustwarden-ui/ # UI optionnelle : templates HTML, admin panel, handlers | |
| 29 | +│ ├── rustwarden-core/ # Library : auth, RBAC, services, middleware, i18n, hooks | |
| 30 | +│ ├── rustwarden-ui/ # UI optionnelle : templates HTML, admin panel, handlers | |
| 31 | +│ └── rustwarden-fediverse/ # Federation ActivityPub optionnelle (RFC 0001+) | |
| 30 | 32 | ├── app/ # Application de reference (12 lignes de main.rs) |
| 31 | 33 | ├── tools/ |
| 32 | 34 | │ └── rustwarden-cli/ # Wizard interactif pour creer de nouveaux projets |
@@ -42,6 +44,7 @@ rustwarden/
| 42 | 44 | |-------|-------------| |
| 43 | 45 | | `rustwarden-core` | Authentification JWT, modeles SeaORM, services (users, roles, permissions, email), middleware (auth, admin, CSRF, rate-limit), i18n, background tasks, hooks | |
| 44 | 46 | | `rustwarden-ui` | Interface HTML par defaut (login, profil, admin panel) avec Askama, DaisyUI, HTMX | |
| 47 | +| `rustwarden-fediverse` | Federation ActivityPub (WebFinger, NodeInfo, actor, inbox, signatures HTTP, auth AP-native, invitations cross-instance, worker outbound, modele de confiance allowlist/blocklist) — optionnel via feature Cargo `fediverse` | | |
| 45 | 48 | | `rustwarden-app` | Binaire de reference utilisant le builder | |
| 46 | 49 | | `rustwarden-cli` | Wizard CLI interactif pour scaffolder des applications autonomes | |
| 47 | 50 |
@@ -274,6 +277,37 @@ RustwardenApp::builder()
| 274 | 277 | | PUT | `/admin/settings` | Mettre a jour les parametres | |
| 275 | 278 | | GET | `/admin/errors` | Page de test d'erreurs | |
| 276 | 279 | |
| 280 | +### Pages admin federation *(feature `fediverse`)* | |
| 281 | + | |
| 282 | +Quand le binaire est compile avec `--features fediverse`, le dashboard admin affiche une carte "Federation ActivityPub" qui pointe vers `/admin/fediverse/settings`. Depuis ce hub : | |
| 283 | + | |
| 284 | +| Methode | Chemin | Description | | |
| 285 | +|---------|--------|-------------| | |
| 286 | +| GET | `/admin/fediverse/settings` | Toggle d'activation + selecteurs (mode de confiance, scope d'interop, authorized-fetch) | | |
| 287 | +| POST | `/admin/fediverse/settings` | Sauvegarder les reglages | | |
| 288 | +| GET | `/admin/fediverse/trust_list` | Liste de confiance (allowlist ou blocklist selon le mode) | | |
| 289 | +| POST | `/admin/fediverse/trust_list/add` | Ajouter un domaine | | |
| 290 | +| POST | `/admin/fediverse/trust_list/delete` | Retirer un domaine | | |
| 291 | +| GET | `/admin/fediverse/queue` | Dashboard de la file d'envoi outbound (RFC 0011) | | |
| 292 | +| GET | `/admin/fediverse/sessions_remote` | Sessions distantes actives | | |
| 293 | +| POST | `/admin/fediverse/sessions_remote/revoke` | Revoquer une session distante | | |
| 294 | +| POST | `/admin/fediverse/sessions_remote/kill_domain` | Kill-switch par domaine | | |
| 295 | +| GET | `/admin/fediverse/nodeinfo` | Stats NodeInfo humanisees | | |
| 296 | +| GET | `/admin/teams/{id}/invites` | Invitations d'equipe cross-instance | | |
| 297 | + | |
| 298 | +Cote utilisateur final, le flow de connexion federee est expose sur `/login/fediverse` (et son SSE `/login/fediverse/sse/{id}`), l'ecran de consentement IdP sur `/fediverse/consent/{id}`, et la page d'acceptation d'invitation sur `/fediverse/invite/{id}`. Le profil utilisateur expose `/profile/connected_apps` pour lister les instances qu'il a autorisees. | |
| 299 | + | |
| 300 | +### Endpoints protocole (publics, hors UI) *(feature `fediverse`)* | |
| 301 | + | |
| 302 | +| Methode | Chemin | Description | | |
| 303 | +|---------|--------|-------------| | |
| 304 | +| GET | `/.well-known/webfinger?resource=acct:user@domain` | Resolution WebFinger RFC 7033 | | |
| 305 | +| GET | `/.well-known/nodeinfo` | Discovery NodeInfo | | |
| 306 | +| GET | `/nodeinfo/2.1` | Document NodeInfo 2.1 | | |
| 307 | +| GET | `/users/{username}` | Person actor JSON (avec content negotiation) | | |
| 308 | +| POST | `/inbox` | Inbox AP (Update / Delete / Revoke) | | |
| 309 | +| POST | `/fediverse/auth/inbox` | Inbox AP-native pour `AuthRequest` / `Accept` / `Reject` | | |
| 310 | + | |
| 277 | 311 | --- |
| 278 | 312 | |
| 279 | 313 | ## Hooks (extension) |
@@ -490,6 +524,47 @@ Le panneau d'administration (`/admin/roles`) permet de :
| 490 | 524 | |
| 491 | 525 | > Les credentials OAuth peuvent etre configures via l'interface Admin (`/admin/settings`) ou via ces variables d'environnement (fallback). |
| 492 | 526 | |
| 527 | +### Federation ActivityPub *(feature `fediverse`)* | |
| 528 | + | |
| 529 | +| Variable | Description | Defaut | | |
| 530 | +|----------|-------------|--------| | |
| 531 | +| `FEDIVERSE_DOMAIN` | Domaine federe expose, ex. `example.org`. Refuse les placeholders type `example.com`, `your.tld`, `change-me.tld`. | requis | | |
| 532 | +| `FEDIVERSE_DEBUG_LOCAL_FEDERATION` | Autorise les base URLs `http://` (tests locaux multi-instances). **Ne jamais activer en production.** | `false` | | |
| 533 | +| `FEDIVERSE_AUTHORIZED_FETCH` | Mode "secure fetch" Mastodon : exige une signature HTTP pour resoudre un acteur. Activer apres audit. | `false` | | |
| 534 | +| `FEDIVERSE_OUTBOUND_WORKERS` | Nombre de workers concurrents qui drainent la file de delivery. | `4` | | |
| 535 | +| `FEDIVERSE_OUTBOUND_MAX_ATTEMPTS` | Tentatives maximum avant qu'un job soit marque `dead`. | `8` | | |
| 536 | +| `FEDIVERSE_OUTBOUND_REQUEST_TIMEOUT_SECS` | Timeout par requete outbound. | `30` | | |
| 537 | +| `FEDIVERSE_FETCH_ACTOR_CACHE_TTL_SECS` | TTL du cache des documents acteur distants. | `86400` | | |
| 538 | +| `FEDIVERSE_AUTH_CHALLENGE_TTL_SECS` | TTL des challenges d'auth AP-native (RFC 0006). | `300` | | |
| 539 | + | |
| 540 | +> La feature est **opt-in a la compilation** : ajouter `--features fediverse` au binaire. Le crate `rustwarden-core` ne tire **aucune** des deps lourdes (`activitypub_federation`, `rsa`, `reqwest`, `ammonia`) tant que le binaire n'active pas la feature. Cote runtime, deux flags supplementaires dans `app_settings` controlent l'experience admin : `fediverse_enabled` (toggle global, defaut `false`) et `fediverse_trust_mode` (`allowlist` / `open_with_blocklist`). Reglage via `/admin/fediverse/settings` ou directement en SQL. | |
| 541 | + | |
| 542 | +Wiring minimal dans `main.rs` (extrait de [`app/src/main.rs`](app/src/main.rs)) : | |
| 543 | + | |
| 544 | +```rust | |
| 545 | +#[cfg(feature = "fediverse")] | |
| 546 | +let builder = { | |
| 547 | + use std::sync::Arc; | |
| 548 | + let db = sea_orm::Database::connect(&std::env::var("DATABASE_URL")?).await?; | |
| 549 | + let config = rustwarden_fediverse::FediverseConfig::from_env()?; | |
| 550 | + let router = rustwarden_fediverse::routes::router(config, db.clone()); | |
| 551 | + let checker: Arc<dyn rustwarden_core::middleware::revocation::RemoteIdentityRevocationChecker> = | |
| 552 | + Arc::new( | |
| 553 | + rustwarden_fediverse::services::revocation::SeaOrmRemoteIdentityRevocationChecker::new( | |
| 554 | + db.clone(), | |
| 555 | + ), | |
| 556 | + ); | |
| 557 | + builder | |
| 558 | + .database(db) | |
| 559 | + .register_protected_prefix("/fediverse/consent") | |
| 560 | + .register_protected_prefix("/fediverse/invite") | |
| 561 | + .register_revocation_checker(checker) | |
| 562 | + .merge_routes(router) | |
| 563 | +}; | |
| 564 | +``` | |
| 565 | + | |
| 566 | +Pour un essai local multi-instances (3 noeuds federes via Docker Compose), voir [`tests/fediverse-testbed/README.md`](tests/fediverse-testbed/README.md). Guide operateur complet : [`docs/fediverse-operations.md`](docs/fediverse-operations.md). | |
| 567 | + | |
| 493 | 568 | --- |
| 494 | 569 | |
| 495 | 570 | ## Migrations |
@@ -581,4 +656,7 @@ La locale est detectee automatiquement dans cet ordre :
| 581 | 656 | | [docs/ANSSI_COMPLIANCE_REPORT.md](docs/ANSSI_COMPLIANCE_REPORT.md) | Rapport de conformite aux recommandations ANSSI | |
| 582 | 657 | | [docs/CSS_STYLE_GUIDE.md](docs/CSS_STYLE_GUIDE.md) | Guide de style CSS/DaisyUI | |
| 583 | 658 | | [docs/configuration-flows.md](docs/configuration-flows.md) | Diagrammes des flux de configuration | |
| 659 | +| [docs/fediverse-operations.md](docs/fediverse-operations.md) | Guide operateur de la federation ActivityPub (10 sections : activation, env vars, app_settings, trust/block list, incidents, observabilite, GC, promotion stable, URLs, limites) | | |
| 660 | +| [docs/rfcs/](docs/rfcs/) | 25 RFCs documentant la conception de la couche federation (0001–0025) | | |
| 661 | +| [tests/fediverse-testbed/README.md](tests/fediverse-testbed/README.md) | Banc d'essai Docker tri-instance (alpha/beta/gamma) pour valider la federation Rustwarden↔Rustwarden | | |
| 584 | 662 | | `.env.sample` | Fichier de reference pour toutes les variables d'environnement | |
M
tools/rustwarden-cli/src/start_project.rs
+62
-0
@@ -29,6 +29,7 @@ pub fn run() -> Result<(), Box<dyn std::error::Error>> {
| 29 | 29 | handle_existing_dir(&project_dir)?; |
| 30 | 30 | |
| 31 | 31 | let with_ui = ask_ui_mode()?; |
| 32 | + let (with_fediverse, fediverse_domain) = ask_fediverse_mode()?; | |
| 32 | 33 | let auto_migrate = ask_auto_migrate()?; |
| 33 | 34 | let port = ask_port()?; |
| 34 | 35 |
@@ -51,6 +52,8 @@ pub fn run() -> Result<(), Box<dyn std::error::Error>> {
| 51 | 52 | app_display_name: app_display_name.clone(), |
| 52 | 53 | description: description.clone(), |
| 53 | 54 | with_ui, |
| 55 | + with_fediverse, | |
| 56 | + fediverse_domain, | |
| 54 | 57 | auto_migrate, |
| 55 | 58 | port, |
| 56 | 59 | db_name, |
@@ -73,6 +76,9 @@ pub fn run() -> Result<(), Box<dyn std::error::Error>> {
| 73 | 76 | if with_ui { |
| 74 | 77 | copy_crate(&rustwarden_root, &project_dir, "rustwarden-ui")?; |
| 75 | 78 | } |
| 79 | + if with_fediverse { | |
| 80 | + copy_crate(&rustwarden_root, &project_dir, "rustwarden-fediverse")?; | |
| 81 | + } | |
| 76 | 82 | |
| 77 | 83 | copy_resources(&rustwarden_root, &project_dir, with_ui)?; |
| 78 | 84 | copy_docs(&rustwarden_root, &project_dir)?; |
@@ -201,6 +207,62 @@ fn handle_existing_dir(project_dir: &Path) -> Result<(), Box<dyn std::error::Err
| 201 | 207 | Ok(()) |
| 202 | 208 | } |
| 203 | 209 | |
| 210 | +/// Demande si le projet doit etre genere avec la couche federation | |
| 211 | +/// ActivityPub (crate `rustwarden-fediverse`). Si oui, demande aussi | |
| 212 | +/// le domaine federe a placer dans `FEDIVERSE_DOMAIN`. | |
| 213 | +/// | |
| 214 | +/// Retourne `(with_fediverse, Some(domain))` ou `(false, None)`. | |
| 215 | +fn ask_fediverse_mode() -> Result<(bool, Option<String>), Box<dyn std::error::Error>> { | |
| 216 | + let enable = Confirm::new() | |
| 217 | + .with_prompt(format!( | |
| 218 | + "{}", | |
| 219 | + style("Activer la federation ActivityPub (RFC 0001+, WebFinger, AP-native auth, invitations cross-instance) ?").cyan() | |
| 220 | + )) | |
| 221 | + .default(false) | |
| 222 | + .interact()?; | |
| 223 | + | |
| 224 | + if !enable { | |
| 225 | + return Ok((false, None)); | |
| 226 | + } | |
| 227 | + | |
| 228 | + let domain: String = Input::new() | |
| 229 | + .with_prompt(format!( | |
| 230 | + "{}", | |
| 231 | + style("Domaine federe (FEDIVERSE_DOMAIN, ex. example.org)").cyan() | |
| 232 | + )) | |
| 233 | + .default("example.org".into()) | |
| 234 | + .validate_with(|input: &String| -> Result<(), String> { | |
| 235 | + let trimmed = input.trim(); | |
| 236 | + if trimmed.is_empty() { | |
| 237 | + return Err("Le domaine ne peut pas etre vide".into()); | |
| 238 | + } | |
| 239 | + // Refuse les placeholders attendus par FediverseConfig::from_env. | |
| 240 | + let lower = trimmed.to_ascii_lowercase(); | |
| 241 | + let stripped = lower | |
| 242 | + .trim_start_matches("http://") | |
| 243 | + .trim_start_matches("https://") | |
| 244 | + .trim_end_matches('/'); | |
| 245 | + let placeholders = [ | |
| 246 | + "example.com", | |
| 247 | + "example.net", | |
| 248 | + "localhost.localdomain", | |
| 249 | + "your.tld", | |
| 250 | + "your-domain.tld", | |
| 251 | + "domain.tld", | |
| 252 | + "change-me.tld", | |
| 253 | + ]; | |
| 254 | + if placeholders.contains(&stripped) { | |
| 255 | + return Err(format!( | |
| 256 | + "`{stripped}` est un placeholder refuse par FediverseConfig — saisir un vrai domaine" | |
| 257 | + )); | |
| 258 | + } | |
| 259 | + Ok(()) | |
| 260 | + }) | |
| 261 | + .interact_text()?; | |
| 262 | + | |
| 263 | + Ok((true, Some(domain.trim().to_string()))) | |
| 264 | +} | |
| 265 | + | |
| 204 | 266 | fn ask_ui_mode() -> Result<bool, Box<dyn std::error::Error>> { |
| 205 | 267 | let options = vec!["Avec interface web (rustwarden-ui)", "API seule (headless)"]; |
| 206 | 268 | let selection = Select::new() |
M
tools/rustwarden-cli/src/templates.rs
+124
-12
@@ -34,6 +34,14 @@ pub struct ProjectConfig {
| 34 | 34 | pub app_display_name: String, |
| 35 | 35 | pub description: String, |
| 36 | 36 | pub with_ui: bool, |
| 37 | + /// Active la federation ActivityPub via le crate `rustwarden-fediverse`. | |
| 38 | + /// Si `true` : copie le crate, ajoute la feature `fediverse` au Cargo.toml, | |
| 39 | + /// injecte le wiring dans `main.rs` et seede les variables `FEDIVERSE_*`. | |
| 40 | + pub with_fediverse: bool, | |
| 41 | + /// Domaine federe declare au boot quand `with_fediverse = true` | |
| 42 | + /// (`FEDIVERSE_DOMAIN`). Rester `None` quand la federation est | |
| 43 | + /// desactivee. | |
| 44 | + pub fediverse_domain: Option<String>, | |
| 37 | 45 | pub auto_migrate: bool, |
| 38 | 46 | pub port: u16, |
| 39 | 47 | pub db_name: String, |
@@ -54,15 +62,40 @@ pub fn cargo_toml(cfg: &ProjectConfig) -> String {
| 54 | 62 | "" |
| 55 | 63 | }; |
| 56 | 64 | |
| 65 | + let fediverse_member = if cfg.with_fediverse { | |
| 66 | + ", \"crates/rustwarden-fediverse\"" | |
| 67 | + } else { | |
| 68 | + "" | |
| 69 | + }; | |
| 70 | + | |
| 57 | 71 | let ui_dep = if cfg.with_ui { |
| 58 | 72 | "\nrustwarden-ui = { path = \"crates/rustwarden-ui\" }" |
| 59 | 73 | } else { |
| 60 | 74 | "" |
| 61 | 75 | }; |
| 62 | 76 | |
| 77 | + let fediverse_dep = if cfg.with_fediverse { | |
| 78 | + "\nrustwarden-fediverse = { path = \"crates/rustwarden-fediverse\", optional = true }" | |
| 79 | + } else { | |
| 80 | + "" | |
| 81 | + }; | |
| 82 | + | |
| 83 | + let features_section = if cfg.with_fediverse { | |
| 84 | + let ui_feature = if cfg.with_ui { | |
| 85 | + ", \"rustwarden-ui/fediverse\"" | |
| 86 | + } else { | |
| 87 | + "" | |
| 88 | + }; | |
| 89 | + format!( | |
| 90 | + "\n[features]\ndefault = [\"fediverse\"]\n# Federation ActivityPub (RFC 0001+).\n# Activable a la compilation. Cf. README §Federation ActivityPub.\nfediverse = [\"dep:rustwarden-fediverse\"{ui_feature}]\n" | |
| 91 | + ) | |
| 92 | + } else { | |
| 93 | + String::new() | |
| 94 | + }; | |
| 95 | + | |
| 63 | 96 | format!( |
| 64 | 97 | r#"[workspace] |
| 65 | -members = ["crates/rustwarden-core"{ui_member}] | |
| 98 | +members = ["crates/rustwarden-core"{ui_member}{fediverse_member}] | |
| 66 | 99 | resolver = "2" |
| 67 | 100 | |
| 68 | 101 | [workspace.lints.clippy] |
@@ -77,9 +110,9 @@ description = "{description}"
| 77 | 110 | [[bin]] |
| 78 | 111 | name = "{name}" |
| 79 | 112 | path = "src/main.rs" |
| 80 | - | |
| 113 | +{features_section} | |
| 81 | 114 | [dependencies] |
| 82 | -rustwarden-core = {{ path = "crates/rustwarden-core" }}{ui_dep} | |
| 115 | +rustwarden-core = {{ path = "crates/rustwarden-core" }}{ui_dep}{fediverse_dep} | |
| 83 | 116 | axum = {{ version = "0.8", features = ["macros"] }} |
| 84 | 117 | tokio = {{ version = "1", features = ["full"] }} |
| 85 | 118 | sea-orm = {{ version = "2.0.0-rc.20", features = ["sqlx-postgres", "runtime-tokio-native-tls", "macros"] }} |
@@ -94,7 +127,10 @@ empty_line_after_doc_comments = "allow"
| 94 | 127 | name = cfg.name, |
| 95 | 128 | description = cfg.description, |
| 96 | 129 | ui_member = ui_member, |
| 130 | + fediverse_member = fediverse_member, | |
| 97 | 131 | ui_dep = ui_dep, |
| 132 | + fediverse_dep = fediverse_dep, | |
| 133 | + features_section = features_section, | |
| 98 | 134 | ) |
| 99 | 135 | } |
| 100 | 136 |
@@ -123,21 +159,65 @@ pub fn main_rs(cfg: &ProjectConfig) -> String {
| 123 | 159 | "" |
| 124 | 160 | }; |
| 125 | 161 | |
| 162 | + // Wiring conditionnel de la federation ActivityPub. Casse la chaine | |
| 163 | + // du builder en 3 etapes pour permettre l'ouverture precoce de la | |
| 164 | + // connexion DB requise par le router fediverse. | |
| 165 | + let (builder_open, fediverse_block, builder_close) = if cfg.with_fediverse { | |
| 166 | + ( | |
| 167 | + "let builder = RustwardenApp::builder()\n", | |
| 168 | + r#" | |
| 169 | + #[cfg(feature = "fediverse")] | |
| 170 | + let builder = { | |
| 171 | + use std::sync::Arc; | |
| 172 | + let db_url = std::env::var("DATABASE_URL") | |
| 173 | + .map_err(|_| anyhow::anyhow!("DATABASE_URL doit etre defini pour la federation"))?; | |
| 174 | + let db = sea_orm::Database::connect(&db_url).await?; | |
| 175 | + let fediverse_config = rustwarden_fediverse::FediverseConfig::from_env() | |
| 176 | + .map_err(|e| anyhow::anyhow!("FediverseConfig invalide : {e}"))?; | |
| 177 | + let fediverse_router = | |
| 178 | + rustwarden_fediverse::routes::router(fediverse_config, db.clone()); | |
| 179 | + let checker: Arc< | |
| 180 | + dyn rustwarden_core::middleware::revocation::RemoteIdentityRevocationChecker, | |
| 181 | + > = Arc::new( | |
| 182 | + rustwarden_fediverse::services::revocation::SeaOrmRemoteIdentityRevocationChecker::new( | |
| 183 | + db.clone(), | |
| 184 | + ), | |
| 185 | + ); | |
| 186 | + builder | |
| 187 | + .database(db) | |
| 188 | + .register_protected_prefix("/fediverse/consent") | |
| 189 | + .register_protected_prefix("/fediverse/invite") | |
| 190 | + .register_revocation_checker(checker) | |
| 191 | + .merge_routes(fediverse_router) | |
| 192 | + }; | |
| 193 | + | |
| 194 | + builder | |
| 195 | +"#, | |
| 196 | + " .merge_routes(app_routes())\n .build()\n .await?\n .run()\n .await\n", | |
| 197 | + ) | |
| 198 | + } else { | |
| 199 | + ( | |
| 200 | + " RustwardenApp::builder()\n", | |
| 201 | + "", | |
| 202 | + " .merge_routes(app_routes())\n .build()\n .await?\n .run()\n .await\n", | |
| 203 | + ) | |
| 204 | + }; | |
| 205 | + | |
| 206 | + let fediverse_use = if cfg.with_fediverse { | |
| 207 | + "#[allow(unused_imports)]\nuse rustwarden_core::middleware::revocation::RemoteIdentityRevocationChecker;\n" | |
| 208 | + } else { | |
| 209 | + "" | |
| 210 | + }; | |
| 211 | + | |
| 126 | 212 | format!( |
| 127 | 213 | r#"use axum::{{routing::get, Router}}; |
| 128 | 214 | use rustwarden_core::prelude::*; |
| 129 | 215 | use sea_orm::DatabaseConnection; |
| 130 | - | |
| 216 | +{fediverse_use} | |
| 131 | 217 | #[tokio::main] |
| 132 | 218 | async fn main() -> anyhow::Result<()> {{ |
| 133 | - RustwardenApp::builder() | |
| 134 | - .from_env() | |
| 135 | -{ui_line}{migrate_line}{email_line}{address_line} .merge_routes(app_routes()) | |
| 136 | - .build() | |
| 137 | - .await? | |
| 138 | - .run() | |
| 139 | - .await | |
| 140 | -}} | |
| 219 | + {builder_open} .from_env() | |
| 220 | +{ui_line}{migrate_line}{email_line}{address_line}{fediverse_block}{builder_close}}} | |
| 141 | 221 | |
| 142 | 222 | fn app_routes() -> Router<DatabaseConnection> {{ |
| 143 | 223 | Router::new() |
@@ -230,6 +310,36 @@ OAUTH_ENCRYPTION_KEY={encryption_key}
| 230 | 310 | } |
| 231 | 311 | } |
| 232 | 312 | |
| 313 | +fn fediverse_env_section(cfg: &ProjectConfig) -> String { | |
| 314 | + if cfg.with_fediverse { | |
| 315 | + let domain = cfg | |
| 316 | + .fediverse_domain | |
| 317 | + .clone() | |
| 318 | + .unwrap_or_else(|| "localhost".to_string()); | |
| 319 | + format!( | |
| 320 | + r#"# --- Federation ActivityPub (RFC 0001+) --- | |
| 321 | +# Active uniquement si le binaire est compile avec --features fediverse. | |
| 322 | +FEDIVERSE_DOMAIN={domain} | |
| 323 | +# FEDIVERSE_DEBUG_LOCAL_FEDERATION=true # autorise http:// (dev local uniquement) | |
| 324 | +# FEDIVERSE_AUTHORIZED_FETCH=false # exiger signature sur fetch acteur | |
| 325 | +# FEDIVERSE_OUTBOUND_WORKERS=4 | |
| 326 | +# FEDIVERSE_OUTBOUND_MAX_ATTEMPTS=8 | |
| 327 | +# FEDIVERSE_OUTBOUND_REQUEST_TIMEOUT_SECS=30 | |
| 328 | +# FEDIVERSE_FETCH_ACTOR_CACHE_TTL_SECS=86400 | |
| 329 | +# FEDIVERSE_AUTH_CHALLENGE_TTL_SECS=300 | |
| 330 | +"# | |
| 331 | + ) | |
| 332 | + } else { | |
| 333 | + r#"# --- Federation ActivityPub (RFC 0001+, desactivee) --- | |
| 334 | +# Pour activer : recompiler avec `--features fediverse` puis decommenter. | |
| 335 | +# FEDIVERSE_DOMAIN=example.org | |
| 336 | +# FEDIVERSE_DEBUG_LOCAL_FEDERATION=false | |
| 337 | +# FEDIVERSE_AUTHORIZED_FETCH=false | |
| 338 | +"# | |
| 339 | + .to_string() | |
| 340 | + } | |
| 341 | +} | |
| 342 | + | |
| 233 | 343 | pub fn dot_env(cfg: &ProjectConfig) -> String { |
| 234 | 344 | let smtp_section = match &cfg.smtp { |
| 235 | 345 | Some(smtp) => format!( |
@@ -345,6 +455,7 @@ DEFAULT_LOCALE=fr
| 345 | 455 | # IMAP_POLL_INTERVAL_SECONDS=300 |
| 346 | 456 | |
| 347 | 457 | {oauth_section} |
| 458 | +{fediverse_section} | |
| 348 | 459 | "#, |
| 349 | 460 | name = cfg.name, |
| 350 | 461 | app_display_name = cfg.app_display_name, |
@@ -358,6 +469,7 @@ DEFAULT_LOCALE=fr
| 358 | 469 | admin_password = cfg.admin_password, |
| 359 | 470 | smtp_section = smtp_section, |
| 360 | 471 | oauth_section = oauth_env_section(cfg), |
| 472 | + fediverse_section = fediverse_env_section(cfg), | |
| 361 | 473 | ) |
| 362 | 474 | } |
| 363 | 475 |
M
tools/rustwarden-cli/src/update_project.rs
+35
-4
@@ -7,7 +7,11 @@ use dialoguer::{Confirm, Input};
| 7 | 7 | use crate::version; |
| 8 | 8 | |
| 9 | 9 | /// Fichiers et dossiers du framework qui sont remplaces lors de la mise a jour. |
| 10 | -const FRAMEWORK_CRATES: &[&str] = &["rustwarden-core", "rustwarden-ui"]; | |
| 10 | +/// `rustwarden-core` est toujours synchronise. `rustwarden-ui` et | |
| 11 | +/// `rustwarden-fediverse` ne sont synchronises que s'ils sont presents | |
| 12 | +/// dans le projet consommateur (detection en debut de `run()`). | |
| 13 | +const FRAMEWORK_CRATES: &[&str] = | |
| 14 | + &["rustwarden-core", "rustwarden-ui", "rustwarden-fediverse"]; | |
| 11 | 15 | |
| 12 | 16 | /// Fichiers du projet consommateur qu'on ne touche JAMAIS. |
| 13 | 17 | const PROTECTED_FILES: &[&str] = &[ |
@@ -41,9 +45,10 @@ pub fn run() -> Result<(), Box<dyn std::error::Error>> {
| 41 | 45 | print_version_info(¤t_version, &new_version); |
| 42 | 46 | |
| 43 | 47 | let has_ui = project_dir.join("crates/rustwarden-ui").exists(); |
| 48 | + let has_fediverse = project_dir.join("crates/rustwarden-fediverse").exists(); | |
| 44 | 49 | let custom_templates = detect_custom_templates(&project_dir, &rustwarden_root, has_ui); |
| 45 | 50 | |
| 46 | - print_update_plan(&project_dir, has_ui, &custom_templates); | |
| 51 | + print_update_plan(&project_dir, has_ui, has_fediverse, &custom_templates); | |
| 47 | 52 | |
| 48 | 53 | if !Confirm::new() |
| 49 | 54 | .with_prompt(format!("{}", style("Proceder a la mise a jour ?").cyan())) |
@@ -58,7 +63,13 @@ pub fn run() -> Result<(), Box<dyn std::error::Error>> {
| 58 | 63 | let backup_dir = create_backup(&project_dir)?; |
| 59 | 64 | |
| 60 | 65 | // --- Mise a jour des crates --- |
| 61 | - let stats = update_crates(&rustwarden_root, &project_dir, has_ui, &custom_templates)?; | |
| 66 | + let stats = update_crates( | |
| 67 | + &rustwarden_root, | |
| 68 | + &project_dir, | |
| 69 | + has_ui, | |
| 70 | + has_fediverse, | |
| 71 | + &custom_templates, | |
| 72 | + )?; | |
| 62 | 73 | |
| 63 | 74 | // --- Reecrire le fichier de version --- |
| 64 | 75 | version::write_version(&project_dir, &new_version)?; |
@@ -207,7 +218,12 @@ fn print_version_info(current: &Option<String>, new: &str) {
| 207 | 218 | } |
| 208 | 219 | |
| 209 | 220 | /// Affiche le plan de mise a jour. |
| 210 | -fn print_update_plan(project_dir: &Path, has_ui: bool, custom_templates: &[String]) { | |
| 221 | +fn print_update_plan( | |
| 222 | + project_dir: &Path, | |
| 223 | + has_ui: bool, | |
| 224 | + has_fediverse: bool, | |
| 225 | + custom_templates: &[String], | |
| 226 | +) { | |
| 211 | 227 | println!("\n{}", style("── Plan de mise a jour ──").cyan().bold()); |
| 212 | 228 | |
| 213 | 229 | println!(" {} {}", style("Projet :").dim(), project_dir.display()); |
@@ -217,6 +233,9 @@ fn print_update_plan(project_dir: &Path, has_ui: bool, custom_templates: &[Strin
| 217 | 233 | if has_ui { |
| 218 | 234 | println!(" - crates/rustwarden-ui/"); |
| 219 | 235 | } |
| 236 | + if has_fediverse { | |
| 237 | + println!(" - crates/rustwarden-fediverse/"); | |
| 238 | + } | |
| 220 | 239 | |
| 221 | 240 | println!( |
| 222 | 241 | "\n {} :", |
@@ -389,6 +408,7 @@ fn update_crates(
| 389 | 408 | rustwarden_root: &Path, |
| 390 | 409 | project_dir: &Path, |
| 391 | 410 | has_ui: bool, |
| 411 | + has_fediverse: bool, | |
| 392 | 412 | custom_templates: &[String], |
| 393 | 413 | ) -> Result<SyncStats, Box<dyn std::error::Error>> { |
| 394 | 414 | println!("\n{}", style("── Mise a jour des crates ──").cyan().bold()); |
@@ -415,6 +435,17 @@ fn update_crates(
| 415 | 435 | )?; |
| 416 | 436 | } |
| 417 | 437 | |
| 438 | + // rustwarden-fediverse : uniquement si le projet l'utilise | |
| 439 | + if has_fediverse { | |
| 440 | + sync_crate( | |
| 441 | + rustwarden_root, | |
| 442 | + project_dir, | |
| 443 | + "rustwarden-fediverse", | |
| 444 | + &[], | |
| 445 | + &mut stats, | |
| 446 | + )?; | |
| 447 | + } | |
| 448 | + | |
| 418 | 449 | Ok(stats) |
| 419 | 450 | } |
| 420 | 451 |
GitRust