Initial commit: split VNOX monorepo into VNOX-Server

Unified server binary (vnox-serverd) combining:
- Gateway (TCP, auth, channels, sessions, SQLite)
- Voice-node (UDP relay, Opus, jitter buffer)

Standalone gateway (vnox-gateway) and voice-node (vnox-voice-node) preserved as sub-crates.
This commit is contained in:
loki5512344 2026-07-09 11:52:56 +02:00
commit e751e0bf5b
94 changed files with 9771 additions and 0 deletions

View file

@ -0,0 +1,113 @@
use anyhow::Result;
use tokio::net::TcpStream;
use crate::{
domain::session,
net::{io, state::State},
proto::{self, DmStartPayload, DmStartResponsePayload, PacketId, SessionCrypto, to_payload},
};
pub async fn handle_dm_start(
stream: &mut TcpStream,
seq: &mut u32,
session_id: &str,
payload: &[u8],
crypto: &SessionCrypto,
state: &State,
) -> Result<()> {
let req: DmStartPayload = serde_json::from_slice(payload)?;
let sess = session::get(&state.sessions, session_id)
.await
.ok_or_else(|| anyhow::anyhow!("session not found"))?;
let my_id = sess.user_id.clone();
drop(sess);
if state
.storage
.is_blocked(&req.target_user_id, &my_id)
.await?
{
io::send_encrypted(
stream,
PacketId::Error,
seq,
&to_payload(&proto::ErrorPayload {
code: proto::ErrorCode::Blocked as u32,
message: "blocked".into(),
}),
crypto,
)
.await?;
return Ok(());
}
if req.target_user_id == my_id {
io::send_encrypted(
stream,
PacketId::Error,
seq,
&to_payload(&proto::ErrorPayload {
code: proto::ErrorCode::InvalidPacket as u32,
message: "cannot DM yourself".into(),
}),
crypto,
)
.await?;
return Ok(());
}
let (dm_id, unread_count) = state
.storage
.find_or_create_dm(&my_id, &req.target_user_id)
.await?;
let other_id = state
.storage
.get_dm_user_id(&dm_id, &my_id)
.await?
.ok_or_else(|| anyhow::anyhow!("user not in DM"))?;
let nickname = state
.storage
.get_dm_nickname(&other_id)
.await?
.unwrap_or_else(|| other_id[..8].to_string());
let messages = state
.storage
.get_dm_messages(&dm_id, 50, None, None)
.await?;
let resp = DmStartResponsePayload {
dm_id,
other_user_id: other_id,
other_nickname: nickname,
messages,
unread_count: unread_count as u32,
};
io::send_encrypted(stream, PacketId::DmStart, seq, &to_payload(&resp), crypto).await?;
Ok(())
}
pub async fn handle_dm_read_ack(
stream: &mut TcpStream,
seq: &mut u32,
session_id: &str,
payload: &[u8],
crypto: &SessionCrypto,
state: &State,
) -> Result<()> {
let req: serde_json::Value = serde_json::from_slice(payload)?;
let dm_id = req["dm_id"]
.as_str()
.ok_or_else(|| anyhow::anyhow!("missing dm_id"))?;
let sess = session::get(&state.sessions, session_id)
.await
.ok_or_else(|| anyhow::anyhow!("session not found"))?;
let my_id = sess.user_id.clone();
drop(sess);
state.storage.reset_dm_unread(dm_id, &my_id).await?;
io::send_encrypted(stream, PacketId::DmReadAck, seq, b"{}", crypto).await?;
Ok(())
}