Merge pull request #35 from EETagent/portfolio_file_upload

Portfolio file upload
This commit is contained in:
Sebastian Pravda 2022-11-09 00:48:07 +01:00 committed by GitHub
commit 9c068dece9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 131 additions and 41 deletions

View file

@ -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))

View file

@ -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))

View file

@ -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(

View file

@ -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())
}

View file

@ -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");
}