Adding servers, fetching servers on the client

This commit is contained in:
2024-10-12 14:57:54 -04:00
Unverified
parent c333bf1b49
commit 87b3125f8e
6 changed files with 262 additions and 120 deletions

View File

@@ -507,11 +507,13 @@ impl RealmAuth for RealmAuthServer {
} }
} }
async fn add_server(self, _: Context, username: String, token: String, domain: String) -> Result<(), ErrorCode> { async fn add_server(self, _: Context, username: String, token: String, domain: String, port: u16) -> Result<(), ErrorCode> {
if !self.is_authorized(&username, &token).await? { if !self.is_authorized(&username, &token).await? {
return Err(Unauthorized); return Err(Unauthorized);
} }
let address = format!("{}:{}", domain, port);
let result = query!("SELECT servers FROM user WHERE username = ?", username).fetch_one(&self.db_pool).await; let result = query!("SELECT servers FROM user WHERE username = ?", username).fetch_one(&self.db_pool).await;
match result { match result {
Ok(row) => { Ok(row) => {
@@ -523,12 +525,12 @@ impl RealmAuth for RealmAuthServer {
} }
}; };
for server in &vec_servers { for server in &vec_servers {
if server.eq(&domain) { if server.eq(&address) {
return Err(AlreadyJoinedServer); return Err(AlreadyJoinedServer);
} }
} }
vec_servers.push(&domain); 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).fetch_one(&self.db_pool).await;
@@ -541,11 +543,13 @@ impl RealmAuth for RealmAuthServer {
} }
} }
async fn remove_server(self, _: Context, username: String, token: String, domain: String) -> Result<(), ErrorCode> { async fn remove_server(self, _: Context, username: String, token: String, domain: String, port: u16) -> Result<(), ErrorCode> {
if !self.is_authorized(&username, &token).await? { if !self.is_authorized(&username, &token).await? {
return Err(Unauthorized); return Err(Unauthorized);
} }
let address = format!("{}:{}", domain, port);
let result = query!("SELECT servers FROM user WHERE username = ?", username).fetch_one(&self.db_pool).await; let result = query!("SELECT servers FROM user WHERE username = ?", username).fetch_one(&self.db_pool).await;
match result { match result {
Ok(row) => { Ok(row) => {
@@ -557,7 +561,7 @@ impl RealmAuth for RealmAuthServer {
} }
}; };
for i in 0..vec_servers.len() { for i in 0..vec_servers.len() {
if vec_servers.get(i).unwrap().eq(&domain) { if vec_servers.get(i).unwrap().eq(&address) {
vec_servers.remove(i); vec_servers.remove(i);
let new_servers = vec_servers.join("|"); let new_servers = vec_servers.join("|");

View File

@@ -18,8 +18,8 @@ pub trait RealmAuth {
async fn sign_out(username: String, token: String) -> Result<(), ErrorCode>; async fn sign_out(username: String, token: String) -> Result<(), ErrorCode>;
async fn delete_account_flow(username: String, token: String) -> Result<(), ErrorCode>; async fn delete_account_flow(username: String, token: String) -> Result<(), ErrorCode>;
async fn finish_delete_account_flow(username: String, token: String, login_code: u32) -> Result<(), ErrorCode>; async fn finish_delete_account_flow(username: String, token: String, login_code: u32) -> Result<(), ErrorCode>;
async fn add_server(username: String, token: String, domain: String) -> Result<(), ErrorCode>; async fn add_server(username: String, token: String, domain: String, port: u16) -> Result<(), ErrorCode>;
async fn remove_server(username: String, token: String, domain: String) -> Result<(), ErrorCode>; async fn remove_server(username: String, token: String, domain: String, port: u16) -> Result<(), ErrorCode>;
async fn get_joined_servers(username: String, token: String) -> Result<Vec<String>, ErrorCode>; async fn get_joined_servers(username: String, token: String) -> Result<Vec<String>, ErrorCode>;
//NOTE: Anyone can call //NOTE: Anyone can call

View File

@@ -7,20 +7,25 @@ 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::types::ClientUser; use crate::types::ClientUser;
use crate::ui::panels; 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.
#[derive(serde::Deserialize, serde::Serialize)] #[derive(serde::Deserialize, serde::Serialize)]
#[serde(default)] // if we add new fields, give them default values when deserializing old state #[serde(default)] // if we add new fields, give them default values when deserializing old state
pub struct TemplateApp { pub struct RealmApp {
// Example stuff: // Example stuff:
pub label: String, pub label: String,
pub selected: bool, pub selected: bool,
pub selected_serverid: Option<String>, pub selected_serverid: Option<String>,
pub selected_roomid: Option<String>, pub selected_roomid: Option<String>,
#[serde(skip)]
pub current_user: Option<ClientUser>, pub current_user: Option<ClientUser>,
pub saved_username: Option<String>,
pub saved_token: Option<String>,
pub saved_auth_address: Option<String>,
#[serde(skip)] #[serde(skip)]
pub value: f32, pub value: f32,
#[serde(skip)] #[serde(skip)]
@@ -40,6 +45,13 @@ pub struct TemplateApp {
#[serde(skip)] #[serde(skip)]
pub signup_window_open: bool, pub signup_window_open: bool,
#[serde(skip)]
pub server_window_open: bool,
#[serde(skip)]
pub server_window_domain: String,
#[serde(skip)]
pub server_window_port: String,
#[serde(skip)] #[serde(skip)]
pub login_start_channel: (Sender<Result<(), ErrorCode>>, Receiver<Result<(), ErrorCode>>), pub login_start_channel: (Sender<Result<(), ErrorCode>>, Receiver<Result<(), ErrorCode>>),
#[serde(skip)] #[serde(skip)]
@@ -47,9 +59,12 @@ pub struct TemplateApp {
#[serde(skip)] #[serde(skip)]
pub fetching_user_data_channel: (Sender<Result<ClientUser, ErrorCode>>, Receiver<Result<ClientUser, ErrorCode>>), pub fetching_user_data_channel: (Sender<Result<ClientUser, ErrorCode>>, Receiver<Result<ClientUser, ErrorCode>>),
#[serde(skip)]
pub added_server_channel: (Sender<Result<String, ErrorCode>>, Receiver<Result<String, ErrorCode>>),
} }
impl Default for TemplateApp { impl Default for RealmApp {
fn default() -> Self { fn default() -> Self {
Self { Self {
// Example stuff: // Example stuff:
@@ -58,6 +73,9 @@ impl Default for TemplateApp {
selected_serverid: None, selected_serverid: None,
selected_roomid: None, selected_roomid: None,
current_user: None, current_user: None,
saved_username: None,
saved_token: None,
saved_auth_address: None,
value: 2.7, value: 2.7,
login_window_open: false, login_window_open: false,
@@ -71,12 +89,18 @@ impl Default for TemplateApp {
signup_window_open: false, signup_window_open: false,
server_window_open: false,
server_window_domain: String::new(),
server_window_port: "5051".to_string(),
fetching_user_data_channel: broadcast::channel(10), fetching_user_data_channel: broadcast::channel(10),
added_server_channel: broadcast::channel(10),
} }
} }
} }
impl TemplateApp { impl RealmApp {
/// Called once before the first frame. /// Called once before the first frame.
pub fn new(cc: &eframe::CreationContext<'_>) -> Self { pub fn new(cc: &eframe::CreationContext<'_>) -> Self {
// This is also where you can customize the look and feel of egui using // This is also where you can customize the look and feel of egui using
@@ -92,12 +116,73 @@ impl TemplateApp {
} }
} }
impl eframe::App for TemplateApp { pub fn fetch_user_data(send_channel: Sender<Result<ClientUser, ErrorCode>>, server_address: String, username: String, token: String) {
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(e) => {
tracing::error!("Failed to connect to server: {}", e);
return;
}
};
let client = RealmAuthClient::new(tarpc::client::Config::default(), connection).spawn();
let result = client.get_all_data(context::current(), username, token.clone()).await;
match result {
Ok(r) => {
if let Err(code) = r {
send_channel.send(Err(code)).unwrap();
} else {
let auth_user = r.unwrap();
let servers: Vec<String> = {
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,
server_address,
username: auth_user.username,
email: auth_user.email,
//avatar: auth_user.avatar,
servers,
token,
})).unwrap();
}
}
Err(_) => {
send_channel.send(Err(RPCError)).unwrap();
}
};
});
}
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) {
// Put your widgets into a `SidePanel`, `TopBottomPanel`, `CentralPanel`, `Window` or `Area`. // Put your widgets into a `SidePanel`, `TopBottomPanel`, `CentralPanel`, `Window` or `Area`.
// For inspiration and more examples, go to https://emilk.github.io/egui // For inspiration and more examples, go to https://emilk.github.io/egui
// Init at launch and refresh our user data from the auth server
if self.current_user.is_none() &&
self.saved_token.is_some() &&
self.saved_auth_address.is_some() &&
self.saved_username.is_some()
{
let send_channel = self.fetching_user_data_channel.0.clone();
let server_address = self.saved_auth_address.clone().unwrap();
let username = self.saved_username.clone().unwrap();
let token = self.saved_token.clone().unwrap();
fetch_user_data(send_channel, server_address, username, token);
}
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,
@@ -119,52 +204,12 @@ impl eframe::App for TemplateApp {
let server_address = self.login_window_server_address.clone(); let server_address = self.login_window_server_address.clone();
let username = self.login_window_username.clone(); let username = self.login_window_username.clone();
let _handle = tokio::spawn(async move { self.saved_token = Some(token.clone());
let mut transport = tarpc::serde_transport::tcp::connect(&server_address, Json::default); self.saved_username = Some(username.clone());
transport.config_mut().max_frame_length(usize::MAX); self.saved_auth_address = Some(server_address.clone());
let result = transport.await; fetch_user_data(send_channel, server_address, username, token);
let connection = match result { }
Ok(connection) => connection,
Err(e) => {
tracing::error!("Failed to connect to server: {}", e);
return;
}
};
let client = RealmAuthClient::new(tarpc::client::Config::default(), connection).spawn();
let result = client.get_all_data(context::current(), username, token.clone()).await;
match result {
Ok(r) => {
if let Err(code) = r {
send_channel.send(Err(code)).unwrap();
} else {
let auth_user = r.unwrap();
let servers: Vec<String> = {
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,
server_address,
username: auth_user.username,
email: auth_user.email,
//avatar: auth_user.avatar,
servers,
token,
})).unwrap();
}
},
Err(_) => {
send_channel.send(Err(RPCError)).unwrap();
},
};
});
},
Err(e) => tracing::error!("Error in login flow: {:?}", e), Err(e) => tracing::error!("Error in login flow: {:?}", e),
} }
} }
@@ -174,19 +219,37 @@ impl eframe::App for TemplateApp {
Ok(client_user) => { Ok(client_user) => {
info!("Got data! User: {:?}", client_user); info!("Got data! User: {:?}", client_user);
self.current_user = Some(client_user); self.current_user = Some(client_user);
}, }
Err(e) => error!("Error in login flow: {:?}", e), Err(e) => error!("Error fetching data: {:?}", e),
}
}
while let Ok(result) = self.added_server_channel.1.try_recv() {
match result {
Ok(address) => {
info!("New server added at: {:?}", address);
self.server_window_open = false;
let send_channel = self.fetching_user_data_channel.0.clone();
let server_address = self.saved_auth_address.clone().unwrap();
let username = self.saved_username.clone().unwrap();
let token = self.saved_token.clone().unwrap();
fetch_user_data(send_channel, server_address, username, token);
}
Err(e) => error!("Error in adding a server: {:?}", e),
} }
} }
// File -> Quit // File -> Quit
panels::top_panel(self, ctx); gui::top_panel(self, ctx);
panels::servers(self, ctx); gui::servers(self, ctx);
panels::rooms(self, ctx); gui::rooms(self, ctx);
panels::messages(self, ctx) gui::messages(self, ctx);
gui::modals(self, ctx)
} }
/// Called by the frame work to save state before shutdown. /// Called by the frame work to save state before shutdown.

