Files
meditation-admin/lib/auth.tsx
T
2026-08-20 18:48:22 +03:30

106 lines
2.9 KiB
TypeScript

"use client";
// Two-step login flow + auth state, exposed through React context.
//
// Step 1: POST {approagency}/auth/login (auth, password, package_name) -> approoToken
// Step 2: POST {meditation}/auth/login-with-approo-v2?token=approoToken&package_name=...
// (multipart: token, package_name) -> meditation bearer token
//
// The meditation token is persisted in localStorage and attached to every
// subsequent request by lib/api.ts.
import {
createContext,
useCallback,
useContext,
useState,
} from "react";
import { APPRO_BASE_URL, MEDITATION_BASE_URL, PACKAGE_NAME } from "./config";
import {
ApiError,
apiFetch,
clearToken,
clearApproToken,
getToken,
setToken,
setApproToken,
toFormData,
} from "./api";
interface AuthState {
token: string | null;
ready: boolean; // hydrated from localStorage yet?
login: (identifier: string, password: string) => Promise<void>;
logout: () => void;
}
const AuthContext = createContext<AuthState | null>(null);
async function approAgencyLogin(
identifier: string,
password: string,
): Promise<string> {
const res = await apiFetch<{ token?: string }>("/auth/login", {
method: "POST",
baseUrl: APPRO_BASE_URL,
auth: false,
body: toFormData({
auth: identifier,
password,
package_name: PACKAGE_NAME,
}),
});
if (!res?.token) {
throw new ApiError("نام کاربری یا رمز عبور نادرست است.", 401, res);
}
return res.token;
}
async function meditationLogin(approoToken: string): Promise<string> {
const res = await apiFetch<{ token?: string }>(
"/auth/login-with-approo-v2",
{
method: "POST",
baseUrl: MEDITATION_BASE_URL,
auth: false,
query: { token: approoToken, package_name: PACKAGE_NAME },
body: toFormData({ token: approoToken, package_name: PACKAGE_NAME }),
},
);
if (!res?.token) {
throw new ApiError("ورود به سرویس مدیتیشن ناموفق بود.", 401, res);
}
return res.token;
}
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [token, setTokenState] = useState<string | null>(() => getToken());
const [ready] = useState(true);
const login = useCallback(async (identifier: string, password: string) => {
const approoToken = await approAgencyLogin(identifier, password);
setApproToken(approoToken);
const medToken = await meditationLogin(approoToken);
setToken(medToken);
setTokenState(medToken);
}, []);
const logout = useCallback(() => {
clearToken();
clearApproToken();
setTokenState(null);
}, []);
return (
<AuthContext.Provider value={{ token, ready, login, logout }}>
{children}
</AuthContext.Provider>
);
}
export function useAuth(): AuthState {
const ctx = useContext(AuthContext);
if (!ctx) throw new Error("useAuth must be used within <AuthProvider>");
return ctx;
}