add /paste route and setup db

Signed-off-by: alok8bb <alok8bb@gmail.com>
This commit is contained in:
alok8bb 2023-07-30 19:09:57 +05:30
parent de72d0be60
commit 0923b46377
No known key found for this signature in database
4 changed files with 130 additions and 6 deletions

25
Cargo.lock generated
View File

@ -935,10 +935,15 @@ dependencies = [
"actix-web", "actix-web",
"config", "config",
"deadpool-postgres", "deadpool-postgres",
"derive_more",
"dotenv", "dotenv",
"env_logger", "env_logger",
"log", "log",
"rand",
"serde", "serde",
"serde_json",
"tokio-pg-mapper",
"tokio-pg-mapper-derive",
"tokio-postgres", "tokio-postgres",
] ]
@ -1515,6 +1520,26 @@ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]]
name = "tokio-pg-mapper"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93f2b78f3566383ffabc553c72bbb2f129962a54886c5c4d8e8c706f84eceab8"
dependencies = [
"tokio-postgres",
]
[[package]]
name = "tokio-pg-mapper-derive"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8548f756cd6eb4069c5af0fb0cec57001fb42bd1fb7330d8f24067ee3fa62608"
dependencies = [
"quote",
"syn 1.0.109",
"tokio-postgres",
]
[[package]] [[package]]
name = "tokio-postgres" name = "tokio-postgres"
version = "0.7.8" version = "0.7.8"

View File

@ -9,8 +9,13 @@ edition = "2021"
actix-web = "4" actix-web = "4"
config = "0.13.3" config = "0.13.3"
deadpool-postgres = { version = "0.10.5", features = ["serde"] } deadpool-postgres = { version = "0.10.5", features = ["serde"] }
derive_more = "0.99.17"
dotenv = "0.15.0" dotenv = "0.15.0"
env_logger = "0.10.0" env_logger = "0.10.0"
log = "0.4.19" log = "0.4.19"
rand = "0.8.5"
serde = { version = "1.0.178", features = ["derive"] } serde = { version = "1.0.178", features = ["derive"] }
serde_json = "1.0.104"
tokio-pg-mapper = "0.2.0"
tokio-pg-mapper-derive = "0.2.0"
tokio-postgres = "0.7.8" tokio-postgres = "0.7.8"

65
src/db.rs Normal file
View File

@ -0,0 +1,65 @@
use deadpool_postgres::Client;
use self::errors::MyError;
mod models {
use serde::{Deserialize, Serialize};
use tokio_pg_mapper_derive::PostgresMapper;
#[derive(Deserialize, PostgresMapper, Serialize)]
#[pg_mapper(table = "pastes")]
pub struct Paste {
pub endpoint: String,
pub content: String,
}
}
pub mod errors {
use actix_web::{HttpResponse, ResponseError};
use deadpool_postgres::PoolError;
use derive_more::{Display, From};
use serde_json::json;
use tokio_pg_mapper::Error as PGMError;
use tokio_postgres::error::Error as PGError;
#[derive(Display, From, Debug)]
pub enum MyError {
NotFound,
PGError(PGError),
PGMError(PGMError),
PoolError(PoolError),
}
impl std::error::Error for MyError {}
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"
})),
}
}
}
}
pub async fn paste_id_exists(client: &Client, paste_id: &String) -> Result<bool, MyError> {
let stmt_ = format!("SELECT 1 FROM bin.pastes WHERE paste_id = $1 LIMIT 1");
let stmt = client.prepare(&stmt_).await.unwrap();
let rows = client.query(&stmt, &[&paste_id]).await?;
Ok(!rows.is_empty())
}
pub async fn add_paste(client: &Client, endpoint: &String, content: String) -> Result<(), MyError> {
let insert_stmt_ = format!("INSERT INTO bin.pastes(paste_id, content) VALUES($1, $2)");
let insert_stmt = client.prepare(&insert_stmt_).await.unwrap();
client.query(&insert_stmt, &[&endpoint, &content]).await?;
Ok(())
}

View File

@ -1,18 +1,46 @@
mod cfg; mod cfg;
mod db;
use crate::db::errors::MyError;
use actix_web::{ use actix_web::{
post, post,
web::{self, Bytes}, web::{self},
App, HttpServer, App, Error, HttpResponse, HttpServer,
}; };
use cfg::AppConfig; use cfg::AppConfig;
use deadpool_postgres::{Client, Pool};
use dotenv::dotenv; use dotenv::dotenv;
use rand::{distributions::Alphanumeric, Rng};
use serde_json::json;
use std::str::{self}; use std::str::{self};
use tokio_postgres::NoTls; use tokio_postgres::NoTls;
fn generate_endpoint(length: u8) -> String {
rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(length as usize)
.map(char::from)
.collect()
}
#[post("/paste")] #[post("/paste")]
async fn greet(bytes: Bytes) -> &'static str { async fn paste(db_pool: web::Data<Pool>, body: String) -> Result<HttpResponse, Error> {
let payload = str::from_utf8(&bytes).unwrap(); let client: Client = db_pool.get().await.map_err(MyError::PoolError)?;
"Hello World!"
let mut endpoint = generate_endpoint(5);
// check if endpoint already exists
loop {
if db::paste_id_exists(&client, &endpoint).await? {
endpoint = generate_endpoint(5)
} else {
break;
}
}
db::add_paste(&client, &endpoint, body).await?;
Ok(HttpResponse::Ok().json(json!({
"paste_id": endpoint,
"message": "code pasted successfully",
})))
} }
#[actix_web::main] #[actix_web::main]
@ -22,12 +50,13 @@ async fn main() -> std::io::Result<()> {
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
let config = AppConfig::from_env().unwrap(); let config = AppConfig::from_env().unwrap();
log::info!("Connecting database");
let pool = config.pg.create_pool(None, NoTls).unwrap(); let pool = config.pg.create_pool(None, NoTls).unwrap();
let server = HttpServer::new(move || { let server = HttpServer::new(move || {
App::new() App::new()
.app_data(web::Data::new(pool.clone())) .app_data(web::Data::new(pool.clone()))
.service(greet) .service(paste)
}) })
.bind(ADDR)? .bind(ADDR)?
.run(); .run();