用户静默注册
This commit is contained in:
+134
-22
@@ -7,36 +7,148 @@ export const BRAND = {
|
||||
};
|
||||
|
||||
export const apiBase = '/api/v1';
|
||||
const CLIENT_APP = 'USER_H5';
|
||||
|
||||
export async function request<T>(
|
||||
clientApp: string,
|
||||
path: string,
|
||||
options: RequestInit = {},
|
||||
): Promise<T> {
|
||||
const token = localStorage.getItem('accessToken');
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Client-App': clientApp,
|
||||
...(options.headers as Record<string, string>),
|
||||
};
|
||||
if (token) headers.Authorization = `Bearer ${token}`;
|
||||
export type UserProfile = {
|
||||
id: string;
|
||||
userNo: string;
|
||||
phone: string | null;
|
||||
phoneVerified: boolean;
|
||||
nickname: string | null;
|
||||
avatarUrl: string | null;
|
||||
hasWechat: boolean;
|
||||
};
|
||||
|
||||
const res = await fetch(`${apiBase}${path}`, { ...options, headers });
|
||||
const json = await res.json();
|
||||
if (json.code !== 0) throw new Error(json.message || '请求失败');
|
||||
return json.data as T;
|
||||
export type SessionPayload = {
|
||||
accessToken: string;
|
||||
refreshToken: string;
|
||||
deviceKey?: string;
|
||||
phoneVerified: boolean;
|
||||
user?: UserProfile;
|
||||
};
|
||||
|
||||
const DEVICE_KEY = 'deviceKey';
|
||||
const ACCESS_TOKEN = 'accessToken';
|
||||
const REFRESH_TOKEN = 'refreshToken';
|
||||
|
||||
export function getDeviceKey() {
|
||||
return localStorage.getItem(DEVICE_KEY);
|
||||
}
|
||||
|
||||
export function saveAuth(data: { accessToken: string; refreshToken?: string }) {
|
||||
localStorage.setItem('accessToken', data.accessToken);
|
||||
if (data.refreshToken) localStorage.setItem('refreshToken', data.refreshToken);
|
||||
export function saveSession(data: SessionPayload) {
|
||||
localStorage.setItem(ACCESS_TOKEN, data.accessToken);
|
||||
localStorage.setItem(REFRESH_TOKEN, data.refreshToken);
|
||||
if (data.deviceKey) localStorage.setItem(DEVICE_KEY, data.deviceKey);
|
||||
}
|
||||
|
||||
export function saveAuth(data: { accessToken: string; refreshToken?: string; deviceKey?: string }) {
|
||||
localStorage.setItem(ACCESS_TOKEN, data.accessToken);
|
||||
if (data.refreshToken) localStorage.setItem(REFRESH_TOKEN, data.refreshToken);
|
||||
if (data.deviceKey) localStorage.setItem(DEVICE_KEY, data.deviceKey);
|
||||
}
|
||||
|
||||
export function clearAuth() {
|
||||
localStorage.removeItem('accessToken');
|
||||
localStorage.removeItem('refreshToken');
|
||||
localStorage.removeItem(ACCESS_TOKEN);
|
||||
localStorage.removeItem(REFRESH_TOKEN);
|
||||
}
|
||||
|
||||
export function isLoggedIn() {
|
||||
return !!localStorage.getItem('accessToken');
|
||||
return !!localStorage.getItem(ACCESS_TOKEN);
|
||||
}
|
||||
|
||||
async function rawRequest<T>(
|
||||
path: string,
|
||||
options: RequestInit = {},
|
||||
token?: string | null,
|
||||
): Promise<T> {
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Client-App': CLIENT_APP,
|
||||
...(options.headers as Record<string, string>),
|
||||
};
|
||||
const authToken = token ?? localStorage.getItem(ACCESS_TOKEN);
|
||||
if (authToken) headers.Authorization = `Bearer ${authToken}`;
|
||||
|
||||
const res = await fetch(`${apiBase}${path}`, { ...options, headers });
|
||||
const json = await res.json();
|
||||
if (json.code !== 0) {
|
||||
const err = new Error(json.message || '请求失败') as Error & { status?: number };
|
||||
err.status = json.code;
|
||||
throw err;
|
||||
}
|
||||
return json.data as T;
|
||||
}
|
||||
|
||||
export async function request<T>(
|
||||
_clientApp: string,
|
||||
path: string,
|
||||
options: RequestInit = {},
|
||||
): Promise<T> {
|
||||
return rawRequest<T>(path, options);
|
||||
}
|
||||
|
||||
export async function bootstrapSession(): Promise<SessionPayload> {
|
||||
const deviceKey = getDeviceKey();
|
||||
const data = await rawRequest<SessionPayload>(
|
||||
'/auth/session/bootstrap',
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify(deviceKey ? { deviceKey } : {}),
|
||||
},
|
||||
null,
|
||||
);
|
||||
saveSession(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function refreshSession(): Promise<SessionPayload | null> {
|
||||
const refreshToken = localStorage.getItem(REFRESH_TOKEN);
|
||||
if (!refreshToken) return null;
|
||||
try {
|
||||
const data = await rawRequest<SessionPayload>(
|
||||
'/auth/token/refresh',
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ refreshToken }),
|
||||
},
|
||||
null,
|
||||
);
|
||||
saveSession(data);
|
||||
return data;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function ensureSession(): Promise<SessionPayload> {
|
||||
if (isLoggedIn()) {
|
||||
try {
|
||||
const me = await rawRequest<UserProfile>('/auth/me');
|
||||
return {
|
||||
accessToken: localStorage.getItem(ACCESS_TOKEN) ?? '',
|
||||
refreshToken: localStorage.getItem(REFRESH_TOKEN) ?? '',
|
||||
deviceKey: getDeviceKey() ?? undefined,
|
||||
phoneVerified: !!me.phoneVerified,
|
||||
user: me,
|
||||
};
|
||||
} catch (e) {
|
||||
const err = e as Error & { status?: number };
|
||||
if (err.status === 401) {
|
||||
clearAuth();
|
||||
} else {
|
||||
const refreshed = await refreshSession();
|
||||
if (refreshed) return refreshed;
|
||||
}
|
||||
}
|
||||
}
|
||||
return bootstrapSession();
|
||||
}
|
||||
|
||||
export async function bindPhone(phone: string, code: string): Promise<SessionPayload> {
|
||||
const data = await rawRequest<SessionPayload>('/auth/phone/bind', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ phone, code }),
|
||||
});
|
||||
saveSession(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user