Merge pull request #3 from recurse-chat/working-server

Working server
This commit is contained in:
2026-08-19 03:14:12 +02:00
committed by GitHub
11 changed files with 154 additions and 29 deletions
Vendored
BIN
View File
Binary file not shown.
Generated
+67
View File
@@ -81,6 +81,12 @@ version = "1.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
[[package]]
name = "bitflags"
version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
[[package]] [[package]]
name = "block-buffer" name = "block-buffer"
version = "0.10.4" version = "0.10.4"
@@ -226,6 +232,7 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"tokio", "tokio",
"tower-http",
] ]
[[package]] [[package]]
@@ -349,6 +356,12 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
] ]
[[package]]
name = "http-range-header"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c"
[[package]] [[package]]
name = "httparse" name = "httparse"
version = "1.10.1" version = "1.10.1"
@@ -432,6 +445,16 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "mime_guess"
version = "2.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
dependencies = [
"mime",
"unicase",
]
[[package]] [[package]]
name = "mio" name = "mio"
version = "1.2.2" version = "1.2.2"
@@ -820,6 +843,19 @@ dependencies = [
"tungstenite", "tungstenite",
] ]
[[package]]
name = "tokio-util"
version = "0.7.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
"pin-project-lite",
"tokio",
]
[[package]] [[package]]
name = "tower" name = "tower"
version = "0.5.3" version = "0.5.3"
@@ -836,6 +872,31 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "tower-http"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b11f75e912b0c2be01b63d8cf8057b8c3f97cf34abb3d431a3a4c8675498e233"
dependencies = [
"bitflags",
"bytes",
"futures-core",
"futures-util",
"http",
"http-body",
"http-body-util",
"http-range-header",
"httpdate",
"mime",
"mime_guess",
"percent-encoding",
"pin-project-lite",
"tokio",
"tokio-util",
"tower-layer",
"tower-service",
]
[[package]] [[package]]
name = "tower-layer" name = "tower-layer"
version = "0.3.3" version = "0.3.3"
@@ -890,6 +951,12 @@ version = "1.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
[[package]]
name = "unicase"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.24" version = "1.0.24"
+1
View File
@@ -12,3 +12,4 @@ tokio = { version = "1.53.1", features = ["rt", "rt-multi-thread", "macros", "sy
ed25519-dalek = { version = "2", features = ["rand_core"] } ed25519-dalek = { version = "2", features = ["rand_core"] }
rand = "0.8" rand = "0.8"
bs58 = "0.5.1" bs58 = "0.5.1"
tower-http = { version = "0.7.0", features = ["fs", "cors"] }
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

+16 -3
View File
@@ -2,7 +2,7 @@ use std::{collections::HashSet, path::PathBuf};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::protocol::ServerMeta; use crate::types::{Channel, ChannelKind, ServerMeta};
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config { pub struct Config {
@@ -18,13 +18,26 @@ impl Config {
meta: ServerMeta { meta: ServerMeta {
name: "New Server".to_string(), name: "New Server".to_string(),
description: String::new(), description: String::new(),
channels: vec![Channel {
id: "text-channels".to_string(),
name: "Text Channels".to_string(),
data: ChannelKind::Category {
channels: vec![Channel {
id: "general".to_string(),
name: "General".to_string(),
data: ChannelKind::Text,
}],
},
}],
}, },
port: 3415, port: 3415,
public_hostname: "localhost:3000".to_string(), public_hostname: "localhost:3415".to_string(),
hostnames: HashSet::from_iter(["localhost:3000".to_string()]), hostnames: HashSet::from_iter(["localhost:3415".to_string()]),
} }
} }
+13 -1
View File
@@ -2,6 +2,7 @@ pub mod config;
pub mod protocol; pub mod protocol;
pub mod server; pub mod server;
pub mod signature; pub mod signature;
pub mod types;
use std::{ use std::{
net::{IpAddr, Ipv4Addr, SocketAddr}, net::{IpAddr, Ipv4Addr, SocketAddr},
@@ -14,6 +15,10 @@ use axum::{
response::Response, response::Response,
routing::{any, get}, routing::{any, get},
}; };
use tower_http::{
cors::{Any, CorsLayer},
services::ServeFile,
};
use crate::server::Server; use crate::server::Server;
@@ -21,10 +26,17 @@ use crate::server::Server;
async fn main() -> anyhow::Result<()> { async fn main() -> anyhow::Result<()> {
let server = Server::new().await?; let server = Server::new().await?;
let cors = CorsLayer::new()
.allow_origin(Any)
.allow_methods(Any)
.allow_headers(Any);
let app = Router::new() let app = Router::new()
.route("/meta", get(meta)) .route("/meta", get(meta))
.route("/", any(ws_handler)) .route("/", any(ws_handler))
.with_state(server.clone()); .route_service("/icon", ServeFile::new("./icon.png"))
.with_state(server.clone())
.layer(cors);
let listener = tokio::net::TcpListener::bind(SocketAddr::new( let listener = tokio::net::TcpListener::bind(SocketAddr::new(
IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
+7 -7
View File
@@ -37,9 +37,9 @@ impl UserConnections {
)); ));
}; };
let server_timestamp = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs(); let server_timestamp = SystemTime::now().duration_since(UNIX_EPOCH)?.as_millis() as u64;
if server_timestamp - timestamp > 2 { if server_timestamp.saturating_sub(timestamp) > 2000 {
send_socket( send_socket(
&mut socket, &mut socket,
&ClientMethod::Error { &ClientMethod::Error {
@@ -53,7 +53,8 @@ impl UserConnections {
return Err(anyhow::anyhow!("Client tampstamp wasn't correct")); return Err(anyhow::anyhow!("Client tampstamp wasn't correct"));
} }
if !server.config.hostnames.contains(&hostname) { if hostname != server.config.public_hostname || !server.config.hostnames.contains(&hostname)
{
send_socket( send_socket(
&mut socket, &mut socket,
&ClientMethod::Error { &ClientMethod::Error {
@@ -100,10 +101,9 @@ impl UserConnections {
&mut socket, &mut socket,
&ClientMethod::Initialized { &ClientMethod::Initialized {
public_key: crate::signature::to_string(&server.key.verifying_key()), public_key: crate::signature::to_string(&server.key.verifying_key()),
signature: server signature: crate::signature::to_string_sig(&server.key.sign(
.key format!("{server_timestamp}@{hostname}@{public_key_string}").as_bytes(),
.sign(format!("{timestamp}@{hostname}@{public_key_string}").as_bytes()) )),
.to_string(),
timestamp: server_timestamp, timestamp: server_timestamp,
hostname, hostname,
+2 -9
View File
@@ -4,17 +4,10 @@ use axum::extract::ws::{Message, Utf8Bytes, WebSocket};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tokio::sync::Mutex; use tokio::sync::Mutex;
use crate::types::ClientMeta;
pub mod initialize; pub mod initialize;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClientMeta {}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerMeta {
pub name: String,
pub description: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "method")] #[serde(tag = "method")]
pub enum ClientMethod { pub enum ClientMethod {
+13 -7
View File
@@ -12,7 +12,8 @@ use tokio::sync::Mutex;
use crate::{ use crate::{
config::Config, config::Config,
protocol::{ClientMeta, ClientMethod, read_loop, send_socket}, protocol::{ClientMethod, read_loop, send_socket},
types::ClientMeta,
}; };
pub struct UserConnections { pub struct UserConnections {
@@ -49,7 +50,7 @@ impl Server {
let mut clients_meta = s.clients.lock().await; let mut clients_meta = s.clients.lock().await;
let client_meta = let clients =
clients_meta clients_meta
.entry(public_key) .entry(public_key)
.or_insert_with(|| UserConnections { .or_insert_with(|| UserConnections {
@@ -59,18 +60,23 @@ impl Server {
connections: HashMap::new(), connections: HashMap::new(),
}); });
client_meta.connections.insert( let conid = clients
client_meta
.counter .counter
.fetch_add(1, std::sync::atomic::Ordering::Relaxed), .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
client.clone(),
); clients.connections.insert(conid, client.clone());
if let Err(e) = read_loop(&client).await { if let Err(e) = read_loop(&client).await {
eprintln!("Failed to handle client: {e}"); eprintln!("Failed to handle client: {e}");
} else { } else {
println!("Client connection closed") println!("Client connection closed")
} }
clients.connections.remove(&conid);
if clients.connections.len() == 0 {
clients_meta.remove(&public_key);
}
} }
Err(e) => { Err(e) => {
+5 -1
View File
@@ -24,6 +24,10 @@ pub fn to_string(key: &VerifyingKey) -> String {
bs58::encode(key.to_bytes()).into_string() bs58::encode(key.to_bytes()).into_string()
} }
pub fn to_string_sig(signature: &Signature) -> String {
bs58::encode(signature.to_bytes()).into_string()
}
pub fn from_string(key: &str) -> anyhow::Result<VerifyingKey> { pub fn from_string(key: &str) -> anyhow::Result<VerifyingKey> {
Ok(VerifyingKey::from_bytes( Ok(VerifyingKey::from_bytes(
&bs58::decode(key) &bs58::decode(key)
@@ -38,6 +42,6 @@ pub fn from_string_sig(signature: &str) -> anyhow::Result<Signature> {
&bs58::decode(signature) &bs58::decode(signature)
.into_vec()? .into_vec()?
.try_into() .try_into()
.map_err(|_| anyhow::anyhow!("Invalid public key"))?, .map_err(|_| anyhow::anyhow!("Invalid signature"))?,
)) ))
} }
+29
View File
@@ -0,0 +1,29 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClientMeta {}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerMeta {
pub name: String,
pub description: String,
pub channels: Vec<Channel>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind")]
#[serde(rename_all = "camelCase")]
pub enum ChannelKind {
Text,
Category { channels: Vec<Channel> },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Channel {
pub id: String,
pub name: String,
#[serde(flatten)]
pub data: ChannelKind,
}