use async_trait::async_trait; use loco_rs::prelude::*; use loco_rs::hash; use sea_orm::{ActiveModelTrait, IntoActiveModel, Set}; use crate::models::users::{self, RegisterParams}; pub struct AdminSeeder; #[async_trait] impl Initializer for AdminSeeder { fn name(&self) -> String { "admin-seeder".to_string() } async fn before_run(&self, ctx: &AppContext) -> Result<()> { let email = std::env::var("ADMIN_EMAIL").unwrap_or_default(); let password = std::env::var("ADMIN_PASSWORD").unwrap_or_default(); let name = std::env::var("ADMIN_NAME").unwrap_or_else(|_| "Admin".to_string()); if email.is_empty() || password.is_empty() { tracing::warn!("ADMIN_EMAIL / ADMIN_PASSWORD not set in .env; admin not seeded"); return Ok(()); } if let Ok(user) = users::Model::find_by_email(&ctx.db, &email).await { // User exists — update password so .env is always the source of truth. let hash = hash::hash_password(&password) .map_err(|e| Error::Message(e.to_string()))?; let mut am = user.into_active_model(); am.password = Set(hash); am.name = Set(name); am.update(&ctx.db).await?; tracing::info!(admin = %email, "admin password synced from .env"); } else { users::Model::create_with_password( &ctx.db, &RegisterParams { email: email.clone(), password, name, }, ) .await?; tracing::info!(admin = %email, "admin user seeded"); } Ok(()) } }