jsgui3-server 0.0.149 → 0.0.150
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/.github/agents/Mobile Developer.agent.md +89 -0
- package/AGENTS.md +4 -0
- package/README.md +130 -0
- package/admin-ui/client.js +73 -43
- package/admin-ui/v1/admin_auth_service.js +197 -0
- package/admin-ui/v1/admin_user_store.js +71 -0
- package/admin-ui/v1/client.js +17 -0
- package/admin-ui/v1/controls/admin_shell.js +1399 -0
- package/admin-ui/v1/controls/group_box.js +84 -0
- package/admin-ui/v1/controls/stat_card.js +125 -0
- package/admin-ui/v1/server.js +658 -0
- package/admin-ui/v1/utils/formatters.js +68 -0
- package/docs/admin-extension-guide.md +345 -0
- package/docs/books/adaptive-control-improvements/01-control-candidate-matrix.md +122 -0
- package/docs/books/adaptive-control-improvements/02-tier-1-layout-playbooks.md +207 -0
- package/docs/books/adaptive-control-improvements/03-tier-2-navigation-form-overlay.md +140 -0
- package/docs/books/adaptive-control-improvements/04-cross-cutting-platform-functionality.md +141 -0
- package/docs/books/adaptive-control-improvements/05-styling-theming-density-upgrades.md +114 -0
- package/docs/books/adaptive-control-improvements/06-testing-quality-gates.md +97 -0
- package/docs/books/adaptive-control-improvements/07-delivery-roadmap-and-ownership.md +137 -0
- package/docs/books/adaptive-control-improvements/08-appendix-tier1-acceptance-and-pr-templates.md +261 -0
- package/docs/books/adaptive-control-improvements/README.md +66 -0
- package/docs/books/admin-ui-authentication/01-threat-model-and-goals.md +124 -0
- package/docs/books/admin-ui-authentication/02-session-model-and-token-model.md +75 -0
- package/docs/books/admin-ui-authentication/03-auth-middleware-patterns.md +77 -0
- package/docs/books/admin-ui-authentication/README.md +25 -0
- package/docs/books/creating-a-new-admin-ui/01-introduction-and-vision.md +130 -0
- package/docs/books/creating-a-new-admin-ui/02-architecture-and-data-flow.md +298 -0
- package/docs/books/creating-a-new-admin-ui/03-server-introspection.md +381 -0
- package/docs/books/creating-a-new-admin-ui/04-admin-module-adapter-layer.md +592 -0
- package/docs/books/creating-a-new-admin-ui/05-domain-controls-stat-cards-and-gauges.md +513 -0
- package/docs/books/creating-a-new-admin-ui/06-domain-controls-process-manager.md +544 -0
- package/docs/books/creating-a-new-admin-ui/07-domain-controls-resource-pool-inspector.md +493 -0
- package/docs/books/creating-a-new-admin-ui/08-domain-controls-route-table-and-api-explorer.md +586 -0
- package/docs/books/creating-a-new-admin-ui/09-domain-controls-log-viewer-and-activity-feed.md +490 -0
- package/docs/books/creating-a-new-admin-ui/10-domain-controls-build-status-and-bundle-inspector.md +526 -0
- package/docs/books/creating-a-new-admin-ui/11-domain-controls-configuration-panel.md +808 -0
- package/docs/books/creating-a-new-admin-ui/12-admin-shell-layout-sidebar-navigation.md +210 -0
- package/docs/books/creating-a-new-admin-ui/13-telemetry-integration.md +556 -0
- package/docs/books/creating-a-new-admin-ui/14-realtime-sse-observable-integration.md +485 -0
- package/docs/books/creating-a-new-admin-ui/15-styling-theming-aero-design-system.md +521 -0
- package/docs/books/creating-a-new-admin-ui/16-testing-and-quality-assurance.md +147 -0
- package/docs/books/creating-a-new-admin-ui/17-next-steps-process-resource-roadmap.md +356 -0
- package/docs/books/creating-a-new-admin-ui/README.md +68 -0
- package/docs/books/device-adaptive-composition/01-platform-feature-audit.md +177 -0
- package/docs/books/device-adaptive-composition/02-responsive-composition-model.md +187 -0
- package/docs/books/device-adaptive-composition/03-data-model-vs-view-model.md +231 -0
- package/docs/books/device-adaptive-composition/04-styling-theme-breakpoints.md +234 -0
- package/docs/books/device-adaptive-composition/05-showcase-app-multi-device-assessment.md +193 -0
- package/docs/books/device-adaptive-composition/06-implementation-patterns-and-apis.md +346 -0
- package/docs/books/device-adaptive-composition/07-testing-harness-and-quality-gates.md +265 -0
- package/docs/books/device-adaptive-composition/08-roadmap-and-adoption-plan.md +250 -0
- package/docs/books/device-adaptive-composition/README.md +47 -0
- package/docs/comparison-report-express-plex-cpanel.md +549 -0
- package/docs/designs/server-admin-interface-aero.svg +611 -0
- package/docs/troubleshooting.md +84 -53
- package/module.js +16 -11
- package/package.json +1 -1
- package/serve-factory.js +1 -0
- package/server.js +199 -0
- package/tests/README.md +5 -0
- package/tests/admin-ui-jsgui-controls.test.js +581 -0
- package/tests/test-runner.js +1 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-071799b982906680f5fd699d.js +0 -40
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-07352945ad5c92654fcb8b65.js +0 -39
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-138a601fadb6191ea314c6fd.js +0 -39
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-171f6c381c2cadf2e9fa7087.js +0 -39
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-1d973388156b84a04373fac9.js +0 -39
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-20e117bc8a10d2cd16234bbe.js +0 -40
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-2b028a82b0e5efddba42425f.js +0 -39
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-4518556cd5c7e059e82b22b8.js +0 -40
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-5bac1aa0f213902f718ed74f.js +0 -40
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-5f9996ac7822caf777d92f56.js +0 -39
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-60a92c702e65fd9cf748e3ec.js +0 -39
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-6164c1f8f738995c541895d2.js +0 -44
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-6718a85eb9e5aa782dd47a05.js +0 -45
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-69e280f14e37aee76a1d4675.js +0 -39
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7570d1b030d44b111ed59c4c.js +0 -39
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7798c9bbd55e510d5039f936.js +0 -42
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-78cd511ea1ef18ecb03d1be5.js +0 -40
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7d482e0b95bcb5e3c543118b.js +0 -43
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-80e9476d1127c55b40fdb36f.js +0 -40
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-810ced55d5320a3088a05b13.js +0 -40
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-8423565f1a40e329afc8c6cf.js +0 -40
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-900bef783b8cee36506ec282.js +0 -39
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-a1a37aff6416fdad74040ddf.js +0 -39
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-ad48d5e8eda40f175b4df090.js +0 -39
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-aec5a2d963015528c9099462.js +0 -39
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-af9d34e0f1722fab9e28c269.js +0 -39
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-b818e4015e2f1fe86280b5ab.js +0 -41
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-bcb2541adc70b7aba61768c5.js +0 -44
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-bfe89d2c78ed44f95ed7dd73.js +0 -40
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-c06f04806a1e688e1187110c.js +0 -40
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-c3f3adf904f585afc544b96a.js +0 -39
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-d45acb873e1d8e32d5e60f2e.js +0 -39
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-db06f132533706f4a0163b8c.js +0 -39
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-f660f40d78b135fc8560a862.js +0 -39
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-f9dee4ec18a96e09bee06bae.js +0 -39
package/docs/troubleshooting.md
CHANGED
|
@@ -197,14 +197,14 @@ input.js:1:0: ERROR: Expected identifier but found "}"
|
|
|
197
197
|
|
|
198
198
|
2. **Verify CSS definition:**
|
|
199
199
|
```javascript
|
|
200
|
-
MyControl.css = `
|
|
201
|
-
.my-control {
|
|
202
|
-
padding: 20px;
|
|
203
|
-
background: #f0f0f0;
|
|
204
|
-
}
|
|
205
|
-
`;
|
|
206
|
-
```
|
|
207
|
-
You can also use `MyControl.scss` or `MyControl.sass` with template literals; these compile to CSS during bundling (ensure the `sass` dependency is installed). To see inline CSS sourcemaps in devtools, enable `style.sourcemaps` (or run with `debug: true`).
|
|
200
|
+
MyControl.css = `
|
|
201
|
+
.my-control {
|
|
202
|
+
padding: 20px;
|
|
203
|
+
background: #f0f0f0;
|
|
204
|
+
}
|
|
205
|
+
`;
|
|
206
|
+
```
|
|
207
|
+
You can also use `MyControl.scss` or `MyControl.sass` with template literals; these compile to CSS during bundling (ensure the `sass` dependency is installed). To see inline CSS sourcemaps in devtools, enable `style.sourcemaps` (or run with `debug: true`).
|
|
208
208
|
|
|
209
209
|
3. **Check activation:**
|
|
210
210
|
```javascript
|
|
@@ -540,6 +540,37 @@ from origin 'http://localhost:3000' has been blocked by CORS policy
|
|
|
540
540
|
delete require.cache[require.resolve('./client.js')];
|
|
541
541
|
```
|
|
542
542
|
|
|
543
|
+
### Generated `.jsgui3-server-cache` Shim Files
|
|
544
|
+
|
|
545
|
+
**Symptoms:**
|
|
546
|
+
- You see many changed files under `.jsgui3-server-cache/jsgui3-html-shims/`
|
|
547
|
+
- Shim files contain absolute machine-specific paths
|
|
548
|
+
- Git status looks noisy after running examples or admin pages
|
|
549
|
+
|
|
550
|
+
**What this is:**
|
|
551
|
+
- These are generated cache artifacts created by the bundling/render pipeline.
|
|
552
|
+
- They are performance helpers, not source-of-truth project files.
|
|
553
|
+
|
|
554
|
+
**Expected behavior:**
|
|
555
|
+
1. They may be regenerated when controls, environment, or module resolution changes.
|
|
556
|
+
2. They can differ across machines (for example, local `node_modules` path vs NVM/global path).
|
|
557
|
+
3. They are safe to delete; they will be recreated when needed.
|
|
558
|
+
|
|
559
|
+
**Recommended project hygiene:**
|
|
560
|
+
1. Ignore cache directories in `.gitignore`:
|
|
561
|
+
```gitignore
|
|
562
|
+
.jsgui3-server-cache/
|
|
563
|
+
**/.jsgui3-server-cache/
|
|
564
|
+
```
|
|
565
|
+
2. If accidentally changed, revert generated shims:
|
|
566
|
+
```bash
|
|
567
|
+
git restore -- .jsgui3-server-cache/jsgui3-html-shims/*.js
|
|
568
|
+
```
|
|
569
|
+
3. If a nested example cache appears, remove it:
|
|
570
|
+
```bash
|
|
571
|
+
rm -rf "examples/controls/1) window/.jsgui3-server-cache"
|
|
572
|
+
```
|
|
573
|
+
|
|
543
574
|
### Source Maps Not Working
|
|
544
575
|
|
|
545
576
|
**Symptoms:**
|
|
@@ -743,48 +774,48 @@ controls.MinimalControl = MinimalControl;
|
|
|
743
774
|
module.exports = jsgui;
|
|
744
775
|
```
|
|
745
776
|
|
|
746
|
-
This minimal setup helps isolate whether the issue is with your specific code or the framework itself.
|
|
747
|
-
|
|
748
|
-
---
|
|
749
|
-
|
|
750
|
-
## Bundling and Test Failures
|
|
751
|
-
|
|
752
|
-
### `waiting for wp_publisher ready` or Publisher Ready Timeout
|
|
753
|
-
|
|
754
|
-
**Symptoms:**
|
|
755
|
-
- Tests hang while starting servers
|
|
756
|
-
- Logs show `waiting for wp_publisher ready`
|
|
757
|
-
- Tests time out without a clear stack trace
|
|
758
|
-
|
|
759
|
-
**Likely cause:** the JS/CSS bundler failed before emitting `ready`.
|
|
760
|
-
|
|
761
|
-
**What to check:**
|
|
762
|
-
1. Look earlier in the log for bundler errors (esbuild, Sass, file resolution).
|
|
763
|
-
2. Confirm `sass` is installed if you are running Sass tests.
|
|
764
|
-
3. Validate the client entry path passed to `src_path_client_js`.
|
|
765
|
-
|
|
766
|
-
### esbuild platform mismatch (Windows vs WSL)
|
|
767
|
-
|
|
768
|
-
**Symptoms:**
|
|
769
|
-
- Error: `You installed esbuild for another platform than the one you're currently using`
|
|
770
|
-
|
|
771
|
-
**Cause:** `node_modules` was installed on a different OS and reused in this environment.
|
|
772
|
-
|
|
773
|
-
**Fix:**
|
|
774
|
-
1. Remove the existing `node_modules` from the current workspace.
|
|
775
|
-
2. Reinstall dependencies in the current environment:
|
|
776
|
-
```bash
|
|
777
|
-
npm install
|
|
778
|
-
```
|
|
779
|
-
3. If you prefer to keep the lockfile untouched, use:
|
|
780
|
-
```bash
|
|
781
|
-
npm ci
|
|
782
|
-
```
|
|
783
|
-
4. As a fast workaround, you can try:
|
|
784
|
-
```bash
|
|
785
|
-
npm rebuild esbuild
|
|
786
|
-
```
|
|
787
|
-
|
|
788
|
-
---
|
|
789
|
-
|
|
790
|
-
Remember: Most issues can be resolved by carefully checking the console output, verifying file paths, and ensuring proper control lifecycle management. Start with the basics and work systematically through the possible causes.
|
|
777
|
+
This minimal setup helps isolate whether the issue is with your specific code or the framework itself.
|
|
778
|
+
|
|
779
|
+
---
|
|
780
|
+
|
|
781
|
+
## Bundling and Test Failures
|
|
782
|
+
|
|
783
|
+
### `waiting for wp_publisher ready` or Publisher Ready Timeout
|
|
784
|
+
|
|
785
|
+
**Symptoms:**
|
|
786
|
+
- Tests hang while starting servers
|
|
787
|
+
- Logs show `waiting for wp_publisher ready`
|
|
788
|
+
- Tests time out without a clear stack trace
|
|
789
|
+
|
|
790
|
+
**Likely cause:** the JS/CSS bundler failed before emitting `ready`.
|
|
791
|
+
|
|
792
|
+
**What to check:**
|
|
793
|
+
1. Look earlier in the log for bundler errors (esbuild, Sass, file resolution).
|
|
794
|
+
2. Confirm `sass` is installed if you are running Sass tests.
|
|
795
|
+
3. Validate the client entry path passed to `src_path_client_js`.
|
|
796
|
+
|
|
797
|
+
### esbuild platform mismatch (Windows vs WSL)
|
|
798
|
+
|
|
799
|
+
**Symptoms:**
|
|
800
|
+
- Error: `You installed esbuild for another platform than the one you're currently using`
|
|
801
|
+
|
|
802
|
+
**Cause:** `node_modules` was installed on a different OS and reused in this environment.
|
|
803
|
+
|
|
804
|
+
**Fix:**
|
|
805
|
+
1. Remove the existing `node_modules` from the current workspace.
|
|
806
|
+
2. Reinstall dependencies in the current environment:
|
|
807
|
+
```bash
|
|
808
|
+
npm install
|
|
809
|
+
```
|
|
810
|
+
3. If you prefer to keep the lockfile untouched, use:
|
|
811
|
+
```bash
|
|
812
|
+
npm ci
|
|
813
|
+
```
|
|
814
|
+
4. As a fast workaround, you can try:
|
|
815
|
+
```bash
|
|
816
|
+
npm rebuild esbuild
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
---
|
|
820
|
+
|
|
821
|
+
Remember: Most issues can be resolved by carefully checking the console output, verifying file paths, and ensuring proper control lifecycle management. Start with the basics and work systematically through the possible causes.
|
package/module.js
CHANGED
|
@@ -16,17 +16,22 @@ jsgui.controls.Active_HTML_Document = require('./controls/Active_HTML_Document')
|
|
|
16
16
|
|
|
17
17
|
//const Resource_Publisher = require('./publishing/http-resource-publisher');
|
|
18
18
|
//jsgui.Resource_Publisher = Resource_Publisher;
|
|
19
|
-
const Server = require('./server');
|
|
20
|
-
jsgui.Server = Server;
|
|
21
|
-
jsgui.serve = Server.serve;
|
|
22
|
-
jsgui.Process_Resource = Server.Process_Resource;
|
|
23
|
-
jsgui.Remote_Process_Resource = Server.Remote_Process_Resource;
|
|
24
|
-
jsgui.HTTP_SSE_Publisher = Server.HTTP_SSE_Publisher;
|
|
25
|
-
if (jsgui.Resource) {
|
|
26
|
-
jsgui.Resource.Process = Server.Process_Resource;
|
|
27
|
-
jsgui.Resource.Remote_Process = Server.Remote_Process_Resource;
|
|
28
|
-
}
|
|
29
|
-
jsgui.fs2 = require('./fs2');
|
|
19
|
+
const Server = require('./server');
|
|
20
|
+
jsgui.Server = Server;
|
|
21
|
+
jsgui.serve = Server.serve;
|
|
22
|
+
jsgui.Process_Resource = Server.Process_Resource;
|
|
23
|
+
jsgui.Remote_Process_Resource = Server.Remote_Process_Resource;
|
|
24
|
+
jsgui.HTTP_SSE_Publisher = Server.HTTP_SSE_Publisher;
|
|
25
|
+
if (jsgui.Resource) {
|
|
26
|
+
jsgui.Resource.Process = Server.Process_Resource;
|
|
27
|
+
jsgui.Resource.Remote_Process = Server.Remote_Process_Resource;
|
|
28
|
+
}
|
|
29
|
+
jsgui.fs2 = require('./fs2');
|
|
30
|
+
|
|
31
|
+
// Admin UI extensibility exports
|
|
32
|
+
jsgui.Admin_Module_V1 = Server.Admin_Module_V1;
|
|
33
|
+
jsgui.Admin_Auth_Service = Server.Admin_Auth_Service;
|
|
34
|
+
jsgui.Admin_User_Store = Server.Admin_User_Store;
|
|
30
35
|
|
|
31
36
|
// Port utilities for auto-port selection
|
|
32
37
|
const port_utils = require('./port-utils');
|
package/package.json
CHANGED
package/serve-factory.js
CHANGED
|
@@ -289,6 +289,7 @@ module.exports = (Server) => {
|
|
|
289
289
|
};
|
|
290
290
|
if (style_config !== undefined) server_spec.style = style_config;
|
|
291
291
|
if (bundler_config !== undefined) server_spec.bundler = bundler_config;
|
|
292
|
+
if (serve_options.admin !== undefined) server_spec.admin = serve_options.admin;
|
|
292
293
|
|
|
293
294
|
if (typeof serve_options.ctrl === 'function') {
|
|
294
295
|
server_spec.Ctrl = serve_options.ctrl;
|
package/server.js
CHANGED
|
@@ -134,6 +134,196 @@ class JSGUI_Single_Process_Server extends Evented_Class {
|
|
|
134
134
|
console.warn('Skipping /admin route registration due to missing control.');
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
// ─── Admin UI V1 (Dashboard with Stat Cards) ─────────
|
|
138
|
+
// Configurable via spec.admin:
|
|
139
|
+
// spec.admin === false → disable admin entirely
|
|
140
|
+
// spec.admin = { enabled: false } → disable admin entirely
|
|
141
|
+
// spec.admin = { sections: [...] } → add custom sidebar sections
|
|
142
|
+
// spec.admin = { endpoints: [...] } → add custom protected endpoints
|
|
143
|
+
const admin_config = spec.admin !== undefined ? spec.admin : {};
|
|
144
|
+
const admin_enabled = admin_config !== false && (admin_config.enabled !== false);
|
|
145
|
+
|
|
146
|
+
const Admin_Module_V1 = require('./admin-ui/v1/server');
|
|
147
|
+
if (admin_enabled) {
|
|
148
|
+
this.admin_v1 = new Admin_Module_V1(typeof admin_config === 'object' ? admin_config : {});
|
|
149
|
+
this.admin_v1.init(this);
|
|
150
|
+
|
|
151
|
+
// Register Admin V1 Page Route
|
|
152
|
+
let Admin_Shell_Control;
|
|
153
|
+
try {
|
|
154
|
+
Admin_Shell_Control = require('./admin-ui/v1/client').controls.Admin_Shell;
|
|
155
|
+
} catch (e) {
|
|
156
|
+
console.warn('Failed to load Admin_Shell control:', e.message || e);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (Admin_Shell_Control) {
|
|
160
|
+
const admin_v1_app = new Webpage({
|
|
161
|
+
content: Admin_Shell_Control,
|
|
162
|
+
title: 'jsgui3 Admin Dashboard'
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
const admin_v1_publisher = new HTTP_Webpage_Publisher({
|
|
166
|
+
name: 'Admin_V1_Publisher',
|
|
167
|
+
webpage: admin_v1_app,
|
|
168
|
+
src_path_client_js: lib_path.join(__dirname, 'admin-ui/v1/client.js')
|
|
169
|
+
});
|
|
170
|
+
admin_v1_publisher.name = 'Admin_V1_Publisher';
|
|
171
|
+
|
|
172
|
+
const is_dev_defaults = !process.env.ADMIN_V1_PASSWORD && process.env.NODE_ENV !== 'production';
|
|
173
|
+
const login_hint = is_dev_defaults
|
|
174
|
+
? '<div class="hint dev-creds">Dev defaults active — username: <code>admin</code> password: <code>admin</code></div><div class="hint">Set ADMIN_V1_USER / ADMIN_V1_PASSWORD env vars for production.</div>'
|
|
175
|
+
: '<div class="hint">Sign in with your configured credentials.</div>';
|
|
176
|
+
const login_html = `<!doctype html>
|
|
177
|
+
<html>
|
|
178
|
+
<head>
|
|
179
|
+
<meta charset="utf-8" />
|
|
180
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
181
|
+
<title>Admin Login</title>
|
|
182
|
+
<style>
|
|
183
|
+
body { margin:0; font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif; background:#111428; color:#e8e8f0; display:flex; align-items:center; justify-content:center; min-height:100vh; }
|
|
184
|
+
.card { width: 340px; background:#1b1f38; border:1px solid #2f3358; border-radius:10px; padding:20px; }
|
|
185
|
+
h1 { margin:0 0 16px; font-size:1rem; }
|
|
186
|
+
label { display:block; margin:10px 0 6px; font-size:0.8rem; color:#9aa0c8; }
|
|
187
|
+
input { width:100%; box-sizing:border-box; background:#13162a; border:1px solid #2e345c; color:#e8e8f0; border-radius:6px; padding:8px 10px; }
|
|
188
|
+
button { margin-top:14px; width:100%; border:1px solid #4facfe; background:#224267; color:#d3ebff; border-radius:6px; padding:8px 10px; cursor:pointer; }
|
|
189
|
+
.err { margin-top:10px; color:#ff8e9f; font-size:0.8rem; min-height:1.1rem; }
|
|
190
|
+
.hint { margin-top:12px; color:#7f86b3; font-size:0.75rem; }
|
|
191
|
+
.dev-creds { color:#43e97b; }
|
|
192
|
+
code { background:#13162a; padding:2px 6px; border-radius:4px; font-size:0.8rem; }
|
|
193
|
+
</style>
|
|
194
|
+
</head>
|
|
195
|
+
<body>
|
|
196
|
+
<div class="card">
|
|
197
|
+
<h1>jsgui3 Admin Login</h1>
|
|
198
|
+
<form id="login-form">
|
|
199
|
+
<label for="username">Username</label>
|
|
200
|
+
<input id="username" autocomplete="username" required />
|
|
201
|
+
<label for="password">Password</label>
|
|
202
|
+
<input id="password" type="password" autocomplete="current-password" required />
|
|
203
|
+
<button type="submit">Sign In</button>
|
|
204
|
+
<div class="err" id="err"></div>
|
|
205
|
+
${login_hint}
|
|
206
|
+
</form>
|
|
207
|
+
</div>
|
|
208
|
+
<script>
|
|
209
|
+
(async () => {
|
|
210
|
+
try {
|
|
211
|
+
const session = await fetch('/api/admin/v1/auth/session', { credentials: 'same-origin' }).then(r => r.json());
|
|
212
|
+
if (session && session.authenticated) {
|
|
213
|
+
location.replace('/admin/v1');
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
} catch (e) {}
|
|
217
|
+
|
|
218
|
+
const form = document.getElementById('login-form');
|
|
219
|
+
const err = document.getElementById('err');
|
|
220
|
+
form.addEventListener('submit', async (e) => {
|
|
221
|
+
e.preventDefault();
|
|
222
|
+
err.textContent = '';
|
|
223
|
+
const username = document.getElementById('username').value;
|
|
224
|
+
const password = document.getElementById('password').value;
|
|
225
|
+
try {
|
|
226
|
+
const res = await fetch('/api/admin/v1/auth/login', {
|
|
227
|
+
method: 'POST',
|
|
228
|
+
headers: { 'Content-Type': 'application/json' },
|
|
229
|
+
credentials: 'same-origin',
|
|
230
|
+
body: JSON.stringify({ username, password })
|
|
231
|
+
});
|
|
232
|
+
const data = await res.json();
|
|
233
|
+
if (!res.ok || !data.ok) {
|
|
234
|
+
err.textContent = data && data.error ? data.error : 'Login failed';
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
location.replace('/admin/v1');
|
|
238
|
+
} catch (e2) {
|
|
239
|
+
err.textContent = 'Network error';
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
})();
|
|
243
|
+
</script>
|
|
244
|
+
</body>
|
|
245
|
+
</html>`;
|
|
246
|
+
|
|
247
|
+
const serve_admin_v1_page = (req, res) => {
|
|
248
|
+
if (req && typeof req.url === 'string' && req.url.startsWith('/admin/v1/login')) {
|
|
249
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
250
|
+
res.end(login_html);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (!this.admin_v1 || !this.admin_v1.is_admin_read_request(req)) {
|
|
255
|
+
res.writeHead(302, { Location: '/admin/v1/login' });
|
|
256
|
+
res.end();
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
if (admin_v1_cached_html) {
|
|
260
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
261
|
+
res.end(admin_v1_cached_html);
|
|
262
|
+
} else {
|
|
263
|
+
res.writeHead(503, { 'Content-Type': 'text/plain' });
|
|
264
|
+
res.end('Admin UI v1 is loading, please retry...');
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
server_router.set_route('/admin/v1/login', null, (req, res) => {
|
|
269
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
270
|
+
res.end(login_html);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
// Namespace admin v1 assets under /admin/v1/ to avoid
|
|
274
|
+
// colliding with the main app's /js/js.js and /css/css.css.
|
|
275
|
+
let admin_v1_cached_html = null;
|
|
276
|
+
|
|
277
|
+
admin_v1_publisher.on('ready', (res_ready) => {
|
|
278
|
+
if (res_ready && res_ready._arr) {
|
|
279
|
+
for (const bundle_item of res_ready._arr) {
|
|
280
|
+
const { type } = bundle_item;
|
|
281
|
+
if (type === 'HTML') {
|
|
282
|
+
// Rewrite asset references so browser fetches
|
|
283
|
+
// the admin-specific JS/CSS, not the main app's.
|
|
284
|
+
let html = bundle_item.text || '';
|
|
285
|
+
html = html.replace(
|
|
286
|
+
/href="\/css\/css\.css"/g,
|
|
287
|
+
'href="/admin/v1/css/css.css"'
|
|
288
|
+
);
|
|
289
|
+
html = html.replace(
|
|
290
|
+
/src="\/js\/js\.js"/g,
|
|
291
|
+
'src="/admin/v1/js/js.js"'
|
|
292
|
+
);
|
|
293
|
+
// Inject viewport meta for mobile rendering
|
|
294
|
+
if (!html.includes('name="viewport"')) {
|
|
295
|
+
html = html.replace(
|
|
296
|
+
/<head([^>]*)>/i,
|
|
297
|
+
'<head$1><meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">'
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
admin_v1_cached_html = html;
|
|
301
|
+
} else {
|
|
302
|
+
// JS → /admin/v1/js/js.js, CSS → /admin/v1/css/css.css
|
|
303
|
+
const namespaced_route =
|
|
304
|
+
type === 'JavaScript' ? '/admin/v1/js/js.js' :
|
|
305
|
+
type === 'CSS' ? '/admin/v1/css/css.css' :
|
|
306
|
+
'/admin/v1' + bundle_item.route;
|
|
307
|
+
const responder = new Static_Route_HTTP_Responder(bundle_item);
|
|
308
|
+
server_router.set_route(namespaced_route, responder, responder.handle_http);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Serve authenticated admin page at /admin/v1
|
|
313
|
+
server_router.set_route('/admin/v1', null, serve_admin_v1_page);
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
// Temporary handler until the publisher finishes bundling
|
|
318
|
+
server_router.set_route('/admin/v1', null, serve_admin_v1_page);
|
|
319
|
+
resource_pool.add(admin_v1_publisher);
|
|
320
|
+
} else {
|
|
321
|
+
console.warn('Skipping /admin/v1 route registration due to missing Admin_Shell control.');
|
|
322
|
+
}
|
|
323
|
+
} else {
|
|
324
|
+
// admin_enabled === false
|
|
325
|
+
this.admin_v1 = null;
|
|
326
|
+
}
|
|
137
327
|
|
|
138
328
|
if (spec.routes) {
|
|
139
329
|
throw 'NYI - will use Website class rather than Website_Resource here'
|
|
@@ -540,6 +730,9 @@ class JSGUI_Single_Process_Server extends Evented_Class {
|
|
|
540
730
|
if (this.sse_publisher) {
|
|
541
731
|
stop_targets.push(this.sse_publisher);
|
|
542
732
|
}
|
|
733
|
+
if (this.admin_v1 && typeof this.admin_v1.destroy === 'function') {
|
|
734
|
+
this.admin_v1.destroy();
|
|
735
|
+
}
|
|
543
736
|
|
|
544
737
|
if (stop_targets.length === 0) {
|
|
545
738
|
close_http_servers(() => finalize_close(null));
|
|
@@ -578,6 +771,12 @@ JSGUI_Single_Process_Server.Publishers = Publishers;
|
|
|
578
771
|
JSGUI_Single_Process_Server.Process_Resource = Process_Resource;
|
|
579
772
|
JSGUI_Single_Process_Server.Remote_Process_Resource = Remote_Process_Resource;
|
|
580
773
|
JSGUI_Single_Process_Server.HTTP_SSE_Publisher = HTTP_SSE_Publisher;
|
|
774
|
+
|
|
775
|
+
// Admin UI extensibility exports
|
|
776
|
+
JSGUI_Single_Process_Server.Admin_Module_V1 = require('./admin-ui/v1/server');
|
|
777
|
+
JSGUI_Single_Process_Server.Admin_Auth_Service = require('./admin-ui/v1/admin_auth_service');
|
|
778
|
+
JSGUI_Single_Process_Server.Admin_User_Store = require('./admin-ui/v1/admin_user_store');
|
|
779
|
+
|
|
581
780
|
JSGUI_Single_Process_Server.serve = require('./serve-factory')(JSGUI_Single_Process_Server);
|
|
582
781
|
|
|
583
782
|
module.exports = JSGUI_Single_Process_Server;
|
package/tests/README.md
CHANGED
|
@@ -22,6 +22,7 @@ tests/
|
|
|
22
22
|
├── publishers.test.js # Component isolation tests for publishers
|
|
23
23
|
├── configuration-validation.test.js # Configuration validation tests
|
|
24
24
|
├── admin-ui-render.test.js # Admin page render regression test
|
|
25
|
+
├── admin-ui-jsgui-controls.test.js # Admin shell interaction + control-first regression test
|
|
25
26
|
├── serve.test.js # Server.serve core behavior tests
|
|
26
27
|
├── serve-resources.test.js # Server.serve + resource integration tests
|
|
27
28
|
├── process-resource.test.js # Process_Resource lifecycle and restart tests
|
|
@@ -59,6 +60,9 @@ node tests/test-runner.js --test=bundlers.test.js
|
|
|
59
60
|
# Optimizer cache behavior
|
|
60
61
|
node tests/test-runner.js --test=control-optimizer-cache-behavior.test.js
|
|
61
62
|
|
|
63
|
+
# Admin UI shell interaction regression
|
|
64
|
+
node tests/test-runner.js --test=admin-ui-jsgui-controls.test.js
|
|
65
|
+
|
|
62
66
|
# Using mocha directly
|
|
63
67
|
npx mocha tests/bundlers.test.js
|
|
64
68
|
```
|
|
@@ -257,6 +261,7 @@ Browser-level interaction checks that combine controls and server resources:
|
|
|
257
261
|
### 12. Core Resource and Serve Reliability Tests
|
|
258
262
|
|
|
259
263
|
- `admin-ui-render.test.js` validates the admin page control renders without clobbering control internals
|
|
264
|
+
- `admin-ui-jsgui-controls.test.js` validates admin shell interactions, custom section nav refresh, retry/logout control behavior, and SSE open/error/heartbeat handling
|
|
260
265
|
- `serve.test.js` validates `Server.serve` startup/route readiness behavior
|
|
261
266
|
- `serve-resources.test.js` validates in-process and process resource wiring in serve mode
|
|
262
267
|
- `process-resource.test.js` validates direct process lifecycle + crash restart handling
|