switch to yaml configuration

Signed-off-by: alok8bb <alok8bb@gmail.com>
This commit is contained in:
alok8bb 2023-07-31 10:37:36 +05:30
parent 895b534207
commit 55842e6e3b
No known key found for this signature in database
6 changed files with 89 additions and 38 deletions

3
.gitignore vendored
View File

@ -1,3 +1,4 @@
/target
.env
/test
/test
configuration.yaml

View File

@ -0,0 +1,19 @@
server:
host: 0.0.0.0
port: 7070
db:
user: "oggy"
password: "very_secure_password"
host: "127.0.0.1"
port: "5432"
dbname: "okiba"
pool:
max_size: 16
app:
redis_uri: "redis://127.0.0.1"
rate_limit:
time_seconds: 60
request_count: 10
paste_id_length: 6

View File

@ -3,6 +3,6 @@ CREATE SCHEMA bin;
CREATE TABLE bin.pastes (
id INT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
paste_id VARCHAR(5) NOT NULL,
paste_id VARCHAR(6) NOT NULL,
content TEXT NOT NULL
)

View File

@ -1,16 +1,37 @@
pub use config::ConfigError;
use config::{File, FileFormat};
use serde::Deserialize;
#[derive(Debug, Default, Deserialize)]
pub struct AppConfig {
pub server_addr: String,
pub pg: deadpool_postgres::Config,
#[derive(Debug, Default, Deserialize, Clone)]
pub struct Config {
pub db: deadpool_postgres::Config,
pub server: ServerConfig,
pub app: ApplicationConfig,
}
impl AppConfig {
#[derive(Debug, Default, Deserialize, Clone)]
pub struct ServerConfig {
pub port: u16,
pub host: String,
}
#[derive(Debug, Default, Deserialize, Clone)]
pub struct ApplicationConfig {
pub redis_uri: String,
pub rate_limit: RateLimiterConfig,
pub paste_id_length: u8,
}
#[derive(Debug, Default, Deserialize, Clone)]
pub struct RateLimiterConfig {
pub time_seconds: u64,
pub request_count: u64,
}
impl Config {
pub fn from_env() -> Result<Self, ConfigError> {
let config = config::Config::builder()
.add_source(::config::Environment::default())
.add_source(File::new("configuration", FileFormat::Yaml))
.build()?;
config.try_deserialize()
}

View File

@ -24,7 +24,6 @@ pub mod errors {
#[derive(Display, From, Debug)]
pub enum MyError {
NotFound,
PGError(PGError),
PGMError(PGMError),
PoolError(PoolError),
@ -33,16 +32,19 @@ pub mod errors {
impl ResponseError for MyError {
fn error_response(&self) -> HttpResponse {
match *self {
MyError::NotFound => HttpResponse::NotFound().json(json!({
"message": "record not found"
})),
MyError::PoolError(ref err) => HttpResponse::InternalServerError().json(json!({
"message": err.to_string()
})),
_ => HttpResponse::InternalServerError().json(json!({
"message": "internal server error"
})),
match self {
MyError::PoolError(ref err) => {
log::error!("{}", err.to_string());
HttpResponse::InternalServerError().json(json!({
"message": err.to_string()
}))
}
error => {
log::error!("{}", error.to_string());
HttpResponse::InternalServerError().json(json!({
"message": "internal server error"
}))
}
}
}
}

View File

@ -10,16 +10,14 @@ use actix_web::{
web::{self},
App, Error, HttpResponse, HttpServer,
};
use cfg::AppConfig;
use cfg::ApplicationConfig;
use cfg::Config;
use deadpool_postgres::{Client, Pool};
use dotenv::dotenv;
use rand::{distributions::Alphanumeric, Rng};
use redis::aio::ConnectionManager;
use serde_json::json;
use std::{
str::{self},
time::Duration,
};
use std::time::Duration;
use tokio_postgres::NoTls;
fn generate_endpoint(length: u8) -> String {
@ -47,14 +45,18 @@ async fn fetch_paste(
}
#[post("/paste")]
async fn new_paste(db_pool: web::Data<Pool>, body: String) -> Result<HttpResponse, Error> {
async fn new_paste(
db_pool: web::Data<Pool>,
app_config: web::Data<ApplicationConfig>,
body: String,
) -> Result<HttpResponse, Error> {
let client: Client = db_pool.get().await.map_err(MyError::PoolError)?;
let mut endpoint = generate_endpoint(5);
let mut endpoint = generate_endpoint(app_config.paste_id_length);
// check if endpoint already exists
loop {
if db::paste_id_exists(&client, &endpoint).await? {
endpoint = generate_endpoint(5)
endpoint = generate_endpoint(app_config.paste_id_length)
} else {
break;
}
@ -70,37 +72,43 @@ async fn new_paste(db_pool: web::Data<Pool>, body: String) -> Result<HttpRespons
#[actix_web::main]
async fn main() -> std::io::Result<()> {
dotenv().ok();
const ADDR: (&str, u16) = ("127.0.0.1", 8080);
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
let config = AppConfig::from_env().unwrap();
let config = Config::from_env().unwrap();
log::info!("Connecting database");
let pool = config.pg.create_pool(None, NoTls).unwrap();
let pool = config.db.create_pool(None, NoTls).unwrap();
let redis_client =
redis::Client::open("redis://127.0.0.1:6379").expect("Couldn't connect to redis database");
let redis_client = redis::Client::open(config.app.redis_uri.clone())
.expect("Couldn't connect to redis database");
let redis_cm = ConnectionManager::new(redis_client).await.unwrap();
let redis_backend = RedisBackend::builder(redis_cm).build();
let server = HttpServer::new(move || {
// 5 requests per 60 seconds
let input = SimpleInputFunctionBuilder::new(Duration::from_secs(60), 5)
.real_ip_key()
.build();
let input = SimpleInputFunctionBuilder::new(
Duration::from_secs(config.app.rate_limit.time_seconds),
config.app.rate_limit.request_count,
)
.real_ip_key()
.build();
let middleware = RateLimiter::builder(redis_backend.clone(), input)
.add_headers()
.build();
App::new()
.app_data(web::Data::new(pool.clone()))
.app_data(web::Data::new(config.app.clone()))
.wrap(middleware)
.service(new_paste)
.service(fetch_paste)
})
.bind(ADDR)?
.bind((&*config.server.host, config.server.port))?
.run();
log::info!("Starting the server at http://{}:{}", ADDR.0, ADDR.1);
log::info!(
"Starting the server at http://{}:{}",
config.server.host,
config.server.port
);
server.await
}