mirror of
https://github.com/danbulant/Portfolio
synced 2026-05-21 21:38:43 +00:00
Merge pull request #35 from EETagent/portfolio_file_upload
Portfolio file upload
This commit is contained in:
commit
9c068dece9
5 changed files with 131 additions and 41 deletions
|
|
@ -3,7 +3,7 @@ use rocket::http::{ContentType, Status};
|
|||
use rocket::outcome::Outcome;
|
||||
use rocket::request::Request;
|
||||
|
||||
struct Letter(Vec<u8>);
|
||||
pub struct Letter(Vec<u8>);
|
||||
|
||||
impl Into<Vec<u8>> for Letter {
|
||||
fn into(self) -> Vec<u8> {
|
||||
|
|
@ -16,9 +16,7 @@ impl<'r> FromData<'r> for Letter {
|
|||
type Error = Option<String>;
|
||||
|
||||
async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> data::Outcome<'r, Self> {
|
||||
let content_type_pdf = ContentType::new("application", "application/pdf");
|
||||
|
||||
if req.content_type() != Some(&content_type_pdf) {
|
||||
if req.content_type() != Some(&ContentType::PDF) {
|
||||
return Outcome::Failure((Status::BadRequest, None))
|
||||
}
|
||||
|
||||
|
|
@ -28,6 +26,7 @@ impl<'r> FromData<'r> for Letter {
|
|||
|
||||
if !data_bytes.is_complete() {
|
||||
// TODO: Over limit
|
||||
return Outcome::Failure((Status::BadRequest, None))
|
||||
}
|
||||
|
||||
let data_bytes = data_bytes.into_inner();
|
||||
|
|
@ -36,6 +35,7 @@ impl<'r> FromData<'r> for Letter {
|
|||
|
||||
if !is_pdf {
|
||||
// TODO: Not PDF
|
||||
return Outcome::Failure((Status::BadRequest, None))
|
||||
}
|
||||
|
||||
Outcome::Success(Letter(data_bytes))
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use rocket::http::{ContentType, Status};
|
|||
use rocket::outcome::Outcome;
|
||||
use rocket::request::Request;
|
||||
|
||||
struct Portfolio(Vec<u8>);
|
||||
pub struct Portfolio(Vec<u8>);
|
||||
|
||||
impl Into<Vec<u8>> for Portfolio {
|
||||
fn into(self) -> Vec<u8> {
|
||||
|
|
@ -16,9 +16,7 @@ impl<'r> FromData<'r> for Portfolio {
|
|||
type Error = Option<String>;
|
||||
|
||||
async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> data::Outcome<'r, Self> {
|
||||
let content_type_zip = ContentType::new("application", "application/zip");
|
||||
|
||||
if req.content_type() != Some(&content_type_zip) {
|
||||
if req.content_type() != Some(&ContentType::ZIP) {
|
||||
return Outcome::Failure((Status::BadRequest, None))
|
||||
}
|
||||
|
||||
|
|
@ -28,6 +26,7 @@ impl<'r> FromData<'r> for Portfolio {
|
|||
|
||||
if !data_bytes.is_complete() {
|
||||
// TODO: Over limit
|
||||
return Outcome::Failure((Status::BadRequest, None))
|
||||
}
|
||||
|
||||
let data_bytes = data_bytes.into_inner();
|
||||
|
|
@ -36,6 +35,7 @@ impl<'r> FromData<'r> for Portfolio {
|
|||
|
||||
if !is_zip {
|
||||
// TODO: Not ZIP
|
||||
return Outcome::Failure((Status::BadRequest, None))
|
||||
}
|
||||
|
||||
Outcome::Success(Portfolio(data_bytes))
|
||||
|
|
|
|||
|
|
@ -42,6 +42,9 @@ async fn start() -> Result<(), rocket::Error> {
|
|||
routes::candidate::login,
|
||||
routes::candidate::whoami,
|
||||
routes::candidate::fill_details,
|
||||
routes::candidate::upload_cover_letter,
|
||||
routes::candidate::upload_portfolio_letter,
|
||||
routes::candidate::upload_portfolio_zip,
|
||||
],
|
||||
)
|
||||
.mount(
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ use rocket::serde::json::Json;
|
|||
|
||||
use sea_orm_rocket::Connection;
|
||||
|
||||
use crate::guards::data::letter::Letter;
|
||||
use crate::guards::data::portfolio::Portfolio;
|
||||
use crate::{guards::request::auth::CandidateAuth, pool::Db, requests};
|
||||
|
||||
#[post("/login", data = "<login_form>")]
|
||||
|
|
@ -47,7 +49,7 @@ pub async fn whoami(session: CandidateAuth) -> Result<String, Custom<String>> {
|
|||
Ok(candidate.application.to_string())
|
||||
}
|
||||
|
||||
#[put("/details", data = "<details>")]
|
||||
#[post("/details", data = "<details>")]
|
||||
pub async fn fill_details(
|
||||
conn: Connection<'_, Db>,
|
||||
details: Json<UserDetails>,
|
||||
|
|
@ -70,3 +72,66 @@ pub async fn fill_details(
|
|||
|
||||
Ok("Details added".to_string())
|
||||
}
|
||||
|
||||
#[post("/coverletter", data = "<letter>")]
|
||||
pub async fn upload_cover_letter(
|
||||
session: CandidateAuth,
|
||||
letter: Letter,
|
||||
) -> Result<String, Custom<String>> {
|
||||
let candidate: entity::candidate::Model = session.into();
|
||||
|
||||
let candidate = CandidateService::add_cover_letter(candidate.application, letter.into()).await;
|
||||
|
||||
if candidate.is_err() {
|
||||
// TODO cleanup
|
||||
let e = candidate.err().unwrap();
|
||||
return Err(Custom(
|
||||
Status::from_code(e.code()).unwrap_or_default(),
|
||||
e.message(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok("Letter added".to_string())
|
||||
}
|
||||
|
||||
#[post("/portfolioletter", data = "<letter>")]
|
||||
pub async fn upload_portfolio_letter(
|
||||
session: CandidateAuth,
|
||||
letter: Letter,
|
||||
) -> Result<String, Custom<String>> {
|
||||
let candidate: entity::candidate::Model = session.into();
|
||||
|
||||
let candidate = CandidateService::add_portfolio_letter(candidate.application, letter.into()).await;
|
||||
|
||||
if candidate.is_err() {
|
||||
// TODO cleanup
|
||||
let e = candidate.err().unwrap();
|
||||
return Err(Custom(
|
||||
Status::from_code(e.code()).unwrap_or_default(),
|
||||
e.message(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok("Letter added".to_string())
|
||||
}
|
||||
|
||||
#[post("/portfolio", data = "<portfolio>")]
|
||||
pub async fn upload_portfolio_zip(
|
||||
session: CandidateAuth,
|
||||
portfolio: Portfolio,
|
||||
) -> Result<String, Custom<String>> {
|
||||
let candidate: entity::candidate::Model = session.into();
|
||||
|
||||
let candidate = CandidateService::add_portfolio_zip(candidate.application, portfolio.into()).await;
|
||||
|
||||
if candidate.is_err() {
|
||||
// TODO cleanup
|
||||
let e = candidate.err().unwrap();
|
||||
return Err(Custom(
|
||||
Status::from_code(e.code()).unwrap_or_default(),
|
||||
e.message(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok("Letter added".to_string())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,7 +67,9 @@ impl EncryptedUserDetails {
|
|||
}
|
||||
}
|
||||
|
||||
fn extract_enc_candidate_details(candidate: candidate::Model) -> Result<UserDetails, ServiceError> {
|
||||
fn extract_enc_candidate_details(
|
||||
candidate: candidate::Model,
|
||||
) -> Result<UserDetails, ServiceError> {
|
||||
let ( // TODO: simplify??
|
||||
Some(name),
|
||||
Some(surname),
|
||||
|
|
@ -152,20 +154,18 @@ impl EncryptedUserDetails {
|
|||
panic!("Failed to encrypt user details"); // TODO
|
||||
};
|
||||
|
||||
Ok(
|
||||
UserDetails {
|
||||
name,
|
||||
surname,
|
||||
birthplace,
|
||||
// birthdate: NaiveDate::from_ymd(2000, 1, 1),
|
||||
address,
|
||||
telephone,
|
||||
citizenship,
|
||||
email,
|
||||
sex,
|
||||
study,
|
||||
}
|
||||
)
|
||||
Ok(UserDetails {
|
||||
name,
|
||||
surname,
|
||||
birthplace,
|
||||
// birthdate: NaiveDate::from_ymd(2000, 1, 1),
|
||||
address,
|
||||
telephone,
|
||||
citizenship,
|
||||
email,
|
||||
sex,
|
||||
study,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -183,7 +183,6 @@ pub struct UserDetails {
|
|||
pub study: String,
|
||||
}
|
||||
|
||||
|
||||
pub struct CandidateService;
|
||||
|
||||
impl CandidateService {
|
||||
|
|
@ -259,13 +258,9 @@ impl CandidateService {
|
|||
|
||||
let enc_details = EncryptedUserDetails::encrypt_form(form, recipients).await;
|
||||
|
||||
Mutation::add_candidate_details(
|
||||
db,
|
||||
user,
|
||||
enc_details,
|
||||
)
|
||||
.await
|
||||
.map_err(|_| ServiceError::DbError)
|
||||
Mutation::add_candidate_details(db, user, enc_details)
|
||||
.await
|
||||
.map_err(|_| ServiceError::DbError)
|
||||
}
|
||||
|
||||
pub async fn decrypt_details(
|
||||
|
|
@ -282,18 +277,36 @@ impl CandidateService {
|
|||
match crypto::verify_password((&password).to_string(), candidate.code.clone()).await {
|
||||
Ok(valid) => {
|
||||
if !valid {
|
||||
return Err(ServiceError::InvalidCredentials)
|
||||
return Err(ServiceError::InvalidCredentials);
|
||||
}
|
||||
},
|
||||
Err(_) => {return Err(ServiceError::InvalidCredentials)}
|
||||
}
|
||||
Err(_) => return Err(ServiceError::InvalidCredentials),
|
||||
}
|
||||
|
||||
let dec_priv_key = crypto::decrypt_password(candidate.private_key.clone(), password).await.ok().unwrap();
|
||||
let dec_priv_key = crypto::decrypt_password(candidate.private_key.clone(), password)
|
||||
.await
|
||||
.ok()
|
||||
.unwrap();
|
||||
let enc_details = EncryptedUserDetails::from_model(candidate)?;
|
||||
|
||||
enc_details.decrypt(dec_priv_key).await
|
||||
}
|
||||
|
||||
pub async fn add_cover_letter(candidate_id: i32, letter: Vec<u8>) -> Result<(), ServiceError> {
|
||||
// TODO
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn add_portfolio_letter(candidate_id: i32, letter: Vec<u8>) -> Result<(), ServiceError> {
|
||||
// TODO
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn add_portfolio_zip(candidate_id: i32, zip: Vec<u8>) -> Result<(), ServiceError> {
|
||||
// TODO
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn login(
|
||||
db: &DbConn,
|
||||
user_id: i32,
|
||||
|
|
@ -326,10 +339,13 @@ impl CandidateService {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use sea_orm::{Database, DbConn};
|
||||
use entity::candidate::Model;
|
||||
use sea_orm::{Database, DbConn};
|
||||
|
||||
use crate::{crypto, services::candidate_service::{CandidateService, UserDetails}};
|
||||
use crate::{
|
||||
crypto,
|
||||
services::candidate_service::{CandidateService, UserDetails},
|
||||
};
|
||||
|
||||
use super::EncryptedUserDetails;
|
||||
|
||||
|
|
@ -398,7 +414,6 @@ mod tests {
|
|||
assert_eq!(secret_message, decrypted_message);
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
async fn put_user_data(db: &DbConn) -> Model {
|
||||
let plain_text_password = "test".to_string();
|
||||
|
|
@ -419,7 +434,10 @@ mod tests {
|
|||
sex: "test".to_string(),
|
||||
study: "test".to_string(),
|
||||
};
|
||||
CandidateService::add_user_details(&db, candidate, form).await.ok().unwrap()
|
||||
CandidateService::add_user_details(&db, candidate, form)
|
||||
.await
|
||||
.ok()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
|
@ -438,7 +456,11 @@ mod tests {
|
|||
let dec_priv_key = crypto::decrypt_password(enc_candidate.private_key.clone(), password)
|
||||
.await
|
||||
.unwrap();
|
||||
let dec_candidate = EncryptedUserDetails::from_model(enc_candidate).unwrap().decrypt(dec_priv_key).await.unwrap();
|
||||
let dec_candidate = EncryptedUserDetails::from_model(enc_candidate)
|
||||
.unwrap()
|
||||
.decrypt(dec_priv_key)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(dec_candidate.name, "test");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue