diff --git a/.env.sample b/.env.sample index f008f04..cc1df28 100644 --- a/.env.sample +++ b/.env.sample @@ -1 +1 @@ -DATABASE_URL=postgresql://miniweb:miniweb@localhost:5432/miniweb +DATABASE_URL=postgresql://miniweb:miniweb@localhost:54321/miniweb diff --git a/Justfile b/Justfile index 7c8481e..dae133e 100644 --- a/Justfile +++ b/Justfile @@ -30,7 +30,7 @@ pg-down: # Run Migrations migrate: - sea-orm-cli up + sea-orm-cli migrate # Install Developer dependencies dev-install: diff --git a/TODOS.md b/TODOS.md index 4365c7a..baec18c 100644 --- a/TODOS.md +++ b/TODOS.md @@ -54,3 +54,13 @@ Probably options should be transmitted via JSON data. ``` - + +## Error System for Repository + +Repository needs a good error system, and Results instead of just returning data; +These errors should encompass: + - Validation Errors: these should allow forms to display issues, but not disrupt the flow + - Wrong Data / Not Authorized and other 400 base + - Not Found Error: for 404s + - Internal Errors: for 500s + \ No newline at end of file diff --git a/entity/src/user.rs b/entity/src/user.rs index 7af86a0..2c49a84 100644 --- a/entity/src/user.rs +++ b/entity/src/user.rs @@ -15,4 +15,4 @@ pub struct Model { #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation {} -impl ActiveModelBehavior for ActiveModel {} \ No newline at end of file +impl ActiveModelBehavior for ActiveModel {} diff --git a/migration/src/m20220101_000001_create_table.rs b/migration/src/m20220101_000001_create_table.rs index fb589df..b2392dc 100644 --- a/migration/src/m20220101_000001_create_table.rs +++ b/migration/src/m20220101_000001_create_table.rs @@ -9,17 +9,17 @@ impl MigrationTrait for Migration { manager .create_table( Table::create() - .table(User::Table) + .table(Users::Table) .if_not_exists() .col( - ColumnDef::new(User::Id) + ColumnDef::new(Users::Id) .integer() .not_null() .auto_increment() .primary_key(), ) - .col(ColumnDef::new(User::Username).string().not_null()) - .col(ColumnDef::new(User::Description).string().not_null()) + .col(ColumnDef::new(Users::Username).string().not_null()) + .col(ColumnDef::new(Users::Description).text()) .to_owned(), ) .await @@ -27,13 +27,13 @@ impl MigrationTrait for Migration { async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { manager - .drop_table(Table::drop().table(User::Table).to_owned()) + .drop_table(Table::drop().table(Users::Table).to_owned()) .await } } #[derive(DeriveIden)] -enum User { +enum Users { Table, Id, Username, diff --git a/src/admin_examples/static_repository.rs b/src/admin_examples/static_repository.rs index 18c864b..1ac82d0 100644 --- a/src/admin_examples/static_repository.rs +++ b/src/admin_examples/static_repository.rs @@ -156,7 +156,7 @@ impl AdminRepository for MyStaticRepository { } } -pub fn register_example(registry: &mut AdminRegistry) { +pub fn register(registry: &mut AdminRegistry) { let app_key = registry.register_app("Example App"); let repo = MyStaticRepository::new(); let model_config = AdminModelConfig { diff --git a/src/admin_examples/user_repository.rs b/src/admin_examples/user_repository.rs index 5d2b8ae..0f98578 100644 --- a/src/admin_examples/user_repository.rs +++ b/src/admin_examples/user_repository.rs @@ -4,7 +4,7 @@ use crate::admin::state::AdminRegistry; use async_trait::async_trait; use entity; use log::{debug, warn}; -use sea_orm::{Database, DatabaseConnection, EntityTrait}; +use sea_orm::{ActiveModelTrait, Database, DatabaseConnection, EntityTrait, Set}; use serde_json::{json, Value}; struct UserRepository { @@ -25,32 +25,48 @@ impl AdminRepository for UserRepository { RepoInfo { name: "User", lookup_key: "id", - display_list: &["id"], - fields: &[], + display_list: &["id", "username"], + fields: &["username", "description"], } .into() } async fn get(&self, model: &RepositoryContext, id: LookupKey) -> Option { + if let LookupKey::Integer(id) = id { + let id: i32 = id as i32; // use try_into() instead. + let get_user = entity::User::find_by_id(id).one(&self.connection).await; + match get_user { + Ok(get_user) => { + if let Some(user) = get_user { + let id = user.id.to_string(); + match serde_json::to_value(&user) { + Ok(item) => { + return Some(model.build_item(&*id, item)); + } + Err(_) => return None, + } + } + } + Err(_) => return None, + } + } None } async fn list(&self, model: &RepositoryContext) -> RepositoryList { - let results = entity::User::find() - .all(&self.connection) - .await - .expect("Error loading users."); + let results = if let Ok(results) = entity::User::find().all(&self.connection).await { + results + } else { + return RepositoryList::Empty; + }; let repository_items: Vec = results .into_iter() - .filter_map(|item| { - match serde_json::to_value(item) { - Ok(fields) => Some(RepositoryItem { - fields, - detail_url: None, // replace with actual value if available - change_url: None, // replace with actual value if available - }), - Err(_) => None, + .filter_map(|item| match serde_json::to_value(&item) { + Ok(fields) => { + let id = item.id.to_string(); + Some(model.build_item(&*id, fields)) } + Err(_) => None, }) .collect(); RepositoryList::List { @@ -58,11 +74,22 @@ impl AdminRepository for UserRepository { } } - async fn create( - &mut self, - model: &RepositoryContext, - mut data: Value, - ) -> Option { + async fn create(&mut self, model: &RepositoryContext, data: Value) -> Option { + let username = data.get("username").unwrap().as_str().unwrap(); + + let mut user = entity::user::ActiveModel { + username: Set(username.to_owned()), + ..Default::default() + }; + + if let Some(value) = data.get("description") { + user.description = Set(value.as_str().map(|s| s.to_owned())); + } + + if let Ok(user) = user.insert(&self.connection).await { + let id = user.id.to_string(); + return Some(model.build_item(&*id, serde_json::to_value(&user).unwrap())); + } None } @@ -88,3 +115,17 @@ impl AdminRepository for UserRepository { None } } + +pub fn register(registry: &mut AdminRegistry, db: DatabaseConnection) { + let app_key = registry.register_app("Auth"); + let repo = UserRepository::new(db); + let model_config = AdminModelConfig { + app_key: app_key, + name: "User".to_owned(), + }; + let model_result = registry.register_model(model_config, repo); + match model_result { + Err(err) => panic!("{}", err), + _ => (), + } +} diff --git a/src/bin/list_users.rs b/src/bin/list_users.rs index 45292da..c5989e3 100644 --- a/src/bin/list_users.rs +++ b/src/bin/list_users.rs @@ -1,6 +1,6 @@ use dotenvy::dotenv; use entity; -use sea_orm::{DatabaseConnection, Database, EntityTrait}; +use sea_orm::{Database, DatabaseConnection, EntityTrait}; use std::env; pub async fn establish_connection() -> DatabaseConnection { @@ -10,13 +10,14 @@ pub async fn establish_connection() -> DatabaseConnection { db.unwrap_or_else(|_| panic!("Error connecting to {}", database_url)) } - #[tokio::main] async fn main() { let connection = establish_connection().await; let results = entity::User::find() - .all(&connection).await.expect("Error loading users."); + .all(&connection) + .await + .expect("Error loading users."); println!("Displaying {} users", results.len()); for user in results { diff --git a/src/main.rs b/src/main.rs index 7b01dac..840be3f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ mod state; use crate::service::{handlers, templates}; use crate::state::AppState; use admin_examples::static_repository; +use admin_examples::user_repository; use axum::{ body::Bytes, extract::MatchedPath, @@ -34,6 +35,12 @@ async fn hello_world(templates: State) -> impl IntoRespons templates.render_html("hello_world.html", ()) } +pub async fn establish_connection() -> sea_orm::DatabaseConnection { + let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set"); + let db = sea_orm::Database::connect(&database_url).await; + db.unwrap_or_else(|_| panic!("Error connecting to {}", database_url)) +} + #[tokio::main] async fn main() { // Load environment @@ -41,10 +48,13 @@ async fn main() { env_logger::init(); info!("Miniweb starting..."); + let db_connection = establish_connection().await; + // Prepare App State let tmpl = templates::Templates::initialize().expect("Template Engine could not be loaded."); let mut admin = admin::state::AdminRegistry::new("admin"); - static_repository::register_example(&mut admin); + static_repository::register(&mut admin); + user_repository::register(&mut admin, db_connection); let state: AppState = AppState { templates: tmpl,