2019-10-17 18:35:44 +00:00
//! Actix route handlers
2020-01-16 04:47:38 +00:00
use crate ::{
globals ::{ CONFIG , EMPTY_HASH , PASSWORD_HASH } ,
setup ,
} ;
2019-10-21 19:19:16 +00:00
use actix_identity ::Identity ;
2020-01-15 05:10:48 +00:00
use actix_web ::{ error ::BlockingError , web , Error , HttpRequest , HttpResponse , Responder } ;
2019-10-15 11:52:20 +00:00
use chrono ::{ DateTime , NaiveDateTime , Utc } ;
2019-10-21 18:15:11 +00:00
use diesel ;
use serde ::Serialize ;
2020-01-15 05:10:48 +00:00
use std ::convert ::Infallible ;
2019-10-21 18:15:11 +00:00
2019-10-22 18:34:26 +00:00
#[ cfg(feature = " dev " ) ]
use crate ::get_env ;
#[ cfg(feature = " dev " ) ]
use std ::{ fs , path ::PathBuf } ;
2019-10-21 18:15:11 +00:00
/// Parses an ID
2019-10-21 19:48:06 +00:00
fn parse_id ( id : & str ) -> Result < i32 , HttpResponse > {
2019-10-21 18:15:11 +00:00
match i32 ::from_str_radix ( id , 36 ) {
2019-10-21 19:48:06 +00:00
Ok ( id ) = > Ok ( id ) ,
2019-10-25 04:03:41 +00:00
Err ( _ ) = > Err ( HttpResponse ::BadRequest ( ) . body ( " Invalid ID " ) ) ,
2019-10-21 18:15:11 +00:00
}
}
2020-01-15 08:26:29 +00:00
/// Authenticates a user
2020-01-16 04:47:38 +00:00
async fn auth ( identity : Identity , request : HttpRequest ) -> Result < ( ) , HttpResponse > {
2019-10-21 21:02:07 +00:00
if identity . identity ( ) . is_some ( ) {
return Ok ( ( ) ) ;
}
2020-01-16 04:47:38 +00:00
if * PASSWORD_HASH = = * EMPTY_HASH {
2019-10-26 17:04:15 +00:00
identity . remember ( " guest " . into ( ) ) ;
return Ok ( ( ) ) ;
}
2019-10-21 21:02:07 +00:00
let header = match request . headers ( ) . get ( " Authorization " ) {
Some ( h ) = > match h . to_str ( ) {
Ok ( h ) = > h ,
2019-10-25 04:03:41 +00:00
Err ( _ ) = > return Err ( HttpResponse ::BadRequest ( ) . body ( " Invalid Authorization header " ) ) ,
2019-10-21 19:19:16 +00:00
} ,
2019-10-21 21:02:07 +00:00
None = > {
return Err ( HttpResponse ::Unauthorized ( )
2019-10-25 03:51:52 +00:00
. header ( " WWW-Authenticate " , " Basic realm= \" filite \" " )
2019-10-25 04:03:41 +00:00
. body ( " Unauthorized " ) )
2019-10-21 21:02:07 +00:00
}
} ;
2019-10-25 03:51:52 +00:00
let connection_string = header . replace ( " Basic " , " " ) ;
let ( user , password ) = match base64 ::decode ( & connection_string ) {
Ok ( c ) = > {
2020-01-15 05:10:48 +00:00
let credentials : Vec < Vec < u8 > > = c
. splitn ( 2 , | b | b = = & b ':' )
. map ( | s | s . to_vec ( ) )
. collect ::< Vec < Vec < u8 > > > ( ) ;
2019-10-25 03:51:52 +00:00
match credentials . len ( ) {
2 = > ( credentials [ 0 ] . clone ( ) , credentials [ 1 ] . clone ( ) ) ,
2019-10-25 04:03:41 +00:00
_ = > return Err ( HttpResponse ::BadRequest ( ) . body ( " Invalid Authorization header " ) ) ,
2019-10-25 03:51:52 +00:00
}
}
2019-10-25 04:03:41 +00:00
Err ( _ ) = > return Err ( HttpResponse ::BadRequest ( ) . body ( " Invalid Authorization header " ) ) ,
2019-10-25 03:51:52 +00:00
} ;
2020-01-16 04:47:38 +00:00
let infallible_hash = move | | -> Result < Vec < u8 > , Infallible > { Ok ( setup ::hash ( & password ) ) } ;
if web ::block ( infallible_hash ) . await . unwrap ( ) = = * PASSWORD_HASH {
2019-10-25 23:41:11 +00:00
match String ::from_utf8 ( user . to_vec ( ) ) {
2019-10-25 03:51:52 +00:00
Ok ( u ) = > {
identity . remember ( u ) ;
Ok ( ( ) )
}
2019-10-25 04:03:41 +00:00
Err ( _ ) = > Err ( HttpResponse ::BadRequest ( ) . body ( " Invalid Authorization header " ) ) ,
2019-10-25 23:41:11 +00:00
}
} else {
Err ( HttpResponse ::Unauthorized ( )
2019-10-25 03:51:52 +00:00
. header ( " WWW-Authenticate " , " Basic realm= \" filite \" " )
2019-10-25 23:41:11 +00:00
. body ( " Unauthorized " ) )
2019-10-21 19:19:16 +00:00
}
}
2020-01-16 03:34:55 +00:00
/// Match result from REPLACE queries
fn match_replace_result < T : Serialize > (
2020-01-15 20:13:08 +00:00
result : Result < T , BlockingError < diesel ::result ::Error > > ,
id : i32 ,
) -> Result < HttpResponse , Error > {
match result {
Ok ( _ ) = > Ok ( HttpResponse ::Created ( ) . body ( format! ( " {} " , radix_fmt ::radix_36 ( id ) ) ) ) ,
Err ( _ ) = > Err ( HttpResponse ::InternalServerError ( )
. body ( " Internal server error " )
. into ( ) ) ,
}
}
2019-10-21 18:15:11 +00:00
/// Handles error from single GET queries using find
2020-01-15 05:10:48 +00:00
fn match_find_error < T > ( error : BlockingError < diesel ::result ::Error > ) -> Result < T , Error > {
2019-10-21 18:15:11 +00:00
match error {
BlockingError ::Error ( e ) = > match e {
2020-01-15 05:10:48 +00:00
diesel ::result ::Error ::NotFound = > {
Err ( HttpResponse ::NotFound ( ) . body ( " Not found " ) . into ( ) )
}
_ = > Err ( HttpResponse ::InternalServerError ( )
. body ( " Internal server error " )
. into ( ) ) ,
2019-10-21 18:15:11 +00:00
} ,
2020-01-15 05:10:48 +00:00
BlockingError ::Canceled = > Err ( HttpResponse ::InternalServerError ( )
. body ( " Internal server error " )
. into ( ) ) ,
2019-10-21 18:15:11 +00:00
}
}
/// Formats a timestamp to the "Last-Modified" header format
fn timestamp_to_last_modified ( timestamp : i32 ) -> String {
let datetime =
2019-10-25 23:41:11 +00:00
DateTime ::< Utc > ::from_utc ( NaiveDateTime ::from_timestamp ( i64 ::from ( timestamp ) , 0 ) , Utc ) ;
2019-10-21 18:15:11 +00:00
datetime . format ( " %a, %d %b %Y %H:%M:%S GMT " ) . to_string ( )
}
2019-10-15 11:52:20 +00:00
2020-01-15 06:50:54 +00:00
/// Escapes text to be inserted in a HTML element
fn escape_html ( text : & str ) -> String {
text . replace ( " & " , " & " )
. replace ( " < " , " < " )
. replace ( " > " , " > " )
}
2019-10-15 11:52:20 +00:00
/// GET multiple entries
macro_rules ! select {
( $m :ident ) = > {
2020-01-15 08:26:29 +00:00
pub async fn select (
2019-10-21 19:19:16 +00:00
request : HttpRequest ,
2019-10-21 17:18:32 +00:00
query : actix_web ::web ::Query < SelectQuery > ,
2019-10-21 19:19:16 +00:00
identity : actix_identity ::Identity ,
2020-01-15 05:10:48 +00:00
) -> Result < actix_web ::HttpResponse , actix_web ::Error > {
2020-01-16 04:47:38 +00:00
crate ::routes ::auth ( identity , request ) . await ? ;
2020-01-15 05:10:48 +00:00
2019-10-21 17:18:32 +00:00
let filters = crate ::queries ::SelectFilters ::from ( query . into_inner ( ) ) ;
2020-01-16 04:47:38 +00:00
match actix_web ::web ::block ( move | | crate ::queries ::$m ::select ( filters ) ) . await {
2020-01-15 05:10:48 +00:00
Ok ( x ) = > Ok ( actix_web ::HttpResponse ::Ok ( ) . json ( x ) ) ,
Err ( _ ) = > Err ( actix_web ::HttpResponse ::InternalServerError ( )
. body ( " Internal server error " )
. into ( ) ) ,
}
2019-10-15 11:52:20 +00:00
}
} ;
}
2019-10-15 13:08:07 +00:00
/// DELETE an entry
macro_rules ! delete {
( $m :ident ) = > {
2020-01-15 05:10:48 +00:00
pub async fn delete (
2019-10-21 19:19:16 +00:00
request : HttpRequest ,
2019-10-21 17:18:32 +00:00
path : actix_web ::web ::Path < String > ,
2019-10-21 19:19:16 +00:00
identity : actix_identity ::Identity ,
2020-01-15 05:10:48 +00:00
) -> Result < actix_web ::HttpResponse , actix_web ::Error > {
2020-01-16 04:47:38 +00:00
crate ::routes ::auth ( identity , request ) . await ? ;
2020-01-15 05:10:48 +00:00
let id = crate ::routes ::parse_id ( & path ) ? ;
2020-01-16 04:47:38 +00:00
match actix_web ::web ::block ( move | | crate ::queries ::$m ::delete ( id ) ) . await {
2020-01-15 05:10:48 +00:00
Ok ( ( ) ) = > Ok ( actix_web ::HttpResponse ::NoContent ( ) . body ( " Deleted " ) ) ,
Err ( e ) = > crate ::routes ::match_find_error ( e ) ,
}
2019-10-15 13:08:07 +00:00
}
} ;
}
2020-01-15 08:26:29 +00:00
/// Verify if an entry exists
2020-01-15 19:48:02 +00:00
macro_rules ! random_id {
2020-01-15 08:26:29 +00:00
( $m :ident ) = > {
2020-01-15 20:58:14 +00:00
use rand ::distributions ::Distribution ;
2020-01-15 19:48:02 +00:00
2020-01-16 04:47:38 +00:00
pub async fn random_id ( ) -> Result < i32 , actix_web ::Error > {
2020-01-15 19:48:02 +00:00
let mut rng = rand ::thread_rng ( ) ;
2020-01-15 20:58:14 +00:00
let distribution = rand ::distributions ::Uniform ::from ( 0 .. i32 ::max_value ( ) ) ;
2020-01-15 19:48:02 +00:00
loop {
2020-01-15 20:58:14 +00:00
let id = distribution . sample ( & mut rng ) ;
2020-01-16 04:47:38 +00:00
match actix_web ::web ::block ( move | | crate ::queries ::$m ::find ( id ) ) . await {
2020-01-15 19:48:02 +00:00
Ok ( _ ) = > continue ,
Err ( e ) = > match e {
actix_web ::error ::BlockingError ::Error ( e ) = > match e {
diesel ::result ::Error ::NotFound = > return Ok ( id ) ,
_ = > {
return Err ( actix_web ::HttpResponse ::InternalServerError ( )
. body ( " Internal server error " )
. into ( ) )
}
} ,
actix_web ::error ::BlockingError ::Canceled = > {
return Err ( actix_web ::HttpResponse ::InternalServerError ( )
. body ( " Internal server error " )
. into ( ) )
}
} ,
}
2020-01-15 08:26:29 +00:00
}
}
} ;
}
2019-10-22 18:34:26 +00:00
#[ cfg(feature = " dev " ) ]
lazy_static! {
2020-01-15 23:17:12 +00:00
static ref INDEX_PATH : PathBuf = {
let mut index_path = PathBuf ::new ( ) ;
index_path . push ( get_env! ( " CARGO_MANIFEST_DIR " ) ) ;
index_path . push ( " resources " ) ;
index_path . push ( " index.html " ) ;
index_path
2019-10-22 18:34:26 +00:00
} ;
}
#[ cfg(not(feature = " dev " )) ]
2020-01-15 23:17:12 +00:00
static INDEX_CONTENTS : & str = include_str! ( " ../resources/index.html " ) ;
2019-10-22 18:34:26 +00:00
2020-01-15 06:50:54 +00:00
static HIGHLIGHT_CONTENTS : & str = include_str! ( " ../resources/highlight.html " ) ;
const HIGHLIGHT_LANGUAGE : & str = r # "<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.10/languages/{{ language }}.min.js"></script>"# ;
2019-10-22 18:34:26 +00:00
/// Index page letting users upload via a UI
2020-01-16 04:47:38 +00:00
pub async fn index ( request : HttpRequest , identity : Identity ) -> impl Responder {
if let Err ( response ) = auth ( identity , request ) . await {
2019-10-25 03:51:52 +00:00
return response ;
}
2019-10-22 18:34:26 +00:00
let contents = {
#[ cfg(feature = " dev " ) ]
{
2020-01-15 23:17:12 +00:00
fs ::read_to_string ( & * INDEX_PATH ) . expect ( " Can't read index.html " )
2019-10-22 18:34:26 +00:00
}
#[ cfg(not(feature = " dev " )) ]
{
2020-01-15 23:17:12 +00:00
INDEX_CONTENTS . to_owned ( )
2019-10-22 18:34:26 +00:00
}
} ;
HttpResponse ::Ok ( )
. header ( " Content-Type " , " text/html " )
. body ( contents )
}
2019-10-15 11:52:20 +00:00
/// GET the config info
2020-01-16 04:47:38 +00:00
pub async fn get_config ( request : HttpRequest , identity : Identity ) -> impl Responder {
match auth ( identity , request ) . await {
Ok ( _ ) = > HttpResponse ::Ok ( ) . json ( & * CONFIG ) ,
2019-10-21 19:24:05 +00:00
Err ( response ) = > response ,
}
2019-10-15 11:52:20 +00:00
}
2019-10-21 20:59:13 +00:00
/// Logout route
2020-01-15 05:10:48 +00:00
pub async fn logout ( identity : Identity ) -> impl Responder {
2019-10-25 23:41:11 +00:00
if identity . identity ( ) . is_some ( ) {
identity . forget ( ) ;
HttpResponse ::Ok ( ) . body ( " Logged out " )
} else {
HttpResponse ::Unauthorized ( )
2019-10-25 03:51:52 +00:00
. header ( " WWW-Authenticate " , " Basic realm= \" filite \" " )
2019-10-25 23:41:11 +00:00
. body ( " Unauthorized " )
2019-10-21 20:59:13 +00:00
}
}
2019-10-15 11:52:20 +00:00
pub mod files {
2020-01-16 03:34:55 +00:00
use crate ::routes ::match_replace_result ;
2019-10-21 18:29:39 +00:00
use crate ::{
2020-01-16 04:47:38 +00:00
globals ::CONFIG ,
2019-10-21 18:29:39 +00:00
queries ::{ self , SelectQuery } ,
2019-10-21 19:19:16 +00:00
routes ::{ auth , match_find_error , parse_id } ,
2019-10-21 18:29:39 +00:00
} ;
2019-10-15 11:52:20 +00:00
use actix_files ::NamedFile ;
2019-10-21 20:59:13 +00:00
use actix_identity ::Identity ;
2020-01-15 08:26:29 +00:00
use actix_multipart ::Multipart ;
use actix_web ::{ web , Error , HttpRequest , HttpResponse } ;
2019-10-17 18:08:07 +00:00
use chrono ::Utc ;
2020-01-15 08:26:29 +00:00
use futures ::StreamExt ;
use std ::{
fs ::{ self , File } ,
io ::Write ,
path ::PathBuf ,
} ;
2019-10-15 11:52:20 +00:00
select! ( files ) ;
2020-01-15 19:48:02 +00:00
delete! ( files ) ;
random_id! ( files ) ;
2019-10-15 11:52:20 +00:00
/// GET a file entry and statically serve it
2020-01-16 04:47:38 +00:00
pub async fn get ( path : web ::Path < String > ) -> Result < NamedFile , Error > {
2020-01-15 05:10:48 +00:00
let id = parse_id ( & path ) ? ;
2020-01-16 04:47:38 +00:00
match web ::block ( move | | queries ::files ::find ( id ) ) . await {
2020-01-15 05:10:48 +00:00
Ok ( file ) = > {
2020-01-16 04:47:38 +00:00
let mut path = CONFIG . files_dir . clone ( ) ;
2020-01-15 05:10:48 +00:00
path . push ( file . filepath ) ;
match NamedFile ::open ( & path ) {
Ok ( nf ) = > Ok ( nf ) ,
Err ( _ ) = > Err ( HttpResponse ::NotFound ( ) . body ( " Not found " ) . into ( ) ) ,
}
}
Err ( e ) = > match_find_error ( e ) ,
}
2019-10-15 11:52:20 +00:00
}
2020-01-16 03:34:55 +00:00
/// Common code for PUT and POST routes
2020-01-16 04:47:38 +00:00
async fn put_post ( id : i32 , mut body : Multipart ) -> Result < HttpResponse , Error > {
let mut path = CONFIG . files_dir . clone ( ) ;
2020-01-16 03:34:55 +00:00
let mut relative_path = PathBuf ::new ( ) ;
let dir_path = path . clone ( ) ;
if web ::block ( move | | fs ::create_dir_all ( dir_path ) )
2020-01-15 08:26:29 +00:00
. await
. is_err ( )
{
return Err ( HttpResponse ::InternalServerError ( )
. body ( " Internal server error " )
. into ( ) ) ;
}
2020-01-15 05:10:48 +00:00
2020-01-15 08:26:29 +00:00
let mut field = match body . next ( ) . await {
Some ( f ) = > f ? ,
None = > {
return Err ( HttpResponse ::BadRequest ( )
. body ( " Empty multipart body " )
. into ( ) )
2020-01-15 05:10:48 +00:00
}
2020-01-15 08:26:29 +00:00
} ;
let content_disposition = match field . content_disposition ( ) {
Some ( cd ) = > cd ,
None = > {
return Err ( HttpResponse ::BadRequest ( )
. body ( " Missing content disposition " )
. into ( ) )
2020-01-15 05:10:48 +00:00
}
2020-01-15 08:26:29 +00:00
} ;
let filename = match content_disposition . get_filename ( ) {
Some ( n ) = > n ,
None = > return Err ( HttpResponse ::BadRequest ( ) . body ( " Missing filename " ) . into ( ) ) ,
} ;
2020-01-16 04:47:38 +00:00
let filename = format! (
" {}.{} " ,
radix_fmt ::radix_36 ( Utc ::now ( ) . timestamp ( ) ) ,
filename
) ;
2020-01-15 08:26:29 +00:00
path . push ( & filename ) ;
relative_path . push ( & filename ) ;
2020-01-16 03:34:55 +00:00
let relative_path = match path . to_str ( ) {
Some ( rp ) = > rp . to_owned ( ) ,
None = > {
return Err ( HttpResponse ::InternalServerError ( )
. body ( " Internal server error " )
. into ( ) )
}
} ;
2020-01-15 08:26:29 +00:00
let mut f = match web ::block ( move | | File ::create ( & path ) ) . await {
Ok ( f ) = > f ,
Err ( _ ) = > {
return Err ( HttpResponse ::InternalServerError ( )
2020-01-15 05:10:48 +00:00
. body ( " Internal server error " )
2020-01-15 08:26:29 +00:00
. into ( ) )
}
} ;
while let Some ( chunk ) = field . next ( ) . await {
let data = match chunk {
Ok ( c ) = > c ,
Err ( _ ) = > {
return Err ( HttpResponse ::BadRequest ( )
. body ( " Invalid multipart data " )
. into ( ) )
}
} ;
f = match web ::block ( move | | match f . write_all ( & data ) {
Ok ( _ ) = > Ok ( f ) ,
Err ( _ ) = > Err ( ( ) ) ,
} )
. await
{
Ok ( f ) = > f ,
Err ( _ ) = > {
return Err ( HttpResponse ::InternalServerError ( )
. body ( " Internal server error " )
. into ( ) )
}
} ;
2020-01-15 05:10:48 +00:00
}
2020-01-15 08:26:29 +00:00
2020-01-16 03:34:55 +00:00
match_replace_result (
2020-01-16 04:47:38 +00:00
web ::block ( move | | queries ::files ::replace ( id , & relative_path ) ) . await ,
2020-01-16 03:34:55 +00:00
id ,
)
}
/// PUT a new file entry
pub async fn put (
request : HttpRequest ,
path : web ::Path < String > ,
body : Multipart ,
identity : Identity ,
) -> Result < HttpResponse , Error > {
2020-01-16 04:47:38 +00:00
auth ( identity , request ) . await ? ;
2020-01-16 03:34:55 +00:00
let id = parse_id ( & path ) ? ;
2020-01-16 04:47:38 +00:00
put_post ( id , body ) . await
2020-01-16 03:34:55 +00:00
}
/// POST a new file entry using a multipart body
pub async fn post (
request : HttpRequest ,
body : Multipart ,
identity : Identity ,
) -> Result < HttpResponse , Error > {
2020-01-16 04:47:38 +00:00
auth ( identity , request ) . await ? ;
let id = random_id ( ) . await ? ;
put_post ( id , body ) . await
2019-10-15 11:52:20 +00:00
}
}
pub mod links {
2019-10-21 18:29:39 +00:00
use crate ::{
queries ::{ self , SelectQuery } ,
2019-10-21 19:19:16 +00:00
routes ::{
2020-01-16 03:34:55 +00:00
auth , match_find_error , match_replace_result , parse_id , timestamp_to_last_modified ,
2019-10-21 19:19:16 +00:00
} ,
2019-10-21 18:15:11 +00:00
} ;
2019-10-21 20:59:13 +00:00
use actix_identity ::Identity ;
2019-10-21 19:19:16 +00:00
use actix_web ::{ web , Error , HttpRequest , HttpResponse } ;
2019-10-15 11:52:20 +00:00
select! ( links ) ;
2020-01-15 20:13:08 +00:00
delete! ( links ) ;
random_id! ( links ) ;
2019-10-15 11:52:20 +00:00
/// GET a link entry and redirect to it
2020-01-16 04:47:38 +00:00
pub async fn get ( path : web ::Path < String > ) -> Result < HttpResponse , Error > {
2020-01-15 05:10:48 +00:00
let id = parse_id ( & path ) ? ;
2020-01-16 04:47:38 +00:00
match web ::block ( move | | queries ::links ::find ( id ) ) . await {
2020-01-15 05:10:48 +00:00
Ok ( link ) = > Ok ( HttpResponse ::Found ( )
. header ( " Location " , link . forward )
. header ( " Last-Modified " , timestamp_to_last_modified ( link . created ) )
. finish ( ) ) ,
Err ( e ) = > match_find_error ( e ) ,
}
2019-10-15 11:52:20 +00:00
}
/// Request body when PUTting links
#[ derive(Deserialize) ]
2020-01-15 20:13:08 +00:00
pub struct PutPostLink {
2019-10-15 11:52:20 +00:00
pub forward : String ,
}
/// PUT a new link entry
2020-01-15 05:10:48 +00:00
pub async fn put (
2019-10-21 19:19:16 +00:00
request : HttpRequest ,
path : web ::Path < String > ,
2020-01-15 20:13:08 +00:00
body : web ::Json < PutPostLink > ,
2019-10-21 20:59:13 +00:00
identity : Identity ,
2020-01-15 05:10:48 +00:00
) -> Result < HttpResponse , Error > {
2020-01-16 04:47:38 +00:00
auth ( identity , request ) . await ? ;
2020-01-15 05:10:48 +00:00
let id = parse_id ( & path ) ? ;
2020-01-16 03:34:55 +00:00
match_replace_result (
2020-01-16 04:47:38 +00:00
web ::block ( move | | queries ::links ::replace ( id , & body . forward ) ) . await ,
2020-01-16 03:34:55 +00:00
id ,
2020-01-15 05:10:48 +00:00
)
2019-10-15 11:52:20 +00:00
}
2019-10-15 13:08:07 +00:00
2020-01-15 20:13:08 +00:00
/// POST a new link entry
pub async fn post (
request : HttpRequest ,
body : web ::Json < PutPostLink > ,
identity : Identity ,
) -> Result < HttpResponse , Error > {
2020-01-16 04:47:38 +00:00
auth ( identity , request ) . await ? ;
let id = random_id ( ) . await ? ;
2020-01-16 03:34:55 +00:00
match_replace_result (
2020-01-16 04:47:38 +00:00
web ::block ( move | | queries ::links ::replace ( id , & body . forward ) ) . await ,
2020-01-15 20:13:08 +00:00
id ,
)
}
2019-10-15 11:52:20 +00:00
}
pub mod texts {
2020-01-15 06:50:54 +00:00
use crate ::routes ::escape_html ;
2020-01-16 04:47:38 +00:00
use crate ::{
globals ::CONFIG ,
routes ::{ HIGHLIGHT_CONTENTS , HIGHLIGHT_LANGUAGE } ,
} ;
2019-10-21 18:29:39 +00:00
use crate ::{
queries ::{ self , SelectQuery } ,
2019-10-21 19:19:16 +00:00
routes ::{
2020-01-16 03:34:55 +00:00
auth , match_find_error , match_replace_result , parse_id , timestamp_to_last_modified ,
2019-10-21 19:19:16 +00:00
} ,
2020-01-15 06:50:54 +00:00
} ;
2019-10-21 20:59:13 +00:00
use actix_identity ::Identity ;
2019-10-21 19:19:16 +00:00
use actix_web ::{ web , Error , HttpRequest , HttpResponse } ;
2019-10-15 11:52:20 +00:00
select! ( texts ) ;
2020-01-15 20:13:08 +00:00
delete! ( texts ) ;
random_id! ( texts ) ;
2019-10-15 11:52:20 +00:00
/// GET a text entry and display it
2020-01-16 04:47:38 +00:00
pub async fn get ( path : web ::Path < String > ) -> Result < HttpResponse , Error > {
2020-01-15 05:10:48 +00:00
let id = parse_id ( & path ) ? ;
2020-01-16 04:47:38 +00:00
match web ::block ( move | | queries ::texts ::find ( id ) ) . await {
2020-01-15 06:50:54 +00:00
Ok ( text ) = > {
let last_modified = timestamp_to_last_modified ( text . created ) ;
if text . highlight {
2020-01-16 04:47:38 +00:00
let languages : Vec < String > = CONFIG
2020-01-15 06:50:54 +00:00
. highlight
. languages
. iter ( )
. map ( | l | HIGHLIGHT_LANGUAGE . replace ( " {{ language }} " , l ) )
. collect ( ) ;
let languages = languages . join ( " \n " ) ;
let contents = HIGHLIGHT_CONTENTS
. replace ( " {{ title }} " , & path )
2020-01-16 04:47:38 +00:00
. replace ( " {{ theme }} " , & CONFIG . highlight . theme )
2020-01-15 06:50:54 +00:00
. replace ( " {{ contents }} " , & escape_html ( & text . contents ) )
. replace ( " {{ languages }} " , & languages ) ;
Ok ( HttpResponse ::Ok ( )
. header ( " Last-Modified " , last_modified )
. header ( " Content-Type " , " text/html " )
. body ( contents ) )
} else {
Ok ( HttpResponse ::Ok ( )
. header ( " Last-Modified " , last_modified )
. body ( text . contents ) )
}
}
2020-01-15 05:10:48 +00:00
Err ( e ) = > match_find_error ( e ) ,
}
2019-10-15 11:52:20 +00:00
}
/// Request body when PUTting texts
#[ derive(Deserialize) ]
2020-01-15 20:58:14 +00:00
pub struct PutPostText {
2019-10-15 11:52:20 +00:00
pub contents : String ,
2020-01-15 06:50:54 +00:00
pub highlight : bool ,
2019-10-15 11:52:20 +00:00
}
/// PUT a new text entry
2020-01-15 05:10:48 +00:00
pub async fn put (
2019-10-21 19:19:16 +00:00
request : HttpRequest ,
path : web ::Path < String > ,
2020-01-15 20:58:14 +00:00
body : web ::Json < PutPostText > ,
2019-10-21 20:59:13 +00:00
identity : Identity ,
2020-01-15 05:10:48 +00:00
) -> Result < HttpResponse , Error > {
2020-01-16 04:47:38 +00:00
auth ( identity , request ) . await ? ;
2020-01-15 05:10:48 +00:00
let id = parse_id ( & path ) ? ;
2020-01-16 03:34:55 +00:00
match_replace_result (
2020-01-16 04:47:38 +00:00
web ::block ( move | | queries ::texts ::replace ( id , & body . contents , body . highlight ) ) . await ,
2020-01-16 03:34:55 +00:00
id ,
2020-01-15 05:10:48 +00:00
)
2019-10-15 11:52:20 +00:00
}
2019-10-15 13:08:07 +00:00
2020-01-15 23:17:12 +00:00
/// POST a new text entry
2020-01-15 20:13:08 +00:00
pub async fn post (
request : HttpRequest ,
2020-01-15 20:58:14 +00:00
body : web ::Json < PutPostText > ,
2020-01-15 20:13:08 +00:00
identity : Identity ,
) -> Result < HttpResponse , Error > {
2020-01-16 04:47:38 +00:00
auth ( identity , request ) . await ? ;
let id = random_id ( ) . await ? ;
2020-01-16 03:34:55 +00:00
match_replace_result (
2020-01-16 04:47:38 +00:00
web ::block ( move | | queries ::texts ::replace ( id , & body . contents , body . highlight ) ) . await ,
2020-01-15 20:13:08 +00:00
id ,
)
}
2019-10-15 11:52:20 +00:00
}