From ef1132cbe163a301dcc4f6508c93b96123872032 Mon Sep 17 00:00:00 2001 From: Sanford Pun Date: Wed, 7 Sep 2022 22:40:40 +0100 Subject: [PATCH] Update Actix3 example --- examples/actix3_example/Cargo.toml | 27 +-- examples/actix3_example/api/Cargo.toml | 22 ++ examples/actix3_example/api/src/lib.rs | 219 +++++++++++++++++ .../{ => api}/static/css/normalize.css | 0 .../{ => api}/static/css/skeleton.css | 0 .../{ => api}/static/css/style.css | 0 .../{ => api}/static/images/favicon.png | Bin .../{ => api}/templates/edit.html.tera | 0 .../{ => api}/templates/error/404.html.tera | 0 .../{ => api}/templates/index.html.tera | 0 .../{ => api}/templates/layout.html.tera | 0 .../{ => api}/templates/new.html.tera | 0 examples/actix3_example/core/Cargo.toml | 30 +++ examples/actix3_example/core/src/lib.rs | 7 + examples/actix3_example/core/src/mutation.rs | 53 ++++ examples/actix3_example/core/src/query.rs | 26 ++ examples/actix3_example/core/tests/mock.rs | 79 ++++++ examples/actix3_example/core/tests/prepare.rs | 50 ++++ examples/actix3_example/src/main.rs | 228 +----------------- 19 files changed, 490 insertions(+), 251 deletions(-) create mode 100644 examples/actix3_example/api/Cargo.toml create mode 100644 examples/actix3_example/api/src/lib.rs rename examples/actix3_example/{ => api}/static/css/normalize.css (100%) rename examples/actix3_example/{ => api}/static/css/skeleton.css (100%) rename examples/actix3_example/{ => api}/static/css/style.css (100%) rename examples/actix3_example/{ => api}/static/images/favicon.png (100%) rename examples/actix3_example/{ => api}/templates/edit.html.tera (100%) rename examples/actix3_example/{ => api}/templates/error/404.html.tera (100%) rename examples/actix3_example/{ => api}/templates/index.html.tera (100%) rename examples/actix3_example/{ => api}/templates/layout.html.tera (100%) rename examples/actix3_example/{ => api}/templates/new.html.tera (100%) create mode 100644 examples/actix3_example/core/Cargo.toml create mode 100644 examples/actix3_example/core/src/lib.rs create mode 100644 examples/actix3_example/core/src/mutation.rs create mode 100644 examples/actix3_example/core/src/query.rs create mode 100644 examples/actix3_example/core/tests/mock.rs create mode 100644 examples/actix3_example/core/tests/prepare.rs diff --git a/examples/actix3_example/Cargo.toml b/examples/actix3_example/Cargo.toml index 8d1f8d6f6..a449de4ca 100644 --- a/examples/actix3_example/Cargo.toml +++ b/examples/actix3_example/Cargo.toml @@ -6,30 +6,7 @@ edition = "2021" publish = false [workspace] -members = [".", "entity", "migration"] +members = [".", "api", "core", "entity", "migration"] [dependencies] -actix-http = "2" -actix-web = "3" -actix-flash = "0.2" -actix-files = "0.5" -futures = { version = "^0.3" } -futures-util = { version = "^0.3" } -tera = "1.8.0" -dotenv = "0.15" -listenfd = "0.3.3" -serde = "1" -tracing-subscriber = { version = "0.3", features = ["env-filter"] } -entity = { path = "entity" } -migration = { path = "migration" } - -[dependencies.sea-orm] -path = "../../" # remove this line in your own project -version = "^0.9.0" # sea-orm version -features = [ - "debug-print", - "runtime-async-std-native-tls", - "sqlx-mysql", - # "sqlx-postgres", - # "sqlx-sqlite", -] +actix3-example-api = { path = "api" } diff --git a/examples/actix3_example/api/Cargo.toml b/examples/actix3_example/api/Cargo.toml new file mode 100644 index 000000000..5f38712e0 --- /dev/null +++ b/examples/actix3_example/api/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "actix3-example-api" +version = "0.1.0" +authors = ["Sam Samai "] +edition = "2021" +publish = false + +[dependencies] +actix3-example-core = { path = "../core" } +actix-http = "2" +actix-web = "3" +actix-flash = "0.2" +actix-files = "0.5" +futures = { version = "^0.3" } +futures-util = { version = "^0.3" } +tera = "1.8.0" +dotenv = "0.15" +listenfd = "0.3.3" +serde = "1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +entity = { path = "../entity" } +migration = { path = "../migration" } diff --git a/examples/actix3_example/api/src/lib.rs b/examples/actix3_example/api/src/lib.rs new file mode 100644 index 000000000..d5160f876 --- /dev/null +++ b/examples/actix3_example/api/src/lib.rs @@ -0,0 +1,219 @@ +use actix3_example_core::{ + sea_orm::{Database, DatabaseConnection}, + Mutation, Query, +}; +use actix_files as fs; +use actix_web::{ + error, get, middleware, post, web, App, Error, HttpRequest, HttpResponse, HttpServer, Result, +}; + +use entity::post; +use listenfd::ListenFd; +use migration::{Migrator, MigratorTrait}; +use serde::{Deserialize, Serialize}; +use std::env; +use tera::Tera; + +const DEFAULT_POSTS_PER_PAGE: usize = 5; + +#[derive(Debug, Clone)] +struct AppState { + templates: tera::Tera, + conn: DatabaseConnection, +} +#[derive(Debug, Deserialize)] +pub struct Params { + page: Option, + posts_per_page: Option, +} + +#[derive(Deserialize, Serialize, Debug, Clone)] +struct FlashData { + kind: String, + message: String, +} + +#[get("/")] +async fn list( + req: HttpRequest, + data: web::Data, + opt_flash: Option>, +) -> Result { + let template = &data.templates; + let conn = &data.conn; + + // get params + let params = web::Query::::from_query(req.query_string()).unwrap(); + + let page = params.page.unwrap_or(1); + let posts_per_page = params.posts_per_page.unwrap_or(DEFAULT_POSTS_PER_PAGE); + + let (posts, num_pages) = Query::find_posts_in_page(conn, page, posts_per_page) + .await + .expect("Cannot find posts in page"); + + let mut ctx = tera::Context::new(); + ctx.insert("posts", &posts); + ctx.insert("page", &page); + ctx.insert("posts_per_page", &posts_per_page); + ctx.insert("num_pages", &num_pages); + + if let Some(flash) = opt_flash { + let flash_inner = flash.into_inner(); + ctx.insert("flash", &flash_inner); + } + + let body = template + .render("index.html.tera", &ctx) + .map_err(|_| error::ErrorInternalServerError("Template error"))?; + Ok(HttpResponse::Ok().content_type("text/html").body(body)) +} + +#[get("/new")] +async fn new(data: web::Data) -> Result { + let template = &data.templates; + let ctx = tera::Context::new(); + let body = template + .render("new.html.tera", &ctx) + .map_err(|_| error::ErrorInternalServerError("Template error"))?; + Ok(HttpResponse::Ok().content_type("text/html").body(body)) +} + +#[post("/")] +async fn create( + data: web::Data, + post_form: web::Form, +) -> actix_flash::Response { + let conn = &data.conn; + + let form = post_form.into_inner(); + + Mutation::create_post(conn, form) + .await + .expect("could not insert post"); + + let flash = FlashData { + kind: "success".to_owned(), + message: "Post successfully added.".to_owned(), + }; + + actix_flash::Response::with_redirect(flash, "/") +} + +#[get("/{id}")] +async fn edit(data: web::Data, id: web::Path) -> Result { + let conn = &data.conn; + let template = &data.templates; + let id = id.into_inner(); + + let post: post::Model = Query::find_post_by_id(conn, id) + .await + .expect("could not find post") + .unwrap_or_else(|| panic!("could not find post with id {}", id)); + + let mut ctx = tera::Context::new(); + ctx.insert("post", &post); + + let body = template + .render("edit.html.tera", &ctx) + .map_err(|_| error::ErrorInternalServerError("Template error"))?; + Ok(HttpResponse::Ok().content_type("text/html").body(body)) +} + +#[post("/{id}")] +async fn update( + data: web::Data, + id: web::Path, + post_form: web::Form, +) -> actix_flash::Response { + let conn = &data.conn; + let form = post_form.into_inner(); + let id = id.into_inner(); + + Mutation::update_post_by_id(conn, id, form) + .await + .expect("could not edit post"); + + let flash = FlashData { + kind: "success".to_owned(), + message: "Post successfully updated.".to_owned(), + }; + + actix_flash::Response::with_redirect(flash, "/") +} + +#[post("/delete/{id}")] +async fn delete( + data: web::Data, + id: web::Path, +) -> actix_flash::Response { + let conn = &data.conn; + let id = id.into_inner(); + + Mutation::delete_post(conn, id) + .await + .expect("could not delete post"); + + let flash = FlashData { + kind: "success".to_owned(), + message: "Post successfully deleted.".to_owned(), + }; + + actix_flash::Response::with_redirect(flash, "/") +} + +#[actix_web::main] +async fn start() -> std::io::Result<()> { + std::env::set_var("RUST_LOG", "debug"); + tracing_subscriber::fmt::init(); + + // get env vars + dotenv::dotenv().ok(); + let db_url = env::var("DATABASE_URL").expect("DATABASE_URL is not set in .env file"); + let host = env::var("HOST").expect("HOST is not set in .env file"); + let port = env::var("PORT").expect("PORT is not set in .env file"); + let server_url = format!("{}:{}", host, port); + + // create post table if not exists + let conn = Database::connect(&db_url).await.unwrap(); + Migrator::up(&conn, None).await.unwrap(); + let templates = Tera::new(concat!(env!("CARGO_MANIFEST_DIR"), "/templates/**/*")).unwrap(); + let state = AppState { templates, conn }; + + let mut listenfd = ListenFd::from_env(); + let mut server = HttpServer::new(move || { + App::new() + .data(state.clone()) + .wrap(middleware::Logger::default()) // enable logger + .wrap(actix_flash::Flash::default()) + .configure(init) + .service(fs::Files::new("/static", "./api/static").show_files_listing()) + }); + + server = match listenfd.take_tcp_listener(0)? { + Some(listener) => server.listen(listener)?, + None => server.bind(&server_url)?, + }; + + println!("Starting server at {}", server_url); + server.run().await?; + + Ok(()) +} + +fn init(cfg: &mut web::ServiceConfig) { + cfg.service(list); + cfg.service(new); + cfg.service(create); + cfg.service(edit); + cfg.service(update); + cfg.service(delete); +} + +pub fn main() { + let result = start(); + + if let Some(err) = result.err() { + println!("Error: {}", err) + } +} diff --git a/examples/actix3_example/static/css/normalize.css b/examples/actix3_example/api/static/css/normalize.css similarity index 100% rename from examples/actix3_example/static/css/normalize.css rename to examples/actix3_example/api/static/css/normalize.css diff --git a/examples/actix3_example/static/css/skeleton.css b/examples/actix3_example/api/static/css/skeleton.css similarity index 100% rename from examples/actix3_example/static/css/skeleton.css rename to examples/actix3_example/api/static/css/skeleton.css diff --git a/examples/actix3_example/static/css/style.css b/examples/actix3_example/api/static/css/style.css similarity index 100% rename from examples/actix3_example/static/css/style.css rename to examples/actix3_example/api/static/css/style.css diff --git a/examples/actix3_example/static/images/favicon.png b/examples/actix3_example/api/static/images/favicon.png similarity index 100% rename from examples/actix3_example/static/images/favicon.png rename to examples/actix3_example/api/static/images/favicon.png diff --git a/examples/actix3_example/templates/edit.html.tera b/examples/actix3_example/api/templates/edit.html.tera similarity index 100% rename from examples/actix3_example/templates/edit.html.tera rename to examples/actix3_example/api/templates/edit.html.tera diff --git a/examples/actix3_example/templates/error/404.html.tera b/examples/actix3_example/api/templates/error/404.html.tera similarity index 100% rename from examples/actix3_example/templates/error/404.html.tera rename to examples/actix3_example/api/templates/error/404.html.tera diff --git a/examples/actix3_example/templates/index.html.tera b/examples/actix3_example/api/templates/index.html.tera similarity index 100% rename from examples/actix3_example/templates/index.html.tera rename to examples/actix3_example/api/templates/index.html.tera diff --git a/examples/actix3_example/templates/layout.html.tera b/examples/actix3_example/api/templates/layout.html.tera similarity index 100% rename from examples/actix3_example/templates/layout.html.tera rename to examples/actix3_example/api/templates/layout.html.tera diff --git a/examples/actix3_example/templates/new.html.tera b/examples/actix3_example/api/templates/new.html.tera similarity index 100% rename from examples/actix3_example/templates/new.html.tera rename to examples/actix3_example/api/templates/new.html.tera diff --git a/examples/actix3_example/core/Cargo.toml b/examples/actix3_example/core/Cargo.toml new file mode 100644 index 000000000..2c4c3f39b --- /dev/null +++ b/examples/actix3_example/core/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "actix3-example-core" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +entity = { path = "../entity" } + +[dependencies.sea-orm] +path = "../../../" # remove this line in your own project +version = "^0.9.0" # sea-orm version +features = [ + "debug-print", + "runtime-async-std-native-tls", + "sqlx-mysql", + # "sqlx-postgres", + # "sqlx-sqlite", +] + +[dev-dependencies] +tokio = { version = "1.20.0", features = ["macros", "rt"] } + +[features] +mock = ["sea-orm/mock"] + +[[test]] +name = "mock" +required-features = ["mock"] diff --git a/examples/actix3_example/core/src/lib.rs b/examples/actix3_example/core/src/lib.rs new file mode 100644 index 000000000..4a80f2391 --- /dev/null +++ b/examples/actix3_example/core/src/lib.rs @@ -0,0 +1,7 @@ +mod mutation; +mod query; + +pub use mutation::*; +pub use query::*; + +pub use sea_orm; diff --git a/examples/actix3_example/core/src/mutation.rs b/examples/actix3_example/core/src/mutation.rs new file mode 100644 index 000000000..7f0150a63 --- /dev/null +++ b/examples/actix3_example/core/src/mutation.rs @@ -0,0 +1,53 @@ +use ::entity::{post, post::Entity as Post}; +use sea_orm::*; + +pub struct Mutation; + +impl Mutation { + pub async fn create_post( + db: &DbConn, + form_data: post::Model, + ) -> Result { + post::ActiveModel { + title: Set(form_data.title.to_owned()), + text: Set(form_data.text.to_owned()), + ..Default::default() + } + .save(db) + .await + } + + pub async fn update_post_by_id( + db: &DbConn, + id: i32, + form_data: post::Model, + ) -> Result { + let post: post::ActiveModel = if let Ok(Some(post)) = Post::find_by_id(id).one(db).await { + post.into() + } else { + return Err(DbErr::Custom("Cannot find post.".to_owned())); + }; + + post::ActiveModel { + id: post.id, + title: Set(form_data.title.to_owned()), + text: Set(form_data.text.to_owned()), + } + .update(db) + .await + } + + pub async fn delete_post(db: &DbConn, id: i32) -> Result { + let post: post::ActiveModel = if let Ok(Some(post)) = Post::find_by_id(id).one(db).await { + post.into() + } else { + return Err(DbErr::Custom("Cannot find post.".to_owned())); + }; + + post.delete(db).await + } + + pub async fn delete_all_posts(db: &DbConn) -> Result { + Post::delete_many().exec(db).await + } +} diff --git a/examples/actix3_example/core/src/query.rs b/examples/actix3_example/core/src/query.rs new file mode 100644 index 000000000..f08e80c77 --- /dev/null +++ b/examples/actix3_example/core/src/query.rs @@ -0,0 +1,26 @@ +use ::entity::{post, post::Entity as Post}; +use sea_orm::*; + +pub struct Query; + +impl Query { + pub async fn find_post_by_id(db: &DbConn, id: i32) -> Result, DbErr> { + Post::find_by_id(id).one(db).await + } + + /// If ok, returns (post models, num pages). + pub async fn find_posts_in_page( + db: &DbConn, + page: usize, + posts_per_page: usize, + ) -> Result<(Vec, usize), DbErr> { + // Setup paginator + let paginator = Post::find() + .order_by_asc(post::Column::Id) + .paginate(db, posts_per_page); + let num_pages = paginator.num_pages().await?; + + // Fetch paginated posts + paginator.fetch_page(page - 1).await.map(|p| (p, num_pages)) + } +} diff --git a/examples/actix3_example/core/tests/mock.rs b/examples/actix3_example/core/tests/mock.rs new file mode 100644 index 000000000..190cb2906 --- /dev/null +++ b/examples/actix3_example/core/tests/mock.rs @@ -0,0 +1,79 @@ +mod prepare; + +use actix3_example_core::{Mutation, Query}; +use entity::post; +use prepare::prepare_mock_db; + +#[tokio::test] +async fn main() { + let db = &prepare_mock_db(); + + { + let post = Query::find_post_by_id(db, 1).await.unwrap().unwrap(); + + assert_eq!(post.id, 1); + } + + { + let post = Query::find_post_by_id(db, 5).await.unwrap().unwrap(); + + assert_eq!(post.id, 5); + } + + { + let post = Mutation::create_post( + db, + post::Model { + id: 0, + title: "Title D".to_owned(), + text: "Text D".to_owned(), + }, + ) + .await + .unwrap(); + + assert_eq!( + post, + post::ActiveModel { + id: sea_orm::ActiveValue::Unchanged(6), + title: sea_orm::ActiveValue::Unchanged("Title D".to_owned()), + text: sea_orm::ActiveValue::Unchanged("Text D".to_owned()) + } + ); + } + + { + let post = Mutation::update_post_by_id( + db, + 1, + post::Model { + id: 1, + title: "New Title A".to_owned(), + text: "New Text A".to_owned(), + }, + ) + .await + .unwrap(); + + assert_eq!( + post, + post::Model { + id: 1, + title: "New Title A".to_owned(), + text: "New Text A".to_owned(), + } + ); + } + + { + let result = Mutation::delete_post(db, 5).await.unwrap(); + + assert_eq!(result.rows_affected, 1); + } + + { + let result = Mutation::delete_all_posts(db).await.unwrap(); + + assert_eq!(result.rows_affected, 5); + } +} diff --git a/examples/actix3_example/core/tests/prepare.rs b/examples/actix3_example/core/tests/prepare.rs new file mode 100644 index 000000000..451804937 --- /dev/null +++ b/examples/actix3_example/core/tests/prepare.rs @@ -0,0 +1,50 @@ +use ::entity::post; +use sea_orm::*; + +#[cfg(feature = "mock")] +pub fn prepare_mock_db() -> DatabaseConnection { + MockDatabase::new(DatabaseBackend::Postgres) + .append_query_results(vec![ + vec![post::Model { + id: 1, + title: "Title A".to_owned(), + text: "Text A".to_owned(), + }], + vec![post::Model { + id: 5, + title: "Title C".to_owned(), + text: "Text C".to_owned(), + }], + vec![post::Model { + id: 6, + title: "Title D".to_owned(), + text: "Text D".to_owned(), + }], + vec![post::Model { + id: 1, + title: "Title A".to_owned(), + text: "Text A".to_owned(), + }], + vec![post::Model { + id: 1, + title: "New Title A".to_owned(), + text: "New Text A".to_owned(), + }], + vec![post::Model { + id: 5, + title: "Title C".to_owned(), + text: "Text C".to_owned(), + }], + ]) + .append_exec_results(vec![ + MockExecResult { + last_insert_id: 6, + rows_affected: 1, + }, + MockExecResult { + last_insert_id: 6, + rows_affected: 5, + }, + ]) + .into_connection() +} diff --git a/examples/actix3_example/src/main.rs b/examples/actix3_example/src/main.rs index 1a6a70223..5a0e63b82 100644 --- a/examples/actix3_example/src/main.rs +++ b/examples/actix3_example/src/main.rs @@ -1,227 +1,3 @@ -use actix_files as fs; -use actix_web::{ - error, get, middleware, post, web, App, Error, HttpRequest, HttpResponse, HttpServer, Result, -}; - -use entity::post; -use entity::post::Entity as Post; -use listenfd::ListenFd; -use migration::{Migrator, MigratorTrait}; -use sea_orm::DatabaseConnection; -use sea_orm::{entity::*, query::*}; -use serde::{Deserialize, Serialize}; -use std::env; -use tera::Tera; - -const DEFAULT_POSTS_PER_PAGE: usize = 5; - -#[derive(Debug, Clone)] -struct AppState { - templates: tera::Tera, - conn: DatabaseConnection, -} -#[derive(Debug, Deserialize)] -pub struct Params { - page: Option, - posts_per_page: Option, -} - -#[derive(Deserialize, Serialize, Debug, Clone)] -struct FlashData { - kind: String, - message: String, -} - -#[get("/")] -async fn list( - req: HttpRequest, - data: web::Data, - opt_flash: Option>, -) -> Result { - let template = &data.templates; - let conn = &data.conn; - - // get params - let params = web::Query::::from_query(req.query_string()).unwrap(); - - let page = params.page.unwrap_or(1); - let posts_per_page = params.posts_per_page.unwrap_or(DEFAULT_POSTS_PER_PAGE); - let paginator = Post::find() - .order_by_asc(post::Column::Id) - .paginate(conn, posts_per_page); - let num_pages = paginator.num_pages().await.ok().unwrap(); - - let posts = paginator - .fetch_page(page - 1) - .await - .expect("could not retrieve posts"); - let mut ctx = tera::Context::new(); - ctx.insert("posts", &posts); - ctx.insert("page", &page); - ctx.insert("posts_per_page", &posts_per_page); - ctx.insert("num_pages", &num_pages); - - if let Some(flash) = opt_flash { - let flash_inner = flash.into_inner(); - ctx.insert("flash", &flash_inner); - } - - let body = template - .render("index.html.tera", &ctx) - .map_err(|_| error::ErrorInternalServerError("Template error"))?; - Ok(HttpResponse::Ok().content_type("text/html").body(body)) -} - -#[get("/new")] -async fn new(data: web::Data) -> Result { - let template = &data.templates; - let ctx = tera::Context::new(); - let body = template - .render("new.html.tera", &ctx) - .map_err(|_| error::ErrorInternalServerError("Template error"))?; - Ok(HttpResponse::Ok().content_type("text/html").body(body)) -} - -#[post("/")] -async fn create( - data: web::Data, - post_form: web::Form, -) -> actix_flash::Response { - let conn = &data.conn; - - let form = post_form.into_inner(); - - post::ActiveModel { - title: Set(form.title.to_owned()), - text: Set(form.text.to_owned()), - ..Default::default() - } - .save(conn) - .await - .expect("could not insert post"); - - let flash = FlashData { - kind: "success".to_owned(), - message: "Post successfully added.".to_owned(), - }; - - actix_flash::Response::with_redirect(flash, "/") -} - -#[get("/{id}")] -async fn edit(data: web::Data, id: web::Path) -> Result { - let conn = &data.conn; - let template = &data.templates; - - let post: post::Model = Post::find_by_id(id.into_inner()) - .one(conn) - .await - .expect("could not find post") - .unwrap(); - - let mut ctx = tera::Context::new(); - ctx.insert("post", &post); - - let body = template - .render("edit.html.tera", &ctx) - .map_err(|_| error::ErrorInternalServerError("Template error"))?; - Ok(HttpResponse::Ok().content_type("text/html").body(body)) -} - -#[post("/{id}")] -async fn update( - data: web::Data, - id: web::Path, - post_form: web::Form, -) -> actix_flash::Response { - let conn = &data.conn; - let form = post_form.into_inner(); - - post::ActiveModel { - id: Set(id.into_inner()), - title: Set(form.title.to_owned()), - text: Set(form.text.to_owned()), - } - .save(conn) - .await - .expect("could not edit post"); - - let flash = FlashData { - kind: "success".to_owned(), - message: "Post successfully updated.".to_owned(), - }; - - actix_flash::Response::with_redirect(flash, "/") -} - -#[post("/delete/{id}")] -async fn delete( - data: web::Data, - id: web::Path, -) -> actix_flash::Response { - let conn = &data.conn; - - let post: post::ActiveModel = Post::find_by_id(id.into_inner()) - .one(conn) - .await - .unwrap() - .unwrap() - .into(); - - post.delete(conn).await.unwrap(); - - let flash = FlashData { - kind: "success".to_owned(), - message: "Post successfully deleted.".to_owned(), - }; - - actix_flash::Response::with_redirect(flash, "/") -} - -#[actix_web::main] -async fn main() -> std::io::Result<()> { - std::env::set_var("RUST_LOG", "debug"); - tracing_subscriber::fmt::init(); - - // get env vars - dotenv::dotenv().ok(); - let db_url = env::var("DATABASE_URL").expect("DATABASE_URL is not set in .env file"); - let host = env::var("HOST").expect("HOST is not set in .env file"); - let port = env::var("PORT").expect("PORT is not set in .env file"); - let server_url = format!("{}:{}", host, port); - - // create post table if not exists - let conn = sea_orm::Database::connect(&db_url).await.unwrap(); - Migrator::up(&conn, None).await.unwrap(); - let templates = Tera::new(concat!(env!("CARGO_MANIFEST_DIR"), "/templates/**/*")).unwrap(); - let state = AppState { templates, conn }; - - let mut listenfd = ListenFd::from_env(); - let mut server = HttpServer::new(move || { - App::new() - .data(state.clone()) - .wrap(middleware::Logger::default()) // enable logger - .wrap(actix_flash::Flash::default()) - .configure(init) - .service(fs::Files::new("/static", "./static").show_files_listing()) - }); - - server = match listenfd.take_tcp_listener(0)? { - Some(listener) => server.listen(listener)?, - None => server.bind(&server_url)?, - }; - - println!("Starting server at {}", server_url); - server.run().await?; - - Ok(()) -} - -pub fn init(cfg: &mut web::ServiceConfig) { - cfg.service(list); - cfg.service(new); - cfg.service(create); - cfg.service(edit); - cfg.service(update); - cfg.service(delete); +fn main() { + actix3_example_api::main(); }