configuration refactoring [wip]
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
use config::{Config, ConfigError};
|
||||
use secrecy::{ExposeSecret, Secret};
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
pub struct Settings {
|
||||
pub database: DatabaseSettings,
|
||||
pub application: ApplicationSettings,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
pub struct ApplicationSettings {
|
||||
pub port: u16,
|
||||
pub host: String,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
pub struct DatabaseSettings {
|
||||
pub username: String,
|
||||
pub password: Secret<String>,
|
||||
pub port: u16,
|
||||
pub host: String,
|
||||
pub database_name: String,
|
||||
}
|
||||
|
||||
impl TryFrom<Config> for Settings {
|
||||
type Error = ConfigError;
|
||||
|
||||
fn try_from(builder: config::Config) -> Result<Self, Self::Error> {
|
||||
// Extract values from the builder and construct Settings
|
||||
let database = builder.get::<DatabaseSettings>("database")?;
|
||||
let application = builder.get::<ApplicationSettings>("application")?;
|
||||
|
||||
Ok(Settings {
|
||||
database,
|
||||
application,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl DatabaseSettings {
|
||||
pub fn connection_string(&self) -> Secret<String> {
|
||||
Secret::new(format!(
|
||||
"postgres://{}:{}@{}:{}/{}",
|
||||
self.username,
|
||||
self.password.expose_secret(),
|
||||
self.host,
|
||||
self.port,
|
||||
self.database_name
|
||||
))
|
||||
}
|
||||
|
||||
pub fn connection_string_without_db(&self) -> Secret<String> {
|
||||
Secret::new(format!(
|
||||
"postgres://{}:{}@{}:{}",
|
||||
self.username,
|
||||
self.password.expose_secret(),
|
||||
self.host,
|
||||
self.port
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_configuration() -> Result<Settings, ConfigError> {
|
||||
let base_path = std::env::current_dir().expect("Failed to determine the current directory.");
|
||||
let configuration_directory = base_path.join("configuration");
|
||||
|
||||
// Detect the running environment
|
||||
// Default to `local`
|
||||
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());
|
||||
// Initialise our configuration reader
|
||||
let settings = config::Config::builder()
|
||||
// Add configuration values from a file named `configuration.yaml`.
|
||||
.add_source(config::File::from(
|
||||
configuration_directory.join("base.yaml"),
|
||||
))
|
||||
.add_source(config::File::from(
|
||||
configuration_directory.join(environment_filename),
|
||||
))
|
||||
.build()?;
|
||||
// Try to convert the configuration values it read into
|
||||
// our Settings type
|
||||
settings.try_deserialize::<Settings>()
|
||||
}
|
||||
|
||||
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 environement. \
|
||||
Use either 'local' or 'production'.",
|
||||
other
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
application:
|
||||
port: 8000
|
||||
host: 127.0.0.1
|
||||
database:
|
||||
host: "127.0.0.1"
|
||||
port: 5432
|
||||
username: "postgres"
|
||||
password: "password"
|
||||
database_name: "newsletter"
|
||||
@@ -1,5 +1,6 @@
|
||||
use diesel::pg::PgConnection;
|
||||
use diesel::prelude::*;
|
||||
use diesel::r2d2::{ConnectionManager, Pool};
|
||||
use dotenv::dotenv;
|
||||
use std::env;
|
||||
|
||||
@@ -10,3 +11,13 @@ pub fn establish_connection() -> PgConnection {
|
||||
PgConnection::establish(&database_url)
|
||||
.unwrap_or_else(|e| panic!("Error connecting to database {}: {}", database_url, e))
|
||||
}
|
||||
|
||||
pub fn get_connection_pool(url: &str) -> Pool<ConnectionManager<PgConnection>> {
|
||||
let manager = ConnectionManager::<PgConnection>::new(url);
|
||||
// Refer to the `r2d2` documentation for more methods to use
|
||||
// when building a connection pool
|
||||
Pool::builder()
|
||||
.test_on_check_out(true)
|
||||
.build(manager)
|
||||
.expect("Could not build connection pool")
|
||||
}
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
extern crate diesel;
|
||||
extern crate dotenv;
|
||||
|
||||
pub mod auth;
|
||||
pub mod configuration;
|
||||
pub mod database;
|
||||
pub mod json_serialization;
|
||||
pub mod models;
|
||||
pub mod reader;
|
||||
pub mod schema;
|
||||
pub mod startup;
|
||||
pub mod views;
|
||||
+16
-51
@@ -1,61 +1,26 @@
|
||||
extern crate diesel;
|
||||
extern crate dotenv;
|
||||
use std::net::TcpListener;
|
||||
|
||||
use actix_service::Service;
|
||||
use actix_web::{App, HttpResponse, HttpServer};
|
||||
use futures::future::{ok, Either};
|
||||
mod auth;
|
||||
mod database;
|
||||
mod json_serialization;
|
||||
mod models;
|
||||
mod reader;
|
||||
mod schema;
|
||||
mod views;
|
||||
use diesel::{
|
||||
r2d2::{ConnectionManager, Pool},
|
||||
PgConnection,
|
||||
};
|
||||
use rss_reader::{configuration::get_configuration, database::get_connection_pool, startup::run};
|
||||
use secrecy::ExposeSecret;
|
||||
|
||||
#[actix_rt::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
env_logger::init();
|
||||
|
||||
HttpServer::new(|| {
|
||||
let app = App::new()
|
||||
.wrap_fn(|req, srv| {
|
||||
let mut passed: bool;
|
||||
let request_url: String = String::from(req.uri().path());
|
||||
let configuration = get_configuration().expect("Failed to read configuration.");
|
||||
|
||||
log::info!("Request Url: {}", request_url);
|
||||
if req.path().contains("/article/") {
|
||||
match auth::process_token(&req) {
|
||||
Ok(_token) => passed = true,
|
||||
Err(_message) => passed = false,
|
||||
}
|
||||
} else {
|
||||
log::warn!("No auth check done.");
|
||||
passed = true;
|
||||
}
|
||||
let connection_pool: Pool<ConnectionManager<PgConnection>> =
|
||||
get_connection_pool(configuration.database.connection_string().expose_secret());
|
||||
|
||||
if req.path().contains("user/create") {
|
||||
passed = true;
|
||||
}
|
||||
let address = format!(
|
||||
"{}:{}",
|
||||
configuration.application.host, configuration.application.port
|
||||
);
|
||||
|
||||
log::info!("passed: {:?}", passed);
|
||||
|
||||
let end_result = match passed {
|
||||
true => Either::Left(srv.call(req)),
|
||||
false => Either::Right(ok(req.into_response(
|
||||
HttpResponse::Unauthorized().finish().map_into_boxed_body(),
|
||||
))),
|
||||
};
|
||||
|
||||
async move {
|
||||
let result = end_result.await?;
|
||||
log::info!("{} -> {}", request_url, &result.status());
|
||||
Ok(result)
|
||||
}
|
||||
})
|
||||
.configure(views::views_factory);
|
||||
app
|
||||
})
|
||||
.bind("127.0.0.1:8001")?
|
||||
.run()
|
||||
.await
|
||||
let listener = TcpListener::bind(address)?;
|
||||
run(listener, connection_pool)?.await
|
||||
}
|
||||
|
||||
+10
-3
@@ -4,7 +4,6 @@ use crate::models::feed_item::rss_feed_item::FeedItem;
|
||||
use crate::reader::structs::feed::FeedAggregate;
|
||||
use crate::schema::feed_item::{feed_id, id, read};
|
||||
use crate::{
|
||||
database::establish_connection,
|
||||
json_serialization::articles::Articles,
|
||||
schema::feed::{self, user_id},
|
||||
schema::feed_item,
|
||||
@@ -12,15 +11,23 @@ use crate::{
|
||||
use actix_web::{web, HttpRequest, Responder};
|
||||
use chrono::Local;
|
||||
use diesel::prelude::*;
|
||||
use diesel::r2d2::{ConnectionManager, Pool};
|
||||
|
||||
use super::structs::article::Article;
|
||||
|
||||
pub async fn get(path: web::Path<JsonUser>, req: HttpRequest) -> impl Responder {
|
||||
pub async fn get(
|
||||
path: web::Path<JsonUser>,
|
||||
req: HttpRequest,
|
||||
pool: web::Data<Pool<ConnectionManager<PgConnection>>>,
|
||||
) -> impl Responder {
|
||||
let request = req.clone();
|
||||
let req_user_id = path.user_id;
|
||||
log::info!("Received user_id: {}", req_user_id);
|
||||
// Clone the Arc containing the connection pool
|
||||
let pool_arc = pool.get_ref().clone();
|
||||
// Acquire a connection from the pool
|
||||
let mut connection = pool_arc.get().expect("Failed to get database connection");
|
||||
|
||||
let mut connection: diesel::PgConnection = establish_connection();
|
||||
let feeds: Vec<Feed> = feed::table
|
||||
.filter(user_id.eq(req_user_id))
|
||||
.load::<Feed>(&mut connection)
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
use std::net::TcpListener;
|
||||
|
||||
use actix_service::Service;
|
||||
use actix_web::web;
|
||||
use actix_web::{dev::Server, App, HttpResponse, HttpServer};
|
||||
use diesel::r2d2::{ConnectionManager, Pool};
|
||||
use diesel::PgConnection;
|
||||
use futures::future::{ok, Either};
|
||||
|
||||
use crate::auth;
|
||||
use crate::views;
|
||||
|
||||
pub fn run(
|
||||
listener: TcpListener,
|
||||
connection: Pool<ConnectionManager<PgConnection>>,
|
||||
) -> Result<Server, std::io::Error> {
|
||||
let wrapper = web::Data::new(connection);
|
||||
let server = HttpServer::new(move || {
|
||||
let app = App::new()
|
||||
.wrap_fn(|req, srv| {
|
||||
let mut passed: bool;
|
||||
let request_url: String = String::from(req.uri().path());
|
||||
|
||||
log::info!("Request Url: {}", request_url);
|
||||
if req.path().contains("/article/") {
|
||||
match auth::process_token(&req) {
|
||||
Ok(_token) => passed = true,
|
||||
Err(_message) => passed = false,
|
||||
}
|
||||
} else {
|
||||
log::warn!("No auth check done.");
|
||||
passed = true;
|
||||
}
|
||||
|
||||
if req.path().contains("user/create") {
|
||||
passed = true;
|
||||
}
|
||||
|
||||
log::info!("passed: {:?}", passed);
|
||||
|
||||
let end_result = match passed {
|
||||
true => Either::Left(srv.call(req)),
|
||||
false => Either::Right(ok(req.into_response(
|
||||
HttpResponse::Unauthorized().finish().map_into_boxed_body(),
|
||||
))),
|
||||
};
|
||||
|
||||
async move {
|
||||
let result = end_result.await?;
|
||||
log::info!("{} -> {}", request_url, &result.status());
|
||||
Ok(result)
|
||||
}
|
||||
})
|
||||
.app_data(wrapper.clone())
|
||||
.configure(views::views_factory);
|
||||
app
|
||||
})
|
||||
.listen(listener)?
|
||||
.run();
|
||||
|
||||
Ok(server)
|
||||
}
|
||||
Reference in New Issue
Block a user