joining server works but...
durian is POS
This commit is contained in:
@@ -189,8 +189,7 @@ impl RealmAuth for RealmAuthServer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for token in tokens {
|
for token in tokens {
|
||||||
let hash = Sha3_256::new().chain(format!("{}{}{}{}", token, server_id, domain, tarpc_port)).finalize();
|
if realm_shared::stoken(token, &server_id, &domain, tarpc_port) == server_token {
|
||||||
if hex::encode(hash) == server_token {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -533,7 +532,7 @@ impl RealmAuth for RealmAuthServer {
|
|||||||
vec_servers.push(&address);
|
vec_servers.push(&address);
|
||||||
let new_servers = vec_servers.join("|");
|
let new_servers = vec_servers.join("|");
|
||||||
|
|
||||||
let result = query!("UPDATE user SET servers = ? WHERE username = ?", new_servers, username).fetch_one(&self.db_pool).await;
|
let result = query!("UPDATE user SET servers = ? WHERE username = ?", new_servers, username).execute(&self.db_pool).await;
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(()),
|
||||||
Err(_) => Err(Error)
|
Err(_) => Err(Error)
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ eframe = { version = "0.29", default-features = false, features = [
|
|||||||
"persistence", # Enable restoring app state when restarting the app.
|
"persistence", # Enable restoring app state when restarting the app.
|
||||||
] }
|
] }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
env_logger = "0.11.5"
|
|
||||||
tokio = "1.40.0"
|
tokio = "1.40.0"
|
||||||
tarpc = { version = "0.34.0", features = ["full"] }
|
tarpc = { version = "0.34.0", features = ["full"] }
|
||||||
tracing = "0.1.40"
|
tracing = "0.1.40"
|
||||||
|
|||||||
@@ -4,9 +4,11 @@ use tokio::sync::broadcast;
|
|||||||
use tokio::sync::broadcast::{Receiver, Sender};
|
use tokio::sync::broadcast::{Receiver, Sender};
|
||||||
use tracing::log::*;
|
use tracing::log::*;
|
||||||
use realm_auth::types::RealmAuthClient;
|
use realm_auth::types::RealmAuthClient;
|
||||||
|
use realm_server::types::{RealmChatClient, User};
|
||||||
|
use realm_shared::stoken;
|
||||||
use realm_shared::types::ErrorCode::*;
|
use realm_shared::types::ErrorCode::*;
|
||||||
use realm_shared::types::ErrorCode;
|
use realm_shared::types::ErrorCode;
|
||||||
use crate::types::ClientUser;
|
use crate::types::{CServer, CUser};
|
||||||
use crate::ui::gui;
|
use crate::ui::gui;
|
||||||
|
|
||||||
/// We derive Deserialize/Serialize so we can persist app state on shutdown.
|
/// We derive Deserialize/Serialize so we can persist app state on shutdown.
|
||||||
@@ -16,16 +18,21 @@ pub struct RealmApp {
|
|||||||
// Example stuff:
|
// Example stuff:
|
||||||
pub label: String,
|
pub label: String,
|
||||||
pub selected: bool,
|
pub selected: bool,
|
||||||
pub selected_serverid: Option<String>,
|
#[serde(skip)]
|
||||||
pub selected_roomid: Option<String>,
|
pub selected_serverid: String,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub selected_roomid: String,
|
||||||
|
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub current_user: Option<ClientUser>,
|
pub current_user: Option<CUser>,
|
||||||
|
|
||||||
pub saved_username: Option<String>,
|
pub saved_username: Option<String>,
|
||||||
pub saved_token: Option<String>,
|
pub saved_token: Option<String>,
|
||||||
pub saved_auth_address: Option<String>,
|
pub saved_auth_address: Option<String>,
|
||||||
|
|
||||||
|
#[serde(skip)]
|
||||||
|
pub active_servers: Option<Vec<CServer>>,
|
||||||
|
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub value: f32,
|
pub value: f32,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
@@ -58,10 +65,15 @@ pub struct RealmApp {
|
|||||||
pub login_ending_channel: (Sender<Result<String, ErrorCode>>, Receiver<Result<String, ErrorCode>>),
|
pub login_ending_channel: (Sender<Result<String, ErrorCode>>, Receiver<Result<String, ErrorCode>>),
|
||||||
|
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub fetching_user_data_channel: (Sender<Result<ClientUser, ErrorCode>>, Receiver<Result<ClientUser, ErrorCode>>),
|
pub fetching_user_data_channel: (Sender<Result<CUser, ErrorCode>>, Receiver<Result<CUser, ErrorCode>>),
|
||||||
|
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub added_server_channel: (Sender<Result<String, ErrorCode>>, Receiver<Result<String, ErrorCode>>),
|
pub add_server_channel: (Sender<Result<String, ErrorCode>>, Receiver<Result<String, ErrorCode>>),
|
||||||
|
#[serde(skip)]
|
||||||
|
pub join_server_channel: (Sender<Result<(), ErrorCode>>, Receiver<Result<(), ErrorCode>>),
|
||||||
|
|
||||||
|
#[serde(skip)]
|
||||||
|
pub fetching_servers_channel: (Sender<Result<CServer, ErrorCode>>, Receiver<Result<CServer, ErrorCode>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for RealmApp {
|
impl Default for RealmApp {
|
||||||
@@ -70,12 +82,13 @@ impl Default for RealmApp {
|
|||||||
// Example stuff:
|
// Example stuff:
|
||||||
label: "Hello World!".to_owned(),
|
label: "Hello World!".to_owned(),
|
||||||
selected: false,
|
selected: false,
|
||||||
selected_serverid: None,
|
selected_serverid: String::new(),
|
||||||
selected_roomid: None,
|
selected_roomid: String::new(),
|
||||||
current_user: None,
|
current_user: None,
|
||||||
saved_username: None,
|
saved_username: None,
|
||||||
saved_token: None,
|
saved_token: None,
|
||||||
saved_auth_address: None,
|
saved_auth_address: None,
|
||||||
|
active_servers: None,
|
||||||
value: 2.7,
|
value: 2.7,
|
||||||
|
|
||||||
login_window_open: false,
|
login_window_open: false,
|
||||||
@@ -94,8 +107,9 @@ impl Default for RealmApp {
|
|||||||
server_window_port: "5051".to_string(),
|
server_window_port: "5051".to_string(),
|
||||||
|
|
||||||
fetching_user_data_channel: broadcast::channel(10),
|
fetching_user_data_channel: broadcast::channel(10),
|
||||||
|
add_server_channel: broadcast::channel(10),
|
||||||
added_server_channel: broadcast::channel(10),
|
join_server_channel: broadcast::channel(10),
|
||||||
|
fetching_servers_channel: broadcast::channel(10),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,21 +130,21 @@ impl RealmApp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fetch_user_data(send_channel: Sender<Result<ClientUser, ErrorCode>>, server_address: String, username: String, token: String) {
|
pub fn fetch_user_data(send_channel: Sender<Result<CUser, ErrorCode>>, server_address: String, username: String, token: String) {
|
||||||
let _handle = tokio::spawn(async move {
|
let _handle = tokio::spawn(async move {
|
||||||
let mut transport = tarpc::serde_transport::tcp::connect(&server_address, Json::default);
|
let mut transport = tarpc::serde_transport::tcp::connect(&server_address, Json::default);
|
||||||
transport.config_mut().max_frame_length(usize::MAX);
|
transport.config_mut().max_frame_length(usize::MAX);
|
||||||
|
|
||||||
let result = transport.await;
|
let result = transport.await;
|
||||||
let connection = match result {
|
let auth_connection = match result {
|
||||||
Ok(connection) => connection,
|
Ok(connection) => connection,
|
||||||
Err(e) => {
|
Err(_) => {
|
||||||
tracing::error!("Failed to connect to server: {}", e);
|
send_channel.send(Err(UnableToConnectToServer)).unwrap();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let client = RealmAuthClient::new(tarpc::client::Config::default(), connection).spawn();
|
let client = RealmAuthClient::new(tarpc::client::Config::default(), auth_connection).spawn();
|
||||||
let result = client.get_all_data(context::current(), username, token.clone()).await;
|
let result = client.get_all_data(context::current(), username, token.clone()).await;
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
@@ -139,20 +153,13 @@ pub fn fetch_user_data(send_channel: Sender<Result<ClientUser, ErrorCode>>, serv
|
|||||||
send_channel.send(Err(code)).unwrap();
|
send_channel.send(Err(code)).unwrap();
|
||||||
} else {
|
} else {
|
||||||
let auth_user = r.unwrap();
|
let auth_user = r.unwrap();
|
||||||
let servers: Vec<String> = {
|
send_channel.send(Ok(CUser {
|
||||||
if auth_user.servers.eq("") {
|
|
||||||
Vec::new()
|
|
||||||
} else {
|
|
||||||
auth_user.servers.split('|').map(|s| s.to_string()).collect()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
send_channel.send(Ok(ClientUser {
|
|
||||||
id: auth_user.id,
|
id: auth_user.id,
|
||||||
server_address,
|
auth_address: server_address,
|
||||||
username: auth_user.username,
|
username: auth_user.username,
|
||||||
email: auth_user.email,
|
email: auth_user.email,
|
||||||
//avatar: auth_user.avatar,
|
//avatar: auth_user.avatar,
|
||||||
servers,
|
server_addresses: auth_user.servers.split('|').map(|s| s.to_string()).collect(),
|
||||||
token,
|
token,
|
||||||
})).unwrap();
|
})).unwrap();
|
||||||
}
|
}
|
||||||
@@ -164,6 +171,38 @@ pub fn fetch_user_data(send_channel: Sender<Result<ClientUser, ErrorCode>>, serv
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn fetch_server_data(addresses: Vec<String>, channel: Sender<Result<CServer, ErrorCode>>) {
|
||||||
|
for server_address in addresses {
|
||||||
|
let send_channel = channel.clone();
|
||||||
|
|
||||||
|
let _handle = tokio::spawn(async move {
|
||||||
|
let mut transport = tarpc::serde_transport::tcp::connect(&server_address, Json::default);
|
||||||
|
transport.config_mut().max_frame_length(usize::MAX);
|
||||||
|
|
||||||
|
let result = transport.await;
|
||||||
|
let connection = match result {
|
||||||
|
Ok(connection) => connection,
|
||||||
|
Err(_) => {
|
||||||
|
send_channel.send(Err(UnableToConnectToServer)).unwrap();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let client = RealmChatClient::new(tarpc::client::Config::default(), connection).spawn();
|
||||||
|
let info = client.get_info(context::current()).await.unwrap();
|
||||||
|
let is_admin = client.is_user_admin(context::current(), info.server_id.clone()).await.unwrap();
|
||||||
|
let is_owner = client.is_user_owner(context::current(), info.server_id.clone()).await.unwrap();
|
||||||
|
send_channel.send(Ok(CServer {
|
||||||
|
server_id: info.server_id,
|
||||||
|
domain: server_address.split(':').collect::<Vec<&str>>()[0].to_string(),
|
||||||
|
port: server_address.split(':').collect::<Vec<&str>>()[1].to_string().parse::<u16>().unwrap(),
|
||||||
|
is_admin,
|
||||||
|
is_owner,
|
||||||
|
})).unwrap();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl eframe::App for RealmApp {
|
impl eframe::App for RealmApp {
|
||||||
/// Called each time the UI needs repainting, which may be many times per second.
|
/// Called each time the UI needs repainting, which may be many times per second.
|
||||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||||
@@ -183,6 +222,13 @@ impl eframe::App for RealmApp {
|
|||||||
fetch_user_data(send_channel, server_address, username, token);
|
fetch_user_data(send_channel, server_address, username, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fetching servers
|
||||||
|
if self.active_servers.is_none() && self.current_user.is_some() {
|
||||||
|
self.active_servers = Some(Vec::new());
|
||||||
|
fetch_server_data(self.current_user.clone().unwrap().server_addresses.clone(), self.fetching_servers_channel.0.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Starting the login flow
|
||||||
while let Ok(result) = self.login_start_channel.1.try_recv() {
|
while let Ok(result) = self.login_start_channel.1.try_recv() {
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => self.login_ready_for_code_input = true,
|
Ok(_) => self.login_ready_for_code_input = true,
|
||||||
@@ -190,6 +236,7 @@ impl eframe::App for RealmApp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// End of the login flow
|
||||||
while let Ok(result) = self.login_ending_channel.1.try_recv() {
|
while let Ok(result) = self.login_ending_channel.1.try_recv() {
|
||||||
match result {
|
match result {
|
||||||
Ok(token) => {
|
Ok(token) => {
|
||||||
@@ -214,6 +261,7 @@ impl eframe::App for RealmApp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fetching user data
|
||||||
while let Ok(result) = self.fetching_user_data_channel.1.try_recv() {
|
while let Ok(result) = self.fetching_user_data_channel.1.try_recv() {
|
||||||
match result {
|
match result {
|
||||||
Ok(client_user) => {
|
Ok(client_user) => {
|
||||||
@@ -224,22 +272,82 @@ impl eframe::App for RealmApp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while let Ok(result) = self.added_server_channel.1.try_recv() {
|
// Adding a server
|
||||||
|
while let Ok(result) = self.add_server_channel.1.try_recv() {
|
||||||
match result {
|
match result {
|
||||||
Ok(address) => {
|
Ok(address) => {
|
||||||
info!("New server added at: {:?}", address);
|
info!("New server added at: {:?}", address);
|
||||||
self.server_window_open = false;
|
self.server_window_open = false;
|
||||||
|
|
||||||
let send_channel = self.fetching_user_data_channel.0.clone();
|
let send_channel = self.join_server_channel.0.clone();
|
||||||
let server_address = self.saved_auth_address.clone().unwrap();
|
let auth_address = self.saved_auth_address.clone().unwrap();
|
||||||
let username = self.saved_username.clone().unwrap();
|
let username = self.saved_username.clone().unwrap();
|
||||||
let token = self.saved_token.clone().unwrap();
|
let token = self.saved_token.clone().unwrap();
|
||||||
fetch_user_data(send_channel, server_address, username, token);
|
|
||||||
|
let thread_username = username.clone();
|
||||||
|
let thread_token = token.clone();
|
||||||
|
let _handle = tokio::spawn(async move {
|
||||||
|
let mut transport = tarpc::serde_transport::tcp::connect(&address, Json::default);
|
||||||
|
transport.config_mut().max_frame_length(usize::MAX);
|
||||||
|
|
||||||
|
let result = transport.await;
|
||||||
|
let connection = match result {
|
||||||
|
Ok(connection) => connection,
|
||||||
|
Err(_) => {
|
||||||
|
send_channel.clone().send(Err(UnableToConnectToServer)).unwrap();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let client = RealmChatClient::new(tarpc::client::Config::default(), connection).spawn();
|
||||||
|
|
||||||
|
let domain = address.split(':').collect::<Vec<&str>>()[0].to_string();
|
||||||
|
let port = address.split(':').collect::<Vec<&str>>()[1].to_string().parse::<u16>().unwrap();
|
||||||
|
|
||||||
|
let info = client.get_info(context::current()).await.unwrap();
|
||||||
|
|
||||||
|
let result = client.join_server(context::current(), stoken(&thread_token, &info.server_id, &domain, port), thread_username).await;
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(_) => {
|
||||||
|
info!("Joined server!");
|
||||||
|
},
|
||||||
|
Err(e) => error!("Error joining server: {:?}", e),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
fetch_user_data(self.fetching_user_data_channel.0.clone(), auth_address, username, token);
|
||||||
}
|
}
|
||||||
Err(e) => error!("Error in adding a server: {:?}", e),
|
Err(e) => error!("Error in adding a server: {:?}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Joining a server
|
||||||
|
while let Ok(result) = self.join_server_channel.1.try_recv() {
|
||||||
|
match result {
|
||||||
|
Ok(_) => {
|
||||||
|
info!("Successfully joined a server");
|
||||||
|
fetch_server_data(self.current_user.clone().unwrap().server_addresses.clone(), self.fetching_servers_channel.0.clone());
|
||||||
|
},
|
||||||
|
Err(code) => {
|
||||||
|
error!("Error joining server: {:?}", code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetching servers
|
||||||
|
while let Ok(result) = self.fetching_servers_channel.1.try_recv() {
|
||||||
|
match result {
|
||||||
|
Ok(server) => {
|
||||||
|
info!("Got server data! Server: {:?}", server);
|
||||||
|
if let Some(active_servers) = &mut self.active_servers {
|
||||||
|
active_servers.push(server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => error!("Error fetching server data: {:?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// File -> Quit
|
// File -> Quit
|
||||||
gui::top_panel(self, ctx);
|
gui::top_panel(self, ctx);
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
use tracing::subscriber;
|
use tracing::*;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> eframe::Result {
|
async fn main() -> eframe::Result {
|
||||||
env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
|
|
||||||
|
|
||||||
let subscriber = tracing_subscriber::fmt()
|
let subscriber = tracing_subscriber::fmt()
|
||||||
.compact()
|
.compact()
|
||||||
.with_file(true)
|
.with_file(true)
|
||||||
.with_line_number(true)
|
.with_line_number(true)
|
||||||
.with_thread_ids(true)
|
.with_thread_ids(true)
|
||||||
.with_target(false)
|
.with_target(true)
|
||||||
.finish();
|
.finish();
|
||||||
|
|
||||||
subscriber::set_global_default(subscriber).unwrap();
|
subscriber::set_global_default(subscriber).unwrap();
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||||
pub struct ClientUser {
|
pub struct CUser {
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
pub server_address: String,
|
pub auth_address: String,
|
||||||
pub username: String,
|
pub username: String,
|
||||||
pub email: String,
|
pub email: String,
|
||||||
//pub avatar: String,
|
//pub avatar: String,
|
||||||
pub servers: Vec<String>,
|
pub server_addresses: Vec<String>,
|
||||||
pub token: String,
|
pub token: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||||
pub struct ClientServer {
|
pub struct CServer {
|
||||||
pub server_id: String,
|
pub server_id: String,
|
||||||
pub domain: String,
|
pub domain: String,
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ pub fn top_panel(app: &mut RealmApp, ctx: &Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if app.current_user.is_some() && ui.button("Logout").clicked() {
|
if app.current_user.is_some() && ui.button("Logout").clicked() {
|
||||||
let address = app.current_user.clone().unwrap().server_address;
|
let address = app.current_user.clone().unwrap().auth_address;
|
||||||
let username = app.current_user.clone().unwrap().username;
|
let username = app.current_user.clone().unwrap().username;
|
||||||
let token = app.current_user.clone().unwrap().token;
|
let token = app.current_user.clone().unwrap().token;
|
||||||
|
|
||||||
@@ -75,6 +75,18 @@ pub fn servers(app: &mut RealmApp, ctx: &Context) {
|
|||||||
if ui.add(SelectableLabel::new(app.selected, "server")).clicked() {
|
if ui.add(SelectableLabel::new(app.selected, "server")).clicked() {
|
||||||
app.selected = !app.selected;
|
app.selected = !app.selected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(active_servers) = &mut app.active_servers {
|
||||||
|
for server in active_servers {
|
||||||
|
if ui.add(SelectableLabel::new(server.server_id.eq(&app.selected_serverid), server.server_id.clone())).clicked() {
|
||||||
|
if app.selected_serverid.eq(&server.server_id) {
|
||||||
|
app.selected_serverid.clear();
|
||||||
|
} else {
|
||||||
|
app.selected_serverid = server.server_id.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,6 +124,14 @@ pub fn messages(app: &mut RealmApp, ctx: &Context) {
|
|||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
|
if let Some(servers) = &app.active_servers {
|
||||||
|
for server in servers {
|
||||||
|
ui.label(format!("Active server: {:?}", server));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.separator();
|
||||||
|
|
||||||
ui.label(format!("Current user: {:?}", app.current_user));
|
ui.label(format!("Current user: {:?}", app.current_user));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -282,10 +302,10 @@ pub fn modals(app: &mut RealmApp, ctx: &Context) {
|
|||||||
if app.current_user.is_some() && ui.button("Add Server").clicked() {
|
if app.current_user.is_some() && ui.button("Add Server").clicked() {
|
||||||
let domain = app.server_window_domain.clone();
|
let domain = app.server_window_domain.clone();
|
||||||
let port = app.server_window_port.clone();
|
let port = app.server_window_port.clone();
|
||||||
let auth_address = app.current_user.clone().unwrap().server_address;
|
let auth_address = app.current_user.clone().unwrap().auth_address;
|
||||||
let auth_username = app.current_user.clone().unwrap().username;
|
let auth_username = app.current_user.clone().unwrap().username;
|
||||||
let auth_token = app.current_user.clone().unwrap().token;
|
let auth_token = app.current_user.clone().unwrap().token;
|
||||||
let send_channel = app.added_server_channel.0.clone();
|
let send_channel = app.add_server_channel.0.clone();
|
||||||
|
|
||||||
let _handle = tokio::spawn(async move {
|
let _handle = tokio::spawn(async move {
|
||||||
let mut transport = tarpc::serde_transport::tcp::connect(auth_address, Json::default);
|
let mut transport = tarpc::serde_transport::tcp::connect(auth_address, Json::default);
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
let port = env::var("PORT").expect("PORT must be set").parse::<u16>()?;
|
let port = env::var("PORT").expect("PORT must be set").parse::<u16>()?;
|
||||||
let server_addr = (IpAddr::V6(Ipv6Addr::LOCALHOST), port);
|
let server_addr = (IpAddr::V6(Ipv6Addr::LOCALHOST), port);
|
||||||
|
|
||||||
let mut inner_manager = PacketManager::new_for_async();
|
let mut inner_manager = PacketManager::new();
|
||||||
|
|
||||||
inner_manager.register_receive_packet::<Greet>(GreetPacketBuilder)?;
|
inner_manager.register_receive_packet::<Greet>(GreetPacketBuilder)?;
|
||||||
inner_manager.register_receive_packet::<UserJoinedEvent>(UserJoinedEventPacketBuilder)?;
|
inner_manager.register_receive_packet::<UserJoinedEvent>(UserJoinedEventPacketBuilder)?;
|
||||||
@@ -83,10 +83,10 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
inner_manager.register_send_packet::<KickedUserEvent>()?;
|
inner_manager.register_send_packet::<KickedUserEvent>()?;
|
||||||
inner_manager.register_send_packet::<BannedUserEvent>()?;
|
inner_manager.register_send_packet::<BannedUserEvent>()?;
|
||||||
|
|
||||||
inner_manager.async_init_server(
|
inner_manager.init_server(
|
||||||
ServerConfig::new(
|
ServerConfig::new(
|
||||||
SocketAddr::from((IpAddr::V6(Ipv6Addr::LOCALHOST), port-1)).to_string(),
|
SocketAddr::from((IpAddr::V6(Ipv6Addr::LOCALHOST), port-1)).to_string(),
|
||||||
0, None, 8, 8)).await?;
|
0, None, 8, 8))?;
|
||||||
|
|
||||||
let manager = Arc::new(Mutex::new(inner_manager));
|
let manager = Arc::new(Mutex::new(inner_manager));
|
||||||
info!("Listening on port {}", port-1);
|
info!("Listening on port {}", port-1);
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ use realm_auth::types::RealmAuthClient;
|
|||||||
use realm_shared::types::ErrorCode::*;
|
use realm_shared::types::ErrorCode::*;
|
||||||
use realm_shared::types::ErrorCode;
|
use realm_shared::types::ErrorCode;
|
||||||
use crate::events::*;
|
use crate::events::*;
|
||||||
use crate::types::{Attachment, Edit, FromRows, Message, MessageData, Reaction, RealmChat, Redaction, Reply, ReplyChain, Room, User};
|
use crate::types::{Attachment, Edit, FromRows, Message, MessageData, Reaction, RealmChat, Redaction, Reply, ReplyChain, Room, ServerInfo, User};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RealmChatServer {
|
pub struct RealmChatServer {
|
||||||
@@ -55,9 +55,9 @@ impl RealmChatServer {
|
|||||||
async fn is_stoken_valid(&self, userid: &str, stoken: &str) -> bool {
|
async fn is_stoken_valid(&self, userid: &str, stoken: &str) -> bool {
|
||||||
match self.cache.get(stoken).await {
|
match self.cache.get(stoken).await {
|
||||||
None => {
|
None => {
|
||||||
if !self.is_user_in_server(userid).await {
|
// if !self.is_user_in_server(userid).await {
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
|
|
||||||
let user_domain = &userid[userid.find(':').unwrap()+1..];
|
let user_domain = &userid[userid.find(':').unwrap()+1..];
|
||||||
|
|
||||||
@@ -94,7 +94,7 @@ impl RealmChatServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn is_user_admin(&self, stoken: &str) -> bool {
|
pub async fn internal_is_user_admin(&self, stoken: &str) -> bool {
|
||||||
if let Some(userid) = self.cache.get(stoken).await {
|
if let Some(userid) = self.cache.get(stoken).await {
|
||||||
let result = query!("SELECT admin FROM user WHERE userid = ?", userid).fetch_one(&self.db_pool).await;
|
let result = query!("SELECT admin FROM user WHERE userid = ?", userid).fetch_one(&self.db_pool).await;
|
||||||
return match result {
|
return match result {
|
||||||
@@ -110,7 +110,7 @@ impl RealmChatServer {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn is_user_owner(&self, stoken: &str) -> bool {
|
pub async fn internal_is_user_owner(&self, stoken: &str) -> bool {
|
||||||
if let Some(userid) = self.cache.get(stoken).await {
|
if let Some(userid) = self.cache.get(stoken).await {
|
||||||
let result = query!("SELECT owner FROM user WHERE userid = ?", userid).fetch_one(&self.db_pool).await;
|
let result = query!("SELECT owner FROM user WHERE userid = ?", userid).fetch_one(&self.db_pool).await;
|
||||||
return match result {
|
return match result {
|
||||||
@@ -136,7 +136,7 @@ impl RealmChatServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn inner_get_all_direct_replies(&self, stoken: &str, head: i64) -> Result<Vec<Message>, ErrorCode> {
|
async fn inner_get_all_direct_replies(&self, stoken: &str, head: i64) -> Result<Vec<Message>, ErrorCode> {
|
||||||
let is_admin = self.is_user_admin(stoken).await;
|
let is_admin = self.internal_is_user_admin(stoken).await;
|
||||||
let result = sqlx::query(&format!("{}{}", FETCH_MESSAGE, "AND message.referencing_id = ?"))
|
let result = sqlx::query(&format!("{}{}", FETCH_MESSAGE, "AND message.referencing_id = ?"))
|
||||||
.bind(is_admin)
|
.bind(is_admin)
|
||||||
.bind(head)
|
.bind(head)
|
||||||
@@ -175,7 +175,7 @@ impl RealmChatServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn inner_get_room(&self, stoken: &str, roomid: &str) -> Result<Room, ErrorCode> {
|
async fn inner_get_room(&self, stoken: &str, roomid: &str) -> Result<Room, ErrorCode> {
|
||||||
let is_admin = self.is_user_admin(&stoken).await;
|
let is_admin = self.internal_is_user_admin(&stoken).await;
|
||||||
let result = query_as!(
|
let result = query_as!(
|
||||||
Room, "SELECT * FROM room WHERE roomid = ? AND admin_only_view = ? OR false", is_admin, roomid).fetch_one(&self.db_pool).await;
|
Room, "SELECT * FROM room WHERE roomid = ? AND admin_only_view = ? OR false", is_admin, roomid).fetch_one(&self.db_pool).await;
|
||||||
|
|
||||||
@@ -204,7 +204,7 @@ impl RealmChatServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn inner_get_message(&self, stoken: &str, id: i64) -> Result<Message, ErrorCode> {
|
async fn inner_get_message(&self, stoken: &str, id: i64) -> Result<Message, ErrorCode> {
|
||||||
let is_admin = self.is_user_admin(&stoken).await;
|
let is_admin = self.internal_is_user_admin(&stoken).await;
|
||||||
let result = sqlx::query(&format!("{}{}", FETCH_MESSAGE, "AND message.id = ?"))
|
let result = sqlx::query(&format!("{}{}", FETCH_MESSAGE, "AND message.id = ?"))
|
||||||
.bind(is_admin)
|
.bind(is_admin)
|
||||||
.bind(id)
|
.bind(id)
|
||||||
@@ -222,12 +222,26 @@ impl RealmChat for RealmChatServer {
|
|||||||
format!("Hello, {name}!")
|
format!("Hello, {name}!")
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn join_server(self, _: Context, stoken: String, user: User) -> Result<User, ErrorCode> {
|
async fn get_info(self, _: Context) -> ServerInfo {
|
||||||
if !self.is_stoken_valid(&user.userid, &stoken).await {
|
ServerInfo {
|
||||||
|
server_id: self.server_id.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn is_user_admin(self, _: Context, stoken: String) -> bool {
|
||||||
|
self.internal_is_user_admin(&stoken).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn is_user_owner(self, _: Context, stoken: String) -> bool {
|
||||||
|
self.internal_is_user_owner(&stoken).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn join_server(self, _: Context, stoken: String, userid: String) -> Result<User, ErrorCode> {
|
||||||
|
if !self.is_stoken_valid(&userid, &stoken).await {
|
||||||
return Err(Unauthorized)
|
return Err(Unauthorized)
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.is_user_in_server(&user.userid).await {
|
if self.is_user_in_server(&userid).await {
|
||||||
return Err(AlreadyJoinedServer)
|
return Err(AlreadyJoinedServer)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,12 +250,13 @@ impl RealmChat for RealmChatServer {
|
|||||||
all_users.is_empty()
|
all_users.is_empty()
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = query!("INSERT INTO user (userid, name, owner, admin) VALUES (?,?,?,?)", user.userid, user.name, is_owner, is_owner).execute(&self.db_pool).await;
|
//TOOD: name support
|
||||||
|
let result = query!("INSERT INTO user (userid, name, owner, admin) VALUES (?,?,?,?)", userid, "userid", is_owner, is_owner).execute(&self.db_pool).await;
|
||||||
|
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let new_user = self.inner_get_user(&user.userid).await?;
|
let new_user = self.inner_get_user(&userid).await?;
|
||||||
|
|
||||||
let result = self.packet_manager.lock().await.broadcast(UserJoinedEvent {
|
let result = self.packet_manager.lock().await.broadcast(UserJoinedEvent {
|
||||||
user: new_user.clone(),
|
user: new_user.clone(),
|
||||||
@@ -256,21 +271,23 @@ impl RealmChat for RealmChatServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn leave_server(self, _: Context, stoken: String, user: User) -> Result<(), ErrorCode> {
|
async fn leave_server(self, _: Context, stoken: String, userid: String) -> Result<(), ErrorCode> {
|
||||||
if !self.is_stoken_valid(&user.userid, &stoken).await {
|
if !self.is_stoken_valid(&userid, &stoken).await {
|
||||||
return Err(Unauthorized)
|
return Err(Unauthorized)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.is_user_in_server(&user.userid).await {
|
if !self.is_user_in_server(&userid).await {
|
||||||
return Err(NotInServer)
|
return Err(NotInServer)
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = query!("DELETE FROM user WHERE userid = ?",user.userid).execute(&self.db_pool).await;
|
let user = self.inner_get_user(&userid).await?;
|
||||||
|
|
||||||
|
let result = query!("DELETE FROM user WHERE userid = ?", userid).execute(&self.db_pool).await;
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let result = self.packet_manager.lock().await.broadcast(UserLeftEvent {
|
let result = self.packet_manager.lock().await.broadcast(UserLeftEvent {
|
||||||
user: user.clone(),
|
user,
|
||||||
});
|
});
|
||||||
|
|
||||||
if result.is_err() {
|
if result.is_err() {
|
||||||
@@ -300,14 +317,14 @@ impl RealmChat for RealmChatServer {
|
|||||||
}
|
}
|
||||||
MessageData::Redaction(r)=> {
|
MessageData::Redaction(r)=> {
|
||||||
let ref_msg = self.inner_get_message(&stoken, r.referencing_id).await?;
|
let ref_msg = self.inner_get_message(&stoken, r.referencing_id).await?;
|
||||||
if !ref_msg.user.userid.eq(&message.user.userid) || !self.is_user_admin(&stoken).await {
|
if !ref_msg.user.userid.eq(&message.user.userid) || !self.internal_is_user_admin(&stoken).await {
|
||||||
return Err(Unauthorized)
|
return Err(Unauthorized)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
let is_admin = self.is_user_admin(&stoken).await;
|
let is_admin = self.internal_is_user_admin(&stoken).await;
|
||||||
let admin_only_send = query!(
|
let admin_only_send = query!(
|
||||||
"SELECT admin_only_send FROM room WHERE roomid = ?",
|
"SELECT admin_only_send FROM room WHERE roomid = ?",
|
||||||
message.room.roomid).fetch_one(&self.db_pool).await;
|
message.room.roomid).fetch_one(&self.db_pool).await;
|
||||||
@@ -379,7 +396,7 @@ impl RealmChat for RealmChatServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn get_message(self, _: Context, stoken: String, id: i64) -> Result<Message, ErrorCode> {
|
async fn get_message(self, _: Context, stoken: String, id: i64) -> Result<Message, ErrorCode> {
|
||||||
let is_admin = self.is_user_admin(&stoken).await;
|
let is_admin = self.internal_is_user_admin(&stoken).await;
|
||||||
let result = sqlx::query(&format!("{}{}", FETCH_MESSAGE, "AND message.id = ?"))
|
let result = sqlx::query(&format!("{}{}", FETCH_MESSAGE, "AND message.id = ?"))
|
||||||
.bind(is_admin)
|
.bind(is_admin)
|
||||||
.bind(id)
|
.bind(id)
|
||||||
@@ -396,7 +413,7 @@ impl RealmChat for RealmChatServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn get_messages_since(self, _: Context, stoken: String, time: DateTime<Utc>) -> Result<Vec<Message>, ErrorCode> {
|
async fn get_messages_since(self, _: Context, stoken: String, time: DateTime<Utc>) -> Result<Vec<Message>, ErrorCode> {
|
||||||
let is_admin = self.is_user_admin(&stoken).await;
|
let is_admin = self.internal_is_user_admin(&stoken).await;
|
||||||
let result = sqlx::query(&format!("{}{}", FETCH_MESSAGE, "AND message.timestamp >= ?"))
|
let result = sqlx::query(&format!("{}{}", FETCH_MESSAGE, "AND message.timestamp >= ?"))
|
||||||
.bind(is_admin)
|
.bind(is_admin)
|
||||||
.bind(time)
|
.bind(time)
|
||||||
@@ -417,7 +434,7 @@ impl RealmChat for RealmChatServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn get_rooms(self, _: Context, stoken: String) -> Result<Vec<Room>, ErrorCode> {
|
async fn get_rooms(self, _: Context, stoken: String) -> Result<Vec<Room>, ErrorCode> {
|
||||||
let is_admin = self.is_user_admin(&stoken).await;
|
let is_admin = self.internal_is_user_admin(&stoken).await;
|
||||||
let result = query_as!(
|
let result = query_as!(
|
||||||
Room, "SELECT * FROM room WHERE admin_only_view = ? OR false", is_admin).fetch_all(&self.db_pool).await;
|
Room, "SELECT * FROM room WHERE admin_only_view = ? OR false", is_admin).fetch_all(&self.db_pool).await;
|
||||||
|
|
||||||
@@ -440,7 +457,7 @@ impl RealmChat for RealmChatServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn create_room(self, _: Context, stoken: String, room: Room) -> Result<Room, ErrorCode> {
|
async fn create_room(self, _: Context, stoken: String, room: Room) -> Result<Room, ErrorCode> {
|
||||||
if !self.is_user_admin(&stoken).await {
|
if !self.internal_is_user_admin(&stoken).await {
|
||||||
return Err(Unauthorized)
|
return Err(Unauthorized)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -465,7 +482,7 @@ impl RealmChat for RealmChatServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn delete_room(self, _: Context, stoken: String, roomid: String) -> Result<(), ErrorCode> {
|
async fn delete_room(self, _: Context, stoken: String, roomid: String) -> Result<(), ErrorCode> {
|
||||||
if !self.is_user_admin(&stoken).await {
|
if !self.internal_is_user_admin(&stoken).await {
|
||||||
return Err(Unauthorized)
|
return Err(Unauthorized)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -488,7 +505,7 @@ impl RealmChat for RealmChatServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn promote_user(self, _: Context, stoken: String, userid: String) -> Result<(), ErrorCode> {
|
async fn promote_user(self, _: Context, stoken: String, userid: String) -> Result<(), ErrorCode> {
|
||||||
if !self.is_user_owner(&stoken).await {
|
if !self.internal_is_user_owner(&stoken).await {
|
||||||
return Err(Unauthorized)
|
return Err(Unauthorized)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -511,7 +528,7 @@ impl RealmChat for RealmChatServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn demote_user(self, _: Context, stoken: String, userid: String) -> Result<(), ErrorCode> {
|
async fn demote_user(self, _: Context, stoken: String, userid: String) -> Result<(), ErrorCode> {
|
||||||
if !self.is_user_owner(&stoken).await {
|
if !self.internal_is_user_owner(&stoken).await {
|
||||||
return Err(Unauthorized)
|
return Err(Unauthorized)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -534,7 +551,7 @@ impl RealmChat for RealmChatServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn kick_user(self, _: Context, stoken: String, userid: String) -> Result<(), ErrorCode> {
|
async fn kick_user(self, _: Context, stoken: String, userid: String) -> Result<(), ErrorCode> {
|
||||||
if !self.is_user_admin(&stoken).await {
|
if !self.internal_is_user_admin(&stoken).await {
|
||||||
return Err(Unauthorized)
|
return Err(Unauthorized)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -557,7 +574,7 @@ impl RealmChat for RealmChatServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn ban_user(self, _: Context, stoken: String, userid: String) -> Result<(), ErrorCode> {
|
async fn ban_user(self, _: Context, stoken: String, userid: String) -> Result<(), ErrorCode> {
|
||||||
if !self.is_user_admin(&stoken).await {
|
if !self.internal_is_user_admin(&stoken).await {
|
||||||
return Err(Unauthorized)
|
return Err(Unauthorized)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -581,7 +598,7 @@ impl RealmChat for RealmChatServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn pardon_user(self, _: Context, stoken: String, userid: String) -> Result<(), ErrorCode> {
|
async fn pardon_user(self, _: Context, stoken: String, userid: String) -> Result<(), ErrorCode> {
|
||||||
if !self.is_user_admin(&stoken).await {
|
if !self.internal_is_user_admin(&stoken).await {
|
||||||
return Err(Unauthorized)
|
return Err(Unauthorized)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,12 @@ use crate::types::MessageData::*;
|
|||||||
pub trait RealmChat {
|
pub trait RealmChat {
|
||||||
async fn test(name: String) -> String;
|
async fn test(name: String) -> String;
|
||||||
|
|
||||||
async fn join_server(stoken: String, user: User) -> Result<User, ErrorCode>;
|
async fn get_info() -> ServerInfo;
|
||||||
async fn leave_server(stoken: String, user: User) -> Result<(), ErrorCode>;
|
async fn is_user_admin(stoken: String) -> bool;
|
||||||
|
async fn is_user_owner(stoken: String) -> bool;
|
||||||
|
|
||||||
|
async fn join_server(stoken: String, userid: String) -> Result<User, ErrorCode>;
|
||||||
|
async fn leave_server(stoken: String, userid: String) -> Result<(), ErrorCode>;
|
||||||
|
|
||||||
//NOTE: Any user authorized as themselves
|
//NOTE: Any user authorized as themselves
|
||||||
async fn send_message(stoken: String, message: Message) -> Result<Message, ErrorCode>;
|
async fn send_message(stoken: String, message: Message) -> Result<Message, ErrorCode>;
|
||||||
@@ -38,6 +42,11 @@ pub trait RealmChat {
|
|||||||
async fn pardon_user(stoken: String, userid: String) -> Result<(), ErrorCode>;
|
async fn pardon_user(stoken: String, userid: String) -> Result<(), ErrorCode>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ServerInfo {
|
||||||
|
pub server_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
|
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
|
||||||
pub struct Message {
|
pub struct Message {
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
|
|||||||
@@ -5,3 +5,5 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { version = "1.0.203", features = ["derive"] }
|
serde = { version = "1.0.203", features = ["derive"] }
|
||||||
|
sha3 = "0.10.8"
|
||||||
|
hex = "0.4.3"
|
||||||
|
|||||||
@@ -1 +1,9 @@
|
|||||||
|
use sha3::digest::Update;
|
||||||
|
use sha3::{Digest, Sha3_256};
|
||||||
|
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
|
pub fn stoken(token: &str, serverid: &str, domain: &str, port: u16) -> String {
|
||||||
|
let hash = Sha3_256::new().chain(format!("{}{}{}{}", token, serverid, domain, port)).finalize();
|
||||||
|
hex::encode(hash)
|
||||||
|
}
|
||||||
@@ -23,4 +23,5 @@ pub enum ErrorCode {
|
|||||||
MalformedDBResponse,
|
MalformedDBResponse,
|
||||||
|
|
||||||
RPCError,
|
RPCError,
|
||||||
|
UnableToConnectToServer,
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user