Added relations to redis ORM

This commit is contained in:
2024-08-13 15:18:47 -04:00
parent 2a5ca3664a
commit b27e1b1617
20 changed files with 240 additions and 150 deletions
+3 -2
View File
@@ -1,7 +1,7 @@
'use strict'; 'use strict';
const {User} = require('../models/user'); const Table = require('../models');
const {AuthToken} = require('../models/token'); const {User, AuthToken} = Table.models;
class Auth{ class Auth{
@@ -35,6 +35,7 @@ class Auth{
throw this.errors.login(); throw this.errors.login();
}catch(error){ }catch(error){
console.log('check error', error);
throw this.errors.login(); throw this.errors.login();
} }
} }
+2 -2
View File
@@ -5,7 +5,7 @@ const {Auth} = require('../controller/auth');
async function auth(req, res, next){ async function auth(req, res, next){
try{ try{
req.token = await Auth.checkToken(req.header('auth-token')); req.token = await Auth.checkToken(req.header('auth-token'));
req.user = await req.token.getUser(); req.user = req.token.user;
return next(); return next();
}catch(error){ }catch(error){
next(error); next(error);
@@ -15,7 +15,7 @@ async function auth(req, res, next){
async function authIO(socket, next){ async function authIO(socket, next){
try{ try{
let token = await Auth.checkToken(socket.handshake.auth.token || 0); let token = await Auth.checkToken(socket.handshake.auth.token || 0);
socket.user = await token.getUser(); socket.user = token.user;
next(); next();
}catch(error){ }catch(error){
next(error); next(error);
@@ -9,9 +9,9 @@ const ModelPs = require('../utils/model_pubsub');
const tldExtract = require('tld-extract').parse_host; const tldExtract = require('tld-extract').parse_host;
const providers = { const providers = {
PorkBun: require('./dns_provider/porkbun'),
DigitalOcean: require('./dns_provider/digitalocean'),
Cloudflare: require('./dns_provider/cloudflare'), Cloudflare: require('./dns_provider/cloudflare'),
DigitalOcean: require('./dns_provider/digitalocean'),
PorkBun: require('./dns_provider/porkbun'),
}; };
class Domain extends Table{ class Domain extends Table{
@@ -23,17 +23,22 @@ class Domain extends Table{
'updated_on': {default: function(){return (new Date).getTime()}, always: true}, 'updated_on': {default: function(){return (new Date).getTime()}, always: true},
'domain': {isRequired: true, type: 'string'}, 'domain': {isRequired: true, type: 'string'},
'dnsProvider_id': {isRequired: true, type: 'string'}, 'dnsProvider_id': {isRequired: true, type: 'string'},
'provider': {model: 'DnsProvider', rel:'one', localKey: 'dnsProvider_id'},
'zoneId': {isRequired: false, type: 'string'},
} }
static async get(data, ...args){ static async get(domain, ...args){
let instance = await super.get(data, ...args); try{
// instance.provider = (await DnsProvider.get(instance.dnsProvider_id)).provider; domain = tldExtract(domain).domain;
}catch{}
let instance = await super.get(domain, ...args);
return instance; return instance;
} }
static async getByProviderId(id){ static async getByProviderId(id, ...args){
let domains = await this.listDetail(); let domains = await this.listDetail(...args);
let results = []; let results = [];
for(let domain of domains){ for(let domain of domains){
if(domain.dnsProvider_id == id) results.push(domain); if(domain.dnsProvider_id == id) results.push(domain);
@@ -41,8 +46,21 @@ class Domain extends Table{
return results; return results;
} }
// toJSON(){
// return {
// ...super.toJSON(),
// provider: {
// displayName: this.provider.constructor.displayName,
// displayIconUni: this.provider.constructor.displayIconUni,
// displayIconHtml: this.provider.constructor.displayIconHtml,
// }
// }
// }
} }
Domain.register(ModelPs(Domain))
class DnsProvider extends Table{ class DnsProvider extends Table{
static _key = 'id'; static _key = 'id';
static _keyMap = { static _keyMap = {
@@ -53,7 +71,7 @@ class DnsProvider extends Table{
'id': {default: ()=>crypto.randomBytes(8).toString("hex")}, 'id': {default: ()=>crypto.randomBytes(8).toString("hex")},
'name': {isRequired: true, type: 'string'}, 'name': {isRequired: true, type: 'string'},
'dnsProvider': {isRequired: true, type: 'string'}, 'dnsProvider': {isRequired: true, type: 'string'},
'zoneId': {isRequired: false, type: 'string'}, 'domains': {model:'Domain', rel: 'many', remoteKey: 'dnsProvider_id'}
} }
static __intraModel(provider){ static __intraModel(provider){
@@ -74,23 +92,16 @@ class DnsProvider extends Table{
static async create(data, ...args){ static async create(data, ...args){
let __intraModel = this.__intraModel(data.dnsProvider) let __intraModel = this.__intraModel(data.dnsProvider)
let provider = new __intraModel.Provider(data); let provider = new __intraModel.Provider(data, ...args);
let res = await provider.listDomains();
let instance = await super.create.call(__intraModel, data); return await super.create.call(__intraModel, data, ...args);
if(data.noUpdate !== false) await instance.updateDomains();
return instance;
} }
static async get(data, ...args){ static async get(data, ...args){
let instance = await super.get(data); let instance = await super.get(data, ...args);
let __intraModel = this.__intraModel(instance.dnsProvider) let __intraModel = this.__intraModel(instance.dnsProvider)
instance = await super.get.call(__intraModel, data); return await super.get.call(__intraModel, data, ...args);
instance.provider = new __intraModel.Provider(instance);
return instance;
} }
static listProviders(){ static listProviders(){
@@ -138,24 +149,33 @@ class DnsProvider extends Table{
toJSON(){ toJSON(){
return { return {
...super.toJSON(), ...super.toJSON(),
displayName: this.provider.constructor.displayName, ...this.constructor.Provider.toJSON()
displayIconHtml: this.provider.constructor.displayIconHtml,
displayIconUni: this.provider.constructor.displayIconUni,
}; };
} }
} }
Domain = ModelPs(Domain); DnsProvider.register(ModelPs(DnsProvider))
DnsProvider = ModelPs(DnsProvider);
module.exports = {Domain, DnsProvider};
if(require.main === module){(async function(){try{ if(require.main === module){(async function(){try{
const conf = require('../conf'); const conf = require('../conf');
console.log(Table.models)
// console.log(await Domain.listDetail())
// console.log(JSON.stringify(await Domain.get('ipa.wtf'), null, 2))
// console.log(JSON.stringify(await Domain.listDetail() ,null, 2))
console.log(JSON.stringify(await Table.models.DnsProvider.listDetail() ,null, 2))
// console.log(await Domain.getByProviderId('e8443e03ac503c7b'));
// console.log(aw) // console.log(aw)
process.exit(0)
}catch(error){ }catch(error){
console.log('IIFE Error:', error); console.log('IIFE Error:', error);
}})()} }})()}
+7 -6
View File
@@ -1,28 +1,29 @@
'use strict'; 'use strict';
const axios = require('axios'); const axios = require('axios');
const {dnsErrors} = require('./common'); const {dnsErrors, DnsApi} = require('./common');
//like the options obj will always use domain data and type //like the options obj will always use domain data and type
// change content to data // change content to data
// change name to domain // change name to domain
class CloudFlare{ class CloudFlare extends DnsApi{
static _keyMap = { static _keyMap = {
token: {isRequired: true, type: 'string', isPrivate: true, displayName: 'API Token'}, token: {isRequired: true, type: 'string', isPrivate: true, displayName: 'API Token'},
} }
static displayName = 'CloudFlare' static displayName = 'CloudFlare'
static displayIconHtml = ` static displayIconHtml = `
<svg width="32px" height="32px" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"> <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<circle cx="512" cy="512" r="512" style="fill:#0080ff"/> <circle cx="512" cy="512" r="512" style="fill:#f38020"/>
<path d="m273.8 669.2-.1-63.7h63.7v63.7h76v-98.8h98.8v98.5c105.1-.1 186.2-104.1 146.1-214.6-14.9-40.9-47.6-73.6-88.5-88.4-110.7-40.2-214.7 41.2-214.7 146.3H256c0-167.5 161.8-298 337.4-243.2 76.8 24 137.7 84.9 161.6 161.6C809.9 606.2 679.4 768 511.9 768v-98.8h-98.6v75.9h-75.9v-75.9h-63.6z" style="fill:#fff"/> <path d="M608.2 592.4c3.1-10.8 1.9-20.7-3.3-28.1-4.8-6.7-12.9-10.6-22.6-11.1l-184.7-2.4c-1.1 0-2.2-.6-2.8-1.5-.6-.9-.7-2.1-.4-3.3.6-1.8 2.4-3.2 4.3-3.3l186.4-2.4c22.1-1 46.1-18.9 54.5-40.8l10.6-27.8c.5-1.2.6-2.4.3-3.6-12-54.3-60.5-94.8-118.4-94.8-53.4 0-98.7 34.5-114.9 82.4-10.5-7.8-23.9-12-38.3-10.6-25.7 2.5-46.2 23.1-48.8 48.8-.6 6.6-.1 13.1 1.4 19.1-41.9 1.2-75.3 35.4-75.3 77.6 0 3.7.3 7.5.8 11.2.3 1.8 1.8 3.1 3.6 3.1h340.9c1.9 0 3.8-1.4 4.3-3.3l2.4-9.2zM667 473.7c-1.6 0-3.4 0-5.1.2-1.2 0-2.2.9-2.7 2.1l-7.2 25c-3.1 10.8-2 20.7 3.3 28.1 4.8 6.7 12.9 10.6 22.7 11.1l39.3 2.4c1.2 0 2.3.6 2.8 1.5.6.9.7 2.3.5 3.3-.6 1.8-2.4 3.2-4.4 3.3l-41 2.4c-22.2 1-46 18.9-54.5 40.8l-3 7.6c-.6 1.5.5 3 2.1 3h140.8c1.6 0 3.1-1 3.6-2.7 2.4-8.7 3.7-17.9 3.7-27.3 0-55.5-45.3-100.8-101-100.8" style="fill:#fff"/>
</svg>` </svg>`
// Cloud icon for cloudflare // Cloud icon for cloudflare
static displayIconUni = '&#xf0c2;' static displayIconUni = '&#xf0c2;'
constructor(token){ constructor(token){
super()
this.token = token.token || token; this.token = token.token || token;
} }
+46 -1
View File
@@ -19,7 +19,52 @@ let dnsErrors = {
}, },
} }
class DnsApi{
errors = {
unauthorized(instance){
let error = new Error('UnauthorizedDnsApi');
error.name = 'UnauthorizedDnsApi';
error.message = `Unauthorized call to ${instance.constructor ? instance.constructor.name : instance.name}`;
error.status = 424;
return error;
},
other(instance, status, message){
let error = new Error('OtherDnsApiError');
error.name = 'OtherDnsApiError';
error.message = `DNS API Error ${instance.constructor ? instance.constructor.name : instance.name}: `;
error.status = 424;
return error;
},
}
static info(){
let svgDataUrl = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(this.displayIconHtml)
.replace(/'/g, '%27')
.replace(/"/g, '%22')}`
return {
displayName: this.displayName,
displayIconUni: this.displayIconUni,
displayIconHtml: svgDataUrl,
fields: this._keyMap,
}
}
static toJSON(){
return {
...this.info(),
}
}
toJSON(){
return this.constructor.toJSON()
}
}
module.exports = { module.exports = {
dnsErrors dnsErrors,
DnsApi,
}; };
+4 -3
View File
@@ -1,16 +1,16 @@
'use strict'; 'use strict';
const axios = require('axios'); const axios = require('axios');
const {dnsErrors} = require('./common'); const {dnsErrors, DnsApi} = require('./common');
class DigitalOcean{ class DigitalOcean extends DnsApi{
static _keyMap = { static _keyMap = {
token: {isRequired: true, type: 'string', isPrivate: true, displayName: 'API Token'}, token: {isRequired: true, type: 'string', isPrivate: true, displayName: 'API Token'},
} }
static displayName = 'DigitalOcean' static displayName = 'DigitalOcean'
static displayIconHtml = ` static displayIconHtml = `
<svg width="32px" height="32px" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"> <svg height="100%" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<circle cx="512" cy="512" r="512" style="fill:#0080ff"/> <circle cx="512" cy="512" r="512" style="fill:#0080ff"/>
<path d="m273.8 669.2-.1-63.7h63.7v63.7h76v-98.8h98.8v98.5c105.1-.1 186.2-104.1 146.1-214.6-14.9-40.9-47.6-73.6-88.5-88.4-110.7-40.2-214.7 41.2-214.7 146.3H256c0-167.5 161.8-298 337.4-243.2 76.8 24 137.7 84.9 161.6 161.6C809.9 606.2 679.4 768 511.9 768v-98.8h-98.6v75.9h-75.9v-75.9h-63.6z" style="fill:#fff"/> <path d="m273.8 669.2-.1-63.7h63.7v63.7h76v-98.8h98.8v98.5c105.1-.1 186.2-104.1 146.1-214.6-14.9-40.9-47.6-73.6-88.5-88.4-110.7-40.2-214.7 41.2-214.7 146.3H256c0-167.5 161.8-298 337.4-243.2 76.8 24 137.7 84.9 161.6 161.6C809.9 606.2 679.4 768 511.9 768v-98.8h-98.6v75.9h-75.9v-75.9h-63.6z" style="fill:#fff"/>
</svg>` </svg>`
@@ -18,6 +18,7 @@ class DigitalOcean{
static displayIconUni = '&#xf391;' static displayIconUni = '&#xf391;'
constructor(token){ constructor(token){
super()
this.token = token.token || token; this.token = token.token || token;
} }
+7 -9
View File
@@ -1,18 +1,19 @@
'use strict'; 'use strict';
const axios = require('axios'); const axios = require('axios');
const {dnsErrors} = require('./common'); const {dnsErrors, DnsApi} = require('./common');
class PorkBun{ class PorkBun extends DnsApi{
static _keyMap = { static _keyMap = {
'apiKey': {isRequired: true, type: 'string', isPrivate: true, displayName: 'API key'}, 'apiKey': {isRequired: true, type: 'string', isPrivate: true, displayName: 'API key'},
'secretApiKey': {isRequired: true, type: 'string', isPrivate: true, displayName: 'API Secret key'}, 'secretApiKey': {isRequired: true, type: 'string', isPrivate: true, displayName: 'API Secret key'},
} }
static displayName = 'DigitalOcean'; static displayName = 'PorkBun';
static displayIconUni = '&#xf7e5';
static displayIconHtml = ` static displayIconHtml = `
<svg width="32px" height="32px" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"> <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<style> <style>
.st1{fill:#fff} .st1{fill:#fff}
</style> </style>
@@ -24,12 +25,9 @@ class PorkBun{
</g> </g>
</g> </g>
</svg>` </svg>`
// '<i class="fa-solid fa-bacon"></i>';
static displayIconUni = '&#xf7e5';
baseUrl = 'https://api.porkbun.com/api/json/v3';
constructor(args){ constructor(args){
super()
this.apiKey = args.apiKey; this.apiKey = args.apiKey;
this.secretApiKey = args.secretApiKey; this.secretApiKey = args.secretApiKey;
} }
@@ -42,7 +40,7 @@ class PorkBun{
secretapikey: this.secretApiKey, secretapikey: this.secretApiKey,
apikey: this.apiKey, apikey: this.apiKey,
}; };
res = await axios.post(`${this.baseUrl}${url}`, data); res = await axios.post(`'https://api.porkbun.com/api/json/v3'${url}`, data);
return res; return res;
}catch(error){ }catch(error){
+3 -7
View File
@@ -1,18 +1,12 @@
'use strict'; 'use strict';
const Table = require('../utils/redis_model'); const Table = require('.');
const ModelPs = require('../utils/model_pubsub'); const ModelPs = require('../utils/model_pubsub');
const tldExtract = require('tld-extract').parse_host; const tldExtract = require('tld-extract').parse_host;
const PorkBun = require('./dns_provider/porkbun');
const LetsEncrypt = require('../utils/letsencrypt'); const LetsEncrypt = require('../utils/letsencrypt');
const conf = require('../conf'); const conf = require('../conf');
// let porkBun = new PorkBun(conf.porkBun.apiKey, conf.porkBun.secretApiKey);
// let letsEncrypt = new LetsEncrypt({
// directoryUrl: LetsEncrypt.AcmeClient.directory.letsencrypt.staging,
// });
class Host extends Table{ class Host extends Table{
static _key = 'host'; static _key = 'host';
@@ -31,6 +25,7 @@ class Host extends Table{
'wildcard_status': {isRequired: false, type: 'string', min: 3, max: 500, default: 'Requesting'}, '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},
'wildcard_expires': {isRequired: false, type: 'number'}, 'wildcard_expires': {isRequired: false, type: 'number'},
'domain': {model: 'Domain', rel: 'one'},
} }
static lookUpObj = {}; static lookUpObj = {};
@@ -322,6 +317,7 @@ class Host extends Table{
return true; return true;
} }
} }
Host.register(ModelPs(Host))
class Cached extends Table{ class Cached extends Table{
+9
View File
@@ -0,0 +1,9 @@
'use strict';
const Table = require('../utils/redis_model')
module.exports = Table;
require('./dns_provider');
require('./host');
require('./token');
require('./user');
+10 -11
View File
@@ -1,7 +1,6 @@
'use strict'; 'use strict';
const Table = require('../utils/redis_model'); const Table = require('.');
const {User} = require('./user');
const UUID = function b(a){return a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,b)}; const UUID = function b(a){return a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,b)};
@@ -11,8 +10,8 @@ class Token extends Table{
'created_by': {isRequired: true, type: 'string', min: 3, max: 500}, 'created_by': {isRequired: true, type: 'string', min: 3, max: 500},
'created_on': {default: function(){return (new Date).getTime()}}, 'created_on': {default: function(){return (new Date).getTime()}},
'updated_on': {default: function(){return (new Date).getTime()}, always: true}, 'updated_on': {default: function(){return (new Date).getTime()}, always: true},
'token': {default: UUID, type: 'string', min: 36, max: 36}, 'token': {default: UUID, type: 'string', min: 36, max: 36, isPrivate: true},
'is_valid': {default: true, type: 'boolean'} 'is_valid': {default: true, type: 'boolean'},
} }
constructor(...args){ constructor(...args){
@@ -28,13 +27,12 @@ class Token extends Table{
} }
} }
class AuthToken extends Token{ Token.register();
constructor(...args){
super(...args);
}
async getUser(){ class AuthToken extends Token{
return await User.get(this.created_by); static _keyMap = {
...super._keyMap,
user: {model: 'User', rel: 'one', localKey: 'created_by'},
} }
static async create(data){ static async create(data){
@@ -42,8 +40,8 @@ class AuthToken extends Token{
return super.create(data) return super.create(data)
} }
} }
AuthToken.register();
class InviteToken extends Token{ class InviteToken extends Token{
static _keyMap = { static _keyMap = {
@@ -66,5 +64,6 @@ class InviteToken extends Token{
} }
} }
} }
InviteToken.register();
module.exports = {Token, InviteToken, AuthToken}; module.exports = {Token, InviteToken, AuthToken};
+1 -4
View File
@@ -58,12 +58,9 @@ class User extends Table{
throw error; throw error;
} }
}; };
} }
module.exports = {User}; User.register();
(async function(){ (async function(){
var defaultUser = 'proxyadmin2' var defaultUser = 'proxyadmin2'
+2 -1
View File
@@ -264,7 +264,7 @@ MIT license
return { return {
...this.__parseData(data), ...this.__parseData(data),
nestedTemplates: this.__parseNestedTemplates(index, data), nestedTemplates: this.__parseNestedTemplates(index, data),
_parent: this.__jqParent ? $.scope[this.__jqParent][this.__jqParentIndex] : undefined, _parent: this._parentData,
}; };
}; };
@@ -307,6 +307,7 @@ MIT license
result.__index = $this.attr('jq-repeat-index'); result.__index = $this.attr('jq-repeat-index');
if($this.attr('jq-repeat-parent')){ if($this.attr('jq-repeat-parent')){
result._parentData = $.scope[$this.attr('jq-repeat-parent')][$this.attr('jq-repeat-parent-index')]
result.__jqParent = $this.attr('jq-repeat-parent'); result.__jqParent = $this.attr('jq-repeat-parent');
result.__jqParentIndex = $this.attr('jq-repeat-parent-index'); result.__jqParentIndex = $this.attr('jq-repeat-parent-index');
} }
+12 -1
View File
@@ -1,7 +1,7 @@
'use strict'; 'use strict';
const router = require('express').Router(); const router = require('express').Router();
const {DnsProvider, Domain} = require('../models/dnsProvider'); const {DnsProvider, Domain} = require('../models').models;
const Model = DnsProvider; const Model = DnsProvider;
@@ -49,6 +49,7 @@ router.get('/domain', async function(req, res, next){
} }
}); });
router.get('/domain/byProvider/:item', async function(req, res, next){ router.get('/domain/byProvider/:item', async function(req, res, next){
try{ try{
return res.json({ return res.json({
@@ -68,6 +69,16 @@ router.post('/domain/refresh/:item', async function(req, res, next){
} }
}) })
router.get('/domain/:item', async function(req, res, next){
try{
return res.json({
results: [await Domain.get(req.params.item)]
});
}catch(error){
return next(error);
}
});
router.get('/:item', async function(req, res, next){ router.get('/:item', async function(req, res, next){
try{ try{
+2 -2
View File
@@ -1,14 +1,14 @@
'use strict'; 'use strict';
const router = require('express').Router(); const router = require('express').Router();
const {Host} = require('../models/host'); const {Host, Domain} = require('../models').models;
const Model = Host; const Model = Host;
router.get('/', async function(req, res, next){ router.get('/', async function(req, res, next){
try{ try{
return res.json({ return res.json({
hosts: await Model[req.query.detail ? "listDetail" : "list"]() results: await Model[req.query.detail ? "listDetail" : "list"](),
}); });
}catch(error){ }catch(error){
return next(error); return next(error);
+1 -1
View File
@@ -1,7 +1,7 @@
'use strict'; 'use strict';
const router = require('express').Router(); const router = require('express').Router();
const {User} = require('../models/user'); const {User} = require('../models').models;
router.get('/', async function(req, res, next){ router.get('/', async function(req, res, next){
try{ try{
+3 -5
View File
@@ -22,10 +22,8 @@ function ModelPs(model){
} }
return new Proxy(model, { return new Proxy(model, {
construct(target, args) { construct(target, args, newTarget) {
let instance = ModelPs(new model(...args)); return ModelPs(Reflect.construct(target, args, newTarget))
return instance;
}, },
get(target, propKey, receiver) { get(target, propKey, receiver) {
if(propKey == 'constructor') return target.constructor; if(propKey == 'constructor') return target.constructor;
@@ -46,7 +44,7 @@ function ModelPs(model){
} }
return res; return res;
}catch(error){ }catch(error){
console.log("grrrr") console.log("grrrr", error)
} }
} }
} else { } else {
+69 -9
View File
@@ -5,14 +5,37 @@ const objValidate = require('../utils/object_validate');
const conf = require('../conf'); const conf = require('../conf');
const client = createClient({}); const client = createClient({});
client.connect() client.connect();
function redisPrefix(key){ function redisPrefix(key){
return `${conf.redis.prefix}${key}`; return `${conf.redis.prefix}${key}`;
} }
class QueryHelper{
hisroty = []
constructor(orgin){
this.orgin = orgin
this.hisroty.push(orgin.constructor.name);
}
static isNotCycle(modleName, queryHelper){
if(queryHelper instanceof this){
if(queryHelper.hisroty.includes(modleName)){
return true;
}
queryHelper.hisroty.push(modleName)
}
}
}
class Table{ class Table{
static redisClient = client; static redisClient = client;
static models = {}
static register = function(Model){
Model = Model || this;
this.models[Model.name] = Model;
}
constructor(data){ constructor(data){
for(let key in data){ for(let key in data){
@@ -20,9 +43,8 @@ class Table{
} }
} }
static async get(index){ static async get(index, queryHelper){
try{ try{
if(typeof index === 'object'){ if(typeof index === 'object'){
index = index[this._key]; index = index[this._key];
} }
@@ -43,13 +65,37 @@ class Table{
// back to native values. // back to native values.
result = objValidate.parseFromString(this._keyMap, result); result = objValidate.parseFromString(this._keyMap, result);
return new this(result); let instance = new this(result);
await instance.buildRelations(queryHelper);
return instance;
}catch(error){ }catch(error){
throw error; throw error;
} }
} }
async buildRelations(queryHelper){
for(let [key, options] of Object.entries(this.constructor._keyMap)){
if(options.model){
let remoteModel = this.constructor.models[options.model]
try{
if(QueryHelper.isNotCycle(remoteModel.name, queryHelper)) continue;
if(options.rel === 'one'){
// console.log('relone:', this[key], queryHelper, remoteModel, await remoteModel.get(this[key], queryHelper || new QueryHelper(this)))
this[key] = await remoteModel.get(this[key] || this[options.localKey || this.constructor._key] , queryHelper || new QueryHelper(this))
}
if(options.rel === 'many'){
this[key] = await remoteModel.listDetail({
[options.remoteKey]: this[options.localKey || this.constructor._key],
},queryHelper || new QueryHelper(this))
}
}catch{}
}
}
}
static async exists(index){ static async exists(index){
if(typeof index === 'object'){ if(typeof index === 'object'){
index = index[this._key]; index = index[this._key];
@@ -73,12 +119,21 @@ class Table{
} }
} }
static async listDetail(){ static async listDetail(options, queryHelper){
// Return a list of the entries as instances. // Return a list of the entries as instances.
let out = []; let out = [];
for(let entry of await this.list()){ for(let entry of await this.list()){
out.push(await this.get(entry)); let instance = await this.get(entry, arguments[arguments.length - 1]);
if(!options) out.push(instance);
let matchCount = 0;
for(let option in options){
if(instance[option] === options[option] && ++matchCount === Object.keys(options).length){
out.push(instance);
break;
}
}
} }
return out; return out;
@@ -213,8 +268,9 @@ class Table{
toJSON(){ toJSON(){
let result = {}; let result = {};
for (const [key, keyProps] of Object.entries(this.constructor._keyMap)) { for (const [key, value] of Object.entries(this)) {
if(!keyProps.isPrivate) result[key] = this[key]; if(this.constructor._keyMap[key] && this.constructor._keyMap[key].isPrivate) continue;
result[key] = value;
} }
return result return result
@@ -222,6 +278,10 @@ class Table{
// return JSON.stringify(result); // return JSON.stringify(result);
} }
toString(){
return this[this.constructor._key];
}
} }
+5 -12
View File
@@ -59,16 +59,10 @@
$.scope.DnsProvider.parseData = function(row){ $.scope.DnsProvider.parseData = function(row){
row['created_on_text'] = moment(row['updated_on'], "x").fromNow(); row['created_on_text'] = moment(row['updated_on'], "x").fromNow();
row['domainsString'] = JSON.stringify(row.domains);
return row return row
}; };
(async function(){
})()
$(document).ready(async function(){ $(document).ready(async function(){
// Set the jq Templates // Set the jq Templates
$.scope.providerField.put = function(){}; $.scope.providerField.put = function(){};
@@ -102,7 +96,6 @@
$.scope[Model].update(pk, data) $.scope[Model].update(pk, data)
}catch{} }catch{}
}); });
}); });
</script> </script>
@@ -182,7 +175,7 @@
<li jq-repeat="DnsProvider" jq-repeat-index="id" style="display:none" class="list-group-item" onload="provider"> <li jq-repeat="DnsProvider" jq-repeat-index="id" style="display:none" class="list-group-item" onload="provider">
<div class="row"> <div class="row">
<div class="col-6"> <div class="col-6">
<h3>{{{ displayIconHtml }}} {{ dnsProvider }}</h3> <h3><img height="32px" src="{{ displayIconHtml }}"/> {{ dnsProvider }}</h3>
<h4>{{ name }}</h4> <h4>{{ name }}</h4>
</div> </div>
<div class="col-6 text-end"> <div class="col-6 text-end">
@@ -200,9 +193,9 @@
{{domain}} {{domain}}
</b> </b>
<script type="text/javascript"> <script type="text/javascript">
app.api.get(`dns/domain/byProvider/{{id}}`, function(error, domains){ try{
$.scope.Domain_{{id}}.push(...domains.results); $.scope.Domain_{{id}}.push(...{{{domainsString}}});
}); }catch{}
</script> </script>
</div> </div>
</li> </li>
+9 -5
View File
@@ -62,10 +62,10 @@
} }
function hostPopulate(){ function hostPopulate(){
app.host.list(function(error, res){ app.api.get('host?detail=1&provider=1', function(error, res){
if(error) return app.util.actionMessage(error, $.scope.hosts.$this, 'danger'); if(error) return app.util.actionMessage(error, $.scope.hosts.$this, 'danger');
for(let host of res){ for(let host of res.results){
$.scope.hosts.push(hostParseRow(host)); $.scope.hosts.push(hostParseRow(host));
} }
@@ -150,9 +150,9 @@
$el.slideUp(); $el.slideUp();
}; };
// app.subscribe(/^model:Host/, function(data, topic){ app.subscribe(/^model:Host/, function(data, topic){
// console.log(topic, data); console.log(topic, data);
// }); });
app.subscribe(/^model:Host:create/, function(data, topic){ app.subscribe(/^model:Host:create/, function(data, topic){
let [a,b, action, host] = topic.split(':'); let [a,b, action, host] = topic.split(':');
@@ -391,6 +391,10 @@
<a target="_blank" href="{{ forcessl_text }}{{ host }}"> <a target="_blank" href="{{ forcessl_text }}{{ host }}">
{{{ forcessl_text }}}{{ host }} {{{ forcessl_text }}}{{ host }}
</a> </a>
{{#domain.provider}}
<br />
<img width="24px" src="{{displayIconHtml}}" /> {{displayName}}
{{/domain.provider}}
</td> </td>
<td> <td>
{{{ targetssl_text }}}{{ ip }}:{{ targetPort }} {{{ targetssl_text }}}{{ ip }}:{{ targetPort }}
-44
View File
@@ -31,55 +31,11 @@
</head> </head>
<body> <body>
<!-- <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="#">Navbar</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="#">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Link</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Dropdown
</a>
<ul class="dropdown-menu" aria-labelledby="navbarDropdown">
<li><a class="dropdown-item" href="#">Action</a></li>
<li><a class="dropdown-item" href="#">Another action</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#">Something else here</a></li>
</ul>
</li>
<li class="nav-item">
<a class="nav-link disabled">Disabled</a>
</li>
</ul>
<form class="d-flex" role="search">
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
<button class="btn btn-outline-success" type="submit">Search</button>
</form>
</div>
</div>
</nav> -->
<nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark"> <nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
<a class="navbar-brand" href="#">Dynamic Proxy <%- title %></a> <a class="navbar-brand" href="#">Dynamic Proxy <%- title %></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
<!-- <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button> -->
<div class="collapse navbar-collapse justify-content-end" id="navbarSupportedContent"> <div class="collapse navbar-collapse justify-content-end" id="navbarSupportedContent">
<ul class="navbar-nav top-nav"> <ul class="navbar-nav top-nav">
<li class="nav-item"> <li class="nav-item">