lykin/src/main.rs

165 lines
4.9 KiB
Rust

mod db;
mod sbot;
mod task_loop;
mod utils;
use std::{env, path::Path};
use async_std::{channel, channel::Sender};
use log::{debug, info, warn};
use rocket::{
fairing::AdHoc,
form::Form,
fs::{relative, FileServer},
get, launch, post,
response::Redirect,
routes, uri, FromForm, State,
};
use rocket_dyn_templates::{tera::Context, Template};
use crate::{db::Database, task_loop::Task};
#[derive(FromForm)]
struct Peer {
public_key: String,
}
#[get("/")]
async fn home(db: &State<Database>) -> Template {
let peers = db.get_peers();
let mut context = Context::new();
context.insert("peers", &peers);
Template::render("home", &context.into_json())
}
#[get("/posts/<public_key>")]
async fn posts(db: &State<Database>, public_key: &str) -> Template {
let peers = db.get_peers();
let posts = db.get_posts(public_key).unwrap();
let mut context = Context::new();
context.insert("selected_peer", &public_key);
context.insert("peers", &peers);
context.insert("posts", &posts);
Template::render("home", &context.into_json())
}
#[get("/posts/<public_key>/<msg_id>")]
async fn post(db: &State<Database>, public_key: &str, msg_id: &str) -> Template {
let peers = db.get_peers();
let posts = db.get_posts(public_key).unwrap();
let post = db.get_post(public_key, msg_id).unwrap();
let mut context = Context::new();
context.insert("selected_peer", &public_key);
context.insert("selected_post", &msg_id);
context.insert("peers", &peers);
context.insert("posts", &posts);
context.insert("post", &post);
Template::render("home", &context.into_json())
}
#[post("/subscribe", data = "<peer>")]
async fn subscribe_form(
db: &State<Database>,
whoami: &State<WhoAmI>,
tx: &State<Sender<Task>>,
peer: Form<Peer>,
) -> Redirect {
if let Ok(_) = utils::validate_public_key(&peer.public_key) {
debug!("public key {} is valid", &peer.public_key);
match db.add_peer(&peer.public_key) {
Ok(_) => {
debug!("added {} to peer tree in database", &peer.public_key);
// TODO: i don't think we actually want to follow...
// we might still have the data in our ssb db, even if we don't follow
match sbot::is_following(&whoami.public_key, &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 = peer.public_key.to_string();
if let Err(e) = tx.send(Task::FetchAll(peer)).await {
warn!("task loop error: {}", e)
}
}
Err(_e) => 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>")]
fn unsubscribe_form(db: &State<Database>, peer: Form<Peer>) -> Redirect {
// validate the public key
match utils::validate_public_key(&peer.public_key) {
Ok(_) => {
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
),
}
}
Err(e) => warn!("{} is invalid: {}", &peer.public_key, e),
}
Redirect::to(uri!(home))
}
struct WhoAmI {
public_key: String,
}
#[launch]
async fn rocket() -> _ {
env_logger::init();
let public_key: String = sbot::whoami().await.expect("whoami sbot call failed");
let whoami = WhoAmI { public_key };
let db = Database::init(Path::new("lykin_db"));
let db_clone = db.clone();
let (tx, rx) = channel::unbounded();
let tx_clone = tx.clone();
task_loop::spawn(rx, db_clone).await;
info!("launching the web server");
rocket::build()
.manage(db)
.manage(whoami)
.manage(tx)
.mount(
"/",
routes![home, subscribe_form, unsubscribe_form, posts, post],
)
.mount("/", FileServer::from(relative!("static")))
.attach(Template::fairing())
.attach(AdHoc::on_shutdown("cancel task loop", |_| {
Box::pin(async move {
tx_clone.send(Task::Cancel).await;
})
}))
}