Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

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

Empty file added Makefile
Empty file.
2 changes: 2 additions & 0 deletions twitar/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ tower = { version = "0.4.12", features=["retry", "limit", "timeout"] }
url = { version = "2.2.2", features = ["serde"] }
urlencoding = "2.1.0"
uuid = { version = "0.8.2", features = ["v4"] }
twitar_macro = { path = "../twitar_macro" }
twitar_macro_derive = { path = "../twitar_macro_derive" }


[dependencies.sqlx]
Expand Down
2 changes: 0 additions & 2 deletions twitar/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# play_twitter


Big Credit to [Egg-mode](https://github.com/egg-mode-rs/egg-mode) as a lot of my implementations was inspired by their design

TODOS!
- [ ] All current redis implementations to be migrated Postgres
- [ ] Only Save post ids on redis
Expand Down
14 changes: 14 additions & 0 deletions twitar/sqlx-data.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,19 @@
}
},
"query": "INSERT INTO auth_two (pkce) VALUES ($1)"
},
"31a5da9b895390da875ff36e4cebd34125f3382a01f9ff523fd47a0b5463b610": {
"describe": {
"columns": [],
"nullable": [],
"parameters": {
"Left": [
"Varchar",
"Varchar",
"Text"
]
}
},
"query": "UPDATE auth_two SET access_token=$1, refresh_token=$2\n WHERE pkce=$3"
}
}
59 changes: 59 additions & 0 deletions twitar/src/base_repository/base_repository.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use futures::{stream, StreamExt};
use redis::{AsyncCommands, ToRedisArgs, RedisError};
use std::{fmt::Display};

use crate::{startup::server::{AppState, LocalAppState}, helpers::commons::AppEnv, configurations::variables::SettingsVars};

