mirror of https://github.com/raftario/filite.git
Refactor, fixes and first template
This commit is contained in:
parent
daef6e9b94
commit
820a82bd60
56
src/auth.rs
56
src/auth.rs
|
@ -10,44 +10,38 @@ use sled::Db;
|
|||
use tokio::task;
|
||||
use warp::{http::HeaderValue, Filter, Rejection};
|
||||
|
||||
pub fn optional(
|
||||
db: &'static Db,
|
||||
config: &'static Config,
|
||||
) -> impl Filter<Extract = (Option<User>,), Error = Rejection> + Copy + Send + Sync + 'static {
|
||||
warp::header::value("Authorization")
|
||||
.and_then(move |header| async move {
|
||||
match user(header, db, config) {
|
||||
Ok(u) => Ok(Some(u)),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
})
|
||||
.or(warp::any().and_then(|| async move { Result::<_, Rejection>::Ok(None) }))
|
||||
.unify()
|
||||
}
|
||||
|
||||
pub fn required(
|
||||
pub fn auth(
|
||||
db: &'static Db,
|
||||
config: &'static Config,
|
||||
) -> impl Filter<Extract = (User,), Error = Rejection> + Copy + Send + Sync + 'static {
|
||||
warp::header::value("Authorization")
|
||||
.and_then(move |header| async move { user(header, db, config) })
|
||||
.or(warp::any().and_then(|| async move {
|
||||
Result::<HeaderValue, Rejection>::Err(crate::reject::unauthorized(
|
||||
"Authentication Required",
|
||||
))
|
||||
}))
|
||||
.unify()
|
||||
.and_then(move |header| user(header, db, config))
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(db))]
|
||||
fn user(header: HeaderValue, db: &Db, config: &Config) -> Result<User, Rejection> {
|
||||
let credentials = Basic::decode(&header).or_400()?;
|
||||
async fn user(header: HeaderValue, db: &Db, config: &Config) -> Result<User, Rejection> {
|
||||
let credentials = Basic::decode(&header).or_bad_request("Invalid Credentials")?;
|
||||
|
||||
let user = crate::db::user(credentials.username(), db)
|
||||
.or_500()?
|
||||
.or_401()?;
|
||||
if !verify(
|
||||
&user.password_hash,
|
||||
credentials.password().as_bytes(),
|
||||
&config.password,
|
||||
)
|
||||
.or_500()?
|
||||
{
|
||||
return Err(crate::reject::unauthorized());
|
||||
.or_unauthorized("Invalid Credentials")?;
|
||||
|
||||
let valid = !task::block_in_place(|| {
|
||||
verify(
|
||||
&user.password_hash,
|
||||
credentials.password().as_bytes(),
|
||||
&config.password,
|
||||
)
|
||||
})
|
||||
.or_500()?;
|
||||
if !valid {
|
||||
return Err(crate::reject::unauthorized("Invalid Credentials"));
|
||||
}
|
||||
|
||||
Ok(user)
|
||||
|
@ -56,10 +50,8 @@ fn user(header: HeaderValue, db: &Db, config: &Config) -> Result<User, Rejection
|
|||
#[tracing::instrument(level = "debug", skip(encoded, password))]
|
||||
fn verify(encoded: &str, password: &[u8], config: &PasswordConfig) -> Result<bool> {
|
||||
let res = match &config.secret {
|
||||
Some(s) => task::block_in_place(move || {
|
||||
argon2::verify_encoded_ext(encoded, password, s.as_bytes(), &[])
|
||||
})?,
|
||||
None => task::block_in_place(move || argon2::verify_encoded(encoded, password))?,
|
||||
Some(s) => argon2::verify_encoded_ext(encoded, password, s.as_bytes(), &[])?,
|
||||
None => argon2::verify_encoded(encoded, password)?,
|
||||
};
|
||||
Ok(res)
|
||||
}
|
||||
|
|
161
src/db.rs
161
src/db.rs
|
@ -5,7 +5,6 @@ use rand::{distributions::Alphanumeric, Rng};
|
|||
use serde::{Deserialize, Serialize};
|
||||
use sled::Db;
|
||||
use std::fmt;
|
||||
use tokio::task;
|
||||
|
||||
#[tracing::instrument(level = "debug")]
|
||||
pub fn connect(config: &DatabaseConfig) -> Result<&'static Db> {
|
||||
|
@ -33,17 +32,7 @@ pub enum FiliteInner {
|
|||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(db))]
|
||||
pub fn filite(id: &str, inc: bool, db: &Db) -> Result<Option<Filite>> {
|
||||
task::block_in_place(move || {
|
||||
if inc {
|
||||
filite_inc(id, db)
|
||||
} else {
|
||||
filite_noinc(id, db)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn filite_inc(id: &str, db: &Db) -> Result<Option<Filite>> {
|
||||
pub fn get(id: &str, db: &Db) -> Result<Option<Filite>> {
|
||||
macro_rules! tryy {
|
||||
($op:expr, $default:expr, $val:ident) => {
|
||||
match $op {
|
||||
|
@ -71,25 +60,14 @@ fn filite_inc(id: &str, db: &Db) -> Result<Option<Filite>> {
|
|||
filite
|
||||
}
|
||||
|
||||
fn filite_noinc(id: &str, db: &Db) -> Result<Option<Filite>> {
|
||||
let bytes = match db.get(id)? {
|
||||
Some(b) => b,
|
||||
None => return Ok(None),
|
||||
};
|
||||
let filite = bincode::deserialize(&bytes)?;
|
||||
Ok(Some(filite))
|
||||
}
|
||||
|
||||
fn insert_filite(id: &str, filite: Filite, db: &Db) -> Result<Option<Filite>> {
|
||||
task::block_in_place(move || {
|
||||
if db.contains_key(id)? {
|
||||
return Ok(None);
|
||||
}
|
||||
if db.contains_key(id)? {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let bytes = bincode::serialize(&filite)?;
|
||||
db.insert(id, bytes)?;
|
||||
Ok(Some(filite))
|
||||
})
|
||||
let bytes = bincode::serialize(&filite)?;
|
||||
db.insert(id, bytes)?;
|
||||
Ok(Some(filite))
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(db))]
|
||||
|
@ -142,34 +120,33 @@ pub fn insert_text(id: &str, owner: String, data: String, db: &Db) -> Result<Opt
|
|||
|
||||
#[tracing::instrument(level = "debug", skip(db))]
|
||||
pub fn delete_filite(id: &str, user: &User, db: &Db) -> Result<Option<Filite>> {
|
||||
task::block_in_place(move || match filite_noinc(id, db)? {
|
||||
Some(f) => {
|
||||
if user.admin || f.owner == user.id {
|
||||
db.remove(id)?;
|
||||
Ok(Some(f))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
None => Ok(None),
|
||||
})
|
||||
let bytes = match db.get(id)? {
|
||||
Some(b) => b,
|
||||
None => return Ok(None),
|
||||
};
|
||||
let filite: Filite = bincode::deserialize(&bytes)?;
|
||||
|
||||
if user.admin || filite.owner == user.id {
|
||||
db.remove(id)?;
|
||||
Ok(Some(filite))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(db))]
|
||||
pub fn random_id(length: usize, db: &Db) -> Result<String> {
|
||||
task::block_in_place(move || {
|
||||
let mut id;
|
||||
loop {
|
||||
id = rand::thread_rng()
|
||||
.sample_iter(Alphanumeric)
|
||||
.take(length)
|
||||
.collect();
|
||||
let mut id;
|
||||
loop {
|
||||
id = rand::thread_rng()
|
||||
.sample_iter(Alphanumeric)
|
||||
.take(length)
|
||||
.collect();
|
||||
|
||||
if !db.contains_key(&id)? {
|
||||
break Ok(id);
|
||||
}
|
||||
if !db.contains_key(&id)? {
|
||||
break Ok(id);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
|
@ -196,19 +173,17 @@ struct DbUser {
|
|||
|
||||
#[tracing::instrument(level = "debug", skip(db))]
|
||||
pub fn user(id: &str, db: &Db) -> Result<Option<User>> {
|
||||
task::block_in_place(move || {
|
||||
let users = db.open_tree("users")?;
|
||||
let bytes = match users.get(id)? {
|
||||
Some(b) => b,
|
||||
None => return Ok(None),
|
||||
};
|
||||
let user: DbUser = bincode::deserialize(&bytes)?;
|
||||
Ok(Some(User {
|
||||
id: id.to_owned(),
|
||||
admin: user.admin,
|
||||
password_hash: user.password_hash,
|
||||
}))
|
||||
})
|
||||
let users = db.open_tree("users")?;
|
||||
let bytes = match users.get(id)? {
|
||||
Some(b) => b,
|
||||
None => return Ok(None),
|
||||
};
|
||||
let user: DbUser = bincode::deserialize(&bytes)?;
|
||||
Ok(Some(User {
|
||||
id: id.to_owned(),
|
||||
admin: user.admin,
|
||||
password_hash: user.password_hash,
|
||||
}))
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(password, db))]
|
||||
|
@ -219,42 +194,38 @@ pub fn insert_user(
|
|||
db: &Db,
|
||||
config: &Config,
|
||||
) -> Result<Option<User>> {
|
||||
task::block_in_place(move || {
|
||||
let users = db.open_tree("users")?;
|
||||
if users.contains_key(id)? {
|
||||
return Ok(None);
|
||||
}
|
||||
let users = db.open_tree("users")?;
|
||||
if users.contains_key(id)? {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let password_hash = crate::auth::hash(password.as_bytes(), &config.password)?;
|
||||
let user = DbUser {
|
||||
admin,
|
||||
password_hash,
|
||||
};
|
||||
let password_hash = crate::auth::hash(password.as_bytes(), &config.password)?;
|
||||
let user = DbUser {
|
||||
admin,
|
||||
password_hash,
|
||||
};
|
||||
|
||||
let bytes = bincode::serialize(&user)?;
|
||||
users.insert(id, bytes)?;
|
||||
Ok(Some(User {
|
||||
id: id.to_owned(),
|
||||
admin: user.admin,
|
||||
password_hash: user.password_hash,
|
||||
}))
|
||||
})
|
||||
let bytes = bincode::serialize(&user)?;
|
||||
users.insert(id, bytes)?;
|
||||
Ok(Some(User {
|
||||
id: id.to_owned(),
|
||||
admin: user.admin,
|
||||
password_hash: user.password_hash,
|
||||
}))
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(db))]
|
||||
pub fn delete_user(id: &str, db: &Db) -> Result<Option<User>> {
|
||||
task::block_in_place(move || {
|
||||
let users = db.open_tree("users")?;
|
||||
match users.remove(id)? {
|
||||
Some(b) => {
|
||||
let user: DbUser = bincode::deserialize(&b)?;
|
||||
Ok(Some(User {
|
||||
id: id.to_owned(),
|
||||
admin: user.admin,
|
||||
password_hash: user.password_hash,
|
||||
}))
|
||||
}
|
||||
None => Ok(None),
|
||||
let users = db.open_tree("users")?;
|
||||
match users.remove(id)? {
|
||||
Some(b) => {
|
||||
let user: DbUser = bincode::deserialize(&b)?;
|
||||
Ok(Some(User {
|
||||
id: id.to_owned(),
|
||||
admin: user.admin,
|
||||
password_hash: user.password_hash,
|
||||
}))
|
||||
}
|
||||
})
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,27 +7,21 @@ use warp::{
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
enum FiliteRejection {
|
||||
BadRequest,
|
||||
Unauthorized,
|
||||
BadRequest(String),
|
||||
Unauthorized(String),
|
||||
NotFound,
|
||||
Conflict,
|
||||
InternalServerError,
|
||||
|
||||
Custom(String, StatusCode),
|
||||
}
|
||||
impl Reject for FiliteRejection {}
|
||||
impl Reply for FiliteRejection {
|
||||
fn into_response(self) -> Response {
|
||||
match self {
|
||||
Self::BadRequest => {
|
||||
warp::reply::with_status("Bad Request", StatusCode::BAD_REQUEST).into_response()
|
||||
Self::BadRequest(reply) => {
|
||||
warp::reply::with_status(reply, StatusCode::BAD_REQUEST).into_response()
|
||||
}
|
||||
Self::Unauthorized => warp::reply::with_status(
|
||||
warp::reply::with_header(
|
||||
"Unauthorized",
|
||||
"WWW-Authenticate",
|
||||
r#"Basic realm="filite""#,
|
||||
),
|
||||
Self::Unauthorized(reply) => warp::reply::with_status(
|
||||
warp::reply::with_header(reply, "WWW-Authenticate", r#"Basic realm="filite""#),
|
||||
StatusCode::UNAUTHORIZED,
|
||||
)
|
||||
.into_response(),
|
||||
|
@ -41,44 +35,28 @@ impl Reply for FiliteRejection {
|
|||
warp::reply::with_status("Internal Server Error", StatusCode::INTERNAL_SERVER_ERROR)
|
||||
.into_response()
|
||||
}
|
||||
|
||||
Self::Custom(reply, status) => warp::reply::with_status(reply, status).into_response(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn unauthorized() -> Rejection {
|
||||
warp::reject::custom(FiliteRejection::Unauthorized)
|
||||
pub fn bad_request(reply: impl ToString) -> Rejection {
|
||||
warp::reject::custom(FiliteRejection::BadRequest(reply.to_string()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn custom<T: ToString>(reply: T, status: StatusCode) -> Rejection {
|
||||
warp::reject::custom(FiliteRejection::Custom(reply.to_string(), status))
|
||||
pub fn unauthorized(reply: impl ToString) -> Rejection {
|
||||
warp::reject::custom(FiliteRejection::Unauthorized(reply.to_string()))
|
||||
}
|
||||
|
||||
pub trait TryExt<T> {
|
||||
fn or_400(self) -> Result<T, Rejection>;
|
||||
fn or_401(self) -> Result<T, Rejection>;
|
||||
fn or_404(self) -> Result<T, Rejection>;
|
||||
fn or_409(self) -> Result<T, Rejection>;
|
||||
|
||||
fn or_500(self) -> Result<T, Rejection>;
|
||||
|
||||
fn or_bad_request(self, reply: impl ToString) -> Result<T, Rejection>;
|
||||
fn or_unauthorized(self, reply: impl ToString) -> Result<T, Rejection>;
|
||||
}
|
||||
|
||||
impl<T, E: Display> TryExt<T> for Result<T, E> {
|
||||
fn or_400(self) -> Result<T, Rejection> {
|
||||
self.map_err(|e| {
|
||||
tracing::info!("{}", e);
|
||||
warp::reject::custom(FiliteRejection::BadRequest)
|
||||
})
|
||||
}
|
||||
fn or_401(self) -> Result<T, Rejection> {
|
||||
self.map_err(|e| {
|
||||
tracing::info!("{}", e);
|
||||
warp::reject::custom(FiliteRejection::Unauthorized)
|
||||
})
|
||||
}
|
||||
fn or_404(self) -> Result<T, Rejection> {
|
||||
self.map_err(|e| {
|
||||
tracing::info!("{}", e);
|
||||
|
@ -98,15 +76,22 @@ impl<T, E: Display> TryExt<T> for Result<T, E> {
|
|||
warp::reject::custom(FiliteRejection::InternalServerError)
|
||||
})
|
||||
}
|
||||
|
||||
fn or_bad_request(self, reply: impl ToString) -> Result<T, Rejection> {
|
||||
self.map_err(|e| {
|
||||
tracing::info!("{}", e);
|
||||
bad_request(reply)
|
||||
})
|
||||
}
|
||||
fn or_unauthorized(self, reply: impl ToString) -> Result<T, Rejection> {
|
||||
self.map_err(|e| {
|
||||
tracing::info!("{}", e);
|
||||
unauthorized(reply)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TryExt<T> for Option<T> {
|
||||
fn or_400(self) -> Result<T, Rejection> {
|
||||
self.ok_or_else(|| warp::reject::custom(FiliteRejection::BadRequest))
|
||||
}
|
||||
fn or_401(self) -> Result<T, Rejection> {
|
||||
self.ok_or_else(|| warp::reject::custom(FiliteRejection::Unauthorized))
|
||||
}
|
||||
fn or_404(self) -> Result<T, Rejection> {
|
||||
self.ok_or_else(|| warp::reject::custom(FiliteRejection::NotFound))
|
||||
}
|
||||
|
@ -117,6 +102,13 @@ impl<T> TryExt<T> for Option<T> {
|
|||
fn or_500(self) -> Result<T, Rejection> {
|
||||
self.ok_or_else(|| warp::reject::custom(FiliteRejection::InternalServerError))
|
||||
}
|
||||
|
||||
fn or_bad_request(self, reply: impl ToString) -> Result<T, Rejection> {
|
||||
self.ok_or_else(move || bad_request(reply))
|
||||
}
|
||||
fn or_unauthorized(self, reply: impl ToString) -> Result<T, Rejection> {
|
||||
self.ok_or_else(move || unauthorized(reply))
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug")]
|
||||
|
|
|
@ -3,8 +3,10 @@ use crate::{
|
|||
db::{Filite, FiliteInner, User},
|
||||
reject::TryExt,
|
||||
};
|
||||
use askama::Template;
|
||||
use bytes::Bytes;
|
||||
use sled::Db;
|
||||
use tokio::task;
|
||||
use warp::{
|
||||
http::{StatusCode, Uri},
|
||||
reply::{Reply, Response},
|
||||
|
@ -15,59 +17,90 @@ pub fn handler(
|
|||
config: &'static Config,
|
||||
db: &'static Db,
|
||||
) -> impl Filter<Extract = (impl Reply,), Error = Rejection> + Copy + Send + Sync + 'static {
|
||||
let filite = warp::path!(String)
|
||||
let spectre = warp::path!("spectre.css").map(|| {
|
||||
warp::reply::with_header(
|
||||
include_str!("../static/spectre.min.css"),
|
||||
"Content-Type",
|
||||
"text/css",
|
||||
)
|
||||
});
|
||||
|
||||
let index = warp::path::end()
|
||||
.and(crate::auth::auth(db, config))
|
||||
.and_then(index);
|
||||
|
||||
let get = warp::path!(String)
|
||||
.and(warp::get())
|
||||
.and_then(move |id| filite(id, db));
|
||||
.and_then(move |id| get(id, db));
|
||||
|
||||
let post_file = warp::path!("f")
|
||||
.and(warp::post())
|
||||
.and(crate::auth::required(db, config))
|
||||
.and(crate::auth::auth(db, config))
|
||||
.and(warp::body::bytes())
|
||||
.and(warp::header::optional("Content-Type"))
|
||||
.and(warp::header::optional("X-ID-Length"))
|
||||
.and_then(move |user, data, mime, len| post_file(user, data, mime, len, db));
|
||||
let put_file = warp::path!("f" / String)
|
||||
.and(warp::put())
|
||||
.and(crate::auth::required(db, config))
|
||||
.and(crate::auth::auth(db, config))
|
||||
.and(warp::body::bytes())
|
||||
.and(warp::header::optional("Content-Type"))
|
||||
.and_then(move |id, user, data, mime| put_file(id, user, data, mime, db));
|
||||
|
||||
let post_link = warp::path!("l")
|
||||
.and(warp::post())
|
||||
.and(crate::auth::required(db, config))
|
||||
.and(crate::auth::auth(db, config))
|
||||
.and(crate::util::body())
|
||||
.and(warp::header::optional("X-ID-Length"))
|
||||
.and_then(move |user, location, len| post_link(user, location, len, db));
|
||||
let put_link = warp::path!("l" / String)
|
||||
.and(warp::put())
|
||||
.and(crate::auth::required(db, config))
|
||||
.and(crate::auth::auth(db, config))
|
||||
.and(crate::util::body())
|
||||
.and_then(move |id, user, location| put_link(id, user, location, db));
|
||||
|
||||
let post_text = warp::path!("t")
|
||||
.and(warp::post())
|
||||
.and(crate::auth::required(db, config))
|
||||
.and(crate::auth::auth(db, config))
|
||||
.and(crate::util::body())
|
||||
.and(warp::header::optional("X-ID-Length"))
|
||||
.and_then(move |user, data, len| post_text(user, data, len, db));
|
||||
let put_text = warp::path!("t" / String)
|
||||
.and(warp::put())
|
||||
.and(crate::auth::required(db, config))
|
||||
.and(crate::auth::auth(db, config))
|
||||
.and(crate::util::body())
|
||||
.and_then(move |id, user, data| put_text(id, user, data, db));
|
||||
|
||||
filite
|
||||
spectre
|
||||
.or(index)
|
||||
.or(get)
|
||||
.or(post_file)
|
||||
.or(put_file)
|
||||
.or(post_link)
|
||||
.or(put_link)
|
||||
.or(post_text)
|
||||
.or(put_text)
|
||||
.recover(crate::reject::handle_rejections)
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug")]
|
||||
async fn index(user: User) -> Result<impl Reply, Rejection> {
|
||||
#[derive(Template)]
|
||||
#[template(path = "index.html")]
|
||||
struct Template<'a> {
|
||||
title: &'a str,
|
||||
user: &'a str,
|
||||
}
|
||||
|
||||
let template = Template {
|
||||
title: "filite",
|
||||
user: &user.id,
|
||||
};
|
||||
Ok(warp::reply::html(template.render().or_500()?))
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(db))]
|
||||
async fn filite(id: String, db: &Db) -> Result<impl Reply, Rejection> {
|
||||
async fn get(id: String, db: &Db) -> Result<impl Reply, Rejection> {
|
||||
impl Reply for Filite {
|
||||
fn into_response(self) -> Response {
|
||||
match self.inner {
|
||||
|
@ -83,7 +116,9 @@ async fn filite(id: String, db: &Db) -> Result<impl Reply, Rejection> {
|
|||
}
|
||||
}
|
||||
|
||||
let filite = crate::db::filite(&id, true, db).or_500()?.or_404()?;
|
||||
let filite = task::block_in_place(|| crate::db::get(&id, db))
|
||||
.or_500()?
|
||||
.or_404()?;
|
||||
Ok(filite)
|
||||
}
|
||||
|
||||
|
@ -95,7 +130,7 @@ async fn post_file(
|
|||
len: Option<usize>,
|
||||
db: &Db,
|
||||
) -> Result<impl Reply, Rejection> {
|
||||
let id = crate::db::random_id(len.unwrap_or(8), db).or_500()?;
|
||||
let id = task::block_in_place(|| crate::db::random_id(len.unwrap_or(8), db)).or_500()?;
|
||||
put_file(id, user, data, mime, db).await
|
||||
}
|
||||
|
||||
|
@ -107,13 +142,15 @@ async fn put_file(
|
|||
mime: Option<String>,
|
||||
db: &Db,
|
||||
) -> Result<impl Reply, Rejection> {
|
||||
crate::db::insert_file(
|
||||
&id,
|
||||
user.id,
|
||||
data.to_vec(),
|
||||
mime.unwrap_or_else(|| "application/octet-stream".to_owned()),
|
||||
db,
|
||||
)
|
||||
task::block_in_place(|| {
|
||||
crate::db::insert_file(
|
||||
&id,
|
||||
user.id,
|
||||
data.to_vec(),
|
||||
mime.unwrap_or_else(|| "application/octet-stream".to_owned()),
|
||||
db,
|
||||
)
|
||||
})
|
||||
.or_500()?
|
||||
.or_409()?;
|
||||
Ok(warp::reply::with_status(id, StatusCode::CREATED))
|
||||
|
@ -126,13 +163,13 @@ async fn post_link(
|
|||
len: Option<usize>,
|
||||
db: &Db,
|
||||
) -> Result<impl Reply, Rejection> {
|
||||
let id = crate::db::random_id(len.unwrap_or(8), db).or_500()?;
|
||||
let id = task::block_in_place(|| crate::db::random_id(len.unwrap_or(8), db)).or_500()?;
|
||||
put_link(id, user, location, db).await
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(db))]
|
||||
async fn put_link(id: String, user: User, location: Uri, db: &Db) -> Result<impl Reply, Rejection> {
|
||||
crate::db::insert_link(&id, user.id, location.to_string(), db)
|
||||
task::block_in_place(|| crate::db::insert_link(&id, user.id, location.to_string(), db))
|
||||
.or_500()?
|
||||
.or_409()?;
|
||||
Ok(warp::reply::with_status(id, StatusCode::CREATED))
|
||||
|
@ -145,13 +182,13 @@ async fn post_text(
|
|||
len: Option<usize>,
|
||||
db: &Db,
|
||||
) -> Result<impl Reply, Rejection> {
|
||||
let id = crate::db::random_id(len.unwrap_or(8), db).or_500()?;
|
||||
let id = task::block_in_place(|| crate::db::random_id(len.unwrap_or(8), db)).or_500()?;
|
||||
put_text(id, user, data, db).await
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(db))]
|
||||
async fn put_text(id: String, user: User, data: String, db: &Db) -> Result<impl Reply, Rejection> {
|
||||
crate::db::insert_text(&id, user.id, data, db)
|
||||
task::block_in_place(|| crate::db::insert_text(&id, user.id, data, db))
|
||||
.or_500()?
|
||||
.or_409()?;
|
||||
Ok(warp::reply::with_status(id, StatusCode::CREATED))
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use bytes::Bytes;
|
||||
use std::str::FromStr;
|
||||
use warp::{http::StatusCode, Filter, Rejection};
|
||||
use warp::{Filter, Rejection};
|
||||
|
||||
pub trait DefaultExt {
|
||||
fn is_default(&self) -> bool;
|
||||
|
@ -18,11 +18,11 @@ where
|
|||
{
|
||||
warp::body::bytes().and_then(|b: Bytes| async move {
|
||||
match std::str::from_utf8(&b) {
|
||||
Ok(s) => match s.parse() {
|
||||
Ok(s) => match T::from_str(s) {
|
||||
Ok(v) => Ok(v),
|
||||
Err(e) => Err(crate::reject::custom(e, StatusCode::BAD_REQUEST)),
|
||||
Err(e) => Err(crate::reject::bad_request(e)),
|
||||
},
|
||||
Err(e) => Err(crate::reject::custom(e, StatusCode::BAD_REQUEST)),
|
||||
Err(e) => Err(crate::reject::bad_request(e)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,31 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>{{ title }}</title>
|
||||
<link rel="stylesheet" href="spectre.css">
|
||||
</head>
|
||||
<body>
|
||||
<ul class="tab">
|
||||
<li class="tab-item active">
|
||||
<a href="#">
|
||||
File
|
||||
</a>
|
||||
</li>
|
||||
<li class="tab-item">
|
||||
<a href="#">
|
||||
Link
|
||||
</a>
|
||||
</li>
|
||||
<li class="tab-item">
|
||||
<a href="#">
|
||||
Text
|
||||
</a>
|
||||
</li>
|
||||
<li class="tab-item tab-action">
|
||||
<span>{{ user }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue