Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
12bf65f865 | ||
|
5eb085067d |
16
Dockerfile
16
Dockerfile
@ -5,16 +5,20 @@ COPY frontend/ .
|
||||
RUN npm install
|
||||
RUN npm run build
|
||||
|
||||
# Stage 2: Build the Rust backend
|
||||
# Stage 2: Build the Rust backend (statically linked)
|
||||
FROM rust:1.83 as backend-builder
|
||||
WORKDIR /app/backend
|
||||
COPY backend/ .
|
||||
RUN cargo build --release
|
||||
# Add the musl target for static linking
|
||||
RUN rustup target add x86_64-unknown-linux-musl
|
||||
# Build the binary with musl
|
||||
RUN cargo build --release --target x86_64-unknown-linux-musl
|
||||
|
||||
# Final Stage: Combine frontend and backend
|
||||
# Final Stage
|
||||
FROM debian:bullseye-slim
|
||||
WORKDIR /app
|
||||
COPY --from=frontend-builder /app/frontend/build ./frontend/dist
|
||||
COPY --from=backend-builder /app/backend/target/release/formies_be ./formies_be
|
||||
COPY --from=frontend-builder /app/frontend/build ./frontend/build
|
||||
# Copy the statically linked binary
|
||||
COPY --from=backend-builder /app/backend/target/x86_64-unknown-linux-musl/release/formies_be ./formies_be
|
||||
EXPOSE 8080
|
||||
CMD ["./backend"]
|
||||
CMD ["./formies_be"]
|
212
backend/Cargo.lock
generated
212
backend/Cargo.lock
generated
@ -338,15 +338,6 @@ version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||
|
||||
[[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"
|
||||
@ -387,12 +378,6 @@ dependencies = [
|
||||
"alloc-stdlib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
@ -516,7 +501,6 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -607,7 +591,6 @@ dependencies = [
|
||||
"env_logger",
|
||||
"futures",
|
||||
"log",
|
||||
"rand_core",
|
||||
"rusqlite",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -720,10 +703,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1007,31 +988,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jsonwebtoken"
|
||||
version = "9.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9ae10193d25051e74945f1ea2d0b42e03cc3b890f7e4cc5faa44997d808193f"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"js-sys",
|
||||
"pem",
|
||||
"ring",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"simple_asn1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "language-tags"
|
||||
version = "0.3.2"
|
||||
@ -1137,40 +1093,12 @@ dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[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-conv"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.36.7"
|
||||
@ -1209,33 +1137,12 @@ dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[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 = "paste"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "pem"
|
||||
version = "3.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.1"
|
||||
@ -1367,21 +1274,6 @@ version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"getrandom",
|
||||
"libc",
|
||||
"spin",
|
||||
"untrusted",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rusqlite"
|
||||
version = "0.29.0"
|
||||
@ -1499,18 +1391,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simple_asn1"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"thiserror",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.9"
|
||||
@ -1536,24 +1416,12 @@ dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.92"
|
||||
@ -1585,26 +1453,6 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.37"
|
||||
@ -1714,12 +1562,6 @@ version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.4"
|
||||
@ -1776,60 +1618,6 @@ version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.9"
|
||||
|
@ -6,7 +6,6 @@ use uuid::Uuid; // UUID for generating unique IDs // Import anyhow
|
||||
pub fn init_db() -> AnyhowResult<Connection> {
|
||||
let conn = Connection::open("form_data.db").context("Failed to open the database")?;
|
||||
|
||||
// Create tables
|
||||
conn.execute(
|
||||
"CREATE TABLE IF NOT EXISTS forms (
|
||||
id TEXT PRIMARY KEY,
|
||||
|
@ -1,10 +1,4 @@
|
||||
use crate::models::{AdminUser, Claims, Form, LoginCredentials, Submission};
|
||||
use actix_web::{web, HttpResponse, Responder};
|
||||
use argon2::{
|
||||
password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
|
||||
Argon2,
|
||||
};
|
||||
use jsonwebtoken::{encode, EncodingKey, Header};
|
||||
use rusqlite::{params, Connection};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::{Arc, Mutex};
|
||||
@ -140,101 +134,3 @@ pub async fn get_submissions(
|
||||
submissions_iter.filter_map(|s| s.ok()).collect();
|
||||
HttpResponse::Ok().json(submissions)
|
||||
}
|
||||
|
||||
pub async fn admin_login(
|
||||
db: web::Data<Arc<Mutex<Connection>>>,
|
||||
credentials: web::Json<LoginCredentials>,
|
||||
) -> impl Responder {
|
||||
let conn = match db.lock() {
|
||||
Ok(conn) => conn,
|
||||
Err(_) => return HttpResponse::InternalServerError().body("Database lock error"),
|
||||
};
|
||||
|
||||
let mut stmt =
|
||||
match conn.prepare("SELECT username, password_hash FROM admin_users WHERE username = ?1") {
|
||||
Ok(stmt) => stmt,
|
||||
Err(e) => {
|
||||
return HttpResponse::InternalServerError().body(format!("Database error: {}", e))
|
||||
}
|
||||
};
|
||||
|
||||
let admin: Option<AdminUser> = match stmt.query_row([&credentials.username], |row| {
|
||||
Ok(AdminUser {
|
||||
username: row.get(0)?,
|
||||
password_hash: row.get(1)?,
|
||||
})
|
||||
}) {
|
||||
Ok(admin) => Some(admin),
|
||||
Err(rusqlite::Error::QueryReturnedNoRows) => None, // No user found
|
||||
Err(e) => return HttpResponse::InternalServerError().body(format!("Query error: {}", e)),
|
||||
};
|
||||
|
||||
match admin {
|
||||
Some(user) => {
|
||||
let parsed_hash = match PasswordHash::new(&user.password_hash) {
|
||||
Ok(hash) => hash,
|
||||
Err(_) => {
|
||||
return HttpResponse::InternalServerError()
|
||||
.body("Invalid password hash format in database")
|
||||
}
|
||||
};
|
||||
|
||||
let argon2 = Argon2::default();
|
||||
let is_valid = argon2
|
||||
.verify_password(credentials.password.as_bytes(), &parsed_hash)
|
||||
.is_ok();
|
||||
|
||||
if is_valid {
|
||||
let expiration = match SystemTime::now().duration_since(UNIX_EPOCH) {
|
||||
Ok(duration) => duration.as_secs() as usize + 24 * 3600,
|
||||
Err(_) => return HttpResponse::InternalServerError().body("System time error"),
|
||||
};
|
||||
|
||||
let claims = Claims {
|
||||
sub: user.username,
|
||||
exp: expiration,
|
||||
};
|
||||
|
||||
let token = match encode(
|
||||
&Header::default(),
|
||||
&claims,
|
||||
&EncodingKey::from_secret("your-secret-key".as_ref()),
|
||||
) {
|
||||
Ok(token) => token,
|
||||
Err(_) => {
|
||||
return HttpResponse::InternalServerError().body("Token generation error")
|
||||
}
|
||||
};
|
||||
|
||||
HttpResponse::Ok().json(json!({ "token": token }))
|
||||
} else {
|
||||
HttpResponse::Unauthorized().json(json!({ "error": "Invalid credentials" }))
|
||||
}
|
||||
}
|
||||
None => HttpResponse::Unauthorized().json(json!({ "error": "Invalid credentials" })),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_admin(
|
||||
db: web::Data<Arc<Mutex<Connection>>>,
|
||||
user: web::Json<LoginCredentials>,
|
||||
) -> impl Responder {
|
||||
let conn = db.lock().unwrap();
|
||||
|
||||
let salt = SaltString::generate(&mut OsRng);
|
||||
let argon2 = Argon2::default();
|
||||
let password_hash = argon2
|
||||
.hash_password(user.password.as_bytes(), &salt)
|
||||
.unwrap()
|
||||
.to_string();
|
||||
|
||||
match conn.execute(
|
||||
"INSERT INTO admin_users (username, password_hash) VALUES (?1, ?2)",
|
||||
params![user.username, password_hash],
|
||||
) {
|
||||
Ok(_) => HttpResponse::Ok().json(json!({
|
||||
"message": "Admin user created successfully"
|
||||
})),
|
||||
Err(e) => HttpResponse::InternalServerError().body(format!("Error: {}", e)),
|
||||
}
|
||||
}
|
||||
|
@ -5,30 +5,9 @@ use std::sync::{Arc, Mutex};
|
||||
|
||||
mod auth;
|
||||
mod db;
|
||||
mod handlers; // Ensure handlers.rs exists
|
||||
mod middleware; // Ensure middleware.rs exists // Ensure db.rs exists
|
||||
mod handlers;
|
||||
mod models;
|
||||
|
||||
use crate::middleware::AuthMiddleware;
|
||||
use handlers::{admin_login, create_admin, create_form, get_forms, get_submissions, submit_form};
|
||||
|
||||
pub fn configure_routes(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(
|
||||
web::scope("")
|
||||
.route("/forms/{id}", web::post().to(submit_form))
|
||||
.route("/admin/login", web::post().to(admin_login))
|
||||
.route("/admin/create", web::post().to(create_admin)),
|
||||
);
|
||||
|
||||
cfg.service(
|
||||
web::scope("")
|
||||
.wrap(AuthMiddleware)
|
||||
.route("/forms", web::get().to(get_forms))
|
||||
.route("/forms", web::post().to(create_form))
|
||||
.route("/forms/{id}/submissions", web::get().to(get_submissions)),
|
||||
);
|
||||
}
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
env_logger::init();
|
||||
@ -45,7 +24,7 @@ async fn main() -> std::io::Result<()> {
|
||||
.allow_any_method(),
|
||||
)
|
||||
.app_data(web::Data::new(db.clone()))
|
||||
.service(fs::Files::new("/", "./frontend/dist").index_file("index.html"))
|
||||
.service(fs::Files::new("/", "frontend/build").index_file("index.html"))
|
||||
.route("/login", web::post().to(handlers::login)) // Public: Login
|
||||
.route(
|
||||
"/forms/{id}/submissions",
|
||||
@ -58,7 +37,7 @@ async fn main() -> std::io::Result<()> {
|
||||
web::get().to(handlers::get_submissions), // Protected
|
||||
)
|
||||
})
|
||||
.bind("127.0.0.1:8080")?
|
||||
.bind("0.0.0.0:8080")?
|
||||
.run()
|
||||
.await
|
||||
}
|
||||
|
@ -1,87 +0,0 @@
|
||||
use crate::models::Claims;
|
||||
use actix_web::body::{BoxBody, MessageBody};
|
||||
use actix_web::dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform};
|
||||
use actix_web::{Error, HttpResponse};
|
||||
use futures::future::{ok, Ready};
|
||||
use serde_json::json;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
|
||||
pub struct AuthMiddleware;
|
||||
|
||||
impl<S, B> Transform<S, ServiceRequest> for AuthMiddleware
|
||||
where
|
||||
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
|
||||
S::Future: 'static,
|
||||
B: MessageBody + 'static,
|
||||
{
|
||||
type Response = ServiceResponse<BoxBody>; // Changed to BoxBody
|
||||
type Error = Error;
|
||||
type Transform = AuthMiddlewareService<S>;
|
||||
type InitError = ();
|
||||
type Future = Ready<Result<Self::Transform, Self::InitError>>;
|
||||
|
||||
fn new_transform(&self, service: S) -> Self::Future {
|
||||
ok(AuthMiddlewareService { service })
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AuthMiddlewareService<S> {
|
||||
service: S,
|
||||
}
|
||||
|
||||
impl<S, B> Service<ServiceRequest> for AuthMiddlewareService<S>
|
||||
where
|
||||
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
|
||||
S::Future: 'static,
|
||||
B: MessageBody + 'static,
|
||||
{
|
||||
type Response = ServiceResponse<BoxBody>; // Changed to BoxBody
|
||||
type Error = Error;
|
||||
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
|
||||
|
||||
forward_ready!(service);
|
||||
|
||||
fn call(&self, req: ServiceRequest) -> Self::Future {
|
||||
if req.path() == "/admin/login" || req.path() == "/admin/create" {
|
||||
let fut = self.service.call(req);
|
||||
return Box::pin(async move {
|
||||
let res = fut.await?;
|
||||
Ok(res.map_into_boxed_body()) // Convert the response body to BoxBody
|
||||
});
|
||||
}
|
||||
|
||||
let auth_header = req.headers().get("Authorization");
|
||||
match auth_header {
|
||||
Some(header) => {
|
||||
let token = header.to_str().unwrap_or("").replace("Bearer ", "");
|
||||
if verify_token(&token) {
|
||||
let fut = self.service.call(req);
|
||||
Box::pin(async move {
|
||||
let res = fut.await?;
|
||||
Ok(res.map_into_boxed_body()) // Convert the response body to BoxBody
|
||||
})
|
||||
} else {
|
||||
let (request, _) = req.into_parts();
|
||||
let response = HttpResponse::Unauthorized()
|
||||
.json(json!({"error": "Invalid token"}))
|
||||
.map_into_boxed_body();
|
||||
Box::pin(async move { Ok(ServiceResponse::new(request, response)) })
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let (request, _) = req.into_parts();
|
||||
let response = HttpResponse::Unauthorized()
|
||||
.json(json!({"error": "No authorization token"}))
|
||||
.map_into_boxed_body();
|
||||
Box::pin(async move { Ok(ServiceResponse::new(request, response)) })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn verify_token(token: &str) -> bool {
|
||||
let validation = jsonwebtoken::Validation::default();
|
||||
let key = jsonwebtoken::DecodingKey::from_secret("your-secret-key".as_ref());
|
||||
jsonwebtoken::decode::<Claims>(token, &key, &validation).is_ok()
|
||||
}
|
@ -13,21 +13,3 @@ pub struct Submission {
|
||||
pub form_id: String,
|
||||
pub data: serde_json::Value, // JSON of submission data
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct AdminUser {
|
||||
pub username: String,
|
||||
pub password_hash: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct LoginCredentials {
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Claims {
|
||||
pub sub: String,
|
||||
pub(crate) exp: usize,
|
||||
}
|
||||
|
@ -15,52 +15,8 @@ function delAuthToken(): void {
|
||||
localStorage.removeItem('auth_token');
|
||||
}
|
||||
|
||||
// A simple function to retrieve the token from local storage or wherever it is stored
|
||||
function getAuthToken(): string | null {
|
||||
return localStorage.getItem('auth_token'); // Assuming the token is stored in localStorage
|
||||
}
|
||||
|
||||
// A simple function to save the token
|
||||
function setAuthToken(token: string): void {
|
||||
localStorage.setItem('auth_token', token);
|
||||
}
|
||||
|
||||
// A simple function to save the token
|
||||
function delAuthToken(): void {
|
||||
localStorage.removeItem('auth_token');
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to make authenticated requests.
|
||||
* @param endpoint The API endpoint (relative to base URL).
|
||||
* @param options Fetch options such as method, headers, and body.
|
||||
* @returns The JSON-parsed response.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
async function authenticatedRequest(endpoint: string, options: RequestInit): Promise<any> {
|
||||
const token = localStorage.getItem('authToken'); // Replace with a secure token storage solution if needed
|
||||
if (!token) {
|
||||
throw new Error('Authentication token is missing. Please log in.');
|
||||
}
|
||||
|
||||
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
|
||||
...options,
|
||||
headers: {
|
||||
...options.headers,
|
||||
Authorization: `Bearer ${token}`, // Include the token in the Authorization header
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Error: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new form (authenticated).
|
||||
* Create a new form.
|
||||
* @param name The name of the form.
|
||||
* @param fields The fields of the form in JSON format.
|
||||
* @returns The ID of the created form.
|
||||
@ -75,10 +31,16 @@ export async function createForm(name: string, fields: unknown): Promise<string>
|
||||
},
|
||||
body: JSON.stringify({ name, fields })
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Error creating form: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all forms (authenticated).
|
||||
* Get all forms.
|
||||
* @returns An array of forms.
|
||||
*/
|
||||
export async function getForms(): Promise<unknown[]> {
|
||||
@ -99,7 +61,7 @@ export async function getForms(): Promise<unknown[]> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit a form (unauthenticated).
|
||||
* Submit a form.
|
||||
* @param formId The ID of the form to submit.
|
||||
* @param data The submission data in JSON format.
|
||||
* @returns The ID of the created submission.
|
||||
@ -123,9 +85,9 @@ export async function submitForm(formId: string, data: unknown): Promise<string>
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin login to get a token.
|
||||
* @param credentials The login credentials (username and password).
|
||||
* @returns The generated JWT token if successful.
|
||||
* Get all submissions for a specific form.
|
||||
* @param formId The ID of the form.
|
||||
* @returns An array of submissions for the form.
|
||||
*/
|
||||
export async function getSubmissions(formId: string): Promise<unknown[]> {
|
||||
const token = getAuthToken(); // Get the token from storage
|
||||
|
@ -1,17 +0,0 @@
|
||||
import type { AdminUser } from './types';
|
||||
|
||||
const key2 = 'username';
|
||||
|
||||
function login(user: AdminUser) {
|
||||
localStorage.setItem(key2, user.username);
|
||||
}
|
||||
|
||||
function logout() {
|
||||
localStorage.removeItem(key2);
|
||||
}
|
||||
|
||||
function loggedIn() {
|
||||
return localStorage.getItem('authToken') !== null;
|
||||
}
|
||||
|
||||
export default { login, logout, loggedIn };
|
@ -17,13 +17,3 @@ export interface Submission {
|
||||
data: Record<string, unknown>;
|
||||
created_at?: string;
|
||||
}
|
||||
|
||||
export interface LoginCredentials {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface AdminUser {
|
||||
username: string;
|
||||
password_hash: string;
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
import { getForms } from '../../../lib/api';
|
||||
import type { Form } from '../../../lib/types';
|
||||
|
||||
let forms: Form[] = [];
|
||||
let forms: any;
|
||||
|
||||
onMount(async () => {
|
||||
forms = await getForms();
|
||||
|
Loading…
Reference in New Issue
Block a user