mirror of
https://github.com/danbulant/Portfolio
synced 2026-06-07 00:30:11 +00:00
Merge pull request #77 from EETagent/cache_delete
This commit is contained in:
commit
0b36b96802
4 changed files with 204 additions and 36 deletions
|
|
@ -1,10 +1,10 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate rocket;
|
extern crate rocket;
|
||||||
|
|
||||||
use rocket::fairing::{self, AdHoc, Fairing, Kind, Info};
|
use rocket::fairing::{self, AdHoc, Fairing, Info, Kind};
|
||||||
|
|
||||||
use rocket::http::Header;
|
use rocket::http::Header;
|
||||||
use rocket::{Build, Rocket, Request, Response};
|
use rocket::{Build, Request, Response, Rocket};
|
||||||
|
|
||||||
use migration::MigratorTrait;
|
use migration::MigratorTrait;
|
||||||
use sea_orm_rocket::Database;
|
use sea_orm_rocket::Database;
|
||||||
|
|
@ -27,14 +27,20 @@ impl Fairing for CORS {
|
||||||
fn info(&self) -> Info {
|
fn info(&self) -> Info {
|
||||||
Info {
|
Info {
|
||||||
name: "Add CORS headers to responses",
|
name: "Add CORS headers to responses",
|
||||||
kind: Kind::Response
|
kind: Kind::Response,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
async fn on_response<'r>(&self, _request: &'r Request<'_>, response: &mut Response<'r>) {
|
async fn on_response<'r>(&self, _request: &'r Request<'_>, response: &mut Response<'r>) {
|
||||||
response.set_header(Header::new("Access-Control-Allow-Origin", "http://localhost:5173"));
|
response.set_header(Header::new(
|
||||||
response.set_header(Header::new("Access-Control-Allow-Methods", "POST, GET, OPTIONS"));
|
"Access-Control-Allow-Origin",
|
||||||
|
"http://localhost:5173",
|
||||||
|
));
|
||||||
|
response.set_header(Header::new(
|
||||||
|
"Access-Control-Allow-Methods",
|
||||||
|
"POST, GET, OPTIONS",
|
||||||
|
));
|
||||||
response.set_header(Header::new("Access-Control-Allow-Headers", "content-type"));
|
response.set_header(Header::new("Access-Control-Allow-Headers", "content-type"));
|
||||||
response.set_header(Header::new("Access-Control-Allow-Credentials", "true"));
|
response.set_header(Header::new("Access-Control-Allow-Credentials", "true"));
|
||||||
}
|
}
|
||||||
|
|
@ -61,7 +67,7 @@ async fn run_migrations(rocket: Rocket<Build>) -> fairing::Result {
|
||||||
Ok(rocket)
|
Ok(rocket)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rocket() -> Rocket<Build>{
|
pub fn rocket() -> Rocket<Build> {
|
||||||
rocket::build()
|
rocket::build()
|
||||||
.attach(CORS)
|
.attach(CORS)
|
||||||
.attach(Db::init())
|
.attach(Db::init())
|
||||||
|
|
@ -86,6 +92,14 @@ pub fn rocket() -> Rocket<Build>{
|
||||||
routes::candidate::upload_cover_letter,
|
routes::candidate::upload_cover_letter,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
.mount(
|
||||||
|
"/candidate/remove",
|
||||||
|
routes![
|
||||||
|
routes::candidate::delete_portfolio_letter,
|
||||||
|
routes::candidate::delete_portfolio_zip,
|
||||||
|
routes::candidate::delete_cover_letter,
|
||||||
|
],
|
||||||
|
)
|
||||||
.mount(
|
.mount(
|
||||||
"/candidate/portfolio",
|
"/candidate/portfolio",
|
||||||
routes![
|
routes![
|
||||||
|
|
@ -108,20 +122,13 @@ pub fn rocket() -> Rocket<Build>{
|
||||||
routes::admin::get_candidate_portfolio,
|
routes::admin::get_candidate_portfolio,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
.mount(
|
.mount("/admin/list", routes![routes::admin::list_candidates,])
|
||||||
"/admin/list",
|
|
||||||
routes![
|
|
||||||
routes::admin::list_candidates,
|
|
||||||
])
|
|
||||||
.register("/", catchers![])
|
.register("/", catchers![])
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn start() -> Result<(), rocket::Error> {
|
async fn start() -> Result<(), rocket::Error> {
|
||||||
rocket()
|
rocket().launch().await.map(|_| ())
|
||||||
.launch()
|
|
||||||
.await
|
|
||||||
.map(|_| ())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,8 @@ pub async fn login(
|
||||||
login_form.password.to_string(),
|
login_form.password.to_string(),
|
||||||
ip_addr.ip().to_string(),
|
ip_addr.ip().to_string(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(to_custom_error)?;
|
.map_err(to_custom_error)?;
|
||||||
|
|
||||||
cookies.add_private(Cookie::new("id", session_token.clone()));
|
cookies.add_private(Cookie::new("id", session_token.clone()));
|
||||||
cookies.add_private(Cookie::new("key", private_key.clone()));
|
cookies.add_private(Cookie::new("key", private_key.clone()));
|
||||||
|
|
@ -43,14 +43,22 @@ pub async fn login(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/logout")]
|
#[post("/logout")]
|
||||||
pub async fn logout(conn: Connection<'_, Db>, _session: CandidateAuth, cookies: &CookieJar<'_>,) -> Result<(), Custom<String>> {
|
pub async fn logout(
|
||||||
|
conn: Connection<'_, Db>,
|
||||||
|
_session: CandidateAuth,
|
||||||
|
cookies: &CookieJar<'_>,
|
||||||
|
) -> Result<(), Custom<String>> {
|
||||||
let db = conn.into_inner();
|
let db = conn.into_inner();
|
||||||
|
|
||||||
let cookie = cookies.get_private("id") // unwrap would be safe here because of the auth guard
|
let cookie = cookies
|
||||||
.ok_or(Custom(Status::Unauthorized, "No session cookie".to_string()))?;
|
.get_private("id") // unwrap would be safe here because of the auth guard
|
||||||
|
.ok_or(Custom(
|
||||||
|
Status::Unauthorized,
|
||||||
|
"No session cookie".to_string(),
|
||||||
|
))?;
|
||||||
let session_id = Uuid::try_parse(cookie.value()) // unwrap would be safe here because of the auth guard
|
let session_id = Uuid::try_parse(cookie.value()) // unwrap would be safe here because of the auth guard
|
||||||
.map_err(|e| Custom(Status::BadRequest, e.to_string()))?;
|
.map_err(|e| Custom(Status::BadRequest, e.to_string()))?;
|
||||||
|
|
||||||
CandidateService::logout(db, session_id)
|
CandidateService::logout(db, session_id)
|
||||||
.await
|
.await
|
||||||
.map_err(to_custom_error)?;
|
.map_err(to_custom_error)?;
|
||||||
|
|
@ -82,9 +90,7 @@ pub async fn post_details(
|
||||||
.await
|
.await
|
||||||
.map_err(to_custom_error)?;
|
.map_err(to_custom_error)?;
|
||||||
|
|
||||||
Ok(
|
Ok(Json(form))
|
||||||
Json(form)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/details")]
|
#[get("/details")]
|
||||||
|
|
@ -118,18 +124,15 @@ pub async fn upload_cover_letter(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/submission_progress")]
|
#[delete("/cover_letter")]
|
||||||
pub async fn submission_progress(
|
pub async fn delete_cover_letter(session: CandidateAuth) -> Result<(), Custom<String>> {
|
||||||
session: CandidateAuth
|
|
||||||
) -> Result<Json<SubmissionProgress>, Custom<String>> {
|
|
||||||
let candidate: entity::candidate::Model = session.into();
|
let candidate: entity::candidate::Model = session.into();
|
||||||
|
|
||||||
let progress = PortfolioService::get_submission_progress(candidate.application)
|
PortfolioService::delete_cover_letter_from_cache(candidate.application)
|
||||||
.await
|
.await
|
||||||
.map(|x| Json(x))
|
.map_err(to_custom_error)?;
|
||||||
.map_err(to_custom_error);
|
|
||||||
|
|
||||||
progress
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/portfolio_letter", data = "<letter>")]
|
#[post("/portfolio_letter", data = "<letter>")]
|
||||||
|
|
@ -146,6 +149,17 @@ pub async fn upload_portfolio_letter(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[delete("/portfolio_letter")]
|
||||||
|
pub async fn delete_portfolio_letter(session: CandidateAuth) -> Result<(), Custom<String>> {
|
||||||
|
let candidate: entity::candidate::Model = session.into();
|
||||||
|
|
||||||
|
PortfolioService::delete_portfolio_letter_from_cache(candidate.application)
|
||||||
|
.await
|
||||||
|
.map_err(to_custom_error)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[post("/portfolio_zip", data = "<portfolio>")]
|
#[post("/portfolio_zip", data = "<portfolio>")]
|
||||||
pub async fn upload_portfolio_zip(
|
pub async fn upload_portfolio_zip(
|
||||||
session: CandidateAuth,
|
session: CandidateAuth,
|
||||||
|
|
@ -160,6 +174,31 @@ pub async fn upload_portfolio_zip(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[delete("/portfolio_zip")]
|
||||||
|
pub async fn delete_portfolio_zip(session: CandidateAuth) -> Result<(), Custom<String>> {
|
||||||
|
let candidate: entity::candidate::Model = session.into();
|
||||||
|
|
||||||
|
PortfolioService::delete_portfolio_zip_from_cache(candidate.application)
|
||||||
|
.await
|
||||||
|
.map_err(to_custom_error)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/submission_progress")]
|
||||||
|
pub async fn submission_progress(
|
||||||
|
session: CandidateAuth,
|
||||||
|
) -> Result<Json<SubmissionProgress>, Custom<String>> {
|
||||||
|
let candidate: entity::candidate::Model = session.into();
|
||||||
|
|
||||||
|
let progress = PortfolioService::get_submission_progress(candidate.application)
|
||||||
|
.await
|
||||||
|
.map(|x| Json(x))
|
||||||
|
.map_err(to_custom_error);
|
||||||
|
|
||||||
|
progress
|
||||||
|
}
|
||||||
|
|
||||||
#[post("/submit")]
|
#[post("/submit")]
|
||||||
pub async fn submit_portfolio(
|
pub async fn submit_portfolio(
|
||||||
conn: Connection<'_, Db>,
|
conn: Connection<'_, Db>,
|
||||||
|
|
@ -177,7 +216,9 @@ pub async fn submit_portfolio(
|
||||||
// TODO: Více kontrol?
|
// TODO: Více kontrol?
|
||||||
if e.code() == 500 {
|
if e.code() == 500 {
|
||||||
// Cleanup
|
// Cleanup
|
||||||
PortfolioService::delete_portfolio(candidate.application).await.unwrap();
|
PortfolioService::delete_portfolio(candidate.application)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
return Err(to_custom_error(e));
|
return Err(to_custom_error(e));
|
||||||
}
|
}
|
||||||
|
|
@ -213,13 +254,16 @@ pub async fn download_portfolio(session: CandidateAuth) -> Result<Vec<u8>, Custo
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use portfolio_core::{models::candidate::ApplicationDetails, crypto, sea_orm::prelude::Uuid};
|
use portfolio_core::{crypto, models::candidate::ApplicationDetails, sea_orm::prelude::Uuid};
|
||||||
use rocket::{
|
use rocket::{
|
||||||
http::{Cookie, Status},
|
http::{Cookie, Status},
|
||||||
local::blocking::Client,
|
local::blocking::Client,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{test::tests::{test_client, APPLICATION_ID, CANDIDATE_PASSWORD}, routes::admin::tests::admin_login};
|
use crate::{
|
||||||
|
routes::admin::tests::admin_login,
|
||||||
|
test::tests::{test_client, APPLICATION_ID, CANDIDATE_PASSWORD},
|
||||||
|
};
|
||||||
|
|
||||||
fn candidate_login(client: &Client) -> (Cookie, Cookie) {
|
fn candidate_login(client: &Client) -> (Cookie, Cookie) {
|
||||||
let response = client
|
let response = client
|
||||||
|
|
|
||||||
|
|
@ -210,7 +210,7 @@ impl PortfolioService {
|
||||||
|
|
||||||
|
|
||||||
/// Returns true if portfolio is ready to be moved to the final directory
|
/// Returns true if portfolio is ready to be moved to the final directory
|
||||||
pub async fn is_portfolio_prepared(candidate_id: i32) -> bool {
|
async fn is_portfolio_prepared(candidate_id: i32) -> bool {
|
||||||
let cache_path = Self::get_file_store_path().join(&candidate_id.to_string()).join("cache");
|
let cache_path = Self::get_file_store_path().join(&candidate_id.to_string()).join("cache");
|
||||||
|
|
||||||
let filenames = vec![FileType::CoverLetterPdf, FileType::PortfolioLetterPdf, FileType::PortfolioZip];
|
let filenames = vec![FileType::CoverLetterPdf, FileType::PortfolioLetterPdf, FileType::PortfolioZip];
|
||||||
|
|
@ -224,6 +224,33 @@ impl PortfolioService {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete single item from cache
|
||||||
|
pub async fn delete_cache_item(candidate_id: i32, file_type: FileType) -> Result<(), ServiceError> {
|
||||||
|
let cache_path = Self::get_file_store_path().join(&candidate_id.to_string()).join("cache");
|
||||||
|
|
||||||
|
tokio::fs::remove_file(cache_path.join(file_type.as_str())).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn delete_cover_letter_from_cache(
|
||||||
|
candidate_id: i32,
|
||||||
|
) -> Result<(), ServiceError> {
|
||||||
|
Self::delete_cache_item(candidate_id, FileType::CoverLetterPdf).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn delete_portfolio_letter_from_cache(
|
||||||
|
candidate_id: i32,
|
||||||
|
) -> Result<(), ServiceError> {
|
||||||
|
Self::delete_cache_item(candidate_id, FileType::PortfolioLetterPdf).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn delete_portfolio_zip_from_cache(
|
||||||
|
candidate_id: i32,
|
||||||
|
) -> Result<(), ServiceError> {
|
||||||
|
Self::delete_cache_item(candidate_id, FileType::PortfolioZip).await
|
||||||
|
}
|
||||||
|
|
||||||
/// Removes all files from cache
|
/// Removes all files from cache
|
||||||
pub async fn delete_cache(candidate_id: i32) -> Result<(), ServiceError> {
|
pub async fn delete_cache(candidate_id: i32) -> Result<(), ServiceError> {
|
||||||
let cache_path = Self::get_file_store_path().join(&candidate_id.to_string()).join("cache");
|
let cache_path = Self::get_file_store_path().join(&candidate_id.to_string()).join("cache");
|
||||||
|
|
@ -403,6 +430,20 @@ mod tests {
|
||||||
clear_data_store_temp_dir(temp_dir).await;
|
clear_data_store_temp_dir(temp_dir).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[serial]
|
||||||
|
async fn test_delete_cover_letter_from_cache() {
|
||||||
|
let (temp_dir, _, application_cache_dir) = create_data_store_temp_dir(APPLICATION_ID).await;
|
||||||
|
|
||||||
|
PortfolioService::add_cover_letter_to_cache(APPLICATION_ID, vec![0]).await.unwrap();
|
||||||
|
|
||||||
|
PortfolioService::delete_cover_letter_from_cache(APPLICATION_ID).await.unwrap();
|
||||||
|
|
||||||
|
assert!(tokio::fs::metadata(application_cache_dir.join("MOTIVACNI_DOPIS.pdf")).await.is_err());
|
||||||
|
|
||||||
|
clear_data_store_temp_dir(temp_dir).await;
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_is_cover_letter() {
|
async fn test_is_cover_letter() {
|
||||||
|
|
@ -415,6 +456,21 @@ mod tests {
|
||||||
clear_data_store_temp_dir(temp_dir).await;
|
clear_data_store_temp_dir(temp_dir).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[serial]
|
||||||
|
async fn test_delete_cache_item() {
|
||||||
|
let (temp_dir, _, application_cache_dir) = create_data_store_temp_dir(APPLICATION_ID).await;
|
||||||
|
|
||||||
|
PortfolioService::add_cover_letter_to_cache(APPLICATION_ID, vec![0]).await.unwrap();
|
||||||
|
|
||||||
|
PortfolioService::delete_cache_item(APPLICATION_ID, FileType::CoverLetterPdf).await.unwrap();
|
||||||
|
|
||||||
|
assert!(tokio::fs::metadata(application_cache_dir.join("MOTIVACNI_DOPIS.pdf")).await.is_err());
|
||||||
|
|
||||||
|
clear_data_store_temp_dir(temp_dir).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_add_portfolio_letter_to_cache() {
|
async fn test_add_portfolio_letter_to_cache() {
|
||||||
|
|
@ -427,6 +483,20 @@ mod tests {
|
||||||
clear_data_store_temp_dir(temp_dir).await;
|
clear_data_store_temp_dir(temp_dir).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[serial]
|
||||||
|
async fn test_delete_portfolio_letter_from_cache() {
|
||||||
|
let (temp_dir, _, application_cache_dir) = create_data_store_temp_dir(APPLICATION_ID).await;
|
||||||
|
|
||||||
|
PortfolioService::add_portfolio_letter_to_cache(APPLICATION_ID, vec![0]).await.unwrap();
|
||||||
|
|
||||||
|
PortfolioService::delete_portfolio_letter_from_cache(APPLICATION_ID).await.unwrap();
|
||||||
|
|
||||||
|
assert!(tokio::fs::metadata(application_cache_dir.join("PORTFOLIO.pdf")).await.is_err());
|
||||||
|
|
||||||
|
clear_data_store_temp_dir(temp_dir).await;
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_is_portfolio_letter() {
|
async fn test_is_portfolio_letter() {
|
||||||
|
|
@ -451,6 +521,20 @@ mod tests {
|
||||||
clear_data_store_temp_dir(temp_dir).await;
|
clear_data_store_temp_dir(temp_dir).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[serial]
|
||||||
|
async fn test_delete_portfolio_zip_from_cache() {
|
||||||
|
let (temp_dir, _, application_cache_dir) = create_data_store_temp_dir(APPLICATION_ID).await;
|
||||||
|
|
||||||
|
PortfolioService::add_portfolio_zip_to_cache(APPLICATION_ID, vec![0]).await.unwrap();
|
||||||
|
|
||||||
|
PortfolioService::delete_portfolio_zip_from_cache(APPLICATION_ID).await.unwrap();
|
||||||
|
|
||||||
|
assert!(tokio::fs::metadata(application_cache_dir.join("PORTFOLIO.zip")).await.is_err());
|
||||||
|
|
||||||
|
clear_data_store_temp_dir(temp_dir).await;
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_is_portfolio_zip() {
|
async fn test_is_portfolio_zip() {
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,17 @@ export const apiUploadCoverLetter = async (
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const apiDeleteCoverLetter = async (): Promise<boolean> => {
|
||||||
|
try {
|
||||||
|
await axios.delete(API_URL + '/candidate/remove/cover_letter', {
|
||||||
|
withCredentials: true,
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
throw errorHandler(e, 'Failed to delete cover letter');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const apiUploadPortfolioLetter = async (
|
export const apiUploadPortfolioLetter = async (
|
||||||
letter: File,
|
letter: File,
|
||||||
progressReporter: (progress: AxiosProgressEvent) => void
|
progressReporter: (progress: AxiosProgressEvent) => void
|
||||||
|
|
@ -130,6 +141,17 @@ export const apiUploadPortfolioLetter = async (
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const apiDeletePortfolioLetter = async (): Promise<boolean> => {
|
||||||
|
try {
|
||||||
|
await axios.delete(API_URL + '/candidate/remove/portfolio_letter', {
|
||||||
|
withCredentials: true,
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
throw errorHandler(e, 'Failed to delete portfolio letter');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const apiUploadPortfolioZip = async (
|
export const apiUploadPortfolioZip = async (
|
||||||
portfolio: File,
|
portfolio: File,
|
||||||
progressReporter: (progress: AxiosProgressEvent) => void
|
progressReporter: (progress: AxiosProgressEvent) => void
|
||||||
|
|
@ -149,6 +171,17 @@ export const apiUploadPortfolioZip = async (
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const apiDeletePortfolioZip = async (): Promise<boolean> => {
|
||||||
|
try {
|
||||||
|
await axios.delete(API_URL + '/candidate/remove/portfolio_zip', {
|
||||||
|
withCredentials: true,
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
throw errorHandler(e, 'Failed to delete portfolio zip');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const apiSubmitPortfolio = async (): Promise<boolean> => {
|
export const apiSubmitPortfolio = async (): Promise<boolean> => {
|
||||||
try {
|
try {
|
||||||
await axios.post(API_URL + '/candidate/portfolio/submit', {}, { withCredentials: true });
|
await axios.post(API_URL + '/candidate/portfolio/submit', {}, { withCredentials: true });
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue