fix typos, improve sentence flow and add links

This commit is contained in:
glyph 2022-08-17 15:48:34 +01:00
parent 4421b3d21d
commit c6a1a44339
1 changed files with 44 additions and 31 deletions

View File

@ -4,7 +4,7 @@
### Introduction
In the first part of the tutorial series we created a basic web server and wrote our first Scuttlebutt-related code. This tutorial installment will add an HTML form and route handler(s) to allow peer subscriptions through the web interface. We will learn how to validate public keys submitted via the form and how to check whether or not we already follow the peer represented by a submitted key. These additions will pave the way for following and unfollowing peers.
In the first part of the tutorial series we created a basic web server and wrote our first Scuttlebutt-related code. This tutorial installment will add an HTML form and route handler(s) to allow peer subscriptions through the web interface. We will learn how to validate public keys submitted via the form and how to check whether or not we follow the peer represented by a submitted key. These additions will pave the way for following and unfollowing peers.
There's a lot of ground to cover today. Let's dive into it.
@ -88,7 +88,7 @@ async fn rocket() -> _ {
### Add Peer Subscription Form and Routes
Now that we've taken care of some housekeeping, we can begin adding new functionality. We need a way to accept a public key; this will allow us to subscribe and unsubscribe to the posts of selected peer. We'll use the Tera templating engine to create HTML templates for our application. Tera is inspired by the Jinja2 template language and is supported by Rocket.
Now that we've taken care of some housekeeping, we can begin adding new functionality. We need a way to accept a public key; this will allow us to subscribe and unsubscribe to the posts of a particular peer. We'll use the [Tera templating engine](https://tera.netlify.app/) to create HTML templates for our application. Tera is inspired by the [Jinja2 template language](https://jinja.palletsprojects.com/en/3.0.x/) and is supported by [Rocket](https://rocket.rs/).
The Tera functionality we require is bundled in the `rocket_dyn_templates` crate. We can add that to our manifest:
@ -96,7 +96,7 @@ The Tera functionality we require is bundled in the `rocket_dyn_templates` crate
`rocket_dyn_templates = { version = "0.1.0-rc.1", features = ["tera"] }`
We also need to modify the Rocket launch code in `src/main.rs` to attach a template fairing:
We will modify the Rocket launch code in `src/main.rs` to attach a template fairing. Fairings are Rocket's approach to structured middleware:
```rust
use rocket_dyn_templates::Template;
@ -129,13 +129,13 @@ For now we'll write some HTML boilerplate code and a form to accept a public key
</head>
<body>
<h1>lykin</h1>
<p>{{ whoami }}</p>
<form action="/subscribe" method="post">
<label for="public_key">Public Key</label>
<input type="text" id="public_key" name="public_key" maxlength=53>
<input type="submit" value="Subscribe">
<input type="submit" value="Unsubscribe" formaction="/unsubscribe">
</form>
<p>{{ whoami }}</p>
<form action="/subscribe" method="post">
<label for="public_key">Public Key</label>
<input type="text" id="public_key" name="public_key" maxlength=53>
<input type="submit" value="Subscribe">
<input type="submit" value="Unsubscribe" formaction="/unsubscribe">
</form>
</body>
</html>
```
@ -178,16 +178,29 @@ pub struct PeerForm {
#[post("/subscribe", data = "<peer>")]
pub async fn subscribe_form(peer: Form<PeerForm>) -> Redirect {
info!("Subscribing to peer {}", &peer.public_key);
info!("Subscribing to peer {}", &peer.public_key);
Redirect::to(uri!(home))
Redirect::to(uri!(home))
}
#[post("/unsubscribe", data = "<peer>")]
pub async fn unsubscribe_form(peer: Form<PeerForm>) -> Redirect {
info!("Unsubscribing to peer {}", &peer.public_key);
info!("Unsubscribing to peer {}", &peer.public_key);
Redirect::to(uri!(home))
Redirect::to(uri!(home))
}
```
Finally, we need to register these two new routes in our Rocket launch code:
`src/main.rs`
```rust
#[launch]
async fn rocket() -> _ {
rocket::build()
.attach(Template::fairing())
.mount("/", routes![home, subscribe_form, unsubscribe_form])
}
```
@ -197,7 +210,7 @@ Run the project with the appropriate log level and ensure that everything is wor
### Validate a Public Key
We can now write some code to validate the input from our subscription form and ensure the data represents a valid Ed25519 key. We'll create a utilities module to house this function:
We can now write some code to validate the input from our subscription form and ensure the data represents a valid Ed25519 Scuttlebutt key. We'll create a utilities module to house this function:
`src/utils.rs`
@ -233,19 +246,19 @@ pub fn validate_public_key(public_key: &str) -> Result<(), String> {
Now the validation function can be called from our subscribe / unsubscribe route handlers, allowing us to ensure the provided public key is valid before using it to make further RPC calls to the sbot:
`src/router.rs`
`src/routes.rs`
```rust
use crate::utils;
#[post("/subscribe", data = "<peer>")]
pub async fn subscribe_form(peer: Form<PeerForm>) -> Redirect {
info!("Subscribing to peer {}", &peer.public_key);
info!("Subscribing to peer {}", &peer.public_key);
if let Err(e) = utils::validate_public_key(&peer.public_key) {
warn!("Public key {} is invalid: {}", &peer.public_key, e);
}
Redirect::to(uri!(home))
Redirect::to(uri!(home))
}
#[post("/unsubscribe", data = "<peer>")]
@ -255,44 +268,44 @@ pub async fn unsubscribe_form(peer: Form<PeerForm>) -> Redirect {
warn!("Public key {} is invalid: {}", &peer.public_key, e);
}
Redirect::to(uri!(home))
Redirect::to(uri!(home))
}
```
### Add Flash Messages
Our log messages are helpful to us during development and production runs but the user of our applications is missing out on valuable information. Currently, they will have no idea whether or not the public keys they submit for subscription are valid or not. Let's add flash message support so we have a channel for reporting back to the user via the UI.
Our log messages are helpful to us during development and production runs but the user of our applications is missing out on valuable information; they will have no idea whether or not the public keys they submit for subscription are valid. Let's add flash message support so we have a means of reporting back to the user via the UI.
Rocket makes this addition very simple, having built-in support for flash message cookies in both the response and request handlers. We will have to update our `src/routes.rs` file as follows:
Rocket makes this addition very simple, having [built-in support for flash message cookies](https://api.rocket.rs/v0.5-rc/rocket/response/struct.Flash.html) in both the response and request handlers. We will have to update our `src/routes.rs` file as follows:
```rust
use rocket::{request::FlashMessage, response::Flash};
#[get("/")]
// Note the addition of the `flash` parameter
// Note the addition of the `flash` parameter.
pub async fn home(flash: Option<FlashMessage<'_>>) -> Template {
// ...
// The `flash` parameter value is added to the template context data
// The `flash` parameter value is added to the template context data.
Template::render("base", context! { whoami: whoami, flash: flash })
}
#[post("/subscribe", data = "<peer>")]
// We return a `Result` type instead of a simple `Redirect`
// We return a `Result` type instead of a simple `Redirect`.
pub async fn subscribe_form(peer: Form<PeerForm>) -> Result<Redirect, Flash<Redirect>> {
info!("Subscribing to peer {}", &peer.public_key);
info!("Subscribing to peer {}", &peer.public_key);
if let Err(e) = utils::validate_public_key(&peer.public_key) {
let validation_err_msg = format!("Public key {} is invalid: {}", &peer.public_key, e);
warn!("Public key {} is invalid: {}", &peer.public_key, e);
return Err(Flash::error(Redirect::to(uri!(home)), validation_err_msg));
}
Ok(Redirect::to(uri!(home)))
Ok(Redirect::to(uri!(home)))
}
#[post("/unsubscribe", data = "<peer>")]
pub async fn unsubscribe_form(peer: Form<PeerForm>) -> Result<Redirect, Flash<Redirect>> {
info!("Unsubscribing to peer {}", &peer.public_key);
info!("Unsubscribing to peer {}", &peer.public_key);
if let Err(e) = utils::validate_public_key(&peer.public_key) {
let validation_err_msg = format!("Public key {} is invalid: {}", &peer.public_key, e);
warn!("Public key {} is invalid: {}", &peer.public_key, e);
@ -321,7 +334,7 @@ Now, if a submitted public key is invalid, a red error message will be displayed
OK, that's a lot of web application shenanigans but I know you're really here for the Scuttlebutt goodness. Let's close-out this installment by writing a function to check whether or not the peer represented by our local go-sbot instance follows another peer; in simpler words: do we follow a peer account or not?
In order to do this using the `golgi` RPC library, we have to construct a `RelationshipQuery` `struct` and call the `friends_is_following()` method.
In order to do this using the `golgi` RPC library, we have to construct a `RelationshipQuery` `struct` and call the `friends_is_following()` method. Let's add a convenience function to initialise the sbot, construct the query and call `friends_is_following()` RPC method:
`src/sbot.rs`
@ -353,14 +366,14 @@ pub async fn subscribe_form(peer: Form<PeerForm>) -> Result<Redirect, Flash<Redi
return Err(Flash::error(Redirect::to(uri!(home)), validation_err_msg));
} else {
info!("Public key {} is valid", &peer.public_key);
// Retrieve the value of the local public key by calling `whoami`
// Retrieve the value of the local public key by calling `whoami`.
if let Ok(whoami) = sbot::whoami().await {
// Do we follow the peer represented by the submitted public key?
match sbot::is_following(&whoami, &peer.public_key).await {
Ok(status) if status.as_str() == "false" => {
info!("Not currently following peer {}", &peer.public_key);
// This is where we will initiate a follow in the next
// installment of the tutorial series
// installment of the tutorial series.
}
Ok(status) if status.as_str() == "true" => {
info!(
@ -412,7 +425,7 @@ The code above is quite verbose due to the fact that we are matching on multiple
### Conclusion
Today we did a lot of work to make our project a more complete web application. We iimproved the organisation of our code base by splitting it into modules, added an HTML form and handlers to enable peer subscription events, learned how to validate public keys and query follow status, and added flash message support to be able to report errors via the UI.
Today we did a lot of work to make our project a more complete web application. We improved the organisation of our codebase by splitting it into modules, added an HTML form and handlers to enable peer subscription events, learned how to validate public keys and query follow status, and added flash message support to be able to report errors via the UI.
If you're confused by any of the code samples above, remember that you can see the complete code for this installment in the git repo.