use std::sync::Arc; use urlencoding::{encode,decode}; use axum::{ http::{ StatusCode, Uri, header::HeaderMap, }, Json, routing::{get,post}, Extension, extract::ConnectInfo, response::{IntoResponse,Response,Redirect}, Router, }; use tower_cookies::Cookies; use crate::{ route, defs::{ AppDBs, AuthState, ReqHandler, ReqHeaderMap, MailMessage, Random, AppConnectInfo, }, users::{ User, UserStatus, UserInvitation, }, handlers::{ add_session_cookie, get_auth_state, }, }; pub fn users_invite_router_handlers() -> Router { async fn invite_signup_handler( header: HeaderMap, uri: Uri, Extension(app_dbs): Extension>, Extension(cookies): Extension, Extension(random): Extension, ConnectInfo(app_connect_info): ConnectInfo, axum::extract::Path(data): axum::extract::Path, // auth_state: AuthState, ) -> Response { let auth_state = get_auth_state(true, &cookies, &app_dbs).await; let mut req_handler = ReqHandler::new( ReqHeaderMap::new(header, &format!("{}",&uri.path().to_string()), &app_connect_info), &app_dbs, &uri, &auth_state, &random, "invite_signup_handler" ); if ! app_dbs.config.signup_mode.contains("invitation") { let msg = format!("Config signup mode not invitation: {}", app_dbs.config.signup_mode ); println!("{}", &msg); let _ = req_handler.trace_req(msg); return Redirect::temporary( &format!("/")).into_response(); } // println!("root_handler: {}",&session_cookie); let session_cookie = decode(&data).unwrap_or_default().to_string(); let auth_state = AuthState::from_cookie(session_cookie.to_owned(), &app_dbs).await; if auth_state.session.is_none() { // TODO make it prettier let _ = req_handler.trace_req(format!("No session found")); return ( StatusCode::NOT_FOUND, req_handler.req_header.header, "No valid invitation found" ).into_response(); } //let user_data = auth_state.user_data(); let mut usr = User::default(); usr.roles = auth_state.user_roles(); usr.email = auth_state.user_email(); let usr_id = auth_state.user_id(); let invite_id = if usr_id == "0" { usr_id } else { usr.email.to_owned() }; // let _uri_path = format!("{}",&uri.path().to_string()); // let file = "hello.html"; // let result = app_dbs.tera.render(&file, &app_dbs.context).unwrap_or_else(|e|{ // println!("Error render {}: {}",&file,e); // String::from("") // }); let mut res_headers = HeaderMap::new(); if req_handler.req_header.is_browser() { res_headers.append(axum::http::header::CONTENT_TYPE,"text/html; charset=utf-8".parse().unwrap()); } req_handler.context.insert("user", &usr); req_handler.context.insert("isadmin", ""); req_handler.context.insert("totp_mode", &format!("{}",&app_dbs.config.totp_mode)); req_handler.context.insert("invite_key", &data); req_handler.context.insert("invite_id", &invite_id); req_handler.context.insert("admin_fields", &app_dbs.config.admin_fields); let result = if let Some(tpl) = app_dbs.config.tpls.get("signup") { req_handler.render_template(&tpl,"signup") } else { String::from("signup") }; let _ = req_handler.trace_req(format!("Invite to '{}' data: {}",&invite_id, &data)); ( res_headers, result.to_owned() ).into_response() } async fn post_invite_handler( header: HeaderMap, uri: Uri, Extension(app_dbs): Extension>, Extension(cookies): Extension, Extension(random): Extension, ConnectInfo(app_connect_info): ConnectInfo, Json(user_invite): Json, ) -> Response { let auth_state = get_auth_state(true, &cookies, &app_dbs).await; let mut req_handler = ReqHandler::new( ReqHeaderMap::new(header, &format!("{}",&uri.path().to_string()), &app_connect_info), &app_dbs, &uri, &auth_state, &random, "post_invite_handler" ); if ! app_dbs.config.signup_mode.contains("invitation") { let msg = format!("Config signup mode not invitation: {}", app_dbs.config.signup_mode ); println!("{}", &msg); let _ = req_handler.trace_req(msg); return Redirect::temporary( &format!("/")).into_response(); } if ! auth_state.has_auth_role(&app_dbs.config.auth_roles) { let _ = req_handler.trace_req(format!("User '{}' not have role 'dev'",&auth_state.user_id())); return Redirect::temporary( &format!("/login?o={}",uri.path().to_string())).into_response(); // return ( // StatusCode::UNAUTHORIZED, // header, // "Error authorization" // ).into_response(); } if ! user_invite.email.contains("@") { let _ = req_handler.trace_req(format!("Invite email '{}' not contains '@'",&user_invite.email)); return ( StatusCode::BAD_REQUEST, req_handler.req_header.header, "Error invitation data" ).into_response(); } let session_token = req_handler.new_token(); let mut usr = User::default(); usr.email = user_invite.email.to_owned(); usr.roles = user_invite.roles.to_owned(); usr.isadmin = user_invite.isadmin; usr.status = UserStatus::Pending; let user_data = usr.session_data(); let session_cookie = add_session_cookie(false,&cookies, &session_token, &user_data, user_invite.expire, &app_dbs, "invitation").await; let session_encoded_key = encode(session_cookie.as_str()); let body=format!("This is an invitation to docserver service"); let subject = format!("DocServer Invitation"); let signup_url= format!( "{}://{}/signup", &app_dbs.config.protocol, &app_dbs.config.hostport, ); let invite_expiration = format!("{} minutes", (&app_dbs.config.session_expire/60) ); req_handler.context.insert("signup_url", &signup_url); req_handler.context.insert("invite_expiration", &invite_expiration); req_handler.context.insert("email_body",&body); req_handler.context.insert("invite_key",&session_encoded_key); req_handler.context.insert("email_subject",&subject); let (status, result) = if app_dbs.config.use_mail && user_invite.send_email { let mail_content= if let Some(tpl) = app_dbs.config.tpls.get("invite_mail_txt") { req_handler.render_template(tpl, "invite") } else { format!("{}\n{}\n{}/signup/{}\n",&subject,&body,&signup_url,&session_cookie) }; let mail_html_content = if let Some(tpl) = app_dbs.config.tpls.get("invite_mail_html") { req_handler.render_template(tpl, "invite") } else { format!("{}\n{}\n{}/signup/{}\n",&subject,&body,&signup_url,&session_cookie) }; let mail_check = MailMessage::check(&app_dbs); if ! mail_check.is_empty() { ( StatusCode::BAD_REQUEST, mail_check ) } else { //"jesus.perezlorenzo@gmail.com", // "jesus@librecloud.online", match MailMessage::new( &app_dbs.config.mail_from, &user_invite.email, &app_dbs.config.mail_reply_to, ) { Ok(mail_message) => { //match mail_message.send_message( match mail_message.send_html_message( &subject, &mail_content, &mail_html_content, &app_dbs ).await { Ok(_) => { let _ = req_handler.trace_req(format!( "Invitation mail sent to: '{}' reset url: {} roles: {}, isadmin: {}, expiration: {} cookie: {}", &user_invite.email, &invite_expiration, user_invite.roles, user_invite.isadmin, &signup_url,&session_cookie )); (StatusCode::OK, format!("Mail sent to {}",&user_invite.email)) }, Err(e) => { println!("Invitation to: {} Error mail message send: {}",&user_invite.email, e); ( StatusCode::INTERNAL_SERVER_ERROR, String::from("Error") ) } } }, Err(e) => { println!("Invitation to: {} Error mail message creation: {}",&user_invite.email, e); ( StatusCode::BAD_REQUEST, String::from("Error") ) } } } } else { let _ = req_handler.trace_req(format!( "Created invitation: '{}' reset url: {} roles: {}, isadmin: {}, expiration: {} cookie: {}", &user_invite.email, &invite_expiration, user_invite.roles, user_invite.isadmin, &signup_url,&session_cookie )); (StatusCode::OK, format!("No mail sent to {}",&user_invite.email)) }; req_handler.prepare_response(); req_handler.context.insert("email_result",&result); let response = if let Some(tpl) = app_dbs.config.tpls.get("invite_output") { req_handler.render_template(tpl, "invite") } else { format!("{}\n{}\n{}/signup/{}\n",&subject,&body,&signup_url,&session_cookie) }; ( status, req_handler.req_header.header, response, ).into_response() } async fn invite_handler( header: HeaderMap, uri: Uri, Extension(app_dbs): Extension>, Extension(cookies): Extension, Extension(random): Extension, ConnectInfo(app_connect_info): ConnectInfo, ) -> Response { let auth_state = get_auth_state(true, &cookies, &app_dbs).await; let mut req_handler = ReqHandler::new( ReqHeaderMap::new(header, &format!("{}",&uri.path().to_string()), &app_connect_info), &app_dbs, &uri, &auth_state, &random, "invite_handler" ); if ! app_dbs.config.signup_mode.contains("invitation") { let msg = format!("Config signup mode not invitation: {}", app_dbs.config.signup_mode ); println!("{}",&msg); let _ = req_handler.trace_req(msg); return Redirect::temporary( &format!("/")).into_response(); } if ! auth_state.has_auth_role(&app_dbs.config.auth_roles) { let _ = req_handler.trace_req(format!("User '{}' not have role 'dev'",&auth_state.user_id())); return Redirect::temporary( &format!("/login?o={}",uri.path().to_string())).into_response(); // return ( // StatusCode::UNAUTHORIZED, // header, // "Error authorization" // ).into_response(); } let title = format!("DocServer Invitation"); let invite_url= format!( "{}://{}/invite", &app_dbs.config.protocol, &app_dbs.config.hostport, ); let invite_expire = format!("{} minutes", (&app_dbs.config.invite_expire/60) ); req_handler.context.insert("target_url", &invite_url); req_handler.context.insert("invite_expire", &invite_expire); if app_dbs.config.use_mail { req_handler.context.insert("use_mail", &app_dbs.config.use_mail); } req_handler.prepare_response(); req_handler.context.insert("with_menu", "1"); let response = if let Some(tpl) = app_dbs.config.tpls.get("invite_create") { req_handler.render_template(tpl, "invite create") } else { format!("{} invite",&title) }; let _ = req_handler.trace_req(format!( "Invitation: url: {}, expiration: {}", &invite_url, &invite_expire, )); ( req_handler.req_header.header, response, ).into_response() // let uri_path = format!("{}",&uri.path().to_string()); // let file = "hello.html"; // let result = app_dbs.tera.render(&file, &app_dbs.context).unwrap_or_else(|e|{ // println!("Error render {}: {}",&file,e); // String::from("") // }); // let mut new_header = header.to_owned(); //new_header.append("Set-Cookie", "session_token=_; Max-Age=0".parse().unwrap()); // cookies.remove(Cookie::new(SESSION_COOKIE_NAME, "")); // ( // header, // result.to_owned() // ).into_response() // "Hello, World!" } route( "/signup/:data", get(invite_signup_handler)) .route("/invite", get(invite_handler)) .route("/invite", post(post_invite_handler)) }