mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-16 17:46:18 +08:00
feat: cleanup fetcher for prepare refactor
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
"axios": "^1.2.0",
|
||||
"encoding": "^0.1.13",
|
||||
"firebase": "^9.13.0",
|
||||
"ky": "^0.33.0",
|
||||
"swr": "^2.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import {
|
||||
signInWithPopup,
|
||||
} from 'firebase/auth';
|
||||
import type { User } from 'firebase/auth';
|
||||
import { login } from './sdks';
|
||||
import { setToken } from './request';
|
||||
|
||||
/**
|
||||
* firebaseConfig reference: https://firebase.google.com/docs/web/setup#add_firebase_to_your_app
|
||||
@@ -32,8 +34,14 @@ const signIn = (email: string, password: string) => {
|
||||
};
|
||||
|
||||
const googleAuthProvider = new GoogleAuthProvider();
|
||||
export const signInWithGoogle = () => {
|
||||
return signInWithPopup(firebaseAuth, googleAuthProvider);
|
||||
export const signInWithGoogle = async () => {
|
||||
const user = await signInWithPopup(firebaseAuth, googleAuthProvider);
|
||||
const idToken = await user.user.getIdToken();
|
||||
const token = await login({ token: idToken, type: 'Google' });
|
||||
setToken({
|
||||
accessToken: token.token,
|
||||
refreshToken: token.refresh,
|
||||
});
|
||||
};
|
||||
|
||||
export const onAuthStateChanged = (callback: (user: User | null) => void) => {
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
export class ServiceError extends Error {
|
||||
public message: string;
|
||||
public code: string;
|
||||
constructor(code: string, message: string) {
|
||||
super(message);
|
||||
this.message = message;
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,8 @@
|
||||
import axios from 'axios';
|
||||
import { setAuthorization } from './request';
|
||||
import { handleResponseError, handleResponse } from './response';
|
||||
import { getToken, isAccessTokenExpired, refreshToken } from './token';
|
||||
|
||||
declare module 'axios' {
|
||||
interface AxiosRequestConfig {
|
||||
/**
|
||||
* If true, request will send with Authorization header.
|
||||
*/
|
||||
withAuthorization?: boolean;
|
||||
/**
|
||||
* If true, indicate this request is refreshing token.
|
||||
*/
|
||||
@@ -15,9 +10,52 @@ declare module 'axios' {
|
||||
}
|
||||
}
|
||||
|
||||
export const request = axios.create({
|
||||
withAuthorization: true,
|
||||
});
|
||||
export const request = axios.create();
|
||||
|
||||
request.interceptors.request.use(setAuthorization);
|
||||
request.interceptors.response.use(handleResponse, handleResponseError);
|
||||
request.interceptors.request.use(async config => {
|
||||
if (!config.headers) {
|
||||
config.headers = {};
|
||||
}
|
||||
|
||||
const token = getToken();
|
||||
if (token) {
|
||||
if (isAccessTokenExpired(token.accessToken)) {
|
||||
await refreshToken();
|
||||
}
|
||||
if (!config.headers) {
|
||||
config.headers = {};
|
||||
}
|
||||
config.headers['Authorization'] = token.accessToken;
|
||||
}
|
||||
|
||||
return config;
|
||||
});
|
||||
request.interceptors.response.use(
|
||||
response => {
|
||||
const { status, data } = response;
|
||||
if (status === 200) {
|
||||
if (data.error) {
|
||||
// TODO - common error handling
|
||||
throw new Error(data.error.message, { cause: data.error.code });
|
||||
}
|
||||
return response;
|
||||
}
|
||||
return response;
|
||||
},
|
||||
async error => {
|
||||
const { response, config, message } = error;
|
||||
const status = response?.status;
|
||||
|
||||
if (status === 401) {
|
||||
if (!config) {
|
||||
throw new Error(message, { cause: status.toString() });
|
||||
}
|
||||
if (config.refreshTokenRequest) {
|
||||
throw new Error(message, { cause: status.toString() });
|
||||
}
|
||||
await refreshToken();
|
||||
return request({ ...config, refreshTokenRequest: true });
|
||||
}
|
||||
return response;
|
||||
}
|
||||
);
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import type { AccessTokenMessage } from './types';
|
||||
import { AccessTokenMessage } from './token';
|
||||
|
||||
type Callback = (user: AccessTokenMessage | null) => void;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
export { request } from './axios';
|
||||
export { ServiceError } from './ServiceError';
|
||||
export { setToken, getToken, authorizationEvent } from './request';
|
||||
export type { AccessTokenMessage } from './request';
|
||||
export type { AccessTokenMessage } from './token';
|
||||
export { authorizationEvent, getToken, setToken } from './token';
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './authorization';
|
||||
export type { AccessTokenMessage } from './types';
|
||||
@@ -1,8 +0,0 @@
|
||||
export interface AccessTokenMessage {
|
||||
create_at: number;
|
||||
exp: number;
|
||||
email: string;
|
||||
id: string;
|
||||
name: string;
|
||||
avatar_url: string;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export {
|
||||
setAuthorization,
|
||||
setToken,
|
||||
getToken,
|
||||
authorizationEvent,
|
||||
} from './authorization';
|
||||
export type { AccessTokenMessage } from './authorization';
|
||||
@@ -1,34 +0,0 @@
|
||||
import type { AxiosResponse, AxiosError } from 'axios';
|
||||
import { ServiceError } from '../ServiceError';
|
||||
import { refreshToken } from '../request/authorization';
|
||||
import { request } from '../axios';
|
||||
|
||||
export async function handleResponseError(error: AxiosError<any, any>) {
|
||||
const { response, config, message } = error;
|
||||
const status = response?.status;
|
||||
|
||||
if (status === 401) {
|
||||
if (!config) {
|
||||
throw new ServiceError(message, status.toString());
|
||||
}
|
||||
if (config.refreshTokenRequest) {
|
||||
throw new ServiceError(message, status.toString());
|
||||
}
|
||||
await refreshToken();
|
||||
return request({ ...config, refreshTokenRequest: true });
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
export async function handleResponse(response: AxiosResponse<any, any>) {
|
||||
const { status, data } = response;
|
||||
if (status === 200) {
|
||||
if (data.error) {
|
||||
// TODO - common error handling
|
||||
const error = new ServiceError(data.error.message, data.error.code);
|
||||
throw error;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export { handleResponseError, handleResponse } from './handle-error';
|
||||
+39
-68
@@ -1,44 +1,17 @@
|
||||
import type { AxiosRequestConfig } from 'axios';
|
||||
import { login } from '../../../sdks';
|
||||
import { AuthorizationEvent } from './events';
|
||||
import type { AccessTokenMessage } from './types';
|
||||
import { login } from '../sdks';
|
||||
|
||||
export interface AccessTokenMessage {
|
||||
create_at: number;
|
||||
exp: number;
|
||||
email: string;
|
||||
id: string;
|
||||
name: string;
|
||||
avatar_url: string;
|
||||
}
|
||||
|
||||
const TOKEN_KEY = 'affine_token';
|
||||
|
||||
export const authorizationEvent = new AuthorizationEvent();
|
||||
authorizationEvent.triggerChange(
|
||||
parseAccessToken(getToken()?.accessToken || '')
|
||||
);
|
||||
|
||||
interface Token {
|
||||
accessToken: string;
|
||||
refreshToken: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* set null to clear token
|
||||
*/
|
||||
export function setToken(token: Token | null): void {
|
||||
if (token === null) {
|
||||
window.localStorage.removeItem(TOKEN_KEY);
|
||||
authorizationEvent.triggerChange(null);
|
||||
return;
|
||||
}
|
||||
window.localStorage.setItem(TOKEN_KEY, JSON.stringify(token));
|
||||
authorizationEvent.triggerChange(parseAccessToken(token.accessToken));
|
||||
}
|
||||
|
||||
export function getToken(): {
|
||||
accessToken: string;
|
||||
refreshToken: string;
|
||||
} | null {
|
||||
try {
|
||||
return JSON.parse(window.localStorage.getItem(TOKEN_KEY) || '');
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function b64DecodeUnicode(str: string) {
|
||||
// Going backwards: from byte stream, to percent-encoding, to original string.
|
||||
return decodeURIComponent(
|
||||
@@ -64,7 +37,7 @@ function parseAccessToken(token: string): AccessTokenMessage | null {
|
||||
}
|
||||
}
|
||||
|
||||
function isAccessTokenExpired(token: string) {
|
||||
export function isAccessTokenExpired(token: string) {
|
||||
const message = parseAccessToken(token);
|
||||
if (!message) {
|
||||
return true;
|
||||
@@ -72,41 +45,39 @@ function isAccessTokenExpired(token: string) {
|
||||
return Date.now() - message.create_at > message.exp;
|
||||
}
|
||||
|
||||
async function isLoggedIn(): Promise<boolean> {
|
||||
const token = getToken();
|
||||
if (!token) {
|
||||
return false;
|
||||
export function getToken(): {
|
||||
accessToken: string;
|
||||
refreshToken: string;
|
||||
} | null {
|
||||
try {
|
||||
return JSON.parse(window.localStorage.getItem(TOKEN_KEY) || '');
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
return isAccessTokenExpired(token.accessToken);
|
||||
}
|
||||
|
||||
export const authorizationEvent = new AuthorizationEvent();
|
||||
authorizationEvent.triggerChange(
|
||||
parseAccessToken(getToken()?.accessToken || '')
|
||||
);
|
||||
|
||||
interface Token {
|
||||
accessToken: string;
|
||||
refreshToken: string;
|
||||
}
|
||||
|
||||
export function setToken(token: Token | null): void {
|
||||
if (token === null) {
|
||||
window.localStorage.removeItem(TOKEN_KEY);
|
||||
authorizationEvent.triggerChange(null);
|
||||
return;
|
||||
}
|
||||
window.localStorage.setItem(TOKEN_KEY, JSON.stringify(token));
|
||||
authorizationEvent.triggerChange(parseAccessToken(token.accessToken));
|
||||
}
|
||||
|
||||
let refreshingToken: ReturnType<typeof login> | undefined;
|
||||
|
||||
export async function setAuthorization(config: AxiosRequestConfig<unknown>) {
|
||||
if (!config.headers) {
|
||||
config.headers = {};
|
||||
}
|
||||
|
||||
if (config.withAuthorization) {
|
||||
let token = getToken();
|
||||
if (!token) {
|
||||
throw new Error('No authorization token.');
|
||||
}
|
||||
if (isAccessTokenExpired(token.accessToken)) {
|
||||
await refreshToken();
|
||||
token = getToken();
|
||||
}
|
||||
if (!token) {
|
||||
throw new Error('No authorization token.');
|
||||
}
|
||||
if (!config.headers) {
|
||||
config.headers = {};
|
||||
}
|
||||
config.headers['Authorization'] = token.accessToken;
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
export const refreshToken = async () => {
|
||||
let token = getToken();
|
||||
if (!token) {
|
||||
@@ -32,7 +32,7 @@ export async function login(params: LoginParams): Promise<LoginResponse> {
|
||||
method: 'POST',
|
||||
data: params,
|
||||
withAuthorization: false,
|
||||
});
|
||||
} as any);
|
||||
return data.data;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user