1 use crate::account::model::{Data, Status, User};
2 use anyhow::{anyhow, Result};
3 use console::style;
4 use dialoguer::{theme::ColorfulTheme, Input, Password};
5 use log::debug;
6 use reqwest::Client;
7 use serde::{Deserialize, Serialize};
8 use smbpndk_model::CommandResult;
9 use smbpndk_networking::constants::BASE_URL;
10 use smbpndk_utils::email_validation;
11 use spinners::Spinner;
12 use std::{
13 fs::{self, create_dir_all, OpenOptions},
14 io::Write,
15 };
16
17 pub struct LoginArgs {
18 pub username: String,
19 pub password: String,
20 }
21
22 #[derive(Debug, Serialize)]
23 struct LoginParams {
24 user: User,
25 }
26
27 #[derive(Debug, Serialize, Deserialize)]
28 struct LoginResult {
29 status: Status,
30 data: Data,
31 }
32
33 pub async fn process_login() -> Result<CommandResult> {
34 println!("Provide your login credentials.");
35 let username = Input::<String>::with_theme(&ColorfulTheme::default())
36 .with_prompt("Username")
37 .validate_with(|email: &String| email_validation(email))
38 .interact()
39 .unwrap();
40 let password = Password::with_theme(&ColorfulTheme::default())
41 .with_prompt("Password")
42 .interact()
43 .unwrap();
44
45 let spinner = Spinner::new(
46 spinners::Spinners::SimpleDotsScrolling,
47 style("Logging in...").green().bold().to_string(),
48 );
49
50 match do_process_login(LoginArgs { username, password }).await {
51 Ok(_) => Ok(CommandResult {
52 spinner,
53 symbol: "✅".to_owned(),
54 msg: "You are logged in!".to_owned(),
55 }),
56 Err(e) => Ok(CommandResult {
57 spinner,
58 symbol: "😩".to_owned(),
59 msg: format!("Failed to login: {e}"),
60 }),
61 }
62 }
63
64 async fn do_process_login(args: LoginArgs) -> Result<()> {
65 let login_params = LoginParams {
66 user: User {
67 email: args.username,
68 password: args.password,
69 },
70 };
71
72 let response = Client::new()
73 .post([BASE_URL, "/v1/users/sign_in"].join(""))
74 .json(&login_params)
75 .send()
76 .await?;
77
78 match response.status() {
79 reqwest::StatusCode::OK => {
80 let headers = response.headers();
81 match headers.get("Authorization") {
82 Some(token) => {
83 debug!("{}", token.to_str()?);
84 match home::home_dir() {
85 Some(path) => {
86 debug!("{}", path.to_str().unwrap());
87 create_dir_all(path.join(".smb"))?;
88 let mut file = OpenOptions::new()
89 .create(true)
90 .write(true)
91 .open([path.to_str().unwrap(), "/.smb/token"].join(""))?;
92 file.write_all(token.to_str()?.as_bytes())?;
93 }
94 None => {
95 let error = anyhow!("Failed to get home directory.");
96 return Err(error);
97 }
98 }
99 }
100 None => {
101 let error = anyhow!("Failed to get token. Probably a backend issue.");
102 return Err(error);
103 }
104 }
105 }
106 _ => {
107 let error = anyhow!("Connection error. Check your username and password.");
108 return Err(error);
109 }
110 }
111
112 Ok(())
113 }
114
115 pub async fn process_logout() -> Result<CommandResult> {
116 let spinner = Spinner::new(
117 spinners::Spinners::SimpleDotsScrolling,
118 style("Logging you out...").green().bold().to_string(),
119 );
120 match home::home_dir() {
121 Some(path) => {
122 debug!("{}", path.to_str().unwrap());
123 fs::remove_file([path.to_str().unwrap(), "/.smb/token"].join(""))?;
124
125 Ok(CommandResult {
126 spinner,
127 symbol: "✅".to_owned(),
128 msg: "You are logged out!".to_owned(),
129 })
130 }
131 None => {
132 let error = anyhow!("Failed to get home directory. Are you logged in?");
133 Err(error)
134 }
135 }
136 }