@ti-engine/web-framework 1.18.1 → 1.19.1

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 (39) hide show
  1. package/.env +4 -4
  2. package/CHANGELOG.md +362 -341
  3. package/README.md +73 -40
  4. package/bin/build/post-install.js +18 -18
  5. package/bin/localization/web-server-labels.json +27 -27
  6. package/bin/static/.well-known/appspecific/com.chrome.devtools.json +5 -5
  7. package/bin/static/fragments/components/component-notification-bar.html +21 -21
  8. package/bin/static/fragments/components/component-sidebar-flyout.html +36 -36
  9. package/bin/static/fragments/components/component-sidebar.html +33 -33
  10. package/bin/static/fragments/components/component-tooltip.html +10 -10
  11. package/bin/static/fragments/components/component-topbar.html +5 -5
  12. package/bin/static/fragments/frame-administration.html +2 -2
  13. package/bin/static/fragments/frame-application.html +18 -18
  14. package/bin/static/fragments/frame-dashboard.html +2 -2
  15. package/bin/static/fragments/frame-login.html +119 -119
  16. package/bin/static/fragments/frame-not-found.html +2 -2
  17. package/bin/static/fragments/frame-profile.html +2 -2
  18. package/bin/static/index.html +22 -22
  19. package/bin/static/scripts/ti-charts.js +1591 -1591
  20. package/bin/static/scripts/ti-framework.css +3194 -3194
  21. package/bin/static/scripts/ti-framework.js +1427 -1427
  22. package/bin/static/scripts/ti-theme-black-glass.css +216 -216
  23. package/bin/static/scripts/ti-theme-daylight.css +87 -87
  24. package/bin/web-app-manager.js +663 -663
  25. package/bin/web-server.js +936 -744
  26. package/bin/web-server.json +48 -48
  27. package/components/admin-config-handlers.js +92 -92
  28. package/components/auth-manager.js +441 -441
  29. package/components/authorization.js +135 -135
  30. package/components/config-change-notifier.js +98 -98
  31. package/components/config-registry.js +260 -260
  32. package/components/config-service.js +360 -360
  33. package/components/config-store.js +246 -246
  34. package/components/definitions.types.js +26 -26
  35. package/components/session-store.js +110 -110
  36. package/components/user.js +132 -132
  37. package/components/web-config-env.js +19 -2
  38. package/components/web-handlers.js +800 -800
  39. package/package.json +76 -67
