parent
98b64ea4a0
commit
86693018c8
13
Cargo.lock
generated
13
Cargo.lock
generated
|
@ -1194,7 +1194,6 @@ dependencies = [
|
|||
"scraper 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-diesel 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-executor 0.2.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"warp 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
@ -1966,17 +1965,6 @@ dependencies = [
|
|||
"tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-diesel"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"diesel 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-core-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"r2d2 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-executor 0.2.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-executor"
|
||||
version = "0.1.8"
|
||||
|
@ -2571,7 +2559,6 @@ dependencies = [
|
|||
"checksum tokio-buf 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fb220f46c53859a4b7ec083e41dec9778ff0b1851c0942b211edb89e0ccdc46"
|
||||
"checksum tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c501eceaf96f0e1793cf26beb63da3d11c738c4a943fdf3746d81d64684c39f"
|
||||
"checksum tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d16217cad7f1b840c5a97dfb3c43b0c871fef423a6e8d2118c604e843662a443"
|
||||
"checksum tokio-diesel 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eb0d9ea6827f7bf84d88c6d19fd53524585663f51d8cde4508e9cdb62fcb1f15"
|
||||
"checksum tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f27ee0e6db01c5f0b2973824547ce7e637b2ed79b891a9677b0de9bd532b6ac"
|
||||
"checksum tokio-executor 0.2.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "345c88d08e60054624ac16be7feef0596a9151b63e20de75ee771effe67c3639"
|
||||
"checksum tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe6dc22b08d6993916647d108a1a7d15b9cd29c4f4496c62b92c45b5041b7af"
|
||||
|
|
|
@ -21,7 +21,6 @@ pulldown-cmark = "0.5.0"
|
|||
rand = "0.7.0"
|
||||
reqwest = "0.9.19"
|
||||
serde = { version = "1.0.88", features = ["derive"] }
|
||||
tokio-diesel = "0.2.0"
|
||||
tokio-executor = { version = "0.2.0-alpha.4", features = ["blocking"] }
|
||||
warp = "0.1.15"
|
||||
|
||||
|
|
|
@ -6,18 +6,17 @@ mod routes;
|
|||
mod schema;
|
||||
|
||||
use diesel::prelude::*;
|
||||
use diesel::r2d2::{ConnectionManager, Pool};
|
||||
use diesel::r2d2::{ConnectionManager, Pool, PooledConnection};
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
|
||||
type PgPool = Pool<ConnectionManager<PgConnection>>;
|
||||
type Connection = PooledConnection<ConnectionManager<PgConnection>>;
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
env_logger::init();
|
||||
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL required");
|
||||
let pool = &*Box::leak(Box::new(
|
||||
Pool::new(ConnectionManager::new(database_url)).expect("Couldn't create a connection pool"),
|
||||
));
|
||||
let pool = Pool::new(ConnectionManager::new(database_url))
|
||||
.expect("Couldn't create a connection connection");
|
||||
diesel_migrations::run_pending_migrations(&pool.get()?)?;
|
||||
warp::serve(routes::routes(pool)).run(([127, 0, 0, 1], 8080));
|
||||
Ok(())
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
use crate::schema::languages::dsl::*;
|
||||
use crate::PgPool;
|
||||
use crate::Connection;
|
||||
use diesel::prelude::*;
|
||||
use futures::Future;
|
||||
use futures03::TryFutureExt;
|
||||
use tokio_diesel::{AsyncError, AsyncRunQueryDsl};
|
||||
use warp::Rejection;
|
||||
|
||||
#[derive(Queryable)]
|
||||
pub struct Language {
|
||||
|
@ -13,12 +11,12 @@ pub struct Language {
|
|||
}
|
||||
|
||||
impl Language {
|
||||
pub fn fetch(pool: &'static PgPool) -> impl Future<Item = Vec<Language>, Error = AsyncError> {
|
||||
pub fn fetch(connection: &Connection) -> Result<Vec<Language>, Rejection> {
|
||||
languages
|
||||
.select((language_id, identifier, name))
|
||||
.order((priority.asc(), name.asc()))
|
||||
.load_async(pool)
|
||||
.compat()
|
||||
.load(connection)
|
||||
.map_err(warp::reject::custom)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
use crate::schema::pastes;
|
||||
use crate::PgPool;
|
||||
use crate::Connection;
|
||||
use ammonia::Builder;
|
||||
use chrono::{DateTime, Utc};
|
||||
use diesel::prelude::*;
|
||||
use futures::Future;
|
||||
use futures03::TryFutureExt;
|
||||
use lazy_static::lazy_static;
|
||||
use log::info;
|
||||
use pulldown_cmark::{Options, Parser};
|
||||
use tokio_diesel::{AsyncError, AsyncRunQueryDsl};
|
||||
use warp::Rejection;
|
||||
|
||||
#[derive(Queryable)]
|
||||
pub struct Paste {
|
||||
|
@ -19,16 +17,15 @@ pub struct Paste {
|
|||
}
|
||||
|
||||
impl Paste {
|
||||
pub fn delete_old(pool: &'static PgPool) -> impl Future<Item = (), Error = AsyncError> {
|
||||
diesel::delete(pastes::table)
|
||||
pub fn delete_old(connection: &Connection) -> Result<(), Rejection> {
|
||||
let pastes = diesel::delete(pastes::table)
|
||||
.filter(pastes::delete_at.lt(Utc::now()))
|
||||
.execute_async(pool)
|
||||
.compat()
|
||||
.map(|pastes| {
|
||||
if pastes > 0 {
|
||||
info!("Deleted {} paste(s)", pastes);
|
||||
}
|
||||
})
|
||||
.execute(connection)
|
||||
.map_err(warp::reject::custom)?;
|
||||
if pastes > 0 {
|
||||
info!("Deleted {} paste(s)", pastes);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use crate::schema::{implementation_wrappers, implementations, languages, shared_wrappers};
|
||||
use crate::PgPool;
|
||||
use crate::Connection;
|
||||
use diesel::prelude::*;
|
||||
use futures::Future;
|
||||
use futures03::prelude::*;
|
||||
use serde::Serialize;
|
||||
use tokio_diesel::{AsyncError, AsyncRunQueryDsl, OptionalExtension};
|
||||
use tokio_executor::blocking;
|
||||
use warp::http::header::CACHE_CONTROL;
|
||||
use warp::{Rejection, Reply};
|
||||
|
@ -58,24 +58,23 @@ struct JsonImplementation {
|
|||
wrappers: Vec<Wrapper>,
|
||||
}
|
||||
|
||||
pub async fn api_language(
|
||||
pub fn api_language(
|
||||
identifier: String,
|
||||
pool: &'static PgPool,
|
||||
) -> Result<impl Reply, Rejection> {
|
||||
let Language { id, mode, mime } = languages::table
|
||||
.filter(languages::identifier.eq(identifier))
|
||||
.select((
|
||||
languages::language_id,
|
||||
languages::highlighter_mode,
|
||||
languages::mime,
|
||||
))
|
||||
.get_result_async(pool)
|
||||
.await
|
||||
.optional()
|
||||
.map_err(warp::reject::custom)?
|
||||
.ok_or_else(warp::reject::not_found)?;
|
||||
let (shared_wrappers, implementations) = future::try_join(
|
||||
shared_wrappers::table
|
||||
connection: Connection,
|
||||
) -> impl Future<Item = impl Reply, Error = Rejection> {
|
||||
blocking::run(move || {
|
||||
let Language { id, mode, mime } = languages::table
|
||||
.filter(languages::identifier.eq(identifier))
|
||||
.select((
|
||||
languages::language_id,
|
||||
languages::highlighter_mode,
|
||||
languages::mime,
|
||||
))
|
||||
.get_result(&connection)
|
||||
.optional()
|
||||
.map_err(warp::reject::custom)?
|
||||
.ok_or_else(warp::reject::not_found)?;
|
||||
let shared_wrappers = shared_wrappers::table
|
||||
.filter(shared_wrappers::language_id.eq(id))
|
||||
.select((
|
||||
shared_wrappers::identifier,
|
||||
|
@ -84,73 +83,67 @@ pub async fn api_language(
|
|||
shared_wrappers::is_formatter,
|
||||
))
|
||||
.order(shared_wrappers::ordering)
|
||||
.load_async(pool),
|
||||
async {
|
||||
let implementations = implementations::table
|
||||
.select((
|
||||
implementations::implementation_id,
|
||||
implementations::identifier,
|
||||
implementations::label,
|
||||
))
|
||||
.filter(implementations::language_id.eq(id))
|
||||
.load_async(pool)
|
||||
.await?;
|
||||
let (implementations, implementation_wrappers) = blocking::run(move || {
|
||||
let implementation_wrappers = ImplementationWrapper::belonging_to(&implementations)
|
||||
.select((
|
||||
implementation_wrappers::implementation_wrapper_id,
|
||||
implementation_wrappers::implementation_id,
|
||||
implementation_wrappers::identifier,
|
||||
implementation_wrappers::label,
|
||||
implementation_wrappers::is_asm,
|
||||
implementation_wrappers::is_formatter,
|
||||
))
|
||||
.order(implementation_wrappers::ordering)
|
||||
.load(&pool.get().map_err(AsyncError::Checkout)?)
|
||||
.map_err(AsyncError::Error)?;
|
||||
Ok((implementations, implementation_wrappers))
|
||||
.load(&connection)
|
||||
.map_err(warp::reject::custom)?;
|
||||
let implementations = implementations::table
|
||||
.select((
|
||||
implementations::implementation_id,
|
||||
implementations::identifier,
|
||||
implementations::label,
|
||||
))
|
||||
.filter(implementations::language_id.eq(id))
|
||||
.load(&connection)
|
||||
.map_err(warp::reject::custom)?;
|
||||
let implementation_wrappers = ImplementationWrapper::belonging_to(&implementations)
|
||||
.select((
|
||||
implementation_wrappers::implementation_wrapper_id,
|
||||
implementation_wrappers::implementation_id,
|
||||
implementation_wrappers::identifier,
|
||||
implementation_wrappers::label,
|
||||
implementation_wrappers::is_asm,
|
||||
implementation_wrappers::is_formatter,
|
||||
))
|
||||
.order(implementation_wrappers::ordering)
|
||||
.load(&connection)
|
||||
.map_err(warp::reject::custom)?;
|
||||
let implementations = implementation_wrappers
|
||||
.grouped_by(&implementations)
|
||||
.into_iter()
|
||||
.zip(implementations)
|
||||
.map(|(wrappers, implementation)| JsonImplementation {
|
||||
identifier: implementation.identifier,
|
||||
label: implementation.label,
|
||||
wrappers: wrappers
|
||||
.into_iter()
|
||||
.map(
|
||||
|ImplementationWrapper {
|
||||
identifier,
|
||||
label,
|
||||
is_asm,
|
||||
is_formatter,
|
||||
..
|
||||
}| {
|
||||
Wrapper {
|
||||
identifier,
|
||||
label,
|
||||
is_asm,
|
||||
is_formatter,
|
||||
}
|
||||
},
|
||||
)
|
||||
.collect(),
|
||||
})
|
||||
.await?;
|
||||
Ok(implementation_wrappers
|
||||
.grouped_by(&implementations)
|
||||
.into_iter()
|
||||
.zip(implementations)
|
||||
.map(|(wrappers, implementation)| JsonImplementation {
|
||||
identifier: implementation.identifier,
|
||||
label: implementation.label,
|
||||
wrappers: wrappers
|
||||
.into_iter()
|
||||
.map(
|
||||
|ImplementationWrapper {
|
||||
identifier,
|
||||
label,
|
||||
is_asm,
|
||||
is_formatter,
|
||||
..
|
||||
}| {
|
||||
Wrapper {
|
||||
identifier,
|
||||
label,
|
||||
is_asm,
|
||||
is_formatter,
|
||||
}
|
||||
},
|
||||
)
|
||||
.collect(),
|
||||
})
|
||||
.collect())
|
||||
},
|
||||
)
|
||||
.await
|
||||
.map_err(warp::reject::custom)?;
|
||||
Ok(warp::reply::with_header(
|
||||
warp::reply::json(&JsonLanguage {
|
||||
mode,
|
||||
mime,
|
||||
shared_wrappers,
|
||||
implementations,
|
||||
}),
|
||||
CACHE_CONTROL,
|
||||
"max-age=14400",
|
||||
))
|
||||
.collect();
|
||||
Ok(warp::reply::with_header(
|
||||
warp::reply::json(&JsonLanguage {
|
||||
mode,
|
||||
mime,
|
||||
shared_wrappers,
|
||||
implementations,
|
||||
}),
|
||||
CACHE_CONTROL,
|
||||
"max-age=14400",
|
||||
))
|
||||
})
|
||||
.compat()
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use crate::schema::languages;
|
||||
use crate::PgPool;
|
||||
use crate::Connection;
|
||||
use diesel::prelude::*;
|
||||
use futures::Future;
|
||||
use futures03::TryFutureExt;
|
||||
use serde::Serialize;
|
||||
use tokio_diesel::AsyncRunQueryDsl;
|
||||
use tokio_executor::blocking;
|
||||
use warp::{Rejection, Reply};
|
||||
|
||||
#[derive(Queryable, Serialize)]
|
||||
|
@ -13,11 +13,13 @@ struct Language {
|
|||
name: String,
|
||||
}
|
||||
|
||||
pub fn languages(pool: &'static PgPool) -> impl Future<Item = impl Reply, Error = Rejection> {
|
||||
languages::table
|
||||
.select((languages::identifier, languages::name))
|
||||
.load_async(pool)
|
||||
.compat()
|
||||
.map(|languages: Vec<Language>| warp::reply::json(&languages))
|
||||
.map_err(warp::reject::custom)
|
||||
pub fn languages(connection: Connection) -> impl Future<Item = impl Reply, Error = Rejection> {
|
||||
blocking::run(move || {
|
||||
let languages: Vec<Language> = languages::table
|
||||
.select((languages::identifier, languages::name))
|
||||
.load(&connection)
|
||||
.map_err(warp::reject::custom)?;
|
||||
Ok(warp::reply::json(&languages))
|
||||
})
|
||||
.compat()
|
||||
}
|
||||
|
|
|
@ -2,51 +2,45 @@ use crate::models::language::{Language, Selection};
|
|||
use crate::models::paste::{ExternPaste, Paste};
|
||||
use crate::schema::{languages, pastes};
|
||||
use crate::templates::RenderRucte;
|
||||
use crate::{templates, PgPool};
|
||||
use crate::{templates, Connection};
|
||||
use diesel::prelude::*;
|
||||
use futures::future::*;
|
||||
use futures03::TryFutureExt;
|
||||
use tokio_diesel::{AsyncRunQueryDsl, OptionalExtension};
|
||||
use tokio_executor::blocking;
|
||||
use warp::http::Response;
|
||||
use warp::{Rejection, Reply};
|
||||
|
||||
pub fn display_paste(
|
||||
requested_identifier: String,
|
||||
pool: &'static PgPool,
|
||||
connection: Connection,
|
||||
) -> impl Future<Item = impl Reply, Error = Rejection> {
|
||||
Paste::delete_old(pool)
|
||||
.and_then(move |()| Language::fetch(pool))
|
||||
.and_then(move |languages| {
|
||||
pastes::table
|
||||
.inner_join(languages::table)
|
||||
.select((
|
||||
pastes::paste,
|
||||
pastes::language_id,
|
||||
pastes::delete_at,
|
||||
languages::is_markdown,
|
||||
))
|
||||
.filter(pastes::identifier.eq(requested_identifier))
|
||||
.get_result_async(pool)
|
||||
.compat()
|
||||
.then(|result| result.optional())
|
||||
.map(|paste| {
|
||||
paste
|
||||
.ok_or_else(warp::reject::not_found)
|
||||
.and_then(|paste: Paste| {
|
||||
let selected_language = Some(paste.language_id);
|
||||
Response::builder().html(|o| {
|
||||
templates::display_paste(
|
||||
o,
|
||||
ExternPaste::from_paste(paste),
|
||||
Selection {
|
||||
languages,
|
||||
selected_language,
|
||||
},
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
blocking::run(move || {
|
||||
Paste::delete_old(&connection)?;
|
||||
let languages = Language::fetch(&connection)?;
|
||||
let paste: Paste = pastes::table
|
||||
.inner_join(languages::table)
|
||||
.select((
|
||||
pastes::paste,
|
||||
pastes::language_id,
|
||||
pastes::delete_at,
|
||||
languages::is_markdown,
|
||||
))
|
||||
.filter(pastes::identifier.eq(requested_identifier))
|
||||
.get_result(&connection)
|
||||
.optional()
|
||||
.map_err(warp::reject::custom)?
|
||||
.ok_or_else(warp::reject::not_found)?;
|
||||
let selected_language = Some(paste.language_id);
|
||||
Response::builder().html(|o| {
|
||||
templates::display_paste(
|
||||
o,
|
||||
ExternPaste::from_paste(paste),
|
||||
Selection {
|
||||
languages,
|
||||
selected_language,
|
||||
},
|
||||
)
|
||||
})
|
||||
.map_err(warp::reject::custom)
|
||||
.flatten()
|
||||
})
|
||||
.compat()
|
||||
}
|
||||
|
|
|
@ -1,22 +1,24 @@
|
|||
use crate::models::language::{Language, Selection};
|
||||
use crate::templates::RenderRucte;
|
||||
use crate::{templates, PgPool};
|
||||
use crate::{templates, Connection};
|
||||
use futures::Future;
|
||||
use futures03::TryFutureExt;
|
||||
use tokio_executor::blocking;
|
||||
use warp::http::Response;
|
||||
use warp::{Rejection, Reply};
|
||||
|
||||
pub fn index(pool: &'static PgPool) -> impl Future<Item = impl Reply, Error = Rejection> {
|
||||
Language::fetch(pool)
|
||||
.map_err(warp::reject::custom)
|
||||
.and_then(|languages| {
|
||||
Response::builder().html(|o| {
|
||||
templates::index(
|
||||
o,
|
||||
Selection {
|
||||
languages,
|
||||
selected_language: None,
|
||||
},
|
||||
)
|
||||
})
|
||||
pub fn index(connection: Connection) -> impl Future<Item = impl Reply, Error = Rejection> {
|
||||
blocking::run(move || {
|
||||
let languages = Language::fetch(&connection)?;
|
||||
Response::builder().html(|o| {
|
||||
templates::index(
|
||||
o,
|
||||
Selection {
|
||||
languages,
|
||||
selected_language: None,
|
||||
},
|
||||
)
|
||||
})
|
||||
})
|
||||
.compat()
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::schema::{languages, pastes};
|
||||
use crate::PgPool;
|
||||
use crate::Connection;
|
||||
use chrono::{DateTime, Duration, Utc};
|
||||
use diesel::prelude::*;
|
||||
use futures::Future;
|
||||
|
@ -7,7 +7,7 @@ use futures03::TryFutureExt;
|
|||
use rand::prelude::*;
|
||||
use serde::de::IgnoredAny;
|
||||
use serde::Deserialize;
|
||||
use tokio_diesel::AsyncRunQueryDsl;
|
||||
use tokio_executor::blocking;
|
||||
use warp::http::Uri;
|
||||
use warp::{Rejection, Reply};
|
||||
|
||||
|
@ -31,40 +31,40 @@ struct NewPaste {
|
|||
|
||||
pub fn insert_paste(
|
||||
form: PasteForm,
|
||||
pool: &'static PgPool,
|
||||
connection: Connection,
|
||||
) -> impl Future<Item = impl Reply, Error = Rejection> {
|
||||
let mut rng = thread_rng();
|
||||
let identifier: String = (0..10)
|
||||
.map(|_| char::from(*CHARACTERS.choose(&mut rng).expect("a random character")))
|
||||
.collect();
|
||||
let cloned_identifier = identifier.clone();
|
||||
let PasteForm {
|
||||
language,
|
||||
code,
|
||||
autodelete,
|
||||
} = form;
|
||||
let delete_at = autodelete.map(|_| Utc::now() + Duration::hours(24));
|
||||
languages::table
|
||||
.select(languages::language_id)
|
||||
.filter(languages::identifier.eq(language))
|
||||
.get_result_async(pool)
|
||||
.compat()
|
||||
.and_then(move |language_id| {
|
||||
diesel::insert_into(pastes::table)
|
||||
.values(NewPaste {
|
||||
identifier,
|
||||
delete_at,
|
||||
language_id,
|
||||
paste: code,
|
||||
})
|
||||
.execute_async(pool)
|
||||
.compat()
|
||||
})
|
||||
.map_err(warp::reject::custom)
|
||||
.and_then(move |_| {
|
||||
blocking::run(move || {
|
||||
let mut rng = thread_rng();
|
||||
let identifier: String = (0..10)
|
||||
.map(|_| char::from(*CHARACTERS.choose(&mut rng).expect("a random character")))
|
||||
.collect();
|
||||
let cloned_identifier = identifier.clone();
|
||||
let PasteForm {
|
||||
language,
|
||||
code,
|
||||
autodelete,
|
||||
} = form;
|
||||
let delete_at = autodelete.map(|_| Utc::now() + Duration::hours(24));
|
||||
let language_id = languages::table
|
||||
.select(languages::language_id)
|
||||
.filter(languages::identifier.eq(language))
|
||||
.get_result(&connection)
|
||||
.map_err(warp::reject::custom)?;
|
||||
diesel::insert_into(pastes::table)
|
||||
.values(NewPaste {
|
||||
identifier,
|
||||
delete_at,
|
||||
language_id,
|
||||
paste: code,
|
||||
})
|
||||
.execute(&connection)
|
||||
.map_err(warp::reject::custom)?;
|
||||
|
||||
Ok(warp::redirect(
|
||||
format!("/{}", cloned_identifier)
|
||||
.parse::<Uri>()
|
||||
.map_err(warp::reject::custom)
|
||||
})
|
||||
.map(warp::redirect)
|
||||
.map_err(warp::reject::custom)?,
|
||||
))
|
||||
})
|
||||
.compat()
|
||||
}
|
||||
|
|
|
@ -7,101 +7,95 @@ mod raw_paste;
|
|||
mod run;
|
||||
|
||||
use crate::templates::{self, RenderRucte};
|
||||
use crate::PgPool;
|
||||
use crate::Connection;
|
||||
use diesel::prelude::*;
|
||||
use diesel::r2d2::{ConnectionManager, Pool};
|
||||
use futures03::TryFutureExt;
|
||||
use std::ffi::OsStr;
|
||||
use std::path::PathBuf;
|
||||
use tokio_executor::blocking;
|
||||
use warp::http::header::{
|
||||
HeaderMap, HeaderValue, CONTENT_SECURITY_POLICY, REFERRER_POLICY, X_FRAME_OPTIONS,
|
||||
};
|
||||
use warp::http::{Response, StatusCode};
|
||||
use warp::{path, Filter, Rejection, Reply};
|
||||
|
||||
fn pool_route(
|
||||
pool: &'static PgPool,
|
||||
) -> impl Filter<Extract = (&'static PgPool,), Error = Rejection> + Copy {
|
||||
warp::any().and_then(move || -> Result<_, Rejection> { Ok(pool) })
|
||||
type PgPool = Pool<ConnectionManager<PgConnection>>;
|
||||
|
||||
fn connection(pool: PgPool) -> impl Filter<Extract = (Connection,), Error = Rejection> + Clone {
|
||||
warp::any().and_then(move || {
|
||||
let pool = pool.clone();
|
||||
blocking::run(move || pool.get().map_err(warp::reject::custom)).compat()
|
||||
})
|
||||
}
|
||||
|
||||
fn index(pool: &'static PgPool) -> impl Filter<Extract = impl Reply, Error = Rejection> + Copy {
|
||||
fn index(pool: PgPool) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
||||
warp::path::end()
|
||||
.and(warp::get2())
|
||||
.and(pool_route(pool))
|
||||
.and(connection(pool))
|
||||
.and_then(index::index)
|
||||
}
|
||||
|
||||
fn display_paste(
|
||||
pool: &'static PgPool,
|
||||
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Copy {
|
||||
fn display_paste(pool: PgPool) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
||||
warp::path::param()
|
||||
.and(warp::path::end())
|
||||
.and(warp::get2())
|
||||
.and(pool_route(pool))
|
||||
.and(connection(pool))
|
||||
.and_then(display_paste::display_paste)
|
||||
}
|
||||
|
||||
fn raw_paste(pool: &'static PgPool) -> impl Filter<Extract = impl Reply, Error = Rejection> + Copy {
|
||||
fn raw_paste(pool: PgPool) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
||||
with_ext("txt")
|
||||
.and(warp::get2())
|
||||
.and(pool_route(pool))
|
||||
.and(connection(pool))
|
||||
.and_then(raw_paste::raw_paste)
|
||||
}
|
||||
|
||||
fn insert_paste(
|
||||
pool: &'static PgPool,
|
||||
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Copy {
|
||||
fn insert_paste(pool: PgPool) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
||||
warp::path::end()
|
||||
.and(warp::post2())
|
||||
.and(warp::body::content_length_limit(1_000_000))
|
||||
.and(warp::body::form())
|
||||
.and(pool_route(pool))
|
||||
.and(connection(pool))
|
||||
.and_then(insert_paste::insert_paste)
|
||||
}
|
||||
|
||||
fn api_language(
|
||||
pool: &'static PgPool,
|
||||
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Copy {
|
||||
fn api_language(pool: PgPool) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
||||
path!("api" / "v0" / "language" / String)
|
||||
.and(warp::path::end())
|
||||
.and(warp::get2())
|
||||
.and(pool_route(pool))
|
||||
.and_then(|identifier, pool| {
|
||||
Box::pin(api_language::api_language(identifier, pool)).compat()
|
||||
})
|
||||
.and(connection(pool))
|
||||
.and_then(api_language::api_language)
|
||||
}
|
||||
|
||||
fn shared_run(
|
||||
pool: &'static PgPool,
|
||||
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Copy {
|
||||
fn shared_run(pool: PgPool) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
||||
path!("api" / "v0" / "run" / String / String)
|
||||
.and(warp::path::end())
|
||||
.and(warp::post2())
|
||||
.and(warp::body::content_length_limit(1_000_000))
|
||||
.and(warp::body::form())
|
||||
.and(pool_route(pool))
|
||||
.and(connection(pool))
|
||||
.and_then(run::shared)
|
||||
}
|
||||
|
||||
fn implementation_run(
|
||||
pool: &'static PgPool,
|
||||
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Copy {
|
||||
pool: PgPool,
|
||||
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
||||
path!("api" / "v0" / "run" / String / String / String)
|
||||
.and(warp::path::end())
|
||||
.and(warp::post2())
|
||||
.and(warp::body::content_length_limit(1_000_000))
|
||||
.and(warp::body::form())
|
||||
.and(pool_route(pool))
|
||||
.and(connection(pool))
|
||||
.and_then(run::implementation)
|
||||
}
|
||||
|
||||
fn api_v1_languages(
|
||||
pool: &'static PgPool,
|
||||
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Copy {
|
||||
fn api_v1_languages(pool: PgPool) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
||||
path!("api" / "v1")
|
||||
.and(warp::path("languages"))
|
||||
.and(warp::path::end())
|
||||
.and(warp::get2())
|
||||
.and(pool_route(pool))
|
||||
.and(connection(pool))
|
||||
.and_then(api_v1::languages::languages)
|
||||
}
|
||||
|
||||
|
@ -115,7 +109,9 @@ fn favicon() -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
|||
.and(warp::fs::file("static/favicon.ico"))
|
||||
}
|
||||
|
||||
pub fn routes(pool: &'static PgPool) -> impl Filter<Extract = (impl Reply,), Error = Rejection> {
|
||||
pub fn routes(
|
||||
pool: Pool<ConnectionManager<PgConnection>>,
|
||||
) -> impl Filter<Extract = (impl Reply,), Error = Rejection> {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(
|
||||
CONTENT_SECURITY_POLICY,
|
||||
|
@ -133,14 +129,14 @@ pub fn routes(pool: &'static PgPool) -> impl Filter<Extract = (impl Reply,), Err
|
|||
);
|
||||
headers.insert(X_FRAME_OPTIONS, HeaderValue::from_static("DENY"));
|
||||
headers.insert(REFERRER_POLICY, HeaderValue::from_static("no-referrer"));
|
||||
index(pool)
|
||||
index(pool.clone())
|
||||
.or(favicon())
|
||||
.or(raw_paste(pool))
|
||||
.or(display_paste(pool))
|
||||
.or(insert_paste(pool))
|
||||
.or(api_language(pool))
|
||||
.or(api_v1_languages(pool))
|
||||
.or(shared_run(pool))
|
||||
.or(raw_paste(pool.clone()))
|
||||
.or(display_paste(pool.clone()))
|
||||
.or(insert_paste(pool.clone()))
|
||||
.or(api_language(pool.clone()))
|
||||
.or(api_v1_languages(pool.clone()))
|
||||
.or(shared_run(pool.clone()))
|
||||
.or(implementation_run(pool))
|
||||
.or(static_dir())
|
||||
.recover(not_found)
|
||||
|
@ -174,7 +170,6 @@ fn not_found(rejection: Rejection) -> Result<impl Reply, Rejection> {
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::routes;
|
||||
use crate::PgPool;
|
||||
use diesel::r2d2::{ConnectionManager, CustomizeConnection, Pool};
|
||||
use diesel::Connection;
|
||||
use lazy_static::lazy_static;
|
||||
|
@ -188,19 +183,17 @@ mod test {
|
|||
use warp::Filter;
|
||||
|
||||
lazy_static! {
|
||||
static ref POOL: PgPool = {
|
||||
static ref ROUTES: BoxedFilter<(Response,)> = {
|
||||
let pool = Pool::builder()
|
||||
.connection_customizer(Box::new(ExecuteWithinTransaction))
|
||||
.max_size(1)
|
||||
.build(ConnectionManager::new(env::var("DATABASE_URL").expect(
|
||||
"Setting DATABASE_URL environment variable required to run tests",
|
||||
)))
|
||||
.expect("Couldn't create a connection pool");
|
||||
.expect("Couldn't create a connection connection");
|
||||
diesel_migrations::run_pending_migrations(&pool.get().unwrap()).unwrap();
|
||||
pool
|
||||
routes(pool).map(Reply::into_response).boxed()
|
||||
};
|
||||
static ref ROUTES: BoxedFilter<(Response,)> =
|
||||
routes(&POOL).map(Reply::into_response).boxed();
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -1,26 +1,25 @@
|
|||
use crate::models::paste::Paste;
|
||||
use crate::schema::pastes::dsl::*;
|
||||
use crate::PgPool;
|
||||
use crate::Connection;
|
||||
use diesel::prelude::*;
|
||||
use futures::Future;
|
||||
use futures03::TryFutureExt;
|
||||
use tokio_diesel::{AsyncRunQueryDsl, OptionalExtension};
|
||||
use tokio_executor::blocking;
|
||||
use warp::Rejection;
|
||||
|
||||
pub fn raw_paste(
|
||||
requested_identifier: String,
|
||||
pool: &'static PgPool,
|
||||
connection: Connection,
|
||||
) -> impl Future<Item = String, Error = Rejection> {
|
||||
Paste::delete_old(pool)
|
||||
.and_then(move |()| {
|
||||
pastes
|
||||
.select(paste)
|
||||
.filter(identifier.eq(requested_identifier))
|
||||
.get_result_async(pool)
|
||||
.compat()
|
||||
.then(|result| result.optional())
|
||||
.map(|paste_contents| paste_contents.ok_or_else(warp::reject::not_found))
|
||||
})
|
||||
.map_err(warp::reject::custom)
|
||||
.flatten()
|
||||
blocking::run(move || {
|
||||
Paste::delete_old(&connection)?;
|
||||
pastes
|
||||
.select(paste)
|
||||
.filter(identifier.eq(requested_identifier))
|
||||
.get_result(&connection)
|
||||
.optional()
|
||||
.map_err(warp::reject::custom)?
|
||||
.ok_or_else(warp::reject::not_found)
|
||||
})
|
||||
.compat()
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::schema::{implementation_wrappers, implementations, languages, shared_wrappers};
|
||||
use crate::PgPool;
|
||||
use crate::Connection;
|
||||
use diesel::prelude::*;
|
||||
use futures::Future;
|
||||
use futures03::TryFutureExt;
|
||||
|
@ -7,7 +7,7 @@ use lazy_static::lazy_static;
|
|||
use reqwest::r#async::Client;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::env;
|
||||
use tokio_diesel::{AsyncRunQueryDsl, OptionalExtension};
|
||||
use tokio_executor::blocking;
|
||||
use warp::{Rejection, Reply};
|
||||
|
||||
lazy_static! {
|
||||
|
@ -49,35 +49,36 @@ pub fn shared(
|
|||
code,
|
||||
compiler_options,
|
||||
}: Form,
|
||||
pool: &'static PgPool,
|
||||
connection: Connection,
|
||||
) -> impl Future<Item = impl Reply, Error = Rejection> {
|
||||
languages::table
|
||||
.inner_join(shared_wrappers::table)
|
||||
.filter(languages::identifier.eq(language))
|
||||
.filter(shared_wrappers::identifier.eq(identifier))
|
||||
.select(shared_wrappers::code)
|
||||
.get_result_async(pool)
|
||||
.compat()
|
||||
.then(|result| result.optional())
|
||||
.map(|wrapper| wrapper.ok_or_else(warp::reject::not_found))
|
||||
.map_err(warp::reject::custom)
|
||||
.flatten()
|
||||
.and_then(move |language_code: String| {
|
||||
CLIENT
|
||||
.post(SANDBOX_URL.as_str())
|
||||
.json(&Request {
|
||||
files: vec![File {
|
||||
name: "code",
|
||||
contents: code,
|
||||
}],
|
||||
stdin: "",
|
||||
code: language_code.replace("%s", &compiler_options),
|
||||
})
|
||||
.send()
|
||||
.and_then(|mut r| r.json())
|
||||
.map_err(warp::reject::custom)
|
||||
})
|
||||
.map(|output: Output| warp::reply::json(&output))
|
||||
blocking::run(move || {
|
||||
languages::table
|
||||
.inner_join(shared_wrappers::table)
|
||||
.filter(languages::identifier.eq(language))
|
||||
.filter(shared_wrappers::identifier.eq(identifier))
|
||||
.select(shared_wrappers::code)
|
||||
.get_result(&connection)
|
||||
.optional()
|
||||
.map_err(warp::reject::custom)?
|
||||
.ok_or_else(warp::reject::not_found)
|
||||
})
|
||||
.compat()
|
||||
.and_then(move |language_code: String| {
|
||||
CLIENT
|
||||
.post(SANDBOX_URL.as_str())
|
||||
.json(&Request {
|
||||
files: vec![File {
|
||||
name: "code",
|
||||
contents: code,
|
||||
}],
|
||||
stdin: "",
|
||||
code: language_code.replace("%s", &compiler_options),
|
||||
})
|
||||
.send()
|
||||
.and_then(|mut r| r.json())
|
||||
.map_err(warp::reject::custom)
|
||||
})
|
||||
.map(|output: Output| warp::reply::json(&output))
|
||||
}
|
||||
|
||||
pub fn implementation(
|
||||
|
@ -88,35 +89,36 @@ pub fn implementation(
|
|||
code,
|
||||
compiler_options,
|
||||
}: Form,
|
||||
pool: &'static PgPool,
|
||||
connection: Connection,
|
||||
) -> impl Future<Item = impl Reply, Error = Rejection> {
|
||||
implementations::table
|
||||
.inner_join(implementation_wrappers::table)
|
||||
.inner_join(languages::table)
|
||||
.filter(languages::identifier.eq(language))
|
||||
.filter(implementations::identifier.eq(implementation))
|
||||
.filter(implementation_wrappers::identifier.eq(identifier))
|
||||
.select(implementation_wrappers::code)
|
||||
.get_result_async(pool)
|
||||
.compat()
|
||||
.then(|result| result.optional())
|
||||
.map(|wrapper| wrapper.ok_or_else(warp::reject::not_found))
|
||||
.map_err(warp::reject::custom)
|
||||
.flatten()
|
||||
.and_then(move |language_code: String| {
|
||||
CLIENT
|
||||
.post(SANDBOX_URL.as_str())
|
||||
.json(&Request {
|
||||
files: vec![File {
|
||||
name: "code",
|
||||
contents: code,
|
||||
}],
|
||||
stdin: "",
|
||||
code: language_code.replace("%s", &compiler_options),
|
||||
})
|
||||
.send()
|
||||
.and_then(|mut r| r.json())
|
||||
.map_err(warp::reject::custom)
|
||||
})
|
||||
.map(|output: Output| warp::reply::json(&output))
|
||||
blocking::run(move || {
|
||||
implementations::table
|
||||
.inner_join(implementation_wrappers::table)
|
||||
.inner_join(languages::table)
|
||||
.filter(languages::identifier.eq(language))
|
||||
.filter(implementations::identifier.eq(implementation))
|
||||
.filter(implementation_wrappers::identifier.eq(identifier))
|
||||
.select(implementation_wrappers::code)
|
||||
.get_result(&connection)
|
||||
.optional()
|
||||
.map_err(warp::reject::custom)?
|
||||
.ok_or_else(warp::reject::not_found)
|
||||
})
|
||||
.compat()
|
||||
.and_then(move |language_code: String| {
|
||||
CLIENT
|
||||
.post(SANDBOX_URL.as_str())
|
||||
.json(&Request {
|
||||
files: vec![File {
|
||||
name: "code",
|
||||
contents: code,
|
||||
}],
|
||||
stdin: "",
|
||||
code: language_code.replace("%s", &compiler_options),
|
||||
})
|
||||
.send()
|
||||
.and_then(|mut r| r.json())
|
||||
.map_err(warp::reject::custom)
|
||||
})
|
||||
.map(|json: Output| warp::reply::json(&json))
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user