bazzar/api/src/routes/admin/mod.rs

194 lines
5.0 KiB
Rust
Raw Normal View History

mod api_v1;
2022-04-18 22:07:52 +02:00
use std::sync::Arc;
2022-04-14 21:40:26 +02:00
use actix::Addr;
use actix_session::Session;
use actix_web::web::{scope, Data, Json, ServiceConfig};
2022-04-14 21:40:26 +02:00
use actix_web::{delete, get, post, HttpResponse};
use serde::{Deserialize, Serialize};
use crate::database::{AccountByIdentity, Database};
use crate::logic::encrypt_password;
2022-04-14 21:40:26 +02:00
use crate::model::{Account, Email, Login, PassHash, Password, PasswordConfirmation, Role};
use crate::routes::{RequireLogin, Result};
use crate::{database, model, routes, Config};
#[macro_export]
macro_rules! admin_send_db {
($db: expr, $msg: expr) => {{
let db = $db;
2022-04-19 16:49:30 +02:00
match db.send($msg).await {
Ok(Ok(res)) => res,
Ok(Err(e)) => {
log::error!("{}", e);
2022-04-19 16:49:30 +02:00
return Err(crate::routes::Error::Admin(Error::Database(e)));
}
Err(e) => {
log::error!("{}", e);
2022-04-19 16:49:30 +02:00
return Err(crate::routes::Error::Admin(Error::DatabaseConnection));
}
2022-04-19 16:49:30 +02:00
}
}};
}
2022-04-14 21:40:26 +02:00
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Can't register new account")]
Register,
#[error("Can't hash password")]
HashPass,
#[error("Internal server error")]
DatabaseConnection,
2022-04-19 16:49:30 +02:00
#[error("Password and password confirmation are different")]
DifferentPasswords,
2022-04-14 21:40:26 +02:00
#[error("{0}")]
Database(#[from] database::Error),
}
#[derive(Serialize)]
pub struct LogoutResponse {}
#[delete("logout")]
2022-04-14 21:40:26 +02:00
async fn logout(session: Session) -> Result<HttpResponse> {
session.require_admin()?;
session.clear();
2022-04-14 21:40:26 +02:00
Ok(HttpResponse::NotImplemented().body(""))
}
#[derive(Deserialize, Debug)]
pub struct SignInInput {
login: Option<Login>,
email: Option<Email>,
password: Password,
}
2022-04-16 20:20:59 +02:00
#[post("/sign-in")]
async fn sign_in(
session: Session,
db: Data<Addr<Database>>,
Json(payload): Json<SignInInput>,
) -> Result<HttpResponse> {
log::debug!("{:?}", payload);
let db = db.into_inner();
2022-04-18 22:07:52 +02:00
let user: model::FullAccount = match db
.send(AccountByIdentity {
email: payload.email,
login: payload.login,
})
.await
{
Ok(Ok(user)) => user,
Ok(Err(e)) => {
log::error!("{}", e);
return Err(routes::Error::Unauthorized);
}
Err(e) => {
log::error!("{}", e);
return Err(routes::Error::Unauthorized);
}
};
if let Err(e) = crate::logic::validate_password(&payload.password, &user.pass_hash) {
log::error!("Password validation failed. {}", e);
Err(routes::Error::Unauthorized)
} else {
if let Err(e) = session.insert("admin_id", *user.id) {
log::error!("{:?}", e);
}
Ok(HttpResponse::Ok().json(model::Account::from(user)))
}
2022-04-14 21:40:26 +02:00
}
#[derive(Deserialize)]
pub struct RegisterInput {
pub login: Login,
pub email: Email,
pub password: Password,
pub password_confirmation: PasswordConfirmation,
pub role: Role,
}
#[derive(Serialize, Default)]
pub struct RegisterResponse {
pub success: bool,
pub errors: Vec<RegisterError>,
pub account: Option<Account>,
}
#[derive(Serialize)]
pub enum RegisterError {
PasswordDiffer,
}
// login_required
2022-04-16 20:20:59 +02:00
#[post("/register")]
2022-04-14 21:40:26 +02:00
async fn register(
session: Session,
Json(input): Json<RegisterInput>,
db: Data<Addr<Database>>,
config: Data<Arc<Config>>,
) -> Result<HttpResponse> {
let mut response = RegisterResponse::default();
session.require_admin()?;
if input.password != input.password_confirmation {
response.errors.push(RegisterError::PasswordDiffer);
}
let hash = match encrypt_password(&input.password, &config.pass_salt) {
2022-04-14 21:40:26 +02:00
Ok(s) => s,
Err(e) => {
log::error!("{e:?}");
return Err(routes::Error::Admin(Error::HashPass));
}
};
match db
.send(database::CreateAccount {
email: input.email,
login: input.login,
2022-04-19 16:49:30 +02:00
pass_hash: PassHash::from(hash),
2022-04-14 21:40:26 +02:00
role: input.role,
})
.await
{
Ok(Ok(account)) => {
response.account = Some(account.into());
}
Ok(Err(e)) => {
log::error!("{}", e);
return Err(super::Error::Admin(Error::Register));
}
Err(e) => {
log::error!("{}", e);
return Err(super::Error::Admin(Error::Register));
}
};
response.success = response.errors.is_empty();
Ok(if response.success {
HttpResponse::Ok().json(response)
2022-04-14 21:40:26 +02:00
} else {
HttpResponse::BadRequest().json(response)
})
}
2022-04-16 20:20:59 +02:00
#[get("")]
2022-04-14 21:40:26 +02:00
async fn landing() -> Result<HttpResponse> {
Ok(HttpResponse::NotImplemented()
.append_header(("Content-Type", "text/html"))
.body(include_str!("../../../assets/index.html")))
2022-04-14 21:40:26 +02:00
}
pub fn configure(config: &mut ServiceConfig) {
2022-04-16 20:20:59 +02:00
config.service(
scope("/admin")
.service(sign_in)
.service(logout)
.service(register)
.service(landing)
.configure(api_v1::configure),
);
2022-04-14 21:40:26 +02:00
}