@@ -1,111 +1,111 @@
1
- /*
2
- * The ti-engine is an open source, free to use—both for personal and commercial projects—framework for the creation of microservice-based solutions using node.js.
3
- * Copyright © 2021-2025 Boris Kostadinov <kostadinov.boris@gmail.com>
4
- * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
5
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
6
- * You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
7
- */
8
-
9
- const cache = require( "@ti-engine/core/cache" );
10
- const logger = require( "@ti-engine/core/logger" );
11
- const exceptions = require( "@ti-engine/core/exceptions" );
12
- const _ = require( "lodash" );
13
- const session = require( "express-session" );
14
-
15
- // The name of the session store in the cache:
16
- const sessionStoreName = "ti:web:sessions";
17
-
18
- /**
19
- * A session store for the web server using the standard 'cache' module of the ti-engine.
20
- * <br/>
21
- * NOTE: This implementation is compatible with the 'express-session' module.
22
- *
23
- * @class SessionStore
24
- * @public
25
- */
26
- class SessionStore extends session.Store {
27
-
28
- /**
29
- * @constructor
30
- */
31
- constructor() {
32
- super();
33
- }
34
-
35
- /**
36
- * Used to store a user session in the cache.
37
- *
38
- * @method
39
- * @param {string} sessionID
40
- * @param {TiSession} session
41
- * @param {function( (Error|TiException|null)= )} callback
42
- * @public
43
- */
44
- set( sessionID, session, callback ) {
45
- cache.instance.hashSetField( sessionStoreName, sessionID, session ).then( () => {
46
- let expire = ( session.cookie && _.isNumber( session.cookie.maxAge ) ) ? session.cookie.maxAge / 1000 : null;
47
- return ( expire ) ? cache.instance.expireValue( sessionID, expire, sessionStoreName ) : null;
48
- } ).then( () => {
49
- callback();
50
- } ).catch( ( error ) => {
51
- logger.log( `Error while trying to store user session in cache!`, logger.logSeverity.ERROR, error );
52
- callback( exceptions.raise( error ) );
53
- } );
54
- }
55
-
56
- /**
57
- * Used to retrieve a user session from the cache.
58
- *
59
- * @method
60
- * @param {string} sessionID
61
- * @param {function( (Error|TiException|null)=, (TiSession)= )} callback
62
- * @public
63
- */
64
- get( sessionID, callback ) {
65
- cache.instance.hashGetField( sessionStoreName, sessionID ).then( ( session ) => {
66
- callback( null, session );
67
- } ).catch( ( error ) => {
68
- logger.log( `Error while trying to fetch user session from cache!`, logger.logSeverity.ERROR, error );
69
- callback( exceptions.raise( error ) );
70
- } );
71
- }
72
-
73
- /**
74
- * Used to remove a user session from the cache.
75
- *
76
- * @method
77
- * @param {string} sessionID
78
- * @param {function( (Error|TiException|null)= )} callback
79
- * @public
80
- */
81
- destroy( sessionID, callback ) {
82
- cache.instance.hashDeleteField( sessionStoreName, sessionID ).then( () => {
83
- callback();
84
- } ).catch( ( error ) => {
85
- logger.log( `Error while trying to remove user session from cache!`, logger.logSeverity.ERROR, error );
86
- callback( exceptions.raise( error ) );
87
- } );
88
- }
89
-
90
- /**
91
- * Used to update the expiration time of a user session in the cache.
92
- *
93
- * @method
94
- * @param {string} sessionID
95
- * @param {TiSession} session
96
- * @param {function( (Error|TiException|null)= )} callback
97
- * @public
98
- */
99
- touch( sessionID, session, callback ) {
100
- let expire = ( session.cookie && _.isNumber( session.cookie.maxAge ) ) ? session.cookie.maxAge / 1000 : null;
101
- cache.instance.expireValue( sessionID, expire, sessionStoreName ).then( () => {
102
- callback();
103
- } ).catch( ( error ) => {
104
- logger.log( `Error while trying to refresh user session expiration in cache!`, logger.logSeverity.ERROR, error );
105
- callback( exceptions.raise( error ) );
106
- } );
107
- }
108
-
109
- }
110
-
1
+ /*
2
+ * The ti-engine is an open source, free to use—both for personal and commercial projects—framework for the creation of microservice-based solutions using node.js.
3
+ * Copyright © 2021-2025 Boris Kostadinov <kostadinov.boris@gmail.com>
4
+ * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
5
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
6
+ * You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
7
+ */
8
+
9
+ const cache = require( "@ti-engine/core/cache" );
10
+ const logger = require( "@ti-engine/core/logger" );
11
+ const exceptions = require( "@ti-engine/core/exceptions" );
12
+ const _ = require( "lodash" );
13
+ const session = require( "express-session" );
14
+
15
+ // The name of the session store in the cache:
16
+ const sessionStoreName = "ti:web:sessions";
17
+
18
+ /**
19
+ * A session store for the web server using the standard 'cache' module of the ti-engine.
20
+ * <br/>
21
+ * NOTE: This implementation is compatible with the 'express-session' module.
22
+ *
23
+ * @class SessionStore
24
+ * @public
25
+ */
26
+ class SessionStore extends session.Store {
27
+
28
+ /**
29
+ * @constructor
30
+ */
31
+ constructor() {
32
+ super();
33
+ }
34
+
35
+ /**
36
+ * Used to store a user session in the cache.
37
+ *
38
+ * @method
39
+ * @param {string} sessionID
40
+ * @param {TiSession} session
41
+ * @param {function( (Error|TiException|null)= )} callback
42
+ * @public
43
+ */
44
+ set( sessionID, session, callback ) {
45
+ cache.instance.hashSetField( sessionStoreName, sessionID, session ).then( () => {
46
+ let expire = ( session.cookie && _.isNumber( session.cookie.maxAge ) ) ? session.cookie.maxAge / 1000 : null;
47
+ return ( expire ) ? cache.instance.expireValue( sessionID, expire, sessionStoreName ) : null;
48
+ } ).then( () => {
49
+ callback();
50
+ } ).catch( ( error ) => {
51
+ logger.log( `Error while trying to store user session in cache!`, logger.logSeverity.ERROR, error );
52
+ callback( exceptions.raise( error ) );
53
+ } );
54
+ }
55
+
56
+ /**
57
+ * Used to retrieve a user session from the cache.
58
+ *
59
+ * @method
60
+ * @param {string} sessionID
61
+ * @param {function( (Error|TiException|null)=, (TiSession)= )} callback
62
+ * @public
63
+ */
64
+ get( sessionID, callback ) {
65
+ cache.instance.hashGetField( sessionStoreName, sessionID ).then( ( session ) => {
66
+ callback( null, session );
67
+ } ).catch( ( error ) => {
68
+ logger.log( `Error while trying to fetch user session from cache!`, logger.logSeverity.ERROR, error );
69
+ callback( exceptions.raise( error ) );
70
+ } );
71
+ }
72
+
73
+ /**
74
+ * Used to remove a user session from the cache.
75
+ *
76
+ * @method
77
+ * @param {string} sessionID
78
+ * @param {function( (Error|TiException|null)= )} callback
79
+ * @public
80
+ */
81
+ destroy( sessionID, callback ) {
82
+ cache.instance.hashDeleteField( sessionStoreName, sessionID ).then( () => {
83
+ callback();
84
+ } ).catch( ( error ) => {
85
+ logger.log( `Error while trying to remove user session from cache!`, logger.logSeverity.ERROR, error );
86
+ callback( exceptions.raise( error ) );
87
+ } );
88
+ }
89
+
90
+ /**
91
+ * Used to update the expiration time of a user session in the cache.
92
+ *
93
+ * @method
94
+ * @param {string} sessionID
95
+ * @param {TiSession} session
96
+ * @param {function( (Error|TiException|null)= )} callback
97
+ * @public
98
+ */
99
+ touch( sessionID, session, callback ) {
100
+ let expire = ( session.cookie && _.isNumber( session.cookie.maxAge ) ) ? session.cookie.maxAge / 1000 : null;
101
+ cache.instance.expireValue( sessionID, expire, sessionStoreName ).then( () => {
102
+ callback();
103
+ } ).catch( ( error ) => {
104
+ logger.log( `Error while trying to refresh user session expiration in cache!`, logger.logSeverity.ERROR, error );
105
+ callback( exceptions.raise( error ) );
106
+ } );
107
+ }
108
+
109
+ }
110
+
111
111
  module.exports = SessionStore;
