diff options
Diffstat (limited to 'Timeline/ClientApp/src/app/data/user.ts')
-rw-r--r-- | Timeline/ClientApp/src/app/data/user.ts | 224 |
1 files changed, 0 insertions, 224 deletions
diff --git a/Timeline/ClientApp/src/app/data/user.ts b/Timeline/ClientApp/src/app/data/user.ts deleted file mode 100644 index 8f787478..00000000 --- a/Timeline/ClientApp/src/app/data/user.ts +++ /dev/null @@ -1,224 +0,0 @@ -import axios, { AxiosError } from 'axios'; -import { useState, useEffect } from 'react'; -import { BehaviorSubject, Observable } from 'rxjs'; - -import { apiBaseUrl } from '../config'; -import { extractErrorCode } from './common'; -import { pushAlert } from '../common/alert-service'; -import { i18nPromise } from '../i18n'; -import { UiLogicError } from '../common'; - -export interface UserAuthInfo { - username: string; - administrator: boolean; -} - -export interface User { - username: string; - administrator: boolean; - nickname: string; - _links: { - avatar: string; - timeline: string; - }; -} - -export interface UserWithToken extends User { - token: string; -} - -interface CreateTokenRequest { - username: string; - password: string; -} - -interface CreateTokenResponse { - token: string; - user: User; -} - -interface VerifyTokenRequest { - token: string; -} - -interface VerifyTokenResponse { - user: User; -} - -export type LoginCredentials = CreateTokenRequest; - -const userSubject = new BehaviorSubject<UserWithToken | null | undefined>( - undefined -); - -export const user$: Observable<UserWithToken | null | undefined> = userSubject; - -export function getCurrentUser(): UserWithToken | null | undefined { - return userSubject.value; -} - -const kCreateTokenUrl = '/token/create'; -const kVerifyTokenUrl = '/token/verify'; -const createTokenUrl = apiBaseUrl + kCreateTokenUrl; -const verifyTokenUrl = apiBaseUrl + kVerifyTokenUrl; - -function verifyToken(token: string): Promise<User> { - return axios - .post<VerifyTokenResponse>(verifyTokenUrl, { - token: token, - } as VerifyTokenRequest) - .then((res) => res.data.user); -} - -const TOKEN_STORAGE_KEY = 'token'; - -export function checkUserLoginState(): Promise<UserWithToken | null> { - if (getCurrentUser() !== undefined) - throw new UiLogicError("Already checked user. Can't check twice."); - - const savedToken = window.localStorage.getItem(TOKEN_STORAGE_KEY); - if (savedToken) { - return verifyToken(savedToken) - .then( - (u) => { - const user: UserWithToken = { - ...u, - token: savedToken, - }; - void i18nPromise.then((t) => { - pushAlert({ - type: 'success', - message: t('user.welcomeBack'), - }); - }); - return user; - }, - (e: AxiosError) => { - if (e.response != null) { - window.localStorage.removeItem(TOKEN_STORAGE_KEY); - void i18nPromise.then((t) => { - pushAlert({ - type: 'danger', - message: t('user.verifyTokenFailed'), - }); - }); - } else { - void i18nPromise.then((t) => { - pushAlert({ - type: 'danger', - message: t('user.verifyTokenFailedNetwork'), - }); - }); - } - - return null; - } - ) - .then((u) => { - userSubject.next(u); - return u; - }); - } - userSubject.next(null); - return Promise.resolve(null); -} - -export class BadCredentialError { - constructor(public innerError: Error) {} - - message = 'login.badCredential'; -} - -export function userLogin( - credentials: LoginCredentials, - rememberMe: boolean -): Promise<UserWithToken> { - if (getCurrentUser()) { - throw new UiLogicError('Already login.'); - } - return axios - .post<CreateTokenResponse>(createTokenUrl, { ...credentials, expire: 30 }) - .catch((e: AxiosError) => { - if (extractErrorCode(e) === 11010101) { - throw new BadCredentialError(e); - } - throw e; - }) - .then((res) => { - const body = res.data; - const token = body.token; - if (rememberMe) { - window.localStorage.setItem(TOKEN_STORAGE_KEY, token); - } - const user = { - ...body.user, - token, - }; - userSubject.next(user); - return user; - }); -} - -export function userLogout(): void { - if (getCurrentUser() === undefined) { - throw new UiLogicError('Please check user first.'); - } - if (getCurrentUser() === null) { - throw new UiLogicError('No login.'); - } - window.localStorage.removeItem(TOKEN_STORAGE_KEY); - userSubject.next(null); -} - -export function useOptionalUser(): UserWithToken | null | undefined { - const [user, setUser] = useState<UserWithToken | null | undefined>( - userSubject.value - ); - useEffect(() => { - const sub = user$.subscribe((u) => setUser(u)); - return () => { - sub.unsubscribe(); - }; - }); - return user; -} - -export function useUser(): UserWithToken | null { - const [user, setUser] = useState<UserWithToken | null>(() => { - const initUser = userSubject.value; - if (initUser === undefined) { - throw new UiLogicError( - "This is a logic error in user module. Current user can't be undefined in useUser." - ); - } - return initUser; - }); - useEffect(() => { - const sub = user$.subscribe((u) => { - if (u === undefined) { - throw new UiLogicError( - "This is a logic error in user module. User emitted can't be undefined later." - ); - } - setUser(u); - }); - return () => { - sub.unsubscribe(); - }; - }); - return user; -} - -export function useUserLoggedIn(): UserWithToken { - const user = useUser(); - if (user == null) { - throw new UiLogicError('You assert user has logged in but actually not.'); - } - return user; -} - -export function fetchUser(username: string): Promise<User> { - return axios - .get<User>(`${apiBaseUrl}/users/${username}`) - .then((res) => res.data); -} |