fixed issue with redis models
This commit is contained in:
+20
-12
@@ -53,20 +53,28 @@ app.use('/api', require('./routes/api'));
|
|||||||
|
|
||||||
// Catch 404 and forward to error handler. If none of the above routes are
|
// Catch 404 and forward to error handler. If none of the above routes are
|
||||||
// used, this is what will be called.
|
// used, this is what will be called.
|
||||||
app.use(function(req, res, next) {
|
app.use(async function(req, res, next) {
|
||||||
var err = new Error('Not Found');
|
try{
|
||||||
err.message = 'Page not found'
|
var err = new Error('Not Found');
|
||||||
err.status = 404;
|
err.message = 'Page not found'
|
||||||
next(err);
|
err.status = 404;
|
||||||
|
next(err);
|
||||||
|
}catch(error){
|
||||||
|
console.log('app 404 catch error', error)
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Error handler. This is where `next()` will go on error
|
// Error handler. This is where `next()` will go on error
|
||||||
app.use(function(err, req, res, next) {
|
app.use(async function(err, req, res, next) {
|
||||||
console.error(err.status || res.status, err.name, req.method, req.url);
|
try{
|
||||||
console.error(err.message);
|
console.error(err.status || res.status, err.name, req.method, req.url);
|
||||||
console.error(err.stack);
|
console.error(err.message);
|
||||||
console.error('=========================================');
|
console.error(err.stack);
|
||||||
|
console.error('=========================================');
|
||||||
|
|
||||||
res.status(err.status || 500);
|
res.status(err.status || 500);
|
||||||
res.json({name: err.name, message: err.message});
|
res.json({name: err.name, message: err.message, keys: err.keys});
|
||||||
|
}catch(error){
|
||||||
|
console.log('error in the catch all error fn....', error);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -45,12 +45,4 @@ class Auth{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Auth.logOut = async function(data){
|
|
||||||
try{
|
|
||||||
}catch(error){
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {Auth};
|
module.exports = {Auth};
|
||||||
|
|||||||
+101
-27
@@ -28,6 +28,7 @@ class Host extends Table{
|
|||||||
'targetssl': {isRequired: false, default: false, type: 'boolean'},
|
'targetssl': {isRequired: false, default: false, type: 'boolean'},
|
||||||
'is_cache': {default: false, isRequired: false, type: 'boolean',},
|
'is_cache': {default: false, isRequired: false, type: 'boolean',},
|
||||||
'is_wildcard': {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},
|
'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{
|
try{
|
||||||
let out = await super.create(data, ...args)
|
let out = await super.create(data, ...args);
|
||||||
await this.buildLookUpObj()
|
await this.buildLookUpObj();
|
||||||
if(out.is_wildcard) await out.createWildcardCert()
|
if(out.is_wildcard) out.createWildcardCert();
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
|
|
||||||
@@ -100,31 +101,104 @@ class Host extends Table{
|
|||||||
async createWildcardCert(){
|
async createWildcardCert(){
|
||||||
if(!this.host.startsWith('*.')) throw new Error('not wild card');
|
if(!this.host.startsWith('*.')) throw new Error('not wild card');
|
||||||
|
|
||||||
let cert = await letsEncrypt.dnsWildcard(this.host, {
|
try{
|
||||||
challengeCreateFn: async (authz, challenge, keyAuthorization) => {
|
let host = this;
|
||||||
let parts = tldExtract(authz.identifier.value);
|
let cert = await letsEncrypt.dnsWildcard(this.host, {
|
||||||
let res = await porkBun.createRecordForce(parts.domain, {type:'TXT', name: `_acme-challenge${parts.sub ? `.${parts.sub}` : ''}`, content: `${keyAuthorization}`});
|
challengeCreateFn: async (authz, challenge, keyAuthorization) => {
|
||||||
},
|
host.update({
|
||||||
challengeRemoveFn: async (authz, challenge, keyAuthorization)=>{
|
wildcard_status: `Adding record for ${authz.identifier.value}`
|
||||||
let parts = tldExtract(authz.identifier.value);
|
});
|
||||||
await porkBun.deleteRecords(parts.domain, {type:'TXT', name: `_acme-challenge${parts.sub ? `.${parts.sub}` : ''}`, content: `${keyAuthorization}`});
|
try{
|
||||||
},
|
let parts = tldExtract(authz.identifier.value);
|
||||||
});
|
|
||||||
|
|
||||||
let toAdd = {
|
console.log('adding DNS record for', `_acme-challenge${parts.sub ? `.${parts.sub}` : ''}`)
|
||||||
cert_pem: cert.cert.split('\n\n')[0],
|
|
||||||
fullchain_pem: cert.cert,
|
let res = await porkBun.createRecordForce(
|
||||||
privkey_pem: cert.key.toString(),
|
parts.domain,
|
||||||
csr_pem: cert.csr.toString(),
|
{
|
||||||
expiry: 4120307657,
|
type:'TXT',
|
||||||
real_expiry: +LetsEncrypt.AcmeClient.crypto.readCertificateInfo(cert.cert).notAfter/1000,
|
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){
|
async update(...args){
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class AuthToken extends Token{
|
|||||||
return await User.get(this.created_by);
|
return await User.get(this.created_by);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async add(data){
|
static async create(data){
|
||||||
data.created_by = data.username;
|
data.created_by = data.username;
|
||||||
return super.create(data)
|
return super.create(data)
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const Table = require('../utils/redis_model');
|
const Table = require('../utils/redis_model');
|
||||||
// const {Token, InviteToken} = require('./token');
|
|
||||||
const bcrypt = require('bcrypt');
|
const bcrypt = require('bcrypt');
|
||||||
const saltRounds = 10;
|
const saltRounds = 10;
|
||||||
|
|
||||||
@@ -19,9 +18,11 @@ class User extends Table{
|
|||||||
|
|
||||||
static backing = 'redis'
|
static backing = 'redis'
|
||||||
|
|
||||||
static async add(data) {
|
static async create(data) {
|
||||||
try{
|
try{
|
||||||
|
console.log('hash?')
|
||||||
data['password'] = await bcrypt.hash(data['password'], saltRounds);
|
data['password'] = await bcrypt.hash(data['password'], saltRounds);
|
||||||
|
console.log('hashed password', data.password);
|
||||||
data['backing'] = data['backing'] || 'redis';
|
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){
|
async setPassword(data){
|
||||||
try{
|
try{
|
||||||
data['password'] = await bcrypt.hash(data['password'], saltRounds);
|
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){
|
static async login(data){
|
||||||
try{
|
try{
|
||||||
|
|
||||||
@@ -109,7 +74,7 @@ module.exports = {User};
|
|||||||
|
|
||||||
|
|
||||||
(async function(){
|
(async function(){
|
||||||
var defaultUser = 'proxyadmin3'
|
var defaultUser = 'proxyadmin2'
|
||||||
try{
|
try{
|
||||||
let user = await User.get(defaultUser);
|
let user = await User.get(defaultUser);
|
||||||
}catch(error){
|
}catch(error){
|
||||||
@@ -117,7 +82,7 @@ module.exports = {User};
|
|||||||
let user = await User.create({
|
let user = await User.create({
|
||||||
username:defaultUser,
|
username:defaultUser,
|
||||||
password: defaultUser,
|
password: defaultUser,
|
||||||
created_by:defaultUser
|
created_by: defaultUser
|
||||||
});
|
});
|
||||||
console.log(defaultUser, 'created', user);
|
console.log(defaultUser, 'created', user);
|
||||||
}catch(error){
|
}catch(error){
|
||||||
|
|||||||
Generated
+1
-1
@@ -15,7 +15,7 @@
|
|||||||
"bcrypt": "^5.1.1",
|
"bcrypt": "^5.1.1",
|
||||||
"bootstrap": "^5.3.3",
|
"bootstrap": "^5.3.3",
|
||||||
"ejs": "^3.1.10",
|
"ejs": "^3.1.10",
|
||||||
"express": "^4.19.2",
|
"express": "^4.18.2",
|
||||||
"extend": "^3.0.2",
|
"extend": "^3.0.2",
|
||||||
"jquery": "^3.7.1",
|
"jquery": "^3.7.1",
|
||||||
"ldapts": "^2.12.0",
|
"ldapts": "^2.12.0",
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@
|
|||||||
"bcrypt": "^5.1.1",
|
"bcrypt": "^5.1.1",
|
||||||
"bootstrap": "^5.3.3",
|
"bootstrap": "^5.3.3",
|
||||||
"ejs": "^3.1.10",
|
"ejs": "^3.1.10",
|
||||||
"express": "^4.19.2",
|
"express": "^4.18.2",
|
||||||
"extend": "^3.0.2",
|
"extend": "^3.0.2",
|
||||||
"jquery": "^3.7.1",
|
"jquery": "^3.7.1",
|
||||||
"ldapts": "^2.12.0",
|
"ldapts": "^2.12.0",
|
||||||
|
|||||||
@@ -252,10 +252,10 @@ app.user = (function(app){
|
|||||||
app.util = (function(app){
|
app.util = (function(app){
|
||||||
|
|
||||||
function getUrlParameter(name){
|
function getUrlParameter(name){
|
||||||
name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
|
name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
|
||||||
var regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
|
var regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
|
||||||
var results = regex.exec(location.search);
|
var results = regex.exec(location.search);
|
||||||
return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
|
return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
|
||||||
};
|
};
|
||||||
|
|
||||||
function actionMessage(message, $target, type, callback){
|
function actionMessage(message, $target, type, callback){
|
||||||
@@ -277,28 +277,38 @@ app.util = (function(app){
|
|||||||
})
|
})
|
||||||
}else{
|
}else{
|
||||||
if(type) $target.addClass('bg-' + type);
|
if(type) $target.addClass('bg-' + type);
|
||||||
message = '<span class="align-middle">' + message + '</span><button class="action-close btn btn-sm btn-outline-dark float-right"><i class="fa-solid fa-xmark"></i></button>'
|
message = '<span class="align-middle">' + message + '</span><button class="action-close btn btn-sm btn-outline-dark float-end"><i class="fa-solid fa-xmark"></i></button>'
|
||||||
$target.html(message).slideDown('fast');
|
$target.html(message).slideDown('fast');
|
||||||
}
|
}
|
||||||
setTimeout(callback,10)
|
setTimeout(callback,10)
|
||||||
}
|
}
|
||||||
|
|
||||||
$.fn.serializeObject = function(){
|
$.fn.serializeObject = function(){
|
||||||
var
|
var
|
||||||
arr = $(this).serializeArray(),
|
arr = $(this).serializeArray(),
|
||||||
obj = {};
|
obj = {};
|
||||||
|
|
||||||
for(var i = 0; i < arr.length; i++){
|
for(var i = 0; i < arr.length; i++){
|
||||||
if(obj[arr[i].name] === undefined) {
|
if(obj[arr[i].name] === undefined) {
|
||||||
obj[arr[i].name] = arr[i].value;
|
if(!arr[i].value) continue;
|
||||||
} else {
|
obj[arr[i].name] = arr[i].value;
|
||||||
if(!(obj[arr[i].name] instanceof Array)) {
|
let type = $(this).parent().find(`[name="${arr[i].name}"]`).attr('type');
|
||||||
obj[arr[i].name] = [obj[arr[i].name]];
|
if(['number', 'range'].includes(type)){
|
||||||
}
|
obj[arr[i].name] = Number(arr[i].value);
|
||||||
obj[arr[i].name].push(arr[i].value);
|
}
|
||||||
}
|
|
||||||
}
|
if(['radio'].includes(type) && ['true', 'false'].includes(arr[i].value)){
|
||||||
return obj;
|
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 {
|
return {
|
||||||
@@ -342,12 +352,12 @@ function formAJAX(btn, del){
|
|||||||
event.preventDefault(); // avoid to execute the actual submit of the form.
|
event.preventDefault(); // avoid to execute the actual submit of the form.
|
||||||
var $form = $(btn).closest('[action]'); // gets the 'form' parent
|
var $form = $(btn).closest('[action]'); // gets the 'form' parent
|
||||||
var formData = $form.find('[name]').serializeObject(); // builds query formDataing
|
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()){
|
if($form.validate && !$form.validate()){
|
||||||
// app.util.actionMessage('Please fix the form errors.', $form, 'danger')
|
app.util.actionMessage('Please fix the form errors.', $form, 'danger')
|
||||||
// return false;
|
return false;
|
||||||
// }
|
}
|
||||||
|
|
||||||
app.util.actionMessage(
|
app.util.actionMessage(
|
||||||
'<div class="spinner-border" role="status"><span class="sr-only">Loading...</span></div>',
|
'<div class="spinner-border" role="status"><span class="sr-only">Loading...</span></div>',
|
||||||
@@ -357,9 +367,21 @@ function formAJAX(btn, del){
|
|||||||
|
|
||||||
app.api[method]($form.attr('action'), formData, function(error, data){
|
app.api[method]($form.attr('action'), formData, function(error, data){
|
||||||
app.util.actionMessage(data.message, $form, error ? 'danger' : 'success'); //re-populate table
|
app.util.actionMessage(data.message, $form, error ? 'danger' : 'success'); //re-populate table
|
||||||
|
$form.validateClear();
|
||||||
if(!error){
|
if(!error){
|
||||||
$form.trigger("reset");
|
$form.trigger("reset");
|
||||||
eval($form.attr('evalAJAX')); //gets JS to run after completion
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ MIT license
|
|||||||
$.scope = {};
|
$.scope = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
var make = function( element ){
|
var make = function(element){
|
||||||
var result = [];
|
var result = [];
|
||||||
|
|
||||||
result.splice = function(inputValue, ...args){
|
result.splice = function(inputValue, ...args){
|
||||||
@@ -20,9 +20,9 @@ MIT license
|
|||||||
|
|
||||||
var index;
|
var index;
|
||||||
//if a string is submitted as the index, try to match it to index number
|
//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
|
index = this.indexOf( arguments[0] );//set where to start
|
||||||
if ( index === -1 ) {
|
if (index === -1) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
@@ -109,6 +109,7 @@ MIT license
|
|||||||
//set and return new array
|
//set and return new array
|
||||||
return Array.prototype.splice.apply(this, toProto);
|
return Array.prototype.splice.apply(this, toProto);
|
||||||
};
|
};
|
||||||
|
|
||||||
result.push = function(){
|
result.push = function(){
|
||||||
//add one or more objects to the array
|
//add one or more objects to the array
|
||||||
|
|
||||||
@@ -123,19 +124,25 @@ MIT license
|
|||||||
//return new array length
|
//return new array length
|
||||||
return this.length;
|
return this.length;
|
||||||
};
|
};
|
||||||
|
|
||||||
result.pop = function(){
|
result.pop = function(){
|
||||||
//remove and return array element
|
//remove and return array element
|
||||||
|
|
||||||
return this.splice( -1, 1 )[0];
|
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++ ){
|
result.reverse = function() {
|
||||||
this.push( temp[i] );
|
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;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -149,6 +156,10 @@ MIT license
|
|||||||
return this.splice( 0, 1 )[0];
|
return this.splice( 0, 1 )[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
result.unshift = function(data){
|
||||||
|
return this.splice(0,0, data)
|
||||||
|
}
|
||||||
|
|
||||||
result.loop = function(){
|
result.loop = function(){
|
||||||
var temp = this[0];
|
var temp = this[0];
|
||||||
this.splice( 0,1 );
|
this.splice( 0,1 );
|
||||||
@@ -156,13 +167,17 @@ MIT license
|
|||||||
|
|
||||||
return temp;
|
return temp;
|
||||||
};
|
};
|
||||||
|
|
||||||
result.loopUp = function(){
|
result.loopUp = function(){
|
||||||
var temp = this[this.length-1];
|
var temp = this[this.length-1];
|
||||||
this.splice( -1, 1 );
|
this.splice( -1, 1 );
|
||||||
this.splice( 0, 0, temp );
|
this.splice( 0, 0, temp );
|
||||||
return temp;
|
return temp;
|
||||||
};
|
};
|
||||||
|
|
||||||
result.indexOf = function( key, value ){
|
result.indexOf = function( key, value ){
|
||||||
|
if(typeof key === 'number') return key;
|
||||||
|
|
||||||
if( typeof value !== 'string' ){
|
if( typeof value !== 'string' ){
|
||||||
value = arguments[0];
|
value = arguments[0];
|
||||||
key = this.__index;
|
key = this.__index;
|
||||||
@@ -175,7 +190,8 @@ MIT license
|
|||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
};
|
};
|
||||||
result.update = function( key, value, update ){
|
|
||||||
|
result.update = function(key, value, data){
|
||||||
//set variables using sting for index
|
//set variables using sting for index
|
||||||
|
|
||||||
// If update is called with no index/key, assume its the 0
|
// If update is called with no index/key, assume its the 0
|
||||||
@@ -186,25 +202,32 @@ MIT license
|
|||||||
return this.splice(0, 1, key);
|
return this.splice(0, 1, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
if( typeof value !== 'string' ){
|
if(typeof value !== 'string'){
|
||||||
update = arguments[1];
|
data = arguments[1];
|
||||||
value = arguments[0];
|
if(typeof key !== 'number'){
|
||||||
key = this.__index;
|
value = arguments[0];
|
||||||
|
key = this.__index;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var index = this.indexOf( key, value );
|
var index = this.indexOf( key, value );
|
||||||
|
|
||||||
if(index === -1) {
|
if(index === -1) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
var object = $.extend( true, {}, this[index], update );
|
this[index] = $.extend( true, this[index], data );
|
||||||
|
|
||||||
var $render = $(Mustache.render(this.__jqTemplate, object));
|
var $render = $(Mustache.render(this.__jqTemplate, this[index]));
|
||||||
$render.attr('jq-repeat-index', index);
|
$render.attr('jq-repeat-index', index);
|
||||||
this[index].__jq_$el.replaceWith($render);
|
|
||||||
|
|
||||||
|
this.__update(this[index].__jq_$el, $render, this[index], this);
|
||||||
this[index].__jq_$el = $render;
|
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){
|
result.__put = function($el, item, list){
|
||||||
$el.show();
|
$el.show();
|
||||||
};
|
};
|
||||||
@@ -213,8 +236,8 @@ MIT license
|
|||||||
$el.remove();
|
$el.remove();
|
||||||
};
|
};
|
||||||
|
|
||||||
result.__update = function($el, item, list){
|
result.__update = function($el, $render, item, list){
|
||||||
console.log('here', $el)
|
$el.replaceWith($render);
|
||||||
$el.show();
|
$el.show();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+53
-70
@@ -1,96 +1,79 @@
|
|||||||
( function( $ ) {
|
( function( $ ) {
|
||||||
var settings = {
|
var settings = {
|
||||||
rule: {
|
rule: {
|
||||||
eq: function( value, options ) {
|
eq: function(value, options){
|
||||||
var compare = $( '[name=' + options + ']' ).val();
|
var compare = $('[name=' + options + ']').val();
|
||||||
|
|
||||||
if ( value != compare ) {
|
if ( value != compare ) {
|
||||||
return "Miss-match";
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
$( '<b>' ).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 ) {
|
if(this.is('[validate]')) return this.validateField(event);
|
||||||
var attr = $input.attr( 'validate' ).split( ':' ), //array of params
|
|
||||||
requirement = attr[1],
|
|
||||||
value = $input.val(), //link to input value
|
|
||||||
rule = attr[0];
|
|
||||||
|
|
||||||
$input.siblings( 'label' ).children( 'b' ).remove(); //removes old error
|
if(!this.attr('isValid')){
|
||||||
$input.parent().removeClass( "has-error" ); //removes has-error class
|
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
|
//checks if field is required, and length
|
||||||
if (isNaN(requirement) === false && requirement && value.length < requirement) {
|
if(!isNaN(options) && value.length < options){
|
||||||
return thisSettings.processValidation( 'Must be ' + requirement + ' characters', $input );
|
message = `Must be ${options} characters`;
|
||||||
}
|
}
|
||||||
|
|
||||||
//checks if empty to stop processing
|
//checks if empty to stop processing
|
||||||
if ( isNaN( requirement ) === false && value.length === 0 ) {
|
if(!isNaN(options) && value.length === 0) {
|
||||||
return;
|
}else if(rule in settings.rule){
|
||||||
|
let message = settings.rule[rule].apply(this, [value, options]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( rule in thisSettings.rule ) {
|
this.validateMessage(message)
|
||||||
return thisSettings.processValidation( thisSettings.rule[rule].apply( this, [value, requirement] ), $input );
|
return !message;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$.fn.validate = function( settingsObj, event ) {
|
$.fn.validateMessage = function(message){
|
||||||
event = event || window.event;
|
if(message && message !== true){
|
||||||
|
this.closest('.form-group').find('b.invalid-feedback').html(message);
|
||||||
failedCount = 0;
|
this.addClass('is-invalid');
|
||||||
var thisForm = false,
|
}else{
|
||||||
thisSettings = $.extend( true, settings, settingsObj );
|
this.removeClass('is-invalid');
|
||||||
|
this.addClass('is-valid');
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
jQuery.extend({
|
jQuery.extend({
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
const router = require('express').Router();
|
const router = require('express').Router();
|
||||||
const { Auth } = require('../controller/auth');
|
const { Auth } = require('../controller/auth');
|
||||||
|
|
||||||
|
|
||||||
router.post('/login', async function(req, res, next){
|
router.post('/login', async function(req, res, next){
|
||||||
try{
|
try{
|
||||||
let auth = await Auth.login(req.body);
|
let auth = await Auth.login(req.body);
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ router.post('/', async function(req, res, next){
|
|||||||
...item,
|
...item,
|
||||||
});
|
});
|
||||||
} catch (error){
|
} catch (error){
|
||||||
return next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -40,4 +40,7 @@ router.get('/login', async function(req, res, next) {
|
|||||||
res.render('login', {...values, redirect: req.query.redirect});
|
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;
|
module.exports = router;
|
||||||
|
|||||||
+45
-15
@@ -7,6 +7,11 @@ const sleep = require('./sleep');
|
|||||||
|
|
||||||
// https://dns.google/resolve?name=${name}&type=TXT
|
// https://dns.google/resolve?name=${name}&type=TXT
|
||||||
|
|
||||||
|
AcmeClient.setLogger((message) => {
|
||||||
|
console.log('ACME:', message);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
class LetsEncrypt{
|
class LetsEncrypt{
|
||||||
static AcmeClient = AcmeClient;
|
static AcmeClient = AcmeClient;
|
||||||
|
|
||||||
@@ -43,32 +48,57 @@ class LetsEncrypt{
|
|||||||
|
|
||||||
try{
|
try{
|
||||||
domain = domain.replace(/^\*\./, '');
|
domain = domain.replace(/^\*\./, '');
|
||||||
|
|
||||||
const [key, csr] = await AcmeClient.crypto.createCsr({
|
const [key, csr] = await AcmeClient.crypto.createCsr({
|
||||||
altNames: [domain, `*.${domain}`],
|
altNames: [domain, `*.${domain}`],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let dnsToAdd = 0;
|
||||||
|
let dnsFound = 0;
|
||||||
|
|
||||||
const cert = await this.client.auto({
|
const cert = await this.client.auto({
|
||||||
csr,
|
csr,
|
||||||
email: 'wmantly@gmail.com',
|
email: 'wmantly@gmail.com',
|
||||||
termsOfServiceAgreed: true,
|
termsOfServiceAgreed: true,
|
||||||
challengePriority: ['dns-01'],
|
challengePriority: ['dns-01'],
|
||||||
|
skipChallengeVerification: true,
|
||||||
challengeCreateFn: async (authz, challenge, keyAuthorization) => {
|
challengeCreateFn: async (authz, challenge, keyAuthorization) => {
|
||||||
// console.log(`start TXT record key=_acme-challenge.${authz.identifier.value} value=${keyAuthorization}`)
|
try{
|
||||||
|
console.log('challenge', challenge)
|
||||||
let resCheck = await axios.get(`https://dns.google/resolve?name=_acme-challenge.${authz.identifier.value}&type=TXT`);
|
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`)
|
||||||
if(resCheck.data.Answer.some(record => record.data === keyAuthorization)) return;
|
dnsToAdd++
|
||||||
|
let resCheck = await axios.get(`https://dns.google/resolve?name=_acme-challenge.${authz.identifier.value}&type=TXT`);
|
||||||
await options.challengeCreateFn(authz, challenge, keyAuthorization);
|
if(resCheck.data.Answer && resCheck.data.Answer.some(record => record.data === keyAuthorization)){
|
||||||
|
await sleep(1000);
|
||||||
let checkCount = 0;
|
dnsFound++
|
||||||
while(true){
|
if(dnsFound === dnsToAdd){
|
||||||
await sleep(1500);
|
options.onDnsCheckFound(authz, dnsFound)
|
||||||
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)){
|
return;
|
||||||
// console.log(`found record for key=_acme-challenge.${authz.identifier.value} value=${keyAuthorization}`)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
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,
|
challengeRemoveFn: options.challengeRemoveFn,
|
||||||
|
|||||||
@@ -12,9 +12,13 @@ function ModelPs(model){
|
|||||||
}
|
}
|
||||||
|
|
||||||
function publish(prop, res, req){
|
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, {
|
return new Proxy(model, {
|
||||||
@@ -28,16 +32,22 @@ function ModelPs(model){
|
|||||||
const targetValue = Reflect.get(target, propKey, receiver);
|
const targetValue = Reflect.get(target, propKey, receiver);
|
||||||
if (typeof targetValue === 'function') {
|
if (typeof targetValue === 'function') {
|
||||||
return function(...args){
|
return function(...args){
|
||||||
|
try{
|
||||||
// let res = targetValue.apply(this, args); // (A)
|
// let res = targetValue.apply(this, args); // (A)
|
||||||
let res = Reflect.apply(targetValue, this, args);
|
var res = Reflect.apply(targetValue, this, args);
|
||||||
if(targetValue.constructor.name === 'AsyncFunction'){
|
if(targetValue.constructor.name === 'AsyncFunction'){
|
||||||
res.then(function(res){
|
res.then(function(res){
|
||||||
publish(propKey, res, ...args);
|
publish(propKey, res, ...args);
|
||||||
});
|
}).catch(function(error){
|
||||||
}else{
|
console.log('toDo, publish errors...')
|
||||||
publish(propKey, res, ...args)
|
});
|
||||||
|
}else{
|
||||||
|
publish(propKey, res, ...args)
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}catch(error){
|
||||||
|
console.log("grrrr")
|
||||||
}
|
}
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return targetValue;
|
return targetValue;
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ function processKeys(map, data, partial){
|
|||||||
continue;
|
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);
|
out[key] = data.hasOwnProperty(key) && data[key] !== undefined ? data[key] : returnOrCall(map[key].default);
|
||||||
|
|
||||||
if(data.hasOwnProperty(key) && process_type[map[key].type]){
|
if(data.hasOwnProperty(key) && process_type[map[key].type]){
|
||||||
@@ -46,7 +48,6 @@ function processKeys(map, data, partial){
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(errors.length !== 0){
|
if(errors.length !== 0){
|
||||||
console.log('errors', errors)
|
|
||||||
throw new ObjectValidateError(errors);
|
throw new ObjectValidateError(errors);
|
||||||
return {__errors__: errors};
|
return {__errors__: errors};
|
||||||
}
|
}
|
||||||
@@ -79,9 +80,10 @@ function parseToString(data){
|
|||||||
return (types[typeof(data)] || String)(data);
|
return (types[typeof(data)] || String)(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ObjectValidateError(message) {
|
function ObjectValidateError(message){
|
||||||
this.name = 'ObjectValidateError';
|
this.name = 'ObjectValidateError';
|
||||||
this.message = (message || {});
|
this.message = (message || {});
|
||||||
|
this.keys = (message || {})
|
||||||
this.status = 422;
|
this.status = 422;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+35
-11
@@ -47,17 +47,17 @@ class Table{
|
|||||||
}catch(error){
|
}catch(error){
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async exists(index){
|
static async exists(index){
|
||||||
try{
|
if(typeof index === 'object'){
|
||||||
await this.get(data);
|
index = index[this._key];
|
||||||
|
|
||||||
return true;
|
|
||||||
}catch(error){
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return await client.SISMEMBER(
|
||||||
|
redisPrefix(this.prototype.constructor.name),
|
||||||
|
index
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async list(){
|
static async list(){
|
||||||
@@ -86,6 +86,7 @@ class Table{
|
|||||||
static async create(data){
|
static async create(data){
|
||||||
// Add a entry to this redis table.
|
// Add a entry to this redis table.
|
||||||
try{
|
try{
|
||||||
|
|
||||||
// Validate the passed data by the keyMap schema.
|
// Validate the passed data by the keyMap schema.
|
||||||
data = objValidate.processKeys(this._keyMap, data);
|
data = objValidate.processKeys(this._keyMap, data);
|
||||||
|
|
||||||
@@ -94,6 +95,10 @@ class Table{
|
|||||||
let error = new Error('EntryNameUsed');
|
let error = new Error('EntryNameUsed');
|
||||||
error.name = 'EntryNameUsed';
|
error.name = 'EntryNameUsed';
|
||||||
error.message = `${this.prototype.constructor.name}:${data[this._key]} already exists`;
|
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;
|
error.status = 409;
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
@@ -107,7 +112,7 @@ class Table{
|
|||||||
|
|
||||||
// Add the values for this entry.
|
// Add the values for this entry.
|
||||||
for(let key of Object.keys(data)){
|
for(let key of Object.keys(data)){
|
||||||
if(!data[key]) continue;
|
if(data[key] === undefined) continue;
|
||||||
await client.HSET(
|
await client.HSET(
|
||||||
redisPrefix(`${this.prototype.constructor.name}_${data[this._key]}`),
|
redisPrefix(`${this.prototype.constructor.name}_${data[this._key]}`),
|
||||||
key,
|
key,
|
||||||
@@ -125,9 +130,26 @@ class Table{
|
|||||||
async update(data, key){
|
async update(data, key){
|
||||||
// Update an existing entry.
|
// Update an existing entry.
|
||||||
try{
|
try{
|
||||||
|
// Validate the passed data, ignoring required fields.
|
||||||
|
data = objValidate.processKeys(this.constructor._keyMap, data, true);
|
||||||
|
|
||||||
// Check to see if entry name changed.
|
// Check to see if entry name changed.
|
||||||
if(data[this.constructor._key] && data[this.constructor._key] !== this[this.constructor._key]){
|
if(data[this.constructor._key] && data[this.constructor._key] !== this[this.constructor._key]){
|
||||||
// Remove the index key from the tables members list.
|
// 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(
|
await client.SREM(
|
||||||
redisPrefix(this.constructor.name),
|
redisPrefix(this.constructor.name),
|
||||||
this[this.constructor._key]
|
this[this.constructor._key]
|
||||||
@@ -139,12 +161,14 @@ class Table{
|
|||||||
data[this.constructor._key]
|
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.
|
// 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
|
// Loop over the data fields and apply them to redis
|
||||||
for(let key of Object.keys(data)){
|
for(let key of Object.keys(data)){
|
||||||
this[key] = data[key];
|
this[key] = data[key];
|
||||||
|
|||||||
+99
-75
@@ -5,7 +5,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
label.control-label{
|
label.form-label{
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-bottom: 1px;
|
margin-bottom: 1px;
|
||||||
}
|
}
|
||||||
@@ -14,7 +14,10 @@
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.editWindow{
|
div.hostEditPanel{
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
div.form-group{
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
/* my Div class for my search bar */
|
/* my Div class for my search bar */
|
||||||
@@ -36,11 +39,13 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
var $editHostForm;
|
||||||
|
|
||||||
function parseHostRow(host) {
|
function parseHostRow(host) {
|
||||||
host['updated_on_text'] = moment(host['updated_on'], "x").fromNow();
|
host['updated_on_text'] = moment(host['updated_on'], "x").fromNow();
|
||||||
host['targetssl_text'] = host['targetssl'] ? 'https://' : 'http://';
|
host['targetssl_text'] = host['targetssl'] ? 'https://' : 'http://';
|
||||||
host['forcessl_text'] = host['forcessl'] ? 'https://' : 'http://';
|
host['forcessl_text'] = host['forcessl'] ? 'https://' : 'http://';
|
||||||
|
host['wildcard_text'] = host['is_wildcard'] ? host['wildcard_status'] : 'Auto';
|
||||||
|
|
||||||
return host;
|
return host;
|
||||||
}
|
}
|
||||||
@@ -66,40 +71,34 @@
|
|||||||
$('#hostsTable').find('tr').each(function(idx, el){
|
$('#hostsTable').find('tr').each(function(idx, el){
|
||||||
$(el).removeClass('table-warning');
|
$(el).removeClass('table-warning');
|
||||||
});
|
});
|
||||||
$('.editWindow').slideUp('fast');
|
$.scope.editHost.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
function editHost(btn, host){
|
function editHost(btn, host){
|
||||||
cancleHostEdit();
|
cancleHostEdit();
|
||||||
$(btn).closest('tr').addClass('table-warning');
|
$(btn).closest('tr').addClass('table-warning');
|
||||||
$('.editWindow .card-title').html("Edit "+ host);
|
host = $.scope.hosts.getByKey(host);
|
||||||
|
$.scope.editHost.update({...host, form: $editHostForm.html()});
|
||||||
|
|
||||||
$('div.editWindow .card-body span').html($('#addHost').html());
|
$.each(host, function( key, value ) { if(typeof value == "boolean"){
|
||||||
$('div.editWindow .card-body span button').remove();
|
$(".hostEditPanel #"+ key +"-"+ value).prop('checked', true)
|
||||||
|
}else{
|
||||||
$(".editWindow input[name='edit_host']").val(host);
|
$(".hostEditPanel input[name='" + key + "']").val(value);
|
||||||
|
}
|
||||||
$('.editWindow').slideDown('fast');
|
|
||||||
|
|
||||||
app.host.get(host, function(error, data){
|
|
||||||
$.each( data.results, function( key, value ) {
|
|
||||||
if(typeof value == "boolean"){
|
|
||||||
$(".editWindow #"+ key +"-"+ value).prop('checked', true)
|
|
||||||
}else{
|
|
||||||
$(".editWindow input[name='" + key + "']").val(value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function deleteHost(host){
|
function deleteHost(host){
|
||||||
app.host.remove({host: host}, function(err, data){
|
app.host.remove({host: host}, function(error, data){
|
||||||
// app.util.actionMessage(`Host ${host} deleted!`, $.scope.hosts.$this, 'danger');
|
if(error) app.util.actionMessage(error.message, $.scope.hosts.$this, 'danger');
|
||||||
$.scope.hosts.remove(host);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
|
$editHostForm = $('#addHost').clone();
|
||||||
|
$editHostForm.find('hr.buttonBreak').nextAll().remove();
|
||||||
|
// $editHostForm.find('.autoSll').addClass('bg-secondary');
|
||||||
|
$editHostForm.find('[name=is_wildcard').attr('disabled', true);
|
||||||
populateHosts(); //populate the table
|
populateHosts(); //populate the table
|
||||||
|
|
||||||
$.scope.hosts.__setTake(function($el, item, list){
|
$.scope.hosts.__setTake(function($el, item, list){
|
||||||
@@ -109,6 +108,19 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$.scope.hosts.__setUpdate(function($el, $render, item, list){
|
||||||
|
$render.show()
|
||||||
|
$el.replaceWith($render);
|
||||||
|
});
|
||||||
|
|
||||||
|
$.scope.editHost.__setPut(function($el, item, list){
|
||||||
|
$el.slideDown();
|
||||||
|
});
|
||||||
|
|
||||||
|
$.scope.editHost.__setTake(function($el, item, list){
|
||||||
|
$el.slideUp();
|
||||||
|
});
|
||||||
|
|
||||||
app.subscribe(/^model:Host/, function(data, topic){
|
app.subscribe(/^model:Host/, function(data, topic){
|
||||||
console.log(topic, data);
|
console.log(topic, data);
|
||||||
});
|
});
|
||||||
@@ -119,7 +131,7 @@
|
|||||||
if($.scope.hosts.indexOf(host) >= 0){
|
if($.scope.hosts.indexOf(host) >= 0){
|
||||||
$.scope.hosts.update(host, parseHostRow(data));
|
$.scope.hosts.update(host, parseHostRow(data));
|
||||||
}else{
|
}else{
|
||||||
$.scope.hosts.splice(0,0, parseHostRow(data))
|
$.scope.hosts.unshift(parseHostRow(data));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -129,11 +141,10 @@
|
|||||||
if($.scope.hosts.indexOf(host) >= 0){
|
if($.scope.hosts.indexOf(host) >= 0){
|
||||||
$.scope.hosts.update(host, parseHostRow(data));
|
$.scope.hosts.update(host, parseHostRow(data));
|
||||||
}else{
|
}else{
|
||||||
$.scope.hosts.splice(0,0, parseHostRow(data))
|
$.scope.hosts.unshift(parseHostRow(data));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
app.subscribe(/^model:Host:remove/, function(data, topic){
|
app.subscribe(/^model:Host:remove/, function(data, topic){
|
||||||
let [a,b, action, host] = topic.split(':');
|
let [a,b, action, host] = topic.split(':');
|
||||||
|
|
||||||
@@ -156,38 +167,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$('form.addHost').on('submit', function(){
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
$form = $(this);
|
|
||||||
var action = $($form.find('button[type="submit"]')[0]).data('type');
|
|
||||||
app.util.actionMessage('', $form);
|
|
||||||
|
|
||||||
if($form.attr('isValid') === 'true'){
|
|
||||||
var formdata = $form.serializeObject();
|
|
||||||
if(formdata.targetPort) formdata.targetPort = Number(formdata.targetPort);
|
|
||||||
if(formdata.targetssl) formdata.targetssl = formdata.targetssl == 'true' ? true : false;
|
|
||||||
if(formdata.forcessl) formdata.forcessl = formdata.forcessl == 'true' ? true : false;
|
|
||||||
|
|
||||||
app.host[action](formdata, function(error, data){
|
|
||||||
if(error){
|
|
||||||
app.util.actionMessage(error + data.message, $form, 'danger');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if($.scope.hosts.indexOf(data.__requestedHost) >= 0){
|
|
||||||
// $.scope.hosts.update(data.__requestedHost, parseHostRow(data));
|
|
||||||
// }else{
|
|
||||||
// // $.scope.hosts.splice(0,0, parseHostRow(data))
|
|
||||||
// }
|
|
||||||
|
|
||||||
if(action == 'edit') $('.editWindow').slideUp('fast');
|
|
||||||
|
|
||||||
$form.trigger('reset');
|
|
||||||
})
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<div class="row" style="display:none">
|
<div class="row" style="display:none">
|
||||||
@@ -195,7 +174,7 @@
|
|||||||
<!--
|
<!--
|
||||||
left column
|
left column
|
||||||
-->
|
-->
|
||||||
<div class="card shadow-lg editWindow" style="display:none">
|
<div jq-repeat="editHost" class="card shadow-lg border-warning hostEditPanel" style="display:none">
|
||||||
<!--
|
<!--
|
||||||
Edit host card
|
Edit host card
|
||||||
-->
|
-->
|
||||||
@@ -204,6 +183,7 @@
|
|||||||
<i class="fa-solid fa-pencil"></i>
|
<i class="fa-solid fa-pencil"></i>
|
||||||
</span>
|
</span>
|
||||||
<span class="card-title">
|
<span class="card-title">
|
||||||
|
Edit {{ host }}
|
||||||
</span>
|
</span>
|
||||||
<span class="float-end">
|
<span class="float-end">
|
||||||
<i class="fa-solid fa-circle-minus"></i>
|
<i class="fa-solid fa-circle-minus"></i>
|
||||||
@@ -212,8 +192,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form class="addHost" onsubmit="$(this).validate()">
|
<form class="addHost" method="PUT" action="/host/{{ host }}" onsubmit="formAJAX(this)" evalAJAX="cancleHostEdit()">
|
||||||
<span></span>
|
{{{ form }}}
|
||||||
<input type="hidden" name="edit_host" />
|
<input type="hidden" name="edit_host" />
|
||||||
<button type="submit" data-type="edit" class="btn btn-warning">
|
<button type="submit" data-type="edit" class="btn btn-warning">
|
||||||
<i class="fa-solid fa-pencil"></i>
|
<i class="fa-solid fa-pencil"></i>
|
||||||
@@ -227,7 +207,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card shadow-lg">
|
<div class="card shadow-lg hostAddPanel">
|
||||||
<!--
|
<!--
|
||||||
Add new host card
|
Add new host card
|
||||||
-->
|
-->
|
||||||
@@ -246,10 +226,13 @@
|
|||||||
<div class="card-header actionMessage" style="display:none"></div>
|
<div class="card-header actionMessage" style="display:none"></div>
|
||||||
|
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form class="addHost" id="addHost" onsubmit="$(this).validate()">
|
<form class="addHost" id="addHost" method="POST" action="host" onsubmit="formAJAX(this)">
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label">Incoming SSL</label>
|
<label class="form-label">
|
||||||
|
Incoming SSL
|
||||||
|
</label>
|
||||||
|
<br />
|
||||||
<div class="radio">
|
<div class="radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="forcessl" id="forcessl-true" value="true" checked>
|
<input type="radio" name="forcessl" id="forcessl-true" value="true" checked>
|
||||||
@@ -264,23 +247,54 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group autoSll">
|
||||||
<label class="control-label">Incoming Host Name</label>
|
<label class="form-label">
|
||||||
<input type="text" name="host" class="form-control" placeholder="ex: proxy.cloud-ops.net" validate=":3" >
|
Auto SSL
|
||||||
|
</label>
|
||||||
|
<div class="radio">
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="is_wildcard" id="is_wildcard-false" value="false" checked>
|
||||||
|
On demand certs
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="radio">
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="is_wildcard" id="is_wildcard-true" value="true">
|
||||||
|
Request Wildcard cert
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label">Target IP or Host Name</label>
|
<label for='host' class="form-label">
|
||||||
|
Incoming Host Name
|
||||||
|
</label>
|
||||||
|
<div>
|
||||||
|
<input type="text" name="host" class="form-control" placeholder="ex: proxy.cloud-ops.net" validate=":3" >
|
||||||
|
<b class="invalid-feedback"></b>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3 form-group">
|
||||||
|
<label for="ip" class="form-label">
|
||||||
|
Target IP or Host Name
|
||||||
|
</label>
|
||||||
<input type="text" name="ip" class="form-control" placeholder="ex: 10.10.10.10" validate=":3" />
|
<input type="text" name="ip" class="form-control" placeholder="ex: 10.10.10.10" validate=":3" />
|
||||||
|
<b class="invalid-feedback"></b>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label">Target TCP Port</label>
|
<label for="targetPort" class="form-label">
|
||||||
|
Target TCP Port
|
||||||
|
</label>
|
||||||
<input type="number" name="targetPort" class="form-control" value="80" min="0" max="65535" />
|
<input type="number" name="targetPort" class="form-control" value="80" min="0" max="65535" />
|
||||||
|
<b class="invalid-feedback"></b>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label">Target SSL</label>
|
<label class="form-label">
|
||||||
|
Target SSL
|
||||||
|
</label>
|
||||||
<div class="radio">
|
<div class="radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="targetssl" id="targetssl-true" value="true">
|
<input type="radio" name="targetssl" id="targetssl-true" value="true">
|
||||||
@@ -293,9 +307,10 @@
|
|||||||
Proxy to HTTP <b>Recommended</b>
|
Proxy to HTTP <b>Recommended</b>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
<b class="invalid-feedback"></b>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr class="buttonBreak" />
|
||||||
<button type="submit" data-type="add" class="btn btn-success">
|
<button type="submit" class="btn btn-success">
|
||||||
<i class="fa-solid fa-plus"></i>
|
<i class="fa-solid fa-plus"></i>
|
||||||
Add
|
Add
|
||||||
</button>
|
</button>
|
||||||
@@ -308,7 +323,7 @@
|
|||||||
<!--
|
<!--
|
||||||
Right column
|
Right column
|
||||||
-->
|
-->
|
||||||
<div class="card shadow-lg">
|
<div class="card shadow-lg hostListPanel">
|
||||||
<!--
|
<!--
|
||||||
List current hosts
|
List current hosts
|
||||||
-->
|
-->
|
||||||
@@ -326,12 +341,17 @@
|
|||||||
|
|
||||||
<div class="card-header actionMessage" style="display:none"></div>
|
<div class="card-header actionMessage" style="display:none"></div>
|
||||||
<div class="card-body search-wrapper">
|
<div class="card-body search-wrapper">
|
||||||
<label class="control-label" for="search" style="margin-left: -5px;">Search Hosts: </label>
|
<label class="form-label" for="search" style="margin-left: -5px;">
|
||||||
|
Search Hosts:
|
||||||
|
</label>
|
||||||
<input type="search" id="search" host-search>
|
<input type="search" id="search" host-search>
|
||||||
</div>
|
</div>
|
||||||
<table class="card-body table table-striped" style="margin-bottom:0">
|
<table class="card-body table table-striped" style="margin-bottom:0">
|
||||||
|
|
||||||
<thead>
|
<thead>
|
||||||
|
<th>
|
||||||
|
SSL
|
||||||
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Host Name
|
Host Name
|
||||||
</th>
|
</th>
|
||||||
@@ -347,6 +367,9 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody id="hostsTable">
|
<tbody id="hostsTable">
|
||||||
<tr action="api" jq-repeat="hosts" jq-repeat-index='host' style="display:none">
|
<tr action="api" jq-repeat="hosts" jq-repeat-index='host' style="display:none">
|
||||||
|
<td>
|
||||||
|
{{{ wildcard_text }}}
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a target="_blank" href="{{ forcessl_text }}{{ host }}">
|
<a target="_blank" href="{{ forcessl_text }}{{ host }}">
|
||||||
{{{ forcessl_text }}}{{ host }}
|
{{{ forcessl_text }}}{{ host }}
|
||||||
@@ -360,12 +383,13 @@
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<!-- <button type="button" class="btn btn-info">
|
<!-- <button type="button" class="btn btn-info">
|
||||||
<i class="fa-brands fa-expeditedssl"></i>
|
<i class="fa-brands fa-expeditedssl"></i>
|
||||||
Cert
|
Cert
|
||||||
</button> -->
|
</button> -->
|
||||||
<button type="button" onclick="editHost(this, '{{ host }}');" class="btn btn-sm btn-warning">
|
<button type="button" onclick="editHost(this, '{{ host }}');" class="btn btn-sm btn-warning">
|
||||||
<i class="fa-solid fa-pencil"></i> Edit
|
<i class="fa-solid fa-pencil"></i>
|
||||||
|
Edit
|
||||||
</button>
|
</button>
|
||||||
<button type="button" onclick="deleteHost('{{ host }}')" class="btn btn-sm btn-danger">
|
<button type="button" onclick="deleteHost('{{ host }}')" class="btn btn-sm btn-danger">
|
||||||
<i class="fa-solid fa-trash-can"></i>
|
<i class="fa-solid fa-trash-can"></i>
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<!-- need to load jq-query cdn or file. -->
|
||||||
|
<script src="https://code.jquery.com/jquery-3.7.1.min.js"
|
||||||
|
integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
|
||||||
|
|
||||||
|
<!-- need to load mustache cdn or file. -->
|
||||||
|
<script src="
|
||||||
|
https://cdn.jsdelivr.net/npm/mustache@4.2.0/mustache.min.js
|
||||||
|
"></script>
|
||||||
|
|
||||||
|
<!-- need to load jq-repeat cdn or file -->
|
||||||
|
<script type="text/javascript" src='/static/lib/js/jq-repeat_new.js'></script>
|
||||||
|
<script>
|
||||||
|
//on document ready. the logic would execute.
|
||||||
|
$(document).ready(function () {
|
||||||
|
$.scope.toDo.__setPut(function($el, item, list){
|
||||||
|
$el.slideDown('slow');
|
||||||
|
})
|
||||||
|
|
||||||
|
// $.scope.toDo.__setUpdate(function($el, $render, item, list){
|
||||||
|
// $el.fadeOut(2000, function() {
|
||||||
|
// $(this).html($render.html()).fadeIn(2000);
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
|
// $.scope.toDo.__setUpdate(function($el, $render, item, list){
|
||||||
|
// $el.animate({'opacity': 0}, 400, function(){
|
||||||
|
// $(this).html($render.html()).animate({'opacity': 1}, 400);
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
|
$.scope.toDo.__setUpdate(function($el, $render, item, list){
|
||||||
|
$el.slideUp(function(){
|
||||||
|
$(this).replaceWith($render)
|
||||||
|
$render.slideDown()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$.scope.toDo.push({ item: "Get milk", done: "Yes" }); // 0
|
||||||
|
$.scope.toDo.push({ item: "Do laundry", done: "No" }); // 1
|
||||||
|
//should take array id or array key
|
||||||
|
|
||||||
|
// - **howMany** _Type: Number_
|
||||||
|
// Number of repeat objects that will be removed. If there are non to be removed, it is not required to use this argument.
|
||||||
|
// - **update** _Type: Array_
|
||||||
|
// This is the array of repeat objects to add. If there are none to this is not required.
|
||||||
|
|
||||||
|
//remove works
|
||||||
|
// $.scope.toDo.splice("Get milk" , "1")
|
||||||
|
|
||||||
|
//update
|
||||||
|
$.scope.toDo.splice(-1,0, { item: "Get Bread", done: "Yes" })
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Document</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<ul>
|
||||||
|
<li jq-repeat="toDo" jq-repeat-index="item" style="display:none;"><span class="item">{{ item }}</span>: {{ done }}</li>
|
||||||
|
</ul>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -67,7 +67,7 @@
|
|||||||
|
|
||||||
<div class="card-header actionMessage" style="display:none"></div>
|
<div class="card-header actionMessage" style="display:none"></div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form action="user/" evalAJAX="
|
<form action="user/" onsubmit="formAJAX(this)" evalAJAX="
|
||||||
$.scope.users.splice(0, 0, data);
|
$.scope.users.splice(0, 0, data);
|
||||||
">
|
">
|
||||||
<input type="hidden" class="form-control" name="delete" value="false" />
|
<input type="hidden" class="form-control" name="delete" value="false" />
|
||||||
@@ -84,7 +84,7 @@
|
|||||||
<input type="password" class="form-control" name="passwordMatch" placeholder="Retype password" validate="eq:password"/>
|
<input type="password" class="form-control" name="passwordMatch" placeholder="Retype password" validate="eq:password"/>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
<button type="button" onclick="formAJAX(this)" class="btn btn-info">
|
<button type="button" class="btn btn-info">
|
||||||
Add
|
Add
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
Reference in New Issue
Block a user