codenotch-react 1.0.22 → 1.0.24
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/dist/auml.d.ts +29 -0
- package/dist/auml.d.ts.map +1 -0
- package/dist/auml.js +185 -0
- package/package.json +5 -1
package/dist/auml.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { SpaRenderStatus } from '@echino/echino.ui.framework/components/SpaBuilder/common/ISpaRenderProps';
|
|
3
|
+
interface IRenderAumlProps {
|
|
4
|
+
auml: string;
|
|
5
|
+
}
|
|
6
|
+
interface IRenderAumlState {
|
|
7
|
+
loaded: boolean;
|
|
8
|
+
inspectorEnabled: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare class RenderAuml extends React.Component<IRenderAumlProps, IRenderAumlState> {
|
|
11
|
+
_refreshTokenTimer: NodeJS.Timeout | undefined;
|
|
12
|
+
constructor(props: IRenderAumlProps);
|
|
13
|
+
componentDidMount(): void;
|
|
14
|
+
componentWillUnmount(): void;
|
|
15
|
+
progress(s: SpaRenderStatus): void;
|
|
16
|
+
getLogo(): React.JSX.Element;
|
|
17
|
+
getMode(theme: boolean | undefined): "dark" | "light";
|
|
18
|
+
retrieveInputs(): {
|
|
19
|
+
[k: string]: string;
|
|
20
|
+
};
|
|
21
|
+
getTimeToTokenExpiration(): number | null;
|
|
22
|
+
getTimeToNewTokenExpiration(expirationDateTime: string): number;
|
|
23
|
+
msToTime(ms: number): string;
|
|
24
|
+
parseJwt(token: string): any;
|
|
25
|
+
render(): React.JSX.Element;
|
|
26
|
+
refreshExpiredToken(): Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
export {};
|
|
29
|
+
//# sourceMappingURL=auml.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auml.d.ts","sourceRoot":"","sources":["../src/auml.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,eAAe,EAAE,MAAM,0EAA0E,CAAC;AAS3G,UAAU,gBAAgB;IACtB,IAAI,EAAE,MAAM,CAAC;CAChB;AACD,UAAU,gBAAgB;IACtB,MAAM,EAAE,OAAO,CAAC;IAChB,gBAAgB,EAAE,OAAO,CAAC;CAC7B;AAED,qBAAa,UAAW,SAAQ,KAAK,CAAC,SAAS,CAAC,gBAAgB,EAAE,gBAAgB,CAAC;IAE/E,kBAAkB,EAAE,MAAM,CAAC,OAAO,GAAG,SAAS,CAAC;gBAEnC,KAAK,EAAE,gBAAgB;IAqBnC,iBAAiB;IAmCjB,oBAAoB;IAQpB,QAAQ,CAAC,CAAC,EAAE,eAAe;IAO3B,OAAO;IAYP,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS;IAclC,cAAc;;;IAMd,wBAAwB,IAAI,MAAM,GAAG,IAAI;IAoCzC,2BAA2B,CAAC,kBAAkB,EAAE,MAAM,GAAG,MAAM;IAO/D,QAAQ,CAAC,EAAE,EAAE,MAAM;IAmBnB,QAAQ,CAAC,KAAK,EAAE,MAAM;IAUtB,MAAM;IAiDA,mBAAmB;CAqC5B"}
|
package/dist/auml.js
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.RenderAuml = void 0;
|
|
7
|
+
const react_1 = __importDefault(require("react"));
|
|
8
|
+
const SpaRenderWithBrowserRouter_1 = require("@echino/echino.ui.framework/components/SpaBuilder/SpaRender/SpaRenderWithBrowserRouter");
|
|
9
|
+
class RenderAuml extends react_1.default.Component {
|
|
10
|
+
constructor(props) {
|
|
11
|
+
super(props);
|
|
12
|
+
this.state = {
|
|
13
|
+
loaded: false,
|
|
14
|
+
inspectorEnabled: false
|
|
15
|
+
};
|
|
16
|
+
// Add a function that we can call from the browser console, allowing us to debug the page
|
|
17
|
+
//TODO we may not want to do that for prod pages
|
|
18
|
+
//@ts-ignore
|
|
19
|
+
window.toggleInspector = () => {
|
|
20
|
+
this.setState({
|
|
21
|
+
inspectorEnabled: !this.state.inspectorEnabled
|
|
22
|
+
}, () => console.log(`Auml inspector is now ${this.state.inspectorEnabled ? 'enabled' : 'disabled'}`));
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
componentDidMount() {
|
|
26
|
+
try {
|
|
27
|
+
// Setup a method to refresh our access token when it expires
|
|
28
|
+
if (this._refreshTokenTimer) {
|
|
29
|
+
clearTimeout(this._refreshTokenTimer);
|
|
30
|
+
}
|
|
31
|
+
if (!user) {
|
|
32
|
+
console.log("No user found, will not refresh the token");
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
let timeToExpireMs = this.getTimeToTokenExpiration();
|
|
36
|
+
if (timeToExpireMs === null) {
|
|
37
|
+
console.log("No identity token found, attempting refreshing the token in 5 minutes");
|
|
38
|
+
this._refreshTokenTimer = setTimeout(() => this.refreshExpiredToken(), 5 * 60 * 1000);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (timeToExpireMs <= 0) {
|
|
42
|
+
// Refresh it immediatelty
|
|
43
|
+
this.refreshExpiredToken();
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
this._refreshTokenTimer = setTimeout(() => this.refreshExpiredToken(), timeToExpireMs);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
console.warn("Could not setup token refresh timer: " + err);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
componentWillUnmount() {
|
|
54
|
+
// Cleanup the refresh of the token
|
|
55
|
+
if (this._refreshTokenTimer) {
|
|
56
|
+
clearTimeout(this._refreshTokenTimer);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
progress(s) {
|
|
60
|
+
//console.log('progessStatus ', s);
|
|
61
|
+
if (s === 'completed') {
|
|
62
|
+
this.setState({ loaded: true });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
getLogo() {
|
|
66
|
+
//@ts-ignore
|
|
67
|
+
let tenant = global.tenant;
|
|
68
|
+
if (tenant.logoUrl) {
|
|
69
|
+
return react_1.default.createElement("img", { src: tenant.logoUrl, alt: tenant.displayName });
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
return react_1.default.createElement("div", { className: 'app-loading-title' }, tenant.displayName);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
getMode(theme) {
|
|
76
|
+
if (theme !== undefined) {
|
|
77
|
+
return theme ? 'dark' : 'light';
|
|
78
|
+
}
|
|
79
|
+
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
|
80
|
+
return 'dark';
|
|
81
|
+
}
|
|
82
|
+
return 'light';
|
|
83
|
+
}
|
|
84
|
+
// Get input from the query parameters in the url
|
|
85
|
+
retrieveInputs() {
|
|
86
|
+
return Object.fromEntries(new URLSearchParams(window.location.search).entries());
|
|
87
|
+
}
|
|
88
|
+
getTimeToTokenExpiration() {
|
|
89
|
+
// The identity token is the only one we have access here on the client
|
|
90
|
+
// We use it to know when the access token (which we can't read) will expire
|
|
91
|
+
let identityToken = null;
|
|
92
|
+
let identityTokenKey = `${tenant.name}IdToken=`;
|
|
93
|
+
let cookies = document.cookie.split(';');
|
|
94
|
+
for (let c of cookies) {
|
|
95
|
+
if (c.trim().startsWith(identityTokenKey)) {
|
|
96
|
+
identityToken = c.trim().slice(identityTokenKey.length);
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (identityToken === null) {
|
|
101
|
+
return null; // token not found
|
|
102
|
+
}
|
|
103
|
+
let identityTokenParsed = this.parseJwt(identityToken);
|
|
104
|
+
let expires = identityTokenParsed.exp; // Timestamp in second since Unix epoch
|
|
105
|
+
let timeToExpiresMs = expires * 1000 - new Date().getTime();
|
|
106
|
+
let timeToExpireStr = this.msToTime(timeToExpiresMs);
|
|
107
|
+
console.log(`Token will expire at ${new Date(expires * 1000).toISOString()} (in ${timeToExpireStr}), setting up a timer to refresh it`);
|
|
108
|
+
// Refresh it a bit before the expiration (5 min)
|
|
109
|
+
timeToExpiresMs -= 5 * 60 * 1000;
|
|
110
|
+
return timeToExpiresMs;
|
|
111
|
+
}
|
|
112
|
+
getTimeToNewTokenExpiration(expirationDateTime) {
|
|
113
|
+
let timeToExpiresMs = new Date(expirationDateTime).getTime() - new Date().getTime();
|
|
114
|
+
// Refresh it a bit before the expiration (5 min)
|
|
115
|
+
timeToExpiresMs -= 5 * 60 * 1000;
|
|
116
|
+
return timeToExpiresMs;
|
|
117
|
+
}
|
|
118
|
+
msToTime(ms) {
|
|
119
|
+
let seconds = Math.floor((ms / 1000) % 60), minutes = Math.floor((ms / (1000 * 60)) % 60), hours = Math.floor((ms / (1000 * 60 * 60)) % 24);
|
|
120
|
+
let timeString = seconds + " seconds";
|
|
121
|
+
if (minutes > 0) {
|
|
122
|
+
timeString = minutes + " minutes " + timeString;
|
|
123
|
+
}
|
|
124
|
+
if (hours > 0) {
|
|
125
|
+
timeString = hours + " hours " + timeString;
|
|
126
|
+
}
|
|
127
|
+
return timeString;
|
|
128
|
+
}
|
|
129
|
+
parseJwt(token) {
|
|
130
|
+
var base64Url = token.split('.')[1];
|
|
131
|
+
var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
|
|
132
|
+
var jsonPayload = decodeURIComponent(window.atob(base64).split('').map(function (c) {
|
|
133
|
+
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
|
134
|
+
}).join(''));
|
|
135
|
+
return JSON.parse(jsonPayload);
|
|
136
|
+
}
|
|
137
|
+
render() {
|
|
138
|
+
//var clusterUrl = this.figureOutClusterUrl();
|
|
139
|
+
let inputs = this.retrieveInputs();
|
|
140
|
+
let theme = undefined;
|
|
141
|
+
if (inputs["_theme"]) {
|
|
142
|
+
theme = inputs["_theme"].toLowerCase() === "dark";
|
|
143
|
+
}
|
|
144
|
+
let aumlManifest = null;
|
|
145
|
+
try {
|
|
146
|
+
let appManifestObj = JSON.parse(appManifest);
|
|
147
|
+
aumlManifest = appManifestObj.auml;
|
|
148
|
+
}
|
|
149
|
+
catch { }
|
|
150
|
+
return (react_1.default.createElement("div", { className: "app" },
|
|
151
|
+
react_1.default.createElement(SpaRenderWithBrowserRouter_1.SpaRenderWithBrowserRouter, { appDescription: this.props.auml, tenant: tenant, serviceName: serviceName, packageVersions: packageVersions, user: user, languages: languages, onProgress: (s) => this.progress(s), input: inputs, theme: theme, manifest: aumlManifest, inspectorEnabled: this.state.inspectorEnabled, children: [] }),
|
|
152
|
+
!this.state.loaded &&
|
|
153
|
+
react_1.default.createElement("div", { className: `app-loading ${this.getMode(theme)}` },
|
|
154
|
+
this.getLogo(),
|
|
155
|
+
react_1.default.createElement("i", { className: "fas fa-circle-notch fa-spin" }))));
|
|
156
|
+
}
|
|
157
|
+
async refreshExpiredToken() {
|
|
158
|
+
console.log("Token will expire soon, requesting a new one...");
|
|
159
|
+
// Using the refresh token we ask for a new access token using /portal/login/refresh
|
|
160
|
+
// If the refresh token has also expired, we will be redirected to the login page
|
|
161
|
+
let redirectUrl = window.location.href; // Where to send us back in case we need to be redirected to the login page
|
|
162
|
+
let url = `${tenant.clusterUrl}/portal/login/refresh?redirectUrl=${encodeURIComponent(redirectUrl)}`;
|
|
163
|
+
let response = await fetch(url); // For this request to work, we need to have a refresh token in the cookies
|
|
164
|
+
if (response.ok) {
|
|
165
|
+
// Setup next refresh
|
|
166
|
+
let timeToExpireMs;
|
|
167
|
+
try {
|
|
168
|
+
let newTokenExpiration = await response.text();
|
|
169
|
+
timeToExpireMs = this.getTimeToNewTokenExpiration(newTokenExpiration);
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
timeToExpireMs = 2 * 60 * 60 * 1000; // refresh in 2 hours
|
|
173
|
+
}
|
|
174
|
+
console.log(`Refresh request ok, next refresh in ${this.msToTime(timeToExpireMs)}`);
|
|
175
|
+
this._refreshTokenTimer = setTimeout(() => this.refreshExpiredToken(), timeToExpireMs);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
let content = await response.text();
|
|
179
|
+
console.error("Could not refresh the token", content);
|
|
180
|
+
console.log("Retrying refreshing the token in 5 minutes...");
|
|
181
|
+
this._refreshTokenTimer = setTimeout(() => this.refreshExpiredToken(), 5 * 60 * 1000);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
exports.RenderAuml = RenderAuml;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codenotch-react",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.24",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"author": "Codenotch SA",
|
|
@@ -13,9 +13,13 @@
|
|
|
13
13
|
"files": [
|
|
14
14
|
"dist/**/*"
|
|
15
15
|
],
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@echino/echino.ui.framework": "1.1.42"
|
|
18
|
+
},
|
|
16
19
|
"devDependencies": {
|
|
17
20
|
"@types/node": "^25.3.2",
|
|
18
21
|
"@types/react": "^16.14.69",
|
|
22
|
+
"@echino/echino.ui.sdk": "^0.3.188",
|
|
19
23
|
"typescript": "^5.9.3"
|
|
20
24
|
},
|
|
21
25
|
"peerDependencies": {
|