mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-10 20:11:49 +00:00
list users endpoint
This commit is contained in:
1 parent
ec42723cf0
commit
d72958b749
3 files changed
+166
-9
No files matched your search
@@ -1,3 +1,4 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use crate::database::helpers::boolean_converters::i8_to_bool;
|
||||
use crate::util::anyhow_result::AnyhowResult;
|
||||
use sqlx::MySqlPool;
|
||||
@@ -29,16 +30,16 @@ pub struct UserForList {
|
||||
pub is_banned: bool,
|
||||
pub user_role_slug: String,
|
||||
|
||||
pub created_at: String,
|
||||
pub updated_at: String,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
pub struct BuildListUsers {
|
||||
pub struct ListUsersQueryBuilder {
|
||||
sort_ascending: bool,
|
||||
per_page: i64,
|
||||
}
|
||||
|
||||
impl BuildListUsers {
|
||||
impl ListUsersQueryBuilder {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
sort_ascending: false,
|
||||
@@ -109,7 +110,7 @@ impl BuildListUsers {
|
||||
let mut query_string = r#"
|
||||
SELECT
|
||||
id as user_id,
|
||||
user_token,
|
||||
token as user_token,
|
||||
username,
|
||||
display_name,
|
||||
display_name,
|
||||
@@ -117,9 +118,8 @@ SELECT
|
||||
is_banned,
|
||||
user_role_slug,
|
||||
created_at,
|
||||
updated_at,
|
||||
updated_at
|
||||
FROM users
|
||||
LIMIT 50
|
||||
"#;
|
||||
|
||||
query_string.to_string()
|
||||
@@ -138,7 +138,7 @@ pub struct UserForListRaw {
|
||||
pub is_banned: i8,
|
||||
pub user_role_slug: String,
|
||||
|
||||
pub created_at: String,
|
||||
pub updated_at: String,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
use actix_http::Error;
|
||||
use actix_http::http::header;
|
||||
use actix_web::cookie::Cookie;
|
||||
use actix_web::dev::HttpResponseBuilder;
|
||||
use actix_web::error::ResponseError;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::{Responder, web, HttpResponse, error, HttpRequest};
|
||||
use crate::database::queries::get_user_profile_by_username::get_user_profile_by_username;
|
||||
use crate::database::query_builders::list_users_query_builder::{ListUsersQueryBuilder, UserForList};
|
||||
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
|
||||
use crate::http_server::web_utils::response_success_helpers::simple_json_success;
|
||||
use crate::http_server::web_utils::serialize_as_json_error::serialize_as_json_error;
|
||||
use crate::server_state::ServerState;
|
||||
use derive_more::{Display, Error};
|
||||
use log::{info, warn, log};
|
||||
use regex::Regex;
|
||||
use sqlx::error::DatabaseError;
|
||||
use sqlx::error::Error::Database;
|
||||
use sqlx::mysql::MySqlDatabaseError;
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ListUsersRequest {
|
||||
// TODO: Implement the endpoint
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct ListUsersResponse {
|
||||
success: bool,
|
||||
users: Vec<UserForList>
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct ListUsersErrorResponse {
|
||||
pub success: bool,
|
||||
pub error_type: ListUsersErrorType,
|
||||
pub error_message: String,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize)]
|
||||
pub enum ListUsersErrorType {
|
||||
BadInput,
|
||||
ServerError,
|
||||
Unauthorized,
|
||||
UserNotFound,
|
||||
}
|
||||
|
||||
impl ListUsersErrorResponse {
|
||||
fn unauthorized() -> Self {
|
||||
Self {
|
||||
success: false,
|
||||
error_type: ListUsersErrorType::Unauthorized,
|
||||
error_message: "unauthorized".to_string()
|
||||
}
|
||||
}
|
||||
fn server_error() -> Self {
|
||||
Self {
|
||||
success: false,
|
||||
error_type: ListUsersErrorType::ServerError,
|
||||
error_message: "server error".to_string()
|
||||
}
|
||||
}
|
||||
fn bad_request(error_message: &str) -> Self {
|
||||
Self {
|
||||
success: false,
|
||||
error_type: ListUsersErrorType::BadInput,
|
||||
error_message: error_message.to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseError for ListUsersErrorResponse {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match self.error_type {
|
||||
ListUsersErrorType::BadInput => StatusCode::BAD_REQUEST,
|
||||
ListUsersErrorType::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
ListUsersErrorType::Unauthorized => StatusCode::UNAUTHORIZED,
|
||||
ListUsersErrorType::UserNotFound => StatusCode::NOT_FOUND,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
serialize_as_json_error(self)
|
||||
}
|
||||
}
|
||||
|
||||
// NB: Not using DeriveMore since Clion doesn't understand it.
|
||||
impl fmt::Display for ListUsersErrorResponse {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", self.error_type)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn list_users_handler(
|
||||
http_request: HttpRequest,
|
||||
request: web::Query<ListUsersRequest>,
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<HttpResponse, ListUsersErrorResponse> {
|
||||
|
||||
let maybe_user_session = server_state
|
||||
.session_checker
|
||||
.maybe_get_user_session(&http_request, &server_state.mysql_pool)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
ListUsersErrorResponse::server_error()
|
||||
})?;
|
||||
|
||||
let user_session = match maybe_user_session {
|
||||
Some(session) => session,
|
||||
None => {
|
||||
warn!("not logged in");
|
||||
return Err(ListUsersErrorResponse::unauthorized());
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: Not the correct permission
|
||||
if !user_session.can_ban_users {
|
||||
warn!("user is not allowed to add bans: {}", user_session.user_token);
|
||||
return Err(ListUsersErrorResponse::unauthorized());
|
||||
}
|
||||
|
||||
let query_results = ListUsersQueryBuilder::new()
|
||||
.sort_ascending(true)
|
||||
.query_for_page(&server_state.mysql_pool)
|
||||
.await;
|
||||
|
||||
let results_page = match query_results {
|
||||
Ok(results) => results,
|
||||
Err(e) => {
|
||||
warn!("Query error: {:?}", e);
|
||||
return Err(ListUsersErrorResponse::server_error());
|
||||
}
|
||||
};
|
||||
|
||||
let response = ListUsersResponse {
|
||||
success: true,
|
||||
users: results_page.users,
|
||||
};
|
||||
|
||||
let body = serde_json::to_string(&response)
|
||||
.map_err(|e| ListUsersErrorResponse::server_error())?;
|
||||
|
||||
Ok(HttpResponse::Ok()
|
||||
.content_type("application/json")
|
||||
.body(body))
|
||||
}
|
||||
@@ -105,6 +105,7 @@ use sqlx::MySqlPool;
|
||||
use sqlx::mysql::MySqlPoolOptions;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use crate::http_server::endpoints::moderation::users::list_users::list_users_handler;
|
||||
|
||||
// TODO TODO TODO TODO
|
||||
// TODO TODO TODO TODO
|
||||
@@ -383,6 +384,14 @@ pub async fn serve(server_state: ServerState) -> AnyhowResult<()>
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
)
|
||||
.service(
|
||||
web::scope("/user")
|
||||
.service(
|
||||
web::resource("/list")
|
||||
.route(web::get().to(list_users_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
)
|
||||
.service(
|
||||
web::scope("/user_bans")
|
||||
.service(
|
||||
|
||||
Reference in new issue
Block a user