fix login fail

Seto Elkahfi committed Aug 5, 2024 at 15:05 UTC b5fcd291bdb674dacf5cebb0475be627958ae6d7
11 files changed +131 -51
.vscode/launch.json
+2
@@ -15,6 +15,8 @@
15 "--no-default-features"
16 ]
17 },
18 + // task for the `beforeDevCommand` if used, must be configured in `.vscode/tasks.json`
19 + "preLaunchTask": "ui:dev"
20 },
21 {
22 "type": "lldb",
.vscode/settings.json
+3
@@ -5,4 +5,7 @@
5 "lib/crate_error_codes/Cargo.toml",
6 "lib/gem_error_codes/ext/gem_error_codes/Cargo.toml",
7 ],
8 + "terminal.integrated.automationProfile.osx": {
9 + "path": "/usr/local/bin/zsh-with-rc",
10 + }
11 }
\ No newline at end of file
.vscode/tasks.json new
+38
@@ -0,0 +1,38 @@
1 +{
2 + "version": "2.0.0",
3 + "tasks": [
4 + {
5 + "label": "sf-desktop:setup",
6 + "type": "shell",
7 + "command": "nvm",
8 + "args": [
9 + "use",
10 + ],
11 + "isBackground": true,
12 + "options": {
13 + "cwd": "${workspaceFolder}/frontend/splitfire-desktop/"
14 + }
15 + },
16 + {
17 + "label": "ui:dev",
18 + "type": "npm",
19 + "isBackground": true,
20 + "script": "next-dev",
21 + "options": {
22 + "cwd": "${workspaceFolder}/frontend/splitfire-desktop/"
23 + },
24 + "dependsOn": [
25 + "sf-desktop:setup"
26 + ]
27 + },
28 + {
29 + "label": "ui:build",
30 + "type": "npm",
31 + "script": "next-build",
32 + "isBackground": true,
33 + "options": {
34 + "cwd": "${workspaceFolder}/frontend/splitfire-desktop/"
35 + }
36 + }
37 + ]
38 +}
\ No newline at end of file
backend/musik88-web/app/controllers/api/v1/user_controller.rb
+1 -1
@@ -18,7 +18,7 @@ module Api
18 render_success
19 else
20 logger.debug 'Invalid credentials.'
21 - render_error 'Invalid credentials.'
21 + render_error(GemErrorCodes.invalid_credentials, 'Invalid credentials.', :unauthorized)
22 end
23 end
24
frontend/splitfire-desktop/app/layout.tsx
+3 -2
@@ -28,7 +28,6 @@ export default function RootLayout({
28 };
29
30 const getCurrentUser = async () => {
31 - setState(State.LOADING);
31 try {
32 const result = await db.currentUser
33 .where({ type: CurrentUserType.MAIN })
@@ -72,6 +71,7 @@ export default function RootLayout({
71 return (
72 <html lang="en" className="[color-scheme:dark]">
73 <body className="bg-gray-1100 overflow-none bg-[url('/grid.svg')] pb-36">
74 + {state === State.LOADED && user && (
75 <Suspense>
76 <UserContext.Provider value={{ user, updateUser }}>
77 <GlobalNav />
@@ -90,7 +90,8 @@ export default function RootLayout({
90 </div>
91 </div>
92 </UserContext.Provider>
93 - </Suspense>
93 + </Suspense>
94 + )}
95 </body>
96 </html>
97 );
frontend/splitfire-desktop/app/login/page.tsx
+44 -27
@@ -17,6 +17,7 @@ import { Button } from "@/components/button";
17 enum State {
18 LOADING,
19 LOADED,
20 + ERROR,
21 }
22
23 export default function Page() {
@@ -26,37 +27,46 @@ export default function Page() {
27 const [state, setState] = useState(State.LOADED);
28 const [emailOrUsername, setEmailOrUsername] = useState("");
29 const [password, setPassword] = useState("");
30 + const [errorMessage, setErrorMessage] = useState("");
31
32 const login = async (update: (user: CurrentUser) => void) => {
31 - setState(State.LOADING);
32 - const payload = {
33 - username: emailOrUsername,
34 - password: password,
35 - };
36 - log.debug("payload", payload);
37 - const response: AccountLoginResponse = await invoke(TAURI_ACCOUNT_LOGIN, payload);
38 - log.debug("response", response);
39 - if (response.status === TauriResponse.ERROR) {
40 - console.error("Login failed", response);
41 - return;
42 - }
43 - // Sanity check
44 - if (response.user === null || response.access_token === null) {
45 - log.error("Login failed", response);
46 - return;
47 - }
33 + try {
34 + setState(State.LOADING);
35 + const payload = {
36 + username: emailOrUsername,
37 + password: password,
38 + };
39 + log.debug("payload", payload);
40 + const response: AccountLoginResponse = await invoke(TAURI_ACCOUNT_LOGIN, payload);
41 + log.debug("response", response);
42 + if (response.status === TauriResponse.ERROR) {
43 + console.error("Login failed", response);
44 + setErrorMessage(response.message);
45 + return;
46 + }
47 + // Sanity check
48 + if (response.user === null || response.access_token === null) {
49 + log.error("Login failed", response);
50 + setErrorMessage(response.message);
51 + return;
52 + }
53
49 - let currentUser: CurrentUser = {
50 - accessToken: response.access_token,
51 - type: CurrentUserType.MAIN,
52 - user: response.user,
53 - mode: Mode.Vocal
54 + let currentUser: CurrentUser = {
55 + accessToken: response.access_token,
56 + type: CurrentUserType.MAIN,
57 + user: response.user,
58 + mode: Mode.Vocal
59 + }
60 + db.currentUser.add(currentUser)
61 + // Update UserContext
62 + update(currentUser);
63 +
64 + router.push("/");
65 + } catch (error) {
66 + log.error("Login failed", error);
67 + setErrorMessage("Login failed. Please try again.");
68 + setState(State.ERROR);
69 }
55 - db.currentUser.add(currentUser)
56 - // Update UserContext
57 - update(currentUser);
58 -
59 - router.push("/");
70 };
71
72 if (state === State.LOADING) {
@@ -74,6 +84,13 @@ export default function Page() {
84 event.preventDefault();
85 }}
86 >
87 + {state === State.ERROR && (
88 + <div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative" role="alert">
89 + <strong className="font-bold">Error!</strong>
90 + <span className="block sm:inline">{errorMessage}</span>
91 + </div>
92 + )
93 + }
94 <Form.Field className="FormField my-6" name="email">
95 <div
96 style={{
frontend/splitfire-desktop/src-tauri/src/rest/account.rs
+21 -14
@@ -5,10 +5,12 @@ use crate::{
5 },
6 models::{
7 account::{
8 - AccountLoginResponse, AccountProfileResponse, AccountRegisterResponse, ErrorResponse, LoginResponse, ProfileResponse, RegisterResponse
8 + AccountLoginResponse, AccountProfileResponse, AccountRegisterResponse, ErrorResponse,
9 + LoginResponse, ProfileResponse, RegisterResponse,
10 },
11 player::TauriResponse,
11 - }, rest::try_parsing_error_codes,
12 + },
13 + rest::try_parsing_error_codes,
14 };
15 use crate_error_codes::UserError;
16 use log::{debug, error};
@@ -17,7 +19,10 @@ use serde_json::json;
19 use tauri;
20
21 #[tauri::command]
20 -pub async fn account_login(username: String, password: String) -> Result<AccountLoginResponse, ErrorResponse> {
22 +pub async fn account_login(
23 + username: String,
24 + password: String,
25 +) -> Result<AccountLoginResponse, ErrorResponse> {
26 debug!(
27 "Logging in with username {}, password {}",
28 username, password
@@ -27,22 +32,14 @@ pub async fn account_login(username: String, password: String) -> Result<Account
32 "username": username,
33 "password": password
34 });
30 - let response = Client::new()
35 + let result = Client::new()
36 .post(account_url_builder(PATH_ACCOUNT_LOGIN))
37 .json(&body)
38 .send()
39 .await;
40
36 - let ok_response = match response {
37 - Ok(response) => {
38 - match response.error_for_status() {
39 - Ok(ok_response) => ok_response,
40 - Err(e) => {
41 - debug!("Response failed: {:?}", e);
42 - return try_parsing_error_codes::<AccountLoginResponse>(response).await;
43 - }
44 - }
45 - },
41 + let response = match result {
42 + Ok(ok_response) => ok_response,
43 Err(e) => {
44 debug!("Failed to get response: {:?}", e);
45 return Err(ErrorResponse {
@@ -51,6 +48,16 @@ pub async fn account_login(username: String, password: String) -> Result<Account
48 });
49 }
50 };
51 +
52 + let ok_response = match response.status() {
53 + reqwest::StatusCode::OK => response,
54 + _ => {
55 + debug!("Failed to login: {:?}", response);
56 + let error_response = try_parsing_error_codes(response).await;
57 + return error_response;
58 + }
59 + };
60 +
61 let token = match ok_response.headers().get("Authorization") {
62 Some(token) => {
63 // Trim Bearer prefix
frontend/splitfire-desktop/src-tauri/src/rest/mod.rs
+1 -1
@@ -10,7 +10,7 @@ async fn try_parsing_error_codes<T>(response: Response) -> Result<T, ErrorRespon
10 let e: ErrorResponse = match response.json().await {
11 Ok(json) => json,
12 Err(e) => {
13 - error!("Failed to parse response: {:?}", e);
13 + error!("Failed to parse error response: {:?}", e);
14 return Err(ErrorResponse {
15 error_code: UserError::ParseError,
16 message: e.to_string(),
lib/crate_error_codes/src/lib.rs
+2
@@ -9,6 +9,7 @@ use ts_rs::TS;
9 pub enum UserError {
10 // User defined error codes starts from 1000
11 UserNotFound = 1000,
12 + InvalidCredentials = 1001,
13
14 // Generic error codes starts from 1
15 InvalidRequest = 1,
@@ -23,6 +24,7 @@ impl UserError {
24 UserError::InvalidRequest => "Invalid request.",
25 UserError::ParseError => "Failed to parse response.",
26 UserError::NetworkError => "Failed to get response.",
27 + UserError::InvalidCredentials => "Invalid credentials.",
28 }
29 }
30
lib/gem_error_codes/Rakefile
+5 -5
@@ -1,14 +1,14 @@
1 # frozen_string_literal: true
2
3 -require "bundler/gem_tasks"
4 -require "rb_sys/extensiontask"
3 +require 'bundler/gem_tasks'
4 +require 'rb_sys/extensiontask'
5
6 task build: :compile
7
8 -GEMSPEC = Gem::Specification.load("gem_error_codes.gemspec")
8 +GEMSPEC = Gem::Specification.load('gem_error_codes.gemspec')
9
10 -RbSys::ExtensionTask.new("gem_error_codes", GEMSPEC) do |ext|
11 - ext.lib_dir = "lib/gem_error_codes"
10 +RbSys::ExtensionTask.new('gem_error_codes', GEMSPEC) do |ext|
11 + ext.lib_dir = 'lib/gem_error_codes'
12 end
13
14 task default: :compile
lib/gem_error_codes/ext/gem_error_codes/src/lib.rs
+11 -1
@@ -1,4 +1,4 @@
1 -use crate_error_codes::UserError::UserNotFound;
1 +use crate_error_codes::UserError::{InvalidCredentials, UserNotFound};
2 use magnus::{function, prelude::*, Error, Ruby};
3
4 fn user_not_found() -> i32 {
@@ -8,10 +8,20 @@ fn user_not_found_message() -> String {
8 UserNotFound.error_message().to_string()
9 }
10
11 +fn invalid_credentials() -> i32 {
12 + InvalidCredentials as i32
13 +}
14 +
15 +fn invalid_credentials_message() -> String {
16 + InvalidCredentials.error_message().to_string()
17 +}
18 +
19 #[magnus::init]
20 fn init(ruby: &Ruby) -> Result<(), Error> {
21 let module = ruby.define_module("GemErrorCodes")?;
22 module.define_singleton_method("user_not_found", function!(user_not_found, 0))?;
23 module.define_singleton_method("user_not_found_message", function!(user_not_found_message, 0))?;
24 + module.define_singleton_method("invalid_credentials", function!(invalid_credentials, 0))?;
25 + module.define_singleton_method("invalid_credentials_message", function!(invalid_credentials_message, 0))?;
26 Ok(())
27 }