Skip to content

Commit 0d5d2b1

Browse files
authored
Merge pull request NginxProxyManager#4283 from badkeyy/feature/show-active-host-in-cert-list
SSL Certificates: Show if cert is in use on host
2 parents 3a01b2c + aedaaa1 commit 0d5d2b1

File tree

8 files changed

+83
-14
lines changed

8 files changed

+83
-14
lines changed

backend/internal/certificate.js

+6
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,9 @@ const internalCertificate = {
313313
.where('is_deleted', 0)
314314
.andWhere('id', data.id)
315315
.allowGraph('[owner]')
316+
.allowGraph('[proxy_hosts]')
317+
.allowGraph('[redirection_hosts]')
318+
.allowGraph('[dead_hosts]')
316319
.first();
317320

318321
if (access_data.permission_visibility !== 'all') {
@@ -464,6 +467,9 @@ const internalCertificate = {
464467
.where('is_deleted', 0)
465468
.groupBy('id')
466469
.allowGraph('[owner]')
470+
.allowGraph('[proxy_hosts]')
471+
.allowGraph('[redirection_hosts]')
472+
.allowGraph('[dead_hosts]')
467473
.orderBy('nice_name', 'ASC');
468474

469475
if (access_data.permission_visibility !== 'all') {

backend/models/certificate.js

+38-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
const db = require('../db');
55
const helpers = require('../lib/helpers');
66
const Model = require('objection').Model;
7-
const User = require('./user');
87
const now = require('./now_helper');
98

109
Model.knex(db);
@@ -68,6 +67,11 @@ class Certificate extends Model {
6867
}
6968

7069
static get relationMappings () {
70+
const ProxyHost = require('./proxy_host');
71+
const DeadHost = require('./dead_host');
72+
const User = require('./user');
73+
const RedirectionHost = require('./redirection_host');
74+
7175
return {
7276
owner: {
7377
relation: Model.HasOneRelation,
@@ -79,6 +83,39 @@ class Certificate extends Model {
7983
modify: function (qb) {
8084
qb.where('user.is_deleted', 0);
8185
}
86+
},
87+
proxy_hosts: {
88+
relation: Model.HasManyRelation,
89+
modelClass: ProxyHost,
90+
join: {
91+
from: 'certificate.id',
92+
to: 'proxy_host.certificate_id'
93+
},
94+
modify: function (qb) {
95+
qb.where('proxy_host.is_deleted', 0);
96+
}
97+
},
98+
dead_hosts: {
99+
relation: Model.HasManyRelation,
100+
modelClass: DeadHost,
101+
join: {
102+
from: 'certificate.id',
103+
to: 'dead_host.certificate_id'
104+
},
105+
modify: function (qb) {
106+
qb.where('dead_host.is_deleted', 0);
107+
}
108+
},
109+
redirection_hosts: {
110+
relation: Model.HasManyRelation,
111+
modelClass: RedirectionHost,
112+
join: {
113+
from: 'certificate.id',
114+
to: 'redirection_host.certificate_id'
115+
},
116+
modify: function (qb) {
117+
qb.where('redirection_host.is_deleted', 0);
118+
}
82119
}
83120
};
84121
}

frontend/js/app/dashboard/main.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,7 @@ module.exports = Mn.View.extend({
5050
onRender: function () {
5151
let view = this;
5252

53-
if (typeof view.stats.hosts === 'undefined') {
54-
Api.Reports.getHostStats()
53+
Api.Reports.getHostStats()
5554
.then(response => {
5655
if (!view.isDestroyed()) {
5756
view.stats.hosts = response;
@@ -61,7 +60,6 @@ module.exports = Mn.View.extend({
6160
.catch(err => {
6261
console.log(err);
6362
});
64-
}
6563
},
6664

6765
/**

frontend/js/app/nginx/certificates/list/item.ejs

+15-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@
3333
<td class="<%- isExpired() ? 'text-danger' : '' %>">
3434
<%- formatDbDate(expires_on, 'Do MMMM YYYY, h:mm a') %>
3535
</td>
36+
<td>
37+
<% if (active_domain_names().length > 0) { %>
38+
<span class="status-icon bg-success"></span> <%- i18n('certificates', 'in-use') %>
39+
<% } else { %>
40+
<span class="status-icon bg-danger"></span> <%- i18n('certificates', 'inactive') %>
41+
<% } %>
42+
</td>
3643
<% if (canManage) { %>
3744
<td class="text-right">
3845
<div class="item-action dropdown">
@@ -48,7 +55,14 @@
4855
<div class="dropdown-divider"></div>
4956
<% } %>
5057
<a href="#" class="delete dropdown-item"><i class="dropdown-icon fe fe-trash-2"></i> <%- i18n('str', 'delete') %></a>
58+
<% if (active_domain_names().length > 0) { %>
59+
<div class="dropdown-divider"></div>
60+
<span class="dropdown-header"><%- i18n('certificates', 'active-domain_names') %></span>
61+
<% active_domain_names().forEach(function(host) { %>
62+
<a href="https://<%- host %>" class="dropdown-item" target="_blank"><%- host %></a>
63+
<% }); %>
64+
<% } %>
5165
</div>
5266
</div>
5367
</td>
54-
<% } %>
68+
<% } %>

frontend/js/app/nginx/certificates/list/item.js

+16-6
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,24 @@ module.exports = Mn.View.extend({
4444
},
4545
},
4646

47-
templateContext: {
48-
canManage: App.Cache.User.canManage('certificates'),
49-
isExpired: function () {
50-
return moment(this.expires_on).isBefore(moment());
51-
},
52-
dns_providers: dns_providers
47+
templateContext: function () {
48+
return {
49+
canManage: App.Cache.User.canManage('certificates'),
50+
isExpired: function () {
51+
return moment(this.expires_on).isBefore(moment());
52+
},
53+
dns_providers: dns_providers,
54+
active_domain_names: function () {
55+
const { proxy_hosts = [], redirect_hosts = [], dead_hosts = [] } = this;
56+
return [...proxy_hosts, ...redirect_hosts, ...dead_hosts].reduce((acc, host) => {
57+
acc.push(...(host.domain_names || []));
58+
return acc;
59+
}, []);
60+
}
61+
};
5362
},
5463

64+
5565
initialize: function () {
5666
this.listenTo(this.model, 'change', this.render);
5767
}

frontend/js/app/nginx/certificates/list/main.ejs

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<th><%- i18n('str', 'name') %></th>
44
<th><%- i18n('all-hosts', 'cert-provider') %></th>
55
<th><%- i18n('str', 'expires') %></th>
6+
<th><%- i18n('str', 'status') %></th>
67
<% if (canManage) { %>
78
<th>&nbsp;</th>
89
<% } %>

frontend/js/app/nginx/certificates/main.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ module.exports = Mn.View.extend({
7474
e.preventDefault();
7575
let query = this.ui.query.val();
7676

77-
this.fetch(['owner'], query)
77+
this.fetch(['owner','proxy_hosts', 'dead_hosts', 'redirection_hosts'], query)
7878
.then(response => this.showData(response))
7979
.catch(err => {
8080
this.showError(err);
@@ -89,7 +89,7 @@ module.exports = Mn.View.extend({
8989
onRender: function () {
9090
let view = this;
9191

92-
view.fetch(['owner'])
92+
view.fetch(['owner','proxy_hosts', 'dead_hosts', 'redirection_hosts'])
9393
.then(response => {
9494
if (!view.isDestroyed()) {
9595
if (response && response.length) {

frontend/js/i18n/messages.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,10 @@
208208
"reachability-other": "There is a server found at this domain but it returned an unexpected status code {code}. Is it the NPM server? Please make sure your domain points to the IP where your NPM instance is running.",
209209
"download": "Download",
210210
"renew-title": "Renew Let's Encrypt Certificate",
211-
"search": "Search Certificate…"
211+
"search": "Search Certificate…",
212+
"in-use" : "In use",
213+
"inactive": "Inactive",
214+
"active-domain_names": "Active domain names"
212215
},
213216
"access-lists": {
214217
"title": "Access Lists",

0 commit comments

Comments
 (0)