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:
commit
e751e0bf5b
94 changed files with 9771 additions and 0 deletions
219
gateway/src/handler/guild/invites.rs
Normal file
219
gateway/src/handler/guild/invites.rs
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
use anyhow::Result;
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
use crate::{
|
||||
domain::{permissions::Permissions, session},
|
||||
net::{io, state::State},
|
||||
proto::{
|
||||
InviteAcceptPayload, InviteCreatePayload, InviteDeletePayload, InviteInfo, PacketId,
|
||||
SessionCrypto, to_payload,
|
||||
},
|
||||
};
|
||||
|
||||
use super::{now_ms, send_err};
|
||||
|
||||
pub async fn handle_invite_create(
|
||||
stream: &mut TcpStream,
|
||||
seq: &mut u32,
|
||||
session_id: &str,
|
||||
payload: &[u8],
|
||||
crypto: &SessionCrypto,
|
||||
state: &State,
|
||||
) -> Result<()> {
|
||||
let req: InviteCreatePayload = serde_json::from_slice(payload)?;
|
||||
let sess = session::get(&state.sessions, session_id)
|
||||
.await
|
||||
.ok_or_else(|| anyhow::anyhow!("session not found"))?;
|
||||
|
||||
if !super::require_perm(
|
||||
state,
|
||||
&req.guild_id,
|
||||
&sess.user_id,
|
||||
Permissions::CREATE_INVITE,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
send_err(
|
||||
stream,
|
||||
seq,
|
||||
crate::proto::ErrorCode::PermissionDenied,
|
||||
"missing CREATE_INVITE permission",
|
||||
crypto,
|
||||
)
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let inv = state
|
||||
.storage
|
||||
.create_invite(
|
||||
&req.guild_id,
|
||||
&sess.user_id,
|
||||
req.max_uses,
|
||||
req.expires_in_seconds,
|
||||
)
|
||||
.await?;
|
||||
|
||||
state
|
||||
.storage
|
||||
.append_audit_log(
|
||||
&req.guild_id,
|
||||
&sess.user_id,
|
||||
"INVITE_CREATE",
|
||||
Some(&inv.id),
|
||||
Some("invite"),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let info = InviteInfo {
|
||||
id: inv.id,
|
||||
guild_id: inv.guild_id,
|
||||
guild_name: inv.guild_name,
|
||||
code: inv.code,
|
||||
creator_id: inv.creator_id,
|
||||
max_uses: inv.max_uses,
|
||||
uses: inv.uses,
|
||||
expires_at: inv.expires_at,
|
||||
created_at: inv.created_at,
|
||||
};
|
||||
io::send_encrypted(
|
||||
stream,
|
||||
PacketId::InviteCreate,
|
||||
seq,
|
||||
&to_payload(&info),
|
||||
crypto,
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn handle_invite_accept(
|
||||
stream: &mut TcpStream,
|
||||
seq: &mut u32,
|
||||
session_id: &str,
|
||||
payload: &[u8],
|
||||
crypto: &SessionCrypto,
|
||||
state: &State,
|
||||
) -> Result<()> {
|
||||
let req: InviteAcceptPayload = serde_json::from_slice(payload)?;
|
||||
let sess = session::get(&state.sessions, session_id)
|
||||
.await
|
||||
.ok_or_else(|| anyhow::anyhow!("session not found"))?;
|
||||
|
||||
let inv = state
|
||||
.storage
|
||||
.get_invite_by_code(&req.code)
|
||||
.await?
|
||||
.ok_or_else(|| anyhow::anyhow!("invite not found"))?;
|
||||
|
||||
if let Some(exp) = inv.expires_at {
|
||||
let now = now_ms();
|
||||
if now > exp {
|
||||
send_err(
|
||||
stream,
|
||||
seq,
|
||||
crate::proto::ErrorCode::InvalidPacket,
|
||||
"invite expired",
|
||||
crypto,
|
||||
)
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
if let Some(max) = inv.max_uses
|
||||
&& inv.uses >= max
|
||||
{
|
||||
send_err(
|
||||
stream,
|
||||
seq,
|
||||
crate::proto::ErrorCode::InvalidPacket,
|
||||
"invite max uses reached",
|
||||
crypto,
|
||||
)
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
state
|
||||
.storage
|
||||
.add_guild_member(&inv.guild_id, &sess.user_id)
|
||||
.await?;
|
||||
state.storage.use_invite(&inv.id).await?;
|
||||
state
|
||||
.storage
|
||||
.append_audit_log(
|
||||
&inv.guild_id,
|
||||
&sess.user_id,
|
||||
"MEMBER_JOIN_INVITE",
|
||||
Some(&sess.user_id),
|
||||
Some("member"),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
io::send_encrypted(
|
||||
stream,
|
||||
PacketId::InviteAccept,
|
||||
seq,
|
||||
&to_payload(&serde_json::json!({"guild_id": inv.guild_id, "guild_name": inv.guild_name})),
|
||||
crypto,
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn handle_invite_delete(
|
||||
stream: &mut TcpStream,
|
||||
seq: &mut u32,
|
||||
session_id: &str,
|
||||
payload: &[u8],
|
||||
crypto: &SessionCrypto,
|
||||
state: &State,
|
||||
) -> Result<()> {
|
||||
let req: InviteDeletePayload = serde_json::from_slice(payload)?;
|
||||
let sess = session::get(&state.sessions, session_id)
|
||||
.await
|
||||
.ok_or_else(|| anyhow::anyhow!("session not found"))?;
|
||||
|
||||
if !super::require_perm(
|
||||
state,
|
||||
&req.guild_id,
|
||||
&sess.user_id,
|
||||
Permissions::MANAGE_GUILD,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
send_err(
|
||||
stream,
|
||||
seq,
|
||||
crate::proto::ErrorCode::PermissionDenied,
|
||||
"missing MANAGE_GUILD permission",
|
||||
crypto,
|
||||
)
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
state.storage.delete_invite(&req.invite_id).await?;
|
||||
state
|
||||
.storage
|
||||
.append_audit_log(
|
||||
&req.guild_id,
|
||||
&sess.user_id,
|
||||
"INVITE_DELETE",
|
||||
Some(&req.invite_id),
|
||||
Some("invite"),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
io::send_encrypted(
|
||||
stream,
|
||||
PacketId::InviteDelete,
|
||||
seq,
|
||||
&to_payload(&serde_json::json!({"invite_id": req.invite_id})),
|
||||
crypto,
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue