1
0
Fork 0
mirror of https://github.com/muerwre/vault-frontend.git synced 2025-04-24 20:36:40 +07:00
This commit is contained in:
muerwre 2019-08-06 18:21:19 +07:00
parent 56d714d655
commit e0bba90d2e
12 changed files with 264 additions and 89 deletions

View file

@ -1,34 +0,0 @@
{
"extends": "airbnb",
"parser": "babel-eslint",
"plugins": [
"react",
"jsx-a11y",
"import"
],
"rules": {
"comma-dangle": 0,
"no-restricted-syntax": 1,
"new-cap": 1,
"no-continue": 1,
"no-underscore-dangle": 1,
"global-require": 1,
"react/no-multi-comp": 1,
"react/jsx-filename-extension": 0,
"camelcase": 1,
"import/no-unresolved": 1,
"import/prefer-default-export": 1,
"import/extensions": 1,
"no-return-assign": 1,
"max-len": 1,
"jsx-a11y/no-static-element-interactions": 0,
"jsx-a11y/click-events-have-key-events": 0,
"jsx-a11y/interactive-supports-focus": 0
},
"globals": {
"document": false,
"window": false,
"HTMLInputElement": false,
"HTMLDivElement": false
}
}

107
.eslintrc.js Normal file
View file

@ -0,0 +1,107 @@
// module.exports = {
// extends: ['airbnb', 'airbnb-base', 'plugin:@typescript-eslint/recommended'],
// // "parser": "babel-eslint",
// parser: '@typescript-eslint/parser',
// parserOptions: {
// ecmaFeatures: {
// jsx: true
// },
// project: './tsconfig.json'
// },
// plugins: ['@typescript-eslint', 'react', 'jsx-a11y', 'import', 'react-hooks'],
// rules: {
// indent: ['error', 2],
// '@typescript-eslint/indent': ['error', 2],
// 'comma-dangle': 0,
// 'no-restricted-syntax': 1,
// 'new-cap': 1,
// 'no-continue': 1,
// 'no-underscore-dangle': 1,
// 'global-require': 1,
// 'react/no-multi-comp': 1,
// 'react/jsx-filename-extension': 0,
// camelcase: 1,
// 'import/no-unresolved': 1,
// 'import/prefer-default-export': 1,
// 'import/extensions': 1,
// 'no-return-assign': 1,
// 'max-len': 1,
// 'jsx-a11y/no-static-element-interactions': 0,
// 'jsx-a11y/click-events-have-key-events': 0,
// 'jsx-a11y/interactive-supports-focus': 0,
// 'react-hooks/rules-of-hooks': 'error',
// 'react-hooks/exhaustive-deps': 'warn',
// 'no-nested-ternary': 1
// },
// globals: {
// document: false,
// window: false,
// HTMLInputElement: false,
// HTMLDivElement: false
// }
// };
module.exports = {
extends: ['plugin:@typescript-eslint/recommended'],
plugins: ['import', '@typescript-eslint'],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: {
jsx: true,
},
project: './tsconfig.json',
},
globals: {
Reactotron: true,
},
rules: {
curly: ['error', 'all'],
'valid-jsdoc': 'error',
'linebreak-style': 'off',
'no-console': 'off',
'object-curly-newline': 'off',
'no-unused-expressions': 'off',
'no-unused-vars': 'off',
'prefer-destructuring': [
'error',
{
VariableDeclarator: {
object: true,
},
},
],
'function-paren-newline': ['error', 'consistent'],
'lines-between-class-members': ['error', 'always', { exceptAfterSingleLine: true }],
'no-multiple-empty-lines': ['error', { max: 1, maxBOF: 0, maxEOF: 0 }],
'eslint-comments/no-unlimited-disable': 'off',
'import/no-unresolved': 'off',
'import/extensions': 'off',
'import/prefer-default-export': 'off',
'prettier/prettier': [
'error',
{
singleQuote: true,
parser: 'flow',
trailingComma: 'all',
printWidth: 100,
},
'@format',
],
'@typescript-eslint/indent': ['error', 2],
'@typescript-eslint/explicit-function-return-type': [
'off',
{
allowExpressions: true,
allowTypedFunctionExpressions: true,
allowHigherOrderFunctions: true,
},
],
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/explicit-member-accessibility': 'off',
'@typescript-eslint/no-empty-interface': 'off',
},
};

101
package-lock.json generated
View file

@ -2599,6 +2599,16 @@
"resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.7.tgz", "resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.7.tgz",
"integrity": "sha512-rzOhiQ55WzAiFgXRtitP/ZUT8iVNyllEpylJ5zHzR4vArUvMB39GTk+Zon/uAM0JxEFAWnwsxC2gH8s+tZ3Myg==" "integrity": "sha512-rzOhiQ55WzAiFgXRtitP/ZUT8iVNyllEpylJ5zHzR4vArUvMB39GTk+Zon/uAM0JxEFAWnwsxC2gH8s+tZ3Myg=="
}, },
"@types/eslint-visitor-keys": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
"integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag=="
},
"@types/json-schema": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.3.tgz",
"integrity": "sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A=="
},
"@types/node": { "@types/node": {
"version": "11.11.1", "version": "11.11.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-11.11.1.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-11.11.1.tgz",
@ -2624,6 +2634,74 @@
"csstype": "^2.2.0" "csstype": "^2.2.0"
} }
}, },
"@typescript-eslint/eslint-plugin": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.13.0.tgz",
"integrity": "sha512-WQHCozMnuNADiqMtsNzp96FNox5sOVpU8Xt4meaT4em8lOG1SrOv92/mUbEHQVh90sldKSfcOc/I0FOb/14G1g==",
"requires": {
"@typescript-eslint/experimental-utils": "1.13.0",
"eslint-utils": "^1.3.1",
"functional-red-black-tree": "^1.0.1",
"regexpp": "^2.0.1",
"tsutils": "^3.7.0"
},
"dependencies": {
"regexpp": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz",
"integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw=="
},
"tsutils": {
"version": "3.17.0",
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.0.tgz",
"integrity": "sha512-fyveWOtAXfumAxIqkcMHuPaaVyLBKjB8Y00ANZkqh+HITBAQscCbQIHwwBTJdvQq7RykLEbOPcUUnJ16X4NA0g==",
"requires": {
"tslib": "^1.8.1"
}
}
}
},
"@typescript-eslint/experimental-utils": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-1.13.0.tgz",
"integrity": "sha512-zmpS6SyqG4ZF64ffaJ6uah6tWWWgZ8m+c54XXgwFtUv0jNz8aJAVx8chMCvnk7yl6xwn8d+d96+tWp7fXzTuDg==",
"requires": {
"@types/json-schema": "^7.0.3",
"@typescript-eslint/typescript-estree": "1.13.0",
"eslint-scope": "^4.0.0"
},
"dependencies": {
"eslint-scope": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz",
"integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==",
"requires": {
"esrecurse": "^4.1.0",
"estraverse": "^4.1.1"
}
}
}
},
"@typescript-eslint/parser": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.13.0.tgz",
"integrity": "sha512-ITMBs52PCPgLb2nGPoeT4iU3HdQZHcPaZVw+7CsFagRJHUhyeTgorEwHXhFf3e7Evzi8oujKNpHc8TONth8AdQ==",
"requires": {
"@types/eslint-visitor-keys": "^1.0.0",
"@typescript-eslint/experimental-utils": "1.13.0",
"@typescript-eslint/typescript-estree": "1.13.0",
"eslint-visitor-keys": "^1.0.0"
}
},
"@typescript-eslint/typescript-estree": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.13.0.tgz",
"integrity": "sha512-b5rCmd2e6DCC6tCTN9GSUAuxdYwCM/k/2wdjHGrIRGPSJotWMCe/dGpi66u42bhuh8q3QBzqM4TMA1GUUCJvdw==",
"requires": {
"lodash.unescape": "4.0.1",
"semver": "5.5.0"
}
},
"@webassemblyjs/ast": { "@webassemblyjs/ast": {
"version": "1.8.5", "version": "1.8.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz",
@ -7286,11 +7364,18 @@
"estraverse": "^4.1.1" "estraverse": "^4.1.1"
} }
}, },
"eslint-utils": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.0.tgz",
"integrity": "sha512-7ehnzPaP5IIEh1r1tkjuIrxqhNkzUJa9z3R92tLJdZIVdWaczEhr3EbhGtsMrVxi1KeR8qA7Off6SWc5WNQqyQ==",
"requires": {
"eslint-visitor-keys": "^1.0.0"
}
},
"eslint-visitor-keys": { "eslint-visitor-keys": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
"integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ=="
"dev": true
}, },
"espree": { "espree": {
"version": "3.5.4", "version": "3.5.4",
@ -7321,7 +7406,6 @@
"version": "4.2.1", "version": "4.2.1",
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
"integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
"dev": true,
"requires": { "requires": {
"estraverse": "^4.1.0" "estraverse": "^4.1.0"
} }
@ -7329,8 +7413,7 @@
"estraverse": { "estraverse": {
"version": "4.2.0", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
"integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM="
"dev": true
}, },
"esutils": { "esutils": {
"version": "2.0.2", "version": "2.0.2",
@ -8507,8 +8590,7 @@
"functional-red-black-tree": { "functional-red-black-tree": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
"integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc="
"dev": true
}, },
"gauge": { "gauge": {
"version": "2.7.4", "version": "2.7.4",
@ -10379,6 +10461,11 @@
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
"integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=" "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
}, },
"lodash.unescape": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz",
"integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw="
},
"lodash.uniq": { "lodash.uniq": {
"version": "4.5.0", "version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",

View file

@ -58,6 +58,8 @@
"@types/classnames": "^2.2.7", "@types/classnames": "^2.2.7",
"@types/node": "^11.9.0", "@types/node": "^11.9.0",
"@types/react": "16.8.23", "@types/react": "16.8.23",
"@typescript-eslint/eslint-plugin": "^1.13.0",
"@typescript-eslint/parser": "^1.13.0",
"axios": "^0.18.0", "axios": "^0.18.0",
"babel-runtime": "^6.26.0", "babel-runtime": "^6.26.0",
"classnames": "^2.2.6", "classnames": "^2.2.6",

View file

@ -7,8 +7,7 @@ import {
} from '~/utils/api'; } from '~/utils/api';
import { API } from '~/constants/api'; import { API } from '~/constants/api';
import { IResultWithStatus } from '~/redux/types'; import { IResultWithStatus } from '~/redux/types';
import { authMeTransform, userLoginTransform } from '~/redux/auth/transforms'; import { userLoginTransform } from '~/redux/auth/transforms';
import { IUser } from '~/redux/auth/types';
export const apiUserLogin = ({ export const apiUserLogin = ({
username, username,
@ -22,10 +21,3 @@ export const apiUserLogin = ({
.then(resultMiddleware) .then(resultMiddleware)
.catch(errorMiddleware) .catch(errorMiddleware)
.then(userLoginTransform); .then(userLoginTransform);
export const getAuthSelf = ({ access }): Promise<IResultWithStatus<{ user: IUser }>> =>
api
.get(API.USER.ME, configWithToken(access))
.then(resultMiddleware)
.catch(errorMiddleware)
.then(authMeTransform);

View file

@ -1,14 +1,14 @@
import {EMPTY_TOKEN, EMPTY_USER, AUTH_USER_ACTIONS} from "~/redux/auth/constants"; import { EMPTY_USER } from "~/redux/auth/constants";
import { createReducer } from "~/utils/reducer"; import { createReducer } from "~/utils/reducer";
import {IAuthState} from "~/redux/auth/types"; import { IAuthState } from "~/redux/auth/types";
import {AUTH_USER_HANDLERS} from "~/redux/auth/handlers"; import { AUTH_USER_HANDLERS } from "~/redux/auth/handlers";
const HANDLERS = { const HANDLERS = {
...AUTH_USER_HANDLERS, ...AUTH_USER_HANDLERS,
}; };
const INITIAL_STATE: IAuthState = { const INITIAL_STATE: IAuthState = {
token: { ...EMPTY_TOKEN }, token: null,
user: { ...EMPTY_USER }, user: { ...EMPTY_USER },
login: { login: {
error: null, error: null,

View file

@ -1,26 +1,43 @@
import {call, put, takeLatest } from 'redux-saga/effects'; import { call, put, takeLatest, select } from 'redux-saga/effects';
import { SagaIterator } from 'redux-saga'; import { SagaIterator } from 'redux-saga';
import {AUTH_USER_ACTIONS} from "~/redux/auth/constants"; import { AUTH_USER_ACTIONS } from "~/redux/auth/constants";
import * as ActionCreators from '~/redux/auth/actions'; import * as ActionCreators from '~/redux/auth/actions';
import {authSetToken, userSetLoginError} from "~/redux/auth/actions"; import { authSetToken, userSetLoginError, authSetUser } from "~/redux/auth/actions";
import {apiUserLogin, getAuthSelf} from "~/redux/auth/api"; import { apiUserLogin } from "~/redux/auth/api";
import {modalSetShown} from "~/redux/modal/actions"; import { modalSetShown, modalShowDialog } from "~/redux/modal/actions";
import { selectToken } from './selectors';
import { push } from 'connected-react-router';
import { URLS } from '~/constants/urls';
import { DIALOGS } from '../modal/constants';
import { IResultWithStatus } from '../types';
import { IToken, IUser } from './types';
export function* reqWrapper(requestAction, props = {}): ReturnType<typeof requestAction> {
const { access } = yield select(selectToken);
const result = yield call(requestAction, { access, ...props });
if (result && result.status === 401) {
yield put(push(URLS.BASE));
yield put(modalShowDialog(DIALOGS.LOGIN));
return result;
}
return result;
}
function* sendLoginRequestSaga({ username, password }: ReturnType<typeof ActionCreators.userSendLoginRequest>): SagaIterator { function* sendLoginRequestSaga({ username, password }: ReturnType<typeof ActionCreators.userSendLoginRequest>): SagaIterator {
if (!username || !password) return; if (!username || !password) return;
const { error, data: { access, refresh }} = yield call(apiUserLogin, { username, password }); const { error, data: { token, user } }: IResultWithStatus<{ token: string, user: IUser }> = yield call(apiUserLogin, { username, password });
console.log({ access, refresh, error }); console.log({ token, error });
if (error) return yield put(userSetLoginError(error)); if (error) return yield put(userSetLoginError(error));
yield put(authSetToken({ access, refresh })); yield put(authSetToken(token));
yield put(authSetUser(user));
const info = yield call(getAuthSelf); // todo: reqWrapper here
// todo: get /auth/me
yield put(modalSetShown(false)); yield put(modalSetShown(false));
} }

View file

@ -1,4 +1,5 @@
import {IState} from "~/redux/store"; import { IState } from "~/redux/store";
export const selectUser = (state: IState): IState['auth']['user'] => state.auth.user; export const selectUser = (state: IState): IState['auth']['user'] => state.auth.user;
export const selectToken = (state: IState): IState['auth']['token'] => state.auth.token;
export const selectAuthLogin = (state: IState): IState['auth']['login'] => state.auth.login; export const selectAuthLogin = (state: IState): IState['auth']['login'] => state.auth.login;

View file

@ -1,8 +1,9 @@
import {IResultWithStatus} from "~/redux/types"; import { IResultWithStatus } from "~/redux/types";
import { HTTP_RESPONSES } from "~/utils/api";
export const userLoginTransform = ({ status, data,error }: IResultWithStatus<any>): IResultWithStatus<any> => { export const userLoginTransform = ({ status, data, error }: IResultWithStatus<any>): IResultWithStatus<any> => {
switch(true) { switch (true) {
case status === 401 || !data.access || data.refresh: case (status === HTTP_RESPONSES.UNAUTHORIZED || !data.token) && status !== HTTP_RESPONSES.CONNECTION_REFUSED:
return { status, data, error: 'Пользователь не найден' }; return { status, data, error: 'Пользователь не найден' };
case status === 200: case status === 200:
@ -12,16 +13,3 @@ export const userLoginTransform = ({ status, data,error }: IResultWithStatus<any
return { status, data, error: error || 'Неизвестная ошибка' }; return { status, data, error: error || 'Неизвестная ошибка' };
} }
}; };
export const authMeTransform = ({ status, data,error }: IResultWithStatus<any>): IResultWithStatus<any> => {
switch(true) {
case status === 401:
return { status, data, error: 'Пользователь не авторизован' };
case status === 200:
return { status, data, error: null };
default:
return { status, data, error: error || 'Неизвестная ошибка' };
}
};

View file

@ -17,7 +17,7 @@ export interface IUser {
export type IAuthState = Readonly<{ export type IAuthState = Readonly<{
user: IUser; user: IUser;
token: IToken; token: string;
login: { login: {
error: string; error: string;

15
src/redux/node/reducer.ts Normal file
View file

@ -0,0 +1,15 @@
import { createReducer } from "~/utils/reducer";
export type INodeState = {
is_loading: boolean;
}
const HANDLERS = {
};
const INITIAL_STATE: INodeState = {
is_loading: false,
};
export default createReducer(INITIAL_STATE, HANDLERS);

View file

@ -38,9 +38,9 @@ export const errorMiddleware = <T extends any>(debug): IResultWithStatus<T> =>
? debug.response ? debug.response
: { : {
status: HTTP_RESPONSES.CONNECTION_REFUSED, status: HTTP_RESPONSES.CONNECTION_REFUSED,
data: null, data: {},
debug, debug,
error: 'Network_Disconnected', error: 'Ошибка сети',
}; };
export const configWithToken = ( export const configWithToken = (