@ti-engine/web-framework 1.19.0 → 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 (38) hide show
  1. package/.env +4 -4
  2. package/CHANGELOG.md +362 -353
  3. package/README.md +73 -73
  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.html +33 -33
  9. package/bin/static/fragments/components/component-tooltip.html +10 -10
  10. package/bin/static/fragments/components/component-topbar.html +5 -5
  11. package/bin/static/fragments/frame-administration.html +2 -2
  12. package/bin/static/fragments/frame-application.html +18 -18
  13. package/bin/static/fragments/frame-dashboard.html +2 -2
  14. package/bin/static/fragments/frame-login.html +119 -119
  15. package/bin/static/fragments/frame-not-found.html +2 -2
  16. package/bin/static/fragments/frame-profile.html +2 -2
  17. package/bin/static/index.html +22 -22
  18. package/bin/static/scripts/ti-charts.js +1591 -1591
  19. package/bin/static/scripts/ti-framework.css +3194 -3194
  20. package/bin/static/scripts/ti-framework.js +1427 -1427
  21. package/bin/static/scripts/ti-theme-black-glass.css +216 -216
  22. package/bin/static/scripts/ti-theme-daylight.css +87 -87
  23. package/bin/web-app-manager.js +663 -663
  24. package/bin/web-server.js +936 -936
  25. package/bin/web-server.json +48 -48
  26. package/components/admin-config-handlers.js +92 -92
  27. package/components/auth-manager.js +441 -441
  28. package/components/authorization.js +135 -135
  29. package/components/config-change-notifier.js +98 -98
  30. package/components/config-registry.js +260 -260
  31. package/components/config-service.js +360 -360
  32. package/components/config-store.js +246 -246
  33. package/components/definitions.types.js +26 -26
  34. package/components/session-store.js +110 -110
  35. package/components/user.js +132 -132
  36. package/components/web-config-env.js +85 -85
  37. package/components/web-handlers.js +800 -800
  38. 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;
