diff --git a/src/api/api.ts b/src/api/api.ts index b022963..0a7ff76 100644 --- a/src/api/api.ts +++ b/src/api/api.ts @@ -5,6 +5,29 @@ export const http = axios.create({ withCredentials: true }); +const baseUrl = process.env.NODE_ENV === "development" ? 'http://localhost:8080/v1' : 'https://api.unmei.nix13.pw/v1'; + +export const response = async (url: string, method: string, body?: any) => { + let bUrl = baseUrl; + if(baseUrl.endsWith('/')) bUrl = bUrl.slice(0, baseUrl.length-1); + if(url.startsWith('/')) url = url.slice(1, baseUrl.length); + + if(typeof body === 'object') body = JSON.stringify(body); + const res = await fetch(`${bUrl}/${url}`, { + method, body, + credentials: "include" + }); + + const json: ApiResponse = await res.json(); + + if(res.status === 429) return new Promise(res => { + setTimeout(res.bind(null, response(url, method, body)), 1500); + }); + if(json?.error) return Promise.reject(json.error_data); + if(!res.ok) return Promise.reject(res.statusText); + return Promise.resolve(json.data); +} + http.interceptors.response.use(undefined, (error: AxiosError) => { if(error.response) { if(error.response?.status === 429) { @@ -20,10 +43,15 @@ http.interceptors.response.use(undefined, (error: AxiosError) => { } }); -const get = (url: string, data?: object) => http.get(url, data).then(r => r?.data?.data); -const post = (url: string, data?: object) => http.post(url, data).then(r => r?.data.data); -const put = (url: string, data?: object) => http.put(url, data).then(r => r?.data.data); -const del = (url: string, data?: object) => http.delete(url, data).then(r => r?.data.data); +const get = (url: string, data?: object) => response(url, 'GET', data); +const post = (url: string, data?: object) => response(url, 'POST', data); +const put = (url: string, data?: object) => response(url, 'PUT', data); +const del = (url: string, data?: object) => response(url, 'DELETE', data); + +// const get = (url: string, data?: object) => http.get(url, data).then(r => r?.data?.data); +// const post = (url: string, data?: object) => http.post(url, data).then(r => r?.data.data); +// const put = (url: string, data?: object) => http.put(url, data).then(r => r?.data.data); +// const del = (url: string, data?: object) => http.delete(url, data).then(r => r?.data.data); const TranslateStatus: { [id: string]: string } = { planned: 'Запланировано', diff --git a/src/api/auth.ts b/src/api/auth.ts new file mode 100644 index 0000000..a1102b5 --- /dev/null +++ b/src/api/auth.ts @@ -0,0 +1,3 @@ +import {post} from "./api"; + +export const restore = (token: string, new_password: string) => post('auth/restore', { token, new_password }); \ No newline at end of file diff --git a/src/api/errors.ts b/src/api/errors.ts index b8637d7..767275b 100644 --- a/src/api/errors.ts +++ b/src/api/errors.ts @@ -1,8 +1,10 @@ const errors: { [key: number]: string } = { 1: 'Неверный логин и/или пароль!', 2: 'Проверка ReCaptcha не пройдена!', + 4: 'Нет доступа!', 5: 'Аккаунт уже активирован! Вы будете перенаправлены на главную через 3 секунды.', 6: 'Токен истек!', + 8: 'Новый и старый пароли совпадают!', 100: 'Не найдено!' }; diff --git a/src/api/novels.ts b/src/api/novels.ts index 2fe650d..00e25a3 100644 --- a/src/api/novels.ts +++ b/src/api/novels.ts @@ -2,7 +2,9 @@ import { get, post, put, del } from "./api"; export const fetchNovels = (orderBy?: string) => get(orderBy ? `novels?sort=${orderBy}` : 'novels'); +export const createNovel = (novel: NovelType) => post('novels', { ...novel }); export const fetchNovel = (id: number) => get(`novels/${id}`); +export const updateNovel = (id: number, novel: NovelType) => put(`novels/${id}`, { ...novel }); export const fetchNovelCharacters = (id: number) => get(`novels/${id}/characters`); export const fetchNovelGenres = (id: number) => get(`novels/${id}/genres`); diff --git a/src/api/users.ts b/src/api/users.ts index e482788..bc0536f 100644 --- a/src/api/users.ts +++ b/src/api/users.ts @@ -1,6 +1,6 @@ import {get, post, put} from "./api"; -export const fetchUsers = () => get('/users'); +export const fetchUsers = () => get('users'); export const fetchUser = (id: number) => get(`users/${id}`); export const updateUser = (user: UserType) => put(`users/${user.id}`, { user }) @@ -8,7 +8,11 @@ export const updateUser = (user: UserType) => put(`users/${user.id}`, { user }) export const fetchCurrentUser = () => get('users/current'); export const fetchUserNovels = (id: number) => get(`users/${id}/novels`); -export const login = (login: string, password: string) => post('auth/login', { login, password }); export const registerUser = (login: string, password: string, email: string, recaptcha: string) => post('auth/register', { login, password, email, recaptcha }); +export const login = (login: string, password: string) => post('auth/login', { login, password }); +export const userLogout = () => post('auth/logout'); + export const activateAccount = (token: string) => post('auth/activate', { token }); -export const userLogout = () => post('auth/logout'); \ No newline at end of file +export const generateActivateLink = () => post('auth/activateToken'); + +export const generateRestoreLink = (email: string, recaptcha: string) => post('auth/restoreToken', { email, recaptcha }); \ No newline at end of file diff --git a/src/components/App/App.sass b/src/components/App/App.sass index 4e36141..8242e4e 100644 --- a/src/components/App/App.sass +++ b/src/components/App/App.sass @@ -115,7 +115,14 @@ input z-index: 100000 padding: 1rem -.Error +.Successful, .Error border: var(--link-color) 1px solid - padding: 1rem + padding: .5rem border-radius: 3px + margin-bottom: 1rem + +.Error + background-color: var(--error-bg-color) + +.Successful + background-color: var(--success-bg-color) diff --git a/src/components/App/App.tsx b/src/components/App/App.tsx index 626b8c2..cc733e6 100644 --- a/src/components/App/App.tsx +++ b/src/components/App/App.tsx @@ -23,6 +23,9 @@ import ActivateAccount from "../ActivateAccount"; import AllNews from "../News/AllNews"; import Post from "../News/Post"; import AdminPanel from "../AdminPanel/AdminPanel"; +import PasswordRestoreGenerate from "../PasswordRestore/PasswordRestoreGenerate"; +import PasswordRestore from "../PasswordRestore/PasswordRestore"; +import Users from "../Users/Users"; type Props = { notifications: React.ReactNode[] @@ -47,8 +50,8 @@ class App extends React.Component { }; const { setUser } = this.props; - fetchCurrentUser().then(currentUser => setUser(currentUser)).catch((err: ApiError) => { - if(err.code !== 3) console.error(err.text); + fetchCurrentUser().then(setUser).catch((err: ApiError) => { + if(err.code !== 3 && err.text) console.error(err.text); }); const theme = localStorage.getItem('theme'); @@ -66,6 +69,8 @@ class App extends React.Component { hideRegisterModal = () => this.setState({ registerModalVisible: false }); render() { + const { loginModalVisible, registerModalVisible } = this.state; + return ( <> @@ -86,6 +91,7 @@ class App extends React.Component { + @@ -93,14 +99,20 @@ class App extends React.Component { + + + - {/**/} - {this.state.loginModalVisible && } - {this.state.registerModalVisible && } + {loginModalVisible && ( + + )} + {registerModalVisible && ( + + )}