Commit 7b0a3557 authored by Lê Bảo Hồng Đức's avatar Lê Bảo Hồng Đức

[tag]0.1-vcci

parents 0d0d7d5e 5243b3bc
Pipeline #52427 passed with stages
in 7 minutes and 2 seconds
...@@ -9,7 +9,7 @@ import "swiper/css"; ...@@ -9,7 +9,7 @@ import "swiper/css";
import { getApiV10Banner } from "@/api/endpoints/banner"; import { getApiV10Banner } from "@/api/endpoints/banner";
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { resolveCmsFileUrl } from "@/lib/api/files"; import { fetchCmsFileById, resolveCmsFileUrl } from "@/lib/api/files";
import { Skeleton } from "@/components/ui/skeleton"; import { Skeleton } from "@/components/ui/skeleton";
type ApiEnvelope<T> = { type ApiEnvelope<T> = {
...@@ -65,12 +65,12 @@ function BannerSlideItem({ ...@@ -65,12 +65,12 @@ function BannerSlideItem({
fileId?: string | null; fileId?: string | null;
}) { }) {
const { data: file, isPending } = useQuery({ const { data: file, isPending } = useQuery({
queryKey: ["file", fileId], queryKey: ["cms-file", fileId],
queryFn: () => Promise.resolve({ path: src }), queryFn: () => fetchCmsFileById(fileId!),
enabled: !!fileId && !src, enabled: !!fileId,
}); });
if (fileId && isPending && !src) { if (isPending) {
return ( return (
<Skeleton className="w-full h-[200px] sm:h-[300px] md:h-[400px] lg:h-[500px]" /> <Skeleton className="w-full h-[200px] sm:h-[300px] md:h-[400px] lg:h-[500px]" />
); );
......
...@@ -7,7 +7,7 @@ import useAuthStore, { ...@@ -7,7 +7,7 @@ import useAuthStore, {
} from "@/store/useAuthStore"; } from "@/store/useAuthStore";
import links from "@/links"; import links from "@/links";
const AUTH_BASE_URL = `${links.apiEndpoint}/auth`; const AUTH_BASE_URL = `${links.apiEndpoint}/api/v1.0/auth`;
const SESSION_EXPIRED_MESSAGE = "Phiên đăng nhập đã hết hạn. Vui lòng đăng nhập lại."; const SESSION_EXPIRED_MESSAGE = "Phiên đăng nhập đã hết hạn. Vui lòng đăng nhập lại.";
interface AuthEnvelope<T> { interface AuthEnvelope<T> {
...@@ -39,6 +39,12 @@ interface LoginResponseData { ...@@ -39,6 +39,12 @@ interface LoginResponseData {
token_type?: string | null; token_type?: string | null;
} }
interface LoginResponseEnvelope {
responseData?: LoginResponseData;
message?: string | null;
message_en?: string | null;
}
type MeResponseData = Partial<AuthenticatedAdminUser>; type MeResponseData = Partial<AuthenticatedAdminUser>;
interface RefreshResponseData { interface RefreshResponseData {
...@@ -49,9 +55,14 @@ interface RefreshResponseData { ...@@ -49,9 +55,14 @@ interface RefreshResponseData {
token_type?: string | null; token_type?: string | null;
} }
interface RefreshResponseEnvelope {
responseData?: RefreshResponseData;
}
interface AuthRequestOptions extends RequestInit { interface AuthRequestOptions extends RequestInit {
skipAuthHeader?: boolean; skipAuthHeader?: boolean;
authToken?: string | null; authToken?: string | null;
noEnvelope?: boolean;
} }
type AuthFailureReason = "missing_refresh_token" | "refresh_failed"; type AuthFailureReason = "missing_refresh_token" | "refresh_failed";
...@@ -163,7 +174,7 @@ async function requestAuth<T>( ...@@ -163,7 +174,7 @@ async function requestAuth<T>(
throw error; throw error;
} }
return getEnvelopeData(data) as T; return init?.noEnvelope ? (data as T) : getEnvelopeData(data) as T;
} }
const redirectToLogin = () => { const redirectToLogin = () => {
...@@ -191,7 +202,7 @@ export async function loginAdmin( ...@@ -191,7 +202,7 @@ export async function loginAdmin(
password: string, password: string,
options?: { persistSession?: boolean }, options?: { persistSession?: boolean },
) { ) {
const payload = await requestAuth<LoginResponseData>("/login", { const payload = await requestAuth<LoginResponseEnvelope>("/login", {
method: "POST", method: "POST",
body: JSON.stringify({ body: JSON.stringify({
email, email,
...@@ -200,30 +211,32 @@ export async function loginAdmin( ...@@ -200,30 +211,32 @@ export async function loginAdmin(
skipAuthHeader: true, skipAuthHeader: true,
}); });
if (!payload.access_token || !payload.refresh_token || !payload.expires_in) { const loginData = payload?.responseData ?? payload as LoginResponseData;
if (!loginData?.access_token || !loginData?.refresh_token || !loginData?.expires_in) {
throw new Error("Thiếu dữ liệu phiên đăng nhập từ API."); throw new Error("Thiếu dữ liệu phiên đăng nhập từ API.");
} }
const me = await requestAuth<MeResponseData>("/me", { const me = await requestAuth<MeResponseData>("/me", {
method: "GET", method: "GET",
authToken: payload.access_token, authToken: loginData.access_token,
}).catch(() => payload.user ?? null); }).catch(() => loginData.user ?? null);
const normalizedUser = normalizeUser(me ?? payload.user); const normalizedUser = normalizeUser(me ?? loginData.user);
useAuthStore.getState().setAuthSession({ useAuthStore.getState().setAuthSession({
accessToken: payload.access_token, accessToken: loginData.access_token,
refreshToken: payload.refresh_token, refreshToken: loginData.refresh_token,
expiresIn: payload.expires_in, expiresIn: loginData.expires_in,
accessTokenExpired: getJwtExpiresAt(payload.access_token), accessTokenExpired: getJwtExpiresAt(loginData.access_token),
user: normalizedUser, user: normalizedUser,
session: normalizeSession(payload.session), session: normalizeSession(loginData.session),
persistSession: options?.persistSession === true, persistSession: options?.persistSession === true,
}); });
useAuthStore.getState().setAppUser(normalizedUser); useAuthStore.getState().setAppUser(normalizedUser);
return payload; return loginData;
} }
export async function logoutAdmin(options?: { export async function logoutAdmin(options?: {
...@@ -290,7 +303,7 @@ export async function refreshAdminAccessToken() { ...@@ -290,7 +303,7 @@ export async function refreshAdminAccessToken() {
store.setAppRefreshing(true); store.setAppRefreshing(true);
try { try {
const payload = await requestAuth<RefreshResponseData>("/refresh", { const payload = await requestAuth<RefreshResponseEnvelope>("/refresh", {
method: "POST", method: "POST",
body: JSON.stringify({ body: JSON.stringify({
refresh_token: refreshToken, refresh_token: refreshToken,
...@@ -298,19 +311,21 @@ export async function refreshAdminAccessToken() { ...@@ -298,19 +311,21 @@ export async function refreshAdminAccessToken() {
skipAuthHeader: true, skipAuthHeader: true,
}); });
if (!payload.access_token || !payload.expires_in) { const refreshData = payload?.responseData ?? payload as RefreshResponseData;
if (!refreshData?.access_token || !refreshData?.expires_in) {
throw new Error("Thiếu access token mới từ API."); throw new Error("Thiếu access token mới từ API.");
} }
useAuthStore.getState().updateAccessToken({ useAuthStore.getState().updateAccessToken({
accessToken: payload.access_token, accessToken: refreshData.access_token,
expiresIn: payload.expires_in, expiresIn: refreshData.expires_in,
accessTokenExpired: getJwtExpiresAt(payload.access_token), accessTokenExpired: getJwtExpiresAt(refreshData.access_token),
refreshToken: payload.refresh_token ?? refreshToken, refreshToken: refreshData.refresh_token ?? refreshToken,
session: normalizeSession(payload.session), session: normalizeSession(refreshData.session),
}); });
return payload.access_token; return refreshData.access_token;
} catch (error) { } catch (error) {
await logoutAdmin({ silent: true, reason: "refresh_failed" }); await logoutAdmin({ silent: true, reason: "refresh_failed" });
throw error; throw error;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment