@strapi/plugin-users-permissions 0.0.0-4fc90398602f

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +1 -0
  3. package/admin/src/components/BoundRoute/getMethodColor.js +41 -0
  4. package/admin/src/components/BoundRoute/index.js +72 -0
  5. package/admin/src/components/FormModal/Input/index.js +121 -0
  6. package/admin/src/components/FormModal/index.js +121 -0
  7. package/admin/src/components/Permissions/PermissionRow/CheckboxWrapper.js +30 -0
  8. package/admin/src/components/Permissions/PermissionRow/SubCategory.js +114 -0
  9. package/admin/src/components/Permissions/PermissionRow/index.js +53 -0
  10. package/admin/src/components/Permissions/index.js +56 -0
  11. package/admin/src/components/Permissions/init.js +9 -0
  12. package/admin/src/components/Permissions/reducer.js +27 -0
  13. package/admin/src/components/Policies/index.js +60 -0
  14. package/admin/src/components/UsersPermissions/index.js +94 -0
  15. package/admin/src/components/UsersPermissions/init.js +10 -0
  16. package/admin/src/components/UsersPermissions/reducer.js +60 -0
  17. package/admin/src/contexts/UsersPermissionsContext/index.js +17 -0
  18. package/admin/src/hooks/index.js +5 -0
  19. package/admin/src/hooks/useFetchRole/index.js +64 -0
  20. package/admin/src/hooks/useFetchRole/reducer.js +31 -0
  21. package/admin/src/hooks/useForm/index.js +70 -0
  22. package/admin/src/hooks/useForm/reducer.js +40 -0
  23. package/admin/src/hooks/usePlugins/index.js +65 -0
  24. package/admin/src/hooks/usePlugins/init.js +5 -0
  25. package/admin/src/hooks/usePlugins/reducer.js +34 -0
  26. package/admin/src/hooks/useRolesList/index.js +63 -0
  27. package/admin/src/hooks/useRolesList/init.js +5 -0
  28. package/admin/src/hooks/useRolesList/reducer.js +31 -0
  29. package/admin/src/index.js +123 -0
  30. package/admin/src/pages/AdvancedSettings/index.js +238 -0
  31. package/admin/src/pages/AdvancedSettings/utils/api.js +13 -0
  32. package/admin/src/pages/AdvancedSettings/utils/layout.js +96 -0
  33. package/admin/src/pages/AdvancedSettings/utils/schema.js +19 -0
  34. package/admin/src/pages/EmailTemplates/components/EmailForm.js +173 -0
  35. package/admin/src/pages/EmailTemplates/components/EmailTable.js +121 -0
  36. package/admin/src/pages/EmailTemplates/index.js +162 -0
  37. package/admin/src/pages/EmailTemplates/utils/api.js +13 -0
  38. package/admin/src/pages/EmailTemplates/utils/schema.js +22 -0
  39. package/admin/src/pages/Providers/index.js +274 -0
  40. package/admin/src/pages/Providers/reducer.js +54 -0
  41. package/admin/src/pages/Providers/utils/api.js +21 -0
  42. package/admin/src/pages/Providers/utils/createProvidersArray.js +21 -0
  43. package/admin/src/pages/Providers/utils/forms.js +244 -0
  44. package/admin/src/pages/Roles/CreatePage/index.js +177 -0
  45. package/admin/src/pages/Roles/CreatePage/utils/schema.js +9 -0
  46. package/admin/src/pages/Roles/EditPage/index.js +190 -0
  47. package/admin/src/pages/Roles/EditPage/utils/schema.js +9 -0
  48. package/admin/src/pages/Roles/ListPage/components/TableBody.js +96 -0
  49. package/admin/src/pages/Roles/ListPage/index.js +216 -0
  50. package/admin/src/pages/Roles/ListPage/utils/api.js +28 -0
  51. package/admin/src/pages/Roles/ProtectedCreatePage/index.js +12 -0
  52. package/admin/src/pages/Roles/ProtectedEditPage/index.js +12 -0
  53. package/admin/src/pages/Roles/ProtectedListPage/index.js +15 -0
  54. package/admin/src/pages/Roles/index.js +27 -0
  55. package/admin/src/permissions.js +31 -0
  56. package/admin/src/pluginId.js +5 -0
  57. package/admin/src/translations/ar.json +40 -0
  58. package/admin/src/translations/cs.json +46 -0
  59. package/admin/src/translations/de.json +58 -0
  60. package/admin/src/translations/dk.json +83 -0
  61. package/admin/src/translations/en.json +83 -0
  62. package/admin/src/translations/es.json +83 -0
  63. package/admin/src/translations/fr.json +46 -0
  64. package/admin/src/translations/id.json +58 -0
  65. package/admin/src/translations/it.json +58 -0
  66. package/admin/src/translations/ja.json +44 -0
  67. package/admin/src/translations/ko.json +83 -0
  68. package/admin/src/translations/ms.json +45 -0
  69. package/admin/src/translations/nl.json +44 -0
  70. package/admin/src/translations/pl.json +83 -0
  71. package/admin/src/translations/pt-BR.json +40 -0
  72. package/admin/src/translations/pt.json +44 -0
  73. package/admin/src/translations/ru.json +58 -0
  74. package/admin/src/translations/sk.json +46 -0
  75. package/admin/src/translations/sv.json +58 -0
  76. package/admin/src/translations/th.json +56 -0
  77. package/admin/src/translations/tr.json +44 -0
  78. package/admin/src/translations/uk.json +45 -0
  79. package/admin/src/translations/vi.json +46 -0
  80. package/admin/src/translations/zh-Hans.json +62 -0
  81. package/admin/src/translations/zh.json +44 -0
  82. package/admin/src/utils/axiosInstance.js +36 -0
  83. package/admin/src/utils/cleanPermissions.js +25 -0
  84. package/admin/src/utils/formatPluginName.js +26 -0
  85. package/admin/src/utils/formatPolicies.js +8 -0
  86. package/admin/src/utils/getRequestURL.js +5 -0
  87. package/admin/src/utils/getTrad.js +5 -0
  88. package/admin/src/utils/index.js +5 -0
  89. package/documentation/content-api.yaml +848 -0
  90. package/jest.config.front.js +10 -0
  91. package/package.json +60 -0
  92. package/server/bootstrap/grant-config.js +123 -0
  93. package/server/bootstrap/index.js +133 -0
  94. package/server/bootstrap/users-permissions-actions.js +80 -0
  95. package/server/config.js +23 -0
  96. package/server/content-types/index.js +11 -0
  97. package/server/content-types/permission/index.js +34 -0
  98. package/server/content-types/role/index.js +51 -0
  99. package/server/content-types/user/index.js +72 -0
  100. package/server/content-types/user/schema-config.js +15 -0
  101. package/server/controllers/auth.js +398 -0
  102. package/server/controllers/content-manager-user.js +175 -0
  103. package/server/controllers/index.js +17 -0
  104. package/server/controllers/permissions.js +26 -0
  105. package/server/controllers/role.js +77 -0
  106. package/server/controllers/settings.js +85 -0
  107. package/server/controllers/user.js +198 -0
  108. package/server/controllers/validation/auth.js +57 -0
  109. package/server/controllers/validation/email-template.js +50 -0
  110. package/server/controllers/validation/user.js +26 -0
  111. package/server/graphql/index.js +44 -0
  112. package/server/graphql/mutations/auth/change-password.js +38 -0
  113. package/server/graphql/mutations/auth/email-confirmation.js +39 -0
  114. package/server/graphql/mutations/auth/forgot-password.js +35 -0
  115. package/server/graphql/mutations/auth/login.js +35 -0
  116. package/server/graphql/mutations/auth/register.js +36 -0
  117. package/server/graphql/mutations/auth/reset-password.js +38 -0
  118. package/server/graphql/mutations/crud/role/create-role.js +34 -0
  119. package/server/graphql/mutations/crud/role/delete-role.js +25 -0
  120. package/server/graphql/mutations/crud/role/update-role.js +35 -0
  121. package/server/graphql/mutations/crud/user/create-user.js +45 -0
  122. package/server/graphql/mutations/crud/user/delete-user.js +39 -0
  123. package/server/graphql/mutations/crud/user/update-user.js +46 -0
  124. package/server/graphql/mutations/index.js +43 -0
  125. package/server/graphql/queries/index.js +13 -0
  126. package/server/graphql/queries/me.js +17 -0
  127. package/server/graphql/resolvers-configs.js +42 -0
  128. package/server/graphql/types/create-role-payload.js +11 -0
  129. package/server/graphql/types/delete-role-payload.js +11 -0
  130. package/server/graphql/types/index.js +21 -0
  131. package/server/graphql/types/login-input.js +13 -0
  132. package/server/graphql/types/login-payload.js +12 -0
  133. package/server/graphql/types/me-role.js +14 -0
  134. package/server/graphql/types/me.js +16 -0
  135. package/server/graphql/types/password-payload.js +11 -0
  136. package/server/graphql/types/register-input.js +13 -0
  137. package/server/graphql/types/update-role-payload.js +11 -0
  138. package/server/graphql/utils.js +27 -0
  139. package/server/index.js +21 -0
  140. package/server/middlewares/index.js +7 -0
  141. package/server/middlewares/rateLimit.js +27 -0
  142. package/server/register.js +23 -0
  143. package/server/routes/admin/index.js +10 -0
  144. package/server/routes/admin/permissions.js +20 -0
  145. package/server/routes/admin/role.js +79 -0
  146. package/server/routes/admin/settings.js +95 -0
  147. package/server/routes/content-api/auth.js +82 -0
  148. package/server/routes/content-api/index.js +11 -0
  149. package/server/routes/content-api/permissions.js +9 -0
  150. package/server/routes/content-api/role.js +29 -0
  151. package/server/routes/content-api/user.js +60 -0
  152. package/server/routes/index.js +6 -0
  153. package/server/services/index.js +17 -0
  154. package/server/services/jwt.js +55 -0
  155. package/server/services/providers-registry.js +292 -0
  156. package/server/services/providers.js +115 -0
  157. package/server/services/role.js +177 -0
  158. package/server/services/user.js +140 -0
  159. package/server/services/users-permissions.js +236 -0
  160. package/server/strategies/users-permissions.js +102 -0
  161. package/server/utils/index.d.ts +16 -0
  162. package/server/utils/index.js +12 -0
  163. package/server/utils/sanitize/index.js +9 -0
  164. package/server/utils/sanitize/sanitizers.js +19 -0
  165. package/server/utils/sanitize/visitors/index.js +5 -0
  166. package/server/utils/sanitize/visitors/remove-user-relation-from-role-entities.js +11 -0
  167. package/strapi-admin.js +3 -0
  168. package/strapi-server.js +3 -0
