feat(api): containerization

- Build SQLx queries beforehand so that we don't have to do PostgreSQL
init right away at service start up
- Created `Dockerfile.production`
- Updated docs
- Seperate configuration files for local and development environments
This commit is contained in:
2024-05-10 19:38:07 -04:00
parent 7b5fa61780
commit daf914bb8e
9 changed files with 119 additions and 12 deletions

View File

@@ -3,7 +3,13 @@ use secrecy::{ExposeSecret, Secret};
#[derive(serde::Deserialize)]
pub struct Settings {
pub database: DatabaseSettings,
pub application_port: u16,
pub application: ApplicationSettings,
}
#[derive(serde::Deserialize)]
pub struct ApplicationSettings {
pub port: u16,
pub host: String,
}
#[derive(serde::Deserialize)]
@@ -16,16 +22,56 @@ pub struct DatabaseSettings {
}
pub fn get_configuration() -> Result<Settings, config::ConfigError> {
let base_path = std::env::current_dir().expect("Failed to determine the current directory");
let configuration_directory = base_path.join("configuration");
let environment: Environment = std::env::var("APP_ENVIRONMENT")
.unwrap_or_else(|_| "local".into())
.try_into()
.expect("Failed to parse APP_ENVIRONMENT.");
let environment_filename = format!("{}.yaml", environment.as_str());
let settings = config::Config::builder()
.add_source(config::File::new(
"configuration.yaml",
config::FileFormat::Yaml,
.add_source(config::File::from(
configuration_directory.join("base.yaml"),
))
.add_source(config::File::from(
configuration_directory.join(environment_filename),
))
.build()?;
settings.try_deserialize::<Settings>()
}
/// The possible runtime environment for our application.
pub enum Environment {
Local,
Production,
}
impl Environment {
pub fn as_str(&self) -> &'static str {
match self {
Environment::Local => "local",
Environment::Production => "production",
}
}
}
impl TryFrom<String> for Environment {
type Error = String;
fn try_from(s: String) -> Result<Self, Self::Error> {
match s.to_lowercase().as_str() {
"local" => Ok(Self::Local),
"production" => Ok(Self::Production),
other => Err(format!(
"{} is not a supported environment. \
Use either `local` or `production`.",
other
)),
}
}
}
impl DatabaseSettings {
pub fn connection_string(&self) -> Secret<String> {
Secret::new(format!(

View File

@@ -16,14 +16,19 @@ async fn main() -> Result<(), std::io::Error> {
);
init_subscriber(subscriber);
let db_conn = PgPool::connect(configuration.database.connection_string().expose_secret())
.await
let db_conn = PgPool::connect_lazy(configuration.database.connection_string().expose_secret())
.expect("Failed to connect to PostgreSQL");
let port_number = configuration.application_port;
let listener = TcpListener::bind(format!("127.0.0.1:{}", port_number))
.unwrap_or_else(|_| panic!("Can't bind to port {} at localhost", port_number));
let listener = TcpListener::bind(format!(
"{}:{}",
configuration.application.host, configuration.application.port
))
.unwrap_or_else(|_| {
panic!(
"Can't bind to port {} at localhost",
configuration.application.port
)
});
// Move the error up the call stack
// otherwise await for the HttpServer