From 38ecaa3c0e9294fe769517f8bd5316f640065f2b Mon Sep 17 00:00:00 2001 From: Pedro Nauck Date: Tue, 5 Mar 2024 18:19:18 -0300 Subject: [PATCH] feat: add social auth --- README.md | 28 +- build.rs | 13 +- src/config.rs | 10 +- src/constants.rs | 1 + src/lib.rs | 7 + src/routes.rs | 29 +- static/index.html | 634 ++++++++++++++++++++++++-------------------- static/sign_in.html | 89 +++++++ 8 files changed, 501 insertions(+), 310 deletions(-) create mode 100644 static/sign_in.html diff --git a/README.md b/README.md index 734503e..ccf25ac 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -Faucet App -=== +# Faucet App + [![build](https://github.com/FuelLabs/faucet/actions/workflows/ci.yml/badge.svg)](https://github.com/FuelLabs/faucet/actions/workflows/ci.yml) [![discord](https://img.shields.io/badge/chat%20on-discord-orange?&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/xfpK4Pe) @@ -7,20 +7,22 @@ A simple faucet app for dispensing tokens on a fuel network. It uses Google capt without requiring any social media based identification. ## Configuration + The faucet makes use of environment variables for configuration. -| Environment Variable | Description | -|----------------------|-------------------------------------------------------------------------| -| RUST_LOG | EnvFilter configuration for adjusting logging granularity. | -| HUMAN_LOGGING | If false, logs will be output as machine readable JSON. | -| CAPTCHA_SECRET | The secret key used for enabling Google captcha authentication. | -| CAPTCHA_KEY | The website key used for enabling Google captcha 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. | +| Environment Variable | Description | +| -------------------- | ----------------------------------------------------------------------------------------------- | +| RUST_LOG | EnvFilter configuration for adjusting logging granularity. | +| HUMAN_LOGGING | If false, logs will be output as machine readable JSON. | +| CAPTCHA_SECRET | The secret key used for enabling Google captcha authentication. | +| CAPTCHA_KEY | The website key used for enabling Google captcha authentication. | +| CLERK_KEY | The 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 | -| SERVICE_PORT | The port the service will listen for http connections on. | -| DISPENSE_AMOUNT | Dispense amount on each faucet | -| MIN_GAS_PRICE | The minimum gas price to use in each transfer | +| SERVICE_PORT | The port the service will listen for http connections on. | +| DISPENSE_AMOUNT | Dispense amount on each faucet | +| MIN_GAS_PRICE | The minimum gas price to use in each transfer | ## Build and Run diff --git a/build.rs b/build.rs index 524401f..1f66c0f 100644 --- a/build.rs +++ b/build.rs @@ -1,12 +1,15 @@ use minify_html::Cfg; use std::{env, fs, path::Path}; -fn main() { +fn build(page: &str, raw: &[u8]) { let out_dir = env::var("OUT_DIR").unwrap(); - let dest_path = Path::new(&out_dir).join("index.html"); - let page = include_bytes!("./static/index.html"); - let minified = minify_html::minify(page, &Cfg::spec_compliant()); - + let dest_path = Path::new(&out_dir).join(page); + let minified = minify_html::minify(raw, &Cfg::spec_compliant()); fs::write(dest_path, minified).expect("failed to save minified index page"); println!("cargo:rerun-if-changed=static"); } + +fn main() { + build("index.html", include_bytes!("./static/index.html")); + build("sign_in.html", include_bytes!("./static/sign_in.html")); +} diff --git a/src/config.rs b/src/config.rs index 3051515..b234449 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,8 +1,8 @@ use crate::constants::{ - CAPTCHA_KEY, CAPTCHA_SECRET, DEFAULT_DISPENSE_INTERVAL, DEFAULT_FAUCET_DISPENSE_AMOUNT, - DEFAULT_NODE_URL, DEFAULT_PORT, DISPENSE_AMOUNT, DISPENSE_INTERVAL, FAUCET_ASSET_ID, - FUEL_NODE_URL, HUMAN_LOGGING, LOG_FILTER, MIN_GAS_PRICE, PUBLIC_FUEL_NODE_URL, SERVICE_PORT, - TIMEOUT_SECONDS, WALLET_SECRET_KEY, + CAPTCHA_KEY, CAPTCHA_SECRET, CLERK_KEY, DEFAULT_DISPENSE_INTERVAL, + DEFAULT_FAUCET_DISPENSE_AMOUNT, DEFAULT_NODE_URL, DEFAULT_PORT, DISPENSE_AMOUNT, + DISPENSE_INTERVAL, FAUCET_ASSET_ID, FUEL_NODE_URL, HUMAN_LOGGING, LOG_FILTER, MIN_GAS_PRICE, + PUBLIC_FUEL_NODE_URL, SERVICE_PORT, TIMEOUT_SECONDS, WALLET_SECRET_KEY, }; use fuels_core::types::AssetId; use secrecy::Secret; @@ -15,6 +15,7 @@ pub struct Config { pub service_port: u16, pub captcha_key: Option, pub captcha_secret: Option>, + pub clerk_key: Option, pub node_url: String, pub public_node_url: String, pub wallet_secret_key: Option>, @@ -36,6 +37,7 @@ impl Default for Config { captcha_secret: env::var_os(CAPTCHA_SECRET) .map(|s| Secret::new(s.into_string().unwrap())), captcha_key: env::var_os(CAPTCHA_KEY).map(|s| s.into_string().unwrap()), + clerk_key: env::var_os(CLERK_KEY).map(|s| s.into_string().unwrap()), node_url: env::var(FUEL_NODE_URL).unwrap_or_else(|_| DEFAULT_NODE_URL.to_string()), public_node_url: env::var(PUBLIC_FUEL_NODE_URL) .unwrap_or_else(|_| DEFAULT_NODE_URL.to_string()), diff --git a/src/constants.rs b/src/constants.rs index 2228508..4f9f468 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -5,6 +5,7 @@ pub const HUMAN_LOGGING: &str = "HUMAN_LOGGING"; pub const CAPTCHA_KEY: &str = "CAPTCHA_KEY"; pub const CAPTCHA_SECRET: &str = "CAPTCHA_SECRET"; pub const WALLET_SECRET_KEY: &str = "WALLET_SECRET_KEY"; +pub const CLERK_KEY: &str = "CLERK_KEY"; pub const PUBLIC_FUEL_NODE_URL: &str = "PUBLIC_FUEL_NODE_URL"; pub const WALLET_SECRET_DEV_KEY: &str = "99ad179d4f892ff3124ccd817408ff8a4452d9c16bb1b4968b8a59797e13cd7a"; diff --git a/src/lib.rs b/src/lib.rs index 019b8bc..6abc7cf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -155,6 +155,13 @@ pub async fn start_server( HeaderValue::from_static("public, max-age=3600, immutable"), )), ) + .route( + "/sign-in", + get(routes::sign_in).layer(SetResponseHeaderLayer::<_>::overriding( + CACHE_CONTROL, + HeaderValue::from_static("public, max-age=3600, immutable"), + )), + ) .route("/health", get(health)) .route("/dispense", get(routes::dispense_info)) .route( diff --git a/src/routes.rs b/src/routes.rs index 5bd91ce..3cbf77f 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -40,7 +40,11 @@ lazy_static::lazy_static! { } #[memoize::memoize] -pub fn render_page(public_node_url: String, captcha_key: Option) -> String { +pub fn render_main( + public_node_url: String, + captcha_key: Option, + clerk_key: String, +) -> String { let template = include_str!(concat!(env!("OUT_DIR"), "/index.html")); // sub in values let mut handlebars = Handlebars::new(); @@ -50,6 +54,7 @@ pub fn render_page(public_node_url: String, captcha_key: Option) -> Stri let mut data = BTreeMap::new(); data.insert("page_title", "Fuel Faucet"); data.insert("public_node_url", public_node_url.as_str()); + data.insert("clerk_public_key", clerk_key.as_str()); // if captcha is enabled, add captcha key if let Some(captcha_key) = &captcha_key { data.insert("captcha_key", captcha_key.as_str()); @@ -58,10 +63,30 @@ pub fn render_page(public_node_url: String, captcha_key: Option) -> Stri handlebars.render("index", &data).unwrap() } +#[memoize::memoize] +pub fn render_sign_in(clerk_key: String) -> String { + let template = include_str!(concat!(env!("OUT_DIR"), "/sign_in.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_key.as_str()); + // render page + handlebars.render("index", &data).unwrap() +} + pub async fn main(Extension(config): Extension) -> Html { let public_node_url = config.public_node_url.clone(); let captcha_key = config.captcha_key.clone(); - Html(render_page(public_node_url, captcha_key)) + let clerk_key = config.clerk_key.clone().unwrap(); + Html(render_main(public_node_url, captcha_key, clerk_key)) +} + +pub async fn sign_in(Extension(config): Extension) -> Html { + let clerk_key = config.clerk_key.clone().unwrap(); + Html(render_sign_in(clerk_key)) } #[tracing::instrument(skip(wallet))] diff --git a/static/index.html b/static/index.html index f76ec24..2c7a2f0 100644 --- a/static/index.html +++ b/static/index.html @@ -1,317 +1,379 @@ - + + + + {{ page_title }} + + + + + - - -
-
- -
-
- - + .button:disabled { + background-color: gray; + } + + .card { + position: relative; + margin-top: 90px; + z-index: 1; + width: 480px; + font-size: 14px; + padding: 40px 20px 20px 20px; + border-radius: 10px; + background-color: white; + box-shadow: 0 0 16px 2px rgba(248, 248, 248, 0.25); + border: 1px solid rgb(229, 231, 235); + max-width: 95%; + } + + .from-control { + display: flex; + flex-direction: column; + } + + .from-control label { + margin-bottom: 0.3em; + color: #333; + } + + .from-control input { + border: 1px solid #888; + border-radius: 4px; + padding: 10px; + } + + .from-control input:invalid { + border: 1px solid red; + } + + .captcha-container { + display: flex; + margin-top: 20px; + justify-content: center; + } + + .bold { + color: #000; + font-weight: bold; + } + + .response-title, + .provider-url, + #response-failure { + text-align: center; + } + + #response-failure { + color: red; + padding: 10px; + } + + #response { + display: none; + } + + .provider-url { + font-size: 12px; + text-align: center; + margin-top: 10px; + color: #949494; + } + + .queued { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + gap: 1rem; + } + + .captcha-area { + height: 100px; + display: flex; + justify-content: center; + align-items: center; + } + + .loader { + border: 0.4rem solid #f3f3f3; + border-top: 0.4rem solid #00f58c; + border-radius: 50%; + width: 2rem; + height: 2rem; + animation: spin 2s linear infinite; + } + + .cl-userButton-root { + position: absolute; + top: 10px; + right: 10px; + } + + @keyframes spin { + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } + } + + .hidden { + display: none; + } + + + +
+
+
+ -

- This is a Test Ether faucet running on the - Test Fuel network. This faucet sends fake Ether - assets to the provided wallet address. -

-
- {{#if captcha_key}} + +
+ + +
+

+ This is a Test Ether faucet running on the + Test Fuel network. This faucet sends fake Ether + assets to the provided wallet address. +

+
+ {{#if captcha_key}}
- {{/if}} - +
+
+ + +
+

Test Ether sent to the wallet

+ See on Fuel Explorer
-
- - -
-

Test Ether sent to the wallet

- See on Fuel Explorer
-
-
Node url: {{ public_node_url }}
- + + - + function handle_error(message) { + document.getElementById("response-failure").innerText = message; + hideWaiting(); + } - \ No newline at end of file + return { give_me_coins: give_me_coins }; + })(); + + + diff --git a/static/sign_in.html b/static/sign_in.html new file mode 100644 index 0000000..c3c71ed --- /dev/null +++ b/static/sign_in.html @@ -0,0 +1,89 @@ + + + + + {{ page_title }} + + + + + + + + + +