lykin/src/routes.rs

229 lines
7.3 KiB
Rust

//! Web server route handlers.
use async_std::channel::Sender;
use log::{debug, warn};
use rocket::{form::Form, get, post, response::Redirect, uri, FromForm, State};
use rocket_dyn_templates::{tera::Context, Template};
use crate::{
db::{Database, Peer},
sbot,
task_loop::Task,
utils, WhoAmI,
};
#[derive(FromForm)]
pub struct PeerForm {
pub public_key: String,
}
#[get("/")]
pub async fn home(db: &State<Database>) -> Template {
let peers = db.get_peers();
let mut context = Context::new();
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()));
}
context.insert("peers", &peers_unread);
Template::render("base", &context.into_json())
}
#[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.
db.remove_post(public_key, msg_id).unwrap();
Redirect::to(uri!(posts(public_key)))
}
#[get("/posts/<public_key>")]
pub async fn posts(db: &State<Database>, public_key: &str) -> Template {
let peers = db.get_peers();
let mut context = Context::new();
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()));
}
context.insert("peers", &peers_unread);
let posts = db.get_posts(public_key).unwrap();
context.insert("selected_peer", &public_key);
context.insert("posts", &posts);
Template::render("base", &context.into_json())
}
#[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();
let mut context = Context::new();
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()));
}
context.insert("peers", &peers_unread);
let posts = db.get_posts(public_key).unwrap();
let post = db.get_post(public_key, msg_id).unwrap();
context.insert("selected_peer", &public_key);
context.insert(
"selected_peer_encoded",
&uri_encode::encode_uri_component(public_key),
);
context.insert("selected_post", &msg_id);
context.insert(
"selected_post_encoded",
&uri_encode::encode_uri_component(msg_id),
);
context.insert("posts", &posts);
context.insert("post", &post);
context.insert("post_is_selected", &true);
Template::render("base", &context.into_json())
}
#[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!(
"failed to find post {} authored by {} in database",
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!(
"failed to find post {} authored by {} in database",
msg_id, public_key
)
}
Redirect::to(uri!(post(public_key, msg_id)))
}
#[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
{
warn!("task loop error: {}", e)
}
// 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 {
warn!("task loop error: {}", e)
}
}
Redirect::to(uri!(home))
}
#[post("/subscribe", data = "<peer>")]
pub async fn subscribe_form(
db: &State<Database>,
whoami: &State<WhoAmI>,
tx: &State<Sender<Task>>,
peer: Form<PeerForm>,
) -> Redirect {
if utils::validate_public_key(&peer.public_key).is_ok() {
debug!("public key {} is valid", &peer.public_key);
// 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) => {
warn!("failed to fetch name for {}: {}", &peer.public_key, e);
String::from("")
}
};
let peer_info = Peer::new(&peer.public_key).set_name(&peer_name);
// Add the peer to the database and then check the follow state.
// Follow the peer if our local instance is not already following.
if db.add_peer(peer_info).is_ok() {
debug!("added {} to peer tree in database", &peer.public_key);
match sbot::is_following(&whoami.0, &peer.public_key).await {
Ok(status) if status.as_str() == "false" => {
match sbot::follow_peer(&peer.public_key).await {
Ok(_) => debug!("followed {}", &peer.public_key),
Err(e) => warn!("failed to follow {}: {}", &peer.public_key, e),
}
}
Ok(status) if status.as_str() == "true" => {
debug!("we already follow {}", &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 {
warn!(
"failed to add {} to peer tree in database",
&peer.public_key
)
}
} else {
warn!("{} is invalid", &peer.public_key);
}
Redirect::to(uri!(home))
}
#[post("/unsubscribe", data = "<peer>")]
pub fn unsubscribe_form(db: &State<Database>, peer: Form<PeerForm>) -> Redirect {
if let Err(e) = utils::validate_public_key(&peer.public_key) {
warn!("{} is invalid: {}", &peer.public_key, e)
} else {
debug!("public key {} is valid", &peer.public_key);
match db.remove_peer(&peer.public_key) {
Ok(_) => debug!("removed {} from peer tree in database", &peer.public_key),
Err(_e) => warn!(
"failed to remove {} from peer tree in database",
&peer.public_key
),
}
}
Redirect::to(uri!(home))
}