feat(api): implemented /user/info
This commit is contained in:
parent
f9595513e0
commit
7bf9e57010
|
@ -21,7 +21,7 @@ __(ND)__ -> Not designed yet.
|
||||||
- [ ] `/deactivate`
|
- [ ] `/deactivate`
|
||||||
- [ ] `/activate`
|
- [ ] `/activate`
|
||||||
- `/user/`
|
- `/user/`
|
||||||
- [ ] `/info`
|
- [X] `/info`
|
||||||
- [ ] `/follow`
|
- [ ] `/follow`
|
||||||
- [ ] `/follows`
|
- [ ] `/follows`
|
||||||
- [ ] `/followers`
|
- [ ] `/followers`
|
||||||
|
|
|
@ -1,18 +1,21 @@
|
||||||
# `/user/info` - GET
|
# `/user/info` - GET
|
||||||
Returns information about the project.
|
|
||||||
|
|
||||||
|
Returns information about the user.
|
||||||
|
|
||||||
## Urlencoded Parameters
|
## Urlencoded Parameters
|
||||||
|
|
||||||
You have to set one parameter.
|
You have to set one parameter.
|
||||||
Setting none or two parameters will result in a _400 Bad Request_ Response.
|
Setting none or two parameters will result in a _400 Bad Request_ Response.
|
||||||
|
|
||||||
| Parameter | Description |
|
| Parameter | Description |
|
||||||
|-----------|--------------------------------------------------------------------|
|
|-----------|--------------------------------------------------------------------|
|
||||||
| name | The username of the user on which the operation will be performed. |
|
| name | The username of the user on which the operation will be performed. |
|
||||||
| id | The userid of the user on which the operation will be performed. |
|
| id | The userid of the user on which the operation will be performed. |
|
||||||
|
|
||||||
|
|
||||||
## Responses
|
## Responses
|
||||||
|
|
||||||
### 200 - Success
|
### 200 - Success
|
||||||
|
|
||||||
__Content - JSON:__
|
__Content - JSON:__
|
||||||
| Field | Description |
|
| Field | Description |
|
||||||
|----------|-------------------------------------------------------------------|
|
|----------|-------------------------------------------------------------------|
|
||||||
|
@ -20,9 +23,15 @@ __Content - JSON:__
|
||||||
| name | The users unique username. |
|
| name | The users unique username. |
|
||||||
| joined | The datetime when the user joined. Represented as UNIX timestamp. |
|
| joined | The datetime when the user joined. Represented as UNIX timestamp. |
|
||||||
| is_admin | A boolean if the user is an admin. |
|
| is_admin | A boolean if the user is an admin. |
|
||||||
|
|
||||||
### 400 - Error: Bad Request
|
### 400 - Error: Bad Request
|
||||||
|
|
||||||
The request was malformed.
|
The request was malformed.
|
||||||
|
|
||||||
### 403 - Error: Forbidden
|
### 403 - Error: Forbidden
|
||||||
|
|
||||||
Blocked for security reasons.
|
Blocked for security reasons.
|
||||||
|
|
||||||
### 404 - Error: Not Found
|
### 404 - Error: Not Found
|
||||||
|
|
||||||
The user wasn't found.
|
The user wasn't found.
|
|
@ -13,6 +13,10 @@ pub struct ID {
|
||||||
flags: u16,
|
flags: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum AccountFlags {
|
||||||
|
Admin,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Account {
|
pub struct Account {
|
||||||
pub id: ID,
|
pub id: ID,
|
||||||
|
@ -35,6 +39,14 @@ impl From<i64> for ID {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<AccountFlags> for u16 {
|
||||||
|
fn from(value: AccountFlags) -> Self {
|
||||||
|
match value {
|
||||||
|
AccountFlags::Admin => 0b0000_0000_0000_0001,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ID {
|
impl ID {
|
||||||
pub fn new(id: i64, flags: u16) -> Self {
|
pub fn new(id: i64, flags: u16) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -200,4 +212,9 @@ impl Account {
|
||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_flag(&self, flag: AccountFlags) -> bool {
|
||||||
|
let mask: u16 = flag.into();
|
||||||
|
(self.flags as u16 & mask) > 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
mod account;
|
mod account;
|
||||||
mod project;
|
mod project;
|
||||||
|
mod user;
|
||||||
|
|
||||||
use actix_web::{web, App, HttpServer};
|
use actix_web::{web, App, HttpServer};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
@ -96,6 +97,7 @@ pub async fn start(port: u16, pool: PgPool) -> Result<()> {
|
||||||
.service(project::calls::create)
|
.service(project::calls::create)
|
||||||
.service(project::calls::info)
|
.service(project::calls::info)
|
||||||
.service(project::calls::delete)
|
.service(project::calls::delete)
|
||||||
|
.service(user::calls::info)
|
||||||
.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))?
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
use crate::api::user::{data, handlers};
|
||||||
|
use crate::api::ApiState;
|
||||||
|
use actix_web::{get, web, HttpResponse, Responder};
|
||||||
|
use log::error;
|
||||||
|
|
||||||
|
#[get("/user/info")]
|
||||||
|
async fn info(data: web::Data<ApiState>, query: web::Query<data::UserIdQuery>) -> impl Responder {
|
||||||
|
let request = if query.id.is_none() || query.username.is_none() {
|
||||||
|
if let Some(id) = query.id {
|
||||||
|
data::InfoRequest::Id(id)
|
||||||
|
} else if let Some(name) = query.into_inner().username {
|
||||||
|
data::InfoRequest::Name(name)
|
||||||
|
} else {
|
||||||
|
// No parameters were supplied
|
||||||
|
return HttpResponse::BadRequest().finish();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Too many parameters were supplied
|
||||||
|
return HttpResponse::BadRequest().finish();
|
||||||
|
};
|
||||||
|
|
||||||
|
match handlers::info(&data.pool, request).await {
|
||||||
|
Ok(resp) => match resp {
|
||||||
|
data::InfoResponse::Success(b) => HttpResponse::Ok().json(web::Json(b)),
|
||||||
|
data::InfoResponse::Blocked => HttpResponse::Forbidden().finish(),
|
||||||
|
data::InfoResponse::NotFound => HttpResponse::NotFound().finish(),
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
error!("While handling info request: {e}");
|
||||||
|
HttpResponse::InternalServerError().finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct UserIdQuery {
|
||||||
|
pub username: Option<String>,
|
||||||
|
pub id: Option<i64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum InfoRequest {
|
||||||
|
Name(String),
|
||||||
|
Id(i64),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct InfoSuccess {
|
||||||
|
pub id: i64,
|
||||||
|
pub name: String,
|
||||||
|
pub joined: i64,
|
||||||
|
pub is_admin: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum InfoResponse {
|
||||||
|
Success(InfoSuccess),
|
||||||
|
Blocked,
|
||||||
|
NotFound,
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
use crate::{
|
||||||
|
api::user::data,
|
||||||
|
accounts::{Account, AccountFlags},
|
||||||
|
security::is_sql_injection,
|
||||||
|
};
|
||||||
|
use anyhow::Result;
|
||||||
|
use sqlx::PgPool;
|
||||||
|
|
||||||
|
pub async fn info(pool: &PgPool, request: data::InfoRequest) -> Result<data::InfoResponse> {
|
||||||
|
let account = match match request {
|
||||||
|
data::InfoRequest::Name(name) => {
|
||||||
|
if is_sql_injection(&name) {
|
||||||
|
return Ok(data::InfoResponse::Blocked);
|
||||||
|
}
|
||||||
|
Account::from_username(pool, &name).await?
|
||||||
|
}
|
||||||
|
data::InfoRequest::Id(id) => Account::from_id(pool, id).await?,
|
||||||
|
} {
|
||||||
|
Some(p) => p,
|
||||||
|
None => return Ok(data::InfoResponse::NotFound),
|
||||||
|
};
|
||||||
|
|
||||||
|
let is_admin = account.has_flag(AccountFlags::Admin);
|
||||||
|
|
||||||
|
Ok(data::InfoResponse::Success(data::InfoSuccess {
|
||||||
|
id: account.id.id(),
|
||||||
|
name: account.username,
|
||||||
|
joined: account.joined.timestamp(),
|
||||||
|
is_admin,
|
||||||
|
}))
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
pub mod calls;
|
||||||
|
pub mod data;
|
||||||
|
mod handlers;
|
Loading…
Reference in New Issue