Files
edr-platform/apps/edr-freight-web/portal/src/services/auth.service.ts

187 lines
5.4 KiB
TypeScript

import { URL_CONSTANTS } from "@/constants/URLS";
import type {
AuthUser,
ChangePasswordPayload,
CheckAvailabilityPayload,
CheckAvailabilityResponse,
ForgotPasswordRequestPayload,
ForgotPasswordVerifyPayload,
GenerateVerificationCodePayload,
LoginPayload,
LoginResponse,
OtpPayload,
OtpResponse,
ResetLinkAccount,
ResetTicket,
ResolveResetLinkPayload,
SendContactOtpPayload,
SendContactOtpResponse,
SetPasswordPayload,
SignupPayload,
SignupResponse,
UpdateAccountNamePayload,
UpdateContactPayload,
UpdateContactResponse,
} from "@/types/auth";
import { client } from "@/utils/api";
import { unwrap } from "@/utils/endpoint";
import { ApiResponse } from "@edr/types";
export const authService = {
login: async (body: LoginPayload) => {
const res = await client.post<LoginResponse>(
URL_CONSTANTS.AUTH.LOGIN,
body,
);
return res.data;
},
createUser: async (body: SignupPayload) => {
const res = await client.post<SignupResponse & ApiResponse<void>>(
URL_CONSTANTS.USERS.SIGN_UP,
body,
);
return res.data;
},
getMyInfo: async () => {
const res = await client.get<AuthUser>(URL_CONSTANTS.USERS.ME);
return res.data;
},
generateVerificationCode: async (body: GenerateVerificationCodePayload) => {
const res = await client.patch<ApiResponse<string>>(
URL_CONSTANTS.USERS.GENERATE_VERIFICATION_CODE,
body,
);
return res.data.data;
},
setPassword: async (body: SetPasswordPayload) => {
const res = await client.patch<ApiResponse<void>>(
URL_CONSTANTS.USERS.SET_PASSWORD,
body,
);
return res.data.data;
},
// The three calls below drive the unauthenticated forgot-password flow.
// Responses under /api/auth are *flattened* by the API's response
// interceptor ({ success, ...payload }), so there is no `.data.data` here.
requestPasswordReset: async (body: ForgotPasswordRequestPayload) => {
await client.post(URL_CONSTANTS.AUTH.FORGOT_PASSWORD_REQUEST, body);
},
verifyPasswordResetOtp: async (body: ForgotPasswordVerifyPayload) => {
const res = await client.post<ResetTicket>(
URL_CONSTANTS.AUTH.FORGOT_PASSWORD_VERIFY,
body,
);
return { userId: res.data.userId, verificationCode: res.data.verificationCode };
},
/**
* Validate a staff-issued reset link before showing the password form, and
* pick up the ticket it carries. Rejected links (expired, already spent) fail
* here rather than after the customer has typed a new password.
*/
resolveResetLink: async (body: ResolveResetLinkPayload) => {
const res = await client.post<ResetLinkAccount>(
URL_CONSTANTS.AUTH.FORGOT_PASSWORD_RESOLVE_LINK,
body,
);
return res.data;
},
/**
* Spend the reset ticket. Distinct from `setPassword` above, which the
* authenticated post-signup flow drives through `useAuth` — this one carries
* its own userId/verificationCode and never touches the session.
*/
resetPassword: async (body: SetPasswordPayload) => {
await client.patch(URL_CONSTANTS.USERS.SET_PASSWORD, body);
},
checkAvailability: async (params: CheckAvailabilityPayload) => {
const res = await client.get<CheckAvailabilityResponse>(
URL_CONSTANTS.USERS.CHECK_AVAILABILITY,
{ params },
);
return res.data;
},
sendOTP: async (body: OtpPayload) => {
const res = await client.post<ApiResponse<OtpResponse>>(
URL_CONSTANTS.OTP.SEND,
body,
);
return res.data.data;
},
verifyOTP: async (body: OtpPayload) => {
const res = await client.post<ApiResponse<OtpResponse>>(
URL_CONSTANTS.OTP.VERIFY,
body,
);
return res.data.data;
},
/**
* Change the signed-in user's password via IAM's own route: it verifies the
* old password with argon and owns the credential write (deactivating the
* previous one), so this app never touches password material.
*/
changePassword: async (body: ChangePasswordPayload) => {
await client.patch(URL_CONSTANTS.AUTH.CHANGE_PASSWORD, body);
},
// The three calls below manage the signed-in user's own account record
// (`/api/me`), which is what OTPs and SMS notifications are delivered to.
// Changing phone/email is OTP-gated server-side: the code goes to the NEW
// value, and the write only lands once it is verified.
sendContactOtp: async (body: SendContactOtpPayload) => {
const res = await client.post<ApiResponse<SendContactOtpResponse>>(
URL_CONSTANTS.ACCOUNT.CONTACT_OTP,
body,
);
return unwrap(res.data);
},
updateContact: async (body: UpdateContactPayload) => {
const res = await client.patch<ApiResponse<UpdateContactResponse>>(
URL_CONSTANTS.ACCOUNT.CONTACT,
body,
);
return unwrap(res.data);
},
updateAccountName: async (body: UpdateAccountNamePayload) => {
const res = await client.patch<ApiResponse<{ success: true }>>(
URL_CONSTANTS.ACCOUNT.NAME,
body,
);
return unwrap(res.data);
},
refreshToken: async () => {
const refreshTokenCookie = document.cookie
.split("; ")
.find((row) => row.startsWith("refresh-token="))
?.split("=")[1];
const res = await client.post<ApiResponse<LoginResponse>>(
URL_CONSTANTS.AUTH.REFRESH_TOKEN,
{ refreshToken: refreshTokenCookie },
);
return res.data.data;
},
logout: async () => {
const res = await client.patch<ApiResponse<void>>(
URL_CONSTANTS.AUTH.LOGOUT,
);
return res.data.data;
},
};