diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json
index 6c963c6..785c99f 100644
--- a/nodejs/package-lock.json
+++ b/nodejs/package-lock.json
@@ -1,19 +1,19 @@
{
"name": "proxy-api",
- "version": "1.4.0",
+ "version": "1.5.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "proxy-api",
- "version": "1.4.0",
+ "version": "1.5.3",
"license": "MIT",
"dependencies": {
"@fortawesome/fontawesome-free": "^7.3.0",
"@popperjs/core": "^2.11.8",
"@simpleworkjs/app-stack": "^1.0.0",
"@simpleworkjs/conf": "^1.2.0",
- "@simpleworkjs/frontend": "^0.2.5",
+ "@simpleworkjs/frontend": "^0.2.6",
"@simpleworkjs/ldap": "^1.0.0",
"@simpleworkjs/oidc-client": "^1.0.0",
"acme-client": "^5.4.0",
@@ -310,9 +310,9 @@
}
},
"node_modules/@simpleworkjs/frontend": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/@simpleworkjs/frontend/-/frontend-0.2.5.tgz",
- "integrity": "sha512-PxR7UVPv3gRpdF0WsuAZplF1vYvKsEJQevVPhz9d72U+69vP/OH3tlaAXjtO/apMHfhT1viOPw2gMVOrPSxYZw==",
+ "version": "0.2.6",
+ "resolved": "https://registry.npmjs.org/@simpleworkjs/frontend/-/frontend-0.2.6.tgz",
+ "integrity": "sha512-2uqvEjxyZ2LE+sfhP6rJcEMmqdViazJ3ZkitWJXInPMWF6DiEZuP5MYqBqJvfDko63CCHEt1/ChFQd7Ry85Pzg==",
"license": "MIT",
"engines": {
"node": ">=18.0.0"
diff --git a/nodejs/package.json b/nodejs/package.json
index d09638c..d92ea6d 100644
--- a/nodejs/package.json
+++ b/nodejs/package.json
@@ -23,7 +23,7 @@
"@popperjs/core": "^2.11.8",
"@simpleworkjs/app-stack": "^1.0.0",
"@simpleworkjs/conf": "^1.2.0",
- "@simpleworkjs/frontend": "^0.2.5",
+ "@simpleworkjs/frontend": "^0.2.6",
"@simpleworkjs/ldap": "^1.0.0",
"@simpleworkjs/oidc-client": "^1.0.0",
"acme-client": "^5.4.0",
diff --git a/nodejs/routes/render.js b/nodejs/routes/render.js
index 8647e92..132fd64 100644
--- a/nodejs/routes/render.js
+++ b/nodejs/routes/render.js
@@ -40,6 +40,15 @@ router.get('/hosts', async function(req, res, next) {
res.render('hosts', {...values});
});
+// Linkable deep-link to a single host's modal, e.g. from the host modal's
+// app.modal `url` option. No server-side use of :host -- the client reads
+// location.pathname itself and opens the matching host's modal once the
+// page's own data has loaded (same idiom sso-manager-node uses for
+// /directory/:slug).
+router.get('/hosts/:host', async function(req, res, next) {
+ res.render('hosts', {...values});
+});
+
router.get('/dns', async function(req, res, next) {
res.render('dns', {...values});
});
diff --git a/nodejs/views/hosts.ejs b/nodejs/views/hosts.ejs
index df00c85..525d0b8 100755
--- a/nodejs/views/hosts.ejs
+++ b/nodejs/views/hosts.ejs
@@ -61,7 +61,7 @@
return host;
}
- function hostPopulate(){
+ function hostPopulate(onLoaded){
app.api.get('host?detail=1&provider=1', function(error, res){
if(error) return app.messages.action(error, $.scope.hosts.$this, 'danger');
@@ -75,6 +75,8 @@
$el.removeClass('table-success');
});
};
+
+ if (onLoaded) onLoaded();
});
}
@@ -90,10 +92,341 @@
// ----- Add / Edit modal --------------------------------------------------
- function hostModal(){
- return bootstrap.Modal.getOrCreateInstance(document.getElementById('hostModal'));
+ function hostModalClose(){ app.modal.close(); }
+
+ // Built once; injected fresh into app.modal's body on every open() call
+ // (unlike the old always-in-DOM static modal). .actionMessage lives
+ // INSIDE the form (not a sibling, as it was before) because formAJAX's
+ // error/success target resolves via $form.closest('div.card') (which
+ // app.modal's plain, card-less .modal-content never matches) falling
+ // back to $form.find('.actionMessage') -- a descendant search.
+ var hostModalBodyHtml = `
+
+ `;
+
+ function openHostModal(title, submitLabel, host){
+ app.modal.open({
+ title: title,
+ size: 'lg',
+ bodyHtml: hostModalBodyHtml,
+ footer: {
+ // created_on/updated_on come back as redis-hash strings, not
+ // numbers -- moment's bare fallback parser mishandles that
+ // (silently "Invalid date"), same reason hostParseRow above
+ // uses the explicit "x" (unix ms) format token.
+ metaHtml: host ? app.modal.formatAudit(host, {formatDate: function(ms){ return moment(ms, 'x').format('YYYY-MM-DD HH:mm'); }}) : '',
+ buttonsHtml: ''
+ + '