/* * asklyphe-auth-frontend register.rs * - registration page routes * * Copyright (C) 2025 Real Microsoft, LLC * * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ use std::ops::Deref; use std::sync::Arc; use askama::Template; use askama_axum::IntoResponse; use asklyphe_common::nats::authservice::{AuthServiceQuery, AuthServiceRequest, AuthServiceResponse, EmailError, PasswordError, RegisterError, RegisterRequest, RegisterResponse, UsernameError}; use asklyphe_common::nats::comms; use asklyphe_common::nats::comms::ServiceResponse; use async_nats::jetstream; use axum::{Extension, Form}; use serde::Deserialize; use tokio::sync::Mutex; use tracing::error; use crate::{BUILT_ON, GIT_COMMIT, VERSION, YEAR}; #[derive(Template)] #[template(path = "register.html")] struct RegisterTemplate { error: Option, success: bool, username: String, email: String, invite_code: String, version: String, git_commit: String, built_on: String, year: String, } #[derive(Deserialize, Debug)] pub struct RegisterForm { username: Option, email: Option, password: Option, // todo: rename fields to adhere to snakecase verifypassword: Option, invitecode: Option, } pub async fn register_get() -> impl IntoResponse { RegisterTemplate { error: None, success: false, username: "".to_string(), email: "".to_string(), invite_code: "".to_string(), version: VERSION.to_string(), git_commit: GIT_COMMIT.to_string(), built_on: BUILT_ON.to_string(), year: YEAR.to_string(), } } pub async fn register_post( Extension(nats): Extension>>, Form(input): Form, ) -> impl IntoResponse { fn register_error(error: &str, username: String, email: String, invite_code: String) -> RegisterTemplate { RegisterTemplate { error: Some(error.to_string()), success: false, username, email, invite_code, version: VERSION.to_string(), git_commit: GIT_COMMIT.to_string(), built_on: BUILT_ON.to_string(), year: YEAR.to_string(), } } let username = input.username; let email = input.email; let password = input.password; let verify_password = input.verifypassword; let invite_code = input.invitecode; if username.is_none() || email.is_none() || password.is_none() || verify_password.is_none() || invite_code.is_none() { return register_error( "one or more fields blank!", username.unwrap_or_default(), email.unwrap_or_default(), invite_code.unwrap_or_default(), ); } let username = username.unwrap(); let email = email.unwrap(); let password = password.unwrap(); let verify_password = verify_password.unwrap(); let invite_code = invite_code.unwrap(); // todo: implement more frontend input checking if username.is_empty() || email.is_empty() || password.is_empty() || verify_password.is_empty() || invite_code.is_empty() { return register_error( "one or more fields blank!", username, email, invite_code, ); } if password != verify_password { return register_error( "passwords do not match!", username, email, invite_code, ); } let response = comms::query_service( comms::Query::AuthService(AuthServiceQuery { request: AuthServiceRequest::RegisterRequest( RegisterRequest { username: username.clone(), email: email.clone(), password, invite_code: invite_code.clone(), } ), replyto: "".to_string(), }), &*nats.lock().await, false, ).await; if let Err(e) = response { error!("internal server error while trying to communicate with auth service! {:?}", e); return register_error( "internal server error! try again, or contact an administrator if the issue persists!", username, email, invite_code, ); } let response = response.unwrap(); let mut internal_server_error = false; match &response { ServiceResponse::SearchService(_) => { error!("sent search service response when asking for auth service!! investigate ASAP!!!"); internal_server_error = true; } ServiceResponse::BingService(_) => { error!("sent bing service response when asking for auth service!! investigate ASAP!!!"); internal_server_error = true; } ServiceResponse::AuthService(r) => { match r { AuthServiceResponse::RegisterResponse(_) => {} x => { error!("auth service gave {} to our login request!", x); internal_server_error = true; } } } } if internal_server_error { return register_error( "internal server error! try again, or contact an administrator if the issue persists!", username, email, invite_code, ); } match response { ServiceResponse::AuthService(AuthServiceResponse::RegisterResponse(r)) => match r { RegisterResponse::Success => { RegisterTemplate { error: None, success: true, username: "".to_string(), email: "".to_string(), invite_code: "".to_string(), version: VERSION.to_string(), git_commit: GIT_COMMIT.to_string(), built_on: BUILT_ON.to_string(), year: YEAR.to_string(), } } RegisterResponse::Failure(e) => { match e { RegisterError::InternalServer(e) => { error!("auth service gave internal server error during our registration query!! {e}"); register_error( "internal server error! try again, or contact an administrator if the issue persists!", username, email, invite_code, ) } RegisterError::Password(e) => { match e { PasswordError::InvalidCharacters => { register_error( "your password contains invalid characters! please only use ascii characters in your password!", username, email, invite_code, ) } PasswordError::TooShort => { register_error( "your password is too short! please have a minimum of 14 characters in your password", username, email, invite_code, ) } PasswordError::TooCommon => { register_error( "your password is insecure! please avoid using commonly used passwords and try again", username, email, invite_code, ) } } } RegisterError::Email(e) => { match e { EmailError::InvalidEmail => { register_error( "the provided email address is invalid! please check your input and try again!", username, email, invite_code, ) } EmailError::EmailTaken => { register_error( "the provided email address is already in use!", username, email, invite_code, ) } EmailError::EmailBlacklisted => { register_error( "the provided email address is blocked! your email server may have been blacklisted for large amounts of spam, please try again with a different email!", username, email, invite_code, ) } } } RegisterError::BadInviteCode => { register_error( "the provided invite code is non-existent, malformed, expired, or already used!", username, email, invite_code, ) } RegisterError::Username(e) => { match e { UsernameError::InvalidCharacters => { register_error( "the provided username contains invalid characters! please only include dashes, underscores, and alphanumeric characters in your username!", username, email, invite_code, ) } UsernameError::TooShort => { register_error( "the provided username is too short! please make sure your username is at least 1 character long!", username, email, invite_code, ) } UsernameError::Taken => { register_error( "the provided username is already in use! if you have forgotten the password for your account, please visit the \"forgot password\" link on the login page!", username, email, invite_code, ) } UsernameError::Inappropriate => { register_error( "uh oh! we don't use that type of language on asklyphe.com! please choose a different username!", username, email, invite_code, ) } } } } } } _ => unreachable!() } }