feat: add BREWLOG_ADMIN_USERNAME to select admin username on first start

This commit is contained in:
Jon Seager 2025-11-26 16:27:50 +00:00
parent 0e06d4c5dc
commit 4358fa63dc
No known key found for this signature in database
5 changed files with 20 additions and 6 deletions

View file

@ -20,13 +20,13 @@ B{rew}log ships as one executable. You decide whether it acts as a server or a c
### First-time setup
On first start, you must set an admin password via the `BREWLOG_ADMIN_PASSWORD` environment variable:
On first start, you must set an admin username and password via the `BREWLOG_ADMIN_USERNAME` and `BREWLOG_ADMIN_PASSWORD` environment variables:
```bash
BREWLOG_ADMIN_PASSWORD="your-secure-password" brewlog serve
BREWLOG_ADMIN_USERNAME="admin" BREWLOG_ADMIN_PASSWORD="your-secure-password" brewlog serve
```
This creates the admin user in the database. On subsequent starts, the environment variable is not required.
This creates the admin user in the database. On subsequent starts, the environment variables are not required.
### Authentication

View file

@ -26,6 +26,7 @@ pub struct ServerConfig {
pub bind_address: SocketAddr,
pub database_url: String,
pub admin_password: Option<String>,
pub admin_username: Option<String>,
}
#[derive(Clone)]
@ -75,7 +76,7 @@ pub async fn serve(config: ServerConfig) -> anyhow::Result<()> {
Arc::new(SqlSessionRepository::new(database.clone_pool()));
// Bootstrap admin user if no users exist
bootstrap_admin_user(&user_repo, config.admin_password).await?;
bootstrap_admin_user(&user_repo, config.admin_username, config.admin_password).await?;
let state = AppState::new(
roaster_repo,
@ -106,6 +107,7 @@ pub async fn serve(config: ServerConfig) -> anyhow::Result<()> {
async fn bootstrap_admin_user(
user_repo: &Arc<dyn UserRepository>,
admin_username: Option<String>,
admin_password: Option<String>,
) -> anyhow::Result<()> {
// Check if any users exist
@ -120,6 +122,13 @@ async fn bootstrap_admin_user(
}
// No users exist - we need to create the admin user
let username = admin_username.ok_or_else(|| {
anyhow::anyhow!(
"No users exist in the database. Please provide BREWLOG_ADMIN_USERNAME \
environment variable to create the admin user."
)
})?;
let password = admin_password.ok_or_else(|| {
anyhow::anyhow!(
"No users exist in the database. Please provide BREWLOG_ADMIN_PASSWORD \
@ -127,11 +136,11 @@ async fn bootstrap_admin_user(
)
})?;
info!("No users found. Creating admin user...");
info!("No users found. Creating admin user '{}'...", username);
let password_hash = hash_password(&password).context("failed to hash admin password")?;
let admin_user = NewUser::new("admin".to_string(), password_hash);
let admin_user = NewUser::new(username, password_hash);
user_repo
.insert(admin_user)

View file

@ -50,6 +50,7 @@ async fn run_server(command: ServeCommand) -> Result<()> {
bind_address: command.bind_address,
database_url: command.database_url,
admin_password: command.admin_password,
admin_username: command.admin_username,
};
serve(config).await

View file

@ -74,6 +74,9 @@ pub struct ServeCommand {
#[arg(long, env = "BREWLOG_ADMIN_PASSWORD")]
pub admin_password: Option<String>,
#[arg(long, env = "BREWLOG_ADMIN_USERNAME")]
pub admin_username: Option<String>,
}
pub(crate) fn print_json<T>(value: &T) -> anyhow::Result<()>

View file

@ -58,6 +58,7 @@ fn ensure_server_started() -> Result<(String, String), String> {
&db_url,
])
.env("BREWLOG_ADMIN_PASSWORD", admin_password)
.env("BREWLOG_ADMIN_USERNAME", "admin")
.env("RUST_LOG", "error")
.stdout(Stdio::null())
.stderr(Stdio::null())