diff --git a/Cargo.toml b/Cargo.toml index deca536..9cbcc16 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,9 @@ edition = "2018" build = "build.rs" default-run = "tenebrous" +[package.metadata.scripts] +grpc-proxy = 'docker run -d --rm -v "$(pwd)"/envoy.yaml:/etc/envoy/envoy.yaml:ro --network=host envoyproxy/envoy:v1.16.1' + [[bin]] name = "tenebrous-migrate" path = "src/migrate.rs" diff --git a/README.md b/README.md index de6d85d..06f3440 100644 --- a/README.md +++ b/README.md @@ -4,41 +4,66 @@ An open source character sheet service for tabletop roleplaying games. Currently under heavy development. +## The Stack + +This project makes use of these technologies: + +- Rust +- Rocket Web Framework +- Tonic gRPC Framework +- Typescript + +Building is backed by: cargo, npm, and webpack. + ## Build Instructions These are very basic build instructions. They assume you already have cargo set up and installed. -### Install Dependencies - -Install dependencies. The exact method depends on your OS. - - * sqlite3 and development headers (Void Linux: `xbps-install - sqlite sqlite-devel`, Ubuntu: `apt install sqlite3 libsqlite3-dev`). - * protoc: protocol buffers compiler. There is one baked into the - build, so you should not need this unless you are not using - Linux/Mac/Windows. - ### Initial Setup -Follow these instructions from the root of the repository. +Quick initial setup instructions that will set up a development +environment. -Set up database: +First, install dependencies by either using the command below (Void +Linux), or reading the "Dependencies Required" section: ``` +xbps-install sqlite sqlite-devel protobuf nodejs docker +``` + +Then run the required `cargo` commands to install management tools and +create a development database: + +``` + cargo install --version=0.2.0 sqlx-cli +cargo install cargo-run-script cargo run --bin tenebrous-migrate ``` -### Run Application +### Dependencies Required -If you are using `rustup`, then it should automatically switch to the -stable version of Rust in this repository. This is because of the -`rust-toolchain` file. +Dependencies required to build the project. The exact installation +method depends on your OS. + + * sqlite3 and development headers (Void Linux: `xbps-install + sqlite sqlite-devel`, Ubuntu: `apt install sqlite3 libsqlite3-dev`). + * protoc: protocol buffers compiler. Needed to compile protobuf files + for both the server and web frontends (Void Linux: `xbps-install + protobuf`). + * Node and npm: Needed to run webpack for compiling and injecting + Typescript into various web pages (Void Linux: `xbps-install + nodejs`). + * Docker: Needed to run the grpc proxy (Void Linux `xbps-install + docker`). + +### Run Application Command line "instructions" to build and run the application: ``` +cargo run-script grpc-proxy # only required if proxy not already running cargo run ``` @@ -65,4 +90,15 @@ to update the SQLx data JSON file: cargo sqlx prepare -- --bin tenebrous ``` +### gRPC-Web Proxy + +The frontend web application makes use of the gRPC-Web protocol to +call gPRC endpoints from the browser. This requires a proxy to +translate the calls from the browser to HTTP2 calls gRPC understands. +For development, executing the `cargo run-script grpc-proxy` command +will start the envoy proxy recommended by Google's gRPC-Web project. + +The envoy configuration assumes you are on Linux. If you are using Mac +OS or Windows, see the note in the envoy configuration. + [rustup]: https://rust-lang.github.io/rustup/index.html diff --git a/envoy/envoy.yaml b/envoy.yaml similarity index 100% rename from envoy/envoy.yaml rename to envoy.yaml diff --git a/src/grpc.rs b/src/grpc.rs new file mode 100644 index 0000000..e92c58a --- /dev/null +++ b/src/grpc.rs @@ -0,0 +1,24 @@ +use crate::models::proto::cofd::api::cofd_api_server::{CofdApi, CofdApiServer}; +use crate::models::proto::cofd::api::UpdateSkillValueRequest; +use crate::models::proto::cofd::cofd_sheet::Skill; +use tonic::{transport::Server, Request, Response, Status}; + +#[derive(Debug)] +pub struct CofdApiService { + pub db: sqlx::SqlitePool, +} + +#[tonic::async_trait] +impl CofdApi for CofdApiService { + async fn update_skill_value( + &self, + request: Request, // Accept request of type HelloRequest + ) -> Result, Status> { + // Return an instance of type HelloReply + println!("Got a request: {:?}", request); + + let reply = Skill::default(); + + Ok(Response::new(reply)) // Send back our formatted greeting + } +} diff --git a/src/main.rs b/src/main.rs index 85d4f52..5748883 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,56 +9,36 @@ extern crate rocket_contrib; #[macro_use] extern crate serde_derive; +use log::{error, info}; use rocket_contrib::serve::StaticFiles; use rocket_contrib::templates::Template; use std::env; +use tonic::transport::Server; pub mod catchers; pub mod db; pub mod errors; +pub mod grpc; pub mod migrator; pub mod models; pub mod routes; -async fn gin_and_tonic() -> Result<(), Box> { - use crate::models::proto::cofd::api::cofd_api_server::{CofdApi, CofdApiServer}; - use crate::models::proto::cofd::api::UpdateSkillValueRequest; - use crate::models::proto::cofd::cofd_sheet::Skill; - use tonic::{transport::Server, Request, Response, Status}; - - #[derive(Debug, Default)] - pub struct MyGreeter {} - - #[tonic::async_trait] - impl CofdApi for MyGreeter { - async fn update_skill_value( - &self, - request: Request, // Accept request of type HelloRequest - ) -> Result, Status> { - // Return an instance of type HelloReply - println!("Got a request: {:?}", request); - - let reply = Skill::default(); - - Ok(Response::new(reply)) // Send back our formatted greeting - } - } - +async fn make_tonic(db: sqlx::SqlitePool) -> Result<(), Box> { + use crate::models::proto::cofd::api::cofd_api_server::CofdApiServer; let addr = "[::1]:9090".parse()?; - let greeter = MyGreeter::default(); + let service = grpc::CofdApiService { db }; - println!("Running tonic"); + info!("Running Tonic"); Server::builder() - .add_service(CofdApiServer::new(greeter)) + .add_service(CofdApiServer::new(service)) .serve(addr) .await?; - println!("ending tonic"); Ok(()) } async fn make_rocket(database: sqlx::SqlitePool) -> Result<(), Box> { - println!("Running rocket"); + info!("Running Rocket"); let root_routes: Vec = { routes::root::routes() .into_iter() @@ -72,7 +52,6 @@ async fn make_rocket(database: sqlx::SqlitePool) -> Result<(), Box Result<(), Box> { migrator::migrate(db_path).await?; let db = crate::db::create_pool(db_path).await?; - // let tonic = gin_and_tonic(); - // let rocket = make_rocket(db); tokio::select! { - result = make_rocket(db) => { - println!("done with rocket: {:?}", result); + result = make_rocket(db.clone()) => { + match result { + Ok(_) => info!("Shutting down Rocket."), + Err(e) => error!("Rocket error: {}", e) + } } - result = gin_and_tonic() => { - println!("done with tonic: {:?}", result); + result = make_tonic(db) => { + match result { + Ok(_) => info!("Shutting down Tonic."), + Err(e) => error!("Tonic error: {}", e) + } } }