Add docker build and update to actix-web v3
This commit is contained in:
parent
9b7d28c6fe
commit
1d1cff4f74
5
.dockerignore
Normal file
5
.dockerignore
Normal file
@ -0,0 +1,5 @@
|
||||
target
|
||||
.git
|
||||
.idea
|
||||
.vscode
|
||||
# test.db
|
1519
Cargo.lock
generated
1519
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
46
Cargo.toml
46
Cargo.toml
@ -5,39 +5,39 @@ authors = ['Rohan Sircar <rohansircar@tuta.io>']
|
||||
edition = '2018'
|
||||
|
||||
[dependencies]
|
||||
actix-web = "2.0.0"
|
||||
actix-rt = "1.1.1"
|
||||
actix-service = "1.0.6"
|
||||
actix-files = "0.2.2"
|
||||
bytes = "0.5.6"
|
||||
futures = "0.3.5"
|
||||
log = "0.4.11"
|
||||
env_logger = "0.7.1"
|
||||
serde_json = "1.0.57"
|
||||
actix-web = "3.3.2"
|
||||
# actix-rt = "1.1.1"
|
||||
actix-service = "2.0.0"
|
||||
actix-files = "0.5.0"
|
||||
bytes = "1.0.1"
|
||||
futures = "0.3.14"
|
||||
log = "0.4.14"
|
||||
env_logger = "0.8.3"
|
||||
serde_json = "1.0.64"
|
||||
json = "0.12.4"
|
||||
listenfd = "0.3.3"
|
||||
dotenv = "0.15.0"
|
||||
r2d2 = "0.8.9"
|
||||
validator = "0.10.1"
|
||||
validator_derive = "0.10.1"
|
||||
validator = "0.13.0"
|
||||
validator_derive = "0.13.0"
|
||||
jsonwebtoken = "7.2.0"
|
||||
actix-identity = "0.2.1"
|
||||
actix-web-httpauth = "0.4.2"
|
||||
rand = "0.7.3"
|
||||
nanoid = "0.3.0"
|
||||
bcrypt = "0.8.2"
|
||||
timeago = "0.2.1"
|
||||
actix-identity = "0.3.1"
|
||||
actix-web-httpauth = "0.5.1"
|
||||
rand = "0.8.3"
|
||||
nanoid = "0.4.0"
|
||||
bcrypt = "0.9.0"
|
||||
timeago = "0.3.0"
|
||||
comp = "0.2.1"
|
||||
regex = "1.3.9"
|
||||
regex = "1.4.5"
|
||||
lazy_static = "1.4.0"
|
||||
lazy-regex = "0.1.4"
|
||||
custom_error = "1.7.1"
|
||||
derive-new = "0.5.8"
|
||||
custom_error = "1.9.2"
|
||||
derive-new = "0.5.9"
|
||||
diesel_migrations = "1.4.0"
|
||||
actix-threadpool = "0.3.3"
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0.115"
|
||||
version = "1.0.125"
|
||||
features = ['derive']
|
||||
|
||||
# [dependencies.yarte]
|
||||
@ -53,7 +53,7 @@ features = [
|
||||
]
|
||||
|
||||
[dependencies.uuid]
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
features = [
|
||||
'serde',
|
||||
'v4',
|
||||
@ -64,7 +64,7 @@ version = "0.23.1"
|
||||
features = ['bundled']
|
||||
|
||||
[dependencies.chrono]
|
||||
version = "0.4.15"
|
||||
version = "0.4.19"
|
||||
features = ['serde']
|
||||
# [build-dependencies.yarte_helpers]
|
||||
# version = '0.9.0'
|
||||
|
46
Dockerfile
Normal file
46
Dockerfile
Normal file
@ -0,0 +1,46 @@
|
||||
FROM rust:1.45 as builder
|
||||
|
||||
# ENV CARGO_HOME=/actix-demo/.cargo
|
||||
RUN USER=root cargo new --bin actix-demo
|
||||
WORKDIR /actix-demo
|
||||
# COPY ./.cargo ./.cargo
|
||||
COPY ./Cargo.toml ./Cargo.toml
|
||||
COPY ./Cargo.lock ./Cargo.lock
|
||||
RUN cargo build --release
|
||||
RUN rm src/*.rs
|
||||
|
||||
COPY ./src ./src
|
||||
|
||||
#RUN rm ./target/release/deps/actix-demo*
|
||||
RUN cargo build --release
|
||||
|
||||
|
||||
|
||||
FROM debian:buster-slim
|
||||
ARG APP=/usr/src/app
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y ca-certificates tzdata \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
ENV TZ=Etc/UTC \
|
||||
APP_USER=appuser
|
||||
|
||||
RUN groupadd $APP_USER \
|
||||
&& useradd -g $APP_USER $APP_USER \
|
||||
&& mkdir -p ${APP}
|
||||
|
||||
COPY ./.env ${APP}/.env
|
||||
COPY ./migrations ${APP}/migrations
|
||||
COPY ./static ${APP}/static
|
||||
COPY ./test.db ${APP}/test.db
|
||||
COPY --from=builder /actix-demo/target/release/actix-demo ${APP}/actix-demo
|
||||
|
||||
RUN chown -R $APP_USER:$APP_USER ${APP}
|
||||
|
||||
USER $APP_USER
|
||||
WORKDIR ${APP}
|
||||
|
||||
CMD ["./actix-demo"]
|
@ -43,19 +43,20 @@ pub fn get_all(
|
||||
|
||||
/// Run query using Diesel to insert a new database row and return the result.
|
||||
pub fn insert_new_user(
|
||||
mut nu: Rc<models::NewUser>,
|
||||
nu: models::NewUser,
|
||||
conn: &SqliteConnection,
|
||||
) -> Result<models::UserDTO, errors::DomainError> {
|
||||
// It is common when using Diesel with Actix web to import schema-related
|
||||
// modules inside a function's scope (rather than the normal module's scope)
|
||||
// to prevent import collisions and namespace pollution.
|
||||
use crate::schema::users::dsl::*;
|
||||
let mut nu2 = Rc::make_mut(&mut nu);
|
||||
nu2.password = hash(&nu2.password, DEFAULT_COST)?;
|
||||
let nu = {
|
||||
let mut nu2 = nu.clone();
|
||||
nu2.password = hash(&nu2.password, DEFAULT_COST)?;
|
||||
nu2
|
||||
};
|
||||
|
||||
diesel::insert_into(users)
|
||||
.values(nu.as_ref())
|
||||
.execute(conn)?;
|
||||
diesel::insert_into(users).values(&nu).execute(conn)?;
|
||||
let user =
|
||||
query::_get_user_by_name(&nu.name).first::<models::UserDTO>(conn)?;
|
||||
Ok(user)
|
||||
|
27
src/main.rs
Normal file → Executable file
27
src/main.rs
Normal file → Executable file
@ -7,14 +7,14 @@ extern crate custom_error;
|
||||
extern crate regex;
|
||||
extern crate validator;
|
||||
|
||||
use actix_web::{middleware, web, App, HttpServer, cookie::SameSite};
|
||||
use actix_web::{cookie::SameSite, middleware, web, App, HttpServer};
|
||||
|
||||
use actix_web_httpauth::middleware::HttpAuthentication;
|
||||
|
||||
use actix_files as fs;
|
||||
use actix_identity::{CookieIdentityPolicy, IdentityService};
|
||||
use rand::Rng;
|
||||
|
||||
use actix_files as fs;
|
||||
use services::UserServiceImpl;
|
||||
|
||||
use diesel::prelude::*;
|
||||
use diesel::r2d2::{self, ConnectionManager};
|
||||
@ -27,6 +27,7 @@ mod middlewares;
|
||||
mod models;
|
||||
mod routes;
|
||||
mod schema;
|
||||
mod services;
|
||||
mod types;
|
||||
mod utils;
|
||||
|
||||
@ -39,14 +40,14 @@ pub struct AppConfig {
|
||||
pool: DbPool,
|
||||
}
|
||||
|
||||
#[actix_rt::main]
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
std::env::set_var("RUST_LOG", "debug");
|
||||
env_logger::init();
|
||||
dotenv::dotenv().ok();
|
||||
|
||||
let basic_auth_middleware =
|
||||
HttpAuthentication::basic(utils::auth::validator);
|
||||
// let _basic_auth_middleware =
|
||||
// HttpAuthentication::basic(utils::auth::validator);
|
||||
|
||||
// set up database connection pool
|
||||
let connspec =
|
||||
@ -67,7 +68,14 @@ async fn main() -> std::io::Result<()> {
|
||||
8
|
||||
});
|
||||
|
||||
let config: AppConfig = AppConfig { pool, hash_cost };
|
||||
let config: AppConfig = AppConfig {
|
||||
pool: pool.clone(),
|
||||
hash_cost,
|
||||
};
|
||||
|
||||
// let user_controller = UserController {
|
||||
// user_service: &user_service,
|
||||
// };
|
||||
|
||||
let addr = std::env::var("BIND_ADDRESS").expect("BIND ADDRESS NOT FOUND");
|
||||
info!("Starting server {}", addr);
|
||||
@ -79,15 +87,16 @@ async fn main() -> std::io::Result<()> {
|
||||
CookieIdentityPolicy::new(&private_key)
|
||||
.name("my-app-auth")
|
||||
.secure(false)
|
||||
.same_site(SameSite::Lax)
|
||||
.same_site(SameSite::Lax),
|
||||
))
|
||||
.wrap(middleware::Logger::default())
|
||||
.service(
|
||||
web::scope("/api/authzd") // endpoint requiring authentication
|
||||
.wrap(basic_auth_middleware.clone())
|
||||
// .wrap(_basic_auth_middleware.clone())
|
||||
.service(routes::users::get_user)
|
||||
.service(routes::users::get_all_users),
|
||||
)
|
||||
// .route("/api/users/get", web::get().to(user_controller.get_user.into()))
|
||||
.service(web::scope("/api/public")) // public endpoint - not implemented yet
|
||||
.service(routes::auth::login)
|
||||
.service(routes::auth::logout)
|
||||
|
@ -68,7 +68,7 @@ pub async fn index(id: Identity) -> String {
|
||||
/// basic auth middleware function
|
||||
pub fn validate_basic_auth(
|
||||
credentials: BasicAuth,
|
||||
config: &web::Data<AppConfig>,
|
||||
config: &AppConfig,
|
||||
) -> Result<bool, errors::DomainError> {
|
||||
let result = if let Some(password_ref) = credentials.password() {
|
||||
let pool = &config.pool;
|
||||
|
@ -1,10 +1,10 @@
|
||||
use actix_web::{get, post, web, HttpResponse};
|
||||
|
||||
use crate::actions;
|
||||
use crate::models;
|
||||
use crate::errors::DomainError;
|
||||
use crate::services::UserService;
|
||||
use crate::AppConfig;
|
||||
use crate::{actions, models};
|
||||
use actix_web::error::ResponseError;
|
||||
use std::rc::Rc;
|
||||
use validator::Validate;
|
||||
|
||||
/// Finds user by UID.
|
||||
@ -12,7 +12,7 @@ use validator::Validate;
|
||||
pub async fn get_user(
|
||||
config: web::Data<AppConfig>,
|
||||
user_id: web::Path<i32>,
|
||||
) -> Result<HttpResponse, impl ResponseError> {
|
||||
) -> Result<HttpResponse, DomainError> {
|
||||
let u_id = user_id.into_inner();
|
||||
// use web::block to offload blocking Diesel code without blocking server thread
|
||||
let res = web::block(move || {
|
||||
@ -21,16 +21,36 @@ pub async fn get_user(
|
||||
actions::find_user_by_uid(u_id, &conn)
|
||||
})
|
||||
.await
|
||||
.and_then(|maybe_user| {
|
||||
if let Some(user) = maybe_user {
|
||||
Ok(HttpResponse::Ok().json(user))
|
||||
} else {
|
||||
let res = HttpResponse::NotFound()
|
||||
.body(format!("No user found with uid: {}", u_id));
|
||||
Ok(res)
|
||||
}
|
||||
});
|
||||
res
|
||||
.map_err(|err| {
|
||||
let res = DomainError::new_generic_error(format!(
|
||||
"No user found with uid: {}",
|
||||
u_id
|
||||
));
|
||||
res
|
||||
})?;
|
||||
if let Some(user) = res {
|
||||
Ok(HttpResponse::Ok().json(user))
|
||||
} else {
|
||||
let res = HttpResponse::NotFound()
|
||||
.body(format!("No user found with uid: {}", u_id));
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/get/users/{user_id}")]
|
||||
pub async fn get_user2(
|
||||
user_service: web::Data<dyn UserService>,
|
||||
user_id: web::Path<i32>,
|
||||
) -> Result<HttpResponse, DomainError> {
|
||||
let u_id = user_id.into_inner();
|
||||
let user = user_service.find_user_by_uid(u_id)?;
|
||||
if let Some(user) = user {
|
||||
Ok(HttpResponse::Ok().json(user))
|
||||
} else {
|
||||
let res = HttpResponse::NotFound()
|
||||
.body(format!("No user found with uid: {}", u_id));
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/get/users")]
|
||||
@ -75,7 +95,7 @@ pub async fn add_user(
|
||||
Ok(_) => web::block(move || {
|
||||
let pool = &config.pool;
|
||||
let conn = pool.get()?;
|
||||
actions::insert_new_user(Rc::new(form.0), &conn)
|
||||
actions::insert_new_user(form.0, &conn)
|
||||
})
|
||||
.await
|
||||
.and_then(|user| {
|
||||
|
2
src/services.rs
Normal file
2
src/services.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod user_service;
|
||||
pub use self::user_service::*;
|
84
src/services/user_service.rs
Normal file
84
src/services/user_service.rs
Normal file
@ -0,0 +1,84 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use diesel::SqliteConnection;
|
||||
|
||||
use crate::{actions, errors, models, types::DbPool};
|
||||
|
||||
pub trait UserService {
|
||||
fn find_user_by_uid(
|
||||
&self,
|
||||
uid: i32,
|
||||
) -> Result<Option<models::UserDTO>, errors::DomainError>;
|
||||
fn _find_user_by_name(
|
||||
&self,
|
||||
user_name: String,
|
||||
) -> Result<Option<models::UserDTO>, errors::DomainError>;
|
||||
|
||||
fn get_all(
|
||||
&self,
|
||||
) -> Result<Option<Vec<models::UserDTO>>, errors::DomainError>;
|
||||
|
||||
fn insert_new_user(
|
||||
&self,
|
||||
nu: models::NewUser,
|
||||
) -> Result<models::UserDTO, errors::DomainError>;
|
||||
|
||||
// fn woot(&self) -> i32;
|
||||
|
||||
fn verify_password<'a>(
|
||||
&self,
|
||||
user_name: &'a String,
|
||||
given_password: &'a String,
|
||||
) -> Result<bool, errors::DomainError>;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UserServiceImpl {
|
||||
pub pool: DbPool,
|
||||
}
|
||||
|
||||
impl UserService for UserServiceImpl {
|
||||
fn find_user_by_uid(
|
||||
&self,
|
||||
uid: i32,
|
||||
) -> Result<Option<models::UserDTO>, errors::DomainError> {
|
||||
let conn = self.pool.get()?;
|
||||
actions::find_user_by_uid(uid, &conn)
|
||||
}
|
||||
|
||||
fn _find_user_by_name(
|
||||
&self,
|
||||
user_name: String,
|
||||
) -> Result<Option<models::UserDTO>, errors::DomainError> {
|
||||
let conn = self.pool.get()?;
|
||||
actions::_find_user_by_name(user_name, &conn)
|
||||
}
|
||||
|
||||
fn get_all(
|
||||
&self,
|
||||
) -> Result<Option<Vec<models::UserDTO>>, errors::DomainError> {
|
||||
let conn = self.pool.get()?;
|
||||
actions::get_all(&conn)
|
||||
}
|
||||
|
||||
fn insert_new_user(
|
||||
&self,
|
||||
nu: models::NewUser,
|
||||
) -> Result<models::UserDTO, errors::DomainError> {
|
||||
let conn = self.pool.get()?;
|
||||
actions::insert_new_user(nu, &conn)
|
||||
}
|
||||
|
||||
fn verify_password<'b>(
|
||||
&self,
|
||||
user_name: &'b String,
|
||||
given_password: &'b String,
|
||||
) -> Result<bool, errors::DomainError> {
|
||||
let conn = self.pool.get()?;
|
||||
actions::verify_password(user_name, given_password, &conn)
|
||||
}
|
||||
|
||||
// async fn woot(&self) -> i32 {
|
||||
// 1
|
||||
// }
|
||||
}
|
43
src/utils/auth.rs
Normal file → Executable file
43
src/utils/auth.rs
Normal file → Executable file
@ -1,28 +1,31 @@
|
||||
use actix_web_httpauth::extractors::basic::BasicAuth;
|
||||
|
||||
use crate::AppConfig;
|
||||
use crate::routes::validate_basic_auth;
|
||||
use crate::AppConfig;
|
||||
|
||||
use actix_web::{dev::ServiceRequest, web, Error};
|
||||
|
||||
// pub async fn validator(
|
||||
// req: ServiceRequest,
|
||||
// credentials: BasicAuth,
|
||||
// ) -> Result<ServiceRequest, Error> {
|
||||
// println!("{}", credentials.user_id());
|
||||
// println!("{:?}", credentials.password());
|
||||
// // verify credentials from db
|
||||
// let config = req.app_data::<AppConfig>().expect("Error getting config");
|
||||
|
||||
pub async fn validator(
|
||||
req: ServiceRequest,
|
||||
credentials: BasicAuth,
|
||||
) -> Result<ServiceRequest, Error> {
|
||||
println!("{}", credentials.user_id());
|
||||
println!("{:?}", credentials.password());
|
||||
// verify credentials from db
|
||||
let config = req.app_data::<AppConfig>().expect("Error getting config");
|
||||
// let valid =
|
||||
// web::block(move || validate_basic_auth(credentials, config)).await?;
|
||||
// if valid {
|
||||
// debug!("Success");
|
||||
// Ok(req)
|
||||
// } else {
|
||||
// println!("blah");
|
||||
// let err: Error = crate::errors::DomainError::new_password_error(
|
||||
// "Wrong password or account does not exist".to_string(),
|
||||
// )
|
||||
// .into();
|
||||
|
||||
let valid =
|
||||
web::block(move || validate_basic_auth(credentials, &config)).await?;
|
||||
if valid {
|
||||
debug!("Success");
|
||||
Ok(req)
|
||||
} else {
|
||||
Err(crate::errors::DomainError::new_password_error(
|
||||
"Wrong password or account does not exist".to_string(),
|
||||
).into())
|
||||
}
|
||||
}
|
||||
// Err(err)
|
||||
// }
|
||||
// }
|
||||
|
Loading…
Reference in New Issue
Block a user