2022-07-14 00:49:57 +03:00
// So the API needs to show for any given user:
// - show balance in USD
// - show deposits history (currency, amounts, transaction id)
// - show number of requests used (so we can calculate average spending over a month, burn rate for a user etc, something like "Your balance will be depleted in xx days)
// - the email address of a user if he opted in to get contacted via email
// - all the monitoring and stats but that will come from someplace else if I understand corectly?
// I wonder how we handle payment
// probably have to do manual withdrawals
2022-08-11 04:53:27 +03:00
use axum ::{
response ::{ IntoResponse , Response } ,
Extension , Json ,
} ;
2022-08-04 04:10:27 +03:00
use axum_client_ip ::ClientIp ;
2022-08-04 02:17:02 +03:00
use entities ::user ;
2022-08-04 04:10:27 +03:00
use ethers ::{ prelude ::Address , types ::Bytes } ;
2022-08-11 04:53:27 +03:00
use reqwest ::StatusCode ;
2022-08-04 04:10:27 +03:00
use sea_orm ::ActiveModelTrait ;
use serde ::Deserialize ;
use std ::sync ::Arc ;
2022-08-04 02:17:02 +03:00
2022-08-10 05:37:34 +03:00
use crate ::app ::Web3ProxyApp ;
2022-08-11 04:53:27 +03:00
use super ::{ rate_limit ::RateLimitResult , errors ::anyhow_error_into_response } ;
2022-08-04 02:17:02 +03:00
2022-07-14 00:49:57 +03:00
pub async fn create_user (
// this argument tells axum to parse the request body
// as JSON into a `CreateUser` type
Json ( payload ) : Json < CreateUser > ,
2022-08-04 04:10:27 +03:00
Extension ( app ) : Extension < Arc < Web3ProxyApp > > ,
ClientIp ( ip ) : ClientIp ,
2022-08-11 04:53:27 +03:00
) -> Response {
let _ip = match app . rate_limit_by_ip ( ip ) . await {
Ok ( x ) = > match x . try_into_response ( ) . await {
Ok ( RateLimitResult ::AllowedIp ( x ) ) = > x ,
Err ( err_response ) = > return err_response ,
_ = > unimplemented! ( ) ,
} ,
Err ( err ) = > return anyhow_error_into_response ( None , None , err ) . into_response ( ) ,
} ;
2022-08-04 04:10:27 +03:00
2022-08-10 05:37:34 +03:00
// TODO: check invite_code against the app's config or database
2022-08-04 04:10:27 +03:00
if payload . invite_code ! = " llam4n0des! " {
todo! ( " proper error message " )
}
// TODO: dont unwrap. proper error
let signature : [ u8 ; 65 ] = payload . signature . as_ref ( ) . try_into ( ) . unwrap ( ) ;
// TODO: calculate the expected message for the current user. include domain and a nonce. let timestamp be automatic
let message : siwe ::Message = " abc123 " . parse ( ) . unwrap ( ) ;
if let Err ( e ) = message . verify ( signature , None , None , None ) {
// message cannot be correctly authenticated
todo! ( " proper error message: {} " , e )
}
2022-08-04 02:17:02 +03:00
let user = user ::ActiveModel {
2022-08-06 03:07:12 +03:00
address : sea_orm ::Set ( payload . address . to_fixed_bytes ( ) . into ( ) ) ,
2022-08-04 04:10:27 +03:00
email : sea_orm ::Set ( payload . email ) ,
2022-08-04 02:17:02 +03:00
.. Default ::default ( )
2022-07-14 00:49:57 +03:00
} ;
2022-08-10 08:56:09 +03:00
let db = app . db_conn . as_ref ( ) . unwrap ( ) ;
2022-08-04 02:17:02 +03:00
2022-08-04 04:10:27 +03:00
// TODO: proper error message
let user = user . insert ( db ) . await . unwrap ( ) ;
2022-08-04 02:17:02 +03:00
2022-08-11 04:53:27 +03:00
// TODO: do not expose user ids
( StatusCode ::CREATED , Json ( user ) ) . into_response ( )
2022-07-14 00:49:57 +03:00
}
// the input to our `create_user` handler
#[ derive(Deserialize) ]
pub struct CreateUser {
2022-08-04 02:17:02 +03:00
address : Address ,
// TODO: make sure the email address is valid
2022-07-14 00:49:57 +03:00
email : Option < String > ,
signature : Bytes ,
2022-08-04 02:17:02 +03:00
invite_code : String ,
2022-07-14 00:49:57 +03:00
}