pub type ColsAndVals<V: Display + Send + Sync + ToRedisArgs> = Vec<(&'static str, V)>;


pub struct DBInsert<V: Display + Send + Sync + ToRedisArgs> {
table: String,
cols_vals: ColsAndVals<V>,
returning: bool,
}


impl<V: Display + Send + Sync + ToRedisArgs> DBInsert<V> {
pub fn create(table: String, cols_vals: ColsAndVals<V>, returning: bool) -> Self {
// println!("")
Self {table, cols_vals, returning}
}

pub async fn execute(self, app_state: LocalAppState) {
let LocalAppState { redis , app_env, db_pool, .. } = app_state;

println!("HERE NOW IN THE EXECUTE!");

match app_env {
AppEnv::Local => {
let mut con = redis.get_async_connection().await.unwrap();

for (key, val) in &self.cols_vals {
// this isn't the appropriate return type in this use case but ok
let _d: String = con.set(key, val).await.unwrap();
}
},
AppEnv::Test | AppEnv::Staging | AppEnv::Production => {
let mut col_names: Vec<&'static str> = vec![];
let mut positions: Vec<String> = vec![];
let mut vals: Vec<&V> = vec![];

for (index, (key, val)) in self.cols_vals.iter().enumerate() {
col_names.push(key);
positions.push(format!("${}", index));
vals.push(val);
}

let querry_str = format!(
r#"INSERT INTO {} ({}) VALUES ({})"#,
self.table,
col_names.join(", "),
positions.join(", "),
);

// sqlx::query!(querry_str).execute(&db_pool).await.unwrap();
},
}
}
}
6 changes: 4 additions & 2 deletions twitar/src/configurations/db_settings.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use sqlx::postgres::{PgConnectOptions, PgSslMode};

use super::variables::{SettingsVars, AppEnv};
use crate::helpers::commons::AppEnv;

use super::variables::{SettingsVars};

// sqlx::migrate!().run(<&your_pool OR &mut your_connection>).await?;

Expand All @@ -18,7 +20,7 @@ impl DatabaseSettings {
pub fn new(vars: SettingsVars) -> Self {
let mut require_ssl = true;

if [AppEnv::Local.to_string(), AppEnv::Test.to_string()].contains(&vars.app_env) {
if [AppEnv::Local, AppEnv::Test].contains(&vars.app_env) {
require_ssl = false;
}

Expand Down
18 changes: 3 additions & 15 deletions twitar/src/configurations/variables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,7 @@ use dotenv::dotenv;
use serde::Deserialize;
use std::env;

use crate::helpers::gen_pkce::Pkce;

#[derive(Debug, Clone, Deserialize, derive_more::Display)]
pub enum AppEnv {
#[display(fmt = "local")]
Local,
#[display(fmt = "test")]
Test,
#[display(fmt = "staging")]
Staging,
#[display(fmt = "production")]
Production,
}
use crate::helpers::{gen_pkce::Pkce, commons::AppEnv};


#[derive(Debug, Clone, Deserialize)]
Expand All @@ -30,7 +18,7 @@ pub struct SettingsVars {
pub api_key_secret: String,
pub client_secret: String,
pub twitter_url: String,
pub app_env: String,
pub app_env: AppEnv,
pub db_host: String,
pub db_port: u16,
pub db_username: String,
Expand Down Expand Up @@ -69,7 +57,7 @@ impl SettingsVars {
api_key_secret: Self::get_var("API_KEY_SECRET"),
client_secret: Self::get_var("CLIENT_SECRET"),
twitter_url: Self::get_var("TWITTER_API"),
app_env: Self::get_var("APP_ENV"),
app_env: AppEnv::new(Self::get_var("APP_ENV")),
db_host: Self::get_var("DB_HOST"),
db_port: Self::get_var("DB_PORT").parse::<u16>().unwrap(),
db_username: Self::get_var("DB_USERNAME"),
Expand Down
1 change: 1 addition & 0 deletions twitar/src/controllers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod refresh_token;
mod user_lookup;
mod destroy;
mod oauth_flow;
mod commons;


pub use not_found::not_found;
Expand Down
25 changes: 16 additions & 9 deletions twitar/src/controllers/authorize_bot.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use hyper::{Method, Body, Response};
use redis::{AsyncCommands};

use crate::helpers::commons::AppEnv;
use crate::startup::server::AppState;
use crate::helpers::response::ApiBody;
use crate::helpers::{
Expand All @@ -14,21 +15,27 @@ use crate::middlewares::request_builder::RequestBuilder;


pub async fn authorize_bot(app_state: AppState) -> TResult<ApiBody> {
let SettingsVars {client_id, callback_url, state, ..} = app_state.env_vars;
let SettingsVars {client_id, callback_url, state, app_env, ..} = app_state.env_vars.clone();
// store this pkce value in redis for the specific user associated by email
let mut con = app_state.redis.get_async_connection().await.unwrap();


let pkce: String = Pkce::new().to_string();
let scopes = vec![Scope::ReadTweet, Scope::ReadUsers, Scope::ReadFollows, Scope::WriteFollows,
Scope::OfflineAccess, Scope::WriteTweet, Scope::WriteLike, Scope::ReadLike];

// - todo()! need macros to write the sqlx query in a prod env and use redis in a local env
// - todo()! need macros to write the sqlx query in a prod env and use redis in a local env : Won't work :eyes
// take classes on rust macros - https://veykril.github.io/tlborm/syntax-extensions.html
// sqlx::query!(r#"INSERT INTO auth_two (pkce) VALUES ($1)"#, pkce)
// .execute(&app_state.db_pool).await.map_err(|e| {eprintln!("ERROR ADDING PKCE {:#?}", e)}).unwrap();
match app_env {
AppEnv::Local | AppEnv::Test => {
con.set("pkce", &pkce).await?;
}
AppEnv::Production | AppEnv::Staging => {
sqlx::query!(r#"INSERT INTO auth_two (pkce) VALUES ($1)"#, pkce)
.execute(&app_state.db_pool).await.map_err(|e| {eprintln!("ERROR ADDING PKCE {:#?}", e)}).unwrap();
}
}

con.set("pkce", &pkce).await?;
// let local_state = app_state.to_local();
// DBInsert::create("auth_two".into(), vec![("pkce", &pkce)], false).execute(local_state).await;

let query_params = KeyVal::new()
.add_list_keyval(vec![
Expand All @@ -52,4 +59,4 @@ pub async fn authorize_bot(app_state: AppState) -> TResult<ApiBody> {
.body(Body::from(request.uri().to_string())).unwrap();

Ok(response_body)
}
}
1 change: 1 addition & 0 deletions twitar/src/controllers/commons.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub struct Controller;
54 changes: 33 additions & 21 deletions twitar/src/controllers/handle_redirect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ use redis::{Client as RedisClient};
use sqlx::{Pool, Postgres};
use crate::{helpers::{
response::{TResult, ApiBody, make_request, ResponseBuilder},
request::{HyperClient}, keyval::KeyVal, commons::GrantType},
request::{HyperClient}, keyval::KeyVal, commons::{GrantType, AppEnv}},
configurations::variables::SettingsVars, errors::response::{TError}, middlewares::request_builder::{RequestBuilder, AuthType},
interceptors::handle_request::{Interceptor, V2TokensType}, startup::server::AppState
};


async fn access_token(hyper_client: HyperClient, db_client: Pool<Postgres>, redis_client: RedisClient, auth_code: String) -> Result<(), TError> {
async fn access_token(hyper_client: HyperClient, db_client: Pool<Postgres>, redis_client: RedisClient, auth_code: String, app_env: AppEnv) -> Result<(), TError> {
let SettingsVars{client_id, callback_url, client_secret, twitter_url, ..} = SettingsVars::new();
let mut con = redis_client.get_async_connection().await.unwrap();

Expand All @@ -36,16 +36,23 @@ async fn access_token(hyper_client: HyperClient, db_client: Pool<Postgres>, redi
// authentication service user_id would be used to insert here
// - todo()! need macros to write the sqlx query in a prod env and use redis in a local env
// take classes on rust macros - https://veykril.github.io/tlborm/syntax-extensions.html
// sqlx::query!(r#"UPDATE auth_two
// SET access_token=$1, refresh_token=$2
// WHERE pkce=$3"#,
// &map.get(V2TokensType::Access), &map.get(V2TokensType::Refresh), pkce)
// .execute(&db_client).await.map_err(|e| {
// // tracing here later
// eprintln!("ERROR ADDING ACCESS TOKEN {:#?}", e)}).unwrap();

redis::cmd("SET").arg(&["access_token", &map.get(V2TokensType::Access)]).query_async(&mut con).await?;
redis::cmd("SET").arg(&["refresh_token", &map.get(V2TokensType::Refresh)]).query_async(&mut con).await?;


match app_env {
AppEnv::Local | AppEnv::Test => {
redis::cmd("SET").arg(&["access_token", &map.get(V2TokensType::Access)]).query_async(&mut con).await?;
redis::cmd("SET").arg(&["refresh_token", &map.get(V2TokensType::Refresh)]).query_async(&mut con).await?;
}
AppEnv::Production | AppEnv::Staging => {
sqlx::query!(r#"UPDATE auth_two SET access_token=$1, refresh_token=$2
WHERE pkce=$3"#,
&map.get(V2TokensType::Access), &map.get(V2TokensType::Refresh), pkce)
.execute(&db_client).await.map_err(|e| {
// tracing here later
eprintln!("ERROR ADDING ACCESS TOKEN {:#?}", e)}).unwrap();
}
}

return Ok(())
}

Expand All @@ -56,7 +63,7 @@ async fn access_token(hyper_client: HyperClient, db_client: Pool<Postgres>, redi
// req: Request<hyper::Body>, hyper_client: HyperClient, redis_client: RedisClient
pub async fn handle_redirect(app_state: AppState) -> TResult<ApiBody> {
let AppState {redis, hyper, req, env_vars, ..} = app_state;
let SettingsVars{state, api_key, twitter_url, ..} = env_vars;
let SettingsVars{state, api_key, twitter_url, app_env, ..} = env_vars;



Expand All @@ -70,10 +77,9 @@ pub async fn handle_redirect(app_state: AppState) -> TResult<ApiBody> {
let oauth_token: String = redis::cmd("GET").arg(&["oauth_token"]).query_async(&mut con).await?;
if k.validate("oauth_token".into(),oauth_token.clone()) {
let verifier = k.get("oauth_verifier").unwrap();
redis::cmd("SET").arg(&["oauth_verifier", verifier]).query_async(&mut con).await?;

let target = format!("{}/oauth/access_token", twitter_url);

let target = format!("{}/oauth/access_token", twitter_url);
let req = RequestBuilder::new(Method::POST, target)
.with_query("oauth_consumer_key", &api_key)
.with_query("oauth_token", &oauth_token)
Expand All @@ -87,9 +93,17 @@ pub async fn handle_redirect(app_state: AppState) -> TResult<ApiBody> {
let params = KeyVal::string_to_keyval(body_string);

if let Some(map) = params {
redis::cmd("SET").arg(&["oauth_token", map.get("oauth_token").unwrap()]).query_async(&mut con).await?;
redis::cmd("SET").arg(&["oauth_token_secret", map.get("oauth_token_secret").unwrap()]).query_async(&mut con).await?;
redis::cmd("SET").arg(&["userid", map.get("user_id").unwrap()]).query_async(&mut con).await?;
match app_env {
AppEnv::Local | AppEnv::Test => {
redis::cmd("SET").arg(&["oauth_verifier", verifier]).query_async(&mut con).await?;
redis::cmd("SET").arg(&["oauth_token", map.get("oauth_token").unwrap()]).query_async(&mut con).await?;
redis::cmd("SET").arg(&["oauth_token_secret", map.get("oauth_token_secret").unwrap()]).query_async(&mut con).await?;
redis::cmd("SET").arg(&["userid", map.get("user_id").unwrap()]).query_async(&mut con).await?;
}
AppEnv::Production | AppEnv::Staging => {
//
}
}

return ResponseBuilder::new("Access Granted".into(), Some(""), StatusCode::OK.as_u16()).reply();
}
Expand All @@ -105,7 +119,7 @@ pub async fn handle_redirect(app_state: AppState) -> TResult<ApiBody> {
if let Some(dict) = is_v2_callback {
if query_params.validate("state".into(), state) {
let code = dict.get("code").unwrap().to_string();
access_token(hyper.clone(), app_state.db_pool, redis, code).await?;
access_token(hyper.clone(), app_state.db_pool, redis, code, app_env).await?;

return ResponseBuilder::new("Access Granted".into(), Some(""), StatusCode::OK.as_u16()).reply();
}
Expand All @@ -119,6 +133,4 @@ pub async fn handle_redirect(app_state: AppState) -> TResult<ApiBody> {


ResponseBuilder::new("Bad request".into(), Some(""), StatusCode::BAD_REQUEST.as_u16()).reply()


}
Loading