@@ -0,0 +1,60 @@
1
+ import React from 'react';
2
+ import { useIntl } from 'react-intl';
3
+ import { Typography } from '@strapi/design-system/Typography';
4
+ import { Stack } from '@strapi/design-system/Stack';
5
+ import { GridItem } from '@strapi/design-system/Grid';
6
+ import { get, isEmpty, without } from 'lodash';
7
+ import { useUsersPermissions } from '../../contexts/UsersPermissionsContext';
8
+ import BoundRoute from '../BoundRoute';
9
+
10
+ const Policies = () => {
11
+ const { formatMessage } = useIntl();
12
+ const { selectedAction, routes } = useUsersPermissions();
13
+
14
+ const path = without(selectedAction.split('.'), 'controllers');
15
+ const controllerRoutes = get(routes, path[0]);
16
+ const pathResolved = path.slice(1).join('.');
17
+
18
+ const displayedRoutes = isEmpty(controllerRoutes)
19
+ ? []
20
+ : controllerRoutes.filter((o) => o.handler.endsWith(pathResolved));
21
+
22
+ return (
23
+ <GridItem
24
+ col={5}
25
+ background="neutral150"
26
+ paddingTop={6}
27
+ paddingBottom={6}
28
+ paddingLeft={7}
29
+ paddingRight={7}
30
+ style={{ minHeight: '100%' }}
31
+ >
32
+ {selectedAction ? (
33
+ <Stack spacing={2}>
34
+ {displayedRoutes.map((route, key) => (
35
+ // eslint-disable-next-line react/no-array-index-key
36
+ <BoundRoute key={key} route={route} />
37
+ ))}
38
+ </Stack>
39
+ ) : (
40
+ <Stack spacing={2}>
41
+ <Typography variant="delta" as="h3">
42
+ {formatMessage({
43
+ id: 'users-permissions.Policies.header.title',
44
+ defaultMessage: 'Advanced settings',
45
+ })}
46
+ </Typography>
47
+ <Typography as="p" textColor="neutral600">
48
+ {formatMessage({
49
+ id: 'users-permissions.Policies.header.hint',
50
+ defaultMessage:
51
+ "Select the application's actions or the plugin's actions and click on the cog icon to display the bound route",
52
+ })}
53
+ </Typography>
54
+ </Stack>
55
+ )}
56
+ </GridItem>
57
+ );
58
+ };
59
+
60
+ export default Policies;
@@ -0,0 +1,94 @@
1
+ import React, { memo, useReducer, forwardRef, useImperativeHandle } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Typography } from '@strapi/design-system/Typography';
4
+ import { Stack } from '@strapi/design-system/Stack';
5
+ import { Grid, GridItem } from '@strapi/design-system/Grid';
6
+ import { useIntl } from 'react-intl';
7
+ import getTrad from '../../utils/getTrad';
8
+ import Policies from '../Policies';
9
+ import Permissions from '../Permissions';
10
+ import reducer, { initialState } from './reducer';
11
+ import { UsersPermissionsProvider } from '../../contexts/UsersPermissionsContext';
12
+ import init from './init';
13
+
14
+ const UsersPermissions = forwardRef(({ permissions, routes }, ref) => {
15
+ const { formatMessage } = useIntl();
16
+ const [state, dispatch] = useReducer(reducer, initialState, (state) =>
17
+ init(state, permissions, routes)
18
+ );
19
+
20
+ useImperativeHandle(ref, () => ({
21
+ getPermissions() {
22
+ return {
23
+ permissions: state.modifiedData,
24
+ };
25
+ },
26
+ resetForm() {
27
+ dispatch({ type: 'ON_RESET' });
28
+ },
29
+ setFormAfterSubmit() {
30
+ dispatch({ type: 'ON_SUBMIT_SUCCEEDED' });
31
+ },
32
+ }));
33
+
34
+ const handleChange = ({ target: { name, value } }) =>
35
+ dispatch({
36
+ type: 'ON_CHANGE',
37
+ keys: name.split('.'),
38
+ value: value === 'empty__string_value' ? '' : value,
39
+ });
40
+
41
+ const handleChangeSelectAll = ({ target: { name, value } }) =>
42
+ dispatch({
43
+ type: 'ON_CHANGE_SELECT_ALL',
44
+ keys: name.split('.'),
45
+ value,
46
+ });
47
+
48
+ const handleSelectedAction = (actionToSelect) =>
49
+ dispatch({
50
+ type: 'SELECT_ACTION',
51
+ actionToSelect,
52
+ });
53
+
54
+ const providerValue = {
55
+ ...state,
56
+ onChange: handleChange,
57
+ onChangeSelectAll: handleChangeSelectAll,
58
+ onSelectedAction: handleSelectedAction,
59
+ };
60
+
61
+ return (
62
+ <UsersPermissionsProvider value={providerValue}>
63
+ <Grid gap={0} shadow="filterShadow" hasRadius background="neutral0">
64
+ <GridItem col={7} paddingTop={6} paddingBottom={6} paddingLeft={7} paddingRight={7}>
65
+ <Stack spacing={6}>
66
+ <Stack spacing={2}>
67
+ <Typography variant="delta" as="h2">
68
+ {formatMessage({
69
+ id: getTrad('Plugins.header.title'),
70
+ defaultMessage: 'Permissions',
71
+ })}
72
+ </Typography>
73
+ <Typography as="p" textColor="neutral600">
74
+ {formatMessage({
75
+ id: getTrad('Plugins.header.description'),
76
+ defaultMessage: 'Only actions bound by a route are listed below.',
77
+ })}
78
+ </Typography>
79
+ </Stack>
80
+ <Permissions />
81
+ </Stack>
82
+ </GridItem>
83
+ <Policies />
84
+ </Grid>
85
+ </UsersPermissionsProvider>
86
+ );
87
+ });
88
+
89
+ UsersPermissions.propTypes = {
90
+ permissions: PropTypes.object.isRequired,
91
+ routes: PropTypes.object.isRequired,
92
+ };
93
+
94
+ export default memo(UsersPermissions);
@@ -0,0 +1,10 @@
1
+ const init = (state, permissions, routes) => {
2
+ return {
3
+ ...state,
4
+ initialData: permissions,
5
+ modifiedData: permissions,
6
+ routes,
7
+ };
8
+ };
9
+
10
+ export default init;
@@ -0,0 +1,60 @@
1
+ /* eslint-disable consistent-return */
2
+ import produce from 'immer';
3
+ import { set, get, take } from 'lodash';
4
+
5
+ export const initialState = {
6
+ initialData: {},
7
+ modifiedData: {},
8
+ routes: {},
9
+ selectedAction: '',
10
+ policies: [],
11
+ };
12
+
13
+ const reducer = (state, action) =>
14
+ produce(state, (draftState) => {
15
+ switch (action.type) {
16
+ case 'ON_CHANGE': {
17
+ const keysLength = action.keys.length;
18
+ const isChangingCheckbox = action.keys[keysLength - 1] === 'enabled';
19
+
20
+ if (action.value && isChangingCheckbox) {
21
+ const selectedAction = take(action.keys, keysLength - 1).join('.');
22
+ draftState.selectedAction = selectedAction;
23
+ }
24
+
25
+ set(draftState, ['modifiedData', ...action.keys], action.value);
26
+ break;
27
+ }
28
+ case 'ON_CHANGE_SELECT_ALL': {
29
+ const pathToValue = ['modifiedData', ...action.keys];
30
+ const oldValues = get(state, pathToValue, {});
31
+ const updatedValues = Object.keys(oldValues).reduce((acc, current) => {
32
+ acc[current] = { ...oldValues[current], enabled: action.value };
33
+
34
+ return acc;
35
+ }, {});
36
+
37
+ set(draftState, pathToValue, updatedValues);
38
+
39
+ break;
40
+ }
41
+ case 'ON_RESET': {
42
+ draftState.modifiedData = state.initialData;
43
+ break;
44
+ }
45
+ case 'ON_SUBMIT_SUCCEEDED': {
46
+ draftState.initialData = state.modifiedData;
47
+ break;
48
+ }
49
+
50
+ case 'SELECT_ACTION': {
51
+ const { actionToSelect } = action;
52
+ draftState.selectedAction = actionToSelect === state.selectedAction ? '' : actionToSelect;
53
+ break;
54
+ }
55
+ default:
56
+ return draftState;
57
+ }
58
+ });
59
+
60
+ export default reducer;
@@ -0,0 +1,17 @@
1
+ import React, { createContext, useContext } from 'react';
2
+ import PropTypes from 'prop-types';
3
+
4
+ const UsersPermissions = createContext({});
5
+
6
+ const UsersPermissionsProvider = ({ children, value }) => {
7
+ return <UsersPermissions.Provider value={value}>{children}</UsersPermissions.Provider>;
8
+ };
9
+
10
+ const useUsersPermissions = () => useContext(UsersPermissions);
11
+
12
+ UsersPermissionsProvider.propTypes = {
13
+ children: PropTypes.node.isRequired,
14
+ value: PropTypes.object.isRequired,
15
+ };
16
+
17
+ export { UsersPermissions, UsersPermissionsProvider, useUsersPermissions };
@@ -0,0 +1,5 @@
1
+ // eslint-disable-next-line import/prefer-default-export
2
+ export { default as useForm } from './useForm';
3
+ export { default as useRolesList } from './useRolesList';
4
+ export { default as usePlugins } from './usePlugins';
5
+ export { default as useFetchRole } from './useFetchRole';
@@ -0,0 +1,64 @@
1
+ import { useCallback, useReducer, useEffect, useRef } from 'react';
2
+ import { useNotification } from '@strapi/helper-plugin';
3
+ import reducer, { initialState } from './reducer';
4
+ import axiosIntance from '../../utils/axiosInstance';
5
+ import pluginId from '../../pluginId';
6
+
7
+ const useFetchRole = (id) => {
8
+ const [state, dispatch] = useReducer(reducer, initialState);
9
+ const toggleNotification = useNotification();
10
+ const isMounted = useRef(null);
11
+
12
+ useEffect(() => {
13
+ isMounted.current = true;
14
+
15
+ if (id) {
16
+ fetchRole(id);
17
+ } else {
18
+ dispatch({
19
+ type: 'GET_DATA_SUCCEEDED',
20
+ role: {},
21
+ });
22
+ }
23
+
24
+ return () => (isMounted.current = false);
25
+ // eslint-disable-next-line react-hooks/exhaustive-deps
26
+ }, [id]);
27
+
28
+ const fetchRole = async (roleId) => {
29
+ try {
30
+ const {
31
+ data: { role },
32
+ } = await axiosIntance.get(`/${pluginId}/roles/${roleId}`);
33
+
34
+ // Prevent updating state on an unmounted component
35
+ if (isMounted.current) {
36
+ dispatch({
37
+ type: 'GET_DATA_SUCCEEDED',
38
+ role,
39
+ });
40
+ }
41
+ } catch (err) {
42
+ console.error(err);
43
+
44
+ dispatch({
45
+ type: 'GET_DATA_ERROR',
46
+ });
47
+ toggleNotification({
48
+ type: 'warning',
49
+ message: { id: 'notification.error' },
50
+ });
51
+ }
52
+ };
53
+
54
+ const handleSubmitSucceeded = useCallback((data) => {
55
+ dispatch({
56
+ type: 'ON_SUBMIT_SUCCEEDED',
57
+ ...data,
58
+ });
59
+ }, []);
60
+
61
+ return { ...state, onSubmitSucceeded: handleSubmitSucceeded };
62
+ };
63
+
64
+ export default useFetchRole;
@@ -0,0 +1,31 @@
1
+ /* eslint-disable consistent-return */
2
+ import produce from 'immer';
3
+
4
+ export const initialState = {
5
+ role: {},
6
+ isLoading: true,
7
+ };
8
+
9
+ const reducer = (state, action) =>
10
+ produce(state, (draftState) => {
11
+ switch (action.type) {
12
+ case 'GET_DATA_SUCCEEDED': {
13
+ draftState.role = action.role;
14
+ draftState.isLoading = false;
15
+ break;
16
+ }
17
+ case 'GET_DATA_ERROR': {
18
+ draftState.isLoading = false;
19
+ break;
20
+ }
21
+ case 'ON_SUBMIT_SUCCEEDED': {
22
+ draftState.role.name = action.name;
23
+ draftState.role.description = action.description;
24
+ break;
25
+ }
26
+ default:
27
+ return draftState;
28
+ }
29
+ });
30
+
31
+ export default reducer;
@@ -0,0 +1,70 @@
1
+ import { useCallback, useEffect, useReducer, useRef } from 'react';
2
+ import { useRBAC, request, useNotification } from '@strapi/helper-plugin';
3
+ import { getRequestURL } from '../../utils';
4
+ import reducer, { initialState } from './reducer';
5
+
6
+ const useUserForm = (endPoint, permissions) => {
7
+ const { isLoading: isLoadingForPermissions, allowedActions } = useRBAC(permissions);
8
+ const [{ isLoading, modifiedData }, dispatch] = useReducer(reducer, initialState);
9
+ const toggleNotification = useNotification();
10
+ const isMounted = useRef(true);
11
+
12
+ const abortController = new AbortController();
13
+ const { signal } = abortController;
14
+
15
+ useEffect(() => {
16
+ const getData = async () => {
17
+ try {
18
+ dispatch({
19
+ type: 'GET_DATA',
20
+ });
21
+
22
+ const data = await request(getRequestURL(endPoint), { method: 'GET', signal });
23
+
24
+ dispatch({
25
+ type: 'GET_DATA_SUCCEEDED',
26
+ data,
27
+ });
28
+ } catch (err) {
29
+ // The user aborted the request
30
+ if (isMounted.current) {
31
+ dispatch({
32
+ type: 'GET_DATA_ERROR',
33
+ });
34
+ console.error(err);
35
+ toggleNotification({
36
+ type: 'warning',
37
+ message: { id: 'notification.error' },
38
+ });
39
+ }
40
+ }
41
+ };
42
+
43
+ if (!isLoadingForPermissions) {
44
+ getData();
45
+ }
46
+
47
+ return () => {
48
+ abortController.abort();
49
+ isMounted.current = false;
50
+ };
51
+ // eslint-disable-next-line react-hooks/exhaustive-deps
52
+ }, [isLoadingForPermissions, endPoint]);
53
+
54
+ const dispatchSubmitSucceeded = useCallback((data) => {
55
+ dispatch({
56
+ type: 'ON_SUBMIT_SUCCEEDED',
57
+ data,
58
+ });
59
+ }, []);
60
+
61
+ return {
62
+ allowedActions,
63
+ dispatchSubmitSucceeded,
64
+ isLoading,
65
+ isLoadingForPermissions,
66
+ modifiedData,
67
+ };
68
+ };
69
+
70
+ export default useUserForm;
@@ -0,0 +1,40 @@
1
+ import produce from 'immer';
2
+
3
+ const initialState = {
4
+ isLoading: true,
5
+ modifiedData: {},
6
+ };
7
+
8
+ const reducer = (state, action) =>
9
+ // eslint-disable-next-line consistent-return
10
+ produce(state, (draftState) => {
11
+ switch (action.type) {
12
+ case 'GET_DATA': {
13
+ draftState.isLoading = true;
14
+ draftState.modifiedData = {};
15
+
16
+ break;
17
+ }
18
+ case 'GET_DATA_SUCCEEDED': {
19
+ draftState.isLoading = false;
20
+ draftState.modifiedData = action.data;
21
+
22
+ break;
23
+ }
24
+ case 'GET_DATA_ERROR': {
25
+ draftState.isLoading = true;
26
+ break;
27
+ }
28
+ case 'ON_SUBMIT_SUCCEEDED': {
29
+ draftState.modifiedData = action.data;
30
+
31
+ break;
32
+ }
33
+ default: {
34
+ return draftState;
35
+ }
36
+ }
37
+ });
38
+
39
+ export default reducer;
40
+ export { initialState };
@@ -0,0 +1,65 @@
1
+ import { useCallback, useEffect, useReducer } from 'react';
2
+ import { useNotification } from '@strapi/helper-plugin';
3
+ import { get } from 'lodash';
4
+ import init from './init';
5
+ import pluginId from '../../pluginId';
6
+ import { cleanPermissions } from '../../utils';
7
+ import axiosInstance from '../../utils/axiosInstance';
8
+ import reducer, { initialState } from './reducer';
9
+
10
+ const usePlugins = (shouldFetchData = true) => {
11
+ const toggleNotification = useNotification();
12
+ const [{ permissions, routes, isLoading }, dispatch] = useReducer(reducer, initialState, () =>
13
+ init(initialState, shouldFetchData)
14
+ );
15
+
16
+ const fetchPlugins = useCallback(async () => {
17
+ try {
18
+ dispatch({
19
+ type: 'GET_DATA',
20
+ });
21
+
22
+ const [{ permissions }, { routes }] = await Promise.all(
23
+ [`/${pluginId}/permissions`, `/${pluginId}/routes`].map(async (endpoint) => {
24
+ const res = await axiosInstance.get(endpoint);
25
+
26
+ return res.data;
27
+ })
28
+ );
29
+
30
+ dispatch({
31
+ type: 'GET_DATA_SUCCEEDED',
32
+ permissions: cleanPermissions(permissions),
33
+ routes,
34
+ });
35
+ } catch (err) {
36
+ const message = get(err, ['response', 'payload', 'message'], 'An error occured');
37
+
38
+ dispatch({
39
+ type: 'GET_DATA_ERROR',
40
+ });
41
+
42
+ if (message !== 'Forbidden') {
43
+ toggleNotification({
44
+ type: 'warning',
45
+ message,
46
+ });
47
+ }
48
+ }
49
+ }, [toggleNotification]);
50
+
51
+ useEffect(() => {
52
+ if (shouldFetchData) {
53
+ fetchPlugins();
54
+ }
55
+ }, [fetchPlugins, shouldFetchData]);
56
+
57
+ return {
58
+ permissions,
59
+ routes,
60
+ getData: fetchPlugins,
61
+ isLoading,
62
+ };
63
+ };
64
+
65
+ export default usePlugins;
@@ -0,0 +1,5 @@
1
+ const init = (initialState, shouldFetchData) => {
2
+ return { ...initialState, isLoading: shouldFetchData };
3
+ };
4
+
5
+ export default init;
@@ -0,0 +1,34 @@
1
+ /* eslint-disable consistent-return */
2
+ import produce from 'immer';
3
+
4
+ export const initialState = {
5
+ permissions: {},
6
+ routes: {},
7
+ isLoading: true,
8
+ };
9
+
10
+ const reducer = (state, action) =>
11
+ produce(state, (draftState) => {
12
+ switch (action.type) {
13
+ case 'GET_DATA': {
14
+ draftState.isLoading = true;
15
+ draftState.permissions = {};
16
+ draftState.routes = {};
17
+ break;
18
+ }
19
+ case 'GET_DATA_SUCCEEDED': {
20
+ draftState.permissions = action.permissions;
21
+ draftState.routes = action.routes;
22
+ draftState.isLoading = false;
23
+ break;
24
+ }
25
+ case 'GET_DATA_ERROR': {
26
+ draftState.isLoading = false;
27
+ break;
28
+ }
29
+ default:
30
+ return draftState;
31
+ }
32
+ });
33
+
34
+ export default reducer;
@@ -0,0 +1,63 @@
1
+ import { useEffect, useReducer, useRef } from 'react';
2
+ import { request, useNotification } from '@strapi/helper-plugin';
3
+ import { get } from 'lodash';
4
+ import init from './init';
5
+ import pluginId from '../../pluginId';
6
+ import reducer, { initialState } from './reducer';
7
+
8
+ const useRolesList = (shouldFetchData = true) => {
9
+ const [{ roles, isLoading }, dispatch] = useReducer(reducer, initialState, () =>
10
+ init(initialState, shouldFetchData)
11
+ );
12
+ const toggleNotification = useNotification();
13
+
14
+ const isMounted = useRef(true);
15
+ const abortController = new AbortController();
16
+ const { signal } = abortController;
17
+
18
+ useEffect(() => {
19
+ if (shouldFetchData) {
20
+ fetchRolesList();
21
+ }
22
+
23
+ return () => {
24
+ abortController.abort();
25
+ isMounted.current = false;
26
+ };
27
+ // eslint-disable-next-line react-hooks/exhaustive-deps
28
+ }, [shouldFetchData]);
29
+
30
+ const fetchRolesList = async () => {
31
+ try {
32
+ dispatch({
33
+ type: 'GET_DATA',
34
+ });
35
+
36
+ const { roles } = await request(`/${pluginId}/roles`, { method: 'GET', signal });
37
+
38
+ dispatch({
39
+ type: 'GET_DATA_SUCCEEDED',
40
+ data: roles,
41
+ });
42
+ } catch (err) {
43
+ const message = get(err, ['response', 'payload', 'message'], 'An error occured');
44
+
45
+ if (isMounted.current) {
46
+ dispatch({
47
+ type: 'GET_DATA_ERROR',
48
+ });
49
+
50
+ if (message !== 'Forbidden') {
51
+ toggleNotification({
52
+ type: 'warning',
53
+ message,
54
+ });
55
+ }
56
+ }
57
+ }
58
+ };
59
+
60
+ return { roles, isLoading, getData: fetchRolesList };
61
+ };
62
+
63
+ export default useRolesList;
@@ -0,0 +1,5 @@
1
+ const init = (initialState, shouldFetchData) => {
2
+ return { ...initialState, isLoading: shouldFetchData };
3
+ };
4
+
5
+ export default init;
@@ -0,0 +1,31 @@
1
+ /* eslint-disable consistent-return */
2
+ import produce from 'immer';
3
+
4
+ export const initialState = {
5
+ roles: [],
6
+ isLoading: true,
7
+ };
8
+
9
+ const reducer = (state, action) =>
10
+ produce(state, (draftState) => {
11
+ switch (action.type) {
12
+ case 'GET_DATA': {
13
+ draftState.isLoading = true;
14
+ draftState.roles = [];
15
+ break;
16
+ }
17
+ case 'GET_DATA_SUCCEEDED': {
18
+ draftState.roles = action.data;
19
+ draftState.isLoading = false;
20
+ break;
21
+ }
22
+ case 'GET_DATA_ERROR': {
23
+ draftState.isLoading = false;
24
+ break;
25
+ }
26
+ default:
27
+ return draftState;
28
+ }
29
+ });
30
+
31
+ export default reducer;