Logging in and Signing up on the client done
Tons of bug fixes too
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -19,8 +19,8 @@ client/.gradle
|
|||||||
client/build
|
client/build
|
||||||
|
|
||||||
server/.idea
|
server/.idea
|
||||||
#server/.env
|
server/.env
|
||||||
#auth/.env
|
auth/.env
|
||||||
|
|
||||||
*.db
|
*.db
|
||||||
*.db-shm
|
*.db-shm
|
||||||
|
|||||||
12
auth/.env
12
auth/.env
@@ -1,12 +0,0 @@
|
|||||||
DATABASE_URL=sqlite:auth.db
|
|
||||||
PORT=5052
|
|
||||||
GOOGLE_CLIENT_ID=972646764847-j9o7v3th4bf1ln18hitgolqp03s6doo4.apps.googleusercontent.com
|
|
||||||
GOOGLE_PRIVATE_KEY=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
|
|
||||||
DOMAIN=realmchat.com
|
|
||||||
|
|
||||||
SERVER_MAIL_PORT=587
|
|
||||||
SERVER_MAIL_ADDRESS=mail.abunchofknowitalls.com
|
|
||||||
SERVER_MAIL_NAME="Realm Chat Support"
|
|
||||||
SERVER_MAIL_FROM_ADDRESS=realmchat@abunchofknowitalls.com
|
|
||||||
SERVER_MAIL_USERNAME=realmchat@abunchofknowitalls.com
|
|
||||||
SERVER_MAIL_PASSWORD=9BB*bD9RnSKMAf
|
|
||||||
@@ -17,6 +17,6 @@ sqlx = { version = "0.8.2", features = [ "runtime-tokio", "tls-rustls", "sqlite"
|
|||||||
sha3 = "0.10.8"
|
sha3 = "0.10.8"
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
mail-send = "0.4.9"
|
lettre = "0.11.9"
|
||||||
regex = "1.10.5"
|
regex = "1.10.5"
|
||||||
realm_shared = { path = "../shared" }
|
realm_shared = { path = "../shared" }
|
||||||
|
|||||||
12
auth/example.env
Normal file
12
auth/example.env
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
DATABASE_URL=sqlite:auth.db
|
||||||
|
PORT=5052
|
||||||
|
GOOGLE_CLIENT_ID=
|
||||||
|
GOOGLE_PRIVATE_KEY=
|
||||||
|
DOMAIN=
|
||||||
|
|
||||||
|
SERVER_MAIL_PORT=
|
||||||
|
SERVER_MAIL_ADDRESS=
|
||||||
|
SERVER_MAIL_NAME=
|
||||||
|
SERVER_MAIL_FROM_ADDRESS=
|
||||||
|
SERVER_MAIL_USERNAME=
|
||||||
|
SERVER_MAIL_PASSWORD=
|
||||||
@@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
<h2>Confirm your email address</h2>
|
<h2>Confirm your email address</h2>
|
||||||
<p>Your 6 digit code is below – enter it into Realm and you will be signed in</p>
|
<p>Your 6 digit code is below – enter it into Realm and you will be signed in</p>
|
||||||
<h3 style="font-size: 2em; margin: 2em 1em">{}</h3>
|
<h3 style="font-size: 2em; margin: 2em 1em">{$LOGIN_CODE}</h3>
|
||||||
<p>If you didn't request this email, there's nothing to worry about it– you can safely ignore it.</p>
|
<p>If you didn't request this email, there's nothing to worry about it– you can safely ignore it.</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<a href="https://realm.abunchofknowtiwalls.com"><strong>Realm</strong></a><br>
|
<a href="https://realm.abunchofknowtiwalls.com"><strong>Realm</strong></a><br>
|
||||||
Need help? Email <a href="mailto:realm-support@abunchofknowtiwalls.com">realm-support@abunchofknowtiwalls.com</a><br>
|
Need help? Email <a href="mailto:realm@abunchofknowtiwalls.com">realm@abunchofknowtiwalls.com</a><br>
|
||||||
© 2024 Realm, Inc.
|
© 2024 Realm, Inc.
|
||||||
</p>
|
</p>
|
||||||
@@ -7,9 +7,5 @@ CREATE TABLE IF NOT EXISTS user (
|
|||||||
avatar TEXT NOT NULL,
|
avatar TEXT NOT NULL,
|
||||||
servers TEXT NOT NULL,
|
servers TEXT NOT NULL,
|
||||||
login_code INT(6),
|
login_code INT(6),
|
||||||
tokens TEXT,
|
tokens TEXT
|
||||||
google_oauth VARCHAR(255),
|
|
||||||
apple_oauth VARCHAR(255),
|
|
||||||
github_oauth VARCHAR(255),
|
|
||||||
discord_oauth VARCHAR(255)
|
|
||||||
);
|
);
|
||||||
@@ -2,17 +2,17 @@ use std::env;
|
|||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use mail_send::{Credentials, SmtpClientBuilder};
|
use lettre::{Message, SmtpTransport, Transport};
|
||||||
use mail_send::mail_builder::MessageBuilder;
|
use lettre::message::header::ContentType;
|
||||||
|
use lettre::message::Mailbox;
|
||||||
|
use lettre::transport::smtp::authentication::Credentials;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use sha3::{Digest, Sha3_256};
|
use sha3::{Digest, Sha3_256};
|
||||||
use sha3::digest::Update;
|
use sha3::digest::Update;
|
||||||
use sqlx::{Pool, query, Sqlite};
|
use sqlx::{Pool, query, Sqlite};
|
||||||
use sqlx::sqlite::{SqliteQueryResult, SqliteRow};
|
|
||||||
use tarpc::context::Context;
|
use tarpc::context::Context;
|
||||||
use tracing::*;
|
use tracing::*;
|
||||||
use tracing::log::__private_api::log;
|
|
||||||
use crate::types::{AuthEmail, AuthUser, RealmAuth};
|
use crate::types::{AuthEmail, AuthUser, RealmAuth};
|
||||||
use realm_shared::types::ErrorCode;
|
use realm_shared::types::ErrorCode;
|
||||||
use realm_shared::types::ErrorCode::*;
|
use realm_shared::types::ErrorCode::*;
|
||||||
@@ -23,7 +23,6 @@ pub struct RealmAuthServer {
|
|||||||
pub db_pool: Pool<Sqlite>,
|
pub db_pool: Pool<Sqlite>,
|
||||||
pub auth_email: AuthEmail,
|
pub auth_email: AuthEmail,
|
||||||
pub template_html: String,
|
pub template_html: String,
|
||||||
pub template_txt: String,
|
|
||||||
pub domain: String,
|
pub domain: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,27 +33,21 @@ impl RealmAuthServer {
|
|||||||
db_pool,
|
db_pool,
|
||||||
auth_email,
|
auth_email,
|
||||||
template_html: std::fs::read_to_string("./login_email.html").expect("A login_email.html file is needed"),
|
template_html: std::fs::read_to_string("./login_email.html").expect("A login_email.html file is needed"),
|
||||||
template_txt: std::fs::read_to_string("./login_email.txt").expect("A login_email.txt file is needed"),
|
|
||||||
domain: env::var("DOMAIN").expect("DOMAIN must be set"),
|
domain: env::var("DOMAIN").expect("DOMAIN must be set"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_login_code(&self) -> u16 {
|
fn gen_login_code(&self) -> u32 {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let mut login_code: u16 = 0;
|
|
||||||
|
|
||||||
for n in 1..=6 {
|
let first_digit: u32 = rng.gen_range(1..10);
|
||||||
if n == 1 {
|
let remaining_digits: u32 = rng.gen_range(0..100_000);
|
||||||
login_code += rng.gen_range(1..=9);
|
|
||||||
}
|
|
||||||
login_code += rng.gen_range(0..=9) * (10^n);
|
|
||||||
}
|
|
||||||
|
|
||||||
login_code
|
first_digit * 100_000 + remaining_digits
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn is_username_taken(&self, username: &str) -> Result<bool, ErrorCode> {
|
async fn is_username_taken(&self, username: &str) -> Result<bool, ErrorCode> {
|
||||||
let result = query!("SELECT NOT EXISTS (SELECT 1 FROM user WHERE username = ?) AS does_exist", username).fetch_one(&self.db_pool).await;
|
let result = query!("SELECT EXISTS (SELECT 1 FROM user WHERE username = ?) AS does_exist", username).fetch_one(&self.db_pool).await;
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(row) => Ok(row.does_exist != 0),
|
Ok(row) => Ok(row.does_exist != 0),
|
||||||
@@ -63,7 +56,7 @@ impl RealmAuthServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn is_email_taken(&self, email: &str) -> Result<bool, ErrorCode> {
|
async fn is_email_taken(&self, email: &str) -> Result<bool, ErrorCode> {
|
||||||
let result = query!("SELECT NOT EXISTS (SELECT 1 FROM user WHERE email = ?) AS does_exist", email).fetch_one(&self.db_pool).await;
|
let result = query!("SELECT EXISTS (SELECT 1 FROM user WHERE email = ?) AS does_exist", email).fetch_one(&self.db_pool).await;
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(row) => Ok(row.does_exist != 0),
|
Ok(row) => Ok(row.does_exist != 0),
|
||||||
@@ -81,57 +74,54 @@ impl RealmAuthServer {
|
|||||||
|
|
||||||
for i in 0..tokens.len() {
|
for i in 0..tokens.len() {
|
||||||
if tokens.get(i).unwrap() == &token {
|
if tokens.get(i).unwrap() == &token {
|
||||||
return Ok(true)
|
return Ok(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(false)
|
Ok(false)
|
||||||
},
|
}
|
||||||
Err(_) => Err(InvalidUsername),
|
Err(_) => Err(InvalidUsername),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_login_message(&self, username: &str, email: &str, login_code: u16) -> Result<(), ErrorCode> {
|
fn send_login_message(&self, username: &str, email: &str, login_code: u32) { // -> Result<(), ErrorCode>
|
||||||
let message = MessageBuilder::new()
|
let auth_email = self.auth_email.clone();
|
||||||
.from((self.auth_email.auth_name.clone(), self.auth_email.auth_username.clone()))
|
let template_html = self.template_html.clone();
|
||||||
.to(vec![
|
let username = username.to_string();
|
||||||
(username, email),
|
let email = email.to_string();
|
||||||
])
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let email = Message::builder()
|
||||||
|
.from(Mailbox::new(Some(auth_email.auth_name.clone()), auth_email.auth_username.clone().parse().unwrap()))
|
||||||
|
.to(Mailbox::new(Some(username.clone()), email.clone().parse().unwrap()))
|
||||||
.subject(format!("Realm confirmation code: {}", &login_code))
|
.subject(format!("Realm confirmation code: {}", &login_code))
|
||||||
.html_body(self.template_html.replace("{}", &login_code.to_string()))
|
.header(ContentType::TEXT_HTML)
|
||||||
.text_body(self.template_txt.replace("{}", &login_code.to_string()));
|
.body(template_html.replace("{$LOGIN_CODE}", &login_code.to_string()))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let result = SmtpClientBuilder::new(&self.auth_email.server_address, self.auth_email.server_port)
|
let creds = Credentials::new(auth_email.auth_username, auth_email.auth_password);
|
||||||
.implicit_tls(false)
|
|
||||||
.credentials(Credentials::new(&self.auth_email.auth_username, &self.auth_email.auth_password))
|
|
||||||
.connect()
|
|
||||||
.await;
|
|
||||||
|
|
||||||
match result {
|
// Open a remote connection to gmail
|
||||||
Ok(mut client) => {
|
let mailer = SmtpTransport::relay(&auth_email.server_address)
|
||||||
let result = client.send(message).await;
|
.unwrap()
|
||||||
match result {
|
.credentials(creds)
|
||||||
Ok(_) => {
|
.build();
|
||||||
Ok(())
|
|
||||||
}
|
// Send the email
|
||||||
Err(_) => {
|
match mailer.send(&email) {
|
||||||
Err(UnableToSendMail)
|
Ok(_) => info!("Email sent successfully!"),
|
||||||
}
|
Err(e) => error!("Could not send email: {e:?}"),
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
Err(UnableToConnectToMail)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn is_login_code_valid(&self, username: &str, login_code: u16) -> Result<bool, ErrorCode> {
|
async fn is_login_code_valid(&self, username: &str, login_code: u32) -> Result<bool, ErrorCode> {
|
||||||
let result = query!("SELECT login_code FROM user WHERE username = ?;", username).fetch_one(&self.db_pool).await;
|
let result = query!("SELECT login_code FROM user WHERE username = ?;", username).fetch_one(&self.db_pool).await;
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(row) => {
|
Ok(row) => {
|
||||||
if row.login_code.unwrap() as u16 != login_code {
|
if row.login_code.unwrap() as u32 != login_code {
|
||||||
return Ok(false)
|
return Ok(false);
|
||||||
}
|
}
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
@@ -141,7 +131,7 @@ impl RealmAuthServer {
|
|||||||
|
|
||||||
fn is_username_valid(&self, username: &str) -> bool {
|
fn is_username_valid(&self, username: &str) -> bool {
|
||||||
if !username.starts_with('@') || !username.contains(':') {
|
if !username.starts_with('@') || !username.contains(':') {
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = &username[1..username.find(':').unwrap()];
|
let name = &username[1..username.find(':').unwrap()];
|
||||||
@@ -149,11 +139,11 @@ impl RealmAuthServer {
|
|||||||
|
|
||||||
let re = Regex::new(r"^[a-zA-Z0-9]+$").unwrap();
|
let re = Regex::new(r"^[a-zA-Z0-9]+$").unwrap();
|
||||||
if !re.is_match(name) {
|
if !re.is_match(name) {
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !domain.eq(&self.domain) {
|
if !domain.eq(&self.domain) {
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
true
|
||||||
@@ -188,12 +178,12 @@ 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();
|
let hash = Sha3_256::new().chain(format!("{}{}{}{}", token, server_id, domain, tarpc_port)).finalize();
|
||||||
if hex::encode(hash) == server_token {
|
if hex::encode(hash) == server_token {
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
false
|
||||||
},
|
}
|
||||||
Err(_) => false,
|
Err(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -202,26 +192,29 @@ impl RealmAuth for RealmAuthServer {
|
|||||||
info!("API Request: create_account_flow( username -> {}, email -> {} )", username, email);
|
info!("API Request: create_account_flow( username -> {}, email -> {} )", username, email);
|
||||||
|
|
||||||
if !self.is_username_valid(&username) {
|
if !self.is_username_valid(&username) {
|
||||||
return Err(InvalidUsername)
|
return Err(InvalidUsername);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.is_username_taken(&username).await? {
|
if self.is_username_taken(&username).await? {
|
||||||
return Err(UsernameTaken)
|
return Err(UsernameTaken);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.is_email_taken(&email).await? {
|
if self.is_email_taken(&email).await? {
|
||||||
return Err(EmailTaken)
|
return Err(EmailTaken);
|
||||||
}
|
}
|
||||||
|
|
||||||
let code = self.gen_login_code();
|
let code = self.gen_login_code();
|
||||||
self.send_login_message(&username, &email, code).await?;
|
self.send_login_message(&username, &email, code);
|
||||||
|
|
||||||
let result = query!("INSERT INTO user (username, email, avatar, login_code, tokens) VALUES (?, ?, '', ?, '')", username, email, code)
|
let result = query!("INSERT INTO user (username, email, new_email, avatar, servers, login_code, tokens) VALUES (?, ?, '', '', '', ?, '')", username, email, code)
|
||||||
.execute(&self.db_pool).await;
|
.execute(&self.db_pool).await;
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(()),
|
||||||
Err(_) => Err(Error)
|
Err(e) => {
|
||||||
|
error!("Error creating account: {e:?}");
|
||||||
|
Err(Error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,7 +222,7 @@ impl RealmAuth for RealmAuthServer {
|
|||||||
info!("API Request: create_login_flow( username -> {}, email -> {} )", username.clone().unwrap_or("None".to_string()), email.clone().unwrap_or("None".to_string()));
|
info!("API Request: create_login_flow( username -> {}, email -> {} )", username.clone().unwrap_or("None".to_string()), email.clone().unwrap_or("None".to_string()));
|
||||||
|
|
||||||
if username.is_none() && email.is_none() {
|
if username.is_none() && email.is_none() {
|
||||||
return Err(Error)
|
return Err(Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
if username.is_none() {
|
if username.is_none() {
|
||||||
@@ -264,17 +257,20 @@ impl RealmAuth for RealmAuthServer {
|
|||||||
.execute(&self.db_pool).await;
|
.execute(&self.db_pool).await;
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => self.send_login_message(&username.unwrap(), &email.unwrap(), code).await,
|
Ok(_) => {
|
||||||
|
self.send_login_message(&username.unwrap(), &email.unwrap(), code);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Err(_) => Err(InvalidUsername)
|
Err(_) => Err(InvalidUsername)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn finish_login_flow(self, _: Context, username: String, login_code: u16) -> Result<String, ErrorCode> {
|
async fn finish_login_flow(self, _: Context, username: String, login_code: u32) -> Result<String, ErrorCode> {
|
||||||
info!("API Request: finish_login_flow( username -> {}, login_code -> {} )", username, login_code);
|
info!("API Request: finish_login_flow( username -> {}, login_code -> {} )", username, login_code);
|
||||||
|
|
||||||
if !self.is_login_code_valid(&username, login_code).await? {
|
if !self.is_login_code_valid(&username, login_code).await? {
|
||||||
error!("Unauthorized request made for finish_login_flow() (bad login code)! username -> {}, login_code -> {}", username, login_code);
|
error!("Unauthorized request made for finish_login_flow() (bad login code)! username -> {}, login_code -> {}", username, login_code);
|
||||||
return Err(InvalidLoginCode)
|
return Err(InvalidLoginCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.reset_login_code(&username).await?;
|
self.reset_login_code(&username).await?;
|
||||||
@@ -307,11 +303,11 @@ impl RealmAuth for RealmAuthServer {
|
|||||||
info!("API Request: change_email_flow( username -> {}, new_email -> {}, token -> {} )", username, new_email, token);
|
info!("API Request: change_email_flow( username -> {}, new_email -> {}, token -> {} )", username, new_email, token);
|
||||||
|
|
||||||
if !self.is_authorized(&username, &token).await? {
|
if !self.is_authorized(&username, &token).await? {
|
||||||
return Err(Unauthorized)
|
return Err(Unauthorized);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.is_email_taken(&new_email).await? {
|
if self.is_email_taken(&new_email).await? {
|
||||||
return Err(EmailTaken)
|
return Err(EmailTaken);
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = query!("UPDATE user SET new_email = ? WHERE username = ?", new_email, username).execute(&self.db_pool).await.unwrap();
|
let _ = query!("UPDATE user SET new_email = ? WHERE username = ?", new_email, username).execute(&self.db_pool).await.unwrap();
|
||||||
@@ -322,27 +318,30 @@ impl RealmAuth for RealmAuthServer {
|
|||||||
.execute(&self.db_pool).await;
|
.execute(&self.db_pool).await;
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => self.send_login_message(&username, &new_email, code).await,
|
Ok(_) => {
|
||||||
|
self.send_login_message(&username, &new_email, code);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Err(_) => Err(InvalidUsername)
|
Err(_) => Err(InvalidUsername)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn finish_change_email_flow(self, _: Context, username: String, new_email: String, token: String, login_code: u16) -> Result<(), ErrorCode> {
|
async fn finish_change_email_flow(self, _: Context, username: String, new_email: String, token: String, login_code: u32) -> Result<(), ErrorCode> {
|
||||||
info!("API Request: finish_change_email_flow( username -> {}, new_email -> {}, token -> {}, login_code -> {} )", username, new_email, token, login_code);
|
info!("API Request: finish_change_email_flow( username -> {}, new_email -> {}, token -> {}, login_code -> {} )", username, new_email, token, login_code);
|
||||||
|
|
||||||
if !self.is_authorized(&username, &token).await? {
|
if !self.is_authorized(&username, &token).await? {
|
||||||
error!("Unauthorized request made for finish_change_email_flow() (bad token)! username -> {}, token -> {}", username, token);
|
error!("Unauthorized request made for finish_change_email_flow() (bad token)! username -> {}, token -> {}", username, token);
|
||||||
return Err(Unauthorized)
|
return Err(Unauthorized);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.is_email_taken(&new_email).await? {
|
if self.is_email_taken(&new_email).await? {
|
||||||
error!("Email already taken for email change (but its the end of the flow?!) username -> {}, new_email -> {}", username, new_email);
|
error!("Email already taken for email change (but its the end of the flow?!) username -> {}, new_email -> {}", username, new_email);
|
||||||
return Err(EmailTaken)
|
return Err(EmailTaken);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.is_login_code_valid(&username, login_code).await? {
|
if !self.is_login_code_valid(&username, login_code).await? {
|
||||||
error!("Unauthorized request made for finish_change_email_flow() (bad login code)! username -> {}, login_code -> {}", username, login_code);
|
error!("Unauthorized request made for finish_change_email_flow() (bad login code)! username -> {}, login_code -> {}", username, login_code);
|
||||||
return Err(InvalidLoginCode)
|
return Err(InvalidLoginCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = query!("UPDATE user SET new_email = NULL WHERE username = ?", username).execute(&self.db_pool).await;
|
let _ = query!("UPDATE user SET new_email = NULL WHERE username = ?", username).execute(&self.db_pool).await;
|
||||||
@@ -354,37 +353,12 @@ impl RealmAuth for RealmAuthServer {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// async fn change_username(self, _: Context, username: String, token: String, new_username: String) -> Result<(), ErrorCode> {
|
|
||||||
// info!("API Request: change_username( username -> {}, token -> {}, new_username -> {} )", username, token, new_username);
|
|
||||||
//
|
|
||||||
// if !self.is_authorized(&username, &token).await? {
|
|
||||||
// error!("Unauthorized request made for change_username()! username -> {}, token -> {}", username, token);
|
|
||||||
// return Err(Unauthorized)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if !self.is_username_valid(&new_username) {
|
|
||||||
// error!("Malformed username in request for change_username()! new_username -> {}", new_username);
|
|
||||||
// return Err(InvalidUsername)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if self.is_username_taken(&new_username).await? {
|
|
||||||
// error!("Username is taken for change_username()! new_username -> {}", new_username);
|
|
||||||
// return Err(UsernameTaken)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// let result = query!("UPDATE user SET username = ? WHERE username = ?", new_username, username).execute(&self.db_pool).await;
|
|
||||||
// match result {
|
|
||||||
// Ok(_) => Ok(()),
|
|
||||||
// Err(_) => Err(Error)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
async fn change_avatar(self, _: Context, username: String, token: String, new_avatar: String) -> Result<(), ErrorCode> {
|
async fn change_avatar(self, _: Context, username: String, token: String, new_avatar: String) -> Result<(), ErrorCode> {
|
||||||
info!("API Request: change_avatar( username -> {}, token -> {}, new_avatar -> {} )", username, token, new_avatar);
|
info!("API Request: change_avatar( username -> {}, token -> {}, new_avatar -> {} )", username, token, new_avatar);
|
||||||
|
|
||||||
if !self.is_authorized(&username, &token).await? {
|
if !self.is_authorized(&username, &token).await? {
|
||||||
error!("Unauthorized request made for change_avatar()! username -> {}, token -> {}", username, token);
|
error!("Unauthorized request made for change_avatar()! username -> {}, token -> {}", username, token);
|
||||||
return Err(Unauthorized)
|
return Err(Unauthorized);
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = query!("UPDATE user SET avatar = ? WHERE username = ?", new_avatar, username).execute(&self.db_pool).await;
|
let result = query!("UPDATE user SET avatar = ? WHERE username = ?", new_avatar, username).execute(&self.db_pool).await;
|
||||||
@@ -399,7 +373,7 @@ impl RealmAuth for RealmAuthServer {
|
|||||||
|
|
||||||
if !self.is_authorized(&username, &token).await? {
|
if !self.is_authorized(&username, &token).await? {
|
||||||
error!("Unauthorized request made for get_all_data()! username -> {}, token -> {}", username, token);
|
error!("Unauthorized request made for get_all_data()! username -> {}, token -> {}", username, token);
|
||||||
return Err(Unauthorized)
|
return Err(Unauthorized);
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = query!(r"SELECT * FROM user WHERE username = ?", username).fetch_one(&self.db_pool).await;
|
let result = query!(r"SELECT * FROM user WHERE username = ?", username).fetch_one(&self.db_pool).await;
|
||||||
@@ -413,10 +387,6 @@ impl RealmAuth for RealmAuthServer {
|
|||||||
servers: row.servers,
|
servers: row.servers,
|
||||||
login_code: None,
|
login_code: None,
|
||||||
bigtoken: row.tokens,
|
bigtoken: row.tokens,
|
||||||
google_oauth: row.google_oauth,
|
|
||||||
apple_oauth: row.apple_oauth,
|
|
||||||
github_oauth: row.github_oauth,
|
|
||||||
discord_oauth: row.discord_oauth,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
@@ -458,11 +428,11 @@ impl RealmAuth for RealmAuthServer {
|
|||||||
|
|
||||||
error!("Unauthorized request made for sign_out()! username -> {}, token -> {}", username, token);
|
error!("Unauthorized request made for sign_out()! username -> {}, token -> {}", username, token);
|
||||||
Err(Unauthorized)
|
Err(Unauthorized)
|
||||||
},
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
error!("Invalid username in request for get_avatar_for_user()! username -> {}", username);
|
error!("Invalid username in request for get_avatar_for_user()! username -> {}", username);
|
||||||
Err(InvalidUsername)
|
Err(InvalidUsername)
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -470,7 +440,7 @@ impl RealmAuth for RealmAuthServer {
|
|||||||
info!("API Request: delete_account_flow( username -> {}, token -> {} )", username, token);
|
info!("API Request: delete_account_flow( username -> {}, token -> {} )", username, token);
|
||||||
|
|
||||||
if !self.is_authorized(&username, &token).await? {
|
if !self.is_authorized(&username, &token).await? {
|
||||||
return Err(Unauthorized)
|
return Err(Unauthorized);
|
||||||
}
|
}
|
||||||
|
|
||||||
let email = match query!("SELECT email FROM user WHERE username = ?;", username).fetch_one(&self.db_pool).await {
|
let email = match query!("SELECT email FROM user WHERE username = ?;", username).fetch_one(&self.db_pool).await {
|
||||||
@@ -484,20 +454,23 @@ impl RealmAuth for RealmAuthServer {
|
|||||||
.execute(&self.db_pool).await;
|
.execute(&self.db_pool).await;
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => self.send_login_message(&username, &email, code).await,
|
Ok(_) => {
|
||||||
|
self.send_login_message(&username, &email, code);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Err(_) => Err(InvalidUsername)
|
Err(_) => Err(InvalidUsername)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn finish_delete_account_flow(self, _: Context, username: String, token: String, login_code: u16) -> Result<(), ErrorCode> {
|
async fn finish_delete_account_flow(self, _: Context, username: String, token: String, login_code: u32) -> Result<(), ErrorCode> {
|
||||||
info!("API Request: finish_delete_account_flow( username -> {}, token -> {}, login_code -> {} )", username, token, login_code);
|
info!("API Request: finish_delete_account_flow( username -> {}, token -> {}, login_code -> {} )", username, token, login_code);
|
||||||
|
|
||||||
if !self.is_authorized(&username, &token).await? {
|
if !self.is_authorized(&username, &token).await? {
|
||||||
return Err(Unauthorized)
|
return Err(Unauthorized);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.is_login_code_valid(&username, login_code).await? {
|
if !self.is_login_code_valid(&username, login_code).await? {
|
||||||
return Err(InvalidLoginCode)
|
return Err(InvalidLoginCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.reset_login_code(&username).await?;
|
self.reset_login_code(&username).await?;
|
||||||
@@ -511,7 +484,7 @@ 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) -> Result<(), ErrorCode> {
|
||||||
if !self.is_authorized(&username, &token).await? {
|
if !self.is_authorized(&username, &token).await? {
|
||||||
return Err(Unauthorized)
|
return Err(Unauthorized);
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
@@ -520,7 +493,7 @@ impl RealmAuth for RealmAuthServer {
|
|||||||
let mut vec_servers = row.servers.split('|').collect::<Vec<&str>>();
|
let mut vec_servers = row.servers.split('|').collect::<Vec<&str>>();
|
||||||
for server in &vec_servers {
|
for server in &vec_servers {
|
||||||
if server.eq(&domain) {
|
if server.eq(&domain) {
|
||||||
return Err(AlreadyJoinedServer)
|
return Err(AlreadyJoinedServer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -539,7 +512,7 @@ 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) -> Result<(), ErrorCode> {
|
||||||
if !self.is_authorized(&username, &token).await? {
|
if !self.is_authorized(&username, &token).await? {
|
||||||
return Err(Unauthorized)
|
return Err(Unauthorized);
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
@@ -555,7 +528,7 @@ impl RealmAuth for RealmAuthServer {
|
|||||||
return match result {
|
return match result {
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(()),
|
||||||
Err(_) => Err(Error)
|
Err(_) => Err(Error)
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -567,7 +540,7 @@ impl RealmAuth for RealmAuthServer {
|
|||||||
|
|
||||||
async fn get_joined_servers(self, _: Context, username: String, token: String) -> Result<Vec<String>, ErrorCode> {
|
async fn get_joined_servers(self, _: Context, username: String, token: String) -> Result<Vec<String>, ErrorCode> {
|
||||||
if !self.is_authorized(&username, &token).await? {
|
if !self.is_authorized(&username, &token).await? {
|
||||||
return Err(Unauthorized)
|
return Err(Unauthorized);
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
|||||||
@@ -7,17 +7,17 @@ pub trait RealmAuth {
|
|||||||
async fn server_token_validation(server_token: String, username: String, server_id: String, domain: String, tarpc_port: u16) -> bool;
|
async fn server_token_validation(server_token: String, username: String, server_id: String, domain: String, tarpc_port: u16) -> bool;
|
||||||
async fn create_account_flow(username: String, email: String) -> Result<(), ErrorCode>; //NOTE: Still require sign in flow
|
async fn create_account_flow(username: String, email: String) -> Result<(), ErrorCode>; //NOTE: Still require sign in flow
|
||||||
async fn create_login_flow(username: Option<String>, email: Option<String>) -> Result<(), ErrorCode>;
|
async fn create_login_flow(username: Option<String>, email: Option<String>) -> Result<(), ErrorCode>;
|
||||||
async fn finish_login_flow(username: String, login_code: u16) -> Result<String, ErrorCode>;
|
async fn finish_login_flow(username: String, login_code: u32) -> Result<String, ErrorCode>;
|
||||||
|
|
||||||
//NOTE: Need to be the user
|
//NOTE: Need to be the user
|
||||||
async fn change_email_flow(username: String, new_email: String, token: String) -> Result<(), ErrorCode>;
|
async fn change_email_flow(username: String, new_email: String, token: String) -> Result<(), ErrorCode>;
|
||||||
async fn finish_change_email_flow(username: String, new_email: String, token: String, login_code: u16) -> Result<(), ErrorCode>;
|
async fn finish_change_email_flow(username: String, new_email: String, token: String, login_code: u32) -> Result<(), ErrorCode>;
|
||||||
// async fn change_username(username: String, token: String, new_username: String) -> Result<(), ErrorCode>;
|
// async fn change_username(username: String, token: String, new_username: String) -> Result<(), ErrorCode>;
|
||||||
async fn change_avatar(username: String, token: String, new_avatar: String) -> Result<(), ErrorCode>;
|
async fn change_avatar(username: String, token: String, new_avatar: String) -> Result<(), ErrorCode>;
|
||||||
async fn get_all_data(username: String, token: String) -> Result<AuthUser, ErrorCode>;
|
async fn get_all_data(username: String, token: String) -> Result<AuthUser, ErrorCode>;
|
||||||
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: u16) -> 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) -> Result<(), ErrorCode>;
|
||||||
async fn remove_server(username: String, token: String, domain: String) -> Result<(), ErrorCode>;
|
async fn remove_server(username: String, token: String, domain: String) -> 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>;
|
||||||
@@ -34,12 +34,8 @@ pub struct AuthUser {
|
|||||||
pub email: String,
|
pub email: String,
|
||||||
pub avatar: String,
|
pub avatar: String,
|
||||||
pub servers: String,
|
pub servers: String,
|
||||||
pub login_code: Option<u16>,
|
pub login_code: Option<u32>,
|
||||||
pub bigtoken: Option<String>,
|
pub bigtoken: Option<String>,
|
||||||
pub google_oauth: Option<String>,
|
|
||||||
pub apple_oauth: Option<String>,
|
|
||||||
pub github_oauth: Option<String>,
|
|
||||||
pub discord_oauth: Option<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
|||||||
@@ -15,3 +15,9 @@ eframe = { version = "0.29", default-features = false, features = [
|
|||||||
] }
|
] }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
env_logger = "0.11.5"
|
env_logger = "0.11.5"
|
||||||
|
tokio = "1.40.0"
|
||||||
|
tarpc = { version = "0.34.0", features = ["full"] }
|
||||||
|
tracing = "0.1.40"
|
||||||
|
tracing-subscriber = "0.3.18"
|
||||||
|
regex = "1.10.6"
|
||||||
|
native-dialog = "0.7.0"
|
||||||
@@ -1,3 +1,8 @@
|
|||||||
|
use tokio::sync::broadcast;
|
||||||
|
use tokio::sync::broadcast::{Receiver, Sender};
|
||||||
|
use tracing::log::info;
|
||||||
|
use realm_shared::types::ErrorCode;
|
||||||
|
use crate::types::ClientUser;
|
||||||
use crate::ui::panels;
|
use crate::ui::panels;
|
||||||
|
|
||||||
/// We derive Deserialize/Serialize so we can persist app state on shutdown.
|
/// We derive Deserialize/Serialize so we can persist app state on shutdown.
|
||||||
@@ -10,8 +15,31 @@ pub struct TemplateApp {
|
|||||||
pub selected_serverid: Option<String>,
|
pub selected_serverid: Option<String>,
|
||||||
pub selected_roomid: Option<String>,
|
pub selected_roomid: Option<String>,
|
||||||
|
|
||||||
#[serde(skip)] // This how you opt-out of serialization of a field
|
pub current_user: Option<ClientUser>,
|
||||||
|
|
||||||
|
#[serde(skip)]
|
||||||
pub value: f32,
|
pub value: f32,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub login_window_open: bool,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub login_window_username: String,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub login_window_code: String,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub login_window_server_address: String,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub login_window_email: String,
|
||||||
|
|
||||||
|
#[serde(skip)]
|
||||||
|
pub login_ready_for_code_input: bool,
|
||||||
|
|
||||||
|
#[serde(skip)]
|
||||||
|
pub signup_window_open: bool,
|
||||||
|
|
||||||
|
#[serde(skip)]
|
||||||
|
pub login_start_channel: (Sender<Result<(), ErrorCode>>, Receiver<Result<(), ErrorCode>>),
|
||||||
|
#[serde(skip)]
|
||||||
|
pub login_ending_channel: (Sender<Result<String, ErrorCode>>, Receiver<Result<String, ErrorCode>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TemplateApp {
|
impl Default for TemplateApp {
|
||||||
@@ -22,7 +50,19 @@ impl Default for TemplateApp {
|
|||||||
selected: false,
|
selected: false,
|
||||||
selected_serverid: None,
|
selected_serverid: None,
|
||||||
selected_roomid: None,
|
selected_roomid: None,
|
||||||
|
current_user: None,
|
||||||
value: 2.7,
|
value: 2.7,
|
||||||
|
|
||||||
|
login_window_open: false,
|
||||||
|
login_window_username: String::new(),
|
||||||
|
login_window_code: String::new(),
|
||||||
|
login_window_server_address: String::new(),
|
||||||
|
login_start_channel: broadcast::channel(10),
|
||||||
|
login_ending_channel: broadcast::channel(10),
|
||||||
|
login_ready_for_code_input: false,
|
||||||
|
login_window_email: String::new(),
|
||||||
|
|
||||||
|
signup_window_open: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -49,8 +89,26 @@ impl eframe::App for TemplateApp {
|
|||||||
// 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
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
},
|
||||||
|
Err(e) => tracing::error!("Error in login flow: {:?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// File -> Quit
|
// File -> Quit
|
||||||
panels::top_panel(ctx);
|
panels::top_panel(self, ctx);
|
||||||
|
|
||||||
panels::servers(self, ctx);
|
panels::servers(self, ctx);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
fn main() -> eframe::Result {
|
#[tokio::main]
|
||||||
|
async fn main() -> eframe::Result {
|
||||||
env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
|
env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
|
||||||
|
|
||||||
let native_options = eframe::NativeOptions {
|
let native_options = eframe::NativeOptions {
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#[derive(serde::Deserialize, serde::Serialize)]
|
||||||
|
pub struct ClientUser {
|
||||||
|
pub id: i64,
|
||||||
|
pub username: String,
|
||||||
|
pub email: String,
|
||||||
|
pub avatar: String,
|
||||||
|
pub servers: Vec<String>,
|
||||||
|
pub token: String,
|
||||||
|
}
|
||||||
@@ -1,25 +1,180 @@
|
|||||||
use egui::{Context, SelectableLabel};
|
use egui::{Context, SelectableLabel};
|
||||||
|
use tarpc::context;
|
||||||
|
use tarpc::tokio_serde::formats::Json;
|
||||||
|
use realm_auth::types::RealmAuthClient;
|
||||||
|
use realm_shared::types::ErrorCode::RPCError;
|
||||||
|
use regex::Regex;
|
||||||
use crate::app::TemplateApp;
|
use crate::app::TemplateApp;
|
||||||
|
|
||||||
pub fn top_panel(ctx: &Context) {
|
pub fn top_panel(app: &mut TemplateApp, ctx: &Context) {
|
||||||
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
|
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
|
||||||
// The top panel is often a good place for a menu bar:
|
|
||||||
|
|
||||||
egui::menu::bar(ui, |ui| {
|
egui::menu::bar(ui, |ui| {
|
||||||
// NOTE: no File->Quit on web pages!
|
|
||||||
let is_web = cfg!(target_arch = "wasm32");
|
|
||||||
if !is_web {
|
|
||||||
ui.menu_button("File", |ui| {
|
ui.menu_button("File", |ui| {
|
||||||
|
if ui.button("Sign Up").clicked() {
|
||||||
|
app.signup_window_open = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ui.button("Login").clicked() {
|
||||||
|
app.login_window_open = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ui.button("Logout").clicked() {
|
||||||
|
// TODO: Logout
|
||||||
|
}
|
||||||
|
|
||||||
if ui.button("Quit").clicked() {
|
if ui.button("Quit").clicked() {
|
||||||
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
|
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ui.add_space(16.0);
|
ui.add_space(16.0);
|
||||||
}
|
|
||||||
|
|
||||||
egui::widgets::global_dark_light_mode_buttons(ui);
|
egui::widgets::global_dark_light_mode_buttons(ui);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
egui::Window::new("Signup")
|
||||||
|
.open(&mut app.signup_window_open)
|
||||||
|
.min_size((500.0, 200.0))
|
||||||
|
.show(ctx, |ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Server Address: ");
|
||||||
|
ui.text_edit_singleline(&mut app.login_window_server_address);
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Username: ");
|
||||||
|
ui.text_edit_singleline(&mut app.login_window_username);
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Email: ");
|
||||||
|
ui.text_edit_singleline(&mut app.login_window_email);
|
||||||
|
});
|
||||||
|
|
||||||
|
if ui.button("Create Account").clicked() {
|
||||||
|
let login_window_server_address = app.login_window_server_address.clone();
|
||||||
|
let login_window_username = app.login_window_username.clone();
|
||||||
|
let login_window_email = app.login_window_email.clone();
|
||||||
|
let send_channel = app.login_start_channel.0.clone();
|
||||||
|
|
||||||
|
let _handle = tokio::spawn(async move {
|
||||||
|
let mut transport = tarpc::serde_transport::tcp::connect(login_window_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.create_account_flow(context::current(), login_window_username, login_window_email).await;
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(r) => send_channel.send(r).unwrap(),
|
||||||
|
Err(_) => send_channel.send(Err(RPCError)).unwrap(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.close_menu()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
egui::Window::new("Login")
|
||||||
|
.open(&mut app.login_window_open)
|
||||||
|
.min_size((500.0, 200.0))
|
||||||
|
.show(ctx, |ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Server Address: ");
|
||||||
|
ui.text_edit_singleline(&mut app.login_window_server_address);
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Username: ");
|
||||||
|
ui.text_edit_singleline(&mut app.login_window_username);
|
||||||
|
});
|
||||||
|
|
||||||
|
if ui.button("Send Login Code").clicked() {
|
||||||
|
let login_window_server_address = app.login_window_server_address.clone();
|
||||||
|
let login_window_username = app.login_window_username.clone();
|
||||||
|
let send_channel = app.login_start_channel.0.clone();
|
||||||
|
|
||||||
|
let _handle = tokio::spawn(async move {
|
||||||
|
let mut transport = tarpc::serde_transport::tcp::connect(login_window_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.create_login_flow(context::current(), Some(login_window_username), None).await;
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(r) => send_channel.send(r).unwrap(),
|
||||||
|
Err(_) => send_channel.send(Err(RPCError)).unwrap(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.close_menu()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
egui::Window::new("Auth Code")
|
||||||
|
.open(&mut app.login_ready_for_code_input)
|
||||||
|
.min_size((500.0, 200.0))
|
||||||
|
.show(ctx, |ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Code: ");
|
||||||
|
if ui.text_edit_singleline(&mut app.login_window_code).changed() {
|
||||||
|
let re = Regex::new(r"[^0-9]+").unwrap();
|
||||||
|
app.login_window_code = re.replace_all(&app.login_window_code, "").to_string();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if ui.button("Login").clicked() {
|
||||||
|
let login_window_server_address = app.login_window_server_address.clone();
|
||||||
|
let login_window_code = app.login_window_code.clone();
|
||||||
|
let login_window_username = app.login_window_username.clone();
|
||||||
|
let send_channel = app.login_ending_channel.0.clone();
|
||||||
|
|
||||||
|
let _handle = tokio::spawn(async move {
|
||||||
|
let mut transport = tarpc::serde_transport::tcp::connect(login_window_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.finish_login_flow(context::current(), login_window_username, login_window_code.parse::<u32>().unwrap()).await;
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(r) => {
|
||||||
|
send_channel.send(r).unwrap();
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
send_channel.send(Err(RPCError)).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.close_menu()
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn servers(app: &mut TemplateApp, ctx: &Context) {
|
pub fn servers(app: &mut TemplateApp, ctx: &Context) {
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
Confirm your email address
|
|
||||||
Your 6 digit code is below-- enter it into Realm and you will be signed in
|
|
||||||
|
|
||||||
{}
|
|
||||||
|
|
||||||
If you didn't request this email, there's nothing to worry about it-- you can safely ignore it.
|
|
||||||
|
|
||||||
Realm
|
|
||||||
Need help? Email realm-support@abunchofknowtiwalls.com
|
|
||||||
© 2024 Realm, Inc.
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
DATABASE_URL=sqlite:server.db
|
|
||||||
DOMAIN=server.realmchat.com
|
|
||||||
SERVER_ID=tester
|
|
||||||
PORT=5051
|
|
||||||
4
server/example.env
Normal file
4
server/example.env
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
DATABASE_URL=sqlite:server.db
|
||||||
|
DOMAIN=
|
||||||
|
SERVER_ID=
|
||||||
|
PORT=5051
|
||||||
@@ -127,7 +127,7 @@ impl RealmChatServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn is_user_in_server(&self, userid: &str) -> bool {
|
async fn is_user_in_server(&self, userid: &str) -> bool {
|
||||||
let result = query!("SELECT NOT EXISTS (SELECT 1 FROM user WHERE userid = ?) AS does_exist", userid).fetch_one(&self.db_pool).await;
|
let result = query!("SELECT EXISTS (SELECT 1 FROM user WHERE userid = ?) AS does_exist", userid).fetch_one(&self.db_pool).await;
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(record) => record.does_exist != 0,
|
Ok(record) => record.does_exist != 0,
|
||||||
|
|||||||
@@ -21,4 +21,6 @@ pub enum ErrorCode {
|
|||||||
UserNotFound,
|
UserNotFound,
|
||||||
DepthTooLarge,
|
DepthTooLarge,
|
||||||
MalformedDBResponse,
|
MalformedDBResponse,
|
||||||
|
|
||||||
|
RPCError,
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user