|
|
|
@ -1,6 +1,7 @@ |
|
|
|
import { create } from "zustand"; |
|
|
|
import { createJSONStorage, persist } from "zustand/middleware"; |
|
|
|
import type { |
|
|
|
UserApiKeyResponse, |
|
|
|
UserInfo, |
|
|
|
UserInfoResponse, |
|
|
|
UserSession, |
|
|
|
@ -10,6 +11,7 @@ import { openLoginRequiredModal } from "@/utils/loginRequiredModal"; |
|
|
|
|
|
|
|
export const USER_SESSION_STORAGE_KEY = "node-banana-user-session"; |
|
|
|
export const USER_INFO_ENDPOINT = "/api/user/info"; |
|
|
|
export const USER_API_KEY_ENDPOINT = "/api/user/apikey"; |
|
|
|
|
|
|
|
const userInfoRequests = new Map<string, Promise<UserSession>>(); |
|
|
|
|
|
|
|
@ -95,24 +97,98 @@ export const fetchUserInfoByToken = async ( |
|
|
|
return request; |
|
|
|
}; |
|
|
|
|
|
|
|
export const fetchGatewayApiKeyByToken = async ( |
|
|
|
token: string, |
|
|
|
fetcher: typeof fetch = fetch, |
|
|
|
): Promise<string | null> => { |
|
|
|
const response = await fetcher(USER_API_KEY_ENDPOINT, { |
|
|
|
method: "GET", |
|
|
|
headers: { |
|
|
|
Accept: "application/json", |
|
|
|
Authorization: `Bearer ${token}`, |
|
|
|
token, |
|
|
|
}, |
|
|
|
}); |
|
|
|
|
|
|
|
if (!response.ok) { |
|
|
|
throw new Error(`Failed to fetch gateway API key: ${response.status}`); |
|
|
|
} |
|
|
|
|
|
|
|
const payload = (await response.json()) as UserApiKeyResponse; |
|
|
|
if (payload.status !== "0000") { |
|
|
|
throw new Error( |
|
|
|
typeof payload.message === "string" |
|
|
|
? payload.message |
|
|
|
: "Failed to fetch gateway API key" |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
const apiKey = payload.data?.apikey; |
|
|
|
return typeof apiKey === "string" && apiKey.trim() ? apiKey.trim() : null; |
|
|
|
}; |
|
|
|
|
|
|
|
export const useUserStore = create<UserStoreState>()( |
|
|
|
persist( |
|
|
|
(set, get) => ({ |
|
|
|
token: null, |
|
|
|
userInfo: null, |
|
|
|
gatewayApiKey: null, |
|
|
|
isLoadingUserInfo: false, |
|
|
|
isLoadingGatewayApiKey: false, |
|
|
|
userInfoError: null, |
|
|
|
gatewayApiKeyError: null, |
|
|
|
setToken: (token) => set({ token }), |
|
|
|
fetchGatewayApiKey: async () => { |
|
|
|
const token = get().token?.trim() || getStoredLoginToken(); |
|
|
|
if (!token) { |
|
|
|
set({ gatewayApiKey: null, gatewayApiKeyError: null }); |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
set({ isLoadingGatewayApiKey: true, gatewayApiKeyError: null }); |
|
|
|
|
|
|
|
try { |
|
|
|
const apiKey = await fetchGatewayApiKeyByToken(token); |
|
|
|
set({ |
|
|
|
gatewayApiKey: apiKey, |
|
|
|
isLoadingGatewayApiKey: false, |
|
|
|
gatewayApiKeyError: null, |
|
|
|
}); |
|
|
|
return apiKey; |
|
|
|
} catch (error) { |
|
|
|
set({ |
|
|
|
gatewayApiKey: null, |
|
|
|
isLoadingGatewayApiKey: false, |
|
|
|
gatewayApiKeyError: getErrorMessage(error), |
|
|
|
}); |
|
|
|
return null; |
|
|
|
} |
|
|
|
}, |
|
|
|
clearGatewayApiKey: () => |
|
|
|
set({ |
|
|
|
gatewayApiKey: null, |
|
|
|
isLoadingGatewayApiKey: false, |
|
|
|
gatewayApiKeyError: null, |
|
|
|
}), |
|
|
|
fetchUserInfo: async (token) => { |
|
|
|
const normalizedToken = resolveLoginToken(token, get().token); |
|
|
|
if (!normalizedToken) { |
|
|
|
set({ token: null, userInfo: null, userInfoError: null }); |
|
|
|
set({ |
|
|
|
token: null, |
|
|
|
userInfo: null, |
|
|
|
gatewayApiKey: null, |
|
|
|
userInfoError: null, |
|
|
|
gatewayApiKeyError: null, |
|
|
|
}); |
|
|
|
openLoginRequiredModal(); |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
const current = get(); |
|
|
|
if (current.token === normalizedToken && current.userInfo) { |
|
|
|
if (!current.gatewayApiKey) { |
|
|
|
await get().fetchGatewayApiKey(); |
|
|
|
} |
|
|
|
return current.userInfo; |
|
|
|
} |
|
|
|
|
|
|
|
@ -130,11 +206,13 @@ export const useUserStore = create<UserStoreState>()( |
|
|
|
isLoadingUserInfo: false, |
|
|
|
userInfoError: null, |
|
|
|
}); |
|
|
|
await get().fetchGatewayApiKey(); |
|
|
|
return session.userInfo; |
|
|
|
} catch (error) { |
|
|
|
set({ |
|
|
|
isLoadingUserInfo: false, |
|
|
|
userInfo: null, |
|
|
|
gatewayApiKey: null, |
|
|
|
userInfoError: getErrorMessage(error), |
|
|
|
}); |
|
|
|
return null; |
|
|
|
@ -144,8 +222,11 @@ export const useUserStore = create<UserStoreState>()( |
|
|
|
set({ |
|
|
|
token: null, |
|
|
|
userInfo: null, |
|
|
|
gatewayApiKey: null, |
|
|
|
isLoadingUserInfo: false, |
|
|
|
isLoadingGatewayApiKey: false, |
|
|
|
userInfoError: null, |
|
|
|
gatewayApiKeyError: null, |
|
|
|
}), |
|
|
|
}), |
|
|
|
{ |
|
|
|
|