5 Commits
Author SHA1 Message Date
schn33fuchs 616efa5282 Fixed name mismatches and missing feature flag 2026-04-25 13:01:41 +02:00
schn33fuchs 0ce17fe805 Routes for auth handling
Also db and structs are now identical
2026-04-24 19:53:43 +02:00
schn33fuchs 51b6f89df2 Added user login, logout and creation functionality
Also minor changes to ticketing and AppState
2026-04-24 19:46:02 +02:00
schn33fuchs fe04483e76 Updated user scheme
Forgot password and username
2026-04-24 17:49:52 +02:00
schn33fuchs b94a94a28e Added Json Web Tokens
Also down migration for users
2026-04-24 17:49:32 +02:00
13 changed files with 716 additions and 17 deletions
+416
View File
@@ -23,6 +23,18 @@ version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
[[package]]
name = "argon2"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
dependencies = [
"base64ct",
"blake2",
"cpufeatures",
"password-hash",
]
[[package]]
name = "atoi"
version = "2.0.0"
@@ -96,19 +108,56 @@ dependencies = [
"tracing",
]
[[package]]
name = "axum-extra"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be44683b41ccb9ab2d23a5230015c9c3c55be97a25e4428366de8873103f7970"
dependencies = [
"axum",
"axum-core",
"bytes",
"cookie",
"form_urlencoded",
"futures-core",
"futures-util",
"headers",
"http",
"http-body",
"http-body-util",
"mime",
"pin-project-lite",
"serde_core",
"serde_html_form",
"serde_path_to_error",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "backend"
version = "0.1.0"
dependencies = [
"argon2",
"axum",
"axum-extra",
"chrono",
"dotenv",
"jsonwebtoken",
"serde",
"serde_json",
"sqlx",
"time",
"tokio",
]
[[package]]
name = "base16ct"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
[[package]]
name = "base64"
version = "0.22.1"
@@ -130,6 +179,15 @@ dependencies = [
"serde_core",
]
[[package]]
name = "blake2"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
dependencies = [
"digest",
]
[[package]]
name = "block-buffer"
version = "0.10.4"
@@ -202,6 +260,17 @@ version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
[[package]]
name = "cookie"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
dependencies = [
"percent-encoding",
"time",
"version_check",
]
[[package]]
name = "core-foundation"
version = "0.10.1"
@@ -257,6 +326,18 @@ version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "crypto-bigint"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
dependencies = [
"generic-array",
"rand_core",
"subtle",
"zeroize",
]
[[package]]
name = "crypto-common"
version = "0.1.7"
@@ -267,6 +348,33 @@ dependencies = [
"typenum",
]
[[package]]
name = "curve25519-dalek"
version = "4.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
dependencies = [
"cfg-if",
"cpufeatures",
"curve25519-dalek-derive",
"digest",
"fiat-crypto",
"rustc_version",
"subtle",
"zeroize",
]
[[package]]
name = "curve25519-dalek-derive"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "der"
version = "0.7.10"
@@ -278,6 +386,15 @@ dependencies = [
"zeroize",
]
[[package]]
name = "deranged"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
dependencies = [
"powerfmt",
]
[[package]]
name = "digest"
version = "0.10.7"
@@ -313,6 +430,44 @@ version = "0.15.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
[[package]]
name = "ecdsa"
version = "0.16.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
dependencies = [
"der",
"digest",
"elliptic-curve",
"rfc6979",
"signature",
"spki",
]
[[package]]
name = "ed25519"
version = "2.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
dependencies = [
"pkcs8",
"signature",
]
[[package]]
name = "ed25519-dalek"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9"
dependencies = [
"curve25519-dalek",
"ed25519",
"serde",
"sha2",
"subtle",
"zeroize",
]
[[package]]
name = "either"
version = "1.15.0"
@@ -322,6 +477,27 @@ dependencies = [
"serde",
]
[[package]]
name = "elliptic-curve"
version = "0.13.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
dependencies = [
"base16ct",
"crypto-bigint",
"digest",
"ff",
"generic-array",
"group",
"hkdf",
"pem-rfc7468",
"pkcs8",
"rand_core",
"sec1",
"subtle",
"zeroize",
]
[[package]]
name = "equivalent"
version = "1.0.2"
@@ -366,6 +542,22 @@ version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
[[package]]
name = "ff"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393"
dependencies = [
"rand_core",
"subtle",
]
[[package]]
name = "fiat-crypto"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
@@ -492,6 +684,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
"zeroize",
]
[[package]]
@@ -518,6 +711,17 @@ dependencies = [
"wasip3",
]
[[package]]
name = "group"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
dependencies = [
"ff",
"rand_core",
"subtle",
]
[[package]]
name = "hashbrown"
version = "0.15.5"
@@ -544,6 +748,30 @@ dependencies = [
"hashbrown 0.15.5",
]
[[package]]
name = "headers"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb"
dependencies = [
"base64",
"bytes",
"headers-core",
"http",
"httpdate",
"mime",
"sha1",
]
[[package]]
name = "headers-core"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4"
dependencies = [
"http",
]
[[package]]
name = "heck"
version = "0.5.0"
@@ -824,6 +1052,29 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "jsonwebtoken"
version = "10.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0529410abe238729a60b108898784df8984c87f6054c9c4fcacc47e4803c1ce1"
dependencies = [
"base64",
"ed25519-dalek",
"getrandom 0.2.17",
"hmac",
"js-sys",
"p256",
"p384",
"pem",
"rand",
"rsa",
"serde",
"serde_json",
"sha2",
"signature",
"simple_asn1",
]
[[package]]
name = "lazy_static"
version = "1.5.0"
@@ -956,6 +1207,16 @@ dependencies = [
"tempfile",
]
[[package]]
name = "num-bigint"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [
"num-integer",
"num-traits",
]
[[package]]
name = "num-bigint-dig"
version = "0.8.6"
@@ -972,6 +1233,12 @@ dependencies = [
"zeroize",
]
[[package]]
name = "num-conv"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967"
[[package]]
name = "num-integer"
version = "0.1.46"
@@ -1052,6 +1319,30 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "p256"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b"
dependencies = [
"ecdsa",
"elliptic-curve",
"primeorder",
"sha2",
]
[[package]]
name = "p384"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6"
dependencies = [
"ecdsa",
"elliptic-curve",
"primeorder",
"sha2",
]
[[package]]
name = "parking"
version = "2.2.1"
@@ -1081,6 +1372,27 @@ dependencies = [
"windows-link",
]
[[package]]
name = "password-hash"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
dependencies = [
"base64ct",
"rand_core",
"subtle",
]
[[package]]
name = "pem"
version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be"
dependencies = [
"base64",
"serde_core",
]
[[package]]
name = "pem-rfc7468"
version = "0.7.0"
@@ -1144,6 +1456,12 @@ dependencies = [
"zerovec",
]
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "ppv-lite86"
version = "0.2.21"
@@ -1163,6 +1481,15 @@ dependencies = [
"syn",
]
[[package]]
name = "primeorder"
version = "0.13.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6"
dependencies = [
"elliptic-curve",
]
[[package]]
name = "proc-macro2"
version = "1.0.106"
@@ -1235,6 +1562,16 @@ dependencies = [
"bitflags",
]
[[package]]
name = "rfc6979"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
dependencies = [
"hmac",
"subtle",
]
[[package]]
name = "rsa"
version = "0.9.10"
@@ -1255,6 +1592,15 @@ dependencies = [
"zeroize",
]
[[package]]
name = "rustc_version"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
dependencies = [
"semver",
]
[[package]]
name = "rustix"
version = "1.1.4"
@@ -1295,6 +1641,20 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "sec1"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
dependencies = [
"base16ct",
"der",
"generic-array",
"pkcs8",
"subtle",
"zeroize",
]
[[package]]
name = "security-framework"
version = "3.7.0"
@@ -1354,6 +1714,19 @@ dependencies = [
"syn",
]
[[package]]
name = "serde_html_form"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2f2d7ff8a2140333718bb329f5c40fc5f0865b84c426183ce14c97d2ab8154f"
dependencies = [
"form_urlencoded",
"indexmap",
"itoa",
"ryu",
"serde_core",
]
[[package]]
name = "serde_json"
version = "1.0.149"
@@ -1428,6 +1801,18 @@ dependencies = [
"rand_core",
]
[[package]]
name = "simple_asn1"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d585997b0ac10be3c5ee635f1bab02d512760d14b7c468801ac8a01d9ae5f1d"
dependencies = [
"num-bigint",
"num-traits",
"thiserror",
"time",
]
[[package]]
name = "slab"
version = "0.4.12"
@@ -1749,6 +2134,37 @@ dependencies = [
"syn",
]
[[package]]
name = "time"
version = "0.3.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
dependencies = [
"deranged",
"itoa",
"num-conv",
"powerfmt",
"serde_core",
"time-core",
"time-macros",
]
[[package]]
name = "time-core"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
[[package]]
name = "time-macros"
version = "0.2.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215"
dependencies = [
"num-conv",
"time-core",
]
[[package]]
name = "tinystr"
version = "0.8.3"
+4
View File
@@ -5,9 +5,13 @@ edition = "2024"
[dependencies]
axum = "0.8.9"
axum-extra = { version = "0.12.6", features = ["cookie", "typed-header", "form"] }
serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.149"
sqlx = { version = "0.8.6", features = ["postgres", "runtime-tokio", "tls-native-tls", "chrono"] }
tokio = { version = "1.52.1", features = ["rt-multi-thread", "macros"] }
dotenv = "0.15.0"
chrono = { version = "0.4.44", features = ["serde"] }
jsonwebtoken = { version = "10.3.0", features = ["rust_crypto"] }
argon2 = "0.5.3"
time = "0.3.47"
+37
View File
@@ -0,0 +1,37 @@
use axum::{Json, http::StatusCode};
use jsonwebtoken::{DecodingKey, EncodingKey, Header, Validation, decode, encode};
use serde::{Deserialize, Serialize};
use crate::models::Claims;
#[derive(Debug, Deserialize, Serialize)]
pub struct Error {
pub status: &'static str,
pub message: String,
}
pub fn encode_token(header: &Header, id: String, key: &EncodingKey) -> String {
let now = chrono::Utc::now();
let issued = now.timestamp() as usize;
let expires = (now + chrono::Duration::minutes(60)).timestamp() as usize;
let claims: Claims = Claims {
subject: id,
issued: issued,
expires: expires,
};
let token = encode(header, &claims, key);
return token.expect("token return failed");
}
pub fn decode_token(token: String, key: &DecodingKey) -> Result<Claims, (StatusCode, Json<Error>)> {
let claims = decode::<Claims>(&token, key, &Validation::default())
.map_err(|_| {
let error = Error {
status: "error",
message: "Invalid Token".to_string(),
};
(StatusCode::UNAUTHORIZED, Json(error))
})?
.claims;
return Ok(claims);
}
+1
View File
@@ -0,0 +1 @@
pub mod jwt;
+16
View File
@@ -0,0 +1,16 @@
#[derive(Debug, Clone)]
pub struct Env {
pub db_url: String,
pub token_secret: String,
}
impl Env {
pub fn load() -> Env {
let db_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");
let token_secret = std::env::var("TOKEN_SECRET").expect("TOKEN_SECRET must be set");
Env {
db_url,
token_secret,
}
}
}
+165
View File
@@ -0,0 +1,165 @@
use std::{sync::Arc, usize};
use argon2::{
Argon2, PasswordHash, PasswordHasher, PasswordVerifier,
password_hash::{SaltString, rand_core::OsRng},
};
use axum::{
Json,
extract::State,
http::{Response, StatusCode, header},
response::IntoResponse,
};
use axum_extra::extract::cookie::{Cookie, SameSite};
use chrono::format;
use jsonwebtoken::{EncodingKey, Header};
use serde_json::json;
use crate::{
AppState,
cookie::jwt::encode_token,
models::{FilteredUser, LoginModel, LoginScheme, UserCreateScheme},
};
pub async fn create_user(
State(data): State<Arc<AppState>>,
Json(request): Json<UserCreateScheme>,
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
if request.username.is_empty() || request.pwd.is_empty() {
return Err((
StatusCode::BAD_REQUEST,
Json(json!({"status": "error", "message": "Missing credential"})),
));
}
let exist_check = sqlx::query_as::<_, UserCreateScheme>(
r#"SELECT first_name, last_name, username, is_admin, pwd FROM users WHERE username = $1"#,
)
.bind(&request.username)
.fetch_optional(&data.db)
.await
.map_err(|e| {
(
StatusCode::BAD_REQUEST,
Json(json!({"status": "error", "message": format!("{:?}", e)})),
)
})?;
if let Some(_) = exist_check {
return Err((
StatusCode::BAD_REQUEST,
Json(json!({"status": "error", "message": "user already exists"})),
));
}
let argon = Argon2::default();
let salt = SaltString::generate(&mut OsRng);
let hashed_pwd = match argon.hash_password(request.pwd.clone().as_bytes(), &salt) {
Ok(h) => h.to_string(),
Err(e) => panic!("Error hashing {:}", e),
};
let user = sqlx::query("INSERT INTO users (username, pwd, first_name, last_name, is_admin) VALUES ($1, $2, $3, $4, $5)")
.bind(request.username)
.bind(&hashed_pwd)
.bind(request.first_name)
.bind(request.last_name)
.bind(request.is_admin)
.execute(&data.db)
.await
.map_err(|e| {
(
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({"status": "error", "message": format!("{}", e)})),
)
})?;
if user.rows_affected() < 1 {
return Err((
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({"status": "error", "message": "Error creating user"})),
));
} else {
Ok(Json(json!({"status": "success", "result": "User created"})))
}
}
pub async fn login(
State(data): State<Arc<AppState>>,
Json(request): Json<LoginScheme>,
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
let user = sqlx::query_as::<_, LoginModel>(r#"SELECT * FROM users WHERE username = $1"#)
.bind(request.username)
.fetch_optional(&data.db)
.await
.map_err(|e| {
(
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({"status": "error", "message": format!("{}", e)})),
)
})?
.ok_or_else(|| {
(
StatusCode::BAD_REQUEST,
Json(json!({"status": "error", "message": "Invalid username"})),
)
})?;
let pwd_hash = PasswordHash::new(&user.pwd);
let valid_pwd = Argon2::default()
.verify_password(&request.pwd.as_bytes(), &pwd_hash.unwrap())
.is_ok();
if !valid_pwd {
let error_response = serde_json::json!({
"status": "error",
"message": "Invalid password"
});
return Err((StatusCode::BAD_REQUEST, Json(error_response)));
}
let token = encode_token(
&Header::default(),
user.id.clone().to_string(),
&EncodingKey::from_secret(data.env.token_secret.as_ref()),
);
let cookie = Cookie::build(("token", token.to_owned()))
.path("/")
.max_age(time::Duration::hours(1))
.same_site(SameSite::Lax)
.http_only(true);
let mut response = Response::new(
json!({"status": "success", "token": token, "user": filter_users(&user)}).to_string(),
);
response
.headers_mut()
.insert(header::SET_COOKIE, cookie.to_string().parse().unwrap());
Ok(response)
}
pub async fn logout() -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
let cookie = Cookie::build(("token", ""))
.path("/")
.max_age(time::Duration::hours(-1))
.same_site(SameSite::Lax)
.http_only(true);
let mut response = Response::new(
json!({"status": "success", "message": "successfully logged out"}).to_string(),
);
response
.headers_mut()
.insert(header::SET_COOKIE, cookie.to_string().parse().unwrap());
Ok(response)
}
fn filter_users(user: &LoginModel) -> FilteredUser {
FilteredUser {
id: user.id,
first_name: user.first_name.clone(),
last_name: user.last_name.clone(),
is_admin: user.is_admin.clone(),
}
}
+1
View File
@@ -1 +1,2 @@
pub mod auth;
pub mod ticket;
+4 -3
View File
@@ -68,8 +68,9 @@ pub async fn delete_ticket(
pub async fn get_tickets(
State(data): State<Arc<AppState>>,
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
let tickets =
sqlx::query_as(r#"SELECT * FROM tickets WHERE status <> 'Archived' ORDER BY date DESC"#)
let tickets = sqlx::query_as::<_, Ticket>(
r#"SELECT * FROM tickets WHERE status <> 'Archived' ORDER BY date DESC"#,
)
.fetch_all(&data.db)
.await
.map_err(|e| {
@@ -144,7 +145,7 @@ pub async fn edit_ticket(
return Err((StatusCode::INTERNAL_SERVER_ERROR, Json(error_response)));
}
let updated_ticket = sqlx::query_as(r#"SELECT * FROM tickets WHERE id = $1"#)
let updated_ticket = sqlx::query_as::<_, Ticket>(r#"SELECT * FROM tickets WHERE id = $1"#)
.bind(id)
.fetch_one(&data.db)
.await
+10 -1
View File
@@ -1,4 +1,6 @@
#![allow(unused_imports)]
mod cookie;
mod env;
mod handlers;
mod models;
mod router;
@@ -11,13 +13,17 @@ use router::create_router;
use serde::{Deserialize, Serialize};
use sqlx::{PgPool, postgres::PgPoolOptions};
use crate::env::Env;
pub struct AppState {
db: PgPool,
env: Env,
}
#[tokio::main]
async fn main() {
dotenv().ok();
let env = Env::load();
let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL variable not set");
let pool = match PgPoolOptions::new().connect(&database_url).await {
Ok(pool) => {
@@ -29,7 +35,10 @@ async fn main() {
std::process::exit(1);
}
};
let app = create_router(Arc::new(AppState { db: pool.clone() }));
let app = create_router(Arc::new(AppState {
db: pool.clone(),
env: env.clone(),
}));
let listener = tokio::net::TcpListener::bind("0.0.0.0:8001").await.unwrap();
axum::serve(listener, app).await;
}
+41 -1
View File
@@ -30,9 +30,10 @@ pub struct TicketResponse {
#[derive(Deserialize, Serialize, PartialEq, Debug)]
pub struct User {
pub id: i16,
pub first_name: String,
pub last_name: String,
pub first_name: String,
pub is_admin: bool,
pub pwd: String,
}
#[derive(Deserialize, Serialize, Debug)]
@@ -47,3 +48,42 @@ pub struct TicketCreateScheme {
pub struct TicketUpdateScheme {
pub status: String,
}
#[derive(Deserialize, Serialize, Debug, sqlx::FromRow)]
pub struct UserCreateScheme {
pub first_name: String,
pub last_name: String,
pub username: String,
pub is_admin: bool,
pub pwd: String,
}
#[derive(Deserialize, Serialize, Debug)]
pub struct LoginScheme {
pub username: String,
pub pwd: String,
}
#[derive(Deserialize, Serialize, Debug, sqlx::FromRow)]
pub struct LoginModel {
pub id: i16,
pub last_name: String,
pub first_name: String,
pub is_admin: bool,
pub pwd: String,
}
#[derive(Debug, Serialize)]
pub struct FilteredUser {
pub id: i16,
pub first_name: String,
pub last_name: String,
pub is_admin: bool,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Claims {
pub subject: String,
pub issued: usize,
pub expires: usize,
}
+7 -1
View File
@@ -7,7 +7,10 @@ use axum::{
use crate::{
AppState,
handlers::ticket::{create_ticket, delete_ticket, edit_ticket, get_ticket_by_id, get_tickets},
handlers::{
auth::{create_user, login, logout},
ticket::{create_ticket, delete_ticket, edit_ticket, get_ticket_by_id, get_tickets},
},
};
pub fn create_router(state: Arc<AppState>) -> Router {
@@ -20,5 +23,8 @@ pub fn create_router(state: Arc<AppState>) -> Router {
.delete(delete_ticket)
.patch(edit_ticket),
)
.route("/api/register", post(create_user))
.route("/api/login", post(login))
.route("/api/logout", get(logout))
.with_state(state)
}
@@ -0,0 +1 @@
DROP TABLE users;
+4 -2
View File
@@ -1,6 +1,8 @@
CREATE TABLE users (
id SMALLINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name VARCHAR(30),
firstname VARCHAR(30),
last_name VARCHAR(30),
first_name VARCHAR(30),
username VARCHAR(30),
pwd TEXT,
is_admin BOOLEAN NOT NULL DEFAULT false
);