diff options
| author | Owen Jacobson <owen@grimoire.ca> | 2025-08-30 02:59:51 -0400 |
|---|---|---|
| committer | Owen Jacobson <owen@grimoire.ca> | 2025-08-30 02:59:51 -0400 |
| commit | 96b62f1018641b3dc28cc189d314a1bff475b751 (patch) | |
| tree | 1e10b660f2c68cb73d3c17703257ec0be51c36af /src/cli.rs | |
| parent | 3bd63e20777126216777b392615dc8144f21bb9a (diff) | |
Allow administrators to rotate VAPID keys immediately if needed.
In spite of my design preference away from CLI tools, this is a CLI tool: `pilcrow --database-url <URL> rotate-vapid-key`. This is something we can implement here and now, which does not require us to confront the long-avoided issue of how to handle the idea that some users are allowed to make server-operational changes and some aren't, by delegating the problem back to the OS.
The implementation is a little half-baked to make it easier to rip out later. I would ordinarily prefer to push both `serve` (the default verb, not actually named in this change) and `rotate-vapid-key` into their own, separate CLI submodules, with their own argument structs, but that change is much more intrusive and would make this effectively permanent. This can be yanked out in a few minutes by deleting a few lines of `cli.rs` and inlining the `serve` function.
Nonetheless, nothing is as permanent as a temporary solution, so I've written at least some bare-minimum operations documentation on how to use this and what it does.
Diffstat (limited to 'src/cli.rs')
| -rw-r--r-- | src/cli.rs | 25 |
1 files changed, 23 insertions, 2 deletions
@@ -10,7 +10,8 @@ use axum::{ middleware, response::{IntoResponse, Response}, }; -use clap::{CommandFactory, Parser}; +use chrono::Utc; +use clap::{CommandFactory, Parser, Subcommand}; use sqlx::sqlite::SqlitePool; use tokio::net; @@ -65,6 +66,15 @@ pub struct Args { /// upgrades #[arg(short = 'D', long, env, default_value = "sqlite://pilcrow.db.backup")] backup_database_url: String, + + #[command(subcommand)] + command: Option<Command>, +} + +#[derive(Subcommand)] +enum Command { + /// Immediately rotate the server's VAPID (Web Push) application key. + RotateVapidKey, } impl Args { @@ -89,6 +99,16 @@ impl Args { let pool = self.pool().await?; let app = App::from(pool); + + match self.command { + None => self.serve(app).await?, + Some(Command::RotateVapidKey) => app.vapid().rotate_key(&Utc::now()).await?, + } + + Result::<_, Error>::Ok(()) + } + + async fn serve(self, app: App) -> Result<(), Error> { let app = routes::routes(&app) .route_layer(middleware::from_fn(clock::middleware)) .route_layer(middleware::map_response(Self::server_info())) @@ -101,7 +121,7 @@ impl Args { println!("{started_msg}"); serve.await?; - Result::<_, Error>::Ok(()) + Ok(()) } async fn listener(&self) -> io::Result<net::TcpListener> { @@ -140,5 +160,6 @@ fn started_msg(listener: &net::TcpListener) -> io::Result<String> { enum Error { Io(#[from] io::Error), Database(#[from] db::Error), + Sqlx(#[from] sqlx::Error), Umask(#[from] umask::Error), } |
