diff --git a/Cargo.lock b/Cargo.lock index daa9397..c055af7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -137,6 +137,21 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d7439c3735f405729d52c3fbbe4de140eaf938a1fe47d227c27f8254d4302a5" +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "envy" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f47e0157f2cb54f5ae1bd371b30a2ae4311e1c028f575cd4e81de7353215965" +dependencies = [ + "serde", +] + [[package]] name = "fdeflate" version = "0.3.0" @@ -897,6 +912,8 @@ name = "time-banner" version = "0.1.0" dependencies = [ "axum", + "dotenvy", + "envy", "futures", "png", "resvg", diff --git a/Cargo.toml b/Cargo.toml index 98284eb..006234f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,3 +15,5 @@ tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } futures = "0.3.28" png = "0.17.9" +dotenvy = "0.15.7" +envy = "0.4.2" diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c931566 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,43 @@ +# Build Stage +FROM rust:1.68.0 as builder + +RUN USER=root cargo new --bin time-banner +WORKDIR ./time-banner +ENV CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse +COPY ./Cargo.toml ./Cargo.toml +# Build empty app with downloaded dependencies to produce a stable image layer for next build +RUN cargo build --release + +# Build web app with own code +RUN rm src/*.rs +ADD . ./ +RUN rm ./target/release/deps/time-banner* +RUN cargo build --release + + +FROM debian:buster-slim +ARG APP=/usr/src/app + +RUN apt-get update \ + && apt-get install -y ca-certificates tzdata \ + && rm -rf /var/lib/apt/lists/* + +ENV TZ=Etc/UTC \ + APP_USER=appuser + +RUN groupadd $APP_USER \ + && useradd -g $APP_USER $APP_USER \ + && mkdir -p ${APP} + +COPY --from=builder /time-banner/target/release/time-banner ${APP}/time-banner + +RUN chown -R $APP_USER:$APP_USER ${APP} + +USER $APP_USER +WORKDIR ${APP} + +EXPOSE 3000 +ENV PORT 3000 + + +CMD ["./time-banner"] \ No newline at end of file diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..64f6963 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,57 @@ +use serde::Deserialize; +use tracing::Level; + +#[derive(Deserialize, Debug)] +pub enum Environment { + Production, + Development, +} + +#[derive(Deserialize, Debug)] +pub struct Configuration { + #[serde(default = "default_env")] + pub env: Environment, + + #[serde(default = "default_port")] + pub port: u16, +} + +fn default_port() -> u16 { + 3000 +} + +fn default_env() -> Environment { + Environment::Development +} + +impl Configuration { + pub fn socket_addr(&self) -> [u8; 4] { + match self.env { + Environment::Production => { + let socket = [0, 0, 0, 0]; + tracing::info!( + "Starting Production on {:?}:{}", + socket.as_slice(), + self.port + ); + socket + } + Environment::Development => { + let socket = [127, 0, 0, 1]; + tracing::info!( + "Starting Development on {:?}:{}", + socket.as_slice(), + self.port + ); + socket + } + } + } + + pub fn log_level(&self) -> Level { + match self.env { + Environment::Production => Level::INFO, + Environment::Development => Level::DEBUG, + } + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 60a109b..20cd081 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +mod config; + use std::net::SocketAddr; use axum::{http::StatusCode, Json, response::IntoResponse, Router, routing::{get, post}}; @@ -5,21 +7,33 @@ use axum::body::{Bytes, Full}; use axum::extract::ConnectInfo; use axum::http::header; use axum::response::Response; +use dotenvy::dotenv; +use config::Configuration; mod svg; + #[tokio::main] async fn main() { - // initialize tracing - tracing_subscriber::fmt::init(); + // Parse dotenv files and expose them as environment variables + dotenv().ok(); - let app = Router::new() - .route("/", get(root)); + // envy uses our Configuration struct to parse environment variables + let config = envy::from_env::().expect("Please provide PORT env var"); + + // initialize tracing + tracing_subscriber::fmt() + // With the log_level from our config + .with_max_level(config.log_level()) + .init(); + + // build our application with a route + let app = Router::new().route("/", get(root_handler)); // run our app with hyper // `axum::Server` is a re-export of `hyper::Server` - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); + let addr = SocketAddr::from((config.socket_addr(), config.port)); + axum::Server::bind(&addr) .serve(app.into_make_service_with_connect_info::()) .await @@ -27,7 +41,7 @@ async fn main() { } // basic handler that responds with a static string -async fn root(connect_info: ConnectInfo) -> impl IntoResponse { +async fn root_handler(connect_info: ConnectInfo) -> impl IntoResponse { let raw_image = svg::get(); if raw_image.is_err() { diff --git a/src/svg.rs b/src/svg.rs index e6d4edf..d9f7fff 100644 --- a/src/svg.rs +++ b/src/svg.rs @@ -29,8 +29,6 @@ pub fn get() -> Result, RenderError> { fontdb.load_system_fonts(); let svg_data = std::fs::read("test.svg").unwrap(); - // print bytes as string - println!("{:?}", String::from_utf8(svg_data.clone()).unwrap()); let mut tree_result = usvg::Tree::from_data(&svg_data, &opt); if tree_result.is_err() { return Err(RenderError { message: Some("Failed to parse".to_string()) }); }