feat(users): add reset password for users
This commit is contained in:
parent
b95e2da129
commit
bd1449e87a
@ -43,6 +43,22 @@ const data = [
|
|||||||
template: 'register',
|
template: 'register',
|
||||||
order: 130,
|
order: 130,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '忘记密码',
|
||||||
|
path: '/lostpassword',
|
||||||
|
type: 'page',
|
||||||
|
inherit: false,
|
||||||
|
template: 'lostpassword',
|
||||||
|
order: 140,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '重置密码',
|
||||||
|
path: '/resetpassword',
|
||||||
|
type: 'page',
|
||||||
|
inherit: false,
|
||||||
|
template: 'resetpassword',
|
||||||
|
order: 150,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
@ -92,7 +108,7 @@ const data = [
|
|||||||
await Storage.create({
|
await Storage.create({
|
||||||
name: `ali-oss`,
|
name: `ali-oss`,
|
||||||
type: 'ali-oss',
|
type: 'ali-oss',
|
||||||
baseUrl: process.env.STORAGE_BASE_URL,
|
baseUrl: process.env.ALIYUN_STORAGE_BASE_URL,
|
||||||
options: {
|
options: {
|
||||||
region: process.env.ALIYUN_OSS_REGION,// 'oss-cn-beijing',
|
region: process.env.ALIYUN_OSS_REGION,// 'oss-cn-beijing',
|
||||||
accessKeyId: process.env.ALIYUN_OSS_ACCESS_KEY_ID,// 'LTAI4GEGDJsdGantisvSaz47',
|
accessKeyId: process.env.ALIYUN_OSS_ACCESS_KEY_ID,// 'LTAI4GEGDJsdGantisvSaz47',
|
||||||
|
@ -32,6 +32,13 @@ export const request: RequestConfig = {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const pathnames = [
|
||||||
|
'/login',
|
||||||
|
'/register',
|
||||||
|
'/lostpassword',
|
||||||
|
'/resetpassword',
|
||||||
|
];
|
||||||
|
|
||||||
export async function getInitialState() {
|
export async function getInitialState() {
|
||||||
const { pathname, search } = location;
|
const { pathname, search } = location;
|
||||||
console.log(location);
|
console.log(location);
|
||||||
@ -43,7 +50,7 @@ export async function getInitialState() {
|
|||||||
redirect = `?redirect=${pathname}${search}`;
|
redirect = `?redirect=${pathname}${search}`;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
if (pathname !== '/login' && pathname !== '/register') {
|
if (!pathnames.includes(pathname)) {
|
||||||
try {
|
try {
|
||||||
const { data = {} } = await umiRequest('/users:check', {
|
const { data = {} } = await umiRequest('/users:check', {
|
||||||
method: 'post',
|
method: 'post',
|
||||||
|
@ -72,7 +72,10 @@ export function Login(props: any) {
|
|||||||
width: '100%',
|
width: '100%',
|
||||||
},
|
},
|
||||||
placeholder: '密码',
|
placeholder: '密码',
|
||||||
}
|
},
|
||||||
|
"x-props": {
|
||||||
|
help: <Link to={'/lostpassword'}>忘记密码?</Link>
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
|
67
packages/app/src/components/views/Form/LostPassword.tsx
Normal file
67
packages/app/src/components/views/Form/LostPassword.tsx
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Tooltip, Card, Button, message } from 'antd';
|
||||||
|
import {
|
||||||
|
SchemaForm,
|
||||||
|
SchemaMarkupField as Field,
|
||||||
|
createFormActions,
|
||||||
|
createAsyncFormActions,
|
||||||
|
Submit,
|
||||||
|
Reset,
|
||||||
|
FormButtonGroup,
|
||||||
|
registerFormFields,
|
||||||
|
FormValidator,
|
||||||
|
setValidationLanguage,
|
||||||
|
} from '@formily/antd';
|
||||||
|
import { QuestionCircleOutlined } from '@ant-design/icons';
|
||||||
|
import { Link, history, request, useModel, useLocation } from 'umi';
|
||||||
|
|
||||||
|
export function LostPassword(props: any) {
|
||||||
|
const actions = createFormActions();
|
||||||
|
const { initialState = {}, loading, error, refresh, setInitialState } = useModel('@@initialState');
|
||||||
|
const { redirect } = props.location.query;
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { systemSettings = {} } = initialState as any;
|
||||||
|
|
||||||
|
console.log({systemSettings});
|
||||||
|
|
||||||
|
const { title } = systemSettings || {};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={'users-form'}>
|
||||||
|
<h1>{title||'NocoBase'}</h1>
|
||||||
|
<h2>忘记密码</h2>
|
||||||
|
<SchemaForm onSubmit={async (values) => {
|
||||||
|
console.log(values);
|
||||||
|
const { data = {} } = await request('/users:lostpassword', {
|
||||||
|
method: 'post',
|
||||||
|
data: values,
|
||||||
|
});
|
||||||
|
message.success(`重置链接已发送至邮箱 ${values.email},请注意查收!`)
|
||||||
|
}} actions={actions} schema={{
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
email: {
|
||||||
|
type: 'string',
|
||||||
|
title: '',
|
||||||
|
required: true,
|
||||||
|
'x-component-props': {
|
||||||
|
size: 'large',
|
||||||
|
placeholder: '邮箱',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
<FormButtonGroup>
|
||||||
|
<Submit size={'large'}>获取新密码</Submit>
|
||||||
|
<Button size={'large'} onClick={() => {
|
||||||
|
history.push('/login');
|
||||||
|
}}>使用已有账号登录</Button>
|
||||||
|
</FormButtonGroup>
|
||||||
|
</SchemaForm>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
151
packages/app/src/components/views/Form/ResetPassword.tsx
Normal file
151
packages/app/src/components/views/Form/ResetPassword.tsx
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Tooltip, Card, Button, message } from 'antd';
|
||||||
|
import {
|
||||||
|
SchemaForm,
|
||||||
|
SchemaMarkupField as Field,
|
||||||
|
createFormActions,
|
||||||
|
createAsyncFormActions,
|
||||||
|
Submit,
|
||||||
|
Reset,
|
||||||
|
FormButtonGroup,
|
||||||
|
registerFormFields,
|
||||||
|
FormEffectHooks,
|
||||||
|
FormValidator,
|
||||||
|
setValidationLanguage,
|
||||||
|
} from '@formily/antd';
|
||||||
|
import { QuestionCircleOutlined } from '@ant-design/icons';
|
||||||
|
import { Redirect, history, request, useModel, useHistory } from 'umi';
|
||||||
|
import api from '@/api-client';
|
||||||
|
import { useRequest } from 'umi';
|
||||||
|
|
||||||
|
const { onFieldValueChange$ } = FormEffectHooks
|
||||||
|
|
||||||
|
const useLinkageValidateEffects = () => {
|
||||||
|
const { setFieldState, getFieldState } = createFormActions()
|
||||||
|
onFieldValueChange$('*(password,confirm)').subscribe(fieldState => {
|
||||||
|
const selfName = fieldState.name
|
||||||
|
const selfValue = fieldState.value
|
||||||
|
const otherName = selfName == 'password' ? 'confirm' : 'password'
|
||||||
|
const otherValue = getFieldState(otherName, state => state.value)
|
||||||
|
setFieldState(otherName, state => {
|
||||||
|
if (selfValue && otherValue && selfValue !== otherValue) {
|
||||||
|
state.errors = ['两次密码输入不一致']
|
||||||
|
} else {
|
||||||
|
state.errors = []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
setFieldState(selfName, state => {
|
||||||
|
if (selfValue && otherValue && selfValue !== otherValue) {
|
||||||
|
state.errors = ['两次密码输入不一致']
|
||||||
|
} else {
|
||||||
|
state.errors = []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ResetPassword(props: any) {
|
||||||
|
const actions = createFormActions();
|
||||||
|
const { initialState = {}, loading: stateLoading, error, refresh, setInitialState } = useModel('@@initialState');
|
||||||
|
|
||||||
|
const history = useHistory<any>();
|
||||||
|
// @ts-ignore
|
||||||
|
const { token } = history.location.query;
|
||||||
|
console.log({ token })
|
||||||
|
const { data = {}, loading } = useRequest(() => {
|
||||||
|
return api.resource('users').getUserByResetToken({
|
||||||
|
token,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (stateLoading || loading) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data.id) {
|
||||||
|
return <Redirect to={'/login'}/>
|
||||||
|
}
|
||||||
|
|
||||||
|
const { systemSettings = {} } = initialState as any;
|
||||||
|
|
||||||
|
console.log({systemSettings});
|
||||||
|
|
||||||
|
const { title } = systemSettings || {};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={'users-form'}>
|
||||||
|
<h1>{title || 'NocoBase'}</h1>
|
||||||
|
<h2>重置密码</h2>
|
||||||
|
<SchemaForm
|
||||||
|
effects={() => {
|
||||||
|
useLinkageValidateEffects()
|
||||||
|
}}
|
||||||
|
initialValues={{
|
||||||
|
email: data.email,
|
||||||
|
}}
|
||||||
|
onSubmit={async (values) => {
|
||||||
|
console.log(values);
|
||||||
|
const { data = {} } = await request('/users:resetpassword', {
|
||||||
|
method: 'post',
|
||||||
|
data: {
|
||||||
|
...values,
|
||||||
|
reset_token: token,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await actions.reset({
|
||||||
|
validate: false,
|
||||||
|
forceClear: true,
|
||||||
|
});
|
||||||
|
message.success('密码重置成功,将跳转至登录页');
|
||||||
|
setTimeout(() => {
|
||||||
|
history.push('/login');
|
||||||
|
}, 1000);
|
||||||
|
}} actions={actions} schema={{
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
email: {
|
||||||
|
type: 'string',
|
||||||
|
title: '',
|
||||||
|
required: true,
|
||||||
|
'x-component-props': {
|
||||||
|
size: 'large',
|
||||||
|
placeholder: '邮箱',
|
||||||
|
disabled: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
password: {
|
||||||
|
type: 'password',
|
||||||
|
title: '',
|
||||||
|
required: true,
|
||||||
|
'x-component-props': {
|
||||||
|
size: 'large',
|
||||||
|
style: {
|
||||||
|
width: '100%',
|
||||||
|
},
|
||||||
|
placeholder: '密码',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirm: {
|
||||||
|
type: 'password',
|
||||||
|
title: '',
|
||||||
|
required: true,
|
||||||
|
'x-component-props': {
|
||||||
|
size: 'large',
|
||||||
|
style: {
|
||||||
|
width: '100%',
|
||||||
|
},
|
||||||
|
placeholder: '确认密码',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
<FormButtonGroup align={'start'}>
|
||||||
|
<Submit size={'large'}>重置密码</Submit>
|
||||||
|
<Button size={'large'} onClick={() => {
|
||||||
|
history.push('/login');
|
||||||
|
}}>使用已有账号登录</Button>
|
||||||
|
</FormButtonGroup>
|
||||||
|
</SchemaForm>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
20
packages/app/src/pages/lostpassword.tsx
Normal file
20
packages/app/src/pages/lostpassword.tsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Link } from 'umi';
|
||||||
|
import { Page } from '@/components/pages';
|
||||||
|
import ViewFactory from '@/components/views';
|
||||||
|
import { Helmet, useModel } from 'umi';
|
||||||
|
import get from 'lodash/get';
|
||||||
|
import { LostPassword } from '@/components/views/Form/LostPassword';
|
||||||
|
|
||||||
|
export default (props: any) => {
|
||||||
|
const { initialState = {}, refresh, setInitialState } = useModel('@@initialState');
|
||||||
|
const siteTitle = get(initialState, 'systemSettings.title');
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Helmet>
|
||||||
|
<title>{siteTitle ? `忘记密码 - ${siteTitle}` : '忘记密码'}</title>
|
||||||
|
</Helmet>
|
||||||
|
<LostPassword {...props}/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
20
packages/app/src/pages/resetpassword.tsx
Normal file
20
packages/app/src/pages/resetpassword.tsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Link } from 'umi';
|
||||||
|
import { Page } from '@/components/pages';
|
||||||
|
import ViewFactory from '@/components/views';
|
||||||
|
import { Helmet, useModel } from 'umi';
|
||||||
|
import get from 'lodash/get';
|
||||||
|
import { ResetPassword } from '@/components/views/Form/ResetPassword';
|
||||||
|
|
||||||
|
export default (props: any) => {
|
||||||
|
const { initialState = {}, refresh, setInitialState } = useModel('@@initialState');
|
||||||
|
const siteTitle = get(initialState, 'systemSettings.title');
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Helmet>
|
||||||
|
<title>{siteTitle ? `重置密码 - ${siteTitle}` : '重置密码'}</title>
|
||||||
|
</Helmet>
|
||||||
|
<ResetPassword {...props}/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -12,5 +12,7 @@ export default {
|
|||||||
'page4': require('@/pages/page4').default,
|
'page4': require('@/pages/page4').default,
|
||||||
login: require('@/pages/login').default,
|
login: require('@/pages/login').default,
|
||||||
register: require('@/pages/register').default,
|
register: require('@/pages/register').default,
|
||||||
|
lostpassword: require('@/pages/lostpassword').default,
|
||||||
|
resetpassword: require('@/pages/resetpassword').default,
|
||||||
AdminLoader: require('@/components/pages/AdminLoader').default,
|
AdminLoader: require('@/components/pages/AdminLoader').default,
|
||||||
};
|
};
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
import { actions } from '@nocobase/actions';
|
|
||||||
|
|
||||||
export default async (ctx: actions.Context, next: actions.Next) => {
|
|
||||||
ctx.body = ctx.state.currentUser;
|
|
||||||
await next();
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
import { actions } from '@nocobase/actions';
|
|
||||||
import { PASSWORD } from '@nocobase/database';
|
|
||||||
import cryptoRandomString from 'crypto-random-string';
|
|
||||||
|
|
||||||
export default async (ctx: actions.Context, next: actions.Next) => {
|
|
||||||
const { uniqueField = 'email', values } = ctx.action.params;
|
|
||||||
// console.log(values);
|
|
||||||
const User = ctx.db.getModel('users');
|
|
||||||
const user = await User.findOne({
|
|
||||||
where: {
|
|
||||||
[uniqueField]: values[uniqueField],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (!user) {
|
|
||||||
ctx.throw(401, 'Unauthorized');
|
|
||||||
}
|
|
||||||
if (!PASSWORD.verify(values.password, user.password)) {
|
|
||||||
ctx.throw(401, 'Unauthorized');
|
|
||||||
}
|
|
||||||
if (!user.token) {
|
|
||||||
user.token = cryptoRandomString({length: 20});
|
|
||||||
await user.save();
|
|
||||||
}
|
|
||||||
ctx.body = {
|
|
||||||
data: user,
|
|
||||||
};
|
|
||||||
await next();
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
import { actions } from '@nocobase/actions';
|
|
||||||
|
|
||||||
export default async (ctx: actions.Context, next: actions.Next) => {
|
|
||||||
ctx.body = {};
|
|
||||||
await next();
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
import { actions } from '@nocobase/actions';
|
|
||||||
|
|
||||||
export default async (ctx: actions.Context, next: actions.Next) => {
|
|
||||||
const User = ctx.db.getModel('users');
|
|
||||||
const { values } = ctx.action.params;
|
|
||||||
const user = await User.create(values);
|
|
||||||
ctx.body = {
|
|
||||||
data: user,
|
|
||||||
};
|
|
||||||
await next();
|
|
||||||
}
|
|
100
packages/plugin-users/src/actions/users.ts
Normal file
100
packages/plugin-users/src/actions/users.ts
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
import { actions } from '@nocobase/actions';
|
||||||
|
import { PASSWORD } from '@nocobase/database';
|
||||||
|
import cryptoRandomString from 'crypto-random-string';
|
||||||
|
|
||||||
|
export async function check(ctx: actions.Context, next: actions.Next) {
|
||||||
|
ctx.body = ctx.state.currentUser;
|
||||||
|
await next();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function login(ctx: actions.Context, next: actions.Next) {
|
||||||
|
const { uniqueField = 'email', values } = ctx.action.params;
|
||||||
|
// console.log(values);
|
||||||
|
const User = ctx.db.getModel('users');
|
||||||
|
const user = await User.findOne({
|
||||||
|
where: {
|
||||||
|
[uniqueField]: values[uniqueField],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!user) {
|
||||||
|
ctx.throw(401, 'Unauthorized');
|
||||||
|
}
|
||||||
|
if (!PASSWORD.verify(values.password, user.password)) {
|
||||||
|
ctx.throw(401, 'Unauthorized');
|
||||||
|
}
|
||||||
|
if (!user.token) {
|
||||||
|
user.token = cryptoRandomString({length: 20});
|
||||||
|
await user.save();
|
||||||
|
}
|
||||||
|
ctx.body = {
|
||||||
|
data: user,
|
||||||
|
};
|
||||||
|
await next();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function logout(ctx: actions.Context, next: actions.Next) {
|
||||||
|
ctx.body = {};
|
||||||
|
await next();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function register(ctx: actions.Context, next: actions.Next) {
|
||||||
|
const User = ctx.db.getModel('users');
|
||||||
|
const { values } = ctx.action.params;
|
||||||
|
const user = await User.create(values);
|
||||||
|
ctx.body = {
|
||||||
|
data: user,
|
||||||
|
};
|
||||||
|
await next();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function lostpassword(ctx: actions.Context, next: actions.Next) {
|
||||||
|
const { values: { email } } = ctx.action.params;
|
||||||
|
const User = ctx.db.getModel('users');
|
||||||
|
const user = await User.findOne({
|
||||||
|
where: {
|
||||||
|
email,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!user) {
|
||||||
|
ctx.throw(401, 'Unauthorized');
|
||||||
|
}
|
||||||
|
user.reset_token = cryptoRandomString({length: 20});
|
||||||
|
await user.save();
|
||||||
|
ctx.body = user;
|
||||||
|
await next();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function resetpassword(ctx: actions.Context, next: actions.Next) {
|
||||||
|
const { values: { email, password, reset_token } } = ctx.action.params;
|
||||||
|
const User = ctx.db.getModel('users');
|
||||||
|
const user = await User.findOne({
|
||||||
|
where: {
|
||||||
|
email,
|
||||||
|
reset_token,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!user) {
|
||||||
|
ctx.throw(401, 'Unauthorized');
|
||||||
|
}
|
||||||
|
user.token = null;
|
||||||
|
user.reset_token = null;
|
||||||
|
user.password = password;
|
||||||
|
await user.save();
|
||||||
|
ctx.body = user;
|
||||||
|
await next();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getUserByResetToken(ctx: actions.Context, next: actions.Next) {
|
||||||
|
const { token } = ctx.action.params;
|
||||||
|
const User = ctx.db.getModel('users');
|
||||||
|
const user = await User.findOne({
|
||||||
|
where: {
|
||||||
|
reset_token: token,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!user) {
|
||||||
|
ctx.throw(401, 'Unauthorized');
|
||||||
|
}
|
||||||
|
ctx.body = user;
|
||||||
|
await next();
|
||||||
|
}
|
@ -85,6 +85,16 @@ export default {
|
|||||||
filterable: false,
|
filterable: false,
|
||||||
developerMode: true,
|
developerMode: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
interface: 'string',
|
||||||
|
type: 'string',
|
||||||
|
name: 'reset_token',
|
||||||
|
title: 'Reset Token',
|
||||||
|
unique: true,
|
||||||
|
hidden: true,
|
||||||
|
filterable: false,
|
||||||
|
developerMode: true,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
|
@ -5,10 +5,7 @@ import Resourcer from '@nocobase/resourcer';
|
|||||||
|
|
||||||
import * as fields from './fields';
|
import * as fields from './fields';
|
||||||
// import hooks from './hooks';
|
// import hooks from './hooks';
|
||||||
import login from './actions/login';
|
import * as usersActions from './actions/users';
|
||||||
import register from './actions/register';
|
|
||||||
import logout from './actions/logout';
|
|
||||||
import check from './actions/check';
|
|
||||||
import { makeOptions } from './hooks/collection-after-create';
|
import { makeOptions } from './hooks/collection-after-create';
|
||||||
import * as middlewares from './middlewares';
|
import * as middlewares from './middlewares';
|
||||||
|
|
||||||
@ -39,12 +36,9 @@ export default async function (options = {}) {
|
|||||||
directory: path.resolve(__dirname, 'collections'),
|
directory: path.resolve(__dirname, 'collections'),
|
||||||
});
|
});
|
||||||
|
|
||||||
resourcer.registerActionHandlers({
|
for (const [key, action] of Object.entries(usersActions)) {
|
||||||
'users:login': login,
|
resourcer.registerActionHandler(`users:${key}`, action);
|
||||||
'users:register': register,
|
}
|
||||||
'users:logout': logout,
|
|
||||||
'users:check': check,
|
|
||||||
});
|
|
||||||
|
|
||||||
resourcer.use(middlewares.parseToken(options));
|
resourcer.use(middlewares.parseToken(options));
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user