@@ -1,133 +1,133 @@
1
- /*
2
- * The ti-engine is an open source, free to use—both for personal and commercial projects—framework for the creation of microservice-based solutions using node.js.
3
- * Copyright © 2021-2025 Boris Kostadinov <kostadinov.boris@gmail.com>
4
- * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
5
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
6
- * You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
7
- */
8
-
9
- /**
10
- * Represents a user in the system.
11
- *
12
- * @class User
13
- * @public
14
- */
15
- class User {
16
-
17
- #userID;
18
- #username;
19
- #email;
20
- #name;
21
- #language;
22
- #roles;
23
- #permissions;
24
- #details;
25
-
26
- /**
27
- * @constructor
28
- * @param {Object} userData
29
- * @param {string} userData.userID
30
- * @param {string} [userData.username]
31
- * @param {string} [userData.email]
32
- * @param {string} [userData.name]
33
- * @param {TiLocalizationLanguage} [userData.language]
34
- * @param {string[]} [userData.roles]
35
- * @param {string[]} [userData.permissions]
36
- * @param {Object} [userData.details]
37
- */
38
- constructor( userData = {} ) {
39
- this.#userID = userData.userID;
40
- this.#username = userData.username;
41
- this.#email = userData.email;
42
- this.#name = userData.name;
43
- this.#language = userData.language;
44
- this.#roles = Array.isArray( userData.roles ) ? userData.roles : [];
45
- this.#permissions = Array.isArray( userData.permissions ) ? userData.permissions : [];
46
- this.#details = userData.details || {};
47
- }
48
-
49
- /**
50
- * @property
51
- * @returns {string}
52
- * @public
53
- */
54
- get userID() {
55
- return this.#userID;
56
- }
57
-
58
- /**
59
- * @property
60
- * @returns {string}
61
- * @public
62
- */
63
- get username() {
64
- return this.#username;
65
- }
66
-
67
- /**
68
- * @property
69
- * @returns {string}
70
- * @public
71
- */
72
- get email() {
73
- return this.#email;
74
- }
75
-
76
- /**
77
- * @property
78
- * @returns {string}
79
- * @public
80
- */
81
- get name() {
82
- return this.#name;
83
- }
84
-
85
- /**
86
- * @property
87
- * @returns {TiLocalizationLanguage}
88
- * @public
89
- */
90
- get language() {
91
- return this.#language;
92
- }
93
-
94
- /**
95
- * @method
96
- * @returns {*}
97
- * @public
98
- */
99
- getDetail( key ) {
100
- return this.#details[ key ];
101
- }
102
-
103
- /**
104
- * @method
105
- * @param {string} key
106
- * @param {*} value
107
- * @public
108
- */
109
- setDetail( key, value ) {
110
- this.#details[ key ] = value;
111
- }
112
-
113
- /**
114
- * @method
115
- * @returns {Object}
116
- * @public
117
- */
118
- asJSON() {
119
- return {
120
- userID: this.#userID,
121
- username: this.#username,
122
- email: this.#email,
123
- name: this.#name,
124
- language: this.#language,
125
- roles: this.#roles,
126
- permissions: this.#permissions,
127
- details: this.#details
128
- };
129
- }
130
-
131
- }
132
-
1
+ /*
2
+ * The ti-engine is an open source, free to use—both for personal and commercial projects—framework for the creation of microservice-based solutions using node.js.
3
+ * Copyright © 2021-2025 Boris Kostadinov <kostadinov.boris@gmail.com>
4
+ * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
5
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
6
+ * You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
7
+ */
8
+
9
+ /**
10
+ * Represents a user in the system.
11
+ *
12
+ * @class User
13
+ * @public
14
+ */
15
+ class User {
16
+
17
+ #userID;
18
+ #username;
19
+ #email;
20
+ #name;
21
+ #language;
22
+ #roles;
23
+ #permissions;
24
+ #details;
25
+
26
+ /**
27
+ * @constructor
28
+ * @param {Object} userData
29
+ * @param {string} userData.userID
30
+ * @param {string} [userData.username]
31
+ * @param {string} [userData.email]
32
+ * @param {string} [userData.name]
33
+ * @param {TiLocalizationLanguage} [userData.language]
34
+ * @param {string[]} [userData.roles]
35
+ * @param {string[]} [userData.permissions]
36
+ * @param {Object} [userData.details]
37
+ */
38
+ constructor( userData = {} ) {
39
+ this.#userID = userData.userID;
40
+ this.#username = userData.username;
41
+ this.#email = userData.email;
42
+ this.#name = userData.name;
43
+ this.#language = userData.language;
44
+ this.#roles = Array.isArray( userData.roles ) ? userData.roles : [];
45
+ this.#permissions = Array.isArray( userData.permissions ) ? userData.permissions : [];
46
+ this.#details = userData.details || {};
47
+ }
48
+
49
+ /**
50
+ * @property
51
+ * @returns {string}
52
+ * @public
53
+ */
54
+ get userID() {
55
+ return this.#userID;
56
+ }
57
+
58
+ /**
59
+ * @property
60
+ * @returns {string}
61
+ * @public
62
+ */
63
+ get username() {
64
+ return this.#username;
65
+ }
66
+
67
+ /**
68
+ * @property
69
+ * @returns {string}
70
+ * @public
71
+ */
72
+ get email() {
73
+ return this.#email;
74
+ }
75
+
76
+ /**
77
+ * @property
78
+ * @returns {string}
79
+ * @public
80
+ */
81
+ get name() {
82
+ return this.#name;
83
+ }
84
+
85
+ /**
86
+ * @property
87
+ * @returns {TiLocalizationLanguage}
88
+ * @public
89
+ */
90
+ get language() {
91
+ return this.#language;
92
+ }
93
+
94
+ /**
95
+ * @method
96
+ * @returns {*}
97
+ * @public
98
+ */
99
+ getDetail( key ) {
100
+ return this.#details[ key ];
101
+ }
102
+
103
+ /**
104
+ * @method
105
+ * @param {string} key
106
+ * @param {*} value
107
+ * @public
108
+ */
109
+ setDetail( key, value ) {
110
+ this.#details[ key ] = value;
111
+ }
112
+
113
+ /**
114
+ * @method
115
+ * @returns {Object}
116
+ * @public
117
+ */
118
+ asJSON() {
119
+ return {
120
+ userID: this.#userID,
121
+ username: this.#username,
122
+ email: this.#email,
123
+ name: this.#name,
124
+ language: this.#language,
125
+ roles: this.#roles,
126
+ permissions: this.#permissions,
127
+ details: this.#details
128
+ };
129
+ }
130
+
131
+ }
132
+
133
133
  module.exports = User;
