wild card UI done

This commit is contained in:
2024-08-06 17:52:00 -04:00
parent 84db245f4a
commit 41919db8f8
6 changed files with 149 additions and 69 deletions
+38 -35
View File
@@ -30,6 +30,7 @@ class Host extends Table{
'is_wildcard': {default: false, isRequired: false, type: 'boolean',},
'wildcard_status': {isRequired: false, type: 'string', min: 3, max: 500, default: 'Requesting'},
'wildcard_parent': {isRequired: false, type: 'string', min: 3, max: 500},
'wildcard_expires': {isRequired: false, type: 'number'},
}
static lookUpObj = {};
@@ -62,7 +63,7 @@ class Host extends Table{
parent: parentOBJ.host
});
}catch(error){
console.error('add cahce error', {...parentOBJ, host, is_cache: true}, error)
console.error('add cache error', {...parentOBJ, host, is_cache: true}, error)
throw error;
}
}
@@ -103,16 +104,18 @@ class Host extends Table{
try{
let host = this;
await host.update({
wildcard_status: 'Requesting',
});
let cert = await letsEncrypt.dnsWildcard(this.host, {
challengeCreateFn: async (authz, challenge, keyAuthorization) => {
host.update({
wildcard_status: `Adding record for ${authz.identifier.value}`
await host.update({
wildcard_status: `Adding record`
});
try{
let parts = tldExtract(authz.identifier.value);
console.log('adding DNS record for', `_acme-challenge${parts.sub ? `.${parts.sub}` : ''}`)
let res = await porkBun.createRecordForce(
parts.domain,
{
@@ -121,56 +124,55 @@ class Host extends Table{
content: `${keyAuthorization}`
}
);
console.log('porkbun res', res)
}catch(error){
console.log('model Host challengeCreateFn error:', error)
host.update({
await host.update({
wildcard_status: `Add DNS record failed`
});
}
},
onDnsCheck: async(authz, checkCount)=>{
host.update({
wildcard_status: `${checkCount} Checking DNS for ${authz.identifier.value}`
await host.update({
wildcard_status: `${checkCount} Checking DNS`
});
},
onDnsCheckFail: async(authz, error)=>{
host.update({
await host.update({
wildcard_status: `DNS check failed for ${authz.identifier.value}`
});
},
onDnsCheckFound: async(authz)=>{
host.update({
wildcard_status: `DNS check found for ${authz.identifier.value}`
await host.update({
wildcard_status: `DNS check found`
});
},
onDnsCheckSuccess: async(authz)=>{
host.update({
wildcard_status: `DNS check success for ${authz.identifier.value}`
await host.update({
wildcard_status: `DNS check success`
})
},
onDnsCheckRemove: async(authz)=>{
host.update({
wildcard_status: `DNS remove record for ${authz.identifier.value}`
await host.update({
wildcard_status: `DNS remove record`
})
},
challengeRemoveFn: async (authz, challenge, keyAuthorization)=>{
host.update({
wildcard_status: `DNS remove record for ${authz.identifier.value}`
await host.update({
wildcard_status: `DNS remove record`
})
try{
// let parts = tldExtract(authz.identifier.value);
// await porkBun.deleteRecords(
// parts.domain,
// {
// type:'TXT',
// name: `_acme-challenge${parts.sub ? `.${parts.sub}` : ''
// }`,
// content: `${keyAuthorization}`}
// );
let parts = tldExtract(authz.identifier.value);
await porkBun.deleteRecords(
parts.domain,
{
type:'TXT',
name: `_acme-challenge${parts.sub ? `.${parts.sub}` : ''
}`,
content: `${keyAuthorization}`}
);
}catch(error){
host.update({
await host.update({
wildcard_status: `DNS remove record failed for ${authz.identifier.value}`
})
}
@@ -188,8 +190,9 @@ class Host extends Table{
await this.constructor.redisClient.SET(`${this.host}:latest`, JSON.stringify(toAdd));
this.update({
wildcard_status: `Done`
await this.update({
wildcard_status: `Done`,
wildcard_expires: toAdd.real_expiry*1000,
});
return this;
@@ -204,8 +207,8 @@ class Host extends Table{
async update(...args){
try{
let out = await super.update(...args)
await this.bustCache(this.host)
await Host.buildLookUpObj()
await this.bustCache(this.host);
await Host.buildLookUpObj();
return out;
} catch(error){
@@ -215,9 +218,9 @@ class Host extends Table{
async remove(...args){
try{
let out = await super.remove(...args)
await Host.buildLookUpObj()
await this.bustCache(this.host)
let out = await super.remove(...args);
await Host.buildLookUpObj();
await this.bustCache(this.host);
return out;
} catch(error){
+8 -1
View File
@@ -26,10 +26,17 @@ app.host = (function(app){
function remove(args, callack){
app.api.delete('host/'+ args.host, function(error, data){
callack(error, data);
})
});
}
function getCert(args, callack){
app.api.get('cert/'+args.host, function(error, data){
callack(error, data);
});
}
return {
getCert,
list: list,
get: get,
add: add,
+16
View File
@@ -311,7 +311,23 @@ app.util = (function(app){
return obj;
};
function downloadFile(filename, text){
// https://stackoverflow.com/a/18197341
var element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
element.setAttribute('download', filename);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
return {
downloadFile: downloadFile,
getUrlParameter: getUrlParameter,
actionMessage: actionMessage
}
+22 -9
View File
@@ -29,7 +29,19 @@ router.post('/', async function(req, res, next){
}
});
router.get('/:item(*)', async function(req, res, next){
router.get('/lookup/:item', async function(req, res, next){
try{
return res.json({
string: req.params.item,
results: await Model.lookUp(req.params.item),
});
}catch(error){
return next(error);
}
});
router.get('/:item', async function(req, res, next){
try{
return res.json({
@@ -41,7 +53,7 @@ router.get('/:item(*)', async function(req, res, next){
}
});
router.put('/:item(*)', async function(req, res, next){
router.put('/:item', async function(req, res, next){
try{
req.body.updated_by = req.user.username;
let item = await Model.get(req.params.item);
@@ -59,7 +71,7 @@ router.put('/:item(*)', async function(req, res, next){
}
});
router.delete('/:item(*)', async function(req, res, next){
router.delete('/:item', async function(req, res, next){
try{
let item = await Model.get(req.params.item);
let count = await item.remove();
@@ -74,15 +86,16 @@ router.delete('/:item(*)', async function(req, res, next){
}
});
router.get('/lookup/:item(*)', async function(req, res, next){
router.put('/:item/renew', async function(req, res, next){
try{
return res.json({
string: req.params.item,
results: await Model.lookUp(req.params.item),
});
let item = await Model.get(req.params.item);
item.createWildcardCert();
return res.json({
message: `Requesting wildcard cert for ${req.params.item}`,
})
}catch(error){
return next(error);
next(error);
}
});
+5 -14
View File
@@ -7,10 +7,6 @@ const sleep = require('./sleep');
// https://dns.google/resolve?name=${name}&type=TXT
AcmeClient.setLogger((message) => {
console.log('ACME:', message);
});
class LetsEncrypt{
static AcmeClient = AcmeClient;
@@ -64,15 +60,12 @@ class LetsEncrypt{
skipChallengeVerification: true,
challengeCreateFn: async (authz, challenge, keyAuthorization) => {
try{
console.log('challenge', challenge)
console.log(`start TXT record key=_acme-challenge.${authz.identifier.value} value=${keyAuthorization} challenge=${challenge} googleDNS=https://dns.google/resolve?name=_acme-challenge.${authz.identifier.value}&type=TXT`)
dnsToAdd++
dnsToAdd++;
let resCheck = await axios.get(`https://dns.google/resolve?name=_acme-challenge.${authz.identifier.value}&type=TXT`);
if(resCheck.data.Answer && resCheck.data.Answer.some(record => record.data === keyAuthorization)){
await sleep(1000);
dnsFound++
dnsFound++;
if(dnsFound === dnsToAdd){
options.onDnsCheckFound(authz, dnsFound)
options.onDnsCheckFound(authz, dnsFound);
}
return;
}
@@ -83,13 +76,11 @@ class LetsEncrypt{
while(true){
options.onDnsCheck(authz, checkCount);
let res = await axios.get(`https://dns.google/resolve?name=_acme-challenge.${authz.identifier.value}&type=TXT`);
// console.log(keyAuthorization, res.data);
if(res.data.Answer && res.data.Answer.some(record => record.data === keyAuthorization)){
dnsFound++
dnsFound++;
if(dnsFound === dnsToAdd){
options.onDnsCheckFound(authz, dnsFound)
options.onDnsCheckFound(authz, dnsFound);
}
// console.log(`found record for key=_acme-challenge.${authz.identifier.value} value=${keyAuthorization}`)
await sleep(10000);
break;
}
+60 -10
View File
@@ -43,9 +43,21 @@
function parseHostRow(host) {
host['updated_on_text'] = moment(host['updated_on'], "x").fromNow();
host['wildcard_expires_text'] = moment(host['wildcard_expires'], "x").fromNow();
host['targetssl_text'] = host['targetssl'] ? 'https://' : 'http://';
host['forcessl_text'] = host['forcessl'] ? 'https://' : 'http://';
host['wildcard_text'] = host['is_wildcard'] ? host['wildcard_status'] : 'Auto';
host['wildcard_text_bg'] = 'warning';
if(!host['is_wildcard']){
host['wildcard_text_bg'] = 'success';
}
if(host['wildcard_status'] == 'Done'){
host['wildcard_text_bg'] = 'success';
host['wildcard_text'] = undefined;
}
if(host['wildcard_status'].includes('failed')){
host['wildcard_text_bg'] = 'danger';
}
return host;
}
@@ -94,6 +106,13 @@
});
}
function downloadCert(host, type){
app.host.getCert({host}, function(error, data){
if(error) app.util.actionMessage(error.message, $.scope.hosts.$this, 'danger');
app.util.downloadFile(`${host}-${type}.crt`, data[type])
});
}
$(document).ready(function(){
$editHostForm = $('#addHost').clone();
$editHostForm.find('hr.buttonBreak').nextAll().remove();
@@ -121,9 +140,9 @@
$el.slideUp();
});
app.subscribe(/^model:Host/, function(data, topic){
console.log(topic, data);
});
// app.subscribe(/^model:Host/, function(data, topic){
// console.log(topic, data);
// });
app.subscribe(/^model:Host:create/, function(data, topic){
let [a,b, action, host] = topic.split(':');
@@ -350,7 +369,7 @@
<thead>
<th>
SSL
SSL Expire
</th>
<th>
Host Name
@@ -365,10 +384,14 @@
Actions
</th>
</thead>
<tbody id="hostsTable">
<tr action="api" jq-repeat="hosts" jq-repeat-index='host' style="display:none">
<td>
<td class="table-{{wildcard_text_bg}}">
{{{ wildcard_text }}}
{{#wildcard_expires}}
<span class="momentFromNow" data-date="{{ wildcard_expires }}" >{{wildcard_expires_text}}</span>
{{/wildcard_expires}}
</td>
<td>
<a target="_blank" href="{{ forcessl_text }}{{ host }}">
@@ -378,15 +401,42 @@
<td>
{{{ targetssl_text }}}{{ ip }}:{{ targetPort }}
</td>
<td class="hidden-xs momentFromNow" data-date="{{ updated_on }}">
<td class="hidden-xs momentFromNow" data-date="{{ updated_on }}" >
{{ updated_on_text }}
</td>
<td>
<div class="btn-group">
<!-- <button type="button" class="btn btn-info">
<i class="fa-brands fa-expeditedssl"></i>
Cert
</button> -->
<div class="btn-group" role="group">
<button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
<i class="fa-brands fa-expeditedssl"></i>
Certs
</button>
<ul class="dropdown-menu">
<li>
<button type="button" class="dropdown-item" onclick="downloadCert('{{host}}', 'cert_pem')">
<i class="fa-solid fa-certificate"></i>
Cert
<i class="fa-solid fa-file-arrow-down float-end"></i>
</button>
</li>
<li>
<button type="button" class="dropdown-item" onclick="downloadCert('{{host}}', 'fullchain_pem')">
<i class="fa-solid fa-link"></i>
Full Chain
<i class="fa-solid fa-file-arrow-down float-end"></i>
</button>
</li>
<li>
<button type="button" class="dropdown-item" onclick="downloadCert('{{host}}', 'privkey_pem')">
<i class="fa-solid fa-key"></i>
Private key
<i class="fa-solid fa-file-arrow-down float-end"></i>
</button>
</li>
</ul>
</div>
<button type="button" onclick="editHost(this, '{{ host }}');" class="btn btn-sm btn-warning">
<i class="fa-solid fa-pencil"></i>
Edit