feat: complete login and user management flow
This commit is contained in:
+73
-6
@@ -1,3 +1,5 @@
|
||||
import { getSessionTenantId, readSession, type LoginSession } from './session';
|
||||
|
||||
type RequestOptions = RequestInit & {
|
||||
tenantId?: string;
|
||||
};
|
||||
@@ -7,8 +9,13 @@ export const DEFAULT_CLIENT_TENANT_ID = 'tenant-a';
|
||||
async function request<T>(path: string, options: RequestOptions = {}): Promise<T> {
|
||||
const headers = new Headers(options.headers);
|
||||
headers.set('Content-Type', 'application/json');
|
||||
if (options.tenantId) {
|
||||
headers.set('x-tenant-id', options.tenantId);
|
||||
const session = readSession();
|
||||
if (session?.accessToken) {
|
||||
headers.set('Authorization', `${session.tokenType} ${session.accessToken}`);
|
||||
}
|
||||
const tenantId = options.tenantId ?? (path.startsWith('/client') ? getSessionTenantId() : undefined);
|
||||
if (tenantId) {
|
||||
headers.set('x-tenant-id', tenantId);
|
||||
}
|
||||
const response = await fetch(`/api${path}`, { ...options, headers });
|
||||
if (!response.ok) {
|
||||
@@ -83,6 +90,40 @@ export type TenantOption = {
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type CaptchaResponse = {
|
||||
captchaId: string;
|
||||
challenge: string;
|
||||
expiresInSeconds: number;
|
||||
};
|
||||
|
||||
export type ManagedUser = {
|
||||
id: string;
|
||||
tenantId?: string | null;
|
||||
username: string;
|
||||
email?: string | null;
|
||||
phone?: string | null;
|
||||
displayName: string;
|
||||
status: string;
|
||||
failedLoginCount: number;
|
||||
lockedUntil?: string | null;
|
||||
lastLoginAt?: string | null;
|
||||
createdAt: string;
|
||||
tenant?: TenantOption | null;
|
||||
roles: Array<{ role: { code: string; name: string; scope: string } }>;
|
||||
};
|
||||
|
||||
export type UserPayload = {
|
||||
tenantId?: string | null;
|
||||
username?: string;
|
||||
email?: string | null;
|
||||
phone?: string | null;
|
||||
displayName: string;
|
||||
password?: string;
|
||||
status?: string;
|
||||
roleCode: 'platform_admin' | 'enterprise_admin';
|
||||
operatorId?: string;
|
||||
};
|
||||
|
||||
export type DashboardResponse = {
|
||||
taskCount: number;
|
||||
messageStatus: Array<{ status: string; _count: { _all: number }; _sum: { amountCents?: number | null; billingUnits?: number | null } }>;
|
||||
@@ -223,7 +264,18 @@ function withQuery(path: string, query: Record<string, string | number | undefin
|
||||
}
|
||||
|
||||
export const adminApi = {
|
||||
getCaptcha: () => request<CaptchaResponse>('/admin/auth/captcha'),
|
||||
login: (body: { login: string; password: string; captchaId: string; captchaText: string }) =>
|
||||
request<LoginSession>('/admin/auth/login', { method: 'POST', body: JSON.stringify(body) }),
|
||||
listTenants: () => request<TenantOption[]>('/admin/tenants'),
|
||||
listUsers: (query: { tenantId?: string; roleCode?: string } = {}) => request<ManagedUser[]>(withQuery('/admin/users', query)),
|
||||
createUser: (body: UserPayload) => request<ManagedUser>('/admin/users', { method: 'POST', body: JSON.stringify(body) }),
|
||||
updateUser: (id: string, body: Omit<UserPayload, 'password'>) => request<ManagedUser>(`/admin/users/${id}`, { method: 'PUT', body: JSON.stringify(body) }),
|
||||
changeUserStatus: (id: string, status: string, operatorId?: string) =>
|
||||
request<ManagedUser>(`/admin/users/${id}/status`, { method: 'POST', body: JSON.stringify({ status, operatorId }) }),
|
||||
deleteUser: (id: string, operatorId?: string) => request<ManagedUser>(`/admin/users/${id}`, { method: 'DELETE', body: JSON.stringify({ operatorId }) }),
|
||||
changeUserPassword: (id: string, password: string, operatorId?: string) =>
|
||||
request<ManagedUser>(`/admin/users/${id}/password`, { method: 'POST', body: JSON.stringify({ password, operatorId }) }),
|
||||
getDashboard: (tenantId?: string) => request<DashboardResponse>(withQuery('/admin/operations/dashboard/statistics', { tenantId })),
|
||||
listSystemLogs: (query: { tenantId?: string; keyword?: string; level?: string; module?: string; range?: string; page?: number; pageSize?: number }) =>
|
||||
request<OperationLogResponse>(withQuery('/admin/system-logs', query)),
|
||||
@@ -293,12 +345,27 @@ export const adminApi = {
|
||||
};
|
||||
|
||||
export const clientApi = {
|
||||
getDashboard: (tenantId = DEFAULT_CLIENT_TENANT_ID) =>
|
||||
getCaptcha: () => request<CaptchaResponse>('/client/auth/captcha'),
|
||||
login: (body: { login: string; password: string; captchaId: string; captchaText: string }) =>
|
||||
request<LoginSession>('/client/auth/login', { method: 'POST', body: JSON.stringify(body) }),
|
||||
listUsers: (tenantId = getSessionTenantId() ?? DEFAULT_CLIENT_TENANT_ID) =>
|
||||
request<ManagedUser[]>('/client/users', { tenantId }),
|
||||
createUser: (body: UserPayload, tenantId = getSessionTenantId() ?? DEFAULT_CLIENT_TENANT_ID) =>
|
||||
request<ManagedUser>('/client/users', { method: 'POST', tenantId, body: JSON.stringify(body) }),
|
||||
updateUser: (id: string, body: Omit<UserPayload, 'password'>, tenantId = getSessionTenantId() ?? DEFAULT_CLIENT_TENANT_ID) =>
|
||||
request<ManagedUser>(`/client/users/${id}`, { method: 'PUT', tenantId, body: JSON.stringify(body) }),
|
||||
changeUserStatus: (id: string, status: string, operatorId?: string, tenantId = getSessionTenantId() ?? DEFAULT_CLIENT_TENANT_ID) =>
|
||||
request<ManagedUser>(`/client/users/${id}/status`, { method: 'POST', tenantId, body: JSON.stringify({ status, operatorId }) }),
|
||||
deleteUser: (id: string, operatorId?: string, tenantId = getSessionTenantId() ?? DEFAULT_CLIENT_TENANT_ID) =>
|
||||
request<ManagedUser>(`/client/users/${id}`, { method: 'DELETE', tenantId, body: JSON.stringify({ operatorId }) }),
|
||||
changeUserPassword: (id: string, password: string, operatorId?: string, tenantId = getSessionTenantId() ?? DEFAULT_CLIENT_TENANT_ID) =>
|
||||
request<ManagedUser>(`/client/users/${id}/password`, { method: 'POST', tenantId, body: JSON.stringify({ password, operatorId }) }),
|
||||
getDashboard: (tenantId = getSessionTenantId() ?? DEFAULT_CLIENT_TENANT_ID) =>
|
||||
request<DashboardResponse>('/client/operations/dashboard', { tenantId }),
|
||||
listSystemLogs: (query: { keyword?: string; level?: string; module?: string; range?: string; page?: number; pageSize?: number }, tenantId = DEFAULT_CLIENT_TENANT_ID) =>
|
||||
listSystemLogs: (query: { keyword?: string; level?: string; module?: string; range?: string; page?: number; pageSize?: number }, tenantId = getSessionTenantId() ?? DEFAULT_CLIENT_TENANT_ID) =>
|
||||
request<OperationLogResponse>(withQuery('/client/operations/system-logs', query), { tenantId }),
|
||||
listTransactions: (tenantId = DEFAULT_CLIENT_TENANT_ID) =>
|
||||
listTransactions: (tenantId = getSessionTenantId() ?? DEFAULT_CLIENT_TENANT_ID) =>
|
||||
request<AccountTransaction[]>('/client/billing/transactions', { tenantId }),
|
||||
listOrders: (tenantId = DEFAULT_CLIENT_TENANT_ID) =>
|
||||
listOrders: (tenantId = getSessionTenantId() ?? DEFAULT_CLIENT_TENANT_ID) =>
|
||||
request<RechargeOrder[]>('/client/billing/orders', { tenantId }),
|
||||
};
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
export type Portal = 'admin' | 'client';
|
||||
|
||||
export type SessionUser = {
|
||||
id: string;
|
||||
tenantId?: string | null;
|
||||
tenantName?: string | null;
|
||||
username: string;
|
||||
email?: string | null;
|
||||
phone?: string | null;
|
||||
displayName: string;
|
||||
roles: string[];
|
||||
};
|
||||
|
||||
export type LoginSession = {
|
||||
accessToken: string;
|
||||
tokenType: string;
|
||||
portal: Portal;
|
||||
user: SessionUser;
|
||||
};
|
||||
|
||||
const sessionKey = 'cmpp-auth-session';
|
||||
|
||||
export function readSession(): LoginSession | null {
|
||||
try {
|
||||
const raw = window.localStorage.getItem(sessionKey);
|
||||
return raw ? JSON.parse(raw) as LoginSession : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function writeSession(session: LoginSession) {
|
||||
window.localStorage.setItem(sessionKey, JSON.stringify(session));
|
||||
}
|
||||
|
||||
export function clearSession() {
|
||||
window.localStorage.removeItem(sessionKey);
|
||||
}
|
||||
|
||||
export function getSessionTenantId() {
|
||||
return readSession()?.user.tenantId ?? undefined;
|
||||
}
|
||||
Reference in New Issue
Block a user