feat: implement login functionality with error handling and session management
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
import { callAuthFunction } from "@/lib/api/http"
|
||||
|
||||
export interface AuthUser {
|
||||
id: string
|
||||
fullName: string | null
|
||||
userName: string | null
|
||||
email: string | null
|
||||
mobileNumber: string | null
|
||||
emailVerified: boolean
|
||||
mobileNumberVerified: boolean
|
||||
isMfaEnabled: boolean
|
||||
roleId: string | null
|
||||
userTypeId: string | null
|
||||
}
|
||||
|
||||
export interface LoginResult {
|
||||
accessToken: string
|
||||
refreshToken: string
|
||||
expiresIn: number
|
||||
user: AuthUser
|
||||
}
|
||||
|
||||
/** POST /api/loginUser (forced functionName "loginUser" server-side). */
|
||||
export function loginUser(params: { identifier: string; password: string; deviceName?: string }) {
|
||||
return callAuthFunction<LoginResult>("/api/loginUser", "loginUser", {
|
||||
identifier: params.identifier,
|
||||
password: params.password,
|
||||
deviceName: params.deviceName ?? (typeof navigator !== "undefined" ? navigator.userAgent : "Unknown Device"),
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// Thin client for the AuthHex service's function-dispatch envelope
|
||||
// (POST { functionName, payload, reference } -> { statusCode, success, message, data }).
|
||||
// See D:\HexDive\ERP_Auth_Service\API_DOCUMENTATION.md for the full contract.
|
||||
|
||||
const AUTH_API_BASE_URL =
|
||||
process.env.NEXT_PUBLIC_AUTH_API_BASE_URL?.replace(/\/$/, "") ?? "https://localhost:7111"
|
||||
|
||||
interface ApiEnvelope<T> {
|
||||
statusCode: number
|
||||
success: boolean
|
||||
message: string | null
|
||||
data: T | null
|
||||
}
|
||||
|
||||
export class AuthApiError extends Error {
|
||||
statusCode: number
|
||||
constructor(message: string, statusCode: number) {
|
||||
super(message)
|
||||
this.name = "AuthApiError"
|
||||
this.statusCode = statusCode
|
||||
}
|
||||
}
|
||||
|
||||
function makeReference() {
|
||||
return typeof crypto !== "undefined" && "randomUUID" in crypto
|
||||
? crypto.randomUUID()
|
||||
: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls one function on an AuthHex route (e.g. POST /api/loginUser).
|
||||
* The service's own `success` flag is not reliable on error paths (it defaults
|
||||
* to true even when statusCode is 4xx/5xx), so failure is judged from
|
||||
* `!response.ok || body.statusCode !== 200`, not from `body.success`.
|
||||
*/
|
||||
export async function callAuthFunction<T>(
|
||||
route: string,
|
||||
functionName: string,
|
||||
payload: Record<string, unknown> = {}
|
||||
): Promise<T> {
|
||||
let response: Response
|
||||
try {
|
||||
response = await fetch(`${AUTH_API_BASE_URL}${route}`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ functionName, payload, reference: makeReference() }),
|
||||
})
|
||||
} catch {
|
||||
throw new AuthApiError(
|
||||
"Could not reach the authentication service. Check your connection and try again.",
|
||||
0
|
||||
)
|
||||
}
|
||||
|
||||
let body: ApiEnvelope<T> | null = null
|
||||
try {
|
||||
body = (await response.json()) as ApiEnvelope<T>
|
||||
} catch {
|
||||
// non-JSON body — body stays null, handled below
|
||||
}
|
||||
|
||||
if (!response.ok || !body || body.statusCode !== 200) {
|
||||
throw new AuthApiError(body?.message || `Request failed (${response.status})`, body?.statusCode ?? response.status)
|
||||
}
|
||||
|
||||
return body.data as T
|
||||
}
|
||||
Reference in New Issue
Block a user