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? {
return Err(Unauthorized);
}
let address = format!("{}:{}", domain, port);
let result = query!("SELECT servers FROM user WHERE username = ?", username).fetch_one(&self.db_pool).await;
match result {
Ok(row) => {
@@ -523,12 +525,12 @@ impl RealmAuth for RealmAuthServer {
}
};
for server in &vec_servers {
if server.eq(&domain) {
if server.eq(&address) {
return Err(AlreadyJoinedServer);
}
}
vec_servers.push(&domain);
vec_servers.push(&address);
let new_servers = vec_servers.join("|");
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? {
return Err(Unauthorized);
}
let address = format!("{}:{}", domain, port);
let result = query!("SELECT servers FROM user WHERE username = ?", username).fetch_one(&self.db_pool).await;
match result {
Ok(row) => {
@@ -557,7 +561,7 @@ impl RealmAuth for RealmAuthServer {
}
};
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);
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 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 add_server(username: String, token: String, domain: String) -> Result<(), ErrorCode>;
async fn remove_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, port: u16) -> Result<(), ErrorCode>;
async fn get_joined_servers(username: String, token: String) -> Result<Vec<String>, ErrorCode>;
//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 crate::types::ClientUser;
use crate::ui::panels;
use crate::ui::gui;
/// We derive Deserialize/Serialize so we can persist app state on shutdown.
#[derive(serde::Deserialize, serde::Serialize)]
#[serde(default)] // if we add new fields, give them default values when deserializing old state
pub struct TemplateApp {
pub struct RealmApp {
// Example stuff:
pub label: String,
pub selected: bool,
pub selected_serverid: Option<String>,
pub selected_roomid: Option<String>,
#[serde(skip)]
pub current_user: Option<ClientUser>,
pub saved_username: Option<String>,
pub saved_token: Option<String>,
pub saved_auth_address: Option<String>,
#[serde(skip)]
pub value: f32,
#[serde(skip)]
@@ -40,6 +45,13 @@ pub struct TemplateApp {
#[serde(skip)]
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)]
pub login_start_channel: (Sender<Result<(), ErrorCode>>, Receiver<Result<(), ErrorCode>>),
#[serde(skip)]
@@ -47,9 +59,12 @@ pub struct TemplateApp {
#[serde(skip)]
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 {
Self {
// Example stuff:
@@ -58,6 +73,9 @@ impl Default for TemplateApp {
selected_serverid: None,
selected_roomid: None,
current_user: None,
saved_username: None,
saved_token: None,
saved_auth_address: None,
value: 2.7,
login_window_open: false,
@@ -71,12 +89,18 @@ impl Default for TemplateApp {
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),
added_server_channel: broadcast::channel(10),
}
}
}
impl TemplateApp {
impl RealmApp {
/// Called once before the first frame.
pub fn new(cc: &eframe::CreationContext<'_>) -> Self {
// This is also where you can customize the look and feel of egui using
@@ -92,33 +116,7 @@ impl TemplateApp {
}
}
impl eframe::App for TemplateApp {
/// 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) {
// Put your widgets into a `SidePanel`, `TopBottomPanel`, `CentralPanel`, `Window` or `Area`.
// For inspiration and more examples, go to https://emilk.github.io/egui
while let Ok(result) = self.login_start_channel.1.try_recv() {
match result {
Ok(_) => self.login_ready_for_code_input = true,
Err(e) => tracing::error!("Error in login/account creation flow: {:?}", e),
}
}
while let Ok(result) = self.login_ending_channel.1.try_recv() {
match result {
Ok(token) => {
info!("Login successful! Token: {token}");
self.login_ready_for_code_input = false;
self.login_window_open = false;
self.signup_window_open = false;
self.login_window_code.clear();
info!("Fetching user data...");
let send_channel = self.fetching_user_data_channel.0.clone();
let server_address = self.login_window_server_address.clone();
let username = self.login_window_username.clone();
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);
@@ -158,13 +156,60 @@ impl eframe::App for TemplateApp {
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.
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
// Put your widgets into a `SidePanel`, `TopBottomPanel`, `CentralPanel`, `Window` or `Area`.
// 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() {
match result {
Ok(_) => self.login_ready_for_code_input = true,
Err(e) => tracing::error!("Error in login/account creation flow: {:?}", e),
}
}
while let Ok(result) = self.login_ending_channel.1.try_recv() {
match result {
Ok(token) => {
info!("Login successful! Token: {token}");
self.login_ready_for_code_input = false;
self.login_window_open = false;
self.signup_window_open = false;
self.login_window_code.clear();
info!("Fetching user data...");
let send_channel = self.fetching_user_data_channel.0.clone();
let server_address = self.login_window_server_address.clone();
let username = self.login_window_username.clone();
self.saved_token = Some(token.clone());
self.saved_username = Some(username.clone());
self.saved_auth_address = Some(server_address.clone());
fetch_user_data(send_channel, server_address, username, token);
}
Err(e) => tracing::error!("Error in login flow: {:?}", e),
}
}
@@ -174,19 +219,37 @@ impl eframe::App for TemplateApp {
Ok(client_user) => {
info!("Got data! User: {:?}", 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
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.

View File

@@ -28,6 +28,6 @@ async fn main() -> eframe::Result {
eframe::run_native(
"Realm",
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 regex::Regex;
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::menu::bar(ui, |ui| {
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.saved_username = None;
app.saved_token = None;
app.saved_auth_address = None;
}
if ui.button("Quit").clicked() {
@@ -54,10 +57,66 @@ pub fn top_panel(app: &mut TemplateApp, ctx: &Context) {
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")
.open(&mut app.signup_window_open)
.min_size((500.0, 200.0))
@@ -201,47 +260,63 @@ pub fn top_panel(app: &mut TemplateApp, ctx: &Context) {
//ui.close_menu()
}
});
}
pub fn servers(app: &mut TemplateApp, ctx: &Context) {
egui::SidePanel::left("servers").show(ctx, |ui| {
ui.heading("Servers");
ui.separator();
if ui.add(SelectableLabel::new(app.selected, "server")).clicked() {
app.selected = !app.selected;
}
egui::Window::new("Add Server")
.open(&mut app.server_window_open)
.min_size((500.0, 200.0))
.show(ctx, |ui| {
ui.horizontal(|ui| {
ui.label("Domain: ");
ui.text_edit_singleline(&mut app.server_window_domain);
});
}
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() {
app.selected = !app.selected;
}
});
}
pub fn messages(app: &mut TemplateApp, 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.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();
}
});
ui.add(egui::Slider::new(&mut app.value, 0.0..=10.0).text("value"));
if ui.button("Increment").clicked() {
app.value += 1.0;
if app.current_user.is_some() && ui.button("Add Server").clicked() {
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();
let _handle = tokio::spawn(async move {
let mut transport = tarpc::serde_transport::tcp::connect(auth_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;
}
};
ui.separator();
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;
ui.label(format!("Current user: {:?}", app.current_user));
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(); },
};
});
}
});
}

View File

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