summaryrefslogtreecommitdiff
path: root/contact-service/src/main.rs
diff options
context:
space:
mode:
authorops <ops@localhost>2026-10-18 13:00:05 +0000
committerops <ops@localhost>2026-10-18 13:00:05 +0000
commite373f154dbeacc5a0e83b4ec73c3aa316239c029 (patch)
treed4d5fb2acf3ae1508aab8bb56a63f750f5ae2824 /contact-service/src/main.rs
parent902cfd395fb03d6147813560ac5fcd7146702a1d (diff)
Remove "Message Me" functionality
Remove the custom message me form to reduce maintenance burden May potentially add it again (improved).
Diffstat (limited to 'contact-service/src/main.rs')
-rw-r--r--contact-service/src/main.rs220
1 files changed, 0 insertions, 220 deletions
diff --git a/contact-service/src/main.rs b/contact-service/src/main.rs
deleted file mode 100644
index 1abfe04..0000000
--- a/contact-service/src/main.rs
+++ /dev/null
@@ -1,220 +0,0 @@
-//! Website Contact Form
-
-// Copyright (C) 2026 Doan Luu Duc Tai <doan@luuduct.ai>
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Affero General Public License as
-// published by the Free Software Foundation, either version 3 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Affero General Public License for more details.
-//
-// You should have received a copy of the GNU Affero General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-use axum::{
- extract::{Form, State},
- response::Html,
- routing::post,
- Router,
-};
-use chrono::Utc;
-use serde::Deserialize;
-use std::io::Write;
-use std::path::PathBuf;
-use std::process::{Command, Stdio};
-use std::sync::Arc;
-
-#[derive(Clone)]
-struct AppConfig {
- bind_addr: String,
- cert_path: PathBuf,
- inbox_dir: PathBuf,
- sq_bin: PathBuf,
-}
-
-impl AppConfig {
- fn from_env() -> Self {
- let home = std::env::var("HOME").unwrap_or_else(|_| "/home/ops".into());
-
- Self {
- bind_addr: std::env::var("BIND_ADDR").unwrap_or_else(|_| "0.0.0.0:8085".into()),
- cert_path: PathBuf::from(
- std::env::var("CERT_PATH")
- .unwrap_or_else(|_| format!("{}/stack/contact-cert.pgp", home)),
- ),
- inbox_dir: PathBuf::from(
- std::env::var("INBOX_DIR")
- .unwrap_or_else(|_| format!("{}/stack/inbox", home)),
- ),
- sq_bin: PathBuf::from(
- std::env::var("SQ_BIN")
- .unwrap_or_else(|_| format!("{}/.cargo/bin/sq", home)),
- ),
- }
- }
-}
-
-#[derive(Deserialize)]
-struct ContactForm {
- sender: Option<String>,
- message: String,
-}
-
-#[tokio::main]
-async fn main() {
- let config = Arc::new(AppConfig::from_env());
-
- let app = Router::new()
- .route("/send-message", post(handle_message))
- .with_state(config.clone());
-
- let listener = tokio::net::TcpListener::bind(&config.bind_addr)
- .await
- .unwrap_or_else(|_| panic!("Failed to bind to {}", config.bind_addr));
-
- println!("Contact backend listening on http://{}", config.bind_addr);
- println!("Using PQC Cert: {:?}", config.cert_path);
- println!("Storing inbox in: {:?}", config.inbox_dir);
-
- axum::serve(listener, app).await.unwrap();
-}
-
-async fn handle_message(
- State(config): State<Arc<AppConfig>>,
- Form(form): Form<ContactForm>,
-) -> Html<&'static str> {
- let message = form.message.trim();
- if message.is_empty() {
- return Html(ERROR_PAGE);
- }
-
- let raw_sender = form.sender.as_deref().unwrap_or("").trim();
- let sender_display = if raw_sender.is_empty() {
- "Anonymous"
- } else {
- raw_sender
- };
-
- let now = Utc::now();
- let timestamp_str = now.format("%Y-%m-%d_%H-%M-%S").to_string();
-
- let sanitized_sender: String = raw_sender
- .chars()
- .filter(|c| c.is_ascii_alphanumeric() || *c == '-' || *c == '_')
- .take(30)
- .collect();
-
- let filename = if sanitized_sender.is_empty() {
- format!("{}.pgp", timestamp_str)
- } else {
- format!("{}_{}.pgp", timestamp_str, sanitized_sender)
- };
-
- let output_path = config.inbox_dir.join(filename);
-
- let payload = format!(
- "=== BEGIN MESSAGE ===\nSender: {}\nTimestamp: {}\n============================\n\n{}",
- sender_display,
- now.to_rfc3339(),
- message
- );
-
- let mut child = match Command::new(&config.sq_bin)
- .args([
- "encrypt",
- "--for-file",
- config.cert_path.to_str().unwrap_or_default(),
- "--without-signature",
- ])
- .stdin(Stdio::piped())
- .stdout(Stdio::piped())
- .spawn()
- {
- Ok(proc) => proc,
- Err(e) => {
- eprintln!("Failed to spawn sq binary ({:?}): {}", config.sq_bin, e);
- return Html(ERROR_PAGE);
- }
- };
-
- if let Some(mut stdin) = child.stdin.take() {
- if stdin.write_all(payload.as_bytes()).is_err() {
- return Html(ERROR_PAGE);
- }
- }
-
- let output = match child.wait_with_output() {
- Ok(out) => out,
- Err(_) => return Html(ERROR_PAGE),
- };
-
- if !output.status.success() {
- eprintln!(
- "sq encryption failed: {}",
- String::from_utf8_lossy(&output.stderr)
- );
- return Html(ERROR_PAGE);
- }
-
- if std::fs::write(&output_path, &output.stdout).is_err() {
- eprintln!("Failed to write to file {:?}", output_path);
- return Html(ERROR_PAGE);
- }
-
- Html(SUCCESS_PAGE)
-}
-
-const SUCCESS_PAGE: &str = r#"<!doctype html>
-<html lang="en">
-<head>
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width,initial-scale=1">
- <title>Message Sent</title>
- <link rel="stylesheet" href="/styles.css">
-</head>
-<body>
- <div id="page">
- <div class="badge">[ JS-free ]</div>
- <div id="content">
- <pre>
-<a href="/" class="logo">
- ____ ___ _ _ _
-| _ \ / _ \ / \ | \ | |
-| | | | | | |/ _ \ | \| |
-| |_| | |_| / ___ \| |\ |
-|____/ \___/_/ \_\_| \_| LUU DUC TAI
-</a>
-
-+------------------------------------------------------------------------------+
-| [+] Success: Message encrypted and delivered to inbox. |
-+------------------------------------------------------------------------------+
-
-<a href="/"><-- Return to Home Page</a>
- </pre>
- <div class="source-footer">
- <a href="https://git.doan.luuduct.ai/stack/tree/contact-service/src/main.rs">[ Source Code ]</a>
- </div>
- </div>
- </div>
-</body>
-</html>"#;
-
-const ERROR_PAGE: &str = r#"<!doctype html>
-<html lang="en">
-<head>
- <meta charset="utf-8">
- <title>Error</title>
- <link rel="stylesheet" href="/styles.css">
-</head>
-<body>
- <pre>
-[!] Error: Unable to process or encrypt message.
-
-<a href="/message.html"><-- Try Again</a>
- </pre>
-</body>
-</html>"#;