diff --git a/nodejs/app.js b/nodejs/app.js
index c278ff0..769dfd9 100755
--- a/nodejs/app.js
+++ b/nodejs/app.js
@@ -53,20 +53,28 @@ app.use('/api', require('./routes/api'));
// Catch 404 and forward to error handler. If none of the above routes are
// used, this is what will be called.
-app.use(function(req, res, next) {
- var err = new Error('Not Found');
- err.message = 'Page not found'
- err.status = 404;
- next(err);
+app.use(async function(req, res, next) {
+ try{
+ var err = new Error('Not Found');
+ err.message = 'Page not found'
+ err.status = 404;
+ next(err);
+ }catch(error){
+ console.log('app 404 catch error', error)
+ }
});
// Error handler. This is where `next()` will go on error
-app.use(function(err, req, res, next) {
- console.error(err.status || res.status, err.name, req.method, req.url);
- console.error(err.message);
- console.error(err.stack);
- console.error('=========================================');
+app.use(async function(err, req, res, next) {
+ try{
+ console.error(err.status || res.status, err.name, req.method, req.url);
+ console.error(err.message);
+ console.error(err.stack);
+ console.error('=========================================');
- res.status(err.status || 500);
- res.json({name: err.name, message: err.message});
+ res.status(err.status || 500);
+ res.json({name: err.name, message: err.message, keys: err.keys});
+ }catch(error){
+ console.log('error in the catch all error fn....', error);
+ }
});
diff --git a/nodejs/controller/auth.js b/nodejs/controller/auth.js
index f57b140..0749b98 100644
--- a/nodejs/controller/auth.js
+++ b/nodejs/controller/auth.js
@@ -45,12 +45,4 @@ class Auth{
}
}
-
-Auth.logOut = async function(data){
- try{
- }catch(error){
- throw error;
- }
-}
-
module.exports = {Auth};
diff --git a/nodejs/models/host.js b/nodejs/models/host.js
index 1c74eca..7ef9e95 100755
--- a/nodejs/models/host.js
+++ b/nodejs/models/host.js
@@ -28,6 +28,7 @@ class Host extends Table{
'targetssl': {isRequired: false, default: false, type: 'boolean'},
'is_cache': {default: false, isRequired: false, type: 'boolean',},
'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},
}
@@ -84,11 +85,11 @@ class Host extends Table{
}
}
- static async add(data, ...args){
+ static async create(data, ...args){
try{
- let out = await super.create(data, ...args)
- await this.buildLookUpObj()
- if(out.is_wildcard) await out.createWildcardCert()
+ let out = await super.create(data, ...args);
+ await this.buildLookUpObj();
+ if(out.is_wildcard) out.createWildcardCert();
return out;
@@ -100,31 +101,104 @@ class Host extends Table{
async createWildcardCert(){
if(!this.host.startsWith('*.')) throw new Error('not wild card');
- let cert = await letsEncrypt.dnsWildcard(this.host, {
- challengeCreateFn: async (authz, challenge, keyAuthorization) => {
- let parts = tldExtract(authz.identifier.value);
- let res = await porkBun.createRecordForce(parts.domain, {type:'TXT', name: `_acme-challenge${parts.sub ? `.${parts.sub}` : ''}`, content: `${keyAuthorization}`});
- },
- challengeRemoveFn: async (authz, challenge, keyAuthorization)=>{
- let parts = tldExtract(authz.identifier.value);
- await porkBun.deleteRecords(parts.domain, {type:'TXT', name: `_acme-challenge${parts.sub ? `.${parts.sub}` : ''}`, content: `${keyAuthorization}`});
- },
- });
+ try{
+ let host = this;
+ let cert = await letsEncrypt.dnsWildcard(this.host, {
+ challengeCreateFn: async (authz, challenge, keyAuthorization) => {
+ host.update({
+ wildcard_status: `Adding record for ${authz.identifier.value}`
+ });
+ try{
+ let parts = tldExtract(authz.identifier.value);
- let toAdd = {
- cert_pem: cert.cert.split('\n\n')[0],
- fullchain_pem: cert.cert,
- privkey_pem: cert.key.toString(),
- csr_pem: cert.csr.toString(),
- expiry: 4120307657,
- real_expiry: +LetsEncrypt.AcmeClient.crypto.readCertificateInfo(cert.cert).notAfter/1000,
+ console.log('adding DNS record for', `_acme-challenge${parts.sub ? `.${parts.sub}` : ''}`)
+
+ let res = await porkBun.createRecordForce(
+ parts.domain,
+ {
+ type:'TXT',
+ name: `_acme-challenge${parts.sub ? `.${parts.sub}` : ''}`,
+ content: `${keyAuthorization}`
+ }
+ );
+ console.log('porkbun res', res)
+ }catch(error){
+ console.log('model Host challengeCreateFn error:', error)
+ host.update({
+ wildcard_status: `Add DNS record failed`
+ });
+ }
+ },
+ onDnsCheck: async(authz, checkCount)=>{
+ host.update({
+ wildcard_status: `${checkCount} Checking DNS for ${authz.identifier.value}`
+ });
+ },
+ onDnsCheckFail: async(authz, error)=>{
+ 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}`
+ });
+ },
+ onDnsCheckSuccess: async(authz)=>{
+ host.update({
+ wildcard_status: `DNS check success for ${authz.identifier.value}`
+ })
+ },
+ onDnsCheckRemove: async(authz)=>{
+ host.update({
+ wildcard_status: `DNS remove record for ${authz.identifier.value}`
+ })
+ },
+ challengeRemoveFn: async (authz, challenge, keyAuthorization)=>{
+ host.update({
+ wildcard_status: `DNS remove record for ${authz.identifier.value}`
+ })
+ try{
+ // 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({
+ wildcard_status: `DNS remove record failed for ${authz.identifier.value}`
+ })
+ }
+ },
+ });
+
+ let toAdd = {
+ cert_pem: cert.cert.split('\n\n')[0],
+ fullchain_pem: cert.cert,
+ privkey_pem: cert.key.toString(),
+ csr_pem: cert.csr.toString(),
+ expiry: 4120307657,
+ real_expiry: +LetsEncrypt.AcmeClient.crypto.readCertificateInfo(cert.cert).notAfter/1000,
+ }
+
+
+ await this.constructor.redisClient.SET(`${this.host}:latest`, JSON.stringify(toAdd));
+ this.update({
+ wildcard_status: `Done`
+ });
+
+ return this;
+ }catch(error){
+ console.log('le failed', error)
+ this.update({
+ wildcard_status: `LE failed`
+ });
}
-
-
- await this.constructor.redisClient.SET(`${this.host}:latest`, JSON.stringify(toAdd));
- return toAdd;
-
- // Get new wildcard cert from
}
async update(...args){
diff --git a/nodejs/models/token.js b/nodejs/models/token.js
index 845e891..ee566a2 100644
--- a/nodejs/models/token.js
+++ b/nodejs/models/token.js
@@ -37,7 +37,7 @@ class AuthToken extends Token{
return await User.get(this.created_by);
}
- static async add(data){
+ static async create(data){
data.created_by = data.username;
return super.create(data)
diff --git a/nodejs/models/user_redis.js b/nodejs/models/user_redis.js
index afefecb..5373d8f 100644
--- a/nodejs/models/user_redis.js
+++ b/nodejs/models/user_redis.js
@@ -1,7 +1,6 @@
'use strict';
const Table = require('../utils/redis_model');
-// const {Token, InviteToken} = require('./token');
const bcrypt = require('bcrypt');
const saltRounds = 10;
@@ -19,9 +18,11 @@ class User extends Table{
static backing = 'redis'
- static async add(data) {
+ static async create(data) {
try{
+ console.log('hash?')
data['password'] = await bcrypt.hash(data['password'], saltRounds);
+ console.log('hashed password', data.password);
data['backing'] = data['backing'] || 'redis';
@@ -32,31 +33,6 @@ class User extends Table{
}
}
-/* static async addByInvite(data){
- try{
- let token = await InviteToken.get(data.token);
-
- if(!token.is_valid){
- let error = new Error('Token Invalid');
- error.name = 'Token Invalid';
- error.message = `Token is not valid or as allready been used. ${data.token}`;
- error.status = 401;
- throw error;
- }
-
- let user = await this.create(data);
-
- if(user){
- await token.consume({claimed_by: user.username});
- return user;
- }
-
- }catch(error){
- throw error;
- }
-
- };*/
-
async setPassword(data){
try{
data['password'] = await bcrypt.hash(data['password'], saltRounds);
@@ -67,17 +43,6 @@ class User extends Table{
}
}
-/* async invite(){
- try{
- let token = await InviteToken.create({created_by: this.username});
-
- return token;
-
- }catch(error){
- throw error;
- }
- }*/
-
static async login(data){
try{
@@ -109,7 +74,7 @@ module.exports = {User};
(async function(){
- var defaultUser = 'proxyadmin3'
+ var defaultUser = 'proxyadmin2'
try{
let user = await User.get(defaultUser);
}catch(error){
@@ -117,7 +82,7 @@ module.exports = {User};
let user = await User.create({
username:defaultUser,
password: defaultUser,
- created_by:defaultUser
+ created_by: defaultUser
});
console.log(defaultUser, 'created', user);
}catch(error){
diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json
index 1c6c0ed..f044463 100644
--- a/nodejs/package-lock.json
+++ b/nodejs/package-lock.json
@@ -15,7 +15,7 @@
"bcrypt": "^5.1.1",
"bootstrap": "^5.3.3",
"ejs": "^3.1.10",
- "express": "^4.19.2",
+ "express": "^4.18.2",
"extend": "^3.0.2",
"jquery": "^3.7.1",
"ldapts": "^2.12.0",
diff --git a/nodejs/package.json b/nodejs/package.json
index 709de97..fc7615b 100755
--- a/nodejs/package.json
+++ b/nodejs/package.json
@@ -18,7 +18,7 @@
"bcrypt": "^5.1.1",
"bootstrap": "^5.3.3",
"ejs": "^3.1.10",
- "express": "^4.19.2",
+ "express": "^4.18.2",
"extend": "^3.0.2",
"jquery": "^3.7.1",
"ldapts": "^2.12.0",
diff --git a/nodejs/public/lib/js/app-base.js b/nodejs/public/lib/js/app-base.js
index e061205..b50d647 100644
--- a/nodejs/public/lib/js/app-base.js
+++ b/nodejs/public/lib/js/app-base.js
@@ -252,10 +252,10 @@ app.user = (function(app){
app.util = (function(app){
function getUrlParameter(name){
- name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
- var regex = new RegExp('[\\?&]' + name + '=([^]*)');
- var results = regex.exec(location.search);
- return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
+ name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
+ var regex = new RegExp('[\\?&]' + name + '=([^]*)');
+ var results = regex.exec(location.search);
+ return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
};
function actionMessage(message, $target, type, callback){
@@ -277,28 +277,38 @@ app.util = (function(app){
})
}else{
if(type) $target.addClass('bg-' + type);
- message = '' + message + ''
+ message = '' + message + ''
$target.html(message).slideDown('fast');
}
setTimeout(callback,10)
}
$.fn.serializeObject = function(){
- var
- arr = $(this).serializeArray(),
- obj = {};
-
- for(var i = 0; i < arr.length; i++){
- if(obj[arr[i].name] === undefined) {
- obj[arr[i].name] = arr[i].value;
- } else {
- if(!(obj[arr[i].name] instanceof Array)) {
- obj[arr[i].name] = [obj[arr[i].name]];
- }
- obj[arr[i].name].push(arr[i].value);
- }
- }
- return obj;
+ var
+ arr = $(this).serializeArray(),
+ obj = {};
+
+ for(var i = 0; i < arr.length; i++){
+ if(obj[arr[i].name] === undefined) {
+ if(!arr[i].value) continue;
+ obj[arr[i].name] = arr[i].value;
+ let type = $(this).parent().find(`[name="${arr[i].name}"]`).attr('type');
+ if(['number', 'range'].includes(type)){
+ obj[arr[i].name] = Number(arr[i].value);
+ }
+
+ if(['radio'].includes(type) && ['true', 'false'].includes(arr[i].value)){
+ obj[arr[i].name] = arr[i].value == 'true' ? true : false;
+ }
+ } else {
+ if(!(obj[arr[i].name] instanceof Array)) {
+ obj[arr[i].name] = [obj[arr[i].name]];
+ }
+ obj[arr[i].name].push(arr[i].value);
+ }
+
+ }
+ return obj;
};
return {
@@ -342,12 +352,12 @@ function formAJAX(btn, del){
event.preventDefault(); // avoid to execute the actual submit of the form.
var $form = $(btn).closest('[action]'); // gets the 'form' parent
var formData = $form.find('[name]').serializeObject(); // builds query formDataing
- var method = $form.attr('method') || 'post';
+ var method = ($form.attr('method') || 'post').toLowerCase();
- // if( !$form.validate()){
- // app.util.actionMessage('Please fix the form errors.', $form, 'danger')
- // return false;
- // }
+ if($form.validate && !$form.validate()){
+ app.util.actionMessage('Please fix the form errors.', $form, 'danger')
+ return false;
+ }
app.util.actionMessage(
'
Loading...
',
@@ -357,9 +367,21 @@ function formAJAX(btn, del){
app.api[method]($form.attr('action'), formData, function(error, data){
app.util.actionMessage(data.message, $form, error ? 'danger' : 'success'); //re-populate table
+ $form.validateClear();
if(!error){
$form.trigger("reset");
eval($form.attr('evalAJAX')); //gets JS to run after completion
+ }else{
+ console.log('formAJAX res error', error, data)
+ if(data && data.name === 'ObjectValidateError'){
+ app.util.actionMessage('Please fix the form errors', $form, 'danger'); //re-populate table
+ }
+ if(data && data.keys){
+ console.log('form key errors', data.keys)
+ for(let keyError of data.keys){
+ $form.find(`[name=${keyError.key}]`).validateMessage(keyError.message);
+ }
+ }
}
});
}
diff --git a/nodejs/public/lib/js/jq-repeat_new.js b/nodejs/public/lib/js/jq-repeat_new.js
index 7834eaa..fb8e16e 100644
--- a/nodejs/public/lib/js/jq-repeat_new.js
+++ b/nodejs/public/lib/js/jq-repeat_new.js
@@ -10,7 +10,7 @@ MIT license
$.scope = {};
}
- var make = function( element ){
+ var make = function(element){
var result = [];
result.splice = function(inputValue, ...args){
@@ -20,9 +20,9 @@ MIT license
var index;
//if a string is submitted as the index, try to match it to index number
- if( typeof arguments[0] === 'string' ){
+ if(typeof arguments[0] === 'string'){
index = this.indexOf( arguments[0] );//set where to start
- if ( index === -1 ) {
+ if (index === -1) {
return [];
}
}else{
@@ -109,6 +109,7 @@ MIT license
//set and return new array
return Array.prototype.splice.apply(this, toProto);
};
+
result.push = function(){
//add one or more objects to the array
@@ -123,19 +124,25 @@ MIT license
//return new array length
return this.length;
};
+
result.pop = function(){
//remove and return array element
return this.splice( -1, 1 )[0];
};
- result.reverse = function() {
- var temp = this.splice( 0 );
- Array.prototype.reverse.apply( temp );
- for( var i = 0; i < temp.length; i++ ){
- this.push( temp[i] );
+ result.reverse = function() {
+ let hold = [];
+ for(let item of this){
+ hold.push(item.__jq_$el.html())
}
+ for(let idx in hold.reverse()){
+ this[idx].__jq_$el.html(hold[idx]);
+ }
+
+ Array.prototype.reverse.apply( this );
+
return this;
};
@@ -149,6 +156,10 @@ MIT license
return this.splice( 0, 1 )[0];
};
+ result.unshift = function(data){
+ return this.splice(0,0, data)
+ }
+
result.loop = function(){
var temp = this[0];
this.splice( 0,1 );
@@ -156,13 +167,17 @@ MIT license
return temp;
};
+
result.loopUp = function(){
var temp = this[this.length-1];
this.splice( -1, 1 );
this.splice( 0, 0, temp );
return temp;
};
+
result.indexOf = function( key, value ){
+ if(typeof key === 'number') return key;
+
if( typeof value !== 'string' ){
value = arguments[0];
key = this.__index;
@@ -175,7 +190,8 @@ MIT license
}
return -1;
};
- result.update = function( key, value, update ){
+
+ result.update = function(key, value, data){
//set variables using sting for index
// If update is called with no index/key, assume its the 0
@@ -186,24 +202,31 @@ MIT license
return this.splice(0, 1, key);
}
- if( typeof value !== 'string' ){
- update = arguments[1];
- value = arguments[0];
- key = this.__index;
+ if(typeof value !== 'string'){
+ data = arguments[1];
+ if(typeof key !== 'number'){
+ value = arguments[0];
+ key = this.__index;
+ }
}
+
var index = this.indexOf( key, value );
+
if(index === -1) {
return [];
}
- var object = $.extend( true, {}, this[index], update );
-
- var $render = $(Mustache.render(this.__jqTemplate, object));
- $render.attr('jq-repeat-index', index);
- this[index].__jq_$el.replaceWith($render);
+ this[index] = $.extend( true, this[index], data );
+ var $render = $(Mustache.render(this.__jqTemplate, this[index]));
+ $render.attr('jq-repeat-index', index);
+
+ this.__update(this[index].__jq_$el, $render, this[index], this);
this[index].__jq_$el = $render;
- this.__update(this[index].__jq_$el, this[index], this);
};
+
+ result.getByKey = function(key, value){
+ return this[this.indexOf(key, value)];
+ }
result.__put = function($el, item, list){
$el.show();
@@ -213,8 +236,8 @@ MIT license
$el.remove();
};
- result.__update = function($el, item, list){
- console.log('here', $el)
+ result.__update = function($el, $render, item, list){
+ $el.replaceWith($render);
$el.show();
};
diff --git a/nodejs/public/lib/js/val.js b/nodejs/public/lib/js/val.js
index d5a75bc..1c340ee 100755
--- a/nodejs/public/lib/js/val.js
+++ b/nodejs/public/lib/js/val.js
@@ -1,96 +1,79 @@
( function( $ ) {
var settings = {
rule: {
- eq: function( value, options ) {
- var compare = $( '[name=' + options + ']' ).val();
+ eq: function(value, options){
+ var compare = $('[name=' + options + ']').val();
if ( value != compare ) {
return "Miss-match";
}
}
},
-
- form: {
- alertCount: false, //pop-up with error count
- alertCountMessage: " errors!"
- },
-
- processValidation: function ( error_message, $input ) {
- if ( typeof error_message == 'undefined' || error_message == true ) {
- return;
- }
-
- $( '' ).html( ' - ' + error_message ).appendTo( $input.siblings( 'label' ) );
- $input.parent().addClass("has-error");
- failedCount++;
- return false;
- }
};
- var failedCount = 0;
+ $.fn.validate = function(event) {
+ // let thisSettings = $.extend(true, settings, settingsObj);
+ let hasErrors = false;
- function processRule( thisSettings, $input ) {
- var attr = $input.attr( 'validate' ).split( ':' ), //array of params
- requirement = attr[1],
- value = $input.val(), //link to input value
- rule = attr[0];
+ if(this.is('[validate]')) return this.validateField(event);
- $input.siblings( 'label' ).children( 'b' ).remove(); //removes old error
- $input.parent().removeClass( "has-error" ); //removes has-error class
+ if(!this.attr('isValid')){
+ console.log('adding reset event')
+ this.on('reset', function(){
+ $(this).attr('isValid', false);
+ $(this).validateClear();
+ })
+ }
+
+ this.find('[validate]').each(function(){
+ if(!$(this).validateField()) hasErrors = true;
+ });
+
+ this.attr('isValid', !hasErrors);
+
+ if(hasErrors && event) event.preventDefault();
+
+ return !hasErrors;
+ };
+
+ $.fn.validateClear = function(){
+ $(this).find('input').each(function(){
+ $(this).removeClass('is-invalid');
+ $(this).removeClass('is-valid');
+ })
+ }
+
+ $.fn.validateField = function(){
+ var attr = this.attr('validate').split(':'); //array of params
+ var rule = attr[0];
+ var options = attr[1];
+ var value = this.val(); //link to input value
+ var message;
//checks if field is required, and length
- if (isNaN(requirement) === false && requirement && value.length < requirement) {
- return thisSettings.processValidation( 'Must be ' + requirement + ' characters', $input );
+ if(!isNaN(options) && value.length < options){
+ message = `Must be ${options} characters`;
}
//checks if empty to stop processing
- if ( isNaN( requirement ) === false && value.length === 0 ) {
- return;
+ if(!isNaN(options) && value.length === 0) {
+ }else if(rule in settings.rule){
+ let message = settings.rule[rule].apply(this, [value, options]);
}
- if ( rule in thisSettings.rule ) {
- return thisSettings.processValidation( thisSettings.rule[rule].apply( this, [value, requirement] ), $input );
- }
+ this.validateMessage(message)
+ return !message;
}
-
- $.fn.validate = function( settingsObj, event ) {
- event = event || window.event;
-
- failedCount = 0;
- var thisForm = false,
- thisSettings = $.extend( true, settings, settingsObj );
- if ( this.is( '[validate]' ) ) {
- processRule( thisSettings, this );
- } else {
- thisForm = true;
- this.find( '[validate]' ).each( function () {
- if(!processRule( thisSettings, $( this ) )){
- // failedCount++;
- }
- });
- }
-
- this.attr('isValid', !failedCount);
- if ( failedCount === 0 ) { //no errors
- return true;
- } else { //errors
- if ( thisForm ){
- if(thisSettings.form.alertCount){
- alert( failedCount + thisSettings.form.alertCountMessage );
- }
- /* if(event) event.returnValue = false;
- if(event) event.preventDefault();
- return false;
-
- if(event.preventDefault) if(event)*/
- //event.returnValue = false;
- event.preventDefault();
- event.defaultPrevented;
-
- }
- return false;
+ $.fn.validateMessage = function(message){
+ if(message && message !== true){
+ this.closest('.form-group').find('b.invalid-feedback').html(message);
+ this.addClass('is-invalid');
+ }else{
+ this.removeClass('is-invalid');
+ this.addClass('is-valid');
}
+ return this;
};
jQuery.extend({
diff --git a/nodejs/routes/auth.js b/nodejs/routes/auth.js
index d651cb8..be6e7f8 100755
--- a/nodejs/routes/auth.js
+++ b/nodejs/routes/auth.js
@@ -3,6 +3,7 @@
const router = require('express').Router();
const { Auth } = require('../controller/auth');
+
router.post('/login', async function(req, res, next){
try{
let auth = await Auth.login(req.body);
diff --git a/nodejs/routes/host.js b/nodejs/routes/host.js
index 6b9b8e5..2ab5430 100755
--- a/nodejs/routes/host.js
+++ b/nodejs/routes/host.js
@@ -25,7 +25,7 @@ router.post('/', async function(req, res, next){
...item,
});
} catch (error){
- return next(error);
+ next(error);
}
});
diff --git a/nodejs/routes/render.js b/nodejs/routes/render.js
index c5d47d4..1a8a0f7 100644
--- a/nodejs/routes/render.js
+++ b/nodejs/routes/render.js
@@ -40,4 +40,7 @@ router.get('/login', async function(req, res, next) {
res.render('login', {...values, redirect: req.query.redirect});
});
+router.get('/test', async function(req, res, next) {
+ res.render('test', {...values, redirect: req.query.redirect});
+});
module.exports = router;
diff --git a/nodejs/utils/letsencrypt.js b/nodejs/utils/letsencrypt.js
index a1141f3..8ea66f4 100644
--- a/nodejs/utils/letsencrypt.js
+++ b/nodejs/utils/letsencrypt.js
@@ -7,6 +7,11 @@ const sleep = require('./sleep');
// https://dns.google/resolve?name=${name}&type=TXT
+AcmeClient.setLogger((message) => {
+ console.log('ACME:', message);
+});
+
+
class LetsEncrypt{
static AcmeClient = AcmeClient;
@@ -43,32 +48,57 @@ class LetsEncrypt{
try{
domain = domain.replace(/^\*\./, '');
+
const [key, csr] = await AcmeClient.crypto.createCsr({
altNames: [domain, `*.${domain}`],
});
+ let dnsToAdd = 0;
+ let dnsFound = 0;
+
const cert = await this.client.auto({
csr,
email: 'wmantly@gmail.com',
termsOfServiceAgreed: true,
challengePriority: ['dns-01'],
+ skipChallengeVerification: true,
challengeCreateFn: async (authz, challenge, keyAuthorization) => {
- // console.log(`start TXT record key=_acme-challenge.${authz.identifier.value} value=${keyAuthorization}`)
-
- let resCheck = await axios.get(`https://dns.google/resolve?name=_acme-challenge.${authz.identifier.value}&type=TXT`);
- if(resCheck.data.Answer.some(record => record.data === keyAuthorization)) return;
-
- await options.challengeCreateFn(authz, challenge, keyAuthorization);
-
- let checkCount = 0;
- while(true){
- await sleep(1500);
- let res = await axios.get(`https://dns.google/resolve?name=_acme-challenge.${authz.identifier.value}&type=TXT`);
- if(res.data.Answer.some(record => record.data === keyAuthorization)){
- // console.log(`found record for key=_acme-challenge.${authz.identifier.value} value=${keyAuthorization}`)
- break;
+ 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++
+ 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++
+ if(dnsFound === dnsToAdd){
+ options.onDnsCheckFound(authz, dnsFound)
+ }
+ return;
}
- if(checkCount++ > 60) throw new Error('challengeCreateFn validation timed out');
+
+ await options.challengeCreateFn(authz, challenge, keyAuthorization);
+
+ let checkCount = 0;
+ 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++
+ if(dnsFound === dnsToAdd){
+ options.onDnsCheckFound(authz, dnsFound)
+ }
+ // console.log(`found record for key=_acme-challenge.${authz.identifier.value} value=${keyAuthorization}`)
+ await sleep(10000);
+ break;
+ }
+ if(checkCount++ > 60) throw new Error('challengeCreateFn validation timed out');
+ await sleep(1500);
+ }
+ }catch(error){
+ console.log('dns check failed error:', error)
+ options.onDnsCheckFail(authz, error)
}
},
challengeRemoveFn: options.challengeRemoveFn,
diff --git a/nodejs/utils/model_pubsub.js b/nodejs/utils/model_pubsub.js
index aac4056..924fb4f 100644
--- a/nodejs/utils/model_pubsub.js
+++ b/nodejs/utils/model_pubsub.js
@@ -12,9 +12,13 @@ function ModelPs(model){
}
function publish(prop, res, req){
- if(!['add', 'create', 'update', 'remove'].includes(prop)) return;
+ try{
+ if(!['add', 'create', 'update', 'remove'].includes(prop)) return;
- ps.publish(`model:${Model.name}:${prop}:${getIndex(res, req)}`, res);
+ ps.publish(`model:${Model.name}:${prop}:${getIndex(res, req)}`, res);
+ }catch(error){
+ console.log('ModelPs.publish ERROR', error)
+ }
}
return new Proxy(model, {
@@ -28,16 +32,22 @@ function ModelPs(model){
const targetValue = Reflect.get(target, propKey, receiver);
if (typeof targetValue === 'function') {
return function(...args){
+ try{
// let res = targetValue.apply(this, args); // (A)
- let res = Reflect.apply(targetValue, this, args);
- if(targetValue.constructor.name === 'AsyncFunction'){
- res.then(function(res){
- publish(propKey, res, ...args);
- });
- }else{
- publish(propKey, res, ...args)
+ var res = Reflect.apply(targetValue, this, args);
+ if(targetValue.constructor.name === 'AsyncFunction'){
+ res.then(function(res){
+ publish(propKey, res, ...args);
+ }).catch(function(error){
+ console.log('toDo, publish errors...')
+ });
+ }else{
+ publish(propKey, res, ...args)
+ }
+ return res;
+ }catch(error){
+ console.log("grrrr")
}
- return res;
}
} else {
return targetValue;
diff --git a/nodejs/utils/object_validate.js b/nodejs/utils/object_validate.js
index 85ac9d0..35c3d21 100644
--- a/nodejs/utils/object_validate.js
+++ b/nodejs/utils/object_validate.js
@@ -33,6 +33,8 @@ function processKeys(map, data, partial){
continue;
}
+ // console.log(key, data[key], map[key].default, data.hasOwnProperty(key) && data[key] !== undefined ? data[key] : returnOrCall(map[key].default))
+
out[key] = data.hasOwnProperty(key) && data[key] !== undefined ? data[key] : returnOrCall(map[key].default);
if(data.hasOwnProperty(key) && process_type[map[key].type]){
@@ -46,7 +48,6 @@ function processKeys(map, data, partial){
}
if(errors.length !== 0){
- console.log('errors', errors)
throw new ObjectValidateError(errors);
return {__errors__: errors};
}
@@ -79,9 +80,10 @@ function parseToString(data){
return (types[typeof(data)] || String)(data);
}
-function ObjectValidateError(message) {
+function ObjectValidateError(message){
this.name = 'ObjectValidateError';
this.message = (message || {});
+ this.keys = (message || {})
this.status = 422;
}
diff --git a/nodejs/utils/redis_model.js b/nodejs/utils/redis_model.js
index d540e5f..a99cb85 100644
--- a/nodejs/utils/redis_model.js
+++ b/nodejs/utils/redis_model.js
@@ -47,17 +47,17 @@ class Table{
}catch(error){
throw error;
}
-
}
static async exists(index){
- try{
- await this.get(data);
-
- return true;
- }catch(error){
- return false;
+ if(typeof index === 'object'){
+ index = index[this._key];
}
+
+ return await client.SISMEMBER(
+ redisPrefix(this.prototype.constructor.name),
+ index
+ );
}
static async list(){
@@ -86,6 +86,7 @@ class Table{
static async create(data){
// Add a entry to this redis table.
try{
+
// Validate the passed data by the keyMap schema.
data = objValidate.processKeys(this._keyMap, data);
@@ -94,6 +95,10 @@ class Table{
let error = new Error('EntryNameUsed');
error.name = 'EntryNameUsed';
error.message = `${this.prototype.constructor.name}:${data[this._key]} already exists`;
+ error.keys = [{
+ key: this._key,
+ message: `${this.prototype.constructor.name}:${data[this._key]} already exists`
+ }]
error.status = 409;
throw error;
@@ -107,7 +112,7 @@ class Table{
// Add the values for this entry.
for(let key of Object.keys(data)){
- if(!data[key]) continue;
+ if(data[key] === undefined) continue;
await client.HSET(
redisPrefix(`${this.prototype.constructor.name}_${data[this._key]}`),
key,
@@ -125,9 +130,26 @@ class Table{
async update(data, key){
// Update an existing entry.
try{
+ // Validate the passed data, ignoring required fields.
+ data = objValidate.processKeys(this.constructor._keyMap, data, true);
+
// Check to see if entry name changed.
if(data[this.constructor._key] && data[this.constructor._key] !== this[this.constructor._key]){
// Remove the index key from the tables members list.
+
+ if(data[this.constructor._key] && await this.constructor.exists(data)){
+ let error = new Error('EntryNameUsed');
+ error.name = 'EntryNameUsed';
+ error.message = `${this.constructor.name}:${data[this.constructor._key]} already exists`;
+ error.keys = [{
+ key: this.constructor._key,
+ message: `${this.constructor.name}:${data[this.constructor._key]} already exists`
+ }]
+ error.status = 409;
+
+ throw error;
+ }
+
await client.SREM(
redisPrefix(this.constructor.name),
this[this.constructor._key]
@@ -139,12 +161,14 @@ class Table{
data[this.constructor._key]
);
+ await client.RENAME(
+ redisPrefix(`${this.constructor.name}_${this[this.constructor._key]}`),
+ redisPrefix(`${this.constructor.name}_${data[this.constructor._key]}`),
+ );
+
}
// Update what ever fields that where passed.
- // Validate the passed data, ignoring required fields.
- data = objValidate.processKeys(this.constructor._keyMap, data, true);
-
// Loop over the data fields and apply them to redis
for(let key of Object.keys(data)){
this[key] = data[key];
diff --git a/nodejs/views/hosts.ejs b/nodejs/views/hosts.ejs
index bab83ff..00634c8 100755
--- a/nodejs/views/hosts.ejs
+++ b/nodejs/views/hosts.ejs
@@ -5,7 +5,7 @@
@@ -195,7 +174,7 @@
-
+
@@ -204,6 +183,7 @@
+ Edit {{ host }}
@@ -212,8 +192,8 @@
-
-