@@ -15,8 +15,8 @@ const tools = require( "@ti-engine/core/tools" );
15
15
  * Each override is applied ONLY when its environment variable is defined, so an absent variable leaves the
16
16
  * configured/default value untouched (fully backward compatible). This gives ti-engine web servers 12-factor,
17
17
  * container-friendly control over network binding, TLS, the session cookie secret, the enabled authentication
18
- * methods, the admin allowlist, and the trusted request origins without editing config files. Note `TI_WEB_AUTH_METHODS`,
19
- * `TI_WEB_AUTH_ADMINS`, and `TI_WEB_TRUSTED_ORIGINS` fully REPLACE their config arrays (`auth.enabledMethods` / `auth.admins` / `trustedOrigins`) rather than
18
+ * methods, the admin allowlist, the trusted request origins, and the `/static` cache policy without editing config files. Note `TI_WEB_AUTH_METHODS`,
19
+ * `TI_WEB_AUTH_ADMINS`, `TI_WEB_TRUSTED_ORIGINS`, and `TI_WEB_STATIC_IMMUTABLE_PATHS` fully REPLACE their config arrays (`auth.enabledMethods` / `auth.admins` / `trustedOrigins` / `staticCache.immutablePaths`) rather than
20
20
  * merging — the config-file merge is by-index and cannot cleanly override an array.
