feat(users): add reset password for users
This commit is contained in:
		
							parent
							
								
									b95e2da129
								
							
						
					
					
						commit
						bd1449e87a
					
				@ -43,6 +43,22 @@ const data = [
 | 
			
		||||
    template: 'register',
 | 
			
		||||
    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 () => {
 | 
			
		||||
@ -92,7 +108,7 @@ const data = [
 | 
			
		||||
    await Storage.create({
 | 
			
		||||
      name: `ali-oss`,
 | 
			
		||||
      type: 'ali-oss',
 | 
			
		||||
      baseUrl: process.env.STORAGE_BASE_URL,
 | 
			
		||||
      baseUrl: process.env.ALIYUN_STORAGE_BASE_URL,
 | 
			
		||||
      options: {
 | 
			
		||||
        region: process.env.ALIYUN_OSS_REGION,// 'oss-cn-beijing',
 | 
			
		||||
        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() {
 | 
			
		||||
  const { pathname, search } = location;
 | 
			
		||||
  console.log(location);
 | 
			
		||||
@ -43,7 +50,7 @@ export async function getInitialState() {
 | 
			
		||||
    redirect = `?redirect=${pathname}${search}`;
 | 
			
		||||
  // }
 | 
			
		||||
 | 
			
		||||
  if (pathname !== '/login' && pathname !== '/register') {
 | 
			
		||||
  if (!pathnames.includes(pathname)) {
 | 
			
		||||
    try {
 | 
			
		||||
      const { data = {} } = await umiRequest('/users:check', {
 | 
			
		||||
        method: 'post',
 | 
			
		||||
 | 
			
		||||
@ -72,7 +72,10 @@ export function Login(props: any) {
 | 
			
		||||
                width: '100%',
 | 
			
		||||
              },
 | 
			
		||||
              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,
 | 
			
		||||
  login: require('@/pages/login').default,
 | 
			
		||||
  register: require('@/pages/register').default,
 | 
			
		||||
  lostpassword: require('@/pages/lostpassword').default,
 | 
			
		||||
  resetpassword: require('@/pages/resetpassword').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,
 | 
			
		||||
      developerMode: true,
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      interface: 'string',
 | 
			
		||||
      type: 'string',
 | 
			
		||||
      name: 'reset_token',
 | 
			
		||||
      title: 'Reset Token',
 | 
			
		||||
      unique: true,
 | 
			
		||||
      hidden: true,
 | 
			
		||||
      filterable: false,
 | 
			
		||||
      developerMode: true,
 | 
			
		||||
    },
 | 
			
		||||
  ],
 | 
			
		||||
  actions: [
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
@ -5,10 +5,7 @@ import Resourcer from '@nocobase/resourcer';
 | 
			
		||||
 | 
			
		||||
import * as fields from './fields';
 | 
			
		||||
// import hooks from './hooks';
 | 
			
		||||
import login from './actions/login';
 | 
			
		||||
import register from './actions/register';
 | 
			
		||||
import logout from './actions/logout';
 | 
			
		||||
import check from './actions/check';
 | 
			
		||||
import * as usersActions from './actions/users';
 | 
			
		||||
import { makeOptions } from './hooks/collection-after-create';
 | 
			
		||||
import * as middlewares from './middlewares';
 | 
			
		||||
 | 
			
		||||
@ -39,12 +36,9 @@ export default async function (options = {}) {
 | 
			
		||||
    directory: path.resolve(__dirname, 'collections'),
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  resourcer.registerActionHandlers({
 | 
			
		||||
    'users:login': login,
 | 
			
		||||
    'users:register': register,
 | 
			
		||||
    'users:logout': logout,
 | 
			
		||||
    'users:check': check,
 | 
			
		||||
  });
 | 
			
		||||
  for (const [key, action] of Object.entries(usersActions)) {
 | 
			
		||||
    resourcer.registerActionHandler(`users:${key}`, action);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  resourcer.use(middlewares.parseToken(options));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user