@canmingir/link 1.2.53 → 1.2.55

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.53",
3
+ "version": "1.2.55",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "link-server": "./server/server.ts"
@@ -41,7 +41,6 @@
41
41
  "autosuggest-highlight": "^3.3.4",
42
42
  "aws-amplify": "^6.16.0",
43
43
  "axios": "^1.10.0",
44
- "axios-auth-refresh": "^3.3.6",
45
44
  "axios-retry": "^4.4.1",
46
45
  "cypress": "^15.3.0",
47
46
  "date-fns": "^3.6.0",
@@ -60,10 +59,10 @@
60
59
  "react-apexcharts": "^1.4.1",
61
60
  "react-chartjs-2": "^5.2.0",
62
61
  "react-dom": "^18.2.0",
63
- "react-is": "^18.3.1",
64
62
  "react-dropzone": "^14.2.3",
65
63
  "react-helmet-async": "^2.0.5",
66
64
  "react-hook-form": "^7.52.1",
65
+ "react-is": "^18.3.1",
67
66
  "react-lazy-load-image-component": "^1.6.2",
68
67
  "react-map-gl": "^7.1.7",
69
68
  "react-markdown": "^9.0.1",
@@ -1,66 +1,30 @@
1
- import React, { useEffect, useRef, useState } from "react";
1
+ import React, { useState } from "react";
2
2
 
3
3
  import Box from "@mui/material/Box";
4
4
  import Link from "@mui/material/Link";
5
5
  import { RouterLink } from "../../routes/components";
6
6
  import config from "../../config/config";
7
7
 
