@canmingir/link 1.2.5 → 1.2.6

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canmingir/link",
3
- "version": "1.2.5",
3
+ "version": "1.2.6",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./index.js",
@@ -1,13 +1,10 @@
1
- import config from "../config/config";
2
1
  import { jwtDecode } from "jwt-decode";
3
2
  import { storage } from "@nucleoidjs/webstorage";
4
3
 
5
- const { name } = config();
6
-
7
4
  let login = true;
8
5
  const itemId = storage.get("itemId");
9
6
  try {
10
- const token = storage.get(name, "accessToken");
7
+ const token = storage.get("link", "accessToken");
11
8
  const decodedToken = jwtDecode(token);
12
9
 
13
10
  if (decodedToken.exp * 1000 < Date.now()) {
package/src/http/index.js CHANGED
@@ -15,8 +15,8 @@ const instance = axios.create({
15
15
  });
16
16
 
17
17
  instance.interceptors.request.use((request) => {
18
- const { name, base } = config();
19
- const accessToken = storage.get(name, "accessToken");
18
+ const { base } = config();
19
+ const accessToken = storage.get("link", "accessToken");
20
20
 
21
21
  if (!accessToken) {
22
22
  window.location.href = base === "/" ? "/login" : `${base}/login`;
@@ -85,14 +85,17 @@ export const fetcher = (url) => instance.get(url).then((res) => res.data);
85
85
 
86
86
  const refreshAuthLogic = async (failedRequest) => {
87
87
  try {
88
- const { name, appId } = config();
88
+ const { appId } = config();
89
89
 
90
90
  const projectId = storage.get("projectId");
91
91
 
92
+ const identityProvider = storage.get("link", "identityProvider");
93
+
92
94
  const { data } = await oauth.post("/oauth", {
93
- refreshToken: storage.get(name, "refreshToken"),
95
+ refreshToken: storage.get("link", "refreshToken"),
94
96
  appId,
95
97
  projectId,
98
+ identityProvider,
96
99
  });
97
100
 
98
101
  const accessToken = data.accessToken;
@@ -100,13 +103,13 @@ const refreshAuthLogic = async (failedRequest) => {
100
103
  failedRequest.response.config.headers["Authorization"] =
101
104
  "Bearer " + accessToken;
102
105
 
103
- storage.set(name, "accessToken", accessToken);
106
+ storage.set("link", "accessToken", accessToken);
104
107
  return Promise.resolve();
105
108
  } catch (error) {
106
109
  const { name, base } = config();
107
110
 
108
- storage.remove(name, "accessToken");
109
- storage.remove(name, "refreshToken");
111
+ storage.remove("link", "accessToken");
112
+ storage.remove("link", "refreshToken");
110
113
 
111
114
  window.location.href = `${window.location.origin}${base}/login`;
112
115
  return Promise.reject(error);
@@ -122,7 +125,7 @@ refreshInterceptor(instance, refreshAuthLogic, {
122
125
  statusCodes: [401, 403],
123
126
  shouldRefresh: () => {
124
127
  const { name, base } = config();
125
- const token = storage.get(name, "accessToken");
128
+ const token = storage.get("link", "accessToken");
126
129
 
127
130
  if (!token) {
128
131
  window.location.href = `${window.location.origin}${base}/login`;
package/src/http/user.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import axios from "axios";
2
2
  import config from "../config/config.js";
3
3
  import http from "./index";
4
+ import { jwtDecode } from "jwt-decode";
5
+ import oauth from "./oauth";
4
6
  import { storage } from "@nucleoidjs/webstorage";
5
7
 
6
8
  const instance = axios.create({
@@ -11,15 +13,8 @@ const instance = axios.create({
11
13
  },
12
14
  });
13
15
 
14
- function getProjectName() {
15
- const { name } = config();
16
- if (name) {
17
- return name;
18
- }
19
- }
20
-
21
16
  instance.interceptors.request.use(async (request) => {
22
- const refreshToken = await storage.get(getProjectName(), "refreshToken");
17
+ const refreshToken = await storage.get("link", "refreshToken");
23
18
  if (refreshToken) {
24
19
  request.headers["Authorization"] = `Bearer ${refreshToken}`;
25
20
  }
@@ -28,25 +23,62 @@ instance.interceptors.request.use(async (request) => {
28
23
 
29
24
  instance.getUserDetails = async () => {
30
25
  try {
31
- const refreshToken = await storage.get(getProjectName(), "refreshToken");
26
+ const refreshToken = await storage.get("link", "refreshToken");
27
+
32
28
  if (!refreshToken) {
33
29
  console.log("No refresh token found");
34
30
  return null;
35
31
  }
36
32
 
33
+ let accessToken = await storage.get("link", "accessToken");
34
+
35
+ if (!accessToken) {
36
+ console.log("No access token found");
37
+ return null;
38
+ }
39
+
40
+ try {
41
+ const decodedToken = jwtDecode(accessToken);
42
+ const isExpired = decodedToken.exp * 1000 < Date.now();
43
+
44
+ if (isExpired) {
45
+ console.log(
46
+ "Access token expired, refreshing before fetching user details..."
47
+ );
48
+ const { appId } = config();
49
+ const projectId = storage.get("projectId");
50
+ const identityProvider = storage.get("link", "identityProvider");
51
+
52
+ const { data } = await oauth.post("/oauth", {
53
+ refreshToken,
54
+ appId,
55
+ projectId,
56
+ identityProvider,
57
+ });
58
+
59
+ accessToken = data.accessToken;
60
+ storage.set("link", "accessToken", accessToken);
61
+ console.log(
62
+ "Access token refreshed successfully, now fetching user details"
63
+ );
64
+ }
65
+ } catch (error) {
66
+ console.error("Error checking or refreshing token:", error);
67
+ return null;
68
+ }
69
+
37
70
  const response = await http.get("/oauth/user", {
38
71
  headers: {
39
- 'X-Refresh-Token': refreshToken
40
- }
72
+ "X-Refresh-Token": refreshToken,
73
+ },
41
74
  });
42
-
75
+
43
76
  if (response.data && response.data.user) {
44
77
  return response.data.user;
45
78
  }
46
-
79
+
47
80
  console.log("No user data received from server");
48
81
  return null;
49
-
50
82
  } catch (error) {
51
83
  console.error("Error fetching user details from server:", error);
52
84
  return null;
@@ -55,7 +87,7 @@ instance.getUserDetails = async () => {
55
87
 
56
88
  instance.getPermittedUsers = async () => {
57
89
  const userIds = [];
58
- const refreshToken = await storage.get(getProjectName(), "refreshToken");
90
+ const refreshToken = await storage.get("link", "refreshToken");
59
91
  const response = await http.get("/permissions");
60
92
 
61
93
  response.data.forEach((permission) => {
@@ -75,4 +107,4 @@ instance.getPermittedUsers = async () => {
75
107
  return users;
76
108
  };
77
109
 
78
- export default instance;
110
+ export default instance;
@@ -9,15 +9,16 @@ import SettingsDialog from "../../widgets/SettingsDialog";
9
9
  import Stack from "@mui/material/Stack";
10
10
  import config from "../../config/config";
11
11
  import { hideScroll } from "../../theme/css";
12
+ import { useResponsive } from "../../hooks/use-responsive";
12
13
  import { useUser } from "../../hooks/use-user";
13
14
 
14
15
  import React, { useState } from "react";
15
16
 
16
17
  export default function NavMini({ only }) {
17
18
  const { user } = useUser();
18
- const { sideMenu } = config().menu;
19
+ const { sideMenu, actionButtons } = config().menu;
19
20
  const [openSettings, setOpenSettings] = useState(false);
20
-
21
+ const lgUp = useResponsive("up", "lg");
21
22
  const handleCloseSettings = () => {
22
23
  setOpenSettings(false);
23
24
  };
@@ -57,21 +58,40 @@ export default function NavMini({ only }) {
57
58
  }}
58
59
  />
59
60
  <Box sx={{ flexGrow: 1 }} />
60
- <Button
61
- data-cy="end-item"
62
- fullWidth={true}
63
- onClick={() => setOpenSettings(true)}
61
+ <Stack
62
+ direction={"column"}
63
+ alignItems={"center"}
64
+ justifyItems={"center"}
65
+ sx={{
66
+ marginBottom: lgUp ? 3 : 0,
67
+ position: lgUp ? "static" : "fixed",
68
+ bottom: lgUp ? "auto" : 66,
69
+ width: "100%",
70
+ }}
71
+ gap={2}
64
72
  >
65
- <Iconify
66
- icon={"ic:baseline-settings"}
73
+ {actionButtons &&
74
+ actionButtons.map((Action, index) => (
75
+ <Box key={index} component={Action}></Box>
76
+ ))}
77
+ <Button
78
+ onClick={() => setOpenSettings(true)}
67
79
  sx={{
68
- width: 32,
69
- height: 32,
70
- color: "text.secondary",
71
- mx: "auto",
80
+ position: lgUp ? "static" : "fixed",
81
+ bottom: lgUp ? "auto" : 16,
82
+ width: "100%",
72
83
  }}
73
- />
74
- </Button>
84
+ >
85
+ <Iconify
86
+ icon={"ic:baseline-settings"}
87
+ sx={{
88
+ width: 32,
89
+ height: 32,
90
+ color: "text.secondary",
91
+ }}
92
+ />
93
+ </Button>
94
+ </Stack>
75
95
  <SettingsDialog open={openSettings} handleClose={handleCloseSettings} />
76
96
  </Stack>
77
97
  </Box>
@@ -16,7 +16,7 @@ function NavHorizontal() {
16
16
 
17
17
  const { user } = useUser();
18
18
 
19
- const { sideMenu } = config().menu;
19
+ const { sideMenu, actionButtons } = config().menu;
20
20
  return (
21
21
  <AppBar component="div" data-cy="nav-horizontal">
22
22
  <Toolbar
@@ -35,6 +35,11 @@ function NavHorizontal() {
35
35
  ...theme.mixins.toolbar,
36
36
  }}
37
37
  />
38
+
39
+ {actionButtons &&
40
+ actionButtons.map((Action, index) => (
41
+ <Box key={index} component={Action}></Box>
42
+ ))}
38
43
  </Toolbar>
39
44
 
40
45
  <HeaderShadow />
@@ -1,10 +1,3 @@
1
- import { Button, DialogActions } from "@mui/material";
2
- import Dialog, { dialogClasses } from "@mui/material/Dialog";
3
- import React, { useCallback, useState } from "react";
4
- import { publish, useEvent } from "@nucleoidai/react-event";
5
- import { storage, useStorage } from "@nucleoidjs/webstorage";
6
- import { useMediaQuery, useTheme } from "@mui/material";
7
-
8
1
  import Box from "@mui/material/Box";
9
2
  import IconButton from "@mui/material/IconButton";
10
3
  import Iconify from "../../../components/Iconify";
@@ -29,9 +22,16 @@ import { useEventListener } from "../../../hooks/use-event-listener";
29
22
  import { useNavigate } from "react-router-dom";
30
23
  import useProjects from "../../../hooks/useProjects";
31
24
 
25
+ import { Button, DialogActions } from "@mui/material";
26
+ import Dialog, { dialogClasses } from "@mui/material/Dialog";
27
+ import React, { useCallback, useState } from "react";
28
+ import { publish, useEvent } from "@nucleoidai/react-event";
29
+ import { storage, useStorage } from "@nucleoidjs/webstorage";
30
+ import { useMediaQuery, useTheme } from "@mui/material";
31
+
32
32
  function ProjectBar() {
33
33
  const label = config().template?.projectBar?.label;
34
- const { appId, name } = config();
34
+ const { appId } = config();
35
35
  const theme = useTheme();
36
36
  const isSmallScreen = useMediaQuery(theme.breakpoints.down(435));
37
37
  const { loading, projects, getProjects } = useProjects();
@@ -91,15 +91,15 @@ function ProjectBar() {
91
91
  const handleSelect = (project) => {
92
92
  const { id: projectId } = project;
93
93
 
94
- const refreshToken = storage.get(name, "refreshToken");
95
- const provider = storage.get(name, "provider");
94
+ const refreshToken = storage.get("link", "refreshToken");
95
+ const identityProvider = storage.get("link", "identityProvider");
96
96
 
97
97
  oauth
98
- .post("/oauth", { appId, refreshToken, projectId, provider })
98
+ .post("/oauth", { appId, refreshToken, projectId, identityProvider })
99
99
  .then(({ data }) => {
100
100
  const { refreshToken, accessToken } = data;
101
- storage.set(name, "accessToken", accessToken);
102
- storage.set(name, "refreshToken", refreshToken);
101
+ storage.set("link", "accessToken", accessToken);
102
+ storage.set("link", "refreshToken", refreshToken);
103
103
  storage.set("projectId", projectId);
104
104
  })
105
105
  .finally(() => {
@@ -19,7 +19,6 @@ import CustomPopover, { usePopover } from "../../components/custom-popover";
19
19
  // ----------------------------------------------------------------------
20
20
 
21
21
  export default function AccountPopover() {
22
- const { name } = config();
23
22
  const { options } = config().menu;
24
23
  const router = useRouter();
25
24
  const { user } = useUser();
@@ -28,8 +27,8 @@ export default function AccountPopover() {
28
27
 
29
28
  const handleLogout = async () => {
30
29
  try {
31
- storage.remove(name, "accessToken");
32
- storage.remove(name, "refreshToken");
30
+ storage.remove("link", "accessToken");
31
+ storage.remove("link", "refreshToken");
33
32
  popover.onClose();
34
33
  router.replace("/login");
35
34
  } catch (error) {
@@ -25,23 +25,19 @@ const FlowNode = ({ node, type, variant, style, plugin }) => {
25
25
  styleTokens = style;
26
26
  }
27
27
 
28
- let plugins = null;
28
+ let _plugin = null;
29
29
  if (plugin) {
30
30
  if (typeof plugin === "function") {
31
- plugin = plugin(type, node);
32
- } else if (
33
- typeof plugin === "object" &&
34
- (typeof plugin.renderNode === "function" ||
35
- typeof plugin.resolveStyle === "function")
36
- ) {
37
- plugins = plugin;
31
+ _plugin = plugin(type, node) || null;
32
+ } else if (typeof plugin === "object") {
33
+ _plugin = plugin;
38
34
  }
39
35
  }
40
36
 
41
37
  let pluginTokens = {};
42
- if (plugins && typeof plugins.resolveStyle === "function") {
38
+ if (_plugin && typeof _plugin.style === "function") {
43
39
  pluginTokens =
44
- plugins.resolveStyle({
40
+ _plugin.style({
45
41
  node,
46
42
  style: styleTokens,
47
43
  }) || {};
@@ -194,8 +190,8 @@ const FlowNode = ({ node, type, variant, style, plugin }) => {
194
190
  };
195
191
 
196
192
  const renderContent = () => {
197
- if (plugin && typeof plugin.renderNode === "function") {
198
- return plugin.renderNode({
193
+ if (_plugin && typeof _plugin.node === "function") {
194
+ return _plugin.node({
199
195
  node,
200
196
  title,
201
197
  subtitle,
@@ -1,5 +1,3 @@
1
- import { useEffect, useRef } from "react";
2
-
3
1
  import Page from "../layouts/Page";
4
2
  import React from "react";
5
3
  import config from "../config/config";
@@ -10,6 +8,8 @@ import { useContext } from "../ContextProvider/ContextProvider";
10
8
  import { useLocation } from "react-router-dom";
11
9
  import { useNavigate } from "react-router-dom";
12
10
 
11
+ import { useEffect, useRef } from "react";
12
+
13
13
  function Callback() {
14
14
  const { project: appConfig, name, appId } = config();
15
15
  const projectBar = config().template?.projectBar;
@@ -28,12 +28,12 @@ function Callback() {
28
28
  const parsedQuery = qs.parse(location.search, { ignoreQueryPrefix: true });
29
29
  const { code, error, error_description, state } = parsedQuery;
30
30
 
31
- let provider;
31
+ let identityProvider;
32
32
  let stateData = {};
33
33
 
34
34
  if (state) {
35
35
  stateData = JSON.parse(decodeURIComponent(state));
36
- provider = stateData.provider;
36
+ identityProvider = stateData.identityProvider;
37
37
  }
38
38
 
39
39
  if (error) {
@@ -59,7 +59,7 @@ function Callback() {
59
59
  google,
60
60
  };
61
61
 
62
- const providerConfig = providerConfigs[provider];
62
+ const providerConfig = providerConfigs[identityProvider];
63
63
 
64
64
  if (!providerConfig) {
65
65
  console.error("Could not determine OAuth provider or redirect URI");
@@ -85,7 +85,7 @@ function Callback() {
85
85
  appId,
86
86
  code,
87
87
  redirectUri,
88
- provider: provider,
88
+ identityProvider: identityProvider,
89
89
  grant_type: "authorization_code",
90
90
  })
91
91
  .then(({ data }) => {
@@ -93,10 +93,10 @@ function Callback() {
93
93
  const refreshToken = data.refreshToken;
94
94
  const userInfo = data.user;
95
95
 
96
- storage.set(name, "accessToken", accessToken);
97
- storage.set(name, "refreshToken", refreshToken);
96
+ storage.set("link", "accessToken", accessToken);
97
+ storage.set("link", "refreshToken", refreshToken);
98
98
  // TODO - update provider info
99
- storage.set(name, "provider", provider);
99
+ storage.set("link", "identityProvider", identityProvider);
100
100
 
101
101
  dispatch({ type: "LOGIN", payload: { user: userInfo } });
102
102
 
@@ -11,7 +11,10 @@ function LoginPage() {
11
11
  const navigate = useNavigate();
12
12
 
13
13
  function token() {
14
- if (storage.get(name, "refreshToken") && storage.get(name, "accessToken")) {
14
+ if (
15
+ storage.get("link", "refreshToken") &&
16
+ storage.get("link", "accessToken")
17
+ ) {
15
18
  return true;
16
19
  } else {
17
20
  return false;
@@ -19,7 +19,10 @@ export default function Auth0LoginView() {
19
19
  const navigate = useNavigate();
20
20
 
21
21
  function token() {
22
- if (storage.get(name, "refreshToken") && storage.get(name, "accessToken")) {
22
+ if (
23
+ storage.get("link", "refreshToken") &&
24
+ storage.get("link", "accessToken")
25
+ ) {
23
26
  return true;
24
27
  } else {
25
28
  return false;
@@ -1,17 +1,17 @@
1
- import { Box, Divider, Link as MuiLink, Typography } from "@mui/material";
2
- import React, { useState } from "react";
3
-
4
1
  import NucleoidLoginForm from "../../components/NucleoidLoginForm";
5
2
  import SocialLoginButtons from "../../components/SocialLoginButtons";
6
3
  import Stack from "@mui/material/Stack";
7
4
  import config from "../../config/config";
8
5
 
6
+ import { Box, Divider, Link as MuiLink, Typography } from "@mui/material";
7
+ import React, { useState } from "react";
8
+
9
9
  const handleOAuthLogin = (
10
10
  { redirectUri, authUrl, clientId, scope },
11
- provider
11
+ identityProvider
12
12
  ) => {
13
13
  const state = JSON.stringify({
14
- provider: provider,
14
+ identityProvider: identityProvider,
15
15
  });
16
16
  const encodedState = encodeURIComponent(state);
17
17
  window.location.href = `${authUrl}?client_id=${clientId}&scope=${scope}&response_type=code&redirect_uri=${redirectUri}&state=${encodedState}`;