(chore): initial MVP release v0.1.0

This commit is contained in:
2025-04-28 23:49:42 +05:30
commit c73022b1a2
200 changed files with 24562 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
use directories::ProjectDirs;
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Config {
pub port: u16,
}
impl Default for Config {
fn default() -> Self {
Self { port: 53511 }
}
}
pub fn get_config_dir() -> Option<PathBuf> {
ProjectDirs::from("com", "neosubhamoy", "neodlp")
.map(|proj_dirs| proj_dirs.config_dir().to_path_buf())
}
pub fn get_config_path() -> Option<PathBuf> {
get_config_dir().map(|dir| dir.join("msghost-config.json"))
}
pub fn load_config() -> Config {
if let Some(config_path) = get_config_path() {
if let Ok(content) = fs::read_to_string(config_path) {
if let Ok(config) = serde_json::from_str(&content) {
return config;
}
}
}
Config::default()
}

View File

@@ -0,0 +1,134 @@
mod config;
use config::load_config;
use futures_util::{SinkExt, StreamExt};
use serde_json::Value;
use std::io::{self, Read, Write};
use std::time::Duration;
use tokio::net::TcpStream;
use tokio::time::sleep;
use tokio_tungstenite::{
connect_async, tungstenite::protocol::Message, MaybeTlsStream, WebSocketStream,
};
fn get_websocket_url() -> String {
let config = load_config();
format!("ws://localhost:{}", config.port)
}
async fn connect_with_retry(
url: &str,
max_attempts: u32,
) -> Result<WebSocketStream<MaybeTlsStream<TcpStream>>, Box<dyn std::error::Error + Send + Sync>> {
let mut attempts = 0;
loop {
match connect_async(url).await {
Ok((ws_stream, _)) => {
eprintln!("Successfully connected to Tauri app :)");
return Ok(ws_stream);
}
Err(e) => {
attempts += 1;
if attempts >= max_attempts {
return Err(Box::new(e));
}
let wait_time = Duration::from_secs(2u64.pow(attempts));
eprintln!(
"Connection attempt {} failed. Retrying in {:?}...",
attempts, wait_time
);
sleep(wait_time).await;
}
}
}
}
fn read_stdin_message() -> Result<String, Box<dyn std::error::Error>> {
let mut stdin = io::stdin();
let mut length_bytes = [0u8; 4];
stdin.read_exact(&mut length_bytes)?;
let length = u32::from_ne_bytes(length_bytes) as usize;
let mut buffer = vec![0u8; length];
stdin.read_exact(&mut buffer)?;
let message = String::from_utf8(buffer)?;
Ok(message)
}
fn write_stdout_message(message: &str) -> Result<(), Box<dyn std::error::Error>> {
let message_bytes = message.as_bytes();
let message_size = message_bytes.len();
io::stdout().write_all(&(message_size as u32).to_ne_bytes())?;
io::stdout().write_all(message_bytes)?;
io::stdout().flush()?;
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
eprintln!("Waiting for message from extension...");
let input = match read_stdin_message() {
Ok(msg) => {
eprintln!("Received message: {}", msg);
msg
}
Err(e) => {
eprintln!("Error reading message: {:?}", e);
return Err(e);
}
};
// Send immediate response to the extension
write_stdout_message(
&serde_json::json!({
"status": "received",
"message": "Message received by native host"
})
.to_string(),
)?;
let parsed: Value = serde_json::from_str(&input)?;
let websocket_url = get_websocket_url();
eprintln!("Attempting to connect to {}", websocket_url);
let mut ws_stream = match connect_with_retry(&websocket_url, 2).await {
Ok(stream) => stream,
Err(e) => {
eprintln!("Failed to connect after multiple attempts: {:?}", e);
write_stdout_message(
&serde_json::json!({
"status": "error",
"message": "Failed to connect to Tauri app"
})
.to_string(),
)?;
return Err(e);
}
};
// Send message to Tauri app
ws_stream
.send(Message::Text(parsed.to_string().into()))
.await?;
// Receive response from Tauri app
if let Some(Ok(msg)) = ws_stream.next().await {
// Send Tauri app's response back to browser extension
if let Message::Text(text) = msg {
write_stdout_message(
&serde_json::json!({
"status": "success",
"response": text.to_string()
})
.to_string(),
)?;
}
}
// Close the connection
ws_stream.close(None).await?;
Ok(())
}