Yaltopia-Hotels/src/lib/auth-api.ts

166 lines
5.2 KiB
TypeScript

import type { RegisterHotelStaffDto, StaffAccess } from "@/lib/types";
const API_ROOT = "/api";
let getPropertyId: () => string | null = () => null;
/** Register property scope for hotel auth-API paths (e.g. staff management). */
export function registerHotelAuthApiContext(ctx: {
getPropertyId: () => string | null;
}) {
getPropertyId = ctx.getPropertyId;
}
function shouldRewriteForHotel(path: string): boolean {
const first = path.split("?")[0];
if (first.startsWith("/auth")) return false;
if (first.startsWith("/properties")) return false;
if (first.startsWith("/admin")) return false;
return true;
}
function rewriteHotelPath(path: string): string {
const pid = getPropertyId();
if (!pid || !shouldRewriteForHotel(path)) return path;
const [pathname, query] = path.split("?");
const q = query ? `?${query}` : "";
return `/properties/${pid}/hotel${pathname}${q}`;
}
function apiUrl(path: string): string {
const base = import.meta.env.VITE_API_BASE_URL ?? "";
const resolved = rewriteHotelPath(path);
return `${base}${API_ROOT}${resolved}`;
}
export async function parseApiError(res: Response): Promise<string> {
const t = await res.text();
try {
const j = JSON.parse(t) as { message?: string | string[]; error?: string };
if (Array.isArray(j.message)) return j.message.join(", ");
if (typeof j.message === "string") return j.message;
if (j.error) return j.error;
} catch {
/* ignore */
}
return t || res.statusText;
}
export type AuthUser = {
id: string;
name: string;
email?: string | null;
phone?: string | null;
role: string;
status?: string;
propertyId?: string | null;
};
export async function postLogin(identifier: string, password: string) {
const res = await fetch(apiUrl("/auth/login"), {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ identifier, password }),
});
if (!res.ok) throw new Error(await parseApiError(res));
return res.json() as Promise<{ access_token: string; user: AuthUser }>;
}
export async function postLoginPhoneRequest(phone: string) {
const res = await fetch(apiUrl("/auth/login-phone-request"), {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ phone }),
});
if (!res.ok) throw new Error(await parseApiError(res));
return res.json() as Promise<{
message: string;
loginRequestToken?: string;
}>;
}
export async function postLoginPhoneVerify(loginRequestToken: string, otp: string) {
const res = await fetch(apiUrl("/auth/login-phone-verify"), {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ loginRequestToken, otp }),
});
if (!res.ok) throw new Error(await parseApiError(res));
return res.json() as Promise<{ access_token: string; user: AuthUser }>;
}
export async function postSendOtp(identifier: string) {
const res = await fetch(apiUrl("/auth/sendOtp"), {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ identifier: identifier.trim() }),
});
if (!res.ok) throw new Error(await parseApiError(res));
return res.json() as Promise<{ message: string }>;
}
export async function postHotelGuestLoginEmailOtp(email: string, otp: string) {
const res = await fetch(apiUrl("/auth/hotel-user/login-email-otp"), {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: email.trim().toLowerCase(), otp: otp.trim() }),
});
if (!res.ok) throw new Error(await parseApiError(res));
return res.json() as Promise<{ access_token: string; user: AuthUser }>;
}
export type PropertyRow = {
id: string;
name: string;
accommodationMode?: string;
};
export async function getProfile(token: string) {
const res = await fetch(apiUrl("/auth/profile"), {
headers: { Authorization: `Bearer ${token}` },
});
if (!res.ok) throw new Error(await parseApiError(res));
return res.json() as Promise<AuthUser & Record<string, unknown>>;
}
export async function getProperties(token: string) {
const res = await fetch(apiUrl("/properties/hotels"), {
headers: { Authorization: `Bearer ${token}` },
});
if (!res.ok) throw new Error(await parseApiError(res));
return res.json() as Promise<PropertyRow[]>;
}
export async function getStaffAccess(token: string) {
const res = await fetch(apiUrl("/staff-access"), {
headers: { Authorization: `Bearer ${token}` },
});
if (!res.ok) throw new Error(await parseApiError(res));
const data = await res.json();
return data.data || data as StaffAccess[];
}
export async function postStaff(token: string, dto: RegisterHotelStaffDto) {
const res = await fetch(apiUrl("/staff"), {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`
},
body: JSON.stringify(dto),
});
if (!res.ok) throw new Error(await parseApiError(res));
return res.json() as Promise<StaffAccess>;
}
export async function deleteStaffAccess(token: string, accessId: string) {
const res = await fetch(apiUrl(`/staff-access/${accessId}`), {
method: "DELETE",
headers: { Authorization: `Bearer ${token}` },
});
if (!res.ok) throw new Error(await parseApiError(res));
return;
}