Przeglądaj źródła

Merge pull request #6694 from weseek/feat/exception-handling

feat: Exception handling
Yuki Takei 3 lat temu
rodzic
commit
e641652e3a

+ 24 - 6
packages/app/src/components/InvitedForm.tsx

@@ -1,4 +1,4 @@
-import React, { useCallback } from 'react';
+import React, { useCallback, useState } from 'react';
 
 import { useTranslation } from 'next-i18next';
 import { useRouter } from 'next/router';
@@ -18,6 +18,7 @@ export const InvitedForm = (props: InvitedFormProps): JSX.Element => {
   const { t } = useTranslation();
   const router = useRouter();
   const { data: user } = useCurrentUser();
+  const [loginErrors, setLoginErrors] = useState<Error[]>([]);
 
   const { invitedFormUsername, invitedFormName } = props;
 
@@ -44,20 +45,36 @@ export const InvitedForm = (props: InvitedFormProps): JSX.Element => {
       router.push(redirectTo);
     }
     catch (err) {
-      // TODO: show errors
+      setLoginErrors(err);
     }
   }, [router]);
 
+  const formNotification = useCallback(() => {
+    return (
+      <>
+        { loginErrors != null && loginErrors.length > 0 ? (
+          <p className="alert alert-danger">
+            { loginErrors.map((err, index) => {
+              return <span key={index}>{ t(err.message) }<br/></span>;
+            }) }
+          </p>
+        ) : (
+          <p className="alert alert-success">
+            <strong>{ t('invited.discription_heading') }</strong><br></br>
+            <small>{ t('invited.discription') }</small>
+          </p>
+        ) }
+      </>
+    );
+  }, [loginErrors, t]);
+
   if (user == null) {
     return <></>;
   }
 
   return (
     <div className="noLogin-dialog p-3 mx-auto" id="noLogin-dialog">
-      <p className="alert alert-success">
-        <strong>{ t('invited.discription_heading') }</strong><br></br>
-        <small>{ t('invited.discription') }</small>
-      </p>
+      { formNotification() }
       <form role="form" onSubmit={submitHandler} id="invited-form">
         {/* Email Form */}
         <div className="input-group">
@@ -121,6 +138,7 @@ export const InvitedForm = (props: InvitedFormProps): JSX.Element => {
             placeholder={t('Password')}
             name="invitedForm[password]"
             required
+            minLength={6}
           />
         </div>
         {/* Create Button */}

+ 15 - 18
packages/app/src/server/middlewares/invited-form-validator.ts

@@ -1,7 +1,10 @@
-import { body, validationResult } from 'express-validator';
+import { NextFunction, Response } from 'express';
+import { body, validationResult, ValidationChain } from 'express-validator';
+import { Request } from 'express-validator/src/base';
 
-// form rules
-export const invitedRules = () => {
+const MININUM_PASSWORD_LENGTH = 6;
+
+export const invitedRules = (): ValidationChain[] => {
   return [
     body('invitedForm.username')
       .matches(/^[\da-zA-Z\-_.]+$/)
@@ -16,33 +19,27 @@ export const invitedRules = () => {
     body('invitedForm.password')
       .matches(/^[\x20-\x7F]*$/)
       .withMessage('message.Password has invalid character')
-      .isLength({ min: 6 })
-      .withMessage('message.Password minimum character should be more than 6 characters')
+      .isLength({ min: MININUM_PASSWORD_LENGTH })
+      .withMessage(`message.Password minimum character should be more than ${MININUM_PASSWORD_LENGTH} characters`)
       .not()
       .isEmpty()
       .withMessage('message.Password field is required'),
   ];
 };
 
-// validation action
-export const invitedValidation = (req, _res, next) => {
+export const invitedValidation = (req: Request, _res: Response, next: () => NextFunction): any => {
   const form = req.body;
-
   const errors = validationResult(req);
+  const extractedErrors: string[] = [];
+
   if (errors.isEmpty()) {
     Object.assign(form, { isValid: true });
-    req.form = form;
-    return next();
+  }
+  else {
+    errors.array().map(err => extractedErrors.push(err.msg));
+    Object.assign(form, { isValid: false, errors: extractedErrors });
   }
 
-  const extractedErrors: string[] = [];
-  errors.array().map(err => extractedErrors.push(err.msg));
-
-  Object.assign(form, {
-    isValid: false,
-    errors: extractedErrors,
-  });
   req.form = form;
-
   return next();
 };

+ 27 - 32
packages/app/src/server/routes/apiv3/invited.ts

@@ -18,39 +18,34 @@ module.exports = (crowi: Crowi): Router => {
       return res.apiv3({ redirectTo: '/login' });
     }
 
-    if (req.method === 'POST' && req.form.isValid) {
-      const user = req.user;
-      const invitedForm = req.form.invitedForm || {};
-      const username = invitedForm.username;
-      const name = invitedForm.name;
-      const password = invitedForm.password;
-
-      // check user upper limit
-      const isUserCountExceedsUpperLimit = await User.isUserCountExceedsUpperLimit();
-      if (isUserCountExceedsUpperLimit) {
-        // req.flash('warningMessage', req.t('message.can_not_activate_maximum_number_of_users'));
-        return res.apiv3({ redirectTo: '/invited' });
-      }
-
-      const creatable = await User.isRegisterableUsername(username);
-      if (creatable) {
-        try {
-          await user.activateInvitedUser(username, name, password);
-          return res.apiv3({ redirectTo: '/' });
-        }
-        catch (err) {
-          // req.flash('warningMessage', req.t('message.failed_to_activate'));
-          return res.render('invited');
-        }
-      }
-      else {
-        // req.flash('warningMessage', req.t('message.unable_to_use_this_user'));
-        debug('username', username);
-        return res.render('invited');
-      }
+    if (!req.form.isValid) {
+      return res.apiv3Err(req.form.errors, 400);
     }
-    else {
-      return res.render('invited');
+
+    const user = req.user;
+    const invitedForm = req.form.invitedForm || {};
+    const username = invitedForm.username;
+    const name = invitedForm.name;
+    const password = invitedForm.password;
+
+    // check user upper limit
+    const isUserCountExceedsUpperLimit = await User.isUserCountExceedsUpperLimit();
+    if (isUserCountExceedsUpperLimit) {
+      return res.apiv3Err('message.can_not_activate_maximum_number_of_users', 403);
+    }
+
+    const creatable = await User.isRegisterableUsername(username);
+    if (!creatable) {
+      debug('username', username);
+      return res.apiv3Err('message.unable_to_use_this_user', 403);
+    }
+
+    try {
+      await user.activateInvitedUser(username, name, password);
+      return res.apiv3({ redirectTo: '/' });
+    }
+    catch (err) {
+      return res.apiv3Err('message.failed_to_activate', 403);
     }
   });