feat(cli): show Web UI URL and auth methods in server start output

Print the HTTP URL (cyan) and enabled auth methods after server start,
so users can see at a glance how to access the server and what login
methods are available.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Bryan Helmkamp 2026-04-16 07:21:55 -04:00
parent 1830724fa4
commit 2d6742d416
No known key found for this signature in database
3 changed files with 36 additions and 2 deletions

View file

@ -4,8 +4,9 @@ use std::time::Duration;
use anyhow::{Result, anyhow, bail};
use chrono::Utc;
use fabro_config::user::load_settings_config;
use fabro_config::{Storage, envfile};
use fabro_config::{Storage, envfile, resolve_server_from_file};
use fabro_server::bind::{Bind, BindRequest};
use fabro_server::jwt_auth::auth_method_name;
use fabro_server::serve;
use fabro_server::serve::{DEFAULT_TCP_PORT, ServeArgs};
use fabro_util::printer::Printer;
@ -36,7 +37,15 @@ pub(crate) async fn execute(
))
.await
} else {
execute_daemon(&bind, &serve_args, &storage_dir, true, printer).await
execute_daemon(
&bind,
&serve_args,
&storage_dir,
true,
Some(styles),
printer,
)
.await
}
}
@ -103,6 +112,7 @@ async fn ensure_server_running_with_bind(
&serve_args,
storage_dir,
false,
None,
Printer::Silent,
)
.await
@ -287,6 +297,7 @@ async fn execute_daemon(
serve_args: &ServeArgs,
storage_dir: &Path,
announce: bool,
styles: Option<&Styles>,
printer: Printer,
) -> Result<()> {
let lock_file = acquire_lock(storage_dir).await?;
@ -386,6 +397,15 @@ async fn execute_daemon(
pid,
record.bind
);
if let Bind::Tcp(addr) = &record.bind {
let url = format!("http://{addr}");
let styled = match styles {
Some(s) => format!("{}", s.cyan.apply_to(&url)),
None => url,
};
fabro_util::printerr!(printer, "Web UI: {styled}");
}
print_auth_methods(printer, serve_args);
print_dev_token(printer, &home, &token);
}
return Ok(());
@ -415,6 +435,17 @@ async fn execute_daemon(
bail!("Server did not become ready within {timeout:?}");
}
fn print_auth_methods(printer: Printer, serve_args: &ServeArgs) {
let settings = load_settings_config(serve_args.config.as_deref()).ok();
let auth_methods = settings
.as_ref()
.and_then(|s| resolve_server_from_file(s).ok())
.map(|s| s.auth.methods)
.unwrap_or_default();
let names: Vec<&str> = auth_methods.iter().map(|m| auth_method_name(*m)).collect();
fabro_util::printerr!(printer, "Auth: {}", names.join(", "));
}
fn print_dev_token(printer: Printer, home: &Home, token: &str) {
fabro_util::printerr!(printer, "Dev token: {token}");
fabro_util::printerr!(printer, "Token file: {}", home.dev_token_path().display());

View file

@ -276,6 +276,8 @@ fn start_with_tcp_host_only_bind_warns_and_falls_back_when_default_port_is_unava
----- stderr -----
Warning: TCP port 32276 is unavailable on 127.0.0.1; falling back to a random port.
Server started (pid [PID]) on [TCP_BIND]
Web UI: http://[TCP_BIND]
Auth: dev-token
Dev token: fabro_dev_[DEV_TOKEN]
Token file: [HOME_DIR]/.fabro/dev-token
");

View file

@ -32,6 +32,7 @@ fn start_status_stop_lifecycle() {
----- stdout -----
----- stderr -----
Server started (pid [PID]) on [SOCKET_PATH]
Auth: dev-token
Dev token: fabro_dev_[DEV_TOKEN]
Token file: [HOME_DIR]/.fabro/dev-token
");