summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDawid Rycerz <dawid@rycerz.xyz>2025-07-21 20:37:16 +0300
committerDawid Rycerz <dawid@rycerz.xyz>2025-07-21 20:37:16 +0300
commit30f50e5b31294abd75c4b629970ad4865108738d (patch)
tree60521071763769c8a3ad13952808cfc1d0454bcf /src
parent0ef072f64456f9e6a0105bd123987d66434a2254 (diff)
feat: add cli option to create user
Diffstat (limited to 'src')
-rw-r--r--src/cli.rs96
-rw-r--r--src/main.rs182
2 files changed, 235 insertions, 43 deletions
diff --git a/src/cli.rs b/src/cli.rs
new file mode 100644
index 0000000..e6cb7b4
--- /dev/null
+++ b/src/cli.rs
@@ -0,0 +1,96 @@
+use clap::{Parser, Subcommand};
+use std::env;
+use sqlx::SqlitePool;
+use uuid::Uuid;
+
+use silmataivas::users::{UserRepository, UserRole};
+use silmataivas::notifications::{NtfySettingsInput, NtfySettingsRepository};
+use silmataivas::weather_thresholds::WeatherThresholdRepository;
+use tracing::{info, warn, error, debug, trace};
+use tracing_subscriber::{EnvFilter, FmtSubscriber};
+
+#[derive(Parser)]
+#[command(name = "silmataivas")]
+struct Cli {
+ /// Increase output verbosity (-v, -vv, -vvv)
+ #[arg(short, long, action = clap::ArgAction::Count, global = true)]
+ verbose: u8,
+ #[command(subcommand)]
+ command: Commands,
+}
+
+#[derive(Subcommand)]
+enum Commands {
+ /// Create a new user with optional UUID
+ CreateUser {
+ uuid: Option<String>,
+ },
+}
+
+#[tokio::main]
+async fn main() -> anyhow::Result<()> {
+ let cli = Cli::parse();
+ // Set up logging based on verbosity
+ let filter = match cli.verbose {
+ 0 => "warn",
+ 1 => "info",
+ 2 => "debug",
+ _ => "trace",
+ };
+ let subscriber = FmtSubscriber::builder()
+ .with_env_filter(EnvFilter::new(filter))
+ .finish();
+ tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed");
+ let db_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
+ let pool = SqlitePool::connect(&db_url).await?;
+
+ match cli.command {
+ Commands::CreateUser { uuid } => {
+ let repo = UserRepository { db: &pool };
+ let user_id = uuid.unwrap_or_else(|| Uuid::new_v4().to_string());
+ let user = repo.create_user(Some(user_id.clone()), Some(UserRole::User)).await?;
+
+ // Set up default NTFY settings
+ let ntfy_repo = NtfySettingsRepository { db: &pool };
+ ntfy_repo.create(NtfySettingsInput {
+ user_id: user.id,
+ enabled: true,
+ topic: user_id.clone(),
+ server_url: "https://ntfy.sh".to_string(),
+ priority: 3,
+ title_template: None,
+ message_template: None,
+ }).await?;
+
+ // Set up default weather thresholds
+ let threshold_repo = WeatherThresholdRepository { db: &pool };
+ threshold_repo.create_threshold(
+ user.id,
+ "temperature".to_string(),
+ 35.0,
+ ">".to_string(),
+ true,
+ Some("Default: temperature > 35°C".to_string()),
+ ).await?;
+ threshold_repo.create_threshold(
+ user.id,
+ "rain".to_string(),
+ 50.0,
+ ">".to_string(),
+ true,
+ Some("Default: rain > 50mm".to_string()),
+ ).await?;
+ threshold_repo.create_threshold(
+ user.id,
+ "wind_speed".to_string(),
+ 80.0,
+ ">".to_string(),
+ true,
+ Some("Default: wind speed > 80km/h".to_string()),
+ ).await?;
+
+ info!("User {} created", user_id);
+ }
+ }
+ Ok(())
+} \ No newline at end of file
diff --git a/src/main.rs b/src/main.rs
index 2e4dc75..4e89234 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -3,10 +3,18 @@ use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::routing::{post, put};
use axum::{Router, routing::get};
+use clap::{Parser, Subcommand};
use serde_json::json;
use sqlx::SqlitePool;
+use std::env;
+use std::net::SocketAddr;
+use std::sync::Arc;
+use tokio::fs;
+use tracing::{error, info};
+use tracing_subscriber::{EnvFilter, FmtSubscriber};
use utoipa::OpenApi;
use utoipa_swagger_ui::SwaggerUi;
+use uuid::Uuid;
mod auth;
mod health;
@@ -894,55 +902,143 @@ pub fn app_with_state(pool: std::sync::Arc<SqlitePool>) -> Router {
.with_state(pool)
}
-use std::env;
-use std::net::SocketAddr;
-use std::sync::Arc;
-use tokio::fs;
-use uuid::Uuid;
+#[derive(Parser)]
+#[command(name = "silmataivas")]
+struct Cli {
+ /// Increase output verbosity (-v, -vv, -vvv)
+ #[arg(short, long, action = clap::ArgAction::Count, global = true)]
+ verbose: u8,
+ #[command(subcommand)]
+ command: Option<Commands>,
+}
+
+#[derive(Subcommand)]
+enum Commands {
+ /// Start the Silmataivas server (default)
+ Server,
+ /// Create a new user with optional UUID
+ CreateUser { uuid: Option<String> },
+}
#[tokio::main]
-async fn main() {
- // Set up database path
- let db_path =
- env::var("DATABASE_URL").unwrap_or_else(|_| "sqlite://./data/silmataivas.db".to_string());
- // Ensure data directory exists
- let _ = fs::create_dir_all("./data").await;
- // Connect to SQLite
- let pool = SqlitePool::connect(&db_path)
- .await
- .expect("Failed to connect to DB");
-
- // Create initial admin user if none exists
- {
- let repo = users::UserRepository { db: &pool };
- match repo.any_admin_exists().await {
- Ok(false) => {
- let admin_token =
- env::var("ADMIN_TOKEN").unwrap_or_else(|_| Uuid::new_v4().to_string());
- match repo
- .create_user(Some(admin_token.clone()), Some(users::UserRole::Admin))
- .await
- {
- Ok(_) => println!("Initial admin user created. Token: {admin_token}"),
- Err(e) => eprintln!("Failed to create initial admin user: {e}"),
+async fn main() -> anyhow::Result<()> {
+ let cli = Cli::parse();
+ // Set up logging based on verbosity
+ let filter = match cli.verbose {
+ 0 => "warn",
+ 1 => "info",
+ 2 => "debug",
+ _ => "trace",
+ };
+ let subscriber = FmtSubscriber::builder()
+ .with_env_filter(EnvFilter::new(filter))
+ .finish();
+ tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed");
+ match cli.command.unwrap_or(Commands::Server) {
+ Commands::Server => {
+ // Set up database path
+ let db_path = env::var("DATABASE_URL")
+ .unwrap_or_else(|_| "sqlite://./data/silmataivas.db".to_string());
+ // Ensure data directory exists
+ let _ = fs::create_dir_all("./data").await;
+ // Connect to SQLite
+ let pool = SqlitePool::connect(&db_path)
+ .await
+ .expect("Failed to connect to DB");
+
+ // Create initial admin user if none exists
+ {
+ let repo = users::UserRepository { db: &pool };
+ match repo.any_admin_exists().await {
+ Ok(false) => {
+ let admin_token =
+ env::var("ADMIN_TOKEN").unwrap_or_else(|_| Uuid::new_v4().to_string());
+ match repo
+ .create_user(Some(admin_token.clone()), Some(users::UserRole::Admin))
+ .await
+ {
+ Ok(_) => info!("Initial admin user created. Token: {}", admin_token),
+ Err(e) => error!("Failed to create initial admin user: {}", e),
+ }
+ }
+ Ok(true) => {
+ // At least one admin exists, do nothing
+ }
+ Err(e) => {
+ error!("Failed to check for existing admin users: {}", e);
+ }
}
}
- Ok(true) => {
- // At least one admin exists, do nothing
- }
- Err(e) => {
- eprintln!("Failed to check for existing admin users: {e}");
- }
+
+ let app = app_with_state(Arc::new(pool));
+ let addr = SocketAddr::from(([0, 0, 0, 0], 4000));
+ let listener = tokio::net::TcpListener::bind(addr)
+ .await
+ .expect("Failed to bind address");
+ info!("Listening on {}", listener.local_addr().unwrap());
+ axum::serve(listener, app).await.unwrap();
}
- }
+ Commands::CreateUser { uuid } => {
+ let db_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
+ let pool = SqlitePool::connect(&db_url).await?;
+ let repo = crate::users::UserRepository { db: &pool };
+ let user_id = uuid.unwrap_or_else(|| Uuid::new_v4().to_string());
+ let user = repo
+ .create_user(Some(user_id.clone()), Some(crate::users::UserRole::User))
+ .await?;
- let app = app_with_state(Arc::new(pool));
- let addr = SocketAddr::from(([0, 0, 0, 0], 4000));
- let listener = tokio::net::TcpListener::bind(addr)
- .await
- .expect("Failed to bind address");
- println!("Listening on {}", listener.local_addr().unwrap());
- axum::serve(listener, app).await.unwrap();
+ // Set up default NTFY settings
+ let ntfy_repo = crate::notifications::NtfySettingsRepository { db: &pool };
+ ntfy_repo
+ .create(crate::notifications::NtfySettingsInput {
+ user_id: user.id,
+ enabled: true,
+ topic: user_id.clone(),
+ server_url: "https://ntfy.sh".to_string(),
+ priority: 3,
+ title_template: None,
+ message_template: None,
+ })
+ .await?;
+
+ // Set up default weather thresholds
+ let threshold_repo =
+ crate::weather_thresholds::WeatherThresholdRepository { db: &pool };
+ threshold_repo
+ .create_threshold(
+ user.id,
+ "temperature".to_string(),
+ 35.0,
+ ">".to_string(),
+ true,
+ Some("Default: temperature > 35°C".to_string()),
+ )
+ .await?;
+ threshold_repo
+ .create_threshold(
+ user.id,
+ "rain".to_string(),
+ 50.0,
+ ">".to_string(),
+ true,
+ Some("Default: rain > 50mm".to_string()),
+ )
+ .await?;
+ threshold_repo
+ .create_threshold(
+ user.id,
+ "wind_speed".to_string(),
+ 80.0,
+ ">".to_string(),
+ true,
+ Some("Default: wind speed > 80km/h".to_string()),
+ )
+ .await?;
+
+ info!("User {} created", user_id);
+ }
+ }
+ Ok(())
}
#[cfg(test)]