/* * asklyphe-auth-frontend login.rs * - login 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, LoginError, LoginRequest, LoginResponse, 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 axum::response::Redirect; use serde::Deserialize; use tokio::sync::Mutex; use tracing::error; use crate::{BUILT_ON, GIT_COMMIT, Opts, VERSION, YEAR}; #[derive(Template)] #[template(path = "login.html")] struct LoginTemplate { error: Option, success: bool, email: String, version: String, git_commit: String, built_on: String, year: String, } #[derive(Deserialize, Debug)] pub struct LoginForm { email: Option, password: Option, } pub async fn login_get() -> impl IntoResponse { LoginTemplate { error: None, success: false, email: "".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 login_post( Extension(nats): Extension>>, Extension(opts): Extension, Form(input): Form, ) -> impl IntoResponse { fn login_error(error: &str, email: String) -> LoginTemplate { LoginTemplate { error: Some(error.to_string()), success: false, email, version: VERSION.to_string(), git_commit: GIT_COMMIT.to_string(), built_on: BUILT_ON.to_string(), year: YEAR.to_string(), } } let email = input.email; let password = input.password; if email.is_none() || password.is_none() { return login_error( "one or more fields blank!", email.unwrap_or_default(), ).into_response(); } let email = email.unwrap(); let password = password.unwrap(); // todo: implement more frontend input checking if email.is_empty() || password.is_empty() { return login_error( "one or more fields blank!", email, ).into_response(); } let response = comms::query_service( comms::Query::AuthService(AuthServiceQuery { request: AuthServiceRequest::LoginRequest( LoginRequest { email: email.clone(), password, } ), 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 login_error( "internal server error! try again, or contact an administrator if the issue persists!", email, ).into_response(); } 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::LoginResponse(_) => {} x => { error!("auth service gave {} to our login request!", x); internal_server_error = true; } } } } if internal_server_error { return login_error( "internal server error! try again, or contact an administrator if the issue persists!", email, ).into_response(); } match response { ServiceResponse::AuthService(AuthServiceResponse::LoginResponse(r)) => match r { LoginResponse::Success(r) => { Redirect::to(&format!("{}/semaphore?one_time_token={}", opts.asklyphe_url, r.token)).into_response() } LoginResponse::Failure(r) => match r { LoginError::InternalServer(error) => { error!("internal server error during login attempt! {error}"); login_error( "internal server error! try again, or contact an administrator if the issue persists!", email, ).into_response() } LoginError::InvalidAccount => { login_error( "your email or password is incorrect! please try again, or click \"forgot password\" if you continue to have trouble!", email, ).into_response() } LoginError::SuspendedAccount => { login_error( "unfortunately your account has been suspended and you are no longer able to log in, for more help check our support pages or contact our support team!", email, ).into_response() } LoginError::AccountNotVerified => { login_error( "your account has not been verified yet! please check your email for a link from us to verify your account!", email, ).into_response() } } } _ => unreachable!() } }