half the login page and better error handling
This commit is contained in:
parent
0ccda2f40b
commit
115657e97c
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -5525,6 +5525,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"siwe",
|
||||
"time 0.3.11",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"toml",
|
||||
|
@ -50,8 +50,10 @@ siwe = "0.4.1"
|
||||
sea-orm = { version = "0.9.1", features = ["macros"] }
|
||||
serde = { version = "1.0.143", features = [] }
|
||||
serde_json = { version = "1.0.83", default-features = false, features = ["alloc", "raw_value"] }
|
||||
# TODO: make sure this time version matches siwe. PR to put this in their prelude
|
||||
time = "0.3.11"
|
||||
tokio = { version = "1.20.1", features = ["full", "tracing"] }
|
||||
# TODO: make sure this uuid version matches what is in sea orm. PR on sea orm to put builder into prelude
|
||||
# TODO: make sure this uuid version matches sea-orm. PR to put this in their prelude
|
||||
tokio-stream = { version = "0.1.9", features = ["sync"] }
|
||||
toml = "0.5.9"
|
||||
tower = "0.4.13"
|
||||
|
@ -5,6 +5,7 @@ use axum::{
|
||||
Json,
|
||||
};
|
||||
use derive_more::From;
|
||||
use redis_rate_limit::{bb8::RunError, RedisError};
|
||||
use serde_json::value::RawValue;
|
||||
use std::error::Error;
|
||||
|
||||
@ -14,12 +15,32 @@ pub type FrontendResult = Result<Response, FrontendErrorResponse>;
|
||||
#[derive(From)]
|
||||
pub enum FrontendErrorResponse {
|
||||
Anyhow(anyhow::Error),
|
||||
BoxError(Box<dyn Error>),
|
||||
Box(Box<dyn Error>),
|
||||
// TODO: should we box these instead?
|
||||
Redis(RedisError),
|
||||
RedisRunError(RunError<RedisError>),
|
||||
}
|
||||
|
||||
impl IntoResponse for FrontendErrorResponse {
|
||||
fn into_response(self) -> Response {
|
||||
todo!("into_response based on the error type")
|
||||
let null_id = RawValue::from_string("null".to_string()).unwrap();
|
||||
|
||||
// TODO: think more about this. this match should probably give us http and jsonrpc codes
|
||||
let err = match self {
|
||||
Self::Anyhow(err) => err,
|
||||
Self::Box(err) => anyhow::anyhow!("Boxed error: {:?}", err),
|
||||
Self::Redis(err) => err.into(),
|
||||
Self::RedisRunError(err) => err.into(),
|
||||
};
|
||||
|
||||
let err = JsonRpcForwardedResponse::from_anyhow_error(err, null_id);
|
||||
|
||||
let code = StatusCode::INTERNAL_SERVER_ERROR;
|
||||
|
||||
// TODO: logs here are too verbose. emit a stat instead? or maybe only log internal errors?
|
||||
// warn!("Responding with error: {:?}", err);
|
||||
|
||||
(code, Json(err)).into_response()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ pub async fn public_proxy_web3_rpc(
|
||||
Extension(app): Extension<Arc<Web3ProxyApp>>,
|
||||
ClientIp(ip): ClientIp,
|
||||
) -> Response {
|
||||
// TODO: dry this up a lot
|
||||
let _ip = match app.rate_limit_by_ip(ip).await {
|
||||
Ok(x) => match x.try_into_response().await {
|
||||
Ok(RateLimitResult::AllowedIp(x)) => x,
|
||||
|
@ -68,7 +68,7 @@ pub async fn serve(port: u16, proxy_app: Arc<Web3ProxyApp>) -> anyhow::Result<()
|
||||
.route("/u/:user_key", get(ws_proxy::user_websocket_handler))
|
||||
.route("/health", get(http::health))
|
||||
.route("/status", get(http::status))
|
||||
.route("/login", get(users::get_login))
|
||||
.route("/login/:user_address", get(users::get_login))
|
||||
.route("/users", post(users::create_user))
|
||||
.route(
|
||||
"/foo",
|
||||
|
@ -21,11 +21,14 @@ use axum_client_ip::ClientIp;
|
||||
use axum_macros::debug_handler;
|
||||
use entities::user;
|
||||
use ethers::{prelude::Address, types::Bytes};
|
||||
use redis_rate_limit::redis::{pipe, AsyncCommands};
|
||||
use redis_rate_limit::redis::AsyncCommands;
|
||||
use reqwest::StatusCode;
|
||||
use sea_orm::ActiveModelTrait;
|
||||
use serde::Deserialize;
|
||||
use siwe::Message;
|
||||
use std::ops::Add;
|
||||
use std::sync::Arc;
|
||||
use time::{Duration, OffsetDateTime};
|
||||
use uuid::Uuid;
|
||||
|
||||
// TODO: how do we customize axum's error response? I think we probably want an enum that implements IntoResponse instead
|
||||
@ -33,7 +36,8 @@ use uuid::Uuid;
|
||||
pub async fn get_login(
|
||||
Extension(app): Extension<Arc<Web3ProxyApp>>,
|
||||
ClientIp(ip): ClientIp,
|
||||
// TODO: what does axum's error handling look like?
|
||||
// TODO: what does axum's error handling look like if the path fails to parse?
|
||||
// TODO: allow ENS names here?
|
||||
Path(user_address): Path<Address>,
|
||||
) -> FrontendResult {
|
||||
// TODO: refactor this to use the try operator
|
||||
@ -51,29 +55,50 @@ pub async fn get_login(
|
||||
// its a better UX to just click "login with ethereum" and have the account created if it doesn't exist
|
||||
// we can prompt for an email and and payment after they log in
|
||||
|
||||
let session_id = uuid::Uuid::new_v4();
|
||||
// TODO: how many seconds? get from config?
|
||||
let expire_seconds: usize = 300;
|
||||
|
||||
// TODO: if no redis, store in local cache?
|
||||
// create a session id and save it in redis
|
||||
let nonce = Uuid::new_v4();
|
||||
|
||||
let issued_at = OffsetDateTime::now_utc();
|
||||
|
||||
let expiration_time = issued_at.add(Duration::new(expire_seconds as i64, 0));
|
||||
|
||||
// TODO: get request_id out of the trace? do we need that when we have a none?
|
||||
|
||||
// TODO: get most of these from the app config
|
||||
let message = Message {
|
||||
domain: "staging.llamanodes.com".parse().unwrap(),
|
||||
address: user_address.to_fixed_bytes(),
|
||||
statement: Some("🦙🦙🦙🦙🦙".to_string()),
|
||||
uri: "https://staging.llamanodes.com/".parse().unwrap(),
|
||||
version: siwe::Version::V1,
|
||||
chain_id: 1,
|
||||
expiration_time: Some(expiration_time.into()),
|
||||
issued_at: issued_at.into(),
|
||||
nonce: nonce.to_string(),
|
||||
not_before: None,
|
||||
request_id: None,
|
||||
resources: vec![],
|
||||
};
|
||||
|
||||
let session_key = format!("pending:{}", nonce);
|
||||
|
||||
// TODO: if no redis server, store in local cache?
|
||||
let redis_pool = app
|
||||
.redis_pool
|
||||
.as_ref()
|
||||
.expect("login requires a redis server");
|
||||
|
||||
let mut redis_conn = redis_pool.get().await.unwrap();
|
||||
let mut redis_conn = redis_pool.get().await?;
|
||||
|
||||
// TODO: how many seconds? get from config?
|
||||
let session_expiration_seconds = 300;
|
||||
// TODO: the address isn't enough. we need to save the actual message
|
||||
redis_conn
|
||||
.set_ex(session_key, message.to_string(), expire_seconds)
|
||||
.await?;
|
||||
|
||||
let reply: String = redis_conn
|
||||
.set_ex(
|
||||
session_id.to_string(),
|
||||
user_address.to_string(),
|
||||
session_expiration_seconds,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
todo!("how should this work? probably keep stuff in redis")
|
||||
Ok(message.to_string().into_response())
|
||||
}
|
||||
|
||||
#[debug_handler]
|
||||
|
Loading…
Reference in New Issue
Block a user