feat(api): implemented /project/delete
This commit is contained in:
parent
e93d8675c4
commit
4bccfeef96
|
@ -26,7 +26,7 @@ __(ND)__ -> Not designed yet.
|
||||||
- [ ] `/projects`
|
- [ ] `/projects`
|
||||||
- `/project`
|
- `/project`
|
||||||
- [X] `/create`
|
- [X] `/create`
|
||||||
- [ ] `/delete`
|
- [X] `/delete`
|
||||||
- [X] `/info`
|
- [X] `/info`
|
||||||
- [ ] `/join`
|
- [ ] `/join`
|
||||||
- `/vault`
|
- `/vault`
|
||||||
|
|
|
@ -24,3 +24,5 @@ The request was malformed.
|
||||||
The provided access token doesn't allow you to perform this operation.
|
The provided access token doesn't allow you to perform this operation.
|
||||||
### 403 - Error: Forbidden
|
### 403 - Error: Forbidden
|
||||||
Blocked for security reasons.
|
Blocked for security reasons.
|
||||||
|
### 404 - Error: Not Found
|
||||||
|
The project wasn't found.
|
|
@ -95,6 +95,7 @@ pub async fn start(port: u16, pool: PgPool) -> Result<()> {
|
||||||
.service(account::calls::tokens_get)
|
.service(account::calls::tokens_get)
|
||||||
.service(project::calls::create)
|
.service(project::calls::create)
|
||||||
.service(project::calls::info)
|
.service(project::calls::info)
|
||||||
|
.service(project::calls::delete)
|
||||||
.app_data(web::Data::new(ApiState { pool: pool.clone() }))
|
.app_data(web::Data::new(ApiState { pool: pool.clone() }))
|
||||||
})
|
})
|
||||||
.bind(("0.0.0.0", port))?
|
.bind(("0.0.0.0", port))?
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::api::ApiState;
|
use crate::api::ApiState;
|
||||||
use crate::api::project::{data, handlers};
|
use crate::api::project::{data, handlers};
|
||||||
use actix_web::{get, post, web, HttpResponse, Responder};
|
use actix_web::{get, post, web, HttpResponse, Responder, delete};
|
||||||
use actix_web_httpauth::extractors::bearer::BearerAuth;
|
use actix_web_httpauth::extractors::bearer::BearerAuth;
|
||||||
use log::error;
|
use log::error;
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ async fn create(data: web::Data<ApiState>, auth: BearerAuth, body: web::Json<dat
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/project/info")]
|
#[get("/project/info")]
|
||||||
async fn info(data: web::Data<ApiState>, query: web::Query<data::InfoQuery>) -> impl Responder {
|
async fn info(data: web::Data<ApiState>, query: web::Query<data::ProjectIdQuery>) -> impl Responder {
|
||||||
|
|
||||||
let request = if query.id.is_none() || query.name.is_none() {
|
let request = if query.id.is_none() || query.name.is_none() {
|
||||||
if let Some(id) = query.id {
|
if let Some(id) = query.id {
|
||||||
|
@ -49,3 +49,34 @@ async fn info(data: web::Data<ApiState>, query: web::Query<data::InfoQuery>) ->
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[delete("/project/delete")]
|
||||||
|
async fn delete(data: web::Data<ApiState>, auth: BearerAuth, query: web::Query<data::ProjectIdQuery>) -> impl Responder {
|
||||||
|
|
||||||
|
let request = if query.id.is_none() || query.name.is_none() {
|
||||||
|
if let Some(id) = query.id {
|
||||||
|
data::DeleteRequest::Id(id)
|
||||||
|
} else if let Some(name) = query.into_inner().name {
|
||||||
|
data::DeleteRequest::Name(name)
|
||||||
|
} else {
|
||||||
|
// No parameters were supplied
|
||||||
|
return HttpResponse::BadRequest().finish();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Too many parameters were supplied
|
||||||
|
return HttpResponse::BadRequest().finish();
|
||||||
|
};
|
||||||
|
|
||||||
|
match handlers::delete(&data.pool, auth.token().to_string(), request).await {
|
||||||
|
Ok(resp) => match resp {
|
||||||
|
data::DeleteResponse::Success => HttpResponse::Ok().finish(),
|
||||||
|
data::DeleteResponse::Unauthorized => HttpResponse::Unauthorized().finish(),
|
||||||
|
data::DeleteResponse::Blocked => HttpResponse::Forbidden().finish(),
|
||||||
|
data::DeleteResponse::NotFound => HttpResponse::NotFound().finish(),
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
error!("While handling info request: {e}");
|
||||||
|
HttpResponse::InternalServerError().finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct ProjectIdQuery {
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub id: Option<i64>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct CreateRequest {
|
pub struct CreateRequest {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
@ -19,12 +26,6 @@ pub enum CreateResponse {
|
||||||
Conflict,
|
Conflict,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
pub struct InfoQuery {
|
|
||||||
pub name: Option<String>,
|
|
||||||
pub id: Option<i64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum InfoRequest {
|
pub enum InfoRequest {
|
||||||
Name(String),
|
Name(String),
|
||||||
|
@ -46,3 +47,17 @@ pub enum InfoResponse {
|
||||||
Blocked,
|
Blocked,
|
||||||
NotFound,
|
NotFound,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum DeleteRequest {
|
||||||
|
Name(String),
|
||||||
|
Id(i64),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum DeleteResponse {
|
||||||
|
Success,
|
||||||
|
Unauthorized,
|
||||||
|
Blocked,
|
||||||
|
NotFound,
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ use crate::{
|
||||||
use anyhow::{Error, Result};
|
use anyhow::{Error, Result};
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use crate::accounts::Account;
|
use crate::accounts::Account;
|
||||||
|
use crate::projects::ProjectMemberFlags;
|
||||||
|
|
||||||
pub async fn create(
|
pub async fn create(
|
||||||
pool: &PgPool,
|
pool: &PgPool,
|
||||||
|
@ -71,3 +72,40 @@ pub async fn info(pool: &PgPool, request: data::InfoRequest) -> Result<data::Inf
|
||||||
members,
|
members,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn delete(
|
||||||
|
pool: &PgPool,
|
||||||
|
auth: String,
|
||||||
|
request: data::DeleteRequest,
|
||||||
|
) -> Result<data::DeleteResponse> {
|
||||||
|
if !auth.is_alpha() {
|
||||||
|
return Ok(data::DeleteResponse::Blocked);
|
||||||
|
}
|
||||||
|
|
||||||
|
let token = match AuthToken::check(pool, &auth).await? {
|
||||||
|
Some(t) => t,
|
||||||
|
None => return Ok(data::DeleteResponse::Unauthorized),
|
||||||
|
};
|
||||||
|
|
||||||
|
let project = match match request {
|
||||||
|
data::DeleteRequest::Name(name) => {
|
||||||
|
if is_sql_injection(&name) {
|
||||||
|
return Ok(data::DeleteResponse::Blocked);
|
||||||
|
}
|
||||||
|
Project::from_name(pool, &name).await?
|
||||||
|
}
|
||||||
|
data::DeleteRequest::Id(id) => Project::from_id(pool, id).await?
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Some(p) => p,
|
||||||
|
None => return Ok(data::DeleteResponse::NotFound)
|
||||||
|
};
|
||||||
|
|
||||||
|
if !project.member_has_flag(&token.account, ProjectMemberFlags::Owner).await {
|
||||||
|
return Ok(data::DeleteResponse::Unauthorized);
|
||||||
|
}
|
||||||
|
|
||||||
|
project.delete(pool).await?;
|
||||||
|
|
||||||
|
Ok(data::DeleteResponse::Success)
|
||||||
|
}
|
|
@ -85,4 +85,27 @@ impl Project {
|
||||||
Err(e) => Err(Error::new(e)),
|
Err(e) => Err(Error::new(e)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn member_has_flag(&self, member: &ID, flag: ProjectMemberFlags) -> bool {
|
||||||
|
let account_id = member.id();
|
||||||
|
let flag_mask: u16 = flag.into();
|
||||||
|
|
||||||
|
for &id in self.members.iter() {
|
||||||
|
if id.id() == account_id && (id.flags() & flag_mask) > 0 {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn delete(&self, pool: &PgPool) -> Result<()> {
|
||||||
|
sqlx::query!(
|
||||||
|
r#"DELETE FROM Projects WHERE id = $1;"#,
|
||||||
|
self.id
|
||||||
|
)
|
||||||
|
.execute(pool)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue