Add server database interaction

This commit is contained in:
Michael Mikovsky
2025-12-01 16:38:33 -07:00
parent 97bd45571b
commit d1a0050f45
10 changed files with 229 additions and 38 deletions
+2
View File
@@ -1,3 +1,5 @@
database/
# Generated by Cargo # Generated by Cargo
# will have compiled files and executables # will have compiled files and executables
/target /target
-4
View File
@@ -18,7 +18,3 @@ backend = "http://localhost:3000/api" # Address to proxy requests to
# no_system_proxy = false # Disable system proxy # no_system_proxy = false # Disable system proxy
# rewrite = "" # Strip the given prefix off paths # rewrite = "" # Strip the given prefix off paths
# no_redirect = false # Disable following redirects of proxy responses # no_redirect = false # Disable following redirects of proxy responses
[[proxy]]
backend = "http://localhost:3000/auth"
+14 -4
View File
@@ -1,6 +1,7 @@
use egui::{Align2, Area, Color32, Frame, Order, Sense, UiKind, Vec2, mutex::Mutex}; use egui::{Align2, Area, Color32, Frame, Order, Sense, UiKind, Vec2, mutex::Mutex};
use serde::{Deserialize, Serialize, de::DeserializeOwned};
use serde_json::json; use serde_json::json;
use std::sync::Arc; use std::{marker::PhantomData, sync::Arc};
use wasm_bindgen::prelude::Closure; use wasm_bindgen::prelude::Closure;
#[derive(serde::Deserialize, serde::Serialize)] #[derive(serde::Deserialize, serde::Serialize)]
@@ -114,7 +115,7 @@ impl Auth {
let state = self.auth_state.clone(); let state = self.auth_state.clone();
crate::httpPost( crate::httpPost(
"/auth", "/api/auth",
&json!({ &json!({
"username": self.username.clone(), "username": self.username.clone(),
"password": self.password.clone() "password": self.password.clone()
@@ -149,15 +150,24 @@ impl Auth {
// }); // });
} }
pub fn test(&self) { pub fn get<T, F>(&self, path: &str, ret: F)
where
F: FnOnce(Result<T, String>) + 'static,
T: DeserializeOwned,
{
if let Some(ref token) = self.token { if let Some(ref token) = self.token {
let state = self.auth_state.clone(); let state = self.auth_state.clone();
crate::httpGetAuth( crate::httpGetAuth(
"/api/test1234/kjhejwer/kwherjwer/iuwehrhiwer/wiuerhjwer", path,
format!("Bearer {}", token.token), format!("Bearer {}", token.token),
Closure::once_into_js(move |ok: bool, response: String| { Closure::once_into_js(move |ok: bool, response: String| {
if ok { if ok {
crate::log(&response); crate::log(&response);
if let Ok(value) = serde_json::from_str::<Result<T, String>>(&response) {
ret(value)
} else {
*(state.lock()) = AuthState::Error("Malformed Response".into());
}
} else { } else {
*(state.lock()) = AuthState::Error(response); *(state.lock()) = AuthState::Error(response);
} }
+24 -4
View File
@@ -1,14 +1,34 @@
use std::sync::{Arc, Mutex};
use egui::Color32;
use crate::auth::Auth; use crate::auth::Auth;
#[derive(Default, serde::Deserialize, serde::Serialize)] #[derive(serde::Deserialize, serde::Serialize)]
pub struct Config { pub struct Config {
response_text: String, response_text: Arc<Mutex<String>>,
}
impl Default for Config {
fn default() -> Self {
Self {
response_text: Arc::new(Mutex::new("NONE".to_string())),
}
}
} }
impl Config { impl Config {
pub fn update(&mut self, auth: &mut Auth, ui: &mut egui::Ui) { pub fn update(&mut self, auth: &mut Auth, ui: &mut egui::Ui) {
if ui.button("Test").clicked() { if ui.button("Set Value").clicked() {
auth.test(); let text_clone = self.response_text.clone();
auth.get("/api/test", move |response: Result<String, String>| {
*text_clone.lock().unwrap() = format!("{:?}", response);
});
} }
ui.horizontal(|ui| {
ui.label("Response: ");
ui.colored_label(Color32::WHITE, &*self.response_text.lock().unwrap());
});
} }
} }
+102 -5
View File
@@ -428,6 +428,15 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "crc32fast"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "crossbeam-channel" name = "crossbeam-channel"
version = "0.5.15" version = "0.5.15"
@@ -437,6 +446,15 @@ dependencies = [
"crossbeam-utils", "crossbeam-utils",
] ]
[[package]]
name = "crossbeam-epoch"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
"crossbeam-utils",
]
[[package]] [[package]]
name = "crossbeam-utils" name = "crossbeam-utils"
version = "0.8.21" version = "0.8.21"
@@ -493,6 +511,16 @@ dependencies = [
"percent-encoding", "percent-encoding",
] ]
[[package]]
name = "fs2"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
dependencies = [
"libc",
"winapi",
]
[[package]] [[package]]
name = "fs_extra" name = "fs_extra"
version = "1.3.0" version = "1.3.0"
@@ -532,6 +560,15 @@ dependencies = [
"pin-utils", "pin-utils",
] ]
[[package]]
name = "fxhash"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
dependencies = [
"byteorder",
]
[[package]] [[package]]
name = "generic-array" name = "generic-array"
version = "0.14.7" version = "0.14.7"
@@ -732,6 +769,15 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "instant"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "is_terminal_polyfill" name = "is_terminal_polyfill"
version = "1.70.2" version = "1.70.2"
@@ -887,6 +933,17 @@ version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
name = "parking_lot"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
dependencies = [
"instant",
"lock_api",
"parking_lot_core 0.8.6",
]
[[package]] [[package]]
name = "parking_lot" name = "parking_lot"
version = "0.12.5" version = "0.12.5"
@@ -894,7 +951,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
dependencies = [ dependencies = [
"lock_api", "lock_api",
"parking_lot_core", "parking_lot_core 0.9.12",
]
[[package]]
name = "parking_lot_core"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
dependencies = [
"cfg-if",
"instant",
"libc",
"redox_syscall 0.2.16",
"smallvec",
"winapi",
] ]
[[package]] [[package]]
@@ -905,7 +976,7 @@ checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"redox_syscall", "redox_syscall 0.5.18",
"smallvec", "smallvec",
"windows-link", "windows-link",
] ]
@@ -1015,6 +1086,15 @@ dependencies = [
"getrandom 0.3.4", "getrandom 0.3.4",
] ]
[[package]]
name = "redox_syscall"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
dependencies = [
"bitflags 1.3.2",
]
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.5.18" version = "0.5.18"
@@ -1195,6 +1275,22 @@ dependencies = [
"time", "time",
] ]
[[package]]
name = "sled"
version = "0.34.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f96b4737c2ce5987354855aed3797279def4ebf734436c6aa4552cf8e169935"
dependencies = [
"crc32fast",
"crossbeam-epoch",
"crossbeam-utils",
"fs2",
"fxhash",
"libc",
"log",
"parking_lot 0.11.2",
]
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.15.1" version = "1.15.1"
@@ -1220,8 +1316,8 @@ dependencies = [
"bitflags 1.3.2", "bitflags 1.3.2",
"cfg_aliases 0.2.1", "cfg_aliases 0.2.1",
"libc", "libc",
"parking_lot", "parking_lot 0.12.5",
"parking_lot_core", "parking_lot_core 0.9.12",
"static_init_macro", "static_init_macro",
"winapi", "winapi",
] ]
@@ -1339,7 +1435,7 @@ dependencies = [
"bytes", "bytes",
"libc", "libc",
"mio", "mio",
"parking_lot", "parking_lot 0.12.5",
"pin-project-lite", "pin-project-lite",
"signal-hook-registry", "signal-hook-registry",
"socket2", "socket2",
@@ -1469,6 +1565,7 @@ dependencies = [
"jsonwebtoken", "jsonwebtoken",
"serde", "serde",
"serde_json", "serde_json",
"sled",
"static_init", "static_init",
"tokio", "tokio",
"unshell-lib", "unshell-lib",
+1
View File
@@ -23,6 +23,7 @@ bcrypt = "0.17.1"
chrono = "0.4.42" chrono = "0.4.42"
static_init = "1.0.4" static_init = "1.0.4"
clap = {version = "4.5.53", features = ["derive"]} clap = {version = "4.5.53", features = ["derive"]}
sled = "0.34.7"
# tracing = "0.1.43" # tracing = "0.1.43"
# tracing-subscriber = {version="0.3.22", features = ["env-filter"] } # tracing-subscriber = {version="0.3.22", features = ["env-filter"] }
+41 -16
View File
@@ -1,6 +1,6 @@
use axum::{ use axum::{
Extension, Router, Extension, Json, Router,
extract::Path, extract::{Path, State},
middleware, middleware,
response::IntoResponse, response::IntoResponse,
routing::{get, post}, routing::{get, post},
@@ -8,34 +8,59 @@ use axum::{
use tokio::net::TcpListener; use tokio::net::TcpListener;
use unshell_lib::info; use unshell_lib::info;
use crate::api::{auth, structs::CurrentUser}; use crate::{
api::{auth, structs::CurrentUser},
database::Database,
};
pub async fn start_api(address: &str) { pub async fn start_api(address: &str, database: Database) {
let listener = TcpListener::bind(address) let listener = TcpListener::bind(address)
.await .await
.expect("Unable to start listener"); .expect("Unable to start listener");
info!("Listening on {}", listener.local_addr().unwrap()); info!("Listening on {}", listener.local_addr().unwrap());
let app = Router::new().route("/auth", post(auth::sign_in)).route( let app = Router::new()
"/api/{*path}", .route("/api/auth", post(auth::sign_in))
get(protected).layer(middleware::from_fn(auth::authorize)), .route(
); "/api/{*path}",
get(get_data).layer(middleware::from_fn(auth::authorize)),
)
.route(
"/api/{*path}",
post(post_data).layer(middleware::from_fn(auth::authorize)),
)
.with_state(database);
axum::serve(listener, app) axum::serve(listener, app)
.await .await
.expect("Error serving application"); .expect("Error serving application");
} }
pub async fn protected( pub async fn get_data(
State(database): State<Database>,
Path(path): Path<String>, Path(path): Path<String>,
Extension(_): Extension<CurrentUser>, Extension(_): Extension<CurrentUser>,
) -> impl IntoResponse { ) -> impl IntoResponse {
info!("{}", path); let result = database.get_value(&path);
// Json(UserResponse {
// email: currentUser.email, Json(serde_json::to_value(result).unwrap())
// first_name: currentUser.first_name,
// last_name: currentUser.last_name,
// })
"Test"
} }
pub async fn post_data(
State(database): State<Database>,
Path(path): Path<String>,
Extension(_): Extension<CurrentUser>,
body: String,
) -> impl IntoResponse {
let result = database.put_value(&path, &body);
Json(serde_json::to_value(result).unwrap())
}
// impl IntoResponse for Option<StrW> {
// // impl IntoResponse for Option<String> {
// fn into_response(self) -> axum::response::Response {
// todo!()
// }
// }
+34 -2
View File
@@ -1,5 +1,37 @@
// Calc 3 ends Tuesday next week use unshell_lib::error;
#[derive(Clone)]
pub struct Database { pub struct Database {
// db: db: sled::Db,
}
impl Database {
pub fn new(database: String) -> Self {
Self {
db: sled::open(database).expect("Failed to open database"),
}
}
pub fn put_value(&self, key: &str, value: &str) -> Result<(), String> {
match self.db.insert(key, value) {
Ok(_) => Ok(()),
Err(e) => {
error!("Failed to load '{}' from database: {}", key, e);
Err(e.to_string())
}
}
}
pub fn get_value(&self, key: &str) -> Result<String, String> {
match self.db.get(key) {
Ok(v) => match v {
Some(v) => Ok(String::from_utf8_lossy(&v.to_vec()).to_string()),
None => Err(format!("Could not find key '{}'", key)),
},
Err(e) => {
error!("Failed to load '{}' from database: {}", key, e);
Err(e.to_string())
}
}
}
} }
+1 -1
View File
@@ -1,5 +1,5 @@
// #![macro_use] // #![macro_use]
mod api; mod api;
mod database; pub mod database;
pub use api::app::start_api; pub use api::app::start_api;
+10 -2
View File
@@ -1,10 +1,12 @@
use unshell_server::start_api; use unshell_server::{database::Database, start_api};
use clap::Parser; use clap::Parser;
use static_init::dynamic; use static_init::dynamic;
#[dynamic] #[dynamic]
static DEFAULT_HOST: String = "localhost".to_string(); static DEFAULT_HOST: String = "localhost".to_string();
#[dynamic]
static DATABASE_NAME: String = "database".to_string();
/// A fictional versioning CLI /// A fictional versioning CLI
#[derive(Debug, Parser)] #[derive(Debug, Parser)]
@@ -18,6 +20,10 @@ pub struct Args {
/// Port to listen /// Port to listen
#[arg(short, long, default_value_t = 3000)] #[arg(short, long, default_value_t = 3000)]
port: usize, port: usize,
/// Name of database folder
#[clap(short, long, default_value_t = DATABASE_NAME.clone())]
database_name: String,
} }
#[tokio::main] #[tokio::main]
@@ -26,5 +32,7 @@ async fn main() {
unshell_lib::logger::PrettyLogger::init(); unshell_lib::logger::PrettyLogger::init();
start_api(&format!("{}:{}", args.host, args.port)).await; let database = Database::new(args.database_name);
start_api(&format!("{}:{}", args.host, args.port), database).await;
} }