2022-08-11 10:41:05 +00:00
|
|
|
//! Web server route handlers.
|
|
|
|
|
2022-06-13 07:24:55 +00:00
|
|
|
use async_std::channel::Sender;
|
2022-08-11 12:52:31 +00:00
|
|
|
use log::{info, warn};
|
2022-08-17 12:38:08 +00:00
|
|
|
use rocket::{
|
|
|
|
form::Form,
|
|
|
|
get, post,
|
|
|
|
request::FlashMessage,
|
|
|
|
response::{Flash, Redirect},
|
|
|
|
uri, FromForm, State,
|
|
|
|
};
|
2022-08-16 16:12:18 +00:00
|
|
|
use rocket_dyn_templates::{context, Template};
|
2022-06-13 07:24:55 +00:00
|
|
|
|
2022-08-11 07:28:37 +00:00
|
|
|
use crate::{
|
|
|
|
db::{Database, Peer},
|
|
|
|
sbot,
|
|
|
|
task_loop::Task,
|
2022-08-16 16:12:18 +00:00
|
|
|
utils,
|
2022-08-11 07:28:37 +00:00
|
|
|
};
|
2022-06-13 07:24:55 +00:00
|
|
|
|
|
|
|
#[derive(FromForm)]
|
2022-08-11 07:28:37 +00:00
|
|
|
pub struct PeerForm {
|
2022-06-13 07:24:55 +00:00
|
|
|
pub public_key: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/")]
|
2022-08-17 12:38:08 +00:00
|
|
|
pub async fn home(db: &State<Database>, flash: Option<FlashMessage<'_>>) -> Template {
|
2022-06-13 07:24:55 +00:00
|
|
|
let peers = db.get_peers();
|
2022-08-11 07:28:37 +00:00
|
|
|
let mut peers_unread = Vec::new();
|
|
|
|
for peer in peers {
|
|
|
|
let unread_count = db.get_unread_post_count(&peer.public_key);
|
|
|
|
peers_unread.push((peer, unread_count.to_string()));
|
|
|
|
}
|
|
|
|
|
2022-08-17 12:38:08 +00:00
|
|
|
Template::render("base", context! { peers: &peers_unread, flash: flash })
|
2022-06-13 07:24:55 +00:00
|
|
|
}
|
|
|
|
|
2022-08-11 07:28:37 +00:00
|
|
|
#[get("/posts/<public_key>/<msg_id>/delete")]
|
|
|
|
pub async fn delete_post(db: &State<Database>, public_key: &str, msg_id: &str) -> Redirect {
|
|
|
|
// Delete the post from the database. This method cannot panic, so we're
|
|
|
|
// safe to unwrap the result.
|
2022-08-11 12:52:31 +00:00
|
|
|
match db.remove_post(public_key, msg_id) {
|
|
|
|
Ok(_) => info!(
|
|
|
|
"Removed post {} by {} from 'posts' database tree",
|
|
|
|
msg_id, public_key
|
|
|
|
),
|
|
|
|
Err(e) => warn!(
|
|
|
|
"Failed to remove post {} by {} from 'posts' database tree: {}",
|
|
|
|
msg_id, public_key, e
|
|
|
|
),
|
|
|
|
}
|
2022-08-11 07:28:37 +00:00
|
|
|
|
|
|
|
Redirect::to(uri!(posts(public_key)))
|
|
|
|
}
|
|
|
|
|
2022-06-13 07:24:55 +00:00
|
|
|
#[get("/posts/<public_key>")]
|
|
|
|
pub async fn posts(db: &State<Database>, public_key: &str) -> Template {
|
|
|
|
let peers = db.get_peers();
|
2022-08-11 07:28:37 +00:00
|
|
|
let mut peers_unread = Vec::new();
|
|
|
|
for peer in peers {
|
|
|
|
let unread_count = db.get_unread_post_count(&peer.public_key);
|
|
|
|
peers_unread.push((peer, unread_count.to_string()));
|
|
|
|
}
|
|
|
|
|
|
|
|
let posts = db.get_posts(public_key).unwrap();
|
|
|
|
|
2022-08-16 16:12:18 +00:00
|
|
|
// Define context data to be rendered in the template.
|
|
|
|
let context = context! {
|
|
|
|
selected_peer: &public_key,
|
|
|
|
peers: &peers_unread,
|
|
|
|
posts: &posts
|
|
|
|
};
|
2022-06-13 07:24:55 +00:00
|
|
|
|
2022-08-16 16:12:18 +00:00
|
|
|
Template::render("base", context)
|
2022-06-13 07:24:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/posts/<public_key>/<msg_id>")]
|
|
|
|
pub async fn post(db: &State<Database>, public_key: &str, msg_id: &str) -> Template {
|
|
|
|
let peers = db.get_peers();
|
2022-08-11 07:28:37 +00:00
|
|
|
let mut peers_unread = Vec::new();
|
|
|
|
for peer in peers {
|
|
|
|
let unread_count = db.get_unread_post_count(&peer.public_key);
|
|
|
|
peers_unread.push((peer, unread_count.to_string()));
|
|
|
|
}
|
|
|
|
|
2022-06-13 07:24:55 +00:00
|
|
|
let posts = db.get_posts(public_key).unwrap();
|
|
|
|
let post = db.get_post(public_key, msg_id).unwrap();
|
|
|
|
|
2022-08-16 16:12:18 +00:00
|
|
|
let context = context! {
|
|
|
|
peers: &peers_unread,
|
|
|
|
selected_peer: &public_key,
|
|
|
|
selected_peer_encoded: &uri_encode::encode_uri_component(public_key),
|
|
|
|
selected_post: &msg_id,
|
|
|
|
selected_post_encoded: &uri_encode::encode_uri_component(msg_id),
|
|
|
|
posts: &posts,
|
|
|
|
post: &post,
|
|
|
|
post_is_selected: &true
|
|
|
|
};
|
|
|
|
|
|
|
|
Template::render("base", context)
|
2022-07-07 08:02:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/posts/<public_key>/<msg_id>/read")]
|
|
|
|
pub async fn mark_post_read(db: &State<Database>, public_key: &str, msg_id: &str) -> Redirect {
|
|
|
|
// Retrieve the post from the database, mark it as read and reinsert it.
|
|
|
|
if let Ok(Some(mut post)) = db.get_post(public_key, msg_id) {
|
|
|
|
post.read = true;
|
|
|
|
db.add_post(public_key, post).unwrap();
|
|
|
|
} else {
|
|
|
|
warn!(
|
2022-08-11 12:52:31 +00:00
|
|
|
"Failed to find post {} authored by {} in 'posts' database tree",
|
2022-07-07 08:02:13 +00:00
|
|
|
msg_id, public_key
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
Redirect::to(uri!(post(public_key, msg_id)))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/posts/<public_key>/<msg_id>/unread")]
|
|
|
|
pub async fn mark_post_unread(db: &State<Database>, public_key: &str, msg_id: &str) -> Redirect {
|
|
|
|
// Retrieve the post from the database, mark it as unread and reinsert it.
|
|
|
|
if let Ok(Some(mut post)) = db.get_post(public_key, msg_id) {
|
|
|
|
post.read = false;
|
|
|
|
db.add_post(public_key, post).unwrap();
|
|
|
|
} else {
|
|
|
|
warn!(
|
2022-08-11 12:52:31 +00:00
|
|
|
"Failed to find post {} authored by {} in 'posts' database tree",
|
2022-07-07 08:02:13 +00:00
|
|
|
msg_id, public_key
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
Redirect::to(uri!(post(public_key, msg_id)))
|
2022-06-13 07:24:55 +00:00
|
|
|
}
|
|
|
|
|
2022-08-11 07:28:37 +00:00
|
|
|
#[get("/posts/download_latest")]
|
|
|
|
pub async fn download_latest_posts(db: &State<Database>, tx: &State<Sender<Task>>) -> Redirect {
|
|
|
|
for peer in db.get_peers() {
|
|
|
|
// Fetch the latest root posts authored by each peer we're
|
|
|
|
// subscribed to. Posts will be added to the key-value database.
|
|
|
|
if let Err(e) = tx
|
|
|
|
.send(Task::FetchLatestPosts(peer.public_key.clone()))
|
|
|
|
.await
|
|
|
|
{
|
2022-08-11 12:52:31 +00:00
|
|
|
warn!("Task loop error: {}", e)
|
2022-08-11 07:28:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Fetch the latest name for each peer we're subscribed to and update
|
|
|
|
// the database.
|
|
|
|
if let Err(e) = tx.send(Task::FetchLatestName(peer.public_key)).await {
|
2022-08-11 12:52:31 +00:00
|
|
|
warn!("Task loop error: {}", e)
|
2022-08-11 07:28:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Redirect::to(uri!(home))
|
|
|
|
}
|
|
|
|
|
2022-06-13 07:24:55 +00:00
|
|
|
#[post("/subscribe", data = "<peer>")]
|
|
|
|
pub async fn subscribe_form(
|
|
|
|
db: &State<Database>,
|
|
|
|
tx: &State<Sender<Task>>,
|
2022-08-11 07:28:37 +00:00
|
|
|
peer: Form<PeerForm>,
|
2022-08-17 12:38:08 +00:00
|
|
|
) -> Result<Redirect, Flash<Redirect>> {
|
2022-08-11 12:52:31 +00:00
|
|
|
info!("Subscribing to peer {}", &peer.public_key);
|
|
|
|
|
2022-08-11 13:27:45 +00:00
|
|
|
if let Err(e) = utils::validate_public_key(&peer.public_key) {
|
2022-08-17 12:38:08 +00:00
|
|
|
let err_msg = format!("Public key {} is invalid: {}", &peer.public_key, e);
|
|
|
|
warn!("{}", err_msg);
|
|
|
|
return Err(Flash::error(Redirect::to(uri!(home)), err_msg));
|
2022-08-11 13:27:45 +00:00
|
|
|
} else {
|
2022-08-11 12:52:31 +00:00
|
|
|
info!("Public key {} is valid", &peer.public_key);
|
2022-08-11 07:28:37 +00:00
|
|
|
|
|
|
|
// Retrieve the name of the peer to which we are subscribing.
|
|
|
|
let peer_name = match sbot::get_name(&peer.public_key).await {
|
|
|
|
Ok(name) => name,
|
|
|
|
Err(e) => {
|
2022-08-11 12:52:31 +00:00
|
|
|
warn!("Failed to fetch name for peer {}: {}", &peer.public_key, e);
|
2022-08-11 07:28:37 +00:00
|
|
|
String::from("")
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let peer_info = Peer::new(&peer.public_key).set_name(&peer_name);
|
|
|
|
|
|
|
|
// Follow the peer if our local instance is not already following.
|
2022-08-22 07:31:06 +00:00
|
|
|
match sbot::follow_if_not_following(&peer.public_key).await {
|
|
|
|
Ok(_) => {
|
|
|
|
// Add the peer to the database.
|
|
|
|
if db.add_peer(peer_info).is_ok() {
|
|
|
|
info!("Added {} to 'peers' database tree", &peer.public_key);
|
|
|
|
let peer_id = peer.public_key.to_string();
|
|
|
|
|
|
|
|
// Fetch all root posts authored by the peer we're subscribing
|
|
|
|
// to. Posts will be added to the key-value database.
|
|
|
|
if let Err(e) = tx.send(Task::FetchAllPosts(peer_id)).await {
|
|
|
|
warn!("Task loop error: {}", e)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
let err_msg = format!(
|
|
|
|
"Failed to add peer {} to 'peers' database tree",
|
|
|
|
&peer.public_key
|
|
|
|
);
|
|
|
|
warn!("{}", err_msg);
|
|
|
|
return Err(Flash::error(Redirect::to(uri!(home)), err_msg));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(e) => {
|
2022-08-22 15:20:23 +00:00
|
|
|
warn!("{}", e);
|
|
|
|
return Err(Flash::error(Redirect::to(uri!(home)), e));
|
2022-08-11 07:28:37 +00:00
|
|
|
}
|
2022-06-13 07:24:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-17 12:38:08 +00:00
|
|
|
Ok(Redirect::to(uri!(home)))
|
2022-06-13 07:24:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[post("/unsubscribe", data = "<peer>")]
|
2022-08-17 12:38:08 +00:00
|
|
|
pub async fn unsubscribe_form(
|
|
|
|
db: &State<Database>,
|
|
|
|
peer: Form<PeerForm>,
|
|
|
|
) -> Result<Redirect, Flash<Redirect>> {
|
2022-08-11 12:52:31 +00:00
|
|
|
info!("Unsubscribing from peer {}", &peer.public_key);
|
|
|
|
|
2022-08-11 07:28:37 +00:00
|
|
|
if let Err(e) = utils::validate_public_key(&peer.public_key) {
|
2022-08-17 12:38:08 +00:00
|
|
|
let err_msg = format!("Public key {} is invalid: {}", &peer.public_key, e);
|
|
|
|
warn!("{}", err_msg);
|
|
|
|
return Err(Flash::error(Redirect::to(uri!(home)), err_msg));
|
2022-08-11 07:28:37 +00:00
|
|
|
} else {
|
2022-08-11 12:52:31 +00:00
|
|
|
info!("Public key {} is valid", &peer.public_key);
|
2022-08-22 15:20:23 +00:00
|
|
|
match sbot::unfollow_if_following(&peer.public_key).await {
|
|
|
|
Ok(_) => {
|
|
|
|
if db.remove_peer(&peer.public_key).is_ok() {
|
|
|
|
info!(
|
|
|
|
"Removed peer {} from 'peers' database tree",
|
|
|
|
&peer.public_key
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
warn!(
|
|
|
|
"Failed to remove peer {} from 'peers' database tree",
|
|
|
|
&peer.public_key
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
warn!("{}", e);
|
|
|
|
return Err(Flash::error(Redirect::to(uri!(home)), e));
|
|
|
|
}
|
2022-06-13 07:24:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-17 12:38:08 +00:00
|
|
|
Ok(Redirect::to(uri!(home)))
|
2022-06-13 07:24:55 +00:00
|
|
|
}
|