Compare commits
No commits in common. "bf0030b7f174e07b5b076f09b46b8fbc15173e79" and "5b86b2173480297a3504d75b4d48369b434b5974" have entirely different histories.
bf0030b7f1
...
5b86b21734
@ -2,6 +2,7 @@
|
|||||||
name = "user-svc-actix"
|
name = "user-svc-actix"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
build = "build.rs"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
@ -14,7 +15,8 @@ chrono = { version = "0.4", features = [ "serde" ] }
|
|||||||
ring = "0.16.20"
|
ring = "0.16.20"
|
||||||
data-encoding = "2.3.2"
|
data-encoding = "2.3.2"
|
||||||
futures-util = "0.3"
|
futures-util = "0.3"
|
||||||
actix-web-utils = "0.2"
|
|
||||||
#log = { version = "0.4", features = ["serde"] }
|
|
||||||
|
|
||||||
dev-dtos = { git = "https://git.franklinblanco.dev/franklinblanco/dev-dtos.git" }
|
[build-dependencies]
|
||||||
|
dotenv = "0.15.0"
|
||||||
|
sqlx = { version = "0.6.0", features = [ "runtime-tokio-rustls", "mysql", "chrono" ] }
|
||||||
|
tokio = { version = "1", features = ["full"] }
|
@ -44,11 +44,7 @@ To build for release on current platform
|
|||||||
cargo build --release
|
cargo build --release
|
||||||
```
|
```
|
||||||
|
|
||||||
To build for release on x86-64 linux From m1 macs
|
To build for release on x86-64 linux
|
||||||
``` bash
|
|
||||||
brew tap SergioBenitez/osxct
|
|
||||||
brew install x86_64-unknown-linux-gnu
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo build --release --target x86_64-unknown-linux-gnu
|
cargo build --release --target x86_64-unknown-linux-gnu
|
||||||
|
28
build.rs
Normal file
28
build.rs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
extern crate dotenv;
|
||||||
|
|
||||||
|
use std::{env, collections::HashMap};
|
||||||
|
|
||||||
|
use sqlx::Connection;
|
||||||
|
use dotenv::dotenv;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main(){
|
||||||
|
println!("cargo:rerun-if-changed=migrations");
|
||||||
|
dotenv().ok();
|
||||||
|
let mut dotenv_vars: HashMap<String, String> = HashMap::new();
|
||||||
|
for (key, val) in env::vars() {
|
||||||
|
dotenv_vars.insert(key, val);
|
||||||
|
}
|
||||||
|
let db_url = match dotenv_vars.get("DATABASE_URL") {
|
||||||
|
Some(var) => {var},
|
||||||
|
None => {panic!("DATABASE_URL env var not found, set it!")}
|
||||||
|
};
|
||||||
|
let mut conn = match sqlx::MySqlConnection::connect(&db_url).await {
|
||||||
|
Ok(res) => {res},
|
||||||
|
Err(e) => {panic!("{}", e)}
|
||||||
|
};
|
||||||
|
match sqlx::migrate!("./migrations").run(&mut conn).await {
|
||||||
|
Ok(()) => {println!("{}", "Successfully ran migrations.")},
|
||||||
|
Err(error) => {panic!("{error}")}
|
||||||
|
};
|
||||||
|
}
|
@ -1,10 +1,9 @@
|
|||||||
CREATE TABLE IF NOT EXISTS user (
|
CREATE TABLE IF NOT EXISTS user (
|
||||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
time_created DATETIME,
|
time_created DATETIME,
|
||||||
last_updated DATETIME,
|
last_updated DATETIME,
|
||||||
app VARCHAR(255) NOT NULL,
|
app VARCHAR(255) NOT NULL,
|
||||||
credential VARCHAR(255) NOT NULL,
|
email VARCHAR(255) NOT NULL,
|
||||||
credential_type VARCHAR(20) NOT NULL,
|
|
||||||
name VARCHAR(255) NOT NULL,
|
name VARCHAR(255) NOT NULL,
|
||||||
password TEXT NOT NULL,
|
password TEXT NOT NULL,
|
||||||
salt TEXT NOT NULL
|
salt TEXT NOT NULL
|
||||||
|
1
migrations/20220726034843_aaa.sql
Normal file
1
migrations/20220726034843_aaa.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
-- Add migration script here
|
@ -1,6 +1,6 @@
|
|||||||
CREATE TABLE IF NOT EXISTS token (
|
CREATE TABLE IF NOT EXISTS token (
|
||||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
user_id INT UNSIGNED NOT NULL,
|
user_id INT NOT NULL,
|
||||||
time_created DATETIME,
|
time_created DATETIME,
|
||||||
last_updated DATETIME,
|
last_updated DATETIME,
|
||||||
auth_token TEXT NOT NULL,
|
auth_token TEXT NOT NULL,
|
||||||
|
8
sql/schema/migrations/tokenup.sql
Normal file
8
sql/schema/migrations/tokenup.sql
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS token (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
user_id INT NOT NULL,
|
||||||
|
time_created TIMESTAMP NOT NULL,
|
||||||
|
last_updated TIMESTAMP NOT NULL,
|
||||||
|
auth_token TEXT NOT NULL,
|
||||||
|
refresh_token TEXT NOT NULL
|
||||||
|
)
|
9
sql/schema/migrations/userup.sql
Normal file
9
sql/schema/migrations/userup.sql
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS user (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
time_created TIMESTAMP NOT NULL,
|
||||||
|
last_updated TIMESTAMP NOT NULL,
|
||||||
|
email VARCHAR(255) NOT NULL,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
password TEXT NOT NULL,
|
||||||
|
salt VARCHAR(255) NOT NULL
|
||||||
|
)
|
@ -1,5 +0,0 @@
|
|||||||
SELECT *
|
|
||||||
FROM user
|
|
||||||
WHERE user.credential = ? AND
|
|
||||||
user.credential_type = ? AND
|
|
||||||
user.app = ?
|
|
4
sql/schema/user/find_with_email.sql
Normal file
4
sql/schema/user/find_with_email.sql
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
SELECT *
|
||||||
|
FROM user
|
||||||
|
WHERE user.email = ? AND
|
||||||
|
user.app = ?
|
@ -1,3 +1,3 @@
|
|||||||
INSERT INTO user
|
INSERT INTO user
|
||||||
(id, time_created, last_updated, app, credential, credential_type, name, password, salt) values
|
(id, time_created, last_updated, app, email, name, password, salt) values
|
||||||
(NULL, NOW(), NOW(), ?, ?, ?, ?, ?, ?)
|
(NULL, NOW(), NOW(), ?, ?, ?, ?, ?)
|
@ -1,7 +1,6 @@
|
|||||||
use std::{collections::HashMap, str::FromStr};
|
use std::collections::HashMap;
|
||||||
|
|
||||||
//use log::{LevelFilter, info};
|
use sqlx::{MySqlPool};
|
||||||
use sqlx::{MySqlPool, mysql::{MySqlConnectOptions, MySqlPoolOptions}};
|
|
||||||
|
|
||||||
pub async fn start_database_connection(env_vars: &HashMap<String, String>) -> Result<MySqlPool, sqlx::Error>{
|
pub async fn start_database_connection(env_vars: &HashMap<String, String>) -> Result<MySqlPool, sqlx::Error>{
|
||||||
let db_url = match env_vars.get("DATABASE_URL") {
|
let db_url = match env_vars.get("DATABASE_URL") {
|
||||||
@ -9,14 +8,11 @@ pub async fn start_database_connection(env_vars: &HashMap<String, String>) -> Re
|
|||||||
None => panic!("DATABASE_URL env var not found")
|
None => panic!("DATABASE_URL env var not found")
|
||||||
};
|
};
|
||||||
let formatted_db_url = &db_url;
|
let formatted_db_url = &db_url;
|
||||||
let options = MySqlConnectOptions::from_str(formatted_db_url)?;
|
sqlx::MySqlPool::connect(&formatted_db_url).await
|
||||||
//options.log_slow_statements(LevelFilter::Warn, Duration::from_secs(1))
|
|
||||||
//.log_statements(LevelFilter::Trace);
|
|
||||||
MySqlPoolOptions::new().connect_with(options).await
|
|
||||||
}
|
}
|
||||||
pub async fn run_all_migrations(conn: &MySqlPool){
|
pub async fn run_all_migrations(conn: &MySqlPool){
|
||||||
match sqlx::migrate!("./migrations").run(conn).await {
|
match sqlx::migrate!("./migrations").run(conn).await {
|
||||||
Ok(()) => {println!("Successfully ran migrations.")},
|
Ok(()) => {println!("{}", "Successfully ran migrations.")},
|
||||||
Err(error) => {panic!("{error}")}
|
Err(error) => {panic!("{error}")}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,11 +1,13 @@
|
|||||||
use dev_dtos::domain::user::token::{AUTH_TOKEN_EXPIRATION_TIME_IN_DAYS, Token, REFRESH_TOKEN_EXPIRATION_TIME_IN_DAYS};
|
|
||||||
use sqlx::MySqlPool;
|
use sqlx::MySqlPool;
|
||||||
use sqlx::{mysql::MySqlQueryResult};
|
use sqlx::{mysql::MySqlQueryResult};
|
||||||
|
|
||||||
|
use crate::r#do::token::Token;
|
||||||
|
use crate::r#do::token::{AUTH_TOKEN_EXPIRATION_TIME_IN_DAYS, REFRESH_TOKEN_EXPIRATION_TIME_IN_DAYS};
|
||||||
|
|
||||||
pub async fn insert_token(conn: &MySqlPool, token: &Token) -> Result<MySqlQueryResult, sqlx::Error> {
|
pub async fn insert_token(conn: &MySqlPool, token: &Token) -> Result<MySqlQueryResult, sqlx::Error> {
|
||||||
sqlx::query_file!("sql/schema/token/insert.sql", token.user_id, token.auth_token, token.refresh_token).execute(conn).await
|
sqlx::query_file!("sql/schema/token/insert.sql", token.user_id, token.auth_token, token.refresh_token).execute(conn).await
|
||||||
}
|
}
|
||||||
pub async fn get_tokens_with_user_id(conn: &MySqlPool, user_id: &u32) -> Result<Vec<Token>, sqlx::Error> {
|
pub async fn get_tokens_with_user_id(conn: &MySqlPool, user_id: &i32) -> Result<Vec<Token>, sqlx::Error> {
|
||||||
sqlx::query_file_as!(Token, "sql/schema/token/find_with_user_id.sql", user_id).fetch_all(conn).await
|
sqlx::query_file_as!(Token, "sql/schema/token/find_with_user_id.sql", user_id).fetch_all(conn).await
|
||||||
}
|
}
|
||||||
pub async fn update_token_with_id(conn: &MySqlPool, token: &Token ) -> Result<MySqlQueryResult, sqlx::Error> {
|
pub async fn update_token_with_id(conn: &MySqlPool, token: &Token ) -> Result<MySqlQueryResult, sqlx::Error> {
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
use dev_dtos::domain::user::user::User;
|
|
||||||
use sqlx::{mysql::MySqlQueryResult, MySqlPool};
|
use sqlx::{mysql::MySqlQueryResult, MySqlPool};
|
||||||
|
|
||||||
|
use crate::r#do::user::User;
|
||||||
|
|
||||||
pub async fn insert_user(conn: &MySqlPool, user_to_insert: &User) -> Result<MySqlQueryResult, sqlx::Error>{
|
pub async fn insert_user(conn: &MySqlPool, user_to_insert: &User) -> Result<MySqlQueryResult, sqlx::Error>{
|
||||||
sqlx::query_file!("sql/schema/user/insert.sql",
|
sqlx::query_file!("sql/schema/user/insert.sql",
|
||||||
user_to_insert.app, user_to_insert.credential, user_to_insert.credential_type, user_to_insert.name, user_to_insert.password, user_to_insert.salt)
|
user_to_insert.app, user_to_insert.email, user_to_insert.name, user_to_insert.password, user_to_insert.salt)
|
||||||
.execute(conn).await
|
.execute(conn).await
|
||||||
}
|
}
|
||||||
pub async fn find_user_by_credential(conn: &MySqlPool, credential: &String, credential_type: &String, app: &String) -> Result<User, sqlx::Error>{
|
pub async fn find_user_by_email(conn: &MySqlPool, email: &String, app: &String) -> Result<User, sqlx::Error>{
|
||||||
sqlx::query_file_as!(User, "sql/schema/user/find_with_credential.sql", credential, credential_type, app).fetch_one(conn).await
|
sqlx::query_file_as!(User, "sql/schema/user/find_with_email.sql", email, app).fetch_one(conn).await
|
||||||
}
|
}
|
||||||
pub async fn find_user_by_id(conn: &MySqlPool, id: &u32) -> Result<User, sqlx::Error> {
|
pub async fn find_user_by_id(conn: &MySqlPool, id: &i32) -> Result<User, sqlx::Error> {
|
||||||
sqlx::query_file_as!(User, "sql/schema/user/find_with_id.sql", id).fetch_one(conn).await
|
sqlx::query_file_as!(User, "sql/schema/user/find_with_id.sql", id).fetch_one(conn).await
|
||||||
}
|
}
|
@ -1,67 +0,0 @@
|
|||||||
use std::{future::{ready, Ready}, rc::Rc, io::Read};
|
|
||||||
|
|
||||||
use actix_web::{
|
|
||||||
dev::{self, Service, ServiceRequest, ServiceResponse, Transform},
|
|
||||||
Error, HttpMessage, web::BytesMut,
|
|
||||||
};
|
|
||||||
use futures_util::{future::LocalBoxFuture, StreamExt, Stream};
|
|
||||||
// There are two steps in middleware processing.
|
|
||||||
// 1. Middleware initialization, middleware factory gets called with
|
|
||||||
// next service in chain as parameter.
|
|
||||||
// 2. Middleware's call method gets called with normal request.
|
|
||||||
pub struct BodyLogger;
|
|
||||||
|
|
||||||
// Middleware factory is `Transform` trait from actix-service crate
|
|
||||||
// `S` - type of the next service
|
|
||||||
// `B` - type of response's body
|
|
||||||
impl<S: 'static, B> Transform<S, ServiceRequest> for BodyLogger
|
|
||||||
where
|
|
||||||
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
|
|
||||||
S::Future: 'static,
|
|
||||||
B: 'static,
|
|
||||||
{
|
|
||||||
type Response = ServiceResponse<B>;
|
|
||||||
type Error = Error;
|
|
||||||
type InitError = ();
|
|
||||||
type Transform = BodyLoggerMiddleware<S>;
|
|
||||||
type Future = Ready<Result<Self::Transform, Self::InitError>>;
|
|
||||||
|
|
||||||
fn new_transform(&self, service: S) -> Self::Future {
|
|
||||||
ready(Ok(BodyLoggerMiddleware { service: Rc::new(service) }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct BodyLoggerMiddleware<S> {
|
|
||||||
service: Rc<S>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S, B> Service<ServiceRequest> for BodyLoggerMiddleware<S>
|
|
||||||
where
|
|
||||||
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
|
|
||||||
S::Future: 'static,
|
|
||||||
B: 'static,
|
|
||||||
{
|
|
||||||
type Response = ServiceResponse<B>;
|
|
||||||
type Error = Error;
|
|
||||||
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
|
||||||
|
|
||||||
dev::forward_ready!(service);
|
|
||||||
|
|
||||||
fn call(&self, mut req: ServiceRequest) -> Self::Future {
|
|
||||||
let svc = self.service.clone();
|
|
||||||
Box::pin(async move {
|
|
||||||
|
|
||||||
let mut body = BytesMut::new();
|
|
||||||
let mut stream = req.take_payload();
|
|
||||||
while let Some(chunk) = stream.next().await {
|
|
||||||
body.extend_from_slice(&chunk?);
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("request body: {body:?}");
|
|
||||||
let res = svc.call(req).await?;
|
|
||||||
|
|
||||||
println!("response: {:?}", res.headers());
|
|
||||||
Ok(res)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,2 +1,3 @@
|
|||||||
pub mod shared_state;
|
pub mod shared_state;
|
||||||
//pub mod loggers;
|
pub mod user;
|
||||||
|
pub mod token;
|
31
src/do/token.rs
Normal file
31
src/do/token.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
use chrono::NaiveDateTime;
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
pub const AUTH_TOKEN_EXPIRATION_TIME_IN_DAYS:i32 = 1;
|
||||||
|
pub const REFRESH_TOKEN_EXPIRATION_TIME_IN_DAYS: i32 = 20;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct Token {
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
pub id: i32,
|
||||||
|
pub user_id: i32,
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
pub time_created: Option<NaiveDateTime>,
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
pub last_updated: Option<NaiveDateTime>,
|
||||||
|
pub auth_token: String,
|
||||||
|
pub refresh_token: String
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Token{
|
||||||
|
pub fn new(user_id: i32, auth_token: String, refresh_token: String) -> Token{
|
||||||
|
Token {
|
||||||
|
id: 0,
|
||||||
|
user_id,
|
||||||
|
time_created: None,
|
||||||
|
last_updated: None,
|
||||||
|
auth_token,
|
||||||
|
refresh_token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
42
src/do/user.rs
Normal file
42
src/do/user.rs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
use chrono::{NaiveDateTime};
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
use crate::dto::user_dtos::UserForCreationDto;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct User{
|
||||||
|
pub id: i32,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub time_created: Option<NaiveDateTime>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub last_updated: Option<NaiveDateTime>,
|
||||||
|
pub app: String,
|
||||||
|
pub email: String,
|
||||||
|
pub name: String,
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
pub password: String,
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
pub salt: String
|
||||||
|
}
|
||||||
|
impl User {
|
||||||
|
pub fn _new() -> User {
|
||||||
|
User { id: 0,
|
||||||
|
time_created: None, // This will be automatically generated from the database
|
||||||
|
last_updated: None, // This will be automatically generated from the database
|
||||||
|
app: "".to_string(),
|
||||||
|
email: "".to_string(),
|
||||||
|
name:"".to_string(),
|
||||||
|
password:"".to_string(),
|
||||||
|
salt: "".to_string() }
|
||||||
|
}
|
||||||
|
pub fn new_for_creation(incoming_user: &UserForCreationDto) -> User{
|
||||||
|
User { id: 0,
|
||||||
|
time_created: None, // This will be automatically generated from the database
|
||||||
|
last_updated: None, // This will be automatically generated from the database
|
||||||
|
app: incoming_user.app.to_string(),
|
||||||
|
email: incoming_user.email.to_string(),
|
||||||
|
name: incoming_user.name.to_string(),
|
||||||
|
password: incoming_user.password.to_string(),
|
||||||
|
salt: "".to_string() }
|
||||||
|
}
|
||||||
|
}
|
@ -1,2 +1,3 @@
|
|||||||
|
pub mod user_dtos;
|
||||||
pub mod message_resources_dtos;
|
pub mod message_resources_dtos;
|
||||||
pub mod hash_dtos;
|
pub mod hash_dtos;
|
21
src/dto/user_dtos.rs
Normal file
21
src/dto/user_dtos.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct UserForCreationDto{
|
||||||
|
pub app: String,
|
||||||
|
pub email: String,
|
||||||
|
pub password: String,
|
||||||
|
pub name: String
|
||||||
|
}
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct UserForLoginDto{
|
||||||
|
pub app: String,
|
||||||
|
pub email: String,
|
||||||
|
pub password: String
|
||||||
|
}
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct UserForAuthenticationDto{
|
||||||
|
pub app: String,
|
||||||
|
pub email: String,
|
||||||
|
pub token: String
|
||||||
|
}
|
@ -4,7 +4,6 @@ mod routes; mod service;
|
|||||||
mod util; mod dto;
|
mod util; mod dto;
|
||||||
mod validation; mod resources;
|
mod validation; mod resources;
|
||||||
|
|
||||||
//use actix_web_utils::utils::logger_util::{self};
|
|
||||||
use r#do::shared_state::SharedStateObj;
|
use r#do::shared_state::SharedStateObj;
|
||||||
use util::env_util;
|
use util::env_util;
|
||||||
use routes::main_router::{start_all_routes, after_startup_fn};
|
use routes::main_router::{start_all_routes, after_startup_fn};
|
||||||
@ -12,9 +11,7 @@ use dao::{main_dao::{self, run_all_migrations}};
|
|||||||
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main(){
|
async fn main() -> Result<(), std::io::Error> {
|
||||||
|
|
||||||
//logger_util::init_logger_custom(2).expect("LOGGER FAILED TO LOAD");
|
|
||||||
|
|
||||||
// Retrieve env variables and send to services that need them.
|
// Retrieve env variables and send to services that need them.
|
||||||
let env_vars = env_util::get_dot_env_map();
|
let env_vars = env_util::get_dot_env_map();
|
||||||
@ -32,6 +29,6 @@ async fn main(){
|
|||||||
let shared_state_obj = SharedStateObj {db_conn, env_vars };
|
let shared_state_obj = SharedStateObj {db_conn, env_vars };
|
||||||
|
|
||||||
// Pass shared state to server and start it
|
// Pass shared state to server and start it
|
||||||
start_all_routes(&after_startup_fn, shared_state_obj).await.unwrap();
|
start_all_routes(&after_startup_fn, shared_state_obj).await
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
|
|
||||||
pub const ERROR_INVALID_EMAIL: (&str, &str) = ("ERROR.INVALID_EMAIL", "Invalid email. Needs to be at least 4 characters, at most 254 and correctly formatted.");
|
pub const ERROR_INVALID_EMAIL: (&str, &str) = ("ERROR.INVALID_EMAIL", "Invalid email. Needs to be at least 4 characters, at most 254 and correctly formatted.");
|
||||||
|
|
||||||
pub const ERROR_INVALID_PHONE_NUMBER: (&str, &str) = ("ERROR.INVALID_PHONE_NUMBER", "Invalid Phone number. Needs to be 10 characters.");
|
|
||||||
|
|
||||||
pub const ERROR_INVALID_NAME: (&str, &str) = ("ERROR.INVALID_NAME", "Invalid name. Names should have at least 4 characters in length and at most 254.");
|
pub const ERROR_INVALID_NAME: (&str, &str) = ("ERROR.INVALID_NAME", "Invalid name. Names should have at least 4 characters in length and at most 254.");
|
||||||
|
|
||||||
pub const ERROR_INVALID_PASSWORD: (&str, &str) = ("ERROR.INVALID_PASSWORD", "Invalid password. Password should have at least 8 characters and at most 128.");
|
pub const ERROR_INVALID_PASSWORD: (&str, &str) = ("ERROR.INVALID_PASSWORD", "Invalid password. Password should have at least 8 characters and at most 128.");
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
use std::sync::{Mutex, Arc};
|
use std::sync::{Mutex, Arc};
|
||||||
use actix_web::{HttpServer, App, web, middleware::Logger};
|
use actix_web::{HttpServer, App, web};
|
||||||
use crate::{r#do::shared_state::SharedStateObj};
|
use crate::r#do::shared_state::SharedStateObj;
|
||||||
use super::user_routes;
|
use super::user_routes;
|
||||||
|
|
||||||
|
|
||||||
// This function is to be used in case code is meant to be run after server startup
|
// This function is to be used in case code is meant to be run after server startup
|
||||||
pub fn after_startup_fn() {
|
pub fn after_startup_fn() {
|
||||||
println!("Started server.")
|
println!("{}", "Started server.");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn start_all_routes(after_startup_fn_call: &dyn Fn(), state: SharedStateObj)
|
pub async fn start_all_routes(after_startup_fn_call: &dyn Fn(), state: SharedStateObj)
|
||||||
@ -35,8 +34,6 @@ pub async fn start_all_routes(after_startup_fn_call: &dyn Fn(), state: SharedSta
|
|||||||
let server_future = HttpServer::new( move || {
|
let server_future = HttpServer::new( move || {
|
||||||
App::new()
|
App::new()
|
||||||
// Define routes & pass in shared state
|
// Define routes & pass in shared state
|
||||||
//.wrap(loggers::BodyLogger) This is how you add middleware
|
|
||||||
.wrap(Logger::default())
|
|
||||||
.app_data(db_conn_state.clone())
|
.app_data(db_conn_state.clone())
|
||||||
.app_data(env_vars_state.clone())
|
.app_data(env_vars_state.clone())
|
||||||
.service(user_routes::create_user)
|
.service(user_routes::create_user)
|
||||||
|
@ -2,10 +2,11 @@ use std::{sync::{Arc}};
|
|||||||
|
|
||||||
use actix_web::{web::{self, Data}, HttpResponse, post, patch, HttpRequest};
|
use actix_web::{web::{self, Data}, HttpResponse, post, patch, HttpRequest};
|
||||||
use chrono::{Utc};
|
use chrono::{Utc};
|
||||||
use dev_dtos::{dtos::user::user_dtos::{UserForLoginDto, UserForCreationDto}, domain::user::{user::User, token::{Token, AUTH_TOKEN_EXPIRATION_TIME_IN_DAYS, REFRESH_TOKEN_EXPIRATION_TIME_IN_DAYS}}};
|
|
||||||
use sqlx::{MySqlPool};
|
use sqlx::{MySqlPool};
|
||||||
|
|
||||||
use crate::{dto::message_resources_dtos::MessageResourceDto, resources::error_messages::{ERROR_USER_ALREADY_EXISTS, ERROR_USER_DOES_NOT_EXIST, ERROR_INCORRECT_TOKEN, ERROR_INVALID_TOKEN, ERROR_MISSING_TOKEN, ERROR_EXPIRED_TOKEN, ERROR_CREATING_TOKEN, ERROR_PASSWORD_INCORRECT}, dao::{user_dao::{find_user_by_credential, insert_user, find_user_by_id}, token_dao::{insert_token, update_token_with_id, self}}, validation::user_validator, util::hasher::{self, generate_multiple_random_token_with_rng}};
|
use crate::{r#do::user::User, dao::{user_dao::{insert_user, find_user_by_email, find_user_by_id}, token_dao::{insert_token, self, update_token_with_id}}, dto::{user_dtos::{UserForCreationDto, UserForLoginDto}, message_resources_dtos::MessageResourceDto}, validation::user_validator, util::hasher::{self, generate_multiple_random_token_with_rng}, r#do::token::Token, resources::error_messages::{ERROR_USER_ALREADY_EXISTS, ERROR_USER_DOES_NOT_EXIST, ERROR_PASSWORD_INCORRECT, ERROR_INVALID_TOKEN, ERROR_MISSING_TOKEN, ERROR_INCORRECT_TOKEN, ERROR_EXPIRED_TOKEN, ERROR_CREATING_TOKEN}, r#do::token::AUTH_TOKEN_EXPIRATION_TIME_IN_DAYS, r#do::token::REFRESH_TOKEN_EXPIRATION_TIME_IN_DAYS};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[post("/user")]
|
#[post("/user")]
|
||||||
pub async fn create_user(incoming_user: web::Json<UserForCreationDto>, db_conn: Data<Arc<MySqlPool>>) -> HttpResponse {
|
pub async fn create_user(incoming_user: web::Json<UserForCreationDto>, db_conn: Data<Arc<MySqlPool>>) -> HttpResponse {
|
||||||
@ -23,7 +24,7 @@ pub async fn create_user(incoming_user: web::Json<UserForCreationDto>, db_conn:
|
|||||||
user_validator::validate_user_for_creation(incoming_user_obj, &mut message_resources);
|
user_validator::validate_user_for_creation(incoming_user_obj, &mut message_resources);
|
||||||
|
|
||||||
// Find if user exists
|
// Find if user exists
|
||||||
match find_user_by_credential(&db_conn, &user_to_insert.credential, &user_to_insert.credential_type, &user_to_insert.app).await{
|
match find_user_by_email(&db_conn, &user_to_insert.email, &user_to_insert.app).await{
|
||||||
Ok(_usr) => {
|
Ok(_usr) => {
|
||||||
message_resources.push(MessageResourceDto::new_from_error_message(ERROR_USER_ALREADY_EXISTS));
|
message_resources.push(MessageResourceDto::new_from_error_message(ERROR_USER_ALREADY_EXISTS));
|
||||||
return HttpResponse::BadRequest().json(web::Json(message_resources));
|
return HttpResponse::BadRequest().json(web::Json(message_resources));
|
||||||
@ -42,7 +43,7 @@ pub async fn create_user(incoming_user: web::Json<UserForCreationDto>, db_conn:
|
|||||||
// Insert user in DB
|
// Insert user in DB
|
||||||
match insert_user(&db_conn, &user_to_insert).await{
|
match insert_user(&db_conn, &user_to_insert).await{
|
||||||
Ok(resultrs) => {
|
Ok(resultrs) => {
|
||||||
user_to_insert.id = resultrs.last_insert_id() as u32;
|
user_to_insert.id = resultrs.last_insert_id() as i32;
|
||||||
},
|
},
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
println!("Error while inserting user in database from create_user method. Log: {}", error);
|
println!("Error while inserting user in database from create_user method. Log: {}", error);
|
||||||
@ -59,7 +60,7 @@ pub async fn create_user(incoming_user: web::Json<UserForCreationDto>, db_conn:
|
|||||||
|
|
||||||
// Insert token in DB
|
// Insert token in DB
|
||||||
match insert_token(&db_conn, &token_to_insert).await{
|
match insert_token(&db_conn, &token_to_insert).await{
|
||||||
Ok(resultrs) => {token_to_insert.id = resultrs.last_insert_id() as u32},
|
Ok(resultrs) => {token_to_insert.id = resultrs.last_insert_id() as i32},
|
||||||
Err(_e) => {return HttpResponse::InternalServerError().finish()}
|
Err(_e) => {return HttpResponse::InternalServerError().finish()}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +82,7 @@ pub async fn authenticate_user_with_password(incoming_user: web::Json<UserForLog
|
|||||||
if message_resources.len() > 0 { return HttpResponse::BadRequest().json(web::Json(message_resources)); }
|
if message_resources.len() > 0 { return HttpResponse::BadRequest().json(web::Json(message_resources)); }
|
||||||
|
|
||||||
// If user exists get it, if it doesn't blow up to the client
|
// If user exists get it, if it doesn't blow up to the client
|
||||||
let persisted_user = match find_user_by_credential(&db_conn, &incoming_user_obj.credential, &incoming_user_obj.credential_type.to_string(), &incoming_user_obj.app).await {
|
let persisted_user = match find_user_by_email(&db_conn, &incoming_user_obj.email, &incoming_user_obj.app).await {
|
||||||
Ok(rs) => {rs},
|
Ok(rs) => {rs},
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
message_resources.push(MessageResourceDto::new_from_error_message(ERROR_USER_DOES_NOT_EXIST));
|
message_resources.push(MessageResourceDto::new_from_error_message(ERROR_USER_DOES_NOT_EXIST));
|
||||||
@ -113,7 +114,7 @@ pub async fn authenticate_user_with_password(incoming_user: web::Json<UserForLog
|
|||||||
|
|
||||||
// Insert token in DB
|
// Insert token in DB
|
||||||
match insert_token(&db_conn, &token_to_insert).await{
|
match insert_token(&db_conn, &token_to_insert).await{
|
||||||
Ok(resultrs) => {token_to_insert.id = resultrs.last_insert_id() as u32},
|
Ok(resultrs) => {token_to_insert.id = resultrs.last_insert_id() as i32},
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
message_resources.push(MessageResourceDto::new_from_error_message(ERROR_CREATING_TOKEN));
|
message_resources.push(MessageResourceDto::new_from_error_message(ERROR_CREATING_TOKEN));
|
||||||
return HttpResponse::InternalServerError().json(message_resources)
|
return HttpResponse::InternalServerError().json(message_resources)
|
||||||
@ -126,7 +127,7 @@ pub async fn authenticate_user_with_password(incoming_user: web::Json<UserForLog
|
|||||||
|
|
||||||
|
|
||||||
#[post("/user/auth/token/{user_id}")]
|
#[post("/user/auth/token/{user_id}")]
|
||||||
pub async fn authenticate_user_with_auth_token(request: HttpRequest, user_id: web::Path<u32>, db_conn: Data<Arc<MySqlPool>>) -> HttpResponse{
|
pub async fn authenticate_user_with_auth_token(request: HttpRequest, user_id: web::Path<i32>, db_conn: Data<Arc<MySqlPool>>) -> HttpResponse{
|
||||||
let mut message_resources: Vec<MessageResourceDto> = Vec::new();
|
let mut message_resources: Vec<MessageResourceDto> = Vec::new();
|
||||||
let headers = request.headers();
|
let headers = request.headers();
|
||||||
let auth_token = match headers.get("auth-token") {
|
let auth_token = match headers.get("auth-token") {
|
||||||
@ -181,7 +182,7 @@ pub async fn authenticate_user_with_auth_token(request: HttpRequest, user_id: we
|
|||||||
|
|
||||||
|
|
||||||
#[patch("/user/refresh/{user_id}")]
|
#[patch("/user/refresh/{user_id}")]
|
||||||
pub async fn refresh_auth_token(request: HttpRequest, user_id: web::Path<u32>, db_conn: Data<Arc<MySqlPool>>) -> HttpResponse{
|
pub async fn refresh_auth_token(request: HttpRequest, user_id: web::Path<i32>, db_conn: Data<Arc<MySqlPool>>) -> HttpResponse{
|
||||||
let mut message_resources: Vec<MessageResourceDto> = Vec::new();
|
let mut message_resources: Vec<MessageResourceDto> = Vec::new();
|
||||||
let headers = request.headers();
|
let headers = request.headers();
|
||||||
let refresh_token = match headers.get("refresh-token") {
|
let refresh_token = match headers.get("refresh-token") {
|
||||||
|
@ -1,19 +1,13 @@
|
|||||||
use dev_dtos::{dtos::user::user_dtos::{UserForCreationDto, UserForLoginDto}, domain::user::credential_type::CredentialType};
|
use crate::{
|
||||||
|
dto::{ user_dtos::{UserForCreationDto, UserForLoginDto}, message_resources_dtos::MessageResourceDto },
|
||||||
|
resources::{ variable_lengths::*, error_messages::* }
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{resources::{variable_lengths::{MAX_EMAIL_LENGTH, MIN_EMAIL_LENGTH, MIN_NAME_LENGTH, MAX_NAME_LENGTH, MIN_PASSWORD_LENGTH, MAX_PASSWORD_LENGTH}, error_messages::{ERROR_INVALID_NAME, ERROR_INVALID_EMAIL, ERROR_INVALID_PASSWORD, ERROR_INVALID_PHONE_NUMBER}}, dto::message_resources_dtos::MessageResourceDto};
|
|
||||||
|
|
||||||
|
|
||||||
//TODO: Regex validation for emails
|
|
||||||
fn validate_user_email(email: &String) -> bool {
|
fn validate_user_email(email: &String) -> bool {
|
||||||
email.len() >= MIN_EMAIL_LENGTH.into() &&
|
email.len() >= MIN_EMAIL_LENGTH.into() &&
|
||||||
email.len() <= MAX_EMAIL_LENGTH.into() &&
|
email.len() <= MAX_EMAIL_LENGTH.into() &&
|
||||||
email.contains('@') &&
|
email.contains('@') &&
|
||||||
email.contains('.')
|
email.contains('.')
|
||||||
}
|
|
||||||
//TODO: Regex validation for phone numbers
|
|
||||||
fn validate_user_phone_number(email: &String) -> bool {
|
|
||||||
email.len() >= CredentialType::get_max_length(&CredentialType::PhoneNumber) &&
|
|
||||||
email.len() <= MAX_EMAIL_LENGTH.into()
|
|
||||||
}
|
}
|
||||||
fn validate_user_name(name: &String) -> bool {
|
fn validate_user_name(name: &String) -> bool {
|
||||||
name.len() >= MIN_NAME_LENGTH.into() &&
|
name.len() >= MIN_NAME_LENGTH.into() &&
|
||||||
@ -25,17 +19,9 @@ fn validate_user_password(password: &String) -> bool {
|
|||||||
}
|
}
|
||||||
// User dto SHOULD die here.
|
// User dto SHOULD die here.
|
||||||
pub fn validate_user_for_creation(user: UserForCreationDto, message_resources: &mut Vec<MessageResourceDto>){
|
pub fn validate_user_for_creation(user: UserForCreationDto, message_resources: &mut Vec<MessageResourceDto>){
|
||||||
match user.credential_type {
|
if !validate_user_email(&user.email) {
|
||||||
CredentialType::Email =>
|
message_resources.push(MessageResourceDto::new_from_error_message(ERROR_INVALID_EMAIL));
|
||||||
if !validate_user_email(&user.credential) {
|
|
||||||
message_resources.push(MessageResourceDto::new_from_error_message(ERROR_INVALID_EMAIL));
|
|
||||||
},
|
|
||||||
CredentialType::PhoneNumber =>
|
|
||||||
if !validate_user_phone_number(&user.credential) {
|
|
||||||
message_resources.push(MessageResourceDto::new_from_error_message(ERROR_INVALID_PHONE_NUMBER));
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !validate_user_name(&user.name) {
|
if !validate_user_name(&user.name) {
|
||||||
message_resources.push(MessageResourceDto::new_from_error_message(ERROR_INVALID_NAME));
|
message_resources.push(MessageResourceDto::new_from_error_message(ERROR_INVALID_NAME));
|
||||||
}
|
}
|
||||||
@ -44,15 +30,8 @@ pub fn validate_user_for_creation(user: UserForCreationDto, message_resources: &
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn validate_user_for_password_authentication(user: &UserForLoginDto, message_resources: &mut Vec<MessageResourceDto>){
|
pub fn validate_user_for_password_authentication(user: &UserForLoginDto, message_resources: &mut Vec<MessageResourceDto>){
|
||||||
match user.credential_type {
|
if !validate_user_email(&user.email) {
|
||||||
CredentialType::Email =>
|
message_resources.push(MessageResourceDto::new_from_error_message(ERROR_INVALID_EMAIL));
|
||||||
if !validate_user_email(&user.credential) {
|
|
||||||
message_resources.push(MessageResourceDto::new_from_error_message(ERROR_INVALID_EMAIL));
|
|
||||||
},
|
|
||||||
CredentialType::PhoneNumber =>
|
|
||||||
if !validate_user_phone_number(&user.credential) {
|
|
||||||
message_resources.push(MessageResourceDto::new_from_error_message(ERROR_INVALID_PHONE_NUMBER));
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
if !validate_user_password(&user.password) {
|
if !validate_user_password(&user.password) {
|
||||||
message_resources.push(MessageResourceDto::new_from_error_message(ERROR_INVALID_PASSWORD));
|
message_resources.push(MessageResourceDto::new_from_error_message(ERROR_INVALID_PASSWORD));
|
||||||
|
Loading…
Reference in New Issue
Block a user