Skip to content

Commit

Permalink
chore: improve frontend
Browse files Browse the repository at this point in the history
  • Loading branch information
pedronauck committed Mar 12, 2024
1 parent 4a4e8e1 commit 9d78c3c
Show file tree
Hide file tree
Showing 16 changed files with 652 additions and 541 deletions.
81 changes: 80 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ description = "A token faucet for onboarding fuel users"
[dependencies]
anyhow = "1.0"
axum = "0.7.4"
axum_static = "1.2.3"
clerk-rs = "0.2.3"
fuel-core-client = "0.22.0"
fuel-tx = "0.43.0"
Expand All @@ -37,7 +38,12 @@ tower = { version = "0.4", features = [
"util",
"timeout",
] }
tower-http = { version = "0.5.2", features = ["cors", "trace", "set-header"] }
tower-http = { version = "0.5.2", features = [
"fs",
"cors",
"trace",
"set-header",
] }
tower-sessions = "0.11.0"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ The faucet makes use of environment variables for configuration.
| CAPTCHA_SECRET | The secret key used for enabling Google captcha authentication. |
| CAPTCHA_KEY | The website key used for enabling Google captcha authentication. |
| CLERK_PUB_KEY | The public key used for enabling clerk authentication. |
| CLERK_SECRET_KEY | The secret key used for enabling clerk authentication. |
| CLERK_SECRET_KEY | The secret key used for enabling clerk authentication. |
| WALLET_SECRET_KEY | A hex formatted string of the wallet private key that owns some tokens. |
| FUEL_NODE_URL | The GraphQL endpoint for connecting to fuel-core. |
| PUBLIC_FUEL_NODE_URL | The public GraphQL endpoint for connecting to fuel-core. Ex.: https://node.fuel.network/graphql |
Expand Down
1 change: 0 additions & 1 deletion build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,4 @@ fn build(page: &str, raw: &[u8]) {

fn main() {
build("index.html", include_bytes!("./static/index.html"));
build("sign_in.html", include_bytes!("./static/sign_in.html"));
}
25 changes: 13 additions & 12 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ mod constants;
mod dispense_tracker;
mod recaptcha;
mod routes;
mod static_files;

pub use dispense_tracker::{Clock, TokioTime};
pub use routes::THE_BIGGEST_AMOUNT;
Expand Down Expand Up @@ -164,8 +165,8 @@ pub async fn start_server(
let app = Router::new()
.route("/", get(routes::main).route_layer(web_layer.clone()))
.route(
"/sign-in",
get(routes::sign_in).route_layer(web_layer.clone()),
"/auth",
get(routes::auth).route_layer(web_layer.clone()),
)
.route(
"/api/validate-session",
Expand All @@ -189,6 +190,7 @@ pub async fn start_server(
.into_inner(),
),
)
.nest("/static", static_files::router("static"))
.layer(
ServiceBuilder::new()
// Handle errors from middleware
Expand All @@ -214,20 +216,19 @@ pub async fn start_server(
.into_inner(),
);

// run the server
let addr = SocketAddr::from(([0, 0, 0, 0], service_config.service_port));
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
let bound_addr = listener.local_addr().unwrap();
let task = tokio::spawn(async move {
axum::serve(listener, app.into_make_service())
.await
.unwrap();
Ok(())
});

// run the server
info!("listening on {}", bound_addr);
(
bound_addr,
tokio::spawn(async move {
axum::serve(listener, app.into_make_service())
.await
.unwrap();
Ok(())
}),
)
(bound_addr, task)
}

async fn handle_error(error: BoxError) -> impl IntoResponse {
Expand Down
18 changes: 11 additions & 7 deletions src/routes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use std::{
time::{SystemTime, UNIX_EPOCH},
};
use tower_sessions::Session;
use tracing::{error, info};
use tracing::{debug, error, info};

// The amount to fetch the biggest input of the faucet.
pub const THE_BIGGEST_AMOUNT: u64 = u32::MAX as u64;
Expand Down Expand Up @@ -62,20 +62,22 @@ pub fn render_main(
if let Some(captcha_key) = &captcha_key {
data.insert("captcha_key", captcha_key.as_str());
}
data.insert("page", "faucet");
// render page
handlebars.render("index", &data).unwrap()
}

#[memoize::memoize]
pub fn render_sign_in(clerk_pub_key: String) -> String {
let template = include_str!(concat!(env!("OUT_DIR"), "/sign_in.html"));
pub fn render_auth(clerk_pub_key: String) -> String {
let template = include_str!(concat!(env!("OUT_DIR"), "/index.html"));
// sub in values
let mut handlebars = Handlebars::new();
handlebars
.register_template_string("index", template)
.unwrap();
let mut data = BTreeMap::new();
data.insert("clerk_public_key", clerk_pub_key.as_str());
data.insert("page", "auth");
// render page
handlebars.render("index", &data).unwrap()
}
Expand All @@ -86,16 +88,16 @@ pub async fn main(
) -> impl IntoResponse {
let public_node_url = config.public_node_url.clone();
let captcha_key = config.captcha_key.clone();
let clerk_pub_key = config.clerk_pub_key.clone().unwrap();
let clerk_pub_key = config.clerk_pub_key.clone().unwrap_or("".to_string());
let jwt_token: Option<String> = session.get("JWT_TOKEN").await.unwrap();

match jwt_token {
Some(_) => Html(render_main(public_node_url, captcha_key, clerk_pub_key)).into_response(),
None => Redirect::temporary("/sign-in").into_response(),
None => Redirect::temporary("/auth").into_response(),
}
}

pub async fn sign_in(
pub async fn auth(
Extension(config): Extension<SharedConfig>,
session: Session,
) -> impl IntoResponse {
Expand All @@ -104,7 +106,7 @@ pub async fn sign_in(

match jwt_token {
Some(_) => Redirect::temporary("/").into_response(),
None => Html(render_sign_in(clerk_pub_key.unwrap())).into_response(),
None => Html(render_auth(clerk_pub_key.unwrap_or("".to_string()))).into_response(),
}
}

Expand Down Expand Up @@ -291,6 +293,8 @@ pub async fn dispense_tokens(
Extension(dispense_tracker): Extension<SharedDispenseTracker>,
Json(input): Json<DispenseInput>,
) -> Result<DispenseResponse, DispenseError> {
println!("dispense_tokens");
dbg!(&input);
// parse deposit address
let address = if let Ok(address) = Address::from_str(input.address.as_str()) {
Ok(address)
Expand Down
48 changes: 48 additions & 0 deletions src/static_files.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use axum::{
body::Body,
http::Request,
middleware::{from_fn, Next},
response::Response,
Router,
};
use std::path::Path;
use tower_http::services::ServeDir;

pub async fn content_type_middleware(req: Request<Body>, next: Next) -> Response {
let uri = req.uri().to_owned();
let path = uri.path();
let splited = path.split('.').collect::<Vec<_>>();
if let Some(extension) = splited.last() {
let mut response = next.run(req).await;
let extension = extension.to_owned().to_lowercase();
let content_type = match extension.as_str() {
"html" => "text/html",
"css" => "text/css",
"js" => "text/javascript",
"ps" => "application/postscript",
_ => "application/octet-stream",
};

if let Ok(content_type) = content_type.parse() {
response.headers_mut().insert("Content-Type", content_type);
}

response
} else {
let mut response = next.run(req).await;

if let Ok(content_type) = "application/octet-stream".parse() {
response.headers_mut().insert("Content-Type", content_type);
}

response
}
}

pub fn router<P: AsRef<Path>>(path: P) -> Router {
let serve_dir = ServeDir::new(path);

Router::new()
.fallback_service(serve_dir)
.layer(from_fn(content_type_middleware))
}
23 changes: 23 additions & 0 deletions static/components/captcha.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { html } from "htm/preact";

export function Captcha({ captchaKey, isHidden }) {
if (isHidden) {
return null;
}
return html`
<div class="h-[100px] flex items-center justify-center mt-4">
${
captchaKey &&
html`
<div class="captcha-container">
<div class="g-recaptcha" data-sitekey="{{ captcha_key }}"></div>
</div>
`
}
<div class="flex flex-col items-center justify-center gap-2 hidden">
<div class="loader"></div>
<div>Waiting until more tokens are available</div>
</div>
</div>
`;
}
22 changes: 22 additions & 0 deletions static/components/checkbox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { html } from "htm/preact";

export function Checkbox({ id, checked, onChange, children }) {
return html`
<div class=${styles.checkboxRow}>
<input
type="checkbox"
id=${id}
name=${id}
class=${styles.checkbox}
checked=${checked}
onChange=${onChange}
/>
<label for=${id}>${children}</label>
</div>
`;
}

const styles = {
checkboxRow: "flex items-center gap-2",
checkbox: "w-4 h-4 text-green-600 bg-gray-100 border-gray-300 rounded",
};
Loading

0 comments on commit 9d78c3c

Please sign in to comment.