remove old scuttlebutt routes file
This commit is contained in:
parent
3c49c067dd
commit
31628a7155
|
@ -1,946 +0,0 @@
|
|||
//! Routes for Scuttlebutt related functionality.
|
||||
|
||||
use log::debug;
|
||||
use peach_lib::sbot::{SbotConfig, SbotStatus};
|
||||
use rocket::{
|
||||
form::{Form, FromForm},
|
||||
fs::TempFile,
|
||||
get, post,
|
||||
request::FlashMessage,
|
||||
response::{Flash, Redirect},
|
||||
uri,
|
||||
};
|
||||
use rocket_dyn_templates::{tera::Context, Template};
|
||||
|
||||
use crate::{
|
||||
context::{
|
||||
scuttlebutt,
|
||||
scuttlebutt::{
|
||||
BlocksContext, FollowsContext, FriendsContext, PrivateContext, ProfileContext,
|
||||
},
|
||||
},
|
||||
routes::authentication::Authenticated,
|
||||
utils,
|
||||
};
|
||||
|
||||
// HELPER FUNCTIONS
|
||||
|
||||
/// Check to see if the go-sbot.service process is currently active.
|
||||
/// Return an error in the form of a `String` if the process
|
||||
/// check command fails. Otherwise, return the state of the process.
|
||||
fn is_sbot_active() -> Result<bool, String> {
|
||||
// retrieve go-sbot systemd process status
|
||||
let sbot_status = match SbotStatus::read() {
|
||||
Ok(status) => status,
|
||||
Err(e) => return Err(format!("Failed to read sbot status: {}", e)),
|
||||
};
|
||||
|
||||
if sbot_status.state == Some("active".to_string()) {
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensure that the given public key is a valid ed25519 key.
|
||||
fn validate_public_key(public_key: &str, redirect_url: String) -> Result<(), Flash<Redirect>> {
|
||||
// ensure the id starts with the correct sigil link
|
||||
if !public_key.starts_with('@') {
|
||||
return Err(Flash::error(
|
||||
Redirect::to(redirect_url),
|
||||
"Invalid key: expected '@' sigil as first character",
|
||||
));
|
||||
}
|
||||
|
||||
// find the dot index denoting the start of the algorithm definition tag
|
||||
let dot_index = match public_key.rfind('.') {
|
||||
Some(index) => index,
|
||||
None => {
|
||||
return Err(Flash::error(
|
||||
Redirect::to(redirect_url),
|
||||
"Invalid key: no dot index was found",
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
// check hashing algorithm (must end with ".ed25519")
|
||||
if !&public_key.ends_with(".ed25519") {
|
||||
return Err(Flash::error(
|
||||
Redirect::to(redirect_url),
|
||||
"Invalid key: hashing algorithm must be ed25519",
|
||||
));
|
||||
}
|
||||
|
||||
// obtain the base64 portion (substring) of the public key
|
||||
let base64_str = &public_key[1..dot_index];
|
||||
|
||||
// length of a base64 encoded ed25519 public key
|
||||
if base64_str.len() != 44 {
|
||||
return Err(Flash::error(
|
||||
Redirect::to(redirect_url),
|
||||
"Invalid key: base64 data length is incorrect",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR INVITES
|
||||
|
||||
#[get("/invites")]
|
||||
pub fn invites(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
||||
// retrieve current ui theme
|
||||
let theme = utils::get_theme();
|
||||
|
||||
let mut context = Context::new();
|
||||
context.insert("theme", &theme);
|
||||
context.insert("back", &Some("/scuttlebutt/peers".to_string()));
|
||||
context.insert("title", &Some("Invites".to_string()));
|
||||
|
||||
// check to see if there is a flash message to display
|
||||
if let Some(flash) = flash {
|
||||
match flash.kind() {
|
||||
// we've been passed a freshly-generated invite code (redirect from post)
|
||||
"code" => {
|
||||
context.insert("invite_code", &Some(flash.message().to_string()));
|
||||
context.insert("flash_name", &Some("success".to_string()));
|
||||
context.insert("flash_msg", &Some("Generated invite code".to_string()));
|
||||
}
|
||||
_ => {
|
||||
// add flash message contents to the context object
|
||||
context.insert("flash_name", &Some(flash.kind().to_string()));
|
||||
context.insert("flash_msg", &Some(flash.message().to_string()));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Template::render("scuttlebutt/invites", &context.into_json())
|
||||
}
|
||||
|
||||
#[derive(Debug, FromForm)]
|
||||
pub struct Invite {
|
||||
pub uses: u16,
|
||||
}
|
||||
|
||||
#[post("/invites", data = "<invite>")]
|
||||
pub async fn create_invite(invite: Form<Invite>, _auth: Authenticated) -> Flash<Redirect> {
|
||||
let uses = invite.uses;
|
||||
|
||||
let url = uri!("/scuttlebutt", invites);
|
||||
|
||||
// we only want to try and interact with the sbot if it's active
|
||||
match is_sbot_active() {
|
||||
Ok(true) => {
|
||||
// retrieve latest go-sbot configuration parameters
|
||||
let sbot_config = SbotConfig::read().ok();
|
||||
|
||||
// initialise sbot connection with ip:port and shscap from config file
|
||||
match scuttlebutt::init_sbot_with_config(&sbot_config).await {
|
||||
Ok(mut sbot_client) => {
|
||||
debug!("Generating Scuttlebutt invite code");
|
||||
match sbot_client.invite_create(uses).await {
|
||||
// construct a custom flash msg to pass along the invite code
|
||||
Ok(code) => Flash::new(Redirect::to(url), "code", code),
|
||||
Err(e) => Flash::error(
|
||||
Redirect::to(url),
|
||||
format!("Failed to create invite code: {}", e),
|
||||
),
|
||||
}
|
||||
}
|
||||
Err(e) => Flash::error(
|
||||
Redirect::to(url),
|
||||
format!("Failed to initialise sbot: {}", e),
|
||||
),
|
||||
}
|
||||
}
|
||||
Ok(false) => {
|
||||
return Flash::warning(
|
||||
Redirect::to(url),
|
||||
"The Sbot is inactive. New invite codes cannot be generated. Visit the Scuttlebutt settings menu to start the Sbot and then try again",
|
||||
)
|
||||
}
|
||||
// failed to retrieve go-sbot systemd process status
|
||||
Err(e) => return Flash::error(Redirect::to(url), e)
|
||||
}
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR /private
|
||||
|
||||
/// A private message composition and publication page.
|
||||
#[get("/private?<public_key>")]
|
||||
pub async fn private(
|
||||
mut public_key: Option<String>,
|
||||
flash: Option<FlashMessage<'_>>,
|
||||
_auth: Authenticated,
|
||||
) -> Template {
|
||||
// display a helpful message if the sbot is inactive
|
||||
if let Ok(false) = is_sbot_active() {
|
||||
// retrieve current ui theme
|
||||
let theme = utils::get_theme();
|
||||
|
||||
let mut context = Context::new();
|
||||
context.insert("theme", &theme);
|
||||
context.insert("back", &Some("/".to_string()));
|
||||
context.insert("title", &Some("Private Messages".to_string()));
|
||||
context.insert(
|
||||
"unavailable_msg",
|
||||
&Some("Private messages cannot be published.".to_string()),
|
||||
);
|
||||
|
||||
// render the "sbot is inactive" template
|
||||
return Template::render("scuttlebutt/inactive", &context.into_json());
|
||||
// otherwise, build the full context and render the private message template
|
||||
} else {
|
||||
if let Some(ref key) = public_key {
|
||||
// `url_decode` replaces '+' with ' ', so we need to revert that
|
||||
public_key = Some(key.replace(' ', "+"));
|
||||
}
|
||||
|
||||
// build the private context object
|
||||
let context = PrivateContext::build(public_key).await;
|
||||
|
||||
match context {
|
||||
// we were able to build the context without errors
|
||||
Ok(mut context) => {
|
||||
// check to see if there is a flash message to display
|
||||
if let Some(flash) = flash {
|
||||
// add flash message contents to the context object
|
||||
context.flash_name = Some(flash.kind().to_string());
|
||||
context.flash_msg = Some(flash.message().to_string());
|
||||
};
|
||||
|
||||
Template::render("scuttlebutt/private", &context)
|
||||
}
|
||||
// an error occurred while building the context
|
||||
Err(e) => {
|
||||
// build the default context and pass along the error message
|
||||
let mut context = PrivateContext::default();
|
||||
context.flash_name = Some("error".to_string());
|
||||
context.flash_msg = Some(e.to_string());
|
||||
|
||||
Template::render("scuttlebutt/private", &context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, FromForm)]
|
||||
pub struct Private {
|
||||
pub id: String,
|
||||
pub text: String,
|
||||
pub recipient: String,
|
||||
}
|
||||
|
||||
/// Publish a private message.
|
||||
#[post("/private", data = "<private>")]
|
||||
pub async fn private_post(private: Form<Private>, _auth: Authenticated) -> Flash<Redirect> {
|
||||
let url = uri!("/scuttlebutt", private(None::<String>));
|
||||
|
||||
// we only want to try and interact with the sbot if it's active
|
||||
match is_sbot_active() {
|
||||
Ok(true) => {
|
||||
// retrieve latest go-sbot configuration parameters
|
||||
let sbot_config = SbotConfig::read().ok();
|
||||
|
||||
let id = &private.id;
|
||||
let text = &private.text;
|
||||
let recipient = &private.recipient;
|
||||
// now we need to add the local id to the recipients vector,
|
||||
// otherwise the local id will not be able to read the message.
|
||||
let recipients = vec![id.to_string(), recipient.to_string()];
|
||||
|
||||
// initialise sbot connection with ip:port and shscap from config file
|
||||
match scuttlebutt::init_sbot_with_config(&sbot_config).await {
|
||||
Ok(mut sbot_client) => {
|
||||
debug!("Publishing a new Scuttlebutt private message");
|
||||
match sbot_client
|
||||
.publish_private(text.to_string(), recipients)
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
Flash::success(Redirect::to(url), format!("Published private message"))
|
||||
}
|
||||
Err(e) => Flash::error(
|
||||
Redirect::to(url),
|
||||
format!("Failed to publish private message: {}", e),
|
||||
),
|
||||
}
|
||||
}
|
||||
Err(e) => Flash::error(
|
||||
Redirect::to(url),
|
||||
format!("Failed to initialise sbot: {}", e),
|
||||
),
|
||||
}
|
||||
}
|
||||
Ok(false) => {
|
||||
return Flash::warning(
|
||||
Redirect::to(url),
|
||||
"The Sbot is inactive. New private message cannot be published. Visit the Scuttlebutt settings menu to start the Sbot and then try again",
|
||||
);
|
||||
}
|
||||
// failed to retrieve go-sbot systemd process status
|
||||
Err(e) => return Flash::error(Redirect::to(url), e),
|
||||
}
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR /search
|
||||
|
||||
/// Search for a peer.
|
||||
#[get("/search")]
|
||||
pub fn search(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
||||
// retrieve current ui theme
|
||||
let theme = utils::get_theme();
|
||||
|
||||
// retrieve go-sbot systemd process status
|
||||
let sbot_status = SbotStatus::read().ok();
|
||||
|
||||
let mut context = Context::new();
|
||||
context.insert("theme", &theme);
|
||||
context.insert("sbot_status", &sbot_status);
|
||||
context.insert("title", &Some("Search"));
|
||||
context.insert("back", &Some("/scuttlebutt/peers"));
|
||||
|
||||
// check to see if there is a flash message to display
|
||||
if let Some(flash) = flash {
|
||||
// add flash message contents to the context object
|
||||
context.insert("flash_name", &Some(flash.kind().to_string()));
|
||||
context.insert("flash_msg", &Some(flash.message().to_string()));
|
||||
};
|
||||
|
||||
Template::render("scuttlebutt/search", &context.into_json())
|
||||
}
|
||||
|
||||
#[derive(Debug, FromForm)]
|
||||
pub struct Peer {
|
||||
// public key
|
||||
pub public_key: String,
|
||||
}
|
||||
|
||||
/// Accept the peer search form and redirect to the profile for that peer.
|
||||
#[post("/search", data = "<peer>")]
|
||||
pub async fn search_post(peer: Form<Peer>, _auth: Authenticated) -> Flash<Redirect> {
|
||||
let public_key = &peer.public_key;
|
||||
|
||||
let search_url = "/scuttlebutt/search".to_string();
|
||||
|
||||
// validate the key before redirecting to profile url
|
||||
if let Err(flash) = validate_public_key(&public_key, search_url) {
|
||||
// redirect with error message
|
||||
return flash;
|
||||
}
|
||||
|
||||
// key has not been validated and we can redirect to the profile page
|
||||
let profile_url = uri!("/scuttlebutt", profile(Some(public_key)));
|
||||
|
||||
Flash::new(
|
||||
Redirect::to(profile_url),
|
||||
// this flash msg will not be displayed in the receiving template
|
||||
"ignore",
|
||||
"Public key validated for profile lookup",
|
||||
)
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR /peers
|
||||
|
||||
/// A peer menu which allows navigating to lists of friends, follows, followers
|
||||
/// and blocks, as well as accessing the invite creation form.
|
||||
#[get("/peers")]
|
||||
pub fn peers(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
||||
// retrieve current ui theme
|
||||
let theme = utils::get_theme();
|
||||
|
||||
let mut context = Context::new();
|
||||
context.insert("theme", &theme);
|
||||
context.insert("title", &Some("Scuttlebutt Peers"));
|
||||
context.insert("back", &Some("/"));
|
||||
|
||||
// display a helpful message if the sbot is inactive
|
||||
if let Ok(false) = is_sbot_active() {
|
||||
context.insert(
|
||||
"unavailable_msg",
|
||||
&Some("Social lists and interactions are unavailable.".to_string()),
|
||||
);
|
||||
|
||||
// render the "sbot is inactive" template
|
||||
return Template::render("scuttlebutt/inactive", &context.into_json());
|
||||
} else {
|
||||
context.insert("sbot_state", &Some("active".to_string()));
|
||||
// check to see if there is a flash message to display
|
||||
if let Some(flash) = flash {
|
||||
// add flash message contents to the context object
|
||||
context.insert("flash_name", &Some(flash.kind().to_string()));
|
||||
context.insert("flash_msg", &Some(flash.message().to_string()));
|
||||
};
|
||||
}
|
||||
|
||||
Template::render("scuttlebutt/peers", &context.into_json())
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR /post/publish
|
||||
|
||||
#[derive(Debug, FromForm)]
|
||||
pub struct Post {
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
/// Publish a public Scuttlebutt post.
|
||||
/// Redirects to profile page of the PeachCloud local identity with a flash
|
||||
/// message describing the outcome of the action (may be successful or
|
||||
/// unsuccessful).
|
||||
#[post("/publish", data = "<post>")]
|
||||
pub async fn publish(post: Form<Post>, _auth: Authenticated) -> Flash<Redirect> {
|
||||
let post_text = &post.text;
|
||||
let url = uri!("/scuttlebutt", profile(None::<String>));
|
||||
|
||||
// we only want to try and interact with the sbot if it's active
|
||||
match is_sbot_active() {
|
||||
Ok(true) => {
|
||||
// retrieve latest go-sbot configuration parameters
|
||||
let sbot_config = SbotConfig::read().ok();
|
||||
|
||||
// initialise sbot connection with ip:port and shscap from config file
|
||||
match scuttlebutt::init_sbot_with_config(&sbot_config).await {
|
||||
Ok(mut sbot_client) => {
|
||||
debug!("Publishing new Scuttlebutt public post");
|
||||
match sbot_client.publish_post(post_text).await {
|
||||
Ok(_) => Flash::success(Redirect::to(url), format!("Published post")),
|
||||
Err(e) => Flash::error(
|
||||
Redirect::to(url),
|
||||
format!("Failed to publish post: {}", e),
|
||||
),
|
||||
}
|
||||
}
|
||||
Err(e) => Flash::error(
|
||||
Redirect::to(url),
|
||||
format!("Failed to initialise sbot: {}", e),
|
||||
),
|
||||
}
|
||||
}
|
||||
Ok(false) => {
|
||||
return Flash::warning(
|
||||
Redirect::to(url),
|
||||
"The Sbot is inactive. New posts cannot be published. Visit the Scuttlebutt settings menu to start the Sbot and then try again",
|
||||
);
|
||||
}
|
||||
Err(e) => return Flash::error(Redirect::to(url), e),
|
||||
}
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR /follow
|
||||
|
||||
/// Follow a Scuttlebutt profile specified by the given public key.
|
||||
/// Redirects to the appropriate profile page with a flash message describing
|
||||
/// the outcome of the action (may be successful or unsuccessful).
|
||||
#[post("/follow", data = "<peer>")]
|
||||
pub async fn follow(peer: Form<Peer>, _auth: Authenticated) -> Flash<Redirect> {
|
||||
let public_key = &peer.public_key;
|
||||
let url = uri!("/scuttlebutt", profile(Some(public_key)));
|
||||
|
||||
// we only want to try and interact with the sbot if it's active
|
||||
match is_sbot_active() {
|
||||
Ok(true) => {
|
||||
// retrieve latest go-sbot configuration parameters
|
||||
let sbot_config = SbotConfig::read().ok();
|
||||
|
||||
// initialise sbot connection with ip:port and shscap from config file
|
||||
match scuttlebutt::init_sbot_with_config(&sbot_config).await {
|
||||
Ok(mut sbot_client) => {
|
||||
debug!("Following Scuttlebutt peer");
|
||||
match sbot_client.follow(public_key).await {
|
||||
Ok(_) => Flash::success(Redirect::to(url), format!("Followed peer")),
|
||||
Err(e) => {
|
||||
Flash::error(Redirect::to(url), format!("Failed to follow peer: {}", e))
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => Flash::error(
|
||||
Redirect::to(url),
|
||||
format!("Failed to initialise sbot: {}", e),
|
||||
),
|
||||
}
|
||||
}
|
||||
Ok(false) => {
|
||||
return Flash::warning(
|
||||
Redirect::to(url),
|
||||
"The Sbot is inactive. Follow messages cannot be published. Visit the Scuttlebutt settings menu to start the Sbot and then try again",
|
||||
);
|
||||
}
|
||||
Err(e) => return Flash::error(Redirect::to(url), e),
|
||||
}
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR /unfollow
|
||||
|
||||
/// Unfollow a Scuttlebutt profile specified by the given public key.
|
||||
/// Redirects to the appropriate profile page with a flash message describing
|
||||
/// the outcome of the action (may be successful or unsuccessful).
|
||||
#[post("/unfollow", data = "<peer>")]
|
||||
pub async fn unfollow(peer: Form<Peer>, _auth: Authenticated) -> Flash<Redirect> {
|
||||
let public_key = &peer.public_key;
|
||||
|
||||
let url = uri!("/scuttlebutt", profile(Some(public_key)));
|
||||
|
||||
// we only want to try and interact with the sbot if it's active
|
||||
match is_sbot_active() {
|
||||
Ok(true) => {
|
||||
// retrieve latest go-sbot configuration parameters
|
||||
let sbot_config = SbotConfig::read().ok();
|
||||
|
||||
// initialise sbot connection with ip:port and shscap from config file
|
||||
match scuttlebutt::init_sbot_with_config(&sbot_config).await {
|
||||
Ok(mut sbot_client) => {
|
||||
debug!("Unfollowing Scuttlebutt peer");
|
||||
match sbot_client.unfollow(public_key).await {
|
||||
Ok(_) => Flash::success(Redirect::to(url), format!("Unfollowed peer")),
|
||||
Err(e) => Flash::error(
|
||||
Redirect::to(url),
|
||||
format!("Failed to unfollow peer: {}", e),
|
||||
),
|
||||
}
|
||||
}
|
||||
Err(e) => Flash::error(
|
||||
Redirect::to(url),
|
||||
format!("Failed to initialise sbot: {}", e),
|
||||
),
|
||||
}
|
||||
}
|
||||
Ok(false) => {
|
||||
return Flash::warning(
|
||||
Redirect::to(url),
|
||||
"The Sbot is inactive. Follow messages cannot be published. Visit the Scuttlebutt settings menu to start the Sbot and then try again",
|
||||
);
|
||||
}
|
||||
Err(e) => return Flash::error(Redirect::to(url), e),
|
||||
}
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR /block
|
||||
|
||||
/// Block a Scuttlebutt profile specified by the given public key.
|
||||
/// Redirects to the appropriate profile page with a flash message describing
|
||||
/// the outcome of the action (may be successful or unsuccessful).
|
||||
#[post("/block", data = "<peer>")]
|
||||
pub async fn block(peer: Form<Peer>, _auth: Authenticated) -> Flash<Redirect> {
|
||||
let public_key = &peer.public_key;
|
||||
let url = uri!("/scuttlebutt", profile(Some(public_key)));
|
||||
|
||||
// we only want to try and interact with the sbot if it's active
|
||||
match is_sbot_active() {
|
||||
Ok(true) => {
|
||||
// retrieve latest go-sbot configuration parameters
|
||||
let sbot_config = SbotConfig::read().ok();
|
||||
|
||||
// initialise sbot connection with ip:port and shscap from config file
|
||||
match scuttlebutt::init_sbot_with_config(&sbot_config).await {
|
||||
Ok(mut sbot_client) => {
|
||||
debug!("Blocking Scuttlebutt peer");
|
||||
match sbot_client.block(public_key).await {
|
||||
Ok(_) => Flash::success(Redirect::to(url), format!("Blocked peer")),
|
||||
Err(e) => {
|
||||
Flash::error(Redirect::to(url), format!("Failed to block peer: {}", e))
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => Flash::error(
|
||||
Redirect::to(url),
|
||||
format!("Failed to initialise sbot: {}", e),
|
||||
),
|
||||
}
|
||||
}
|
||||
Ok(false) => {
|
||||
return Flash::warning(
|
||||
Redirect::to(url),
|
||||
"The Sbot is inactive. Follow messages cannot be published. Visit the Scuttlebutt settings menu to start the Sbot and then try again",
|
||||
);
|
||||
}
|
||||
Err(e) => return Flash::error(Redirect::to(url), e),
|
||||
}
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR /unblock
|
||||
|
||||
/// Unblock a Scuttlebutt profile specified by the given public key.
|
||||
/// Redirects to the appropriate profile page with a flash message describing
|
||||
/// the outcome of the action (may be successful or unsuccessful).
|
||||
#[post("/unblock", data = "<peer>")]
|
||||
pub async fn unblock(peer: Form<Peer>, _auth: Authenticated) -> Flash<Redirect> {
|
||||
let public_key = &peer.public_key;
|
||||
let url = uri!("/scuttlebutt", profile(Some(public_key)));
|
||||
|
||||
// we only want to try and interact with the sbot if it's active
|
||||
match is_sbot_active() {
|
||||
Ok(true) => {
|
||||
// retrieve latest go-sbot configuration parameters
|
||||
let sbot_config = SbotConfig::read().ok();
|
||||
|
||||
// initialise sbot connection with ip:port and shscap from config file
|
||||
match scuttlebutt::init_sbot_with_config(&sbot_config).await {
|
||||
Ok(mut sbot_client) => {
|
||||
debug!("Unblocking Scuttlebutt peer");
|
||||
match sbot_client.unblock(public_key).await {
|
||||
Ok(_) => Flash::success(Redirect::to(url), format!("Unblocked peer")),
|
||||
Err(e) => Flash::error(
|
||||
Redirect::to(url),
|
||||
format!("Failed to unblock peer: {}", e),
|
||||
),
|
||||
}
|
||||
}
|
||||
Err(e) => Flash::error(
|
||||
Redirect::to(url),
|
||||
format!("Failed to initialise sbot: {}", e),
|
||||
),
|
||||
}
|
||||
}
|
||||
Ok(false) => {
|
||||
return Flash::warning(
|
||||
Redirect::to(url),
|
||||
"The Sbot is inactive. Follow messages cannot be published. Visit the Scuttlebutt settings menu to start the Sbot and then try again",
|
||||
);
|
||||
}
|
||||
Err(e) => return Flash::error(Redirect::to(url), e),
|
||||
}
|
||||
}
|
||||
|
||||
// ROUTES FOR /profile
|
||||
|
||||
/// A Scuttlebutt profile, specified by a public key. It may be our own profile
|
||||
/// or the profile of a peer. If the public key query parameter is not provided,
|
||||
/// the local profile is displayed (ie. the profile of the public key associated
|
||||
/// with the local PeachCloud device).
|
||||
#[get("/profile?<public_key>")]
|
||||
pub async fn profile(
|
||||
mut public_key: Option<String>,
|
||||
flash: Option<FlashMessage<'_>>,
|
||||
_auth: Authenticated,
|
||||
) -> Template {
|
||||
// display a helpful message if the sbot is inactive
|
||||
if let Ok(false) = is_sbot_active() {
|
||||
// retrieve current ui theme
|
||||
let theme = utils::get_theme();
|
||||
|
||||
let mut context = Context::new();
|
||||
context.insert("theme", &theme);
|
||||
context.insert("back", &Some("/".to_string()));
|
||||
context.insert("title", &Some("Profile".to_string()));
|
||||
context.insert(
|
||||
"unavailable_msg",
|
||||
&Some("Profile data cannot be retrieved.".to_string()),
|
||||
);
|
||||
|
||||
// render the "sbot is inactive" template
|
||||
return Template::render("scuttlebutt/inactive", &context.into_json());
|
||||
} else {
|
||||
if let Some(ref key) = public_key {
|
||||
// `url_decode` replaces '+' with ' ', so we need to revert that
|
||||
public_key = Some(key.replace(' ', "+"));
|
||||
}
|
||||
|
||||
// build the profile context object
|
||||
let context = ProfileContext::build(public_key).await;
|
||||
|
||||
match context {
|
||||
// we were able to build the context without errors
|
||||
Ok(mut context) => {
|
||||
// check to see if there is a flash message to display
|
||||
if let Some(flash) = flash {
|
||||
// add flash message contents to the context object
|
||||
context.flash_name = Some(flash.kind().to_string());
|
||||
context.flash_msg = Some(flash.message().to_string());
|
||||
};
|
||||
|
||||
Template::render("scuttlebutt/profile", &context)
|
||||
}
|
||||
// an error occurred while building the context
|
||||
Err(e) => {
|
||||
// build the default context and pass along the error message
|
||||
let mut context = ProfileContext::default();
|
||||
|
||||
// flash name and msg will be `Some` if the sbot is inactive (in
|
||||
// that case, they are set by the context builder).
|
||||
// otherwise, we need to assign the name and returned error msg
|
||||
// to the flash.
|
||||
if context.flash_name.is_none() || context.flash_msg.is_none() {
|
||||
context.flash_name = Some("error".to_string());
|
||||
context.flash_msg = Some(e.to_string());
|
||||
}
|
||||
|
||||
Template::render("scuttlebutt/profile", &context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR /profile/update
|
||||
|
||||
/// Serve a form for the purpose of updating the name, description and picture
|
||||
/// for the local Scuttlebutt profile.
|
||||
#[get("/profile/update")]
|
||||
pub async fn update_profile(flash: Option<FlashMessage<'_>>, _auth: Authenticated) -> Template {
|
||||
// display a helpful message if the sbot is inactive
|
||||
if let Ok(false) = is_sbot_active() {
|
||||
// retrieve current ui theme
|
||||
let theme = utils::get_theme();
|
||||
|
||||
let mut context = Context::new();
|
||||
context.insert("theme", &theme);
|
||||
context.insert("back", &Some("/".to_string()));
|
||||
context.insert("title", &Some("Profile".to_string()));
|
||||
context.insert(
|
||||
"unavailable_msg",
|
||||
&Some("Profile data cannot be retrieved.".to_string()),
|
||||
);
|
||||
|
||||
// render the "sbot is inactive" template
|
||||
return Template::render("scuttlebutt/inactive", &context.into_json());
|
||||
} else {
|
||||
// build the profile context object
|
||||
let context = ProfileContext::build(None).await;
|
||||
|
||||
match context {
|
||||
// we were able to build the context without errors
|
||||
Ok(mut context) => {
|
||||
context.back = Some("/scuttlebutt/profile".to_string());
|
||||
|
||||
// check to see if there is a flash message to display
|
||||
if let Some(flash) = flash {
|
||||
// add flash message contents to the context object
|
||||
context.flash_name = Some(flash.kind().to_string());
|
||||
context.flash_msg = Some(flash.message().to_string());
|
||||
};
|
||||
|
||||
Template::render("scuttlebutt/update_profile", &context)
|
||||
}
|
||||
// an error occurred while building the context
|
||||
Err(e) => {
|
||||
// build the default context and pass along the error message
|
||||
let mut context = ProfileContext::default();
|
||||
context.flash_name = Some("error".to_string());
|
||||
context.flash_msg = Some(e.to_string());
|
||||
|
||||
Template::render("scuttlebutt/update_profile", &context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, FromForm)]
|
||||
pub struct Profile<'f> {
|
||||
pub id: String,
|
||||
pub current_name: String,
|
||||
pub current_description: String,
|
||||
pub new_name: String,
|
||||
pub new_description: String,
|
||||
pub image: TempFile<'f>,
|
||||
}
|
||||
|
||||
/// Update the name, description and picture for the local Scuttlebutt profile.
|
||||
/// Redirects to profile page of the PeachCloud local identity with a flash
|
||||
/// message describing the outcome of the action (may be successful or
|
||||
/// unsuccessful).
|
||||
#[post("/profile/update", data = "<profile>")]
|
||||
pub async fn update_profile_post(
|
||||
mut profile: Form<Profile<'_>>,
|
||||
_auth: Authenticated,
|
||||
) -> Flash<Redirect> {
|
||||
let url = uri!("/scuttlebutt", update_profile);
|
||||
|
||||
// we only want to try and interact with the sbot if it's active
|
||||
match is_sbot_active() {
|
||||
Ok(true) => {
|
||||
// retrieve latest go-sbot configuration parameters
|
||||
let sbot_config = SbotConfig::read().ok();
|
||||
|
||||
// initialise sbot connection with ip:port and shscap from config file
|
||||
match scuttlebutt::init_sbot_with_config(&sbot_config).await {
|
||||
Ok(mut sbot_client) => {
|
||||
// track whether the name, description or image have been updated
|
||||
let mut name_updated: bool = false;
|
||||
let mut description_updated: bool = false;
|
||||
let image_updated: bool;
|
||||
|
||||
// only update the name if it has changed
|
||||
if profile.new_name != profile.current_name {
|
||||
debug!("Publishing new Scuttlebutt profile name");
|
||||
let publish_name_res = sbot_client.publish_name(&profile.new_name).await;
|
||||
if publish_name_res.is_err() {
|
||||
return Flash::error(
|
||||
Redirect::to(url),
|
||||
format!("Failed to update name: {}", publish_name_res.unwrap_err()),
|
||||
);
|
||||
} else {
|
||||
name_updated = true
|
||||
}
|
||||
}
|
||||
|
||||
// only update the description if it has changed
|
||||
if profile.new_description != profile.current_description {
|
||||
debug!("Publishing new Scuttlebutt profile description");
|
||||
let publish_description_res = sbot_client
|
||||
.publish_description(&profile.new_description)
|
||||
.await;
|
||||
|
||||
if publish_description_res.is_err() {
|
||||
return Flash::error(
|
||||
Redirect::to(url),
|
||||
format!(
|
||||
"Failed to update description: {}",
|
||||
publish_description_res.unwrap_err()
|
||||
),
|
||||
);
|
||||
} else {
|
||||
description_updated = true
|
||||
}
|
||||
}
|
||||
|
||||
// only update the image if a file was uploaded
|
||||
if profile.image.name().is_some() {
|
||||
match utils::write_blob_to_store(&mut profile.image).await {
|
||||
Ok(blob_id) => {
|
||||
// if the file was successfully added to the blobstore,
|
||||
// publish an about image message with the blob id
|
||||
let publish_image_res = sbot_client.publish_image(&blob_id).await;
|
||||
|
||||
if publish_image_res.is_err() {
|
||||
return Flash::error(
|
||||
Redirect::to(url),
|
||||
format!(
|
||||
"Failed to update image: {}",
|
||||
publish_image_res.unwrap_err()
|
||||
),
|
||||
);
|
||||
} else {
|
||||
image_updated = true
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
return Flash::error(
|
||||
Redirect::to(url),
|
||||
format!("Failed to add image to blobstore: {}", e),
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
image_updated = false
|
||||
}
|
||||
|
||||
if name_updated || description_updated || image_updated {
|
||||
return Flash::success(Redirect::to(url), "Profile updated");
|
||||
} else {
|
||||
// no updates were made but no errors were encountered either
|
||||
return Flash::success(Redirect::to(url), "Profile info unchanged");
|
||||
}
|
||||
}
|
||||
Err(e) => Flash::error(
|
||||
Redirect::to(url),
|
||||
format!("Failed to initialise sbot: {}", e),
|
||||
),
|
||||
}
|
||||
}
|
||||
Ok(false) => {
|
||||
return Flash::warning(
|
||||
Redirect::to(url),
|
||||
"The Sbot is inactive. Profile data cannot be updated. Visit the Scuttlebutt settings menu to start the Sbot and then try again",
|
||||
);
|
||||
}
|
||||
Err(e) => return Flash::error(Redirect::to(url), e),
|
||||
}
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR /friends
|
||||
|
||||
/// A list of friends (mutual follows), with each list item displaying the
|
||||
/// name, image and public key of the peer.
|
||||
#[get("/friends")]
|
||||
pub async fn friends(flash: Option<FlashMessage<'_>>, _auth: Authenticated) -> Template {
|
||||
// build the friends context object
|
||||
let context = FriendsContext::build().await;
|
||||
|
||||
match context {
|
||||
// we were able to build the context without errors
|
||||
Ok(mut context) => {
|
||||
// check to see if there is a flash message to display
|
||||
if let Some(flash) = flash {
|
||||
// add flash message contents to the context object
|
||||
context.flash_name = Some(flash.kind().to_string());
|
||||
context.flash_msg = Some(flash.message().to_string());
|
||||
};
|
||||
|
||||
Template::render("scuttlebutt/peers_list", &context)
|
||||
}
|
||||
// an error occurred while building the context
|
||||
Err(e) => {
|
||||
// build the default context and pass along the error message
|
||||
let mut context = FriendsContext::default();
|
||||
context.flash_name = Some("error".to_string());
|
||||
context.flash_msg = Some(e.to_string());
|
||||
|
||||
Template::render("scuttlebutt/peers_list", &context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR /follows
|
||||
|
||||
/// A list of follows (peers we follow who do not follow us), with each list item displaying the name, image and public
|
||||
/// key of the peer.
|
||||
#[get("/follows")]
|
||||
pub async fn follows(flash: Option<FlashMessage<'_>>, _auth: Authenticated) -> Template {
|
||||
// build the follows context object
|
||||
let context = FollowsContext::build().await;
|
||||
|
||||
match context {
|
||||
// we were able to build the context without errors
|
||||
Ok(mut context) => {
|
||||
// check to see if there is a flash message to display
|
||||
if let Some(flash) = flash {
|
||||
// add flash message contents to the context object
|
||||
context.flash_name = Some(flash.kind().to_string());
|
||||
context.flash_msg = Some(flash.message().to_string());
|
||||
};
|
||||
|
||||
Template::render("scuttlebutt/peers_list", &context)
|
||||
}
|
||||
// an error occurred while building the context
|
||||
Err(e) => {
|
||||
// build the default context and pass along the error message
|
||||
let mut context = FollowsContext::default();
|
||||
context.flash_name = Some("error".to_string());
|
||||
context.flash_msg = Some(e.to_string());
|
||||
|
||||
Template::render("scuttlebutt/peers_list", &context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR /blocks
|
||||
|
||||
/// A list of blocks (peers we've blocked previously), with each list item
|
||||
/// displaying the name, image and public key of the peer.
|
||||
#[get("/blocks")]
|
||||
pub async fn blocks(flash: Option<FlashMessage<'_>>, _auth: Authenticated) -> Template {
|
||||
// build the blocks context object
|
||||
let context = BlocksContext::build().await;
|
||||
|
||||
match context {
|
||||
// we were able to build the context without errors
|
||||
Ok(mut context) => {
|
||||
// check to see if there is a flash message to display
|
||||
if let Some(flash) = flash {
|
||||
// add flash message contents to the context object
|
||||
context.flash_name = Some(flash.kind().to_string());
|
||||
context.flash_msg = Some(flash.message().to_string());
|
||||
};
|
||||
|
||||
Template::render("scuttlebutt/peers_list", &context)
|
||||
}
|
||||
// an error occurred while building the context
|
||||
Err(e) => {
|
||||
// build the default context and pass along the error message
|
||||
let mut context = BlocksContext::default();
|
||||
context.flash_name = Some("error".to_string());
|
||||
context.flash_msg = Some(e.to_string());
|
||||
|
||||
Template::render("scuttlebutt/peers_list", &context)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue