This commit is contained in:
ValdiANS
2025-09-26 11:06:31 +07:00
parent a5524686a6
commit 2e1b0fef2b
36 changed files with 8716 additions and 79 deletions
View File
+25
View File
@@ -0,0 +1,25 @@
import { create } from 'zustand';
import { UserWithRoles } from '@/types/api';
type AuthStore = {
user?: UserWithRoles;
isLoadingUser?: boolean;
setUser: (newUserData?: UserWithRoles) => void;
setIsLoadingUser: (isLoading?: boolean) => void;
};
const useAuthStore = create<AuthStore>()((set) => ({
user: undefined,
isLoadingUser: false,
setUser: (newUserData) => set({ user: newUserData }),
setIsLoadingUser: (isLoading) => set({ isLoadingUser: Boolean(isLoading) }),
}));
export const useAuth = () => {
const { user, setUser } = useAuthStore();
return {
user,
setUser,
};
};
+22
View File
@@ -0,0 +1,22 @@
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
export type AuthMode = 'none' | 'cookie' | 'bearer';
export type RequestOptions<B = unknown> = {
method?: HttpMethod;
body?: B;
query?: Record<string, unknown>;
headers?: Record<string, string>;
auth?: AuthMode; // 'cookie' | 'bearer' | 'none'
token?: string; // required if auth === 'bearer'
timeoutMs?: number;
};
export class HttpError extends Error {
constructor(
public status: number,
public code?: string,
public data?: unknown
) {
super(`HTTP ${status}${code ? ` ${code}` : ''}`);
}
}
+63
View File
@@ -0,0 +1,63 @@
import axios from 'axios';
import type { AxiosRequestConfig } from 'axios';
import { HttpError, RequestOptions } from '@/services/http/base';
const BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL ?? '';
const axiosClient = axios.create({ baseURL: BASE_URL, timeout: 10_000 });
export async function httpClient<T, B = unknown>(
path: string,
opts: RequestOptions<B> = {}
): Promise<T> {
const isCookieAuth = opts.auth === 'cookie';
const isBearerAuth = opts.auth === 'bearer' && !!opts.token;
const config: AxiosRequestConfig = {
url: path,
method: opts.method ?? 'GET',
params: opts.query,
data: opts.body,
timeout: opts.timeoutMs ?? 10_000,
withCredentials: isCookieAuth,
headers: {
'Content-Type': 'application/json',
...(opts.headers ?? {}),
...(isBearerAuth ? { Authorization: `Bearer ${opts.token}` } : {}),
},
};
try {
const res = await axiosClient.request<T>(config);
return res.data;
} catch (e: any) {
if (axios.isAxiosError(e)) {
throw e;
}
throw new HttpError(e.response?.status ?? 0, e.code, e.response?.data);
}
}
type SWRHttpKey<B = unknown> =
| string
| [path: string, opts?: RequestOptions<B>]
| { path: string; opts?: RequestOptions<B> };
export async function httpClientFetcher<T = unknown, B = unknown>(
key: SWRHttpKey<B>
): Promise<T> {
if (!key) throw new Error('Invalid SWR key');
let path: string;
let opts: RequestOptions<B> | undefined;
if (typeof key === 'string') {
path = key;
} else if (Array.isArray(key)) {
[path, opts] = key;
} else {
({ path, opts } = key);
}
return httpClient<T, B>(path, opts);
}