View File

@@ -28,6 +28,6 @@ async fn main() -> eframe::Result {
eframe::run_native( eframe::run_native(
"Realm", "Realm",
native_options, native_options,
Box::new(|cc| Ok(Box::new(realm_client::app::TemplateApp::new(cc)))), Box::new(|cc| Ok(Box::new(realm_client::app::RealmApp::new(cc)))),
) )
} }

View File

@@ -5,9 +5,9 @@ use realm_auth::types::RealmAuthClient;
use realm_shared::types::ErrorCode::RPCError; use realm_shared::types::ErrorCode::RPCError;
use regex::Regex; use regex::Regex;
use tracing::log::*; use tracing::log::*;
use crate::app::TemplateApp; use crate::app::RealmApp;
pub fn top_panel(app: &mut TemplateApp, ctx: &Context) { pub fn top_panel(app: &mut RealmApp, ctx: &Context) {
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| { egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
egui::menu::bar(ui, |ui| { egui::menu::bar(ui, |ui| {
if app.current_user.is_none() && ui.button("Sign Up").clicked() { if app.current_user.is_none() && ui.button("Sign Up").clicked() {
@@ -46,6 +46,9 @@ pub fn top_panel(app: &mut TemplateApp, ctx: &Context) {
}); });
app.current_user = None; app.current_user = None;
app.saved_username = None;
app.saved_token = None;
app.saved_auth_address = None;
} }
if ui.button("Quit").clicked() { if ui.button("Quit").clicked() {
@@ -54,10 +57,66 @@ pub fn top_panel(app: &mut TemplateApp, ctx: &Context) {
ui.add_space(16.0); ui.add_space(16.0);
egui::widgets::global_dark_light_mode_buttons(ui); egui::widgets::global_theme_preference_buttons(ui);
}); });
}); });
}
pub fn servers(app: &mut RealmApp, ctx: &Context) {
egui::SidePanel::left("servers").show(ctx, |ui| {
ui.horizontal(|ui| {
ui.heading("Servers");
if app.current_user.is_some() && ui.button("+").clicked() {
app.server_window_open = true;
}
});
ui.separator();
if ui.add(SelectableLabel::new(app.selected, "server")).clicked() {
app.selected = !app.selected;
}
});
}
pub fn rooms(app: &mut RealmApp, ctx: &Context) {
egui::SidePanel::left("rooms").show(ctx, |ui| {
ui.heading("Rooms");
ui.separator();
if ui.add(SelectableLabel::new(app.selected, "room")).clicked() {
app.selected = !app.selected;
}
});
}
pub fn messages(app: &mut RealmApp, ctx: &Context) {
egui::CentralPanel::default().show(ctx, |ui| {
// The central panel the region left after adding TopPanel's and SidePanel's
ui.heading("eframe template");
ui.horizontal(|ui| {
ui.label("Write something: ");
ui.text_edit_singleline(&mut app.label);
});
ui.add(egui::Slider::new(&mut app.value, 0.0..=10.0).text("value"));
if ui.button("Increment").clicked() {
app.value += 1.0;
}
ui.separator();
ui.label(format!("Saved username: {:?}", app.saved_username));
ui.label(format!("Saved token: {:?}", app.saved_token));
ui.label(format!("Saved auth address: {:?}", app.saved_auth_address));
ui.separator();
ui.label(format!("Current user: {:?}", app.current_user));
});
}
pub fn modals(app: &mut RealmApp, ctx: &Context) {
egui::Window::new("Signup") egui::Window::new("Signup")
.open(&mut app.signup_window_open) .open(&mut app.signup_window_open)
.min_size((500.0, 200.0)) .min_size((500.0, 200.0))
@@ -201,47 +260,63 @@ pub fn top_panel(app: &mut TemplateApp, ctx: &Context) {
//ui.close_menu() //ui.close_menu()
} }
}); });
}
pub fn servers(app: &mut TemplateApp, ctx: &Context) { egui::Window::new("Add Server")
egui::SidePanel::left("servers").show(ctx, |ui| { .open(&mut app.server_window_open)
ui.heading("Servers"); .min_size((500.0, 200.0))
ui.separator(); .show(ctx, |ui| {
ui.horizontal(|ui| {
ui.label("Domain: ");
ui.text_edit_singleline(&mut app.server_window_domain);
});
if ui.add(SelectableLabel::new(app.selected, "server")).clicked() { ui.horizontal(|ui| {
app.selected = !app.selected; ui.label("Port: ");
} if ui.text_edit_singleline(&mut app.server_window_port).changed() {
}); let re = Regex::new(r"[^0-9]+").unwrap();
} app.login_window_code = re.replace_all(&app.server_window_port, "").to_string();
}
});
pub fn rooms(app: &mut TemplateApp, ctx: &Context) {
egui::SidePanel::left("rooms").show(ctx, |ui| {
ui.heading("Rooms");
ui.separator();
if ui.add(SelectableLabel::new(app.selected, "room")).clicked() { if app.current_user.is_some() && ui.button("Add Server").clicked() {
app.selected = !app.selected; let domain = app.server_window_domain.clone();
} let port = app.server_window_port.clone();
}); let auth_address = app.current_user.clone().unwrap().server_address;
} let auth_username = app.current_user.clone().unwrap().username;
let auth_token = app.current_user.clone().unwrap().token;
let send_channel = app.added_server_channel.0.clone();
pub fn messages(app: &mut TemplateApp, ctx: &Context) { let _handle = tokio::spawn(async move {
egui::CentralPanel::default().show(ctx, |ui| { let mut transport = tarpc::serde_transport::tcp::connect(auth_address, Json::default);
// The central panel the region left after adding TopPanel's and SidePanel's transport.config_mut().max_frame_length(usize::MAX);
ui.heading("eframe template");
ui.horizontal(|ui| { let result = transport.await;
ui.label("Write something: "); let connection = match result {
ui.text_edit_singleline(&mut app.label); Ok(connection) => connection,
Err(e) => {
tracing::error!("Failed to connect to server: {}", e);
return;
}
};
let client = RealmAuthClient::new(tarpc::client::Config::default(), connection).spawn();
let result = client.add_server(
context::current(), auth_username, auth_token, domain.clone(), port.parse::<u16>().unwrap()).await;
match result {
Ok(r) => {
match r {
Ok(_) => {
info!("Server added successfully!");
send_channel.send(Ok(format!("{}:{}", domain, port))).unwrap();
},
Err(e) => error!("Error adding server: {:?}", e),
}
},
Err(_) => { send_channel.send(Err(RPCError)).unwrap(); },
};
});
}
}); });
ui.add(egui::Slider::new(&mut app.value, 0.0..=10.0).text("value"));
if ui.button("Increment").clicked() {
app.value += 1.0;
}
ui.separator();
ui.label(format!("Current user: {:?}", app.current_user));
});
} }

View File

@@ -1 +1 @@
pub mod panels; pub mod gui;