From 542bc6471b56a514738584f1444fb7a999ac5959 Mon Sep 17 00:00:00 2001 From: KanjiG Date: Tue, 28 Jul 2026 20:42:29 +0200 Subject: [PATCH] Initial commit: Veritas Suite Main Server and Core Engine --- .cargo/config.toml | 6 + Cargo.toml | 42 + crates/veritas-core/Cargo.toml | 13 + crates/veritas-core/src/hwid.rs | 17 + crates/veritas-core/src/lib.rs | 115 +++ crates/veritas-core/src/license.rs | 82 ++ crates/veritas-server/Cargo.toml | 20 + crates/veritas-server/src/main.rs | 813 ++++++++++++++++++++ crates/veritas-server/static/admin.html | 169 ++++ crates/veritas-server/static/app.js | 472 ++++++++++++ crates/veritas-server/static/downloads.html | 122 +++ crates/veritas-server/static/index.html | 178 +++++ crates/veritas-server/static/portal.html | 223 ++++++ crates/veritas-server/static/styles.css | 598 ++++++++++++++ vps_account_db.sh | 6 + vps_admin_db.sh | 4 + vps_setup_db.sh | 4 + web Server data.txt | 6 + 18 files changed, 2890 insertions(+) create mode 100644 .cargo/config.toml create mode 100644 Cargo.toml create mode 100644 crates/veritas-core/Cargo.toml create mode 100644 crates/veritas-core/src/hwid.rs create mode 100644 crates/veritas-core/src/lib.rs create mode 100644 crates/veritas-core/src/license.rs create mode 100644 crates/veritas-server/Cargo.toml create mode 100644 crates/veritas-server/src/main.rs create mode 100644 crates/veritas-server/static/admin.html create mode 100644 crates/veritas-server/static/app.js create mode 100644 crates/veritas-server/static/downloads.html create mode 100644 crates/veritas-server/static/index.html create mode 100644 crates/veritas-server/static/portal.html create mode 100644 crates/veritas-server/static/styles.css create mode 100644 vps_account_db.sh create mode 100644 vps_admin_db.sh create mode 100644 vps_setup_db.sh create mode 100644 web Server data.txt diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..1bfd7e4 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,6 @@ +[target.x86_64-pc-windows-gnullvm] +rustflags = [ + "-C", "link-arg=-static", + "-C", "link-arg=-static-libgcc", + "-C", "link-self-contained=yes" +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..428e99f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,42 @@ +[workspace] +resolver = "2" +members = [ + "crates/veritas-core", + "crates/veritas-veritas/vre", + "crates/veritas-veritas/vim", + "crates/veritas-veritas/van", + "crates/veritas-veritas/registry-explorer", + "crates/veritas-aegis/auditor-wipe", + "crates/veritas-aegis/ransomguard", + "crates/veritas-aegis/triage", + "crates/veritas-aegis/processguard", + "crates/veritas-cura/one-click-clean", + "crates/veritas-cura/easyrescue", + "crates/veritas-cura/privacy-shield", + "crates/veritas-cura/systemdoktor", + "crates/veritas-cura/spooler-reset", + "crates/veritas-cura/update-fix", + "crates/veritas-cura/system-integrator", + "crates/veritas-cura/port-doctor", + "crates/veritas-cura/association-fix", + "crates/veritas-server", +] + +[workspace.package] +version = "1.0.0" +edition = "2021" +authors = ["Veritas Suite Team"] +license = "MIT OR Apache-2.0" + +[workspace.dependencies] +tokio = { version = "1.38", features = ["full"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +anyhow = "1.0" +tracing = "0.1" +tracing-subscriber = "0.3" +sha2 = "0.10" +rsa = "0.9" +chrono = { version = "0.4", default-features = false, features = ["serde", "std", "clock"] } +axum = "0.7" +sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres", "chrono", "uuid"] } diff --git a/crates/veritas-core/Cargo.toml b/crates/veritas-core/Cargo.toml new file mode 100644 index 0000000..0d1c2b4 --- /dev/null +++ b/crates/veritas-core/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "veritas-core" +version.workspace = true +edition.workspace = true +authors.workspace = true + +[dependencies] +serde.workspace = true +serde_json.workspace = true +sha2.workspace = true +rsa.workspace = true +chrono.workspace = true +anyhow.workspace = true diff --git a/crates/veritas-core/src/hwid.rs b/crates/veritas-core/src/hwid.rs new file mode 100644 index 0000000..f9001c8 --- /dev/null +++ b/crates/veritas-core/src/hwid.rs @@ -0,0 +1,17 @@ +use sha2::{Digest, Sha256}; + +/// Computes a unique hardware fingerprint (CPU / Machine identifier). +pub fn generate_hardware_id() -> String { + let mut hasher = Sha256::new(); + let hostname = std::env::var("COMPUTERNAME") + .or_else(|_| std::env::var("HOSTNAME")) + .unwrap_or_else(|_| "UNKNOWN-HOST".to_string()); + + let username = std::env::var("USERNAME") + .or_else(|_| std::env::var("USER")) + .unwrap_or_else(|_| "UNKNOWN-USER".to_string()); + + let raw_id = format!("{}:{}", hostname, username); + hasher.update(raw_id.as_bytes()); + format!("{:x}", hasher.finalize()) +} diff --git a/crates/veritas-core/src/lib.rs b/crates/veritas-core/src/lib.rs new file mode 100644 index 0000000..270b425 --- /dev/null +++ b/crates/veritas-core/src/lib.rs @@ -0,0 +1,115 @@ +use serde::{Deserialize, Serialize}; + +pub mod hwid; +pub mod license; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ActivationRequest { + pub license_key: String, + pub hardware_id: String, + pub app_name: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ActivationResponse { + pub success: bool, + pub message: String, + pub signed_token: Option, + pub expires_at: Option>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct RegisterRequest { + pub email: String, + pub password: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LoginRequest { + pub email: String, + pub password: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AuthResponse { + pub success: bool, + pub message: String, + pub user_id: Option, + pub email: Option, + pub auth_token: Option, + pub is_admin: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct UserLicenseInfo { + pub license_key: String, + pub plan_name: String, + pub max_devices: i32, + pub activated_devices: i64, + pub active: bool, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct UserDashboardResponse { + pub success: bool, + pub email: String, + pub licenses: Vec, +} + +// Admin Panel Structures +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AdminUserItem { + pub id: i32, + pub email: String, + pub is_admin: bool, + pub created_at: String, + pub license_count: i64, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AdminLicenseItem { + pub id: i32, + pub user_id: Option, + pub user_email: Option, + pub license_key: String, + pub plan_name: String, + pub max_devices: i32, + pub activated_devices: i64, + pub active: bool, + pub created_at: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AdminStatsResponse { + pub success: bool, + pub total_users: i64, + pub total_licenses: i64, + pub total_activations: i64, + pub users: Vec, + pub licenses: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CreateLicenseRequest { + pub user_id: i32, + pub plan_name: String, + pub max_devices: i32, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ToggleLicenseRequest { + pub license_key: String, + pub active: bool, +} + +// Download Center Structures +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SoftwareDownloadItem { + pub name: String, + pub version: String, + pub category: String, + pub file_size: String, + pub download_url: String, + pub description: String, + pub is_available: bool, +} diff --git a/crates/veritas-core/src/license.rs b/crates/veritas-core/src/license.rs new file mode 100644 index 0000000..c4a10d7 --- /dev/null +++ b/crates/veritas-core/src/license.rs @@ -0,0 +1,82 @@ +use crate::{hwid::generate_hardware_id, ActivationRequest}; +use sha2::{Digest, Sha256}; + +pub struct LicenseClient { + pub server_url: String, + pub app_name: String, + pub secret: String, +} + +impl LicenseClient { + pub fn new(server_url: impl Into, app_name: impl Into) -> Self { + Self { + server_url: server_url.into(), + app_name: app_name.into(), + secret: "VERITAS_SECRET_KEY_SIGNING_2026".to_string(), + } + } + + pub fn generate_request(&self, license_key: &str) -> ActivationRequest { + ActivationRequest { + license_key: license_key.to_string(), + hardware_id: generate_hardware_id(), + app_name: self.app_name.clone(), + } + } + + pub fn verify_token_offline(&self, token: &str, license_key: &str, target_hwid: &str) -> bool { + let parts: Vec<&str> = token.split(':').collect(); + if parts.len() != 4 { + return false; + } + + let token_key = parts[0]; + let token_hwid = parts[1]; + let token_exp: i64 = match parts[2].parse() { + Ok(exp) => exp, + Err(_) => return false, + }; + let token_sig = parts[3]; + + if token_key != license_key || token_hwid != target_hwid { + return false; + } + + if chrono::Utc::now().timestamp() > token_exp { + return false; + } + + let signature_input = format!( + "{}:{}:{}:{}", + token_key, token_hwid, token_exp, self.secret + ); + let mut hasher = Sha256::new(); + hasher.update(signature_input.as_bytes()); + let expected_sig = format!("{:x}", hasher.finalize()); + + token_sig == expected_sig + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_token_verification() { + let client = LicenseClient::new("http://45.156.84.238", "VRE"); + let hwid = "WINDOWS-TEST-HWID-001"; + let key = "VERITAS-PRO-DEMO-2026"; + let exp = chrono::Utc::now().timestamp() + 3600; + + let sig_input = format!("{}:{}:{}:{}", key, hwid, exp, client.secret); + let mut hasher = Sha256::new(); + hasher.update(sig_input.as_bytes()); + let sig = format!("{:x}", hasher.finalize()); + + let token = format!("{}:{}:{}:{}", key, hwid, exp, sig); + + assert!(client.verify_token_offline(&token, key, hwid)); + assert!(!client.verify_token_offline(&token, "WRONG-KEY", hwid)); + } +} diff --git a/crates/veritas-server/Cargo.toml b/crates/veritas-server/Cargo.toml new file mode 100644 index 0000000..027fb64 --- /dev/null +++ b/crates/veritas-server/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "veritas-server" +version.workspace = true +edition.workspace = true +authors.workspace = true + +[dependencies] +veritas-core = { path = "../veritas-core" } +tokio.workspace = true +axum.workspace = true +serde.workspace = true +serde_json.workspace = true +tracing.workspace = true +tracing-subscriber.workspace = true +anyhow.workspace = true +chrono.workspace = true +rsa.workspace = true +sha2.workspace = true +sqlx = { workspace = true } +tower-http = { version = "0.5", features = ["fs", "cors"] } diff --git a/crates/veritas-server/src/main.rs b/crates/veritas-server/src/main.rs new file mode 100644 index 0000000..0c9eed2 --- /dev/null +++ b/crates/veritas-server/src/main.rs @@ -0,0 +1,813 @@ +use axum::{ + extract::{Query, State}, + http::{header, HeaderMap, HeaderValue, StatusCode}, + middleware::{self, Next}, + response::{Html, IntoResponse, Redirect, Response}, + routing::{get, post}, + Json, Router, +}; +use serde::Deserialize; +use sha2::{Digest, Sha256}; +use sqlx::{PgPool, Row}; +use std::{collections::HashMap, net::SocketAddr, path::Path, sync::Arc}; +use tokio::sync::Mutex as AsyncMutex; +use tower_http::services::ServeDir; +use tracing_subscriber::fmt; +use veritas_core::{ + ActivationRequest, ActivationResponse, AdminLicenseItem, AdminStatsResponse, AdminUserItem, + AuthResponse, CreateLicenseRequest, LoginRequest, RegisterRequest, SoftwareDownloadItem, + ToggleLicenseRequest, UserDashboardResponse, UserLicenseInfo, +}; + +#[derive(Clone)] +pub struct AppState { + pub db: Option, + pub secret: String, + pub static_dir: String, + pub rate_limiter: Arc>>, +} + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + fmt::init(); + + let db_url = std::env::var("DATABASE_URL").unwrap_or_else(|_| { + "postgres://veritas:veritas_db_password_2026!@localhost/veritas_db".to_string() + }); + + let db_pool = match PgPool::connect(&db_url).await { + Ok(pool) => { + println!("✅ Connected to PostgreSQL database successfully."); + Some(pool) + } + Err(e) => { + println!("⚠️ PostgreSQL connection failed ({}), running in fallback mode.", e); + None + } + }; + + let static_dir = if Path::new("crates/veritas-server/static").exists() { + "crates/veritas-server/static".to_string() + } else { + "static".to_string() + }; + + println!("📁 Serving Web Portal static files from: {}", static_dir); + + let state = Arc::new(AppState { + db: db_pool, + secret: "VERITAS_SECRET_KEY_SIGNING_2026_ENTERPRISE_HMAC".to_string(), + static_dir, + rate_limiter: Arc::new(AsyncMutex::new(HashMap::new())), + }); + + let app = Router::new() + // Clean Pretty URLs (NO .html in URL bar) + .route("/", get(serve_index)) + .route("/downloads", get(serve_downloads)) + .route("/portal", get(serve_portal)) + .route("/admin", get(serve_admin)) + // Redirect legacy .html URLs to clean URLs + .route("/index.html", get(|| async { Redirect::permanent("/") })) + .route("/downloads.html", get(|| async { Redirect::permanent("/downloads") })) + .route("/portal.html", get(|| async { Redirect::permanent("/portal") })) + .route("/admin.html", get(|| async { Redirect::permanent("/admin") })) + // Core API Endpoints + .route("/health", get(health_check)) + .route("/api/v1/activate", post(activate_license)) + .route("/api/v1/auth/register", post(register_user)) + .route("/api/v1/auth/login", post(login_user)) + .route("/api/v1/account/dashboard", get(get_dashboard)) + .route("/api/v1/downloads/list", get(get_downloads)) + // Server-Side Protected Admin Endpoints (Requires Admin JWT Token) + .route("/api/v1/admin/stats", get(get_admin_stats)) + .route("/api/v1/admin/create-license", post(admin_create_license)) + .route("/api/v1/admin/toggle-license", post(admin_toggle_license)) + .fallback_service(ServeDir::new(&state.static_dir)) + .layer(middleware::from_fn(add_security_headers)) + .with_state(state); + + let port: u16 = std::env::var("PORT") + .unwrap_or_else(|_| "8080".to_string()) + .parse() + .unwrap_or(8080); + + let addr = SocketAddr::from(([0, 0, 0, 0], port)); + println!("🌐 Veritas Suite Enterprise Secure Server running on http://{}", addr); + + let listener = tokio::net::TcpListener::bind(addr).await?; + axum::serve(listener, app).await?; + + Ok(()) +} + +// TASK 3: HTTP Security Headers Middleware +async fn add_security_headers(req: axum::extract::Request, next: Next) -> Response { + let mut res = next.run(req).await; + let headers = res.headers_mut(); + + headers.insert( + header::STRICT_TRANSPORT_SECURITY, + HeaderValue::from_static("max-age=31536000; includeSubDomains; preload"), + ); + headers.insert( + header::X_FRAME_OPTIONS, + HeaderValue::from_static("DENY"), + ); + headers.insert( + header::X_CONTENT_TYPE_OPTIONS, + HeaderValue::from_static("nosniff"), + ); + headers.insert( + header::X_XSS_PROTECTION, + HeaderValue::from_static("1; mode=block"), + ); + headers.insert( + header::CONTENT_SECURITY_POLICY, + HeaderValue::from_static("default-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://fonts.gstatic.com; img-src 'self' data:; font-src 'self' https://fonts.gstatic.com;"), + ); + + res +} + +async fn serve_index(State(state): State>) -> impl IntoResponse { + let path = Path::new(&state.static_dir).join("index.html"); + match std::fs::read_to_string(path) { + Ok(html) => Html(html).into_response(), + Err(_) => (StatusCode::NOT_FOUND, "Index page not found").into_response(), + } +} + +async fn serve_downloads(State(state): State>) -> impl IntoResponse { + let path = Path::new(&state.static_dir).join("downloads.html"); + match std::fs::read_to_string(path) { + Ok(html) => Html(html).into_response(), + Err(_) => (StatusCode::NOT_FOUND, "Downloads page not found").into_response(), + } +} + +async fn serve_portal(State(state): State>) -> impl IntoResponse { + let path = Path::new(&state.static_dir).join("portal.html"); + match std::fs::read_to_string(path) { + Ok(html) => Html(html).into_response(), + Err(_) => (StatusCode::NOT_FOUND, "Portal page not found").into_response(), + } +} + +async fn serve_admin(State(state): State>) -> impl IntoResponse { + let path = Path::new(&state.static_dir).join("admin.html"); + match std::fs::read_to_string(path) { + Ok(html) => Html(html).into_response(), + Err(_) => (StatusCode::NOT_FOUND, "Admin page not found").into_response(), + } +} + +async fn health_check() -> &'static str { + "OK - Veritas Suite Enterprise Secure Backend v1.4" +} + +fn hash_password(password: &str) -> String { + let mut hasher = Sha256::new(); + hasher.update(format!("VERITAS_SALT_2026:{}", password).as_bytes()); + format!("{:x}", hasher.finalize()) +} + +// TASK 2: Cryptographic JWT Generator & Validator with Expiration +fn create_jwt_token(user_id: i32, email: &str, is_admin: bool, secret: &str) -> String { + let exp = (chrono::Utc::now() + chrono::Duration::days(30)).timestamp(); + let payload = format!("{}:{}:{}:{}", user_id, email, is_admin, exp); + + let mut hasher = Sha256::new(); + hasher.update(format!("{}:{}", payload, secret).as_bytes()); + let sig = format!("{:x}", hasher.finalize()); + + format!("{}.{}", payload, sig) +} + +fn verify_jwt_token(token: &str, secret: &str) -> Option<(i32, String, bool)> { + let parts: Vec<&str> = token.split('.').collect(); + if parts.len() != 2 { + return None; + } + + let payload = parts[0]; + let sig = parts[1]; + + let mut hasher = Sha256::new(); + hasher.update(format!("{}:{}", payload, secret).as_bytes()); + let expected_sig = format!("{:x}", hasher.finalize()); + + if sig != expected_sig { + return None; + } + + let fields: Vec<&str> = payload.split(':').collect(); + if fields.len() != 4 { + return None; + } + + let uid: i32 = fields[0].parse().ok()?; + let email = fields[1].to_string(); + let is_admin: bool = fields[2].parse().ok()?; + let exp: i64 = fields[3].parse().ok()?; + + // Check expiration timestamp + if chrono::Utc::now().timestamp() > exp { + return None; + } + + Some((uid, email, is_admin)) +} + +// Verify Admin Token Guard +async fn verify_admin_token(headers: &HeaderMap, state: &AppState) -> bool { + let auth_header = match headers.get("Authorization") { + Some(val) => match val.to_str() { + Ok(s) => s, + Err(_) => return false, + }, + None => return false, + }; + + let token = auth_header.trim_start_matches("Bearer ").trim(); + if token.is_empty() { + return false; + } + + if let Some((_uid, _email, is_admin)) = verify_jwt_token(token, &state.secret) { + return is_admin; + } + + false +} + +// TASK 1: API Rate Limiter Guard (Brute Force Protection) +async fn check_rate_limit(state: &AppState, key: &str) -> bool { + let mut limiter = state.rate_limiter.lock().await; + let now = chrono::Utc::now().timestamp(); + + let entry = limiter.entry(key.to_string()).or_insert((0, now)); + + // Reset window after 60 seconds + if now - entry.1 > 60 { + entry.0 = 0; + entry.1 = now; + } + + entry.0 += 1; + entry.0 <= 5 // Max 5 attempts per 60s +} + +async fn register_user( + State(state): State>, + Json(payload): Json, +) -> Json { + if payload.email.trim().is_empty() || payload.password.trim().is_empty() { + return Json(AuthResponse { + success: false, + message: "Email and password are required.".to_string(), + user_id: None, + email: None, + auth_token: None, + is_admin: None, + }); + } + + if let Some(pool) = &state.db { + let pass_hash = hash_password(&payload.password); + let insert_res = sqlx::query( + "INSERT INTO users (email, password_hash) VALUES ($1, $2) RETURNING id, is_admin" + ) + .bind(&payload.email) + .bind(&pass_hash) + .fetch_one(pool) + .await; + + match insert_res { + Ok(row) => { + let user_id: i32 = row.get("id"); + let is_admin: bool = row.get("is_admin"); + + let lic_key = format!("VERITAS-PRO-{}-{}", user_id, &pass_hash[..8].to_uppercase()); + let _ = sqlx::query( + "INSERT INTO licenses (user_id, license_key, plan_name, max_devices) VALUES ($1, $2, $3, $4)" + ) + .bind(user_id) + .bind(&lic_key) + .bind("Veritas Suite Pro") + .bind(5) + .execute(pool) + .await; + + let jwt = create_jwt_token(user_id, &payload.email, is_admin, &state.secret); + + Json(AuthResponse { + success: true, + message: "User registered successfully. License key generated.".to_string(), + user_id: Some(user_id), + email: Some(payload.email), + auth_token: Some(jwt), + is_admin: Some(is_admin), + }) + } + Err(_) => Json(AuthResponse { + success: false, + message: "Email already registered or registration failed.".to_string(), + user_id: None, + email: None, + auth_token: None, + is_admin: None, + }), + } + } else { + Json(AuthResponse { + success: false, + message: "Database unavailable.".to_string(), + user_id: None, + email: None, + auth_token: None, + is_admin: None, + }) + } +} + +async fn login_user( + State(state): State>, + Json(payload): Json, +) -> Json { + // Check Rate Limiting for IP / email combination + if !check_rate_limit(&state, &payload.email).await { + return Json(AuthResponse { + success: false, + message: "⚠️ Zu viele Anmeldeversuche. Bitte warte 60 Sekunden (Brute-Force Schutz).".to_string(), + user_id: None, + email: None, + auth_token: None, + is_admin: None, + }); + } + + if let Some(pool) = &state.db { + let pass_hash = hash_password(&payload.password); + let user_res = sqlx::query( + "SELECT id, email, is_admin FROM users WHERE email = $1 AND password_hash = $2" + ) + .bind(&payload.email) + .bind(&pass_hash) + .fetch_optional(pool) + .await; + + match user_res { + Ok(Some(row)) => { + let user_id: i32 = row.get("id"); + let email: String = row.get("email"); + let is_admin: bool = row.get("is_admin"); + let jwt = create_jwt_token(user_id, &email, is_admin, &state.secret); + + Json(AuthResponse { + success: true, + message: "Login successful.".to_string(), + user_id: Some(user_id), + email: Some(email), + auth_token: Some(jwt), + is_admin: Some(is_admin), + }) + } + _ => Json(AuthResponse { + success: false, + message: "Invalid email or password.".to_string(), + user_id: None, + email: None, + auth_token: None, + is_admin: None, + }), + } + } else { + Json(AuthResponse { + success: false, + message: "Database unavailable.".to_string(), + user_id: None, + email: None, + auth_token: None, + is_admin: None, + }) + } +} + +#[derive(Deserialize)] +pub struct DashboardQuery { + pub user_id: i32, +} + +async fn get_dashboard( + State(state): State>, + Query(query): Query, +) -> Json { + if let Some(pool) = &state.db { + let user_res = sqlx::query("SELECT email FROM users WHERE id = $1") + .bind(query.user_id) + .fetch_optional(pool) + .await; + + if let Ok(Some(u_row)) = user_res { + let email: String = u_row.get("email"); + let lics = sqlx::query( + "SELECT license_key, plan_name, max_devices, active FROM licenses WHERE user_id = $1" + ) + .bind(query.user_id) + .fetch_all(pool) + .await + .unwrap_or_default(); + + let mut license_infos = Vec::new(); + for l in lics { + let lic_key: String = l.get("license_key"); + let plan_name: String = l.get("plan_name"); + let max_devices: i32 = l.get("max_devices"); + let active: bool = l.get("active"); + + let count_res = sqlx::query( + "SELECT COUNT(*) as count FROM activations WHERE license_key = $1" + ) + .bind(&lic_key) + .fetch_one(pool) + .await; + let count: i64 = count_res.map(|r| r.get("count")).unwrap_or(0); + + license_infos.push(UserLicenseInfo { + license_key: lic_key, + plan_name, + max_devices, + activated_devices: count, + active, + }); + } + + return Json(UserDashboardResponse { + success: true, + email, + licenses: license_infos, + }); + } + } + + Json(UserDashboardResponse { + success: false, + email: "".to_string(), + licenses: vec![], + }) +} + +async fn get_downloads() -> Json> { + let items = vec![ + SoftwareDownloadItem { + name: "Veritas SpoolerReset".to_string(), + version: "v1.2.0 (Commercial Edition)".to_string(), + category: "Windows Fixer (Cura)".to_string(), + file_size: "14.2 MB".to_string(), + download_url: "/spooler-reset.exe".to_string(), + description: "Autonomous Windows Druckspooler & Warteschlangen-Reparatur mit GPU GUI, UAC Elevation & VPS Lizenzprüfung.".to_string(), + is_available: true, + }, + SoftwareDownloadItem { + name: "Veritas RansomGuard".to_string(), + version: "v1.0.0 (Commercial Edition)".to_string(), + category: "IT-Sicherheit (Aegis)".to_string(), + file_size: "14.4 MB".to_string(), + download_url: "/ransomguard.exe".to_string(), + description: "Echtzeit Shannon-Entropie Scanner & Kanarienvogel-Köder gegen Krypto-Trojaner mit Autostart & VPS Aktivierung.".to_string(), + is_available: true, + }, + ]; + Json(items) +} + +// Server-Side Protected Admin APIs (Requires valid Admin JWT token) +async fn get_admin_stats( + headers: HeaderMap, + State(state): State>, +) -> Json { + if !verify_admin_token(&headers, &state).await { + return Json(AdminStatsResponse { + success: false, + total_users: 0, + total_licenses: 0, + total_activations: 0, + users: vec![], + licenses: vec![], + }); + } + + if let Some(pool) = &state.db { + let u_count: i64 = sqlx::query("SELECT COUNT(*) as count FROM users") + .fetch_one(pool) + .await + .map(|r| r.get("count")) + .unwrap_or(0); + + let l_count: i64 = sqlx::query("SELECT COUNT(*) as count FROM licenses") + .fetch_one(pool) + .await + .map(|r| r.get("count")) + .unwrap_or(0); + + let a_count: i64 = sqlx::query("SELECT COUNT(*) as count FROM activations") + .fetch_one(pool) + .await + .map(|r| r.get("count")) + .unwrap_or(0); + + let users_rows = sqlx::query( + "SELECT u.id, u.email, u.is_admin, u.created_at, COUNT(l.id) as l_count FROM users u LEFT JOIN licenses l ON u.id = l.user_id GROUP BY u.id, u.email, u.is_admin, u.created_at ORDER BY u.id DESC" + ) + .fetch_all(pool) + .await + .unwrap_or_default(); + + let mut users_list = Vec::new(); + for r in users_rows { + let created: chrono::NaiveDateTime = r.get("created_at"); + users_list.push(AdminUserItem { + id: r.get("id"), + email: r.get("email"), + is_admin: r.get("is_admin"), + created_at: created.format("%Y-%m-%d %H:%M").to_string(), + license_count: r.get("l_count"), + }); + } + + let lics_rows = sqlx::query( + "SELECT l.id, l.user_id, u.email as user_email, l.license_key, l.plan_name, l.max_devices, l.active, l.created_at FROM licenses l LEFT JOIN users u ON l.user_id = u.id ORDER BY l.id DESC" + ) + .fetch_all(pool) + .await + .unwrap_or_default(); + + let mut lics_list = Vec::new(); + for r in lics_rows { + let lic_key: String = r.get("license_key"); + let created: chrono::NaiveDateTime = r.get("created_at"); + + let count_res = sqlx::query("SELECT COUNT(*) as count FROM activations WHERE license_key = $1") + .bind(&lic_key) + .fetch_one(pool) + .await; + let act_count: i64 = count_res.map(|cr| cr.get("count")).unwrap_or(0); + + lics_list.push(AdminLicenseItem { + id: r.get("id"), + user_id: r.get("user_id"), + user_email: r.get("user_email"), + license_key: lic_key, + plan_name: r.get("plan_name"), + max_devices: r.get("max_devices"), + activated_devices: act_count, + active: r.get("active"), + created_at: created.format("%Y-%m-%d %H:%M").to_string(), + }); + } + + return Json(AdminStatsResponse { + success: true, + total_users: u_count, + total_licenses: l_count, + total_activations: a_count, + users: users_list, + licenses: lics_list, + }); + } + + Json(AdminStatsResponse { + success: false, + total_users: 0, + total_licenses: 0, + total_activations: 0, + users: vec![], + licenses: vec![], + }) +} + +async fn admin_create_license( + headers: HeaderMap, + State(state): State>, + Json(payload): Json, +) -> Json { + if !verify_admin_token(&headers, &state).await { + return Json(ActivationResponse { + success: false, + message: "Unauthorized: Admin privileges required.".to_string(), + signed_token: None, + expires_at: None, + }); + } + + if let Some(pool) = &state.db { + let lic_key = format!( + "VERITAS-{}-{}-{}", + payload.plan_name.to_uppercase().replace(" ", ""), + payload.user_id, + &sha2::Sha256::digest(chrono::Utc::now().to_string().as_bytes())[..4] + .iter() + .map(|b| format!("{:02X}", b)) + .collect::() + ); + + let res = sqlx::query( + "INSERT INTO licenses (user_id, license_key, plan_name, max_devices) VALUES ($1, $2, $3, $4)" + ) + .bind(payload.user_id) + .bind(&lic_key) + .bind(&payload.plan_name) + .bind(payload.max_devices) + .execute(pool) + .await; + + if res.is_ok() { + return Json(ActivationResponse { + success: true, + message: format!("License created: {}", lic_key), + signed_token: Some(lic_key), + expires_at: None, + }); + } + } + + Json(ActivationResponse { + success: false, + message: "Failed to create license.".to_string(), + signed_token: None, + expires_at: None, + }) +} + +async fn admin_toggle_license( + headers: HeaderMap, + State(state): State>, + Json(payload): Json, +) -> Json { + if !verify_admin_token(&headers, &state).await { + return Json(ActivationResponse { + success: false, + message: "Unauthorized: Admin privileges required.".to_string(), + signed_token: None, + expires_at: None, + }); + } + + if let Some(pool) = &state.db { + let res = sqlx::query("UPDATE licenses SET active = $1 WHERE license_key = $2") + .bind(payload.active) + .bind(&payload.license_key) + .execute(pool) + .await; + + if res.is_ok() { + return Json(ActivationResponse { + success: true, + message: format!("License active state set to {}", payload.active), + signed_token: None, + expires_at: None, + }); + } + } + + Json(ActivationResponse { + success: false, + message: "Failed to update license state.".to_string(), + signed_token: None, + expires_at: None, + }) +} + +async fn activate_license( + State(state): State>, + Json(payload): Json, +) -> Json { + tracing::info!( + "Activation request: key={}, hwid={}, app={}", + payload.license_key, + payload.hardware_id, + payload.app_name + ); + + if payload.license_key.trim().is_empty() || payload.hardware_id.trim().is_empty() { + return Json(ActivationResponse { + success: false, + message: "License key and Hardware ID are required.".to_string(), + signed_token: None, + expires_at: None, + }); + } + + if let Some(pool) = &state.db { + let lic_result = sqlx::query( + "SELECT max_devices, active FROM licenses WHERE license_key = $1" + ) + .bind(&payload.license_key) + .fetch_optional(pool) + .await; + + match lic_result { + Ok(Some(row)) => { + let active: bool = row.get("active"); + let max_devices: i32 = row.get("max_devices"); + + if !active { + return Json(ActivationResponse { + success: false, + message: "License key has been deactivated.".to_string(), + signed_token: None, + expires_at: None, + }); + } + + let count_result = sqlx::query( + "SELECT COUNT(*) as count FROM activations WHERE license_key = $1" + ) + .bind(&payload.license_key) + .fetch_one(pool) + .await; + + let activated_count: i64 = count_result.map(|r| r.get("count")).unwrap_or(0); + let max_devices = max_devices as i64; + + let is_already_active = sqlx::query( + "SELECT id FROM activations WHERE license_key = $1 AND hardware_id = $2" + ) + .bind(&payload.license_key) + .bind(&payload.hardware_id) + .fetch_optional(pool) + .await + .ok() + .flatten() + .is_some(); + + if !is_already_active && activated_count >= max_devices { + return Json(ActivationResponse { + success: false, + message: format!( + "Device limit reached ({}/{} max devices).", + activated_count, max_devices + ), + signed_token: None, + expires_at: None, + }); + } + + let _ = sqlx::query( + "INSERT INTO activations (license_key, hardware_id, app_name) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING" + ) + .bind(&payload.license_key) + .bind(&payload.hardware_id) + .bind(&payload.app_name) + .execute(pool) + .await; + } + Ok(None) => { + return Json(ActivationResponse { + success: false, + message: "Invalid license key.".to_string(), + signed_token: None, + expires_at: None, + }); + } + Err(e) => { + tracing::error!("DB Query Error: {}", e); + return Json(ActivationResponse { + success: false, + message: "Internal server error validating license.".to_string(), + signed_token: None, + expires_at: None, + }); + } + } + } + + let expires = chrono::Utc::now() + chrono::Duration::days(365); + let signature_input = format!( + "{}:{}:{}:{}", + payload.license_key, + payload.hardware_id, + expires.timestamp(), + state.secret + ); + + let mut hasher = Sha256::new(); + hasher.update(signature_input.as_bytes()); + let sig_hex = format!("{:x}", hasher.finalize()); + + let token = format!( + "{}:{}:{}:{}", + payload.license_key, + payload.hardware_id, + expires.timestamp(), + sig_hex + ); + + Json(ActivationResponse { + success: true, + message: "License activated successfully.".to_string(), + signed_token: Some(token), + expires_at: Some(expires), + }) +} diff --git a/crates/veritas-server/static/admin.html b/crates/veritas-server/static/admin.html new file mode 100644 index 0000000..2cafdf7 --- /dev/null +++ b/crates/veritas-server/static/admin.html @@ -0,0 +1,169 @@ + + + + + + Administrator Kontrollzentrum | Veritas Suite + + + + + + + + + +
+
+ + + + + +
+
+ + + + + +
+
🔒
+

Zugriff geschützt

+

+ Die Admin-Zentrale ist geschützt. Bitte melde dich zuerst im Kundenportal mit deinem Administrator-Konto an. +

+ + 👤 Zum Kundenportal & Admin-Login + +
+ + + +
+
+ + +
+ +
+ + + + diff --git a/crates/veritas-server/static/app.js b/crates/veritas-server/static/app.js new file mode 100644 index 0000000..4b28bc7 --- /dev/null +++ b/crates/veritas-server/static/app.js @@ -0,0 +1,472 @@ +// Veritas Suite Web Portal App Logic + +const API_BASE = ""; + +document.addEventListener("DOMContentLoaded", () => { + checkServerHealth(); + restoreSession(); + fetchDownloads(); + + // Check if we are on admin page + if (window.location.pathname === "/admin" || window.location.pathname.endsWith("admin.html")) { + checkAdminPageSession(); + } +}); + +// Check Server Connection +async function checkServerHealth() { + const indicator = document.querySelector(".status-indicator"); + const statusText = document.getElementById("status-text"); + + try { + const response = await fetch(`${API_BASE}/health`); + if (response.ok) { + if (indicator) indicator.classList.add("online"); + if (statusText) statusText.textContent = "VPS Server: Online (200 OK)"; + } else { + if (statusText) statusText.textContent = "VPS Server: Fehler"; + } + } catch (e) { + if (statusText) statusText.textContent = "VPS Server: Offline"; + } +} + +// Filter Sparten Tools +function filterSparten(category) { + document.querySelectorAll(".filter-btn").forEach(btn => btn.classList.remove("active")); + + const activeBtn = Array.from(document.querySelectorAll(".filter-btn")).find(btn => + btn.getAttribute("onclick") && btn.getAttribute("onclick").includes(`'${category}'`) + ); + if (activeBtn) activeBtn.classList.add("active"); + + const cards = document.querySelectorAll(".tool-card"); + cards.forEach(card => { + if (category === "all" || card.getAttribute("data-sparte") === category) { + card.style.display = "flex"; + } else { + card.style.display = "none"; + } + }); +} + +// Restore Session from LocalStorage +function restoreSession() { + const sessionStr = localStorage.getItem("veritas_session"); + if (sessionStr) { + try { + const session = JSON.parse(sessionStr); + if (session && session.user_id) { + showLoggedInState(session); + } else { + showLoggedOutState(); + } + } catch (e) { + localStorage.removeItem("veritas_session"); + showLoggedOutState(); + } + } else { + showLoggedOutState(); + } +} + +function showLoggedInState(session) { + const loggedOutDiv = document.getElementById("dash-logged-out"); + const loggedInDiv = document.getElementById("dash-logged-in"); + const userEmailSpan = document.getElementById("dash-user-email"); + const userStatusBar = document.getElementById("user-status-bar"); + const authForms = document.getElementById("auth-forms-container"); + const navAdminLink = document.getElementById("nav-admin-link"); + const btnAdminPanel = document.getElementById("btn-admin-panel"); + + if (loggedOutDiv) loggedOutDiv.style.display = "none"; + if (loggedInDiv) loggedInDiv.style.display = "block"; + if (userEmailSpan) userEmailSpan.textContent = session.email; + if (userStatusBar) userStatusBar.style.display = "inline-flex"; + if (authForms) authForms.style.display = "none"; + + // Show Admin Buttons ONLY if logged-in user is an Admin + if (session.is_admin) { + if (navAdminLink) navAdminLink.style.display = "inline-flex"; + if (btnAdminPanel) btnAdminPanel.style.display = "inline-block"; + } else { + if (navAdminLink) navAdminLink.style.display = "none"; + if (btnAdminPanel) btnAdminPanel.style.display = "none"; + } + + refreshDashboard(); +} + +function showLoggedOutState() { + const loggedOutDiv = document.getElementById("dash-logged-out"); + const loggedInDiv = document.getElementById("dash-logged-in"); + const userStatusBar = document.getElementById("user-status-bar"); + const authForms = document.getElementById("auth-forms-container"); + const navAdminLink = document.getElementById("nav-admin-link"); + const btnAdminPanel = document.getElementById("btn-admin-panel"); + + if (loggedOutDiv) loggedOutDiv.style.display = "block"; + if (loggedInDiv) loggedInDiv.style.display = "none"; + if (userStatusBar) userStatusBar.style.display = "none"; + if (authForms) authForms.style.display = "grid"; + if (navAdminLink) navAdminLink.style.display = "none"; + if (btnAdminPanel) btnAdminPanel.style.display = "none"; +} + +function logout() { + localStorage.removeItem("veritas_session"); + showLoggedOutState(); + showAlert("Erfolgreich abgemeldet.", "success"); + if (window.location.pathname === "/admin" || window.location.pathname.endsWith("admin.html")) { + window.location.href = "/portal"; + } +} + +// Handle Customer Registration +async function handleRegister(event) { + event.preventDefault(); + const email = document.getElementById("reg-email").value; + const password = document.getElementById("reg-password").value; + + try { + const response = await fetch(`${API_BASE}/api/v1/auth/register`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ email, password }) + }); + const data = await response.json(); + + if (data.success) { + showAlert(data.message, "success"); + const session = { user_id: data.user_id, email: data.email, token: data.auth_token, is_admin: data.is_admin }; + localStorage.setItem("veritas_session", JSON.stringify(session)); + showLoggedInState(session); + } else { + showAlert(data.message, "error"); + } + } catch (e) { + showAlert("Netzwerkfehler bei der Registrierung.", "error"); + } +} + +// Handle Customer & Admin Login in Portal +async function handleLogin(event) { + event.preventDefault(); + const email = document.getElementById("login-email").value; + const password = document.getElementById("login-password").value; + + try { + const response = await fetch(`${API_BASE}/api/v1/auth/login`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ email, password }) + }); + const data = await response.json(); + + if (data.success) { + showAlert(data.message, "success"); + const session = { user_id: data.user_id, email: data.email, token: data.auth_token, is_admin: data.is_admin }; + localStorage.setItem("veritas_session", JSON.stringify(session)); + showLoggedInState(session); + + if (data.is_admin) { + showAlert("👑 Willkommen Admin! Du hast Zugriff auf die Admin-Zentrale.", "success"); + } + } else { + showAlert(data.message, "error"); + } + } catch (e) { + showAlert("Netzwerkfehler beim Anmelden.", "error"); + } +} + +// Check Admin Page Session (Requires Admin Login in Customer Portal) +function checkAdminPageSession() { + const sessionStr = localStorage.getItem("veritas_session"); + const loggedOutState = document.getElementById("admin-logged-out-state"); + const loggedInState = document.getElementById("admin-logged-in-state"); + const userEmailDisplay = document.getElementById("admin-user-email-display"); + + if (sessionStr) { + try { + const session = JSON.parse(sessionStr); + if (session && session.is_admin) { + if (loggedOutState) loggedOutState.style.display = "none"; + if (loggedInState) loggedInState.style.display = "block"; + if (userEmailDisplay) userEmailDisplay.textContent = session.email; + fetchAdminStats(); + return; + } + } catch (e) {} + } + + if (loggedOutState) loggedOutState.style.display = "block"; + if (loggedInState) loggedInState.style.display = "none"; +} + +function logoutAdmin() { + logout(); +} + +// Refresh Customer Dashboard Data +async function refreshDashboard() { + const sessionStr = localStorage.getItem("veritas_session"); + if (!sessionStr) return; + + const session = JSON.parse(sessionStr); + const container = document.getElementById("license-list"); + if (!container) return; + container.innerHTML = "

Lizenzen werden geladen...

"; + + try { + const response = await fetch(`${API_BASE}/api/v1/account/dashboard?user_id=${session.user_id}`); + const data = await response.json(); + + if (data.success && data.licenses && data.licenses.length > 0) { + container.innerHTML = ""; + data.licenses.forEach(lic => { + const percent = Math.min(100, (lic.activated_devices / lic.max_devices) * 100); + const cardHtml = ` +
+
+ 🛡️ ${lic.plan_name} + ${lic.active ? '🟢 AKTIV' : '🔴 GESPERRT'} +
+
+ ${lic.license_key} + +
+
+
+ Aktivierte Geräte + ${lic.activated_devices} / ${lic.max_devices} Geräte +
+
+
+
+
+
+ `; + container.innerHTML += cardHtml; + + const simKeyInput = document.getElementById("sim-key"); + if (simKeyInput) simKeyInput.value = lic.license_key; + }); + } else { + container.innerHTML = "

Keine aktiven Lizenzen für dieses Konto gefunden.

"; + } + } catch (e) { + container.innerHTML = "

Fehler beim Laden des Dashboards.

"; + } +} + +// Fetch Software Downloads +async function fetchDownloads() { + const container = document.getElementById("downloads-container"); + if (!container) return; + + try { + const response = await fetch(`${API_BASE}/api/v1/downloads/list`); + const items = await response.json(); + + container.innerHTML = ""; + items.forEach(item => { + const card = ` +
+
+ ${item.category} + ${item.version} +
+

${item.name}

+

${item.description}

+ +
+ `; + container.innerHTML += card; + }); + } catch (e) { + container.innerHTML = "

Fehler beim Laden der Download-Liste.

"; + } +} + +// Fetch Admin Stats (Sends Authorization Bearer token) +async function fetchAdminStats() { + const sessionStr = localStorage.getItem("veritas_session"); + if (!sessionStr) return; + const session = JSON.parse(sessionStr); + + try { + const response = await fetch(`${API_BASE}/api/v1/admin/stats`, { + headers: { + "Authorization": `Bearer ${session.token}` + } + }); + const data = await response.json(); + + if (data.success) { + const usersEl = document.getElementById("admin-stat-users"); + const licsEl = document.getElementById("admin-stat-licenses"); + const actsEl = document.getElementById("admin-stat-activations"); + + if (usersEl) usersEl.textContent = data.total_users; + if (licsEl) licsEl.textContent = data.total_licenses; + if (actsEl) actsEl.textContent = data.total_activations; + + const tbody = document.getElementById("admin-licenses-table-body"); + if (tbody) { + tbody.innerHTML = ""; + data.licenses.forEach(lic => { + const tr = ` + + #${lic.id} + ${lic.license_key} + ${lic.user_email || 'Gast / System'} + ${lic.plan_name} + ${lic.activated_devices} / ${lic.max_devices} + ${lic.active ? '🟢 AKTIV' : '🔴 GESPERRT'} + + + + + `; + tbody.innerHTML += tr; + }); + } + } else { + showAdminAlert("Zugriff verweigert: " + (data.message || "Admin Privilegien erforderlich"), "error"); + } + } catch (e) { + console.error("Admin stats fetch failed", e); + } +} + +// Admin Create License Handler (Sends Authorization Bearer token) +async function handleAdminCreateLicense(event) { + event.preventDefault(); + const sessionStr = localStorage.getItem("veritas_session"); + if (!sessionStr) return; + const session = JSON.parse(sessionStr); + + const userId = parseInt(document.getElementById("admin-create-uid").value); + const planName = document.getElementById("admin-create-plan").value; + const maxDevices = parseInt(document.getElementById("admin-create-max").value); + + try { + const response = await fetch(`${API_BASE}/api/v1/admin/create-license`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${session.token}` + }, + body: JSON.stringify({ user_id: userId, plan_name: planName, max_devices: maxDevices }) + }); + const data = await response.json(); + + if (data.success) { + showAdminAlert("Lizenz erfolgreich erstellt: " + data.message, "success"); + fetchAdminStats(); + } else { + showAdminAlert("Fehler: " + data.message, "error"); + } + } catch (e) { + showAdminAlert("Netzwerkfehler beim Erstellen der Lizenz.", "error"); + } +} + +// Admin Toggle License Active/Deactive (Sends Authorization Bearer token) +async function toggleLicense(licenseKey, newActiveState) { + const sessionStr = localStorage.getItem("veritas_session"); + if (!sessionStr) return; + const session = JSON.parse(sessionStr); + + try { + const response = await fetch(`${API_BASE}/api/v1/admin/toggle-license`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${session.token}` + }, + body: JSON.stringify({ license_key: licenseKey, active: newActiveState }) + }); + const data = await response.json(); + + if (data.success) { + showAdminAlert("Lizenz-Status aktualisiert.", "success"); + fetchAdminStats(); + } else { + showAdminAlert("Fehler: " + data.message, "error"); + } + } catch (e) { + showAdminAlert("Netzwerkfehler beim Ändern des Lizenzstatus.", "error"); + } +} + +// Handle Activation Test +async function handleTestActivation(event) { + event.preventDefault(); + const licenseKey = document.getElementById("sim-key").value; + const hardwareId = document.getElementById("sim-hwid").value; + const appName = document.getElementById("sim-app").value; + + const outputBox = document.getElementById("sim-output-box"); + const jsonText = document.getElementById("sim-json-text"); + + try { + const response = await fetch(`${API_BASE}/api/v1/activate`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + license_key: licenseKey, + hardware_id: hardwareId, + app_name: appName + }) + }); + const data = await response.json(); + + outputBox.style.display = "block"; + jsonText.textContent = JSON.stringify(data, null, 2); + + refreshDashboard(); + } catch (e) { + outputBox.style.display = "block"; + jsonText.textContent = "Fehler bei der Kommunikation mit dem VPS Server."; + } +} + +// Helper: Show Alert for Customer Portal +function showAlert(msg, type) { + const box = document.getElementById("auth-alert"); + if (!box) return; + box.textContent = msg; + box.className = `alert-box ${type}`; + box.style.display = "block"; + setTimeout(() => { + box.style.display = "none"; + }, 5000); +} + +// Helper: Show Alert for Admin Portal +function showAdminAlert(msg, type) { + const box = document.getElementById("admin-alert"); + if (!box) return; + box.textContent = msg; + box.className = `alert-box ${type}`; + box.style.display = "block"; + setTimeout(() => { + box.style.display = "none"; + }, 5000); +} + +// Helper: Copy Key +function copyKey(text) { + navigator.clipboard.writeText(text).then(() => { + alert("Lizenzschlüssel in die Zwischenablage kopiert!"); + }); +} diff --git a/crates/veritas-server/static/downloads.html b/crates/veritas-server/static/downloads.html new file mode 100644 index 0000000..ed8284a --- /dev/null +++ b/crates/veritas-server/static/downloads.html @@ -0,0 +1,122 @@ + + + + + + Software Download-Center | Veritas Suite + + + + + + + + + +
+
+
+ + + + + +
+
+
+ OFFIZIELLES DOWNLOAD-CENTER +

Veritas Suite Software-Pakete

+

Lade fertige, frei stehende Windows Executables (.exe) direkt herunter. Alle Tools sind für 64-Bit Windows optimiert.

+
+ + +
+
+
+ CURA FIXER + ✅ Bereit zum Download +
+

Veritas SpoolerReset

+

Autonomous Windows Druckspooler & Warteschlangen-Reparatur mit GPU GUI, UAC Elevation & VPS Lizenzprüfung.

+ +
+ +
+
+ AEGIS SECURITY + ✅ Bereit zum Download +
+

Veritas RansomGuard

+

Echtzeit Shannon-Entropie Scanner & Kanarienvogel-Köder gegen Krypto-Trojaner mit Autostart & VPS Aktivierung.

+ +
+
+ + +
+

🖥️ Systemanforderungen & Installation

+
    +
  • Betriebssystem: Windows 10, Windows 11 oder Windows Server 2019/2022 (64-Bit).
  • +
  • Voraussetzungen: Keine externen Runtime-DLLs erforderlich (100% Statisch kompiliertes MinGW/GCC Rust Binary).
  • +
  • Rechte: Administrator-Rechte empfohlen (Erfordert UAC Bestätigung beim Start).
  • +
  • Lizenzierung: Nach dem Start einfach mit deinen Kundenkonto-Daten von https://kanjiv.at/portal anmelden.
  • +
+
+
+
+ + + + + + + diff --git a/crates/veritas-server/static/index.html b/crates/veritas-server/static/index.html new file mode 100644 index 0000000..c128055 --- /dev/null +++ b/crates/veritas-server/static/index.html @@ -0,0 +1,178 @@ + + + + + + Veritas Suite v1.0 | Cyber-Security, Forensik & Windows Optimization + + + + + + + + + +
+
+
+ + + + + +
+
+ + Fertiggestellte Software & Live Cloud-Backend +
+ +

+ Hochperformante Software für Security & Windows-Reparatur +

+ +

+ Die Veritas Suite bietet fertige, kommerzielle High-Performance Tools mit kryptografischem Lizenz- & Cloud-Ökosystem. Entwickelt in sicherem, rasend schnellem Rust. +

+ + + + +
+
+ 2 FERTIG + Programme bereit +
+
+
+ 2 + Aktive Sparten +
+
+
+ 100% LIVE + Download-Status +
+
+
+ VPS ONLINE + Cloud-Backend +
+
+ +
+ + +
+
+
+ FERTIGE SOFTWARE-PAKETE +

Verfügbare Programme der Veritas Suite

+

Übersicht aller einsatzbereiten Werkzeuge zum direkten Download.

+
+ + +
+ + + +
+ + +
+ + +
+
+ AEGIS SECURITY + ✅ Bereit zum Download +
+

Veritas RansomGuard

+

Echtzeit Shannon-Entropie Scanner im Hintergrund. Stoppt Krypto-Trojaner vor Massenverschlüsselungen & setzt Kanarienvogel-Köder gegen Ransomware.

+ +
+ + +
+
+ CURA FIXER + ✅ Bereit zum Download +
+

Veritas SpoolerReset

+

Autonomous Windows Druckspooler & Warteschlangen-Reparatur mit GPU GUI, automatischem UAC Elevation & VPS Lizenzprüfung.

+ +
+ +
+
+
he + + + + + + + + + + + + + diff --git a/crates/veritas-server/static/portal.html b/crates/veritas-server/static/portal.html new file mode 100644 index 0000000..99495a0 --- /dev/null +++ b/crates/veritas-server/static/portal.html @@ -0,0 +1,223 @@ + + + + + + Kundenportal & Control Center | Veritas Suite + + + + + + + + + +
+
+
+ + + + + +
+
+ + +
+ ALL-IN-ONE CONTROL CENTER +

Veritas Kundenportal

+

Verwalte deine Lizenzen, deine Anmeldedaten und deine Software-Downloads auf einer zentralen Übersichtsseite.

+
+ + + + + +
+
+
+ 👤 +
+

Konto & Authentifizierung

+

Melde dich an oder erstelle ein neues Konto mit automatischer Pro-Lizenz.

+
+
+ + + +
+ + +
+ +
+

🔑 Anmelden

+

Zugang zu deinen lizenzierten Tools

+
+
+ + +
+
+ + +
+ +
+
+ + +
+

⚡ Registrieren

+

Erhalte sofort eine Veritas Pro Lizenz (5 Geräte)

+
+
+ + +
+
+ + +
+ +
+
+
+
+ + +
+
+ 🔑 +
+

Deine Software-Lizenzen

+

Aktive Lizenzen, Schlüssel und verknüpfte Geräte-Slots

+
+
+ +
+
🔒
+

Bitte melde dich oben an, um deine Lizenzen einzusehen.

+
+ + +
+ + +
+
+ 📥 +
+

Software Download-Center

+

Lade die fertigen Windows Executables (.exe) direkt herunter

+
+
+ +
+
+ + +
+
+ +
+

Online Aktivierungs-Tester

+

Testet den Handshake zwischen den Windows-Tools und dem VPS Server

+
+
+ +
+
+
+ + +
+
+ + +
+
+ +
+ + +
+ + +
+ + +
+ +
+
+ + + + + + + diff --git a/crates/veritas-server/static/styles.css b/crates/veritas-server/static/styles.css new file mode 100644 index 0000000..660ccc3 --- /dev/null +++ b/crates/veritas-server/static/styles.css @@ -0,0 +1,598 @@ +/* ========================================================================== + VERITAS SUITE v1.0 - PREMIUM DESIGN SYSTEM (Obsidian & Cyber Glass) + ========================================================================== */ + +:root { + --bg-main: #030712; + --bg-surface: rgba(15, 23, 42, 0.75); + --bg-panel: rgba(30, 41, 59, 0.55); + --border-glass: rgba(255, 255, 255, 0.12); + --border-light: rgba(255, 255, 255, 0.22); + + --indigo: #6366f1; + --indigo-glow: rgba(99, 102, 241, 0.45); + --cyan: #38bdf8; + --cyan-glow: rgba(56, 189, 248, 0.45); + --emerald: #10b981; + --emerald-glow: rgba(16, 185, 129, 0.45); + --amber: #f59e0b; + --amber-glow: rgba(245, 158, 11, 0.45); + --rose: #f43f5e; + + --text-heading: #ffffff; + --text-body: #cbd5e1; + --text-muted: #94a3b8; + --text-dim: #64748b; + + --font-heading: 'Outfit', sans-serif; + --font-body: 'Inter', sans-serif; + + --radius-sm: 8px; + --radius-md: 16px; + --radius-lg: 24px; + --transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1); +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + background-color: var(--bg-main); + color: var(--text-body); + font-family: var(--font-body); + line-height: 1.6; + overflow-x: hidden; + min-height: 100vh; +} + +h1, h2, h3, h4, h5 { + font-family: var(--font-heading); + color: var(--text-heading); +} + +/* Background Glow Orbs */ +.glow-orb { + position: fixed; + border-radius: 50%; + filter: blur(150px); + z-index: -1; + pointer-events: none; + opacity: 0.6; +} +.orb-purple { + width: 600px; + height: 600px; + background: radial-gradient(circle, var(--indigo) 0%, transparent 70%); + top: -200px; + left: -150px; +} +.orb-cyan { + width: 500px; + height: 500px; + background: radial-gradient(circle, var(--cyan) 0%, transparent 70%); + top: 30%; + right: -200px; +} +.orb-emerald { + width: 550px; + height: 550px; + background: radial-gradient(circle, var(--emerald) 0%, transparent 70%); + bottom: -200px; + left: 20%; +} + +/* Glass Panel Component */ +.glass-panel { + background: var(--bg-surface); + backdrop-filter: blur(24px); + -webkit-backdrop-filter: blur(24px); + border: 1px solid var(--border-glass); + border-radius: var(--radius-md); + box-shadow: 0 20px 40px -15px rgba(0, 0, 0, 0.6); + transition: var(--transition); +} +.glass-panel:hover { + border-color: rgba(255, 255, 255, 0.2); + box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.7), 0 0 20px rgba(99, 102, 241, 0.15); +} + +/* Container */ +.container { + max-width: 1240px; + margin: 0 auto; + padding: 0 1.5rem; +} + +/* Navbar */ +.navbar { + position: sticky; + top: 0; + z-index: 100; + background: rgba(3, 7, 18, 0.88); + backdrop-filter: blur(20px); + border-bottom: 1px solid var(--border-glass); +} +.nav-container { + max-width: 1240px; + margin: 0 auto; + padding: 1rem 1.5rem; + display: flex; + justify-content: space-between; + align-items: center; +} +.logo { + display: flex; + align-items: center; + gap: 0.75rem; + text-decoration: none; +} +.logo-shield { + width: 42px; + height: 42px; + background: linear-gradient(135deg, var(--indigo) 0%, #38bdf8 100%); + border-radius: 12px; + display: flex; + align-items: center; + justify-content: center; + color: white; + box-shadow: 0 0 25px var(--indigo-glow); +} +.logo-brand { + display: flex; + flex-direction: column; +} +.brand-name { + font-family: var(--font-heading); + font-size: 1.3rem; + font-weight: 800; + letter-spacing: 0.05em; + color: white; +} +.brand-tag { + font-size: 0.7rem; + color: var(--cyan); + font-weight: 700; + letter-spacing: 0.1em; +} + +.nav-links { + display: flex; + gap: 1.5rem; + align-items: center; +} +.nav-item { + color: var(--text-muted); + text-decoration: none; + font-weight: 500; + font-size: 0.95rem; + transition: var(--transition); +} +.nav-item:hover, .nav-item.active { + color: white; +} +.portal-link { + display: flex; + align-items: center; + gap: 0.4rem; + padding: 0.55rem 1.2rem; + background: rgba(99, 102, 241, 0.18); + border: 1px solid rgba(99, 102, 241, 0.4); + border-radius: var(--radius-sm); + color: #a5b4fc; +} +.portal-link:hover { + background: var(--indigo); + color: white; + box-shadow: 0 0 25px var(--indigo-glow); +} + +.server-status-pill { + display: flex; + align-items: center; + gap: 0.6rem; + padding: 0.4rem 0.9rem; + background: rgba(255, 255, 255, 0.04); + border: 1px solid var(--border-glass); + border-radius: 50px; + font-size: 0.8rem; + color: var(--text-muted); +} +.status-indicator { + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--amber); + box-shadow: 0 0 10px var(--amber-glow); +} +.status-indicator.online { + background: var(--emerald); + box-shadow: 0 0 14px var(--emerald-glow); +} + +/* Hero Section */ +.hero { + padding: 6rem 1.5rem 4rem 1.5rem; + text-align: center; +} +.hero-container { + max-width: 900px; + margin: 0 auto; + display: flex; + flex-direction: column; + align-items: center; +} +.hero-badge { + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.45rem 1.3rem; + background: rgba(56, 189, 248, 0.12); + border: 1px solid rgba(56, 189, 248, 0.35); + border-radius: 50px; + color: #38bdf8; + font-size: 0.85rem; + font-weight: 600; + margin-bottom: 1.5rem; +} + +.hero-heading { + font-size: 3.6rem; + font-weight: 800; + line-height: 1.15; + margin-bottom: 1.5rem; +} +.gradient-text { + background: linear-gradient(135deg, #a5b4fc 0%, #38bdf8 50%, #34d399 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} +.hero-description { + font-size: 1.2rem; + color: var(--text-muted); + max-width: 720px; + margin-bottom: 2.5rem; +} + +.hero-buttons { + display: flex; + gap: 1.2rem; + margin-bottom: 3.5rem; +} + +/* Buttons */ +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.6rem; + padding: 0.85rem 1.8rem; + border-radius: var(--radius-sm); + font-weight: 600; + font-size: 1rem; + text-decoration: none; + cursor: pointer; + border: none; + transition: var(--transition); +} +.btn-glow { + background: linear-gradient(135deg, var(--indigo) 0%, #4f46e5 100%); + color: white; + box-shadow: 0 0 25px var(--indigo-glow); +} +.btn-glow:hover { + transform: translateY(-2px); + box-shadow: 0 0 35px var(--indigo-glow); +} +.btn-glass { + background: rgba(255, 255, 255, 0.05); + border: 1px solid var(--border-glass); + color: var(--text-heading); +} +.btn-glass:hover { + background: rgba(255, 255, 255, 0.12); +} +.btn-emerald { + background: linear-gradient(135deg, var(--emerald) 0%, #059669 100%); + color: white; + box-shadow: 0 0 25px var(--emerald-glow); +} +.btn-emerald:hover { + transform: translateY(-2px); + box-shadow: 0 0 35px var(--emerald-glow); +} +.btn-block { + width: 100%; +} +.btn-outline-danger { + background: rgba(244, 63, 94, 0.12); + border: 1px solid rgba(244, 63, 94, 0.4); + color: #fda4af; + padding: 0.45rem 1.1rem; + font-size: 0.85rem; + border-radius: var(--radius-sm); + cursor: pointer; +} +.btn-outline-danger:hover { + background: var(--rose); + color: white; +} +.btn-small { + padding: 0.4rem 0.9rem; + font-size: 0.85rem; +} + +/* Hero Stats Bar */ +.hero-stats { + width: 100%; + padding: 1.8rem 2.5rem; + display: flex; + justify-content: space-around; + align-items: center; +} +.stat-box { + display: flex; + flex-direction: column; + align-items: center; +} +.stat-val { + font-family: var(--font-heading); + font-size: 2.2rem; + font-weight: 800; + color: white; +} +.gradient-cyan { + background: linear-gradient(135deg, var(--cyan), #38bdf8); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} +.gradient-emerald { + background: linear-gradient(135deg, var(--emerald), #34d399); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} +.stat-desc { + font-size: 0.85rem; + color: var(--text-muted); + text-transform: uppercase; + letter-spacing: 0.05em; +} +.stat-sep { + width: 1px; + height: 40px; + background: var(--border-glass); +} + +/* Section Title Box */ +.section { + padding: 5rem 0; +} +.section-title-box { + text-align: center; + margin-bottom: 3rem; +} +.sub-title { + font-size: 0.8rem; + font-weight: 700; + letter-spacing: 0.15em; + color: var(--cyan); + margin-bottom: 0.5rem; + display: block; +} +.section-title-box h2 { + font-size: 2.5rem; + font-weight: 800; + margin-bottom: 0.75rem; +} +.section-title-box p { + color: var(--text-muted); + font-size: 1.1rem; +} + +/* Sparten Filter Bar */ +.filter-bar { + display: flex; + justify-content: center; + gap: 0.8rem; + margin-bottom: 3rem; + flex-wrap: wrap; +} +.filter-btn { + padding: 0.65rem 1.5rem; + background: rgba(255, 255, 255, 0.04); + border: 1px solid var(--border-glass); + border-radius: 50px; + color: var(--text-muted); + font-weight: 600; + font-size: 0.9rem; + cursor: pointer; + transition: var(--transition); +} +.filter-btn:hover { + color: white; + background: rgba(255, 255, 255, 0.08); +} +.filter-btn.active { + background: var(--indigo); + color: white; + border-color: var(--indigo); + box-shadow: 0 0 25px var(--indigo-glow); +} + +/* Tools Grid */ +.tools-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); + gap: 1.8rem; +} +.tool-card { + padding: 1.8rem; + display: flex; + flex-direction: column; + justify-content: space-between; + transition: var(--transition); + position: relative; + overflow: hidden; +} +.tool-card:hover { + transform: translateY(-6px); + border-color: var(--border-light); + box-shadow: 0 20px 40px -10px rgba(99, 102, 241, 0.25); +} +.card-top { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1rem; +} +.sparte-tag { + font-size: 0.7rem; + font-weight: 800; + letter-spacing: 0.08em; + padding: 0.25rem 0.6rem; + border-radius: 4px; +} +.tag-veritas { + background: rgba(99, 102, 241, 0.18); + color: #a5b4fc; + border: 1px solid rgba(99, 102, 241, 0.4); +} +.tag-aegis { + background: rgba(244, 63, 94, 0.18); + color: #fda4af; + border: 1px solid rgba(244, 63, 94, 0.4); +} +.tag-cura { + background: rgba(16, 185, 129, 0.18); + color: #6ee7b7; + border: 1px solid rgba(16, 185, 129, 0.4); +} + +/* Status Badges */ +.status-badge { + font-size: 0.75rem; + font-weight: 700; + padding: 0.3rem 0.75rem; + border-radius: 50px; +} +.badge-active { + background: rgba(16, 185, 129, 0.2); + color: #34d399; + border: 1px solid rgba(16, 185, 129, 0.4); +} +.badge-dev { + background: rgba(56, 189, 248, 0.2); + color: #38bdf8; + border: 1px solid rgba(56, 189, 248, 0.4); +} +.badge-planned { + background: rgba(245, 158, 11, 0.2); + color: #fde047; + border: 1px solid rgba(245, 158, 11, 0.4); +} + +.tool-card h3 { + font-size: 1.35rem; + margin-bottom: 0.6rem; +} +.tool-desc { + color: var(--text-muted); + font-size: 0.92rem; + margin-bottom: 1.5rem; + line-height: 1.5; +} +.card-footer { + display: flex; + gap: 0.5rem; + margin-top: auto; +} +.tech-tag { + font-size: 0.75rem; + background: rgba(255, 255, 255, 0.05); + border: 1px solid var(--border-glass); + padding: 0.25rem 0.7rem; + border-radius: 4px; + color: var(--text-dim); +} + +/* Form Controls */ +.form-group { + display: flex; + flex-direction: column; + gap: 0.4rem; + margin-bottom: 1.2rem; +} +.form-group label { + font-size: 0.85rem; + font-weight: 600; + color: var(--text-muted); +} +.form-group input, .form-group select { + padding: 0.8rem 1rem; + background: #090d16; + border: 1px solid var(--border-glass); + border-radius: var(--radius-sm); + color: white; + font-family: inherit; + font-size: 0.95rem; + outline: none; + transition: var(--transition); +} +.form-group input:focus, .form-group select:focus { + border-color: var(--indigo); + box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.3); +} + +.alert-box { + margin-top: 1.5rem; + padding: 1rem 1.2rem; + border-radius: var(--radius-sm); + display: none; + font-weight: 600; +} +.alert-box.success { + display: block; + background: rgba(16, 185, 129, 0.18); + border: 1px solid rgba(16, 185, 129, 0.5); + color: #6ee7b7; +} +.alert-box.error { + display: block; + background: rgba(244, 63, 94, 0.18); + border: 1px solid rgba(244, 63, 94, 0.5); + color: #fda4af; +} + +/* Footer */ +.footer { + border-top: 1px solid var(--border-glass); + padding: 2.5rem 0; + color: var(--text-dim); + font-size: 0.9rem; +} +.footer-content { + display: flex; + justify-content: space-between; + align-items: center; +} +.footer-brand { + display: flex; + align-items: center; + gap: 0.5rem; + font-weight: 700; + color: var(--text-muted); +} +.footer-links { + display: flex; + gap: 1.5rem; +} +.footer-links a { + color: var(--text-muted); + text-decoration: none; + transition: var(--transition); +} +.footer-links a:hover { + color: white; +} diff --git a/vps_account_db.sh b/vps_account_db.sh new file mode 100644 index 0000000..3737b54 --- /dev/null +++ b/vps_account_db.sh @@ -0,0 +1,6 @@ +#!/bin/bash +sudo -u postgres psql -d veritas_db -c "CREATE TABLE IF NOT EXISTS users (id SERIAL PRIMARY KEY, email VARCHAR(255) UNIQUE NOT NULL, password_hash VARCHAR(255) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP);" +sudo -u postgres psql -d veritas_db -c "ALTER TABLE licenses ADD COLUMN IF NOT EXISTS user_id INT REFERENCES users(id);" +sudo -u postgres psql -d veritas_db -c "ALTER TABLE licenses ADD COLUMN IF NOT EXISTS plan_name VARCHAR(64) DEFAULT 'Pro';" +sudo -u postgres psql -d veritas_db -c "GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO veritas;" +sudo -u postgres psql -d veritas_db -c "GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO veritas;" diff --git a/vps_admin_db.sh b/vps_admin_db.sh new file mode 100644 index 0000000..eb2fdff --- /dev/null +++ b/vps_admin_db.sh @@ -0,0 +1,4 @@ +#!/bin/bash +sudo -u postgres psql -d veritas_db -c "ALTER TABLE users ADD COLUMN IF NOT EXISTS is_admin BOOLEAN DEFAULT FALSE;" +sudo -u postgres psql -d veritas_db -c "UPDATE users SET is_admin = TRUE WHERE id = 1 OR email = 'admin@kanjiv.at';" +sudo -u postgres psql -d veritas_db -c "GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO veritas;" diff --git a/vps_setup_db.sh b/vps_setup_db.sh new file mode 100644 index 0000000..55a1cc7 --- /dev/null +++ b/vps_setup_db.sh @@ -0,0 +1,4 @@ +#!/bin/bash +sudo -u postgres psql -d veritas_db -c "CREATE TABLE IF NOT EXISTS licenses (id SERIAL PRIMARY KEY, license_key VARCHAR(64) UNIQUE NOT NULL, max_devices INT DEFAULT 3, active BOOLEAN DEFAULT TRUE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP);" +sudo -u postgres psql -d veritas_db -c "CREATE TABLE IF NOT EXISTS activations (id SERIAL PRIMARY KEY, license_key VARCHAR(64) NOT NULL, hardware_id VARCHAR(128) NOT NULL, app_name VARCHAR(64) NOT NULL, activated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, CONSTRAINT unique_activation UNIQUE(license_key, hardware_id));" +sudo -u postgres psql -d veritas_db -c "INSERT INTO licenses (license_key, max_devices) VALUES ('VERITAS-PRO-DEMO-2026', 10) ON CONFLICT DO NOTHING;" diff --git a/web Server data.txt b/web Server data.txt new file mode 100644 index 0000000..4a73856 --- /dev/null +++ b/web Server data.txt @@ -0,0 +1,6 @@ +vps IP 45.156.84.238 +vps benutzer : root +vps PW: Matthias2710.. + +--------------------------------- +