21
21
  *
22
22
  * @method
@@ -62,6 +62,23 @@ function applyWebConfigEnvOverrides( config, env = process.env ) {
62
62
  if ( env.TI_WEB_TRUSTED_ORIGINS !== undefined ) {
63
63
  config.trustedOrigins = env.TI_WEB_TRUSTED_ORIGINS.split( "," ).map( ( origin ) => origin.trim() ).filter( ( origin ) => origin.length > 0 );
64
64
  }
65
+ if ( env.TI_WEB_STATIC_MAX_AGE !== undefined ) {
66
+ // Seconds, matching the `Cache-Control` directive itself. A non-integer is left to the config value rather
67
+ // than coerced, exactly as TI_WEB_PORT is — `TiWebServer.resolveStaticCachePolicy` reports the bad value.
68
+ const maxAge = Number( env.TI_WEB_STATIC_MAX_AGE );
69
+ if ( Number.isInteger( maxAge ) && maxAge >= 0 ) {
70
+ config.staticCache = config.staticCache || {};
71
+ config.staticCache.maxAge = maxAge;
72
+ }
73
+ }
74
+ if ( env.TI_WEB_STATIC_IMMUTABLE !== undefined ) {
75
+ config.staticCache = config.staticCache || {};
76
+ config.staticCache.immutable = tools.toBool( env.TI_WEB_STATIC_IMMUTABLE );
77
+ }
78
+ if ( env.TI_WEB_STATIC_IMMUTABLE_PATHS !== undefined ) {
79
+ config.staticCache = config.staticCache || {};
80
+ config.staticCache.immutablePaths = env.TI_WEB_STATIC_IMMUTABLE_PATHS.split( "," ).map( ( prefix ) => prefix.trim() ).filter( ( prefix ) => prefix.length > 0 );
81
+ }
65
82
  return config;
66
83
  }
67
84