2019-10-17 18:35:44 +00:00
//! Actix route handlers
2019-10-21 19:19:16 +00:00
use crate ::setup ::{ self , Config } ;
use actix_identity ::Identity ;
2020-01-15 05:10:48 +00:00
use actix_web ::{ error ::BlockingError , web , Error , HttpRequest , HttpResponse , Responder } ;
2019-10-21 20:59:13 +00:00
use base64 ;
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 05:10:48 +00:00
async fn auth (
2019-10-25 03:39:34 +00:00
identity : Identity ,
request : HttpRequest ,
password_hash : & [ u8 ] ,
) -> Result < ( ) , HttpResponse > {
2019-10-21 21:02:07 +00:00
if identity . identity ( ) . is_some ( ) {
return Ok ( ( ) ) ;
}
2019-10-26 17:04:15 +00:00
if password_hash = = setup ::hash ( " " ) . as_slice ( ) {
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-15 05:10:48 +00:00
let infallible_hash = move | | -> Result < Vec < u8 > , Infallible > { Ok ( setup ::hash ( password ) ) } ;
if web ::block ( infallible_hash ) . await . unwrap ( ) . as_slice ( ) = = 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
}
}
2019-10-21 18:15:11 +00:00
/// Match result from REPLACE queries
#[ inline(always) ]
fn match_replace_result < T : Serialize > (
result : Result < T , BlockingError < diesel ::result ::Error > > ,
2020-01-15 05:10:48 +00:00
) -> Result < HttpResponse , Error > {
2019-10-21 18:15:11 +00:00
match result {
Ok ( x ) = > Ok ( HttpResponse ::Created ( ) . json ( x ) ) ,
2020-01-15 05:10:48 +00:00
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
#[ inline(always) ]
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 05:10:48 +00:00
pub async fn gets (
2019-10-21 19:19:16 +00:00
request : HttpRequest ,
2019-10-21 17:18:32 +00:00
query : actix_web ::web ::Query < SelectQuery > ,
pool : actix_web ::web ::Data < Pool > ,
2019-10-21 19:19:16 +00:00
identity : actix_identity ::Identity ,
2019-10-25 03:39:34 +00:00
password_hash : actix_web ::web ::Data < Vec < u8 > > ,
2020-01-15 05:10:48 +00:00
) -> Result < actix_web ::HttpResponse , actix_web ::Error > {
crate ::routes ::auth ( identity , request , & password_hash ) . await ? ;
2019-10-21 17:18:32 +00:00
let filters = crate ::queries ::SelectFilters ::from ( query . into_inner ( ) ) ;
2020-01-15 05:10:48 +00:00
match actix_web ::web ::block ( move | | crate ::queries ::$m ::select ( filters , pool ) ) . await {
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 > ,
pool : actix_web ::web ::Data < Pool > ,
2019-10-21 19:19:16 +00:00
identity : actix_identity ::Identity ,
2019-10-25 03:39:34 +00:00
password_hash : actix_web ::web ::Data < Vec < u8 > > ,
2020-01-15 05:10:48 +00:00
) -> Result < actix_web ::HttpResponse , actix_web ::Error > {
crate ::routes ::auth ( identity , request , & password_hash ) . await ? ;
let id = crate ::routes ::parse_id ( & path ) ? ;
match actix_web ::web ::block ( move | | crate ::queries ::$m ::delete ( id , pool ) ) . await {
Ok ( ( ) ) = > Ok ( actix_web ::HttpResponse ::NoContent ( ) . body ( " Deleted " ) ) ,
Err ( e ) = > crate ::routes ::match_find_error ( e ) ,
}
2019-10-15 13:08:07 +00:00
}
} ;
}
2019-10-22 18:34:26 +00:00
#[ cfg(feature = " dev " ) ]
lazy_static! {
static ref RESOURCES_DIR : PathBuf = {
let mut ressources_dir = PathBuf ::new ( ) ;
ressources_dir . push ( get_env! ( " CARGO_MANIFEST_DIR " ) ) ;
ressources_dir . push ( " resources " ) ;
ressources_dir
} ;
static ref HTML_PATH : PathBuf = {
let mut html_path = RESOURCES_DIR . clone ( ) ;
html_path . push ( " index.html " ) ;
html_path
} ;
static ref JS_PATH : PathBuf = {
let mut js_path = RESOURCES_DIR . clone ( ) ;
js_path . push ( " script.js " ) ;
js_path
} ;
static ref CSS_PATH : PathBuf = {
let mut css_path = RESOURCES_DIR . clone ( ) ;
css_path . push ( " style.css " ) ;
css_path
} ;
}
#[ cfg(not(feature = " dev " )) ]
lazy_static! {
static ref INDEX_CONTENTS : String = {
2019-10-25 18:16:43 +00:00
let html = include_str! ( " ../resources/index.html " ) ;
let js = include_str! ( " ../resources/script.js " ) ;
let css = include_str! ( " ../resources/style.css " ) ;
2019-10-22 18:34:26 +00:00
html . replace ( " {{ js }} " , js ) . replace ( " {{ css }} " , css )
} ;
}
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-15 05:10:48 +00:00
pub async fn index (
2019-10-25 03:51:52 +00:00
request : HttpRequest ,
identity : Identity ,
password_hash : web ::Data < Vec < u8 > > ,
) -> impl Responder {
2020-01-15 05:10:48 +00:00
if let Err ( response ) = auth ( identity , request , & password_hash ) . await {
2019-10-25 03:51:52 +00:00
return response ;
}
2019-10-22 18:34:26 +00:00
let contents = {
#[ cfg(feature = " dev " ) ]
{
let html = fs ::read_to_string ( & * HTML_PATH ) . expect ( " Can't read index.html " ) ;
let js = fs ::read_to_string ( & * JS_PATH ) . expect ( " Can't read script.js " ) ;
let css = fs ::read_to_string ( & * CSS_PATH ) . expect ( " Can't read style.css " ) ;
html . replace ( " {{ js }} " , & js ) . replace ( " {{ css }} " , & css )
}
#[ cfg(not(feature = " dev " )) ]
{
2020-01-15 05:10:48 +00:00
( & * INDEX_CONTENTS ) . clone ( )
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-15 05:10:48 +00:00
pub async fn get_config (
2019-10-21 19:24:05 +00:00
request : HttpRequest ,
config : web ::Data < Config > ,
2019-10-22 18:34:26 +00:00
identity : Identity ,
2019-10-25 03:39:34 +00:00
password_hash : web ::Data < Vec < u8 > > ,
2019-10-21 19:24:05 +00:00
) -> impl Responder {
2020-01-15 05:10:48 +00:00
match auth ( identity , request , & password_hash ) . await {
2019-10-21 19:24:05 +00:00
Ok ( _ ) = > HttpResponse ::Ok ( ) . json ( config . get_ref ( ) ) ,
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 {
2019-10-21 18:29:39 +00:00
use crate ::{
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
setup ::Config ,
Pool ,
} ;
2019-10-15 11:52:20 +00:00
use actix_files ::NamedFile ;
2019-10-21 20:59:13 +00:00
use actix_identity ::Identity ;
2019-10-21 19:19:16 +00:00
use actix_web ::{ error ::BlockingError , http , web , Error , HttpRequest , HttpResponse } ;
2019-10-17 18:08:07 +00:00
use chrono ::Utc ;
2019-10-21 18:29:39 +00:00
use std ::{ fs , path ::PathBuf } ;
2019-10-15 11:52:20 +00:00
select! ( files ) ;
/// GET a file entry and statically serve it
2020-01-15 05:10:48 +00:00
pub async fn get (
2019-10-15 11:52:20 +00:00
path : web ::Path < String > ,
pool : web ::Data < Pool > ,
config : web ::Data < Config > ,
2020-01-15 05:10:48 +00:00
) -> Result < NamedFile , Error > {
let id = parse_id ( & path ) ? ;
match web ::block ( move | | queries ::files ::find ( id , pool ) ) . await {
Ok ( file ) = > {
let mut path = config . files_dir . clone ( ) ;
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
}
/// Request body when PUTting files
#[ derive(Deserialize) ]
pub struct PutFile {
pub base64 : String ,
pub filename : String ,
}
/// PUT a new file 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 > ,
body : web ::Json < PutFile > ,
2019-10-15 11:52:20 +00:00
pool : web ::Data < Pool > ,
2019-10-21 19:24:05 +00:00
config : web ::Data < Config > ,
2019-10-21 20:59:13 +00:00
identity : Identity ,
2019-10-25 03:39:34 +00:00
password_hash : web ::Data < Vec < u8 > > ,
2020-01-15 05:10:48 +00:00
) -> Result < HttpResponse , Error > {
auth ( identity , request , & password_hash ) . await ? ;
let id = parse_id ( & path ) ? ;
let result = web ::block ( move | | {
let mut path = config . files_dir . clone ( ) ;
let mut relative_path = PathBuf ::new ( ) ;
if fs ::create_dir_all ( & path ) . is_err ( ) {
return Err ( http ::StatusCode ::from_u16 ( 500 ) . unwrap ( ) ) ;
}
let mut filename = body . filename . clone ( ) ;
filename = format! ( " {:x} . {} " , Utc ::now ( ) . timestamp ( ) , filename ) ;
path . push ( & filename ) ;
relative_path . push ( & filename ) ;
let relative_path = match relative_path . to_str ( ) {
Some ( rp ) = > rp ,
None = > return Err ( http ::StatusCode ::from_u16 ( 500 ) . unwrap ( ) ) ,
} ;
let contents = match base64 ::decode ( & body . base64 ) {
Ok ( contents ) = > contents ,
Err ( _ ) = > return Err ( http ::StatusCode ::from_u16 ( 400 ) . unwrap ( ) ) ,
} ;
if fs ::write ( & path , contents ) . is_err ( ) {
return Err ( http ::StatusCode ::from_u16 ( 500 ) . unwrap ( ) ) ;
}
match queries ::files ::replace ( id , relative_path , pool ) {
Ok ( file ) = > Ok ( file ) ,
Err ( _ ) = > Err ( http ::StatusCode ::from_u16 ( 500 ) . unwrap ( ) ) ,
}
} )
. await ;
match result {
Ok ( file ) = > Ok ( HttpResponse ::Created ( ) . json ( file ) ) ,
Err ( e ) = > match e {
BlockingError ::Error ( sc ) = > Err ( HttpResponse ::new ( sc ) . into ( ) ) ,
BlockingError ::Canceled = > Err ( HttpResponse ::InternalServerError ( )
. body ( " Internal server error " )
. into ( ) ) ,
} ,
}
2019-10-15 11:52:20 +00:00
}
2019-10-15 13:08:07 +00:00
delete! ( files ) ;
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 ::{
auth , match_find_error , match_replace_result , parse_id , timestamp_to_last_modified ,
} ,
2019-10-21 18:29:39 +00:00
Pool ,
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 ) ;
/// GET a link entry and redirect to it
2020-01-15 05:10:48 +00:00
pub async fn get (
2019-10-15 11:52:20 +00:00
path : web ::Path < String > ,
pool : web ::Data < Pool > ,
2020-01-15 05:10:48 +00:00
) -> Result < HttpResponse , Error > {
let id = parse_id ( & path ) ? ;
match web ::block ( move | | queries ::links ::find ( id , pool ) ) . await {
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) ]
pub struct PutLink {
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 > ,
body : web ::Json < PutLink > ,
2019-10-15 11:52:20 +00:00
pool : web ::Data < Pool > ,
2019-10-21 20:59:13 +00:00
identity : Identity ,
2019-10-25 03:39:34 +00:00
password_hash : web ::Data < Vec < u8 > > ,
2020-01-15 05:10:48 +00:00
) -> Result < HttpResponse , Error > {
auth ( identity , request , & password_hash ) . await ? ;
let id = parse_id ( & path ) ? ;
match_replace_result (
web ::block ( move | | queries ::links ::replace ( id , & body . forward , pool ) ) . await ,
)
2019-10-15 11:52:20 +00:00
}
2019-10-15 13:08:07 +00:00
delete! ( links ) ;
2019-10-15 11:52:20 +00:00
}
pub mod texts {
2020-01-15 06:50:54 +00:00
use crate ::routes ::escape_html ;
2019-10-21 18:29:39 +00:00
use crate ::{
queries ::{ self , SelectQuery } ,
2019-10-21 19:19:16 +00:00
routes ::{
auth , match_find_error , match_replace_result , parse_id , timestamp_to_last_modified ,
} ,
2019-10-21 18:29:39 +00:00
Pool ,
2019-10-21 18:15:11 +00:00
} ;
2020-01-15 06:50:54 +00:00
use crate ::{
routes ::{ HIGHLIGHT_CONTENTS , HIGHLIGHT_LANGUAGE } ,
setup ::Config ,
} ;
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 ) ;
/// GET a text entry and display it
2020-01-15 05:10:48 +00:00
pub async fn get (
2020-01-15 06:50:54 +00:00
config : web ::Data < Config > ,
2019-10-15 11:52:20 +00:00
path : web ::Path < String > ,
pool : web ::Data < Pool > ,
2020-01-15 05:10:48 +00:00
) -> Result < HttpResponse , Error > {
let id = parse_id ( & path ) ? ;
match web ::block ( move | | queries ::texts ::find ( id , pool ) ) . await {
2020-01-15 06:50:54 +00:00
Ok ( text ) = > {
let last_modified = timestamp_to_last_modified ( text . created ) ;
if text . highlight {
let languages : Vec < String > = config
. highlight
. languages
. iter ( )
. map ( | l | HIGHLIGHT_LANGUAGE . replace ( " {{ language }} " , l ) )
. collect ( ) ;
let languages = languages . join ( " \n " ) ;
let contents = HIGHLIGHT_CONTENTS
. replace ( " {{ title }} " , & path )
. replace ( " {{ theme }} " , & config . highlight . theme )
. 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) ]
pub struct PutText {
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 > ,
body : web ::Json < PutText > ,
2019-10-15 11:52:20 +00:00
pool : web ::Data < Pool > ,
2019-10-21 20:59:13 +00:00
identity : Identity ,
2019-10-25 03:39:34 +00:00
password_hash : web ::Data < Vec < u8 > > ,
2020-01-15 05:10:48 +00:00
) -> Result < HttpResponse , Error > {
auth ( identity , request , & password_hash ) . await ? ;
let id = parse_id ( & path ) ? ;
match_replace_result (
2020-01-15 06:50:54 +00:00
web ::block ( move | | queries ::texts ::replace ( id , & body . contents , body . highlight , pool ) )
. await ,
2020-01-15 05:10:48 +00:00
)
2019-10-15 11:52:20 +00:00
}
2019-10-15 13:08:07 +00:00
delete! ( texts ) ;
2019-10-15 11:52:20 +00:00
}