@@ -1,85 +1,85 @@
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-2026 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
- "use strict";
10
-
11
- const tools = require( "@ti-engine/core/tools" );
12
-
13
- /**
14
- * Applies TI_WEB_* environment-variable overrides onto an (already-merged) web server configuration object.
15
- * Each override is applied ONLY when its environment variable is defined, so an absent variable leaves the
16
- * configured/default value untouched (fully backward compatible). This gives ti-engine web servers 12-factor,
17
- * container-friendly control over network binding, TLS, the session cookie secret, the enabled authentication
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
- * merging — the config-file merge is by-index and cannot cleanly override an array.
21
- *
22
- * @method
23
- * @param {Object} config The web server configuration to augment (mutated in place and returned).
24
- * @param {Object} [env=process.env] The environment source (injectable for testing).
25
- * @returns {Object} The same config object, with any present overrides applied.
26
- * @public
27
- */
28
- function applyWebConfigEnvOverrides( config, env = process.env ) {
29
- if ( !config || typeof config !== "object" ) {
30
- return config;
31
- }
32
- if ( env.TI_WEB_HOST !== undefined ) {
33
- config.host = env.TI_WEB_HOST;
34
- }
35
- if ( env.TI_WEB_PORT !== undefined ) {
36
- const port = Number( env.TI_WEB_PORT );
37
- if ( Number.isInteger( port ) ) {
38
- config.port = port;
39
- }
40
- }
41
- if ( env.TI_WEB_USE_TLS !== undefined ) {
42
- config.useTLS = tools.toBool( env.TI_WEB_USE_TLS );
43
- }
44
- if ( env.TI_WEB_TLS_CERT_PATH !== undefined ) {
45
- config.tlsCertPath = env.TI_WEB_TLS_CERT_PATH;
46
- }
47
- if ( env.TI_WEB_TLS_KEY_PATH !== undefined ) {
48
- config.tlsKeyPath = env.TI_WEB_TLS_KEY_PATH;
49
- }
50
- if ( env.TI_WEB_COOKIE_SECRET !== undefined ) {
51
- config.cookies = config.cookies || {};
52
- config.cookies.secret = env.TI_WEB_COOKIE_SECRET;
53
- }
54
- if ( env.TI_WEB_AUTH_METHODS !== undefined ) {
55
- config.auth = config.auth || {};
56
- config.auth.enabledMethods = env.TI_WEB_AUTH_METHODS.split( "," ).map( ( method ) => method.trim() ).filter( ( method ) => method.length > 0 );
57
- }
58
- if ( env.TI_WEB_AUTH_ADMINS !== undefined ) {
59
- config.auth = config.auth || {};
60
- config.auth.admins = env.TI_WEB_AUTH_ADMINS.split( "," ).map( ( entry ) => entry.trim() ).filter( ( entry ) => entry.length > 0 );
61
- }
62
- if ( env.TI_WEB_TRUSTED_ORIGINS !== undefined ) {
63
- config.trustedOrigins = env.TI_WEB_TRUSTED_ORIGINS.split( "," ).map( ( origin ) => origin.trim() ).filter( ( origin ) => origin.length > 0 );
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
- }
82
- return config;
83
- }
84
-
85
- module.exports = applyWebConfigEnvOverrides;
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-2026 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
+ "use strict";
10
+
11
+ const tools = require( "@ti-engine/core/tools" );
12
+
13
+ /**
14
+ * Applies TI_WEB_* environment-variable overrides onto an (already-merged) web server configuration object.
15
+ * Each override is applied ONLY when its environment variable is defined, so an absent variable leaves the
16
+ * configured/default value untouched (fully backward compatible). This gives ti-engine web servers 12-factor,
17
+ * container-friendly control over network binding, TLS, the session cookie secret, the enabled authentication
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
+ * merging — the config-file merge is by-index and cannot cleanly override an array.
21
+ *
22
+ * @method
23
+ * @param {Object} config The web server configuration to augment (mutated in place and returned).
24
+ * @param {Object} [env=process.env] The environment source (injectable for testing).
25
+ * @returns {Object} The same config object, with any present overrides applied.
26
+ * @public
27
+ */
28
+ function applyWebConfigEnvOverrides( config, env = process.env ) {
29
+ if ( !config || typeof config !== "object" ) {
30
+ return config;
31
+ }
32
+ if ( env.TI_WEB_HOST !== undefined ) {
33
+ config.host = env.TI_WEB_HOST;
34
+ }
35
+ if ( env.TI_WEB_PORT !== undefined ) {
36
+ const port = Number( env.TI_WEB_PORT );
37
+ if ( Number.isInteger( port ) ) {
38
+ config.port = port;
39
+ }
40
+ }
41
+ if ( env.TI_WEB_USE_TLS !== undefined ) {
42
+ config.useTLS = tools.toBool( env.TI_WEB_USE_TLS );
43
+ }
44
+ if ( env.TI_WEB_TLS_CERT_PATH !== undefined ) {
45
+ config.tlsCertPath = env.TI_WEB_TLS_CERT_PATH;
46
+ }
47
+ if ( env.TI_WEB_TLS_KEY_PATH !== undefined ) {
48
+ config.tlsKeyPath = env.TI_WEB_TLS_KEY_PATH;
49
+ }
50
+ if ( env.TI_WEB_COOKIE_SECRET !== undefined ) {
51
+ config.cookies = config.cookies || {};
52
+ config.cookies.secret = env.TI_WEB_COOKIE_SECRET;
53
+ }
54
+ if ( env.TI_WEB_AUTH_METHODS !== undefined ) {
55
+ config.auth = config.auth || {};
56
+ config.auth.enabledMethods = env.TI_WEB_AUTH_METHODS.split( "," ).map( ( method ) => method.trim() ).filter( ( method ) => method.length > 0 );
57
+ }
58
+ if ( env.TI_WEB_AUTH_ADMINS !== undefined ) {
59
+ config.auth = config.auth || {};
60
+ config.auth.admins = env.TI_WEB_AUTH_ADMINS.split( "," ).map( ( entry ) => entry.trim() ).filter( ( entry ) => entry.length > 0 );
61
+ }
62
+ if ( env.TI_WEB_TRUSTED_ORIGINS !== undefined ) {
63
+ config.trustedOrigins = env.TI_WEB_TRUSTED_ORIGINS.split( "," ).map( ( origin ) => origin.trim() ).filter( ( origin ) => origin.length > 0 );
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
+ }
82
+ return config;
83
+ }
84
+
85
+ module.exports = applyWebConfigEnvOverrides;