full with cli options and config support and all endpoints for crud
operations
This commit is contained in:
1 parent
c5d44c6466
commit
6d250c9c1a
10 files changed
+483
-126
No files matched your search
Generated
+134
@@ -2,6 +2,62 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2"
|
||||
dependencies = [
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.89"
|
||||
@@ -92,11 +148,59 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.49"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||
|
||||
[[package]]
|
||||
name = "embedded-web"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
"clap",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
@@ -152,6 +256,12 @@ dependencies = [
|
||||
"pin-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "1.3.1"
|
||||
@@ -235,6 +345,12 @@ dependencies = [
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.15"
|
||||
@@ -312,6 +428,12 @@ version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.5"
|
||||
@@ -498,6 +620,12 @@ dependencies = [
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.110"
|
||||
@@ -643,6 +771,12 @@ version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.1"
|
||||
|
||||
@@ -10,3 +10,5 @@ tower = "0.4"
|
||||
tracing-subscriber = "0.3" # Добавьте эту строку
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
clap = { version = "4.4", features = ["derive"] }
|
||||
anyhow = "1.0"
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"host": "0.0.0.0",
|
||||
"port": 8888,
|
||||
"log_level": "debug"
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
use clap::Parser;
|
||||
use serde::Deserialize;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct FileConfig {
|
||||
host: Option<String>,
|
||||
port: Option<u16>,
|
||||
log_level: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version, about)]
|
||||
struct Cli {
|
||||
#[arg(short, long)]
|
||||
config: Option<PathBuf>,
|
||||
|
||||
#[arg(short, long)]
|
||||
addr: Option<String>,
|
||||
|
||||
#[arg(short, long)]
|
||||
port: Option<u16>,
|
||||
|
||||
#[arg(short, long)]
|
||||
log_level: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Config {
|
||||
pub host: String,
|
||||
pub port: u16,
|
||||
pub log_level: String,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub async fn new() -> anyhow::Result<Self> {
|
||||
let cli: Cli = Cli::parse();
|
||||
let file_config: FileConfig = if let Some(config_path) = &cli.config {
|
||||
println!("Loading config from file");
|
||||
Self::load_file_config(config_path).await?
|
||||
} else {
|
||||
FileConfig {
|
||||
host: None,
|
||||
port: None,
|
||||
log_level: None,
|
||||
}
|
||||
};
|
||||
|
||||
let host: String = cli
|
||||
.addr
|
||||
.or(file_config.host)
|
||||
.unwrap_or_else(|| "0.0.0.0".to_string());
|
||||
let port: u16 = cli.port.or(file_config.port).unwrap_or(8888);
|
||||
let log_level: String = cli
|
||||
.log_level
|
||||
.or(file_config.log_level)
|
||||
.unwrap_or_else(|| "info".to_string());
|
||||
|
||||
Ok(Config {
|
||||
host,
|
||||
port,
|
||||
log_level,
|
||||
})
|
||||
}
|
||||
|
||||
async fn load_file_config(path: &PathBuf) -> anyhow::Result<FileConfig> {
|
||||
let content = tokio::fs::read_to_string(path).await?;
|
||||
let config: FileConfig = serde_json::from_str(&content)?;
|
||||
Ok(config)
|
||||
}
|
||||
}
|
||||
@@ -1,47 +1,34 @@
|
||||
use std::fs;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
// mod model;
|
||||
use crate::model;
|
||||
|
||||
pub fn get_all_users() -> Vec<model::User> {
|
||||
let data = fs::read_to_string("users.json").expect("Unable to read file");
|
||||
serde_json::from_str(&data).expect("Unable to parse JSON")
|
||||
pub type UserStorage = Arc<Mutex<HashMap<String, model::User>>>;
|
||||
|
||||
// storage initialization
|
||||
pub async fn init_user_storage() -> UserStorage {
|
||||
let users = read_users_from_file().await.unwrap_or_default();
|
||||
let map: HashMap<String, model::User> = users
|
||||
.into_iter()
|
||||
.map(|user| (user.id.clone(), user))
|
||||
.collect();
|
||||
Arc::new(Mutex::new(map))
|
||||
}
|
||||
|
||||
pub fn get_user_by_id(id: String) -> Option<model::User> {
|
||||
let users = get_all_users();
|
||||
users.into_iter().find(|user| user.id == id)
|
||||
// async read file
|
||||
async fn read_users_from_file() -> Result<Vec<model::User>, Box<dyn std::error::Error>> {
|
||||
let content = tokio::fs::read_to_string("users.json").await?;
|
||||
let users: Vec<model::User> = serde_json::from_str(&content)?;
|
||||
Ok(users)
|
||||
}
|
||||
|
||||
pub fn get_user_by_username(username: &str) -> Option<model::User> {
|
||||
let users = get_all_users();
|
||||
users.into_iter().find(|user| user.username == username)
|
||||
}
|
||||
|
||||
pub fn insert_user(user: model::User) -> model::GetUserResponse {
|
||||
let mut users = get_all_users();
|
||||
let respuser = model::GetUserResponse {
|
||||
id: user.id.clone(),
|
||||
username: user.username.clone(),
|
||||
email: user.email.clone(),
|
||||
group: user.group.clone(),
|
||||
is_superuser: user.is_superuser.clone(),
|
||||
};
|
||||
users.push(user);
|
||||
let data = serde_json::to_string(&users).expect("Unable to serialize JSON");
|
||||
fs::write("users.json", data).expect("Unable to write file");
|
||||
respuser
|
||||
}
|
||||
|
||||
pub fn delete_user(id: String) -> bool {
|
||||
let mut users = get_all_users();
|
||||
let user_exists = users.iter().any(|user| user.id == id);
|
||||
if user_exists {
|
||||
users.retain(|user| user.id != id);
|
||||
let data = serde_json::to_string_pretty(&users).expect("Unable to serialize JSON");
|
||||
fs::write("users.json", data).expect("Unable to write file");
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
// async write file
|
||||
pub async fn write_users_to_file(
|
||||
users: &HashMap<String, model::User>,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let users_vec: Vec<model::User> = users.values().cloned().collect();
|
||||
let content = serde_json::to_string_pretty(&users_vec)?;
|
||||
tokio::fs::write("users.json", content).await?;
|
||||
Ok(())
|
||||
}
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
use crate::db;
|
||||
use crate::model;
|
||||
use crate::state;
|
||||
use axum::Json;
|
||||
use axum::extract::State;
|
||||
|
||||
pub async fn get_all_users_handler(
|
||||
State(state): State<state::AppState>,
|
||||
) -> Json<Vec<model::GetUserResponse>> {
|
||||
let storage_guard = state.storage.lock().await;
|
||||
let users: Vec<model::User> = storage_guard.values().cloned().collect();
|
||||
let mapped_users = users
|
||||
.into_iter()
|
||||
.map(|user| model::GetUserResponse {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
group: user.group,
|
||||
is_superuser: user.is_superuser,
|
||||
})
|
||||
.collect();
|
||||
Json(mapped_users)
|
||||
}
|
||||
|
||||
pub async fn get_user_by_id_handler(
|
||||
State(state): State<state::AppState>,
|
||||
Json(payload): Json<model::GetUserByIdRequest>,
|
||||
) -> Result<Json<model::GetUserResponse>, Json<model::ErrorResponse>> {
|
||||
let storage_guard = state.storage.lock().await;
|
||||
storage_guard
|
||||
.get(&payload.id)
|
||||
.cloned()
|
||||
.map(|user| {
|
||||
Json(model::GetUserResponse {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
group: user.group,
|
||||
is_superuser: user.is_superuser,
|
||||
})
|
||||
})
|
||||
.ok_or_else(|| {
|
||||
Json(model::ErrorResponse {
|
||||
error: "User not found".to_string(),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn get_user_by_username_handler(
|
||||
State(state): State<state::AppState>,
|
||||
Json(payload): Json<model::GetUserByUsernameRequest>,
|
||||
) -> Result<Json<model::GetUserResponse>, Json<model::ErrorResponse>> {
|
||||
let storage_guard = state.storage.lock().await;
|
||||
storage_guard
|
||||
.get(&payload.username)
|
||||
.cloned()
|
||||
.map(|user| {
|
||||
Json(model::GetUserResponse {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
group: user.group,
|
||||
is_superuser: user.is_superuser,
|
||||
})
|
||||
})
|
||||
.ok_or_else(|| {
|
||||
Json(model::ErrorResponse {
|
||||
error: "User not found".to_string(),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn add_user_handler(
|
||||
State(state): State<state::AppState>,
|
||||
Json(payload): Json<model::AddUserRequest>,
|
||||
) -> Result<Json<model::GetUserResponse>, Json<model::ErrorResponse>> {
|
||||
let user = model::User {
|
||||
id: payload.id.clone(),
|
||||
username: payload.username.clone(),
|
||||
email: payload.email.clone(),
|
||||
password: payload.password.clone(),
|
||||
group: payload.group.clone(),
|
||||
is_superuser: payload.is_superuser,
|
||||
};
|
||||
let mut storage_guard = state.storage.lock().await;
|
||||
if storage_guard.contains_key(&user.id) {
|
||||
return Err(Json(model::ErrorResponse {
|
||||
error: "User already exists".to_string(),
|
||||
}));
|
||||
}
|
||||
storage_guard.insert(user.id.clone(), user.clone());
|
||||
let storage_clone = storage_guard.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = db::write_users_to_file(&storage_clone).await {
|
||||
eprintln!("Failed to write users to file: {}", e);
|
||||
}
|
||||
});
|
||||
Ok(Json(model::GetUserResponse {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
group: user.group,
|
||||
is_superuser: user.is_superuser,
|
||||
}))
|
||||
}
|
||||
|
||||
pub async fn delete_user_handler(
|
||||
State(state): State<state::AppState>,
|
||||
Json(payload): Json<model::DeleteUserRequest>,
|
||||
) -> Result<Json<model::DeleteUserResponse>, Json<model::ErrorResponse>> {
|
||||
let mut storage_guard = state.storage.lock().await;
|
||||
if storage_guard.remove(&payload.id).is_none() {
|
||||
return Err(Json(model::ErrorResponse {
|
||||
error: "User not found".to_string(),
|
||||
}));
|
||||
}
|
||||
let storage_clone = storage_guard.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = db::write_users_to_file(&storage_clone).await {
|
||||
eprintln!("Failed to write users to file: {}", e);
|
||||
}
|
||||
});
|
||||
Ok(Json(model::DeleteUserResponse { success: true }))
|
||||
}
|
||||
|
||||
pub async fn update_user_handler(
|
||||
State(state): State<state::AppState>,
|
||||
Json(payload): Json<model::UpdateUserRequest>,
|
||||
) -> Result<Json<model::GetUserResponse>, Json<model::ErrorResponse>> {
|
||||
let mut storage_guard = state.storage.lock().await;
|
||||
if !storage_guard.contains_key(&payload.id) {
|
||||
return Err(Json(model::ErrorResponse {
|
||||
error: "User not found".to_string(),
|
||||
}));
|
||||
}
|
||||
let old_user = storage_guard.get(&payload.id).unwrap().clone();
|
||||
storage_guard.insert(
|
||||
payload.id.clone(),
|
||||
model::User {
|
||||
id: payload.id.clone(),
|
||||
username: payload.username.clone(),
|
||||
email: payload.email.clone(),
|
||||
password: old_user.password.clone(),
|
||||
group: payload.group.clone(),
|
||||
is_superuser: payload.is_superuser,
|
||||
},
|
||||
);
|
||||
let storage_clone = storage_guard.clone(); // write to file in background
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = db::write_users_to_file(&storage_clone).await {
|
||||
eprintln!("Failed to write users to file: {}", e);
|
||||
}
|
||||
});
|
||||
|
||||
Ok(Json(model::GetUserResponse {
|
||||
id: payload.id.clone(),
|
||||
username: payload.username.clone(),
|
||||
email: payload.email.clone(),
|
||||
group: payload.group.clone(),
|
||||
is_superuser: payload.is_superuser,
|
||||
}))
|
||||
}
|
||||
+7
-85
@@ -1,91 +1,13 @@
|
||||
use axum::{Json, Router, routing::delete, routing::post};
|
||||
use std::net::SocketAddr;
|
||||
|
||||
mod config;
|
||||
mod db;
|
||||
mod handlers;
|
||||
mod model;
|
||||
mod startup;
|
||||
mod state;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
let app = Router::new()
|
||||
.route("/api/users", post(get_all_users_handler))
|
||||
.route("/api/users/user", post(get_user_by_id_handler))
|
||||
.route(
|
||||
"/api/users/user/username",
|
||||
post(get_user_by_username_handler),
|
||||
)
|
||||
.route("/api/users/add", post(add_user_handler))
|
||||
.route("/api/users", delete(delete_user_handler));
|
||||
|
||||
let addr = SocketAddr::from(([0, 0, 0, 0], 8888));
|
||||
println!("Сервер запускается на {}", addr);
|
||||
|
||||
axum::serve(tokio::net::TcpListener::bind(addr).await.unwrap(), app)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
async fn get_all_users_handler() -> Json<Vec<model::GetUserResponse>> {
|
||||
let users = db::get_all_users();
|
||||
let mapped_users = users
|
||||
.into_iter()
|
||||
.map(|user| model::GetUserResponse {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
group: user.group,
|
||||
is_superuser: user.is_superuser,
|
||||
})
|
||||
.collect();
|
||||
Json(mapped_users)
|
||||
}
|
||||
|
||||
async fn get_user_by_id_handler(
|
||||
Json(payload): Json<model::GetUserByIdRequest>,
|
||||
) -> Json<Option<model::GetUserResponse>> {
|
||||
let user = db::get_user_by_id(payload.id);
|
||||
let mapped_user = user.map(|user| model::GetUserResponse {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
group: user.group,
|
||||
is_superuser: user.is_superuser,
|
||||
});
|
||||
Json(mapped_user)
|
||||
}
|
||||
|
||||
async fn get_user_by_username_handler(
|
||||
Json(payload): Json<model::GetUserByUsernameRequest>,
|
||||
) -> Json<Option<model::GetUserResponse>> {
|
||||
let user = db::get_user_by_username(&payload.username);
|
||||
let mapped_user = user.map(|user| model::GetUserResponse {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
group: user.group,
|
||||
is_superuser: user.is_superuser,
|
||||
});
|
||||
Json(mapped_user)
|
||||
}
|
||||
|
||||
async fn add_user_handler(
|
||||
Json(payload): Json<model::AddUserRequest>,
|
||||
) -> Json<model::GetUserResponse> {
|
||||
let user = model::User {
|
||||
id: payload.id.clone(),
|
||||
username: payload.username.clone(),
|
||||
email: payload.email.clone(),
|
||||
password: payload.password.clone(),
|
||||
group: payload.group.clone(),
|
||||
is_superuser: payload.is_superuser,
|
||||
};
|
||||
Json(db::insert_user(user))
|
||||
}
|
||||
|
||||
async fn delete_user_handler(
|
||||
Json(payload): Json<model::DeleteUserRequest>,
|
||||
) -> Json<model::DeleteUserResponse> {
|
||||
let deleted: bool = db::delete_user(payload.id);
|
||||
Json(model::DeleteUserResponse { success: deleted })
|
||||
startup::start_server().await?;
|
||||
Ok(())
|
||||
}
|
||||
+16
-2
@@ -1,6 +1,6 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct User {
|
||||
pub id: String,
|
||||
pub username: String,
|
||||
@@ -20,7 +20,7 @@ pub struct GetUserByUsernameRequest {
|
||||
pub username: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct AddUserRequest {
|
||||
pub id: String,
|
||||
pub username: String,
|
||||
@@ -48,3 +48,17 @@ pub struct DeleteUserRequest {
|
||||
pub struct DeleteUserResponse {
|
||||
pub success: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct UpdateUserRequest {
|
||||
pub id: String,
|
||||
pub username: String,
|
||||
pub email: String,
|
||||
pub group: String,
|
||||
pub is_superuser: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct ErrorResponse {
|
||||
pub error: String,
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
use crate::handlers;
|
||||
use crate::state;
|
||||
use axum::{Router, routing::delete, routing::post, routing::put};
|
||||
use std::net::SocketAddr;
|
||||
|
||||
pub async fn start_server() -> anyhow::Result<()> {
|
||||
let state = state::AppState::new().await?;
|
||||
print_config(&state.config);
|
||||
let app = create_app(state.clone());
|
||||
let addr: SocketAddr = format!("{}:{}", state.config.host, state.config.port)
|
||||
.parse()
|
||||
.expect("Invalid host or port configuration");
|
||||
axum::serve(tokio::net::TcpListener::bind(addr).await.unwrap(), app)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
tokio::signal::ctrl_c().await?;
|
||||
println!("Server stopped");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create_app(state: state::AppState) -> Router {
|
||||
Router::new()
|
||||
.route("/api/users", post(handlers::get_all_users_handler))
|
||||
.route("/api/users/user", post(handlers::get_user_by_id_handler))
|
||||
.route(
|
||||
"/api/users/user/username",
|
||||
post(handlers::get_user_by_username_handler),
|
||||
)
|
||||
.route("/api/users/add", post(handlers::add_user_handler))
|
||||
.route("/api/users", delete(handlers::delete_user_handler))
|
||||
.route("/api/users", put(handlers::update_user_handler))
|
||||
.with_state(state)
|
||||
}
|
||||
|
||||
fn print_config(config: &crate::config::Config) {
|
||||
println!("Host: {}", config.host.clone());
|
||||
println!("Port: {}", config.port);
|
||||
println!("log_level: {}", config.log_level);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
use crate::config;
|
||||
use crate::db;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AppState {
|
||||
pub storage: db::UserStorage,
|
||||
pub config: config::Config,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
pub async fn new() -> anyhow::Result<Self> {
|
||||
let config = config::Config::new().await?;
|
||||
let user_storage = db::init_user_storage().await;
|
||||
let state = AppState {
|
||||
storage: user_storage,
|
||||
config: config.clone(),
|
||||
};
|
||||
Ok(state)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user