165 lines
4.9 KiB
Rust
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;
|
|
})
|
|
}))
|
|
}
|