8
- const resolvedDimensions = {};
9
-
10
8
  const Logo = ({ disabledLink = false, sx, maxSize = 65, isLogin = false }) => {
11
9
  const { icon } = config().template.login;
12
- const key = `${icon}_${maxSize}_${isLogin}`;
13
-
14
- const [dimensions, setDimensions] = useState(
15
- resolvedDimensions[key] || { width: maxSize, height: maxSize },
16
- );
17
-
18
- useEffect(() => {
19
- if (!icon || resolvedDimensions[key]) return;
20
10
 
21
- const img = new Image();
22
- img.onload = () => {
23
- const { naturalWidth, naturalHeight } = img;
24
- const isSquare = naturalWidth === naturalHeight;
25
- let newDimensions;
26
-
27
- if (naturalWidth > maxSize || naturalHeight > maxSize) {
28
- if (isSquare) {
29
- newDimensions = {
30
- width: isLogin ? maxSize : 40,
31
- height: isLogin ? maxSize : 40,
32
- };
33
- } else {
34
- const aspectRatio = naturalWidth / naturalHeight;
35
- if (naturalWidth >= naturalHeight) {
36
- newDimensions = {
37
- width: maxSize,
38
- height: Math.round(maxSize / aspectRatio),
39
- };
40
- } else {
41
- newDimensions = {
42
- width: Math.round(maxSize * aspectRatio),
43
- height: maxSize,
44
- };
45
- }
46
- }
47
- } else {
48
- newDimensions = { width: naturalWidth, height: naturalHeight };
49
- }
50
-
51
- resolvedDimensions[key] = newDimensions;
52
- setDimensions(newDimensions);
53
- };
54
- img.src = icon;
55
- }, [icon, maxSize, key]);
11
+ const [isSquare, setIsSquare] = useState(false);
12
+ const squareCap = isSquare && !isLogin ? 40 : maxSize;
56
13
 
57
14
  const logo = (
58
15
  <Box
59
16
  component="img"
60
17
  src={icon}
18
+ onLoad={(e) => {
19
+ const { naturalWidth, naturalHeight } = e.currentTarget;
20
+ setIsSquare(naturalWidth === naturalHeight);
21
+ }}
61
22
  sx={{
62
- width: dimensions.width,
63
- height: dimensions.height,
23
+ maxWidth: squareCap,
24
+ maxHeight: squareCap,
25
+ width: "auto",
26
+ height: "auto",
27
+ objectFit: "contain",
64
28
  cursor: "pointer",
65
29
  ...sx,
66
30
  }}
package/src/http/index.js CHANGED
@@ -1,7 +1,5 @@
1
1
  import axios from "axios";
2
2
  import config from "../config/config";
3
- import createAuthRefreshInterceptor from "axios-auth-refresh";
4
- import { jwtDecode } from "jwt-decode";
5
3
  import oauth from "./oauth";
6
4
  import { publish } from "@nucleoidai/react-event";
7
5
  import { storage } from "@nucleoidjs/webstorage";
@@ -29,6 +27,40 @@ instance.interceptors.request.use((request) => {
29
27
  return request;
30
28
  });
31
29
 
30
+ let isRefreshing = false;
31
+ let failedQueue = [];
32
+
33
+ function processQueue(error, token = null) {
34
+ failedQueue.forEach(({ resolve, reject }) => {
35
+ if (error) reject(error);
36
+ else resolve(token);
37
+ });
38
+ failedQueue = [];
39
+ }
40
+
41
+ async function doRefresh() {
42
+ const { appId } = config();
43
+ const projectId = storage.get("projectId");
44
+ const identityProvider = storage.get("link", "identityProvider");
45
+
46
+ const { data } = await oauth.post("/oauth", {
47
+ refreshToken: storage.get("link", "refreshToken"),
48
+ appId,
49
+ projectId,
50
+ identityProvider,
51
+ ...(identityProvider === "DEMO" && {
52
+ username: "admin",
53
+ password: "admin",
54
+ }),
55
+ });
56
+
57
+ storage.set("link", "accessToken", data.accessToken);
58
+ if (data.refreshToken) {
59
+ storage.set("link", "refreshToken", data.refreshToken);
60
+ }
61
+ return data.accessToken;
62
+ }
63
+
32
64
  instance.interceptors.response.use(
33
65
  (response) => {
34
66
  if (response.headers["content-type"] === "application/json") {
@@ -39,17 +71,48 @@ instance.interceptors.response.use(
39
71
  },
40
72
  async (error) => {
41
73
  publish("LOADED", { loading: false });
74
+ const originalRequest = error.config;
42
75
  const statusCode = error.response?.status;
43
76
 
44
- if (statusCode === 500) {
45
- const token = storage.get("link", "accessToken");
77
+ if (statusCode === 401 && !originalRequest._retried) {
78
+ originalRequest._retried = true;
79
+
80
+ const usedToken = originalRequest.headers["Authorization"]?.replace(
81
+ "Bearer ",
82
+ ""
83
+ );
84
+ const storedToken = storage.get("link", "accessToken");
85
+
86
+ if (storedToken && storedToken !== usedToken) {
87
+ originalRequest.headers["Authorization"] = `Bearer ${storedToken}`;
88
+ return instance(originalRequest);
89
+ }
90
+
91
+ if (isRefreshing) {
92
+ return new Promise((resolve, reject) => {
93
+ failedQueue.push({ resolve, reject });
94
+ }).then((token) => {
95
+ originalRequest.headers["Authorization"] = `Bearer ${token}`;
96
+ return instance(originalRequest);
97
+ });
98
+ }
99
+
100
+ isRefreshing = true;
101
+
46
102
  try {
47
- const decodedToken = jwtDecode(token);
48
- if (decodedToken.exp * 1000 < Date.now()) {
49
- return Promise.reject(error);
50
- }
51
- } catch (_) {
52
- return Promise.reject(error);
103
+ const token = await doRefresh();
104
+ processQueue(null, token);
105
+ originalRequest.headers["Authorization"] = `Bearer ${token}`;
106
+ return instance(originalRequest);
107
+ } catch (refreshError) {
108
+ processQueue(refreshError, null);
109
+ const { base } = config();
110
+ storage.remove("link", "accessToken");
111
+ storage.remove("link", "refreshToken");
112
+ window.location.href = `${window.location.origin}${base}/login`;
113
+ return Promise.reject(refreshError);
114
+ } finally {
115
+ isRefreshing = false;
53
116
  }
54
117
  }
55
118
 
@@ -91,95 +154,9 @@ instance.interceptors.response.use(
91
154
  break;
92
155
  }
93
156
  return Promise.reject(error);
94
- },
157
+ }
95
158
  );
96
159
 
97
160
  export const fetcher = (url) => instance.get(url).then((res) => res.data);
98
161
 
99
- const refreshAuthLogic = async (failedRequest) => {
100
- try {
101
- const { appId } = config();
102
- const projectId = storage.get("projectId");
103
- const identityProvider = storage.get("link", "identityProvider");
104
-
105
- const { data } = await oauth.post("/oauth", {
106
- refreshToken: storage.get("link", "refreshToken"),
107
- appId,
108
- projectId,
109
- identityProvider,
110
- ...(identityProvider === "DEMO" && {
111
- username: "admin",
112
- password: "admin",
113
- }),
114
- });
115
-
116
- const { accessToken, refreshToken } = data;
117
-
118
- failedRequest.response.config.headers["Authorization"] =
119
- "Bearer " + accessToken;
120
-
121
- storage.set("link", "accessToken", accessToken);
122
- if (refreshToken) {
123
- storage.set("link", "refreshToken", refreshToken);
124
- }
125
-
126
- return Promise.resolve();
127
- } catch (error) {
128
- const { base } = config();
129
-
130
- storage.remove("link", "accessToken");
131
- storage.remove("link", "refreshToken");
132
-
133
- window.location.href = `${window.location.origin}${base}/login`;
134
- return Promise.reject(error);
135
- }
136
- };
137
-
138
- const refreshInterceptor =
139
- typeof createAuthRefreshInterceptor === "function"
140
- ? createAuthRefreshInterceptor
141
- : createAuthRefreshInterceptor.default;
142
-
143
- refreshInterceptor(instance, refreshAuthLogic, {
144
- statusCodes: [401, 403, 500],
145
- pauseInstanceWhileRefreshing: true,
146
- shouldRefresh: (error) => {
147
- const { base } = config();
148
- const token = storage.get("link", "accessToken");
149
- const identityProvider = storage.get("link", "identityProvider");
150
- const statusCode = error.response?.status;
151
-
152
- if (!token) {
153
- window.location.href = `${window.location.origin}${base}/login`;
154
- return false;
155
- }
156
-
157
- if (identityProvider === "DEMO") {
158
- if (statusCode !== 500) {
159
- return true;
160
- }
161
-
162
- try {
163
- const decodedToken = jwtDecode(token);
164
- return decodedToken.exp * 1000 < Date.now();
165
- } catch (_) {
166
- // If we can't decode, attempt refresh once.
167
- return true;
168
- }
169
- }
170
-
171
- try {
172
- const decodedToken = jwtDecode(token);
173
- if (decodedToken.exp * 1000 < Date.now()) {
174
- return true;
175
- }
176
- } catch (err) {
177
- window.location.href = `${window.location.origin}${base}/login`;
178
- return false;
179
- }
180
-
181
- return false;
182
- },
183
- });
184
-
185
162
  export default instance;
package/src/http/oauth.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import axios from "axios";
2
2
  import axiosRetry from "axios-retry";
3
3
  import config from "../config/config.js";
4
- import qs from "qs";
5
4
 
6
5
  const instance = axios.create({
7
6
  baseURL: config().api,
@@ -12,16 +11,6 @@ const instance = axios.create({
12
11
  },
13
12
  });
14
13
 
15
- axiosRetry(instance, { retries: 3 });
16
-
17
- instance.interceptors.response.use(
18
- (response) => {
19
- response.data = qs.parse(response.data);
20
- return response;
21
- },
22
- (error) => {
23
- return Promise.reject(error);
24
- }
25
- );
14
+ axiosRetry(instance, { retries: 2 });
26
15
 
27
16
  export default instance;
package/src/http/user.js CHANGED
@@ -2,7 +2,6 @@ import axios from "axios";
2
2
  import config from "../config/config.js";
3
3
  import http from "./index";
4
4
  import { jwtDecode } from "jwt-decode";
5
- import oauth from "./oauth";
6
5
  import { storage } from "@nucleoidjs/webstorage";
7
6
 
8
7
  const instance = axios.create({
@@ -23,51 +22,9 @@ instance.interceptors.request.use(async (request) => {
23
22
 
24
23
  instance.getUserDetails = async () => {
25
24
  try {
26
- const refreshToken = await storage.get("link", "refreshToken");
25
+ const refreshToken = storage.get("link", "refreshToken");
27
26
 
28
27
  if (!refreshToken) {
29
- console.log("No refresh token found");
30
- return null;
31
- }
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
- ...(identityProvider === "DEMO" && {
58
- username: "admin",
59
- password: "admin",
60
- }),
61
- });
62
-
63
- accessToken = data.accessToken;
64
- storage.set("link", "accessToken", accessToken);
65
- console.log(
66
- "Access token refreshed successfully, now fetching user details",
67
- );
68
- }
69
- } catch (error) {
70
- console.error("Error checking or refreshing token:", error);
71
28
  return null;
72
29
  }
73
30
 
@@ -81,7 +38,6 @@ instance.getUserDetails = async () => {
81
38
  return response.data.user;
82
39
  }
83
40
 
84
- console.log("No user data received from server");
85
41
  return null;
86
42
  } catch (error) {
87
43
  console.error("Error fetching user details from server:", error);
@@ -100,7 +56,7 @@ instance.getPermittedUsers = async () => {
100
56
  ...new Set(
101
57
  response.data
102
58
  .filter((p) => p.appId === appId && p.projectId === projectId)
103
- .map((p) => p.userId),
59
+ .map((p) => p.userId)
104
60
  ),
105
61
  ];
106
62
 
@@ -122,7 +78,7 @@ instance.getPermittedUsers = async () => {
122
78
  }
123
79
  const { data } = await axios.get(
124
80
  `https://api.github.com/user/${userId}`,
125
- { headers: { Accept: "application/vnd.github+json" } },
81
+ { headers: { Accept: "application/vnd.github+json" } }
126
82
  );
127
83
  return {
128
84
  id: String(data.id),
@@ -132,7 +88,7 @@ instance.getPermittedUsers = async () => {
132
88
  avatarUrl: data.avatar_url || null,
133
89
  email: data.email || null,
134
90
  };
135
- }),
91
+ })
136
92
  );
137
93
 
138
94
  return results
@@ -14,6 +14,7 @@ export const Flow = ({
14
14
  onChange,
15
15
  height,
16
16
  initialZoom,
17
+ centered = false,
17
18
  }) => {
18
19
  const [floatingNodes, setFloatingNodes] = useState([]);
19
20
 
@@ -90,6 +91,7 @@ export const Flow = ({
90
91
  floatingNodes={floatingNodes}
91
92
  height={height}
92
93
  initialZoom={initialZoom}
94
+ centered={centered}
93
95
  />
94
96
  </Box>
95
97
  );
@@ -18,6 +18,7 @@ const FlowNode = ({
18
18
  node,
19
19
  height,
20
20
  initialZoom,
21
+ centered,
21
22
  ...props
22
23
  }) => {
23
24
  if (!isRoot) {
@@ -52,6 +53,7 @@ const FlowNode = ({
52
53
  plugin={plugin}
53
54
  height={height}
54
55
  initialZoom={initialZoom}
56
+ centered={centered}
55
57
  >
56
58
  {node && (
57
59
  <FlowNodeView
@@ -18,6 +18,7 @@ const FlowViewport = ({
18
18
  plugin,
19
19
  height = "100vh",
20
20
  initialZoom = 1,
21
+ centered = false,
21
22
  sx = {},
22
23
  ...rest
23
24
  }) => {
@@ -45,6 +46,8 @@ const FlowViewport = ({
45
46
  } = useSelection();
46
47
 
47
48
  useEffect(() => {
49
+ if (centered) return;
50
+
48
51
  const container = containerRef.current;
49
52
  const inner = innerRef.current;
50
53
  if (!container || !inner) return;
@@ -64,7 +67,7 @@ const FlowViewport = ({
64
67
  observer.observe(container);
65
68
 
66
69
  return () => observer.disconnect();
67
- }, []);
70
+ }, [centered]);
68
71
 
69
72
  useEffect(() => {
70
73
  const handleMouseMove = (e) => {
@@ -310,11 +313,11 @@ const FlowViewport = ({
310
313
  height: height,
311
314
  display: "flex",
312
315
  alignItems: "center",
313
- justifyContent: shouldCenter ? "center" : "flex-start",
316
+ justifyContent: centered || shouldCenter ? "center" : "flex-start",
314
317
  transition: isDragging ? "none" : "transform 0.1s ease-out",
315
318
  pointerEvents: "auto",
316
319
  position: "relative",
317
- pl: variant === "horizontal" ? 4 : 0,
320
+ pl: centered ? 0 : variant === "horizontal" ? 4 : 0,
318
321
  }}
319
322
  >
320
323
  {children}
package/vite/vite.js CHANGED
@@ -51,7 +51,6 @@ async function vite() {
51
51
  include: [
52
52
  "@mui/material",
53
53
  "@nucleoidai/platform",
54
- "axios-auth-refresh",
55
54
  "@emotion/react",
56
55
  "@emotion/styled",
57
56
  "@emotion/css",
@@ -67,7 +66,7 @@ async function vite() {
67
66
  find: /^src(.+)/,
68
67
  replacement: path.join(
69
68
  process.cwd(),
70
- "/node_modules/@nucleoidai/platform/minimal/src/$1",
69
+ "/node_modules/@nucleoidai/platform/minimal/src/$1"
71
70
  ),
72
71
  },
73
72
  ],