Basic create user account
This commit is contained in:
parent
9912428fd6
commit
0bfdc305b1
@ -33,5 +33,5 @@ diesel migation run
|
||||
> Note: You can access the database directly using this command:
|
||||
>
|
||||
> ```bash
|
||||
> PGPASSWORD=pass psql -h localhost -p 5432 -U user -d bioclimsol
|
||||
> PGPASSWORD=pass psql -h localhost -p 5432 -U user -d geneit
|
||||
> ```
|
32
geneit_backend/Cargo.lock
generated
32
geneit_backend/Cargo.lock
generated
@ -68,6 +68,17 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-remote-ip"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7629b357d4705cf3f1e31f989f48ecd56027112f7d52dcf06dd96ee197065f8e"
|
||||
dependencies = [
|
||||
"actix-web",
|
||||
"futures-util",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-router"
|
||||
version = "0.5.1"
|
||||
@ -285,6 +296,12 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
@ -584,6 +601,17 @@ version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.28"
|
||||
@ -603,16 +631,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-macro",
|
||||
"futures-task",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "geneit_backend"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"actix-remote-ip",
|
||||
"actix-web",
|
||||
"anyhow",
|
||||
"clap",
|
||||
"diesel",
|
||||
"env_logger",
|
||||
|
@ -10,6 +10,8 @@ log = "0.4.17"
|
||||
env_logger = "0.10.0"
|
||||
clap = { version = "4.3.0", features = ["derive", "env"] }
|
||||
lazy_static = "1.4.0"
|
||||
anyhow = "1.0.71"
|
||||
actix-web = "4.3.1"
|
||||
diesel = { version = "2.0.4", features = ["postgres"] }
|
||||
serde = { version = "1.0.163", features = ["derive"] }
|
||||
serde = { version = "1.0.163", features = ["derive"] }
|
||||
actix-remote-ip = "0.1.0"
|
@ -16,17 +16,25 @@ pub struct AppConfig {
|
||||
#[clap(short, long, env)]
|
||||
pub proxy_ip: Option<String>,
|
||||
|
||||
/// PostgreSQL connexion chain
|
||||
#[clap(long, env, default_value = "postgres://localhost/geneit")]
|
||||
db_chain: String,
|
||||
/// PostgreSQL database host
|
||||
#[clap(long, env, default_value = "localhost")]
|
||||
db_host: String,
|
||||
|
||||
/// PostgreSQL database port
|
||||
#[clap(long, env, default_value_t = 5432)]
|
||||
db_port: u16,
|
||||
|
||||
/// PostgreSQL username
|
||||
#[clap(long, env, default_value = "user")]
|
||||
db_username: String,
|
||||
|
||||
/// PostgreSQL password
|
||||
#[clap(long, env, default_value = "user")]
|
||||
#[clap(long, env, default_value = "pass")]
|
||||
db_password: String,
|
||||
|
||||
/// PostgreSQL database name
|
||||
#[clap(long, env, default_value = "geneit")]
|
||||
db_name: String,
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
@ -40,4 +48,12 @@ impl AppConfig {
|
||||
pub fn get() -> &'static AppConfig {
|
||||
&ARGS
|
||||
}
|
||||
}
|
||||
|
||||
/// Get full db connection chain
|
||||
pub fn db_connection_chain(&self) -> String {
|
||||
format!(
|
||||
"postgres://{}:{}@{}:{}/{}",
|
||||
self.db_username, self.db_password, self.db_host, self.db_port, self.db_name
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,11 @@ impl SizeConstraint {
|
||||
pub fn new(min: usize, max: usize) -> Self {
|
||||
Self { min, max }
|
||||
}
|
||||
|
||||
pub fn validate(&self, val: &str) -> bool {
|
||||
let len = val.trim().len();
|
||||
len >= self.min && len <= self.max
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize)]
|
||||
|
@ -1 +1,4 @@
|
||||
//! # API controller
|
||||
|
||||
pub mod auth_controller;
|
||||
pub mod config_controller;
|
||||
|
43
geneit_backend/src/controllers/auth_controller.rs
Normal file
43
geneit_backend/src/controllers/auth_controller.rs
Normal file
@ -0,0 +1,43 @@
|
||||
use crate::constants::StaticConstraints;
|
||||
use crate::services::users_service;
|
||||
use actix_remote_ip::RemoteIP;
|
||||
use actix_web::error::ErrorInternalServerError;
|
||||
use actix_web::{web, HttpResponse};
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
pub struct CreateAccountBody {
|
||||
name: String,
|
||||
email: String,
|
||||
}
|
||||
|
||||
/// Create a new account
|
||||
pub async fn create_account(
|
||||
_remote_ip: RemoteIP,
|
||||
req: web::Json<CreateAccountBody>,
|
||||
) -> actix_web::Result<HttpResponse> {
|
||||
// TODO : rate limiting
|
||||
|
||||
// TODO : check if email is valid
|
||||
|
||||
// Check parameters
|
||||
let constraints = StaticConstraints::default();
|
||||
if !constraints.user_name_len.validate(&req.name) || !constraints.mail_len.validate(&req.email)
|
||||
{
|
||||
return Ok(HttpResponse::BadRequest().json("Size constraints were not respected!"));
|
||||
}
|
||||
|
||||
// TODO : check the mail address
|
||||
|
||||
// Create the account
|
||||
let user_id = users_service::create_account(&req.name, &req.email)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
log::error!("Failed to create user! {e}");
|
||||
ErrorInternalServerError(e)
|
||||
})?;
|
||||
|
||||
// TODO : trigger reset password (send mail)
|
||||
|
||||
// Account successfully created
|
||||
Ok(HttpResponse::Created().finish())
|
||||
}
|
25
geneit_backend/src/db_connection.rs
Normal file
25
geneit_backend/src/db_connection.rs
Normal file
@ -0,0 +1,25 @@
|
||||
//! # Database connection management
|
||||
|
||||
use crate::app_config::AppConfig;
|
||||
use diesel::{Connection, PgConnection};
|
||||
use std::cell::RefCell;
|
||||
thread_local! {
|
||||
static POSTGRES_CONNECTION: RefCell<Option<PgConnection>> = RefCell::new(None);
|
||||
}
|
||||
|
||||
/// Execute a request on the database
|
||||
pub fn execute<E, I>(cb: E) -> anyhow::Result<I>
|
||||
where
|
||||
E: FnOnce(&mut PgConnection) -> anyhow::Result<I>,
|
||||
{
|
||||
// Establish connection if required
|
||||
if POSTGRES_CONNECTION.with(|i| i.borrow().is_none()) {
|
||||
let database_url = AppConfig::get().db_connection_chain();
|
||||
let conn = PgConnection::establish(&database_url)
|
||||
.unwrap_or_else(|_| panic!("Error connecting to {}", database_url));
|
||||
|
||||
POSTGRES_CONNECTION.with(|i| *i.borrow_mut() = Some(conn))
|
||||
}
|
||||
|
||||
POSTGRES_CONNECTION.with(|i| cb(i.borrow_mut().as_mut().unwrap()))
|
||||
}
|
@ -1,4 +1,10 @@
|
||||
pub mod app_config;
|
||||
pub mod constants;
|
||||
pub mod controllers;
|
||||
pub mod services;
|
||||
pub mod utils;
|
||||
|
||||
// Diesel specific
|
||||
pub mod db_connection;
|
||||
pub mod models;
|
||||
pub mod schema;
|
||||
|
@ -1,6 +1,8 @@
|
||||
use actix_remote_ip::RemoteIPConfig;
|
||||
use actix_web::middleware::Logger;
|
||||
use actix_web::{web, App, HttpServer};
|
||||
use geneit_backend::app_config::AppConfig;
|
||||
use geneit_backend::controllers::config_controller;
|
||||
use geneit_backend::controllers::{auth_controller, config_controller};
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
@ -10,12 +12,21 @@ async fn main() -> std::io::Result<()> {
|
||||
|
||||
HttpServer::new(|| {
|
||||
App::new()
|
||||
.wrap(Logger::default())
|
||||
.app_data(web::Data::new(RemoteIPConfig {
|
||||
proxy: AppConfig::get().proxy_ip.clone(),
|
||||
}))
|
||||
// Config controller
|
||||
.route("/", web::get().to(config_controller::home))
|
||||
.route(
|
||||
"/config/static",
|
||||
web::get().to(config_controller::static_config),
|
||||
)
|
||||
// Auth controller
|
||||
.route(
|
||||
"/auth/create_account",
|
||||
web::post().to(auth_controller::create_account),
|
||||
)
|
||||
})
|
||||
.bind(AppConfig::get().listen_address.as_str())?
|
||||
.run()
|
||||
|
34
geneit_backend/src/models.rs
Normal file
34
geneit_backend/src/models.rs
Normal file
@ -0,0 +1,34 @@
|
||||
use crate::schema::users;
|
||||
use diesel::prelude::*;
|
||||
|
||||
/// User ID holder
|
||||
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
|
||||
pub struct UserID(i32);
|
||||
|
||||
#[derive(Queryable, Debug)]
|
||||
pub struct User {
|
||||
id: i32,
|
||||
name: String,
|
||||
email: String,
|
||||
password: Option<String>,
|
||||
reset_password_token: Option<String>,
|
||||
time_create: i64,
|
||||
time_gen_reset_token: i64,
|
||||
time_activate: i64,
|
||||
active: bool,
|
||||
admin: bool,
|
||||
}
|
||||
|
||||
impl User {
|
||||
pub fn id(&self) -> UserID {
|
||||
UserID(self.id)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Insertable)]
|
||||
#[diesel(table_name = users)]
|
||||
pub struct NewUser<'a> {
|
||||
pub name: &'a str,
|
||||
pub email: &'a str,
|
||||
pub time_create: i64,
|
||||
}
|
3
geneit_backend/src/services/mod.rs
Normal file
3
geneit_backend/src/services/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
//! # Backend services
|
||||
|
||||
pub mod users_service;
|
22
geneit_backend/src/services/users_service.rs
Normal file
22
geneit_backend/src/services/users_service.rs
Normal file
@ -0,0 +1,22 @@
|
||||
//! # Users service
|
||||
|
||||
use crate::db_connection;
|
||||
use crate::models::{NewUser, User};
|
||||
use crate::schema::users;
|
||||
use crate::utils::time_utils::time;
|
||||
use diesel::prelude::*;
|
||||
|
||||
/// Create a new account
|
||||
pub async fn create_account(name: &str, email: &str) -> anyhow::Result<User> {
|
||||
db_connection::execute(|conn| {
|
||||
let res = diesel::insert_into(users::table)
|
||||
.values(&NewUser {
|
||||
name: name.trim(),
|
||||
email: email.trim(),
|
||||
time_create: time() as i64,
|
||||
})
|
||||
.get_result(conn)?;
|
||||
|
||||
Ok(res)
|
||||
})
|
||||
}
|
3
geneit_backend/src/utils.rs
Normal file
3
geneit_backend/src/utils.rs
Normal file
@ -0,0 +1,3 @@
|
||||
//! # App utilities
|
||||
|
||||
pub mod time_utils;
|
11
geneit_backend/src/utils/time_utils.rs
Normal file
11
geneit_backend/src/utils/time_utils.rs
Normal file
@ -0,0 +1,11 @@
|
||||
//! # Time utilities
|
||||
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
/// Get the current time since epoch
|
||||
pub fn time() -> u64 {
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs()
|
||||
}
|
Loading…
Reference in New Issue
Block a user