2021-03-09 15:49:06 +13:00
// Global specifiers
#![ forbid(unsafe_code) ]
2021-12-27 18:18:20 +13:00
#![ allow(clippy::cmp_owned) ]
2021-03-09 15:49:06 +13:00
2020-10-26 09:25:59 +13:00
// Reference local files
2023-01-03 22:55:22 +13:00
mod config ;
2022-11-10 05:16:51 +13:00
mod duplicates ;
2023-01-30 22:02:43 +13:00
mod instance_info ;
2023-06-06 12:31:25 +12:00
mod oauth ;
2020-10-26 09:25:59 +13:00
mod post ;
2021-01-01 12:54:13 +13:00
mod search ;
2021-01-06 15:04:49 +13:00
mod settings ;
2020-10-26 09:25:59 +13:00
mod subreddit ;
2020-10-26 16:57:19 +13:00
mod user ;
2020-11-26 10:53:30 +13:00
mod utils ;
2020-10-26 09:25:59 +13:00
2021-02-14 12:02:38 +13:00
// Import Crates
2023-01-12 21:41:59 +13:00
use clap ::{ Arg , ArgAction , Command } ;
2021-02-14 12:02:38 +13:00
2021-03-18 11:30:33 +13:00
use futures_lite ::FutureExt ;
2021-03-18 12:32:28 +13:00
use hyper ::{ header ::HeaderValue , Body , Request , Response } ;
2021-02-10 06:38:52 +13:00
2021-03-18 11:30:33 +13:00
mod client ;
2022-11-05 21:29:04 +13:00
use client ::{ canonical_path , proxy } ;
2023-06-09 06:33:54 +12:00
use log ::info ;
2023-01-30 22:02:43 +13:00
use once_cell ::sync ::Lazy ;
2021-03-18 11:30:33 +13:00
use server ::RequestExt ;
2022-05-21 13:41:31 +12:00
use utils ::{ error , redirect , ThemeAssets } ;
2021-03-18 11:30:33 +13:00
2023-06-09 06:33:54 +12:00
use crate ::client ::OAUTH_CLIENT ;
2021-03-18 11:30:33 +13:00
mod server ;
2021-02-10 06:38:52 +13:00
2020-10-26 09:25:59 +13:00
// Create Services
2021-02-01 23:10:53 +13:00
// Required for the manifest to be valid
2021-03-18 11:30:33 +13:00
async fn pwa_logo ( ) -> Result < Response < Body > , String > {
Ok (
Response ::builder ( )
. status ( 200 )
. header ( " content-type " , " image/png " )
. body ( include_bytes! ( " ../static/logo.png " ) . as_ref ( ) . into ( ) )
. unwrap_or_default ( ) ,
)
2021-02-01 23:10:53 +13:00
}
// Required for iOS App Icons
2021-03-18 11:30:33 +13:00
async fn iphone_logo ( ) -> Result < Response < Body > , String > {
2021-02-10 06:38:52 +13:00
Ok (
2021-03-18 11:30:33 +13:00
Response ::builder ( )
. status ( 200 )
. header ( " content-type " , " image/png " )
. body ( include_bytes! ( " ../static/apple-touch-icon.png " ) . as_ref ( ) . into ( ) )
. unwrap_or_default ( ) ,
2021-02-10 06:38:52 +13:00
)
2021-02-01 23:10:53 +13:00
}
2021-03-18 11:30:33 +13:00
async fn favicon ( ) -> Result < Response < Body > , String > {
2021-02-10 06:38:52 +13:00
Ok (
2021-03-18 11:30:33 +13:00
Response ::builder ( )
. status ( 200 )
. header ( " content-type " , " image/vnd.microsoft.icon " )
2021-02-10 06:38:52 +13:00
. header ( " Cache-Control " , " public, max-age=1209600, s-maxage=86400 " )
2021-03-18 11:30:33 +13:00
. body ( include_bytes! ( " ../static/favicon.ico " ) . as_ref ( ) . into ( ) )
. unwrap_or_default ( ) ,
2021-02-10 06:38:52 +13:00
)
2020-10-26 09:25:59 +13:00
}
2021-05-20 11:09:08 +12:00
async fn font ( ) -> Result < Response < Body > , String > {
Ok (
Response ::builder ( )
. status ( 200 )
. header ( " content-type " , " font/woff2 " )
2021-11-29 11:47:50 +13:00
. header ( " Cache-Control " , " public, max-age=1209600, s-maxage=86400 " )
2021-05-20 11:09:08 +12:00
. body ( include_bytes! ( " ../static/Inter.var.woff2 " ) . as_ref ( ) . into ( ) )
. unwrap_or_default ( ) ,
)
}
2021-03-18 11:30:33 +13:00
async fn resource ( body : & str , content_type : & str , cache : bool ) -> Result < Response < Body > , String > {
let mut res = Response ::builder ( )
. status ( 200 )
. header ( " content-type " , content_type )
. body ( body . to_string ( ) . into ( ) )
. unwrap_or_default ( ) ;
2021-02-25 06:26:01 +13:00
if cache {
2021-03-21 18:10:31 +13:00
if let Ok ( val ) = HeaderValue ::from_str ( " public, max-age=1209600, s-maxage=86400 " ) {
res . headers_mut ( ) . insert ( " Cache-Control " , val ) ;
2021-03-18 12:32:28 +13:00
}
2021-02-25 06:26:01 +13:00
}
Ok ( res )
}
2022-05-21 13:41:31 +12:00
async fn style ( ) -> Result < Response < Body > , String > {
let mut res = include_str! ( " ../static/style.css " ) . to_string ( ) ;
for file in ThemeAssets ::iter ( ) {
2022-05-21 14:20:44 +12:00
res . push ( '\n' ) ;
2022-05-21 13:41:31 +12:00
let theme = ThemeAssets ::get ( file . as_ref ( ) ) . unwrap ( ) ;
res . push_str ( std ::str ::from_utf8 ( theme . data . as_ref ( ) ) . unwrap ( ) ) ;
}
Ok (
Response ::builder ( )
. status ( 200 )
. header ( " content-type " , " text/css " )
. header ( " Cache-Control " , " public, max-age=1209600, s-maxage=86400 " )
. body ( res . to_string ( ) . into ( ) )
. unwrap_or_default ( ) ,
)
}
2021-03-18 11:30:33 +13:00
#[ tokio::main ]
async fn main ( ) {
2023-06-07 07:05:20 +12:00
// Load environment variables
2023-06-07 07:07:11 +12:00
_ = dotenvy ::dotenv ( ) ;
2023-06-07 07:05:20 +12:00
// Initialize logger
pretty_env_logger ::init ( ) ;
2023-12-27 12:25:52 +13:00
let matches = Command ::new ( " Redlib " )
2021-02-19 08:40:10 +13:00
. version ( env! ( " CARGO_PKG_VERSION " ) )
. about ( " Private front-end for Reddit written in Rust " )
2021-05-27 15:30:08 +12:00
. arg (
2022-01-06 13:39:56 +13:00
Arg ::new ( " redirect-https " )
. short ( 'r' )
2021-05-27 15:30:08 +12:00
. long ( " redirect-https " )
. help ( " Redirect all HTTP requests to HTTPS (no longer functional) " )
2022-11-01 16:23:59 +13:00
. num_args ( 0 ) ,
2021-05-27 15:30:08 +12:00
)
2021-02-19 08:40:10 +13:00
. arg (
2022-01-06 13:39:56 +13:00
Arg ::new ( " address " )
. short ( 'a' )
2021-02-19 08:40:10 +13:00
. long ( " address " )
. value_name ( " ADDRESS " )
. help ( " Sets address to listen on " )
2021-02-19 08:49:50 +13:00
. default_value ( " 0.0.0.0 " )
2022-11-01 16:23:59 +13:00
. num_args ( 1 ) ,
2021-02-19 08:49:50 +13:00
)
. arg (
2022-01-06 13:39:56 +13:00
Arg ::new ( " port " )
. short ( 'p' )
2021-02-19 08:49:50 +13:00
. long ( " port " )
. value_name ( " PORT " )
2023-01-12 21:41:59 +13:00
. env ( " PORT " )
2021-02-19 08:49:50 +13:00
. help ( " Port to listen on " )
. default_value ( " 8080 " )
2023-01-12 21:41:59 +13:00
. action ( ArgAction ::Set )
2022-11-01 16:23:59 +13:00
. num_args ( 1 ) ,
2021-02-19 08:40:10 +13:00
)
2021-03-21 18:10:31 +13:00
. arg (
2022-01-06 13:39:56 +13:00
Arg ::new ( " hsts " )
. short ( 'H' )
2021-03-21 18:10:31 +13:00
. long ( " hsts " )
. value_name ( " EXPIRE_TIME " )
. help ( " HSTS header to tell browsers that this site should only be accessed over HTTPS " )
. default_value ( " 604800 " )
2022-11-01 16:23:59 +13:00
. num_args ( 1 ) ,
2021-03-21 18:10:31 +13:00
)
2021-02-19 08:40:10 +13:00
. get_matches ( ) ;
2023-01-12 21:41:59 +13:00
let address = matches . get_one ::< String > ( " address " ) . unwrap ( ) ;
let port = matches . get_one ::< String > ( " port " ) . unwrap ( ) ;
2022-11-01 16:23:59 +13:00
let hsts = matches . get_one ( " hsts " ) . map ( | m : & String | m . as_str ( ) ) ;
2021-02-19 08:40:10 +13:00
2023-01-12 21:41:59 +13:00
let listener = [ address , " : " , port ] . concat ( ) ;
2020-11-23 16:21:07 +13:00
2023-12-27 12:25:52 +13:00
println! ( " Starting Redlib... " ) ;
2021-02-25 08:00:04 +13:00
2021-03-18 11:30:33 +13:00
// Begin constructing a server
let mut app = server ::Server ::new ( ) ;
2021-02-10 06:38:52 +13:00
2023-01-30 22:02:43 +13:00
// Force evaluation of statics. In instance_info case, we need to evaluate
2023-03-09 17:53:23 +13:00
// the timestamp so deploy date is accurate - in config case, we need to
2023-06-09 06:33:54 +12:00
// evaluate the configuration to avoid paying penalty at first request -
// in OAUTH case, we need to retrieve the token to avoid paying penalty
// at first request
2023-01-30 22:02:43 +13:00
2023-06-09 06:33:54 +12:00
info! ( " Evaluating config. " ) ;
2023-01-30 22:02:43 +13:00
Lazy ::force ( & config ::CONFIG ) ;
2023-06-09 06:33:54 +12:00
info! ( " Evaluating instance info. " ) ;
2023-01-30 22:02:43 +13:00
Lazy ::force ( & instance_info ::INSTANCE_INFO ) ;
2023-06-09 06:33:54 +12:00
info! ( " Creating OAUTH client. " ) ;
Lazy ::force ( & OAUTH_CLIENT ) ;
2023-01-30 22:02:43 +13:00
2021-03-18 11:30:33 +13:00
// Define default headers (added to all responses)
app . default_headers = headers! {
" Referrer-Policy " = > " no-referrer " ,
" X-Content-Type-Options " = > " nosniff " ,
" X-Frame-Options " = > " DENY " ,
2021-05-20 11:09:08 +12:00
" Content-Security-Policy " = > " default-src 'none'; font-src 'self'; script-src 'self' blob:; manifest-src 'self'; media-src 'self' data: blob: about:; style-src 'self' 'unsafe-inline'; base-uri 'none'; img-src 'self' data:; form-action 'self'; frame-ancestors 'none'; connect-src 'self'; worker-src blob:; "
2021-03-18 11:30:33 +13:00
} ;
2021-02-10 06:38:52 +13:00
2021-03-21 18:10:31 +13:00
if let Some ( expire_time ) = hsts {
if let Ok ( val ) = HeaderValue ::from_str ( & format! ( " max-age= {} " , expire_time ) ) {
app . default_headers . insert ( " Strict-Transport-Security " , val ) ;
}
}
2021-02-10 06:38:52 +13:00
// Read static files
2022-05-21 13:41:31 +12:00
app . at ( " /style.css " ) . get ( | _ | style ( ) . boxed ( ) ) ;
2021-02-26 06:07:45 +13:00
app
2021-03-18 11:30:33 +13:00
. at ( " /manifest.json " )
. get ( | _ | resource ( include_str! ( " ../static/manifest.json " ) , " application/json " , false ) . boxed ( ) ) ;
2023-04-26 23:52:12 +12:00
app . at ( " /robots.txt " ) . get ( | _ | {
resource (
2023-12-27 12:25:52 +13:00
if match config ::get_setting ( " REDLIB_ROBOTS_DISABLE_INDEXING " ) {
2023-04-26 23:52:12 +12:00
Some ( val ) = > val = = " on " ,
None = > false ,
} {
" User-agent: * \n Disallow: / "
} else {
" User-agent: * \n Disallow: /u/ \n Disallow: /user/ "
} ,
" text/plain " ,
true ,
)
. boxed ( )
} ) ;
2021-03-18 11:30:33 +13:00
app . at ( " /favicon.ico " ) . get ( | _ | favicon ( ) . boxed ( ) ) ;
app . at ( " /logo.png " ) . get ( | _ | pwa_logo ( ) . boxed ( ) ) ;
2021-05-20 11:09:08 +12:00
app . at ( " /Inter.var.woff2 " ) . get ( | _ | font ( ) . boxed ( ) ) ;
2021-03-18 11:30:33 +13:00
app . at ( " /touch-icon-iphone.png " ) . get ( | _ | iphone_logo ( ) . boxed ( ) ) ;
app . at ( " /apple-touch-icon.png " ) . get ( | _ | iphone_logo ( ) . boxed ( ) ) ;
2021-05-10 13:25:52 +12:00
app
. at ( " /playHLSVideo.js " )
. get ( | _ | resource ( include_str! ( " ../static/playHLSVideo.js " ) , " text/javascript " , false ) . boxed ( ) ) ;
app
. at ( " /hls.min.js " )
. get ( | _ | resource ( include_str! ( " ../static/hls.min.js " ) , " text/javascript " , false ) . boxed ( ) ) ;
2021-02-10 06:38:52 +13:00
2023-12-27 12:25:52 +13:00
// Proxy media through Redlib
2021-03-18 11:30:33 +13:00
app . at ( " /vid/:id/:size " ) . get ( | r | proxy ( r , " https://v.redd.it/{id}/DASH_{size} " ) . boxed ( ) ) ;
2021-05-10 13:25:52 +12:00
app . at ( " /hls/:id/*path " ) . get ( | r | proxy ( r , " https://v.redd.it/{id}/{path} " ) . boxed ( ) ) ;
2021-11-25 15:08:27 +13:00
app . at ( " /img/*path " ) . get ( | r | proxy ( r , " https://i.redd.it/{path} " ) . boxed ( ) ) ;
2021-03-18 11:30:33 +13:00
app . at ( " /thumb/:point/:id " ) . get ( | r | proxy ( r , " https://{point}.thumbs.redditmedia.com/{id} " ) . boxed ( ) ) ;
app . at ( " /emoji/:id/:name " ) . get ( | r | proxy ( r , " https://emoji.redditmedia.com/{id}/{name} " ) . boxed ( ) ) ;
2021-11-29 11:47:50 +13:00
app
. at ( " /preview/:loc/award_images/:fullname/:id " )
. get ( | r | proxy ( r , " https://{loc}view.redd.it/award_images/{fullname}/{id} " ) . boxed ( ) ) ;
2021-04-09 17:26:03 +12:00
app . at ( " /preview/:loc/:id " ) . get ( | r | proxy ( r , " https://{loc}view.redd.it/{id} " ) . boxed ( ) ) ;
2021-03-18 11:30:33 +13:00
app . at ( " /style/*path " ) . get ( | r | proxy ( r , " https://styles.redditmedia.com/{path} " ) . boxed ( ) ) ;
app . at ( " /static/*path " ) . get ( | r | proxy ( r , " https://www.redditstatic.com/{path} " ) . boxed ( ) ) ;
2021-02-10 06:38:52 +13:00
// Browse user profile
2021-03-18 11:30:33 +13:00
app
. at ( " /u/:name " )
. get ( | r | async move { Ok ( redirect ( format! ( " /user/ {} " , r . param ( " name " ) . unwrap_or_default ( ) ) ) ) } . boxed ( ) ) ;
app . at ( " /u/:name/comments/:id/:title " ) . get ( | r | post ::item ( r ) . boxed ( ) ) ;
app . at ( " /u/:name/comments/:id/:title/:comment_id " ) . get ( | r | post ::item ( r ) . boxed ( ) ) ;
2021-02-10 06:38:52 +13:00
2021-03-18 17:40:55 +13:00
app . at ( " /user/[deleted] " ) . get ( | req | error ( req , " User has deleted their account " . to_string ( ) ) . boxed ( ) ) ;
2021-03-18 11:30:33 +13:00
app . at ( " /user/:name " ) . get ( | r | user ::profile ( r ) . boxed ( ) ) ;
2022-03-14 08:06:27 +13:00
app . at ( " /user/:name/:listing " ) . get ( | r | user ::profile ( r ) . boxed ( ) ) ;
2021-03-18 11:30:33 +13:00
app . at ( " /user/:name/comments/:id " ) . get ( | r | post ::item ( r ) . boxed ( ) ) ;
app . at ( " /user/:name/comments/:id/:title " ) . get ( | r | post ::item ( r ) . boxed ( ) ) ;
app . at ( " /user/:name/comments/:id/:title/:comment_id " ) . get ( | r | post ::item ( r ) . boxed ( ) ) ;
2021-02-10 06:38:52 +13:00
// Configure settings
2021-03-18 11:30:33 +13:00
app . at ( " /settings " ) . get ( | r | settings ::get ( r ) . boxed ( ) ) . post ( | r | settings ::set ( r ) . boxed ( ) ) ;
app . at ( " /settings/restore " ) . get ( | r | settings ::restore ( r ) . boxed ( ) ) ;
2021-05-10 13:25:52 +12:00
app . at ( " /settings/update " ) . get ( | r | settings ::update ( r ) . boxed ( ) ) ;
2021-02-10 06:38:52 +13:00
// Subreddit services
2021-05-17 03:53:39 +12:00
app
. at ( " /r/:sub " )
. get ( | r | subreddit ::community ( r ) . boxed ( ) )
. post ( | r | subreddit ::add_quarantine_exception ( r ) . boxed ( ) ) ;
2021-02-25 06:26:01 +13:00
2021-03-20 18:04:44 +13:00
app
. at ( " /r/u_:name " )
. get ( | r | async move { Ok ( redirect ( format! ( " /user/ {} " , r . param ( " name " ) . unwrap_or_default ( ) ) ) ) } . boxed ( ) ) ;
2021-11-26 17:02:04 +13:00
app . at ( " /r/:sub/subscribe " ) . post ( | r | subreddit ::subscriptions_filters ( r ) . boxed ( ) ) ;
app . at ( " /r/:sub/unsubscribe " ) . post ( | r | subreddit ::subscriptions_filters ( r ) . boxed ( ) ) ;
app . at ( " /r/:sub/filter " ) . post ( | r | subreddit ::subscriptions_filters ( r ) . boxed ( ) ) ;
app . at ( " /r/:sub/unfilter " ) . post ( | r | subreddit ::subscriptions_filters ( r ) . boxed ( ) ) ;
2021-02-25 06:26:01 +13:00
2021-03-18 11:30:33 +13:00
app . at ( " /r/:sub/comments/:id " ) . get ( | r | post ::item ( r ) . boxed ( ) ) ;
app . at ( " /r/:sub/comments/:id/:title " ) . get ( | r | post ::item ( r ) . boxed ( ) ) ;
app . at ( " /r/:sub/comments/:id/:title/:comment_id " ) . get ( | r | post ::item ( r ) . boxed ( ) ) ;
2022-11-02 16:53:42 +13:00
app . at ( " /comments/:id " ) . get ( | r | post ::item ( r ) . boxed ( ) ) ;
app . at ( " /comments/:id/comments " ) . get ( | r | post ::item ( r ) . boxed ( ) ) ;
app . at ( " /comments/:id/comments/:comment_id " ) . get ( | r | post ::item ( r ) . boxed ( ) ) ;
app . at ( " /comments/:id/:title " ) . get ( | r | post ::item ( r ) . boxed ( ) ) ;
app . at ( " /comments/:id/:title/:comment_id " ) . get ( | r | post ::item ( r ) . boxed ( ) ) ;
2021-02-25 06:26:01 +13:00
2022-11-10 05:16:51 +13:00
app . at ( " /r/:sub/duplicates/:id " ) . get ( | r | duplicates ::item ( r ) . boxed ( ) ) ;
app . at ( " /r/:sub/duplicates/:id/:title " ) . get ( | r | duplicates ::item ( r ) . boxed ( ) ) ;
app . at ( " /duplicates/:id " ) . get ( | r | duplicates ::item ( r ) . boxed ( ) ) ;
app . at ( " /duplicates/:id/:title " ) . get ( | r | duplicates ::item ( r ) . boxed ( ) ) ;
2021-03-18 11:30:33 +13:00
app . at ( " /r/:sub/search " ) . get ( | r | search ::find ( r ) . boxed ( ) ) ;
2021-02-25 06:26:01 +13:00
2021-03-18 17:40:55 +13:00
app
. at ( " /r/:sub/w " )
. get ( | r | async move { Ok ( redirect ( format! ( " /r/ {} /wiki " , r . param ( " sub " ) . unwrap_or_default ( ) ) ) ) } . boxed ( ) ) ;
app
2021-04-15 16:53:17 +12:00
. at ( " /r/:sub/w/*page " )
2021-03-18 17:40:55 +13:00
. get ( | r | async move { Ok ( redirect ( format! ( " /r/ {} /wiki/ {} " , r . param ( " sub " ) . unwrap_or_default ( ) , r . param ( " wiki " ) . unwrap_or_default ( ) ) ) ) } . boxed ( ) ) ;
app . at ( " /r/:sub/wiki " ) . get ( | r | subreddit ::wiki ( r ) . boxed ( ) ) ;
2021-04-15 16:53:17 +12:00
app . at ( " /r/:sub/wiki/*page " ) . get ( | r | subreddit ::wiki ( r ) . boxed ( ) ) ;
2021-02-25 06:26:01 +13:00
2021-03-22 15:28:05 +13:00
app . at ( " /r/:sub/about/sidebar " ) . get ( | r | subreddit ::sidebar ( r ) . boxed ( ) ) ;
2021-03-18 11:30:33 +13:00
app . at ( " /r/:sub/:sort " ) . get ( | r | subreddit ::community ( r ) . boxed ( ) ) ;
2021-02-10 06:38:52 +13:00
// Front page
2021-03-18 11:30:33 +13:00
app . at ( " / " ) . get ( | r | subreddit ::community ( r ) . boxed ( ) ) ;
2021-02-10 06:38:52 +13:00
// View Reddit wiki
2021-03-21 11:42:47 +13:00
app . at ( " /w " ) . get ( | _ | async { Ok ( redirect ( " /wiki " . to_string ( ) ) ) } . boxed ( ) ) ;
2021-03-18 17:40:55 +13:00
app
2021-04-15 16:53:17 +12:00
. at ( " /w/*page " )
2021-03-18 17:40:55 +13:00
. get ( | r | async move { Ok ( redirect ( format! ( " /wiki/ {} " , r . param ( " page " ) . unwrap_or_default ( ) ) ) ) } . boxed ( ) ) ;
2021-03-18 11:30:33 +13:00
app . at ( " /wiki " ) . get ( | r | subreddit ::wiki ( r ) . boxed ( ) ) ;
2021-04-15 16:53:17 +12:00
app . at ( " /wiki/*page " ) . get ( | r | subreddit ::wiki ( r ) . boxed ( ) ) ;
2021-02-10 06:38:52 +13:00
// Search all of Reddit
2021-03-18 11:30:33 +13:00
app . at ( " /search " ) . get ( | r | search ::find ( r ) . boxed ( ) ) ;
2021-02-10 06:38:52 +13:00
2021-02-22 07:13:20 +13:00
// Handle about pages
2021-03-18 11:30:33 +13:00
app . at ( " /about " ) . get ( | req | error ( req , " About pages aren't added yet " . to_string ( ) ) . boxed ( ) ) ;
2023-01-30 22:02:43 +13:00
// Instance info page
app . at ( " /info " ) . get ( | r | instance_info ::instance_info ( r ) . boxed ( ) ) ;
app . at ( " /info.:extension " ) . get ( | r | instance_info ::instance_info ( r ) . boxed ( ) ) ;
2023-10-05 05:55:33 +13:00
// Handle obfuscated share links.
// Note that this still forces the server to follow the share link to get to the post, so maybe this wants to be updated with a warning before it follow it
app . at ( " /r/:sub/s/:id " ) . get ( | req : Request < Body > | {
Box ::pin ( async move {
let sub = req . param ( " sub " ) . unwrap_or_default ( ) ;
match req . param ( " id " ) . as_deref ( ) {
// Share link
Some ( id ) if ( 8 .. 12 ) . contains ( & id . len ( ) ) = > match canonical_path ( format! ( " /r/ {} /s/ {} " , sub , id ) ) . await {
2023-11-22 18:34:13 +13:00
Ok ( Some ( path ) ) = > Ok ( redirect ( path . split ( '?' ) . next ( ) . unwrap_or_default ( ) . to_string ( ) ) ) ,
Ok ( None ) = > error ( req , " Post ID is invalid. It may point to a post on a community that has been banned. " ) . await ,
2023-10-05 05:55:33 +13:00
Err ( e ) = > error ( req , e ) . await ,
} ,
// Error message for unknown pages
_ = > error ( req , " Nothing here " . to_string ( ) ) . await ,
}
} )
} ) ;
2022-11-05 21:29:04 +13:00
app . at ( " /:id " ) . get ( | req : Request < Body > | {
Box ::pin ( async move {
match req . param ( " id " ) . as_deref ( ) {
// Sort front page
Some ( " best " | " hot " | " new " | " top " | " rising " | " controversial " ) = > subreddit ::community ( req ) . await ,
// Short link for post
2023-01-02 12:06:58 +13:00
Some ( id ) if ( 5 .. 8 ) . contains ( & id . len ( ) ) = > match canonical_path ( format! ( " / {} " , id ) ) . await {
2022-11-05 21:29:04 +13:00
Ok ( path_opt ) = > match path_opt {
Some ( path ) = > Ok ( redirect ( path ) ) ,
None = > error ( req , " Post ID is invalid. It may point to a post on a community that has been banned. " ) . await ,
} ,
Err ( e ) = > error ( req , e ) . await ,
} ,
// Error message for unknown pages
_ = > error ( req , " Nothing here " . to_string ( ) ) . await ,
}
} )
2021-02-10 06:38:52 +13:00
} ) ;
// Default service in case no routes match
2021-03-18 11:30:33 +13:00
app . at ( " /* " ) . get ( | req | error ( req , " Nothing here " . to_string ( ) ) . boxed ( ) ) ;
2021-02-10 06:38:52 +13:00
2023-12-27 12:25:52 +13:00
println! ( " Running Redlib v {} on {} ! " , env! ( " CARGO_PKG_VERSION " ) , listener ) ;
2021-02-25 08:00:04 +13:00
2021-03-18 11:30:33 +13:00
let server = app . listen ( listener ) ;
2021-02-25 06:26:01 +13:00
2021-03-18 11:30:33 +13:00
// Run this server for... forever!
if let Err ( e ) = server . await {
eprintln! ( " Server error: {} " , e ) ;
}
2020-10-26 16:57:19 +13:00
}