mirror of
https://github.com/xiaoxinpro/nginx-proxy-manager-zh.git
synced 2025-01-22 12:58:13 -05:00
WIP
This commit is contained in:
parent
c5450eaa1a
commit
c629deb56c
@ -12,6 +12,8 @@ services:
|
||||
volumes:
|
||||
- ./data/letsencrypt:/etc/letsencrypt
|
||||
- .:/srv/app
|
||||
- ~/.yarnrc:/root/.yarnrc
|
||||
- ~/.npmrc:/root/.npmrc
|
||||
depends_on:
|
||||
- db
|
||||
links:
|
||||
|
@ -18,6 +18,8 @@
|
||||
"jquery": "^3.3.1",
|
||||
"jquery-mask-plugin": "^1.14.15",
|
||||
"jquery-serializejson": "^2.8.1",
|
||||
"messageformat": "^2.0.2",
|
||||
"messageformat-loader": "^0.7.0",
|
||||
"mini-css-extract-plugin": "^0.4.0",
|
||||
"moment": "^2.22.2",
|
||||
"node-sass": "^4.9.0",
|
||||
|
@ -26,7 +26,7 @@ const internalDeadHost = {
|
||||
.where('is_deleted', 0)
|
||||
.groupBy('id')
|
||||
.omit(['is_deleted'])
|
||||
.orderBy('domain_name', 'ASC');
|
||||
.orderBy('domain_names', 'ASC');
|
||||
|
||||
if (access_data.permission_visibility !== 'all') {
|
||||
query.andWhere('owner_user_id', access.token.get('attrs').id);
|
||||
@ -35,7 +35,7 @@ const internalDeadHost = {
|
||||
// Query is used for searching
|
||||
if (typeof search_query === 'string') {
|
||||
query.where(function () {
|
||||
this.where('domain_name', 'like', '%' + search_query + '%');
|
||||
this.where('domain_names', 'like', '%' + search_query + '%');
|
||||
});
|
||||
}
|
||||
|
||||
|
96
src/backend/internal/host.js
Normal file
96
src/backend/internal/host.js
Normal file
@ -0,0 +1,96 @@
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
const error = require('../lib/error');
|
||||
const proxyHostModel = require('../models/proxy_host');
|
||||
const redirectionHostModel = require('../models/redirection_host');
|
||||
const deadHostModel = require('../models/dead_host');
|
||||
|
||||
const internalHost = {
|
||||
|
||||
/**
|
||||
* Internal use only, checks to see if the domain is already taken by any other record
|
||||
*
|
||||
* @param {String} hostname
|
||||
* @param {String} [ignore_type] 'proxy', 'redirection', 'dead'
|
||||
* @param {Integer} [ignore_id] Must be supplied if type was also supplied
|
||||
* @returns {Promise}
|
||||
*/
|
||||
isHostnameTaken: function (hostname, ignore_type, ignore_id) {
|
||||
let promises = [
|
||||
proxyHostModel
|
||||
.query()
|
||||
.where('is_deleted', 0)
|
||||
.andWhere('domain_names', 'like', '%' + hostname + '%'),
|
||||
redirectionHostModel
|
||||
.query()
|
||||
.where('is_deleted', 0)
|
||||
.andWhere('domain_names', 'like', '%' + hostname + '%'),
|
||||
deadHostModel
|
||||
.query()
|
||||
.where('is_deleted', 0)
|
||||
.andWhere('domain_names', 'like', '%' + hostname + '%')
|
||||
];
|
||||
|
||||
return Promise.all(promises)
|
||||
.then(promises_results => {
|
||||
let is_taken = false;
|
||||
|
||||
if (promises_results[0]) {
|
||||
// Proxy Hosts
|
||||
if (internalHost._checkHostnameRecordsTaken(hostname, promises_results[0], ignore_type === 'proxy' && ignore_id ? ignore_id : 0)) {
|
||||
is_taken = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (promises_results[1]) {
|
||||
// Redirection Hosts
|
||||
if (internalHost._checkHostnameRecordsTaken(hostname, promises_results[1], ignore_type === 'redirection' && ignore_id ? ignore_id : 0)) {
|
||||
is_taken = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (promises_results[1]) {
|
||||
// Dead Hosts
|
||||
if (internalHost._checkHostnameRecordsTaken(hostname, promises_results[2], ignore_type === 'dead' && ignore_id ? ignore_id : 0)) {
|
||||
is_taken = true;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
hostname: hostname,
|
||||
is_taken: is_taken
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Private call only
|
||||
*
|
||||
* @param {String} hostname
|
||||
* @param {Array} existing_rows
|
||||
* @param {Integer} [ignore_id]
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
_checkHostnameRecordsTaken: function (hostname, existing_rows, ignore_id) {
|
||||
let is_taken = false;
|
||||
|
||||
if (existing_rows && existing_rows.length) {
|
||||
existing_rows.map(function (existing_row) {
|
||||
existing_row.domain_names.map(function (existing_hostname) {
|
||||
// Does this domain match?
|
||||
if (existing_hostname.toLowerCase() === hostname.toLowerCase()) {
|
||||
if (!ignore_id || ignore_id !== existing_row.id) {
|
||||
is_taken = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return is_taken;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
module.exports = internalHost;
|
@ -3,6 +3,7 @@
|
||||
const _ = require('lodash');
|
||||
const error = require('../lib/error');
|
||||
const proxyHostModel = require('../models/proxy_host');
|
||||
const internalHost = require('./host');
|
||||
|
||||
function omissions () {
|
||||
return ['is_deleted'];
|
||||
@ -16,60 +17,39 @@ const internalProxyHost = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
create: (access, data) => {
|
||||
let auth = data.auth || null;
|
||||
delete data.auth;
|
||||
|
||||
data.avatar = data.avatar || '';
|
||||
data.roles = data.roles || [];
|
||||
|
||||
if (typeof data.is_disabled !== 'undefined') {
|
||||
data.is_disabled = data.is_disabled ? 1 : 0;
|
||||
}
|
||||
|
||||
return access.can('proxy_hosts:create', data)
|
||||
.then(() => {
|
||||
data.avatar = gravatar.url(data.email, {default: 'mm'});
|
||||
.then(access_data => {
|
||||
// Get a list of the domain names and check each of them against existing records
|
||||
let domain_name_check_promises = [];
|
||||
|
||||
return userModel
|
||||
data.domain_names.map(function (domain_name) {
|
||||
domain_name_check_promises.push(internalHost.isHostnameTaken(domain_name));
|
||||
});
|
||||
|
||||
return Promise.all(domain_name_check_promises)
|
||||
.then(check_results => {
|
||||
check_results.map(function (result) {
|
||||
if (result.is_taken) {
|
||||
throw new error.ValidationError(result.hostname + ' is already in use');
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
// At this point the domains should have been checked
|
||||
data.owner_user_id = access.token.get('attrs').id;
|
||||
|
||||
if (typeof data.meta === 'undefined') {
|
||||
data.meta = {};
|
||||
}
|
||||
|
||||
return proxyHostModel
|
||||
.query()
|
||||
.omit(omissions())
|
||||
.insertAndFetch(data);
|
||||
})
|
||||
.then(user => {
|
||||
if (auth) {
|
||||
return authModel
|
||||
.query()
|
||||
.insert({
|
||||
user_id: user.id,
|
||||
type: auth.type,
|
||||
secret: auth.secret,
|
||||
meta: {}
|
||||
})
|
||||
.then(() => {
|
||||
return user;
|
||||
});
|
||||
} else {
|
||||
return user;
|
||||
}
|
||||
})
|
||||
.then(user => {
|
||||
// Create permissions row as well
|
||||
let is_admin = data.roles.indexOf('admin') !== -1;
|
||||
|
||||
return userPermissionModel
|
||||
.query()
|
||||
.insert({
|
||||
user_id: user.id,
|
||||
visibility: is_admin ? 'all' : 'user',
|
||||
proxy_hosts: 'manage',
|
||||
redirection_hosts: 'manage',
|
||||
dead_hosts: 'manage',
|
||||
streams: 'manage',
|
||||
access_lists: 'manage'
|
||||
})
|
||||
.then(() => {
|
||||
return internalProxyHost.get(access, {id: user.id, expand: ['permissions']});
|
||||
});
|
||||
.then(row => {
|
||||
return _.omit(row, omissions());
|
||||
});
|
||||
},
|
||||
|
||||
@ -82,63 +62,49 @@ const internalProxyHost = {
|
||||
* @return {Promise}
|
||||
*/
|
||||
update: (access, data) => {
|
||||
if (typeof data.is_disabled !== 'undefined') {
|
||||
data.is_disabled = data.is_disabled ? 1 : 0;
|
||||
}
|
||||
|
||||
return access.can('proxy_hosts:update', data.id)
|
||||
.then(() => {
|
||||
.then(access_data => {
|
||||
// Get a list of the domain names and check each of them against existing records
|
||||
let domain_name_check_promises = [];
|
||||
|
||||
// Make sure that the user being updated doesn't change their email to another user that is already using it
|
||||
// 1. get user we want to update
|
||||
return internalProxyHost.get(access, {id: data.id})
|
||||
.then(user => {
|
||||
|
||||
// 2. if email is to be changed, find other users with that email
|
||||
if (typeof data.email !== 'undefined') {
|
||||
data.email = data.email.toLowerCase().trim();
|
||||
|
||||
if (user.email !== data.email) {
|
||||
return internalProxyHost.isEmailAvailable(data.email, data.id)
|
||||
.then(available => {
|
||||
if (!available) {
|
||||
throw new error.ValidationError('Email address already in use - ' + data.email);
|
||||
}
|
||||
|
||||
return user;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// No change to email:
|
||||
return user;
|
||||
if (typeof data.domain_names !== 'undefined') {
|
||||
data.domain_names.map(function (domain_name) {
|
||||
domain_name_check_promises.push(internalHost.isHostnameTaken(domain_name, 'proxy', data.id));
|
||||
});
|
||||
})
|
||||
.then(user => {
|
||||
if (user.id !== data.id) {
|
||||
// Sanity check that something crazy hasn't happened
|
||||
throw new error.InternalValidationError('User could not be updated, IDs do not match: ' + user.id + ' !== ' + data.id);
|
||||
|
||||
return Promise.all(domain_name_check_promises)
|
||||
.then(check_results => {
|
||||
check_results.map(function (result) {
|
||||
if (result.is_taken) {
|
||||
throw new error.ValidationError(result.hostname + ' is already in use');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
data.avatar = gravatar.url(data.email || user.email, {default: 'mm'});
|
||||
|
||||
return userModel
|
||||
.query()
|
||||
.omit(omissions())
|
||||
.patchAndFetchById(user.id, data)
|
||||
.then(saved_user => {
|
||||
return _.omit(saved_user, omissions());
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
return internalProxyHost.get(access, {id: data.id});
|
||||
})
|
||||
.then(row => {
|
||||
if (row.id !== data.id) {
|
||||
// Sanity check that something crazy hasn't happened
|
||||
throw new error.InternalValidationError('Proxy Host could not be updated, IDs do not match: ' + row.id + ' !== ' + data.id);
|
||||
}
|
||||
|
||||
return proxyHostModel
|
||||
.query()
|
||||
.omit(omissions())
|
||||
.patchAndFetchById(row.id, data)
|
||||
.then(saved_row => {
|
||||
return _.omit(saved_row, omissions());
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Access} access
|
||||
* @param {Object} [data]
|
||||
* @param {Integer} [data.id] Defaults to the token user
|
||||
* @param {Object} data
|
||||
* @param {Integer} data.id
|
||||
* @param {Array} [data.expand]
|
||||
* @param {Array} [data.omit]
|
||||
* @return {Promise}
|
||||
@ -153,14 +119,18 @@ const internalProxyHost = {
|
||||
}
|
||||
|
||||
return access.can('proxy_hosts:get', data.id)
|
||||
.then(() => {
|
||||
let query = userModel
|
||||
.then(access_data => {
|
||||
let query = proxyHostModel
|
||||
.query()
|
||||
.where('is_deleted', 0)
|
||||
.andWhere('id', data.id)
|
||||
.allowEager('[permissions]')
|
||||
.first();
|
||||
|
||||
if (access_data.permission_visibility !== 'all') {
|
||||
query.andWhere('owner_user_id', access.token.get('attrs').id);
|
||||
}
|
||||
|
||||
// Custom omissions
|
||||
if (typeof data.omit !== 'undefined' && data.omit !== null) {
|
||||
query.omit(data.omit);
|
||||
@ -193,19 +163,14 @@ const internalProxyHost = {
|
||||
.then(() => {
|
||||
return internalProxyHost.get(access, {id: data.id});
|
||||
})
|
||||
.then(user => {
|
||||
if (!user) {
|
||||
.then(row => {
|
||||
if (!row) {
|
||||
throw new error.ItemNotFoundError(data.id);
|
||||
}
|
||||
|
||||
// Make sure user can't delete themselves
|
||||
if (user.id === access.token.get('attrs').id) {
|
||||
throw new error.PermissionError('You cannot delete yourself.');
|
||||
}
|
||||
|
||||
return userModel
|
||||
return proxyHostModel
|
||||
.query()
|
||||
.where('id', user.id)
|
||||
.where('id', row.id)
|
||||
.patch({
|
||||
is_deleted: 1
|
||||
});
|
||||
@ -231,7 +196,8 @@ const internalProxyHost = {
|
||||
.where('is_deleted', 0)
|
||||
.groupBy('id')
|
||||
.omit(['is_deleted'])
|
||||
.orderBy('domain_name', 'ASC');
|
||||
.allowEager('[owner,access_list]')
|
||||
.orderBy('domain_names', 'ASC');
|
||||
|
||||
if (access_data.permission_visibility !== 'all') {
|
||||
query.andWhere('owner_user_id', access.token.get('attrs').id);
|
||||
@ -240,7 +206,7 @@ const internalProxyHost = {
|
||||
// Query is used for searching
|
||||
if (typeof search_query === 'string') {
|
||||
query.where(function () {
|
||||
this.where('domain_name', 'like', '%' + search_query + '%');
|
||||
this.where('domain_names', 'like', '%' + search_query + '%');
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ const internalProxyHost = {
|
||||
.where('is_deleted', 0)
|
||||
.groupBy('id')
|
||||
.omit(['is_deleted'])
|
||||
.orderBy('domain_name', 'ASC');
|
||||
.orderBy('domain_names', 'ASC');
|
||||
|
||||
if (access_data.permission_visibility !== 'all') {
|
||||
query.andWhere('owner_user_id', access.token.get('attrs').id);
|
||||
@ -35,7 +35,7 @@ const internalProxyHost = {
|
||||
// Query is used for searching
|
||||
if (typeof search_query === 'string') {
|
||||
query.where(function () {
|
||||
this.where('domain_name', 'like', '%' + search_query + '%');
|
||||
this.where('domain_names', 'like', '%' + search_query + '%');
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -290,6 +290,7 @@ const internalUser = {
|
||||
.where('is_deleted', 0)
|
||||
.groupBy('id')
|
||||
.omit(['is_deleted'])
|
||||
.allowEager('[permissions]')
|
||||
.orderBy('name', 'ASC');
|
||||
|
||||
// Query is used for searching
|
||||
|
@ -301,8 +301,8 @@ module.exports = function (token_string) {
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
//logger.error(err.message);
|
||||
//logger.error(err.errors);
|
||||
logger.error(err.message);
|
||||
logger.error(err.errors);
|
||||
|
||||
throw new error.PermissionError('Permission Denied', err);
|
||||
});
|
||||
|
23
src/backend/lib/access/proxy_hosts-create.json
Normal file
23
src/backend/lib/access/proxy_hosts-create.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "roles#/definitions/admin"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["permission_proxy_hosts", "roles"],
|
||||
"properties": {
|
||||
"permission_proxy_hosts": {
|
||||
"$ref": "perms#/definitions/manage"
|
||||
},
|
||||
"roles": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": ["user"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
23
src/backend/lib/access/proxy_hosts-delete.json
Normal file
23
src/backend/lib/access/proxy_hosts-delete.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "roles#/definitions/admin"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["permission_proxy_hosts", "roles"],
|
||||
"properties": {
|
||||
"permission_proxy_hosts": {
|
||||
"$ref": "perms#/definitions/manage"
|
||||
},
|
||||
"roles": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": ["user"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
23
src/backend/lib/access/proxy_hosts-get.json
Normal file
23
src/backend/lib/access/proxy_hosts-get.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "roles#/definitions/admin"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["permission_proxy_hosts", "roles"],
|
||||
"properties": {
|
||||
"permission_proxy_hosts": {
|
||||
"$ref": "perms#/definitions/view"
|
||||
},
|
||||
"roles": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": ["user"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
23
src/backend/lib/access/proxy_hosts-update.json
Normal file
23
src/backend/lib/access/proxy_hosts-update.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "roles#/definitions/admin"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["permission_proxy_hosts", "roles"],
|
||||
"properties": {
|
||||
"permission_proxy_hosts": {
|
||||
"$ref": "perms#/definitions/manage"
|
||||
},
|
||||
"roles": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": ["user"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -67,7 +67,7 @@ exports.up = function (knex/*, Promise*/) {
|
||||
table.dateTime('modified_on').notNull();
|
||||
table.integer('owner_user_id').notNull().unsigned();
|
||||
table.integer('is_deleted').notNull().unsigned().defaultTo(0);
|
||||
table.string('domain_name').notNull();
|
||||
table.json('domain_names').notNull();
|
||||
table.string('forward_ip').notNull();
|
||||
table.integer('forward_port').notNull().unsigned();
|
||||
table.integer('access_list_id').notNull().unsigned().defaultTo(0);
|
||||
@ -88,7 +88,7 @@ exports.up = function (knex/*, Promise*/) {
|
||||
table.dateTime('modified_on').notNull();
|
||||
table.integer('owner_user_id').notNull().unsigned();
|
||||
table.integer('is_deleted').notNull().unsigned().defaultTo(0);
|
||||
table.string('domain_name').notNull();
|
||||
table.json('domain_names').notNull();
|
||||
table.string('forward_domain_name').notNull();
|
||||
table.integer('preserve_path').notNull().unsigned().defaultTo(0);
|
||||
table.integer('ssl_enabled').notNull().unsigned().defaultTo(0);
|
||||
@ -106,7 +106,7 @@ exports.up = function (knex/*, Promise*/) {
|
||||
table.dateTime('modified_on').notNull();
|
||||
table.integer('owner_user_id').notNull().unsigned();
|
||||
table.integer('is_deleted').notNull().unsigned().defaultTo(0);
|
||||
table.string('domain_name').notNull();
|
||||
table.json('domain_names').notNull();
|
||||
table.integer('ssl_enabled').notNull().unsigned().defaultTo(0);
|
||||
table.string('ssl_provider').notNull().defaultTo('');
|
||||
table.json('meta').notNull();
|
||||
|
52
src/backend/models/access_list.js
Normal file
52
src/backend/models/access_list.js
Normal file
@ -0,0 +1,52 @@
|
||||
// Objection Docs:
|
||||
// http://vincit.github.io/objection.js/
|
||||
|
||||
'use strict';
|
||||
|
||||
const db = require('../db');
|
||||
const Model = require('objection').Model;
|
||||
const User = require('./user');
|
||||
|
||||
Model.knex(db);
|
||||
|
||||
class AccessList extends Model {
|
||||
$beforeInsert () {
|
||||
this.created_on = Model.raw('NOW()');
|
||||
this.modified_on = Model.raw('NOW()');
|
||||
}
|
||||
|
||||
$beforeUpdate () {
|
||||
this.modified_on = Model.raw('NOW()');
|
||||
}
|
||||
|
||||
static get name () {
|
||||
return 'AccessList';
|
||||
}
|
||||
|
||||
static get tableName () {
|
||||
return 'access_list';
|
||||
}
|
||||
|
||||
static get jsonAttributes () {
|
||||
return ['meta'];
|
||||
}
|
||||
|
||||
static get relationMappings () {
|
||||
return {
|
||||
owner: {
|
||||
relation: Model.HasOneRelation,
|
||||
modelClass: User,
|
||||
join: {
|
||||
from: 'access_list.owner_user_id',
|
||||
to: 'user.id'
|
||||
},
|
||||
modify: function (qb) {
|
||||
qb.where('user.is_deleted', 0);
|
||||
qb.omit(['id', 'created_on', 'modified_on', 'is_deleted', 'email', 'roles']);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AccessList;
|
51
src/backend/models/access_list_auth.js
Normal file
51
src/backend/models/access_list_auth.js
Normal file
@ -0,0 +1,51 @@
|
||||
// Objection Docs:
|
||||
// http://vincit.github.io/objection.js/
|
||||
|
||||
'use strict';
|
||||
|
||||
const db = require('../db');
|
||||
const Model = require('objection').Model;
|
||||
|
||||
Model.knex(db);
|
||||
|
||||
class AccessListAuth extends Model {
|
||||
$beforeInsert () {
|
||||
this.created_on = Model.raw('NOW()');
|
||||
this.modified_on = Model.raw('NOW()');
|
||||
}
|
||||
|
||||
$beforeUpdate () {
|
||||
this.modified_on = Model.raw('NOW()');
|
||||
}
|
||||
|
||||
static get name () {
|
||||
return 'AccessListAuth';
|
||||
}
|
||||
|
||||
static get tableName () {
|
||||
return 'access_list_auth';
|
||||
}
|
||||
|
||||
static get jsonAttributes () {
|
||||
return ['meta'];
|
||||
}
|
||||
|
||||
static get relationMappings () {
|
||||
return {
|
||||
access_list: {
|
||||
relation: Model.HasOneRelation,
|
||||
modelClass: './access_list',
|
||||
join: {
|
||||
from: 'access_list_auth.access_list_id',
|
||||
to: 'access_list.id'
|
||||
},
|
||||
modify: function (qb) {
|
||||
qb.where('access_list.is_deleted', 0);
|
||||
qb.omit(['created_on', 'modified_on', 'is_deleted', 'access_list_id']);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AccessListAuth;
|
@ -27,6 +27,10 @@ class DeadHost extends Model {
|
||||
return 'dead_host';
|
||||
}
|
||||
|
||||
static get jsonAttributes () {
|
||||
return ['domain_names', 'meta'];
|
||||
}
|
||||
|
||||
static get relationMappings () {
|
||||
return {
|
||||
owner: {
|
||||
@ -38,7 +42,7 @@ class DeadHost extends Model {
|
||||
},
|
||||
modify: function (qb) {
|
||||
qb.where('user.is_deleted', 0);
|
||||
qb.omit(['created_on', 'modified_on', 'is_deleted', 'email', 'roles']);
|
||||
qb.omit(['id', 'created_on', 'modified_on', 'is_deleted', 'email', 'roles']);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -3,9 +3,10 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
const db = require('../db');
|
||||
const Model = require('objection').Model;
|
||||
const User = require('./user');
|
||||
const db = require('../db');
|
||||
const Model = require('objection').Model;
|
||||
const User = require('./user');
|
||||
const AccessList = require('./access_list');
|
||||
|
||||
Model.knex(db);
|
||||
|
||||
@ -13,10 +14,14 @@ class ProxyHost extends Model {
|
||||
$beforeInsert () {
|
||||
this.created_on = Model.raw('NOW()');
|
||||
this.modified_on = Model.raw('NOW()');
|
||||
this.domain_names.sort();
|
||||
}
|
||||
|
||||
$beforeUpdate () {
|
||||
this.modified_on = Model.raw('NOW()');
|
||||
if (typeof this.domain_names !== 'undefined') {
|
||||
this.domain_names.sort();
|
||||
}
|
||||
}
|
||||
|
||||
static get name () {
|
||||
@ -27,9 +32,13 @@ class ProxyHost extends Model {
|
||||
return 'proxy_host';
|
||||
}
|
||||
|
||||
static get jsonAttributes () {
|
||||
return ['domain_names', 'meta'];
|
||||
}
|
||||
|
||||
static get relationMappings () {
|
||||
return {
|
||||
owner: {
|
||||
owner: {
|
||||
relation: Model.HasOneRelation,
|
||||
modelClass: User,
|
||||
join: {
|
||||
@ -38,7 +47,19 @@ class ProxyHost extends Model {
|
||||
},
|
||||
modify: function (qb) {
|
||||
qb.where('user.is_deleted', 0);
|
||||
qb.omit(['created_on', 'modified_on', 'is_deleted', 'email', 'roles']);
|
||||
qb.omit(['id', 'created_on', 'modified_on', 'is_deleted', 'email', 'roles']);
|
||||
}
|
||||
},
|
||||
access_list: {
|
||||
relation: Model.HasOneRelation,
|
||||
modelClass: AccessList,
|
||||
join: {
|
||||
from: 'proxy_host.access_list_id',
|
||||
to: 'access_list.id'
|
||||
},
|
||||
modify: function (qb) {
|
||||
qb.where('access_list.is_deleted', 0);
|
||||
qb.omit(['id', 'created_on', 'modified_on', 'is_deleted']);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -27,6 +27,10 @@ class RedirectionHost extends Model {
|
||||
return 'redirection_host';
|
||||
}
|
||||
|
||||
static get jsonAttributes () {
|
||||
return ['domain_names', 'meta'];
|
||||
}
|
||||
|
||||
static get relationMappings () {
|
||||
return {
|
||||
owner: {
|
||||
@ -38,7 +42,7 @@ class RedirectionHost extends Model {
|
||||
},
|
||||
modify: function (qb) {
|
||||
qb.where('user.is_deleted', 0);
|
||||
qb.omit(['created_on', 'modified_on', 'is_deleted', 'email', 'roles']);
|
||||
qb.omit(['id', 'created_on', 'modified_on', 'is_deleted', 'email', 'roles']);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -27,6 +27,10 @@ class Stream extends Model {
|
||||
return 'stream';
|
||||
}
|
||||
|
||||
static get jsonAttributes () {
|
||||
return ['meta'];
|
||||
}
|
||||
|
||||
static get relationMappings () {
|
||||
return {
|
||||
owner: {
|
||||
@ -38,7 +42,7 @@ class Stream extends Model {
|
||||
},
|
||||
modify: function (qb) {
|
||||
qb.where('user.is_deleted', 0);
|
||||
qb.omit(['created_on', 'modified_on', 'is_deleted', 'email', 'roles']);
|
||||
qb.omit(['id', 'created_on', 'modified_on', 'is_deleted', 'email', 'roles']);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -104,7 +104,7 @@ router
|
||||
})
|
||||
.then(data => {
|
||||
return internalProxyHost.get(res.locals.access, {
|
||||
id: data.host_id,
|
||||
id: parseInt(data.host_id, 10),
|
||||
expand: data.expand
|
||||
});
|
||||
})
|
||||
@ -123,7 +123,7 @@ router
|
||||
.put((req, res, next) => {
|
||||
apiValidator({$ref: 'endpoints/proxy-hosts#/links/2/schema'}, req.body)
|
||||
.then(payload => {
|
||||
payload.id = req.params.host_id;
|
||||
payload.id = parseInt(req.params.host_id, 10);
|
||||
return internalProxyHost.update(res.locals.access, payload);
|
||||
})
|
||||
.then(result => {
|
||||
@ -139,7 +139,7 @@ router
|
||||
* Update and existing proxy-host
|
||||
*/
|
||||
.delete((req, res, next) => {
|
||||
internalProxyHost.delete(res.locals.access, {id: req.params.host_id})
|
||||
internalProxyHost.delete(res.locals.access, {id: parseInt(req.params.host_id, 10)})
|
||||
.then(result => {
|
||||
res.status(200)
|
||||
.send(result);
|
||||
|
@ -134,6 +134,31 @@
|
||||
"type": "string",
|
||||
"minLength": 8,
|
||||
"maxLength": 255
|
||||
},
|
||||
"domain_names": {
|
||||
"description": "Domain Names separated by a comma",
|
||||
"example": "*.jc21.com,blog.jc21.com",
|
||||
"type": "array",
|
||||
"maxItems": 15,
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^(?:\\*\\.)?(?:[^.*]+\\.?)+[^.]$"
|
||||
}
|
||||
},
|
||||
"ssl_enabled": {
|
||||
"description": "Is SSL Enabled",
|
||||
"example": true,
|
||||
"type": "boolean"
|
||||
},
|
||||
"ssl_forced": {
|
||||
"description": "Is SSL Forced",
|
||||
"example": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
"ssl_provider": {
|
||||
"type": "string",
|
||||
"pattern": "^(letsencrypt|other)$"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"$id": "endpoints/proxy-hosts",
|
||||
"title": "Users",
|
||||
"title": "Proxy Hosts",
|
||||
"description": "Endpoints relating to Proxy Hosts",
|
||||
"stability": "stable",
|
||||
"type": "object",
|
||||
@ -15,49 +15,78 @@
|
||||
"modified_on": {
|
||||
"$ref": "../definitions.json#/definitions/modified_on"
|
||||
},
|
||||
"name": {
|
||||
"description": "Name",
|
||||
"example": "Jamie Curnow",
|
||||
"domain_names": {
|
||||
"$ref": "../definitions.json#/definitions/domain_names"
|
||||
},
|
||||
"forward_ip": {
|
||||
"type": "string",
|
||||
"minLength": 2,
|
||||
"maxLength": 100
|
||||
"format": "ipv4"
|
||||
},
|
||||
"nickname": {
|
||||
"description": "Nickname",
|
||||
"example": "Jamie",
|
||||
"type": "string",
|
||||
"minLength": 2,
|
||||
"maxLength": 50
|
||||
"forward_port": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 65535
|
||||
},
|
||||
"email": {
|
||||
"$ref": "../definitions.json#/definitions/email"
|
||||
"ssl_enabled": {
|
||||
"$ref": "../definitions.json#/definitions/ssl_enabled"
|
||||
},
|
||||
"avatar": {
|
||||
"description": "Avatar",
|
||||
"example": "http://somewhere.jpg",
|
||||
"type": "string",
|
||||
"minLength": 2,
|
||||
"maxLength": 150,
|
||||
"readOnly": true
|
||||
"ssl_forced": {
|
||||
"$ref": "../definitions.json#/definitions/ssl_forced"
|
||||
},
|
||||
"roles": {
|
||||
"description": "Roles",
|
||||
"example": [
|
||||
"admin"
|
||||
],
|
||||
"type": "array"
|
||||
"ssl_provider": {
|
||||
"$ref": "../definitions.json#/definitions/ssl_provider"
|
||||
},
|
||||
"is_disabled": {
|
||||
"description": "Is Disabled",
|
||||
"example": false,
|
||||
"type": "boolean"
|
||||
"meta": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"letsencrypt_email": {
|
||||
"type": "string",
|
||||
"format": "email"
|
||||
},
|
||||
"letsencrypt_agree": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"$ref": "#/definitions/id"
|
||||
},
|
||||
"created_on": {
|
||||
"$ref": "#/definitions/created_on"
|
||||
},
|
||||
"modified_on": {
|
||||
"$ref": "#/definitions/modified_on"
|
||||
},
|
||||
"domain_names": {
|
||||
"$ref": "#/definitions/domain_names"
|
||||
},
|
||||
"forward_ip": {
|
||||
"$ref": "#/definitions/forward_ip"
|
||||
},
|
||||
"forward_port": {
|
||||
"$ref": "#/definitions/forward_port"
|
||||
},
|
||||
"ssl_enabled": {
|
||||
"$ref": "#/definitions/ssl_enabled"
|
||||
},
|
||||
"ssl_forced": {
|
||||
"$ref": "#/definitions/ssl_forced"
|
||||
},
|
||||
"ssl_provider": {
|
||||
"$ref": "#/definitions/ssl_provider"
|
||||
},
|
||||
"meta": {
|
||||
"$ref": "#/definitions/meta"
|
||||
}
|
||||
},
|
||||
"links": [
|
||||
{
|
||||
"title": "List",
|
||||
"description": "Returns a list of Users",
|
||||
"href": "/users",
|
||||
"description": "Returns a list of Proxy Hosts",
|
||||
"href": "/nginx/proxy-hosts",
|
||||
"access": "private",
|
||||
"method": "GET",
|
||||
"rel": "self",
|
||||
@ -73,8 +102,8 @@
|
||||
},
|
||||
{
|
||||
"title": "Create",
|
||||
"description": "Creates a new User",
|
||||
"href": "/users",
|
||||
"description": "Creates a new Proxy Host",
|
||||
"href": "/nginx/proxy-hosts",
|
||||
"access": "private",
|
||||
"method": "POST",
|
||||
"rel": "create",
|
||||
@ -84,33 +113,31 @@
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"nickname",
|
||||
"email"
|
||||
"domain_names",
|
||||
"forward_ip",
|
||||
"forward_port"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"$ref": "#/definitions/name"
|
||||
"domain_names": {
|
||||
"$ref": "#/definitions/domain_names"
|
||||
},
|
||||
"nickname": {
|
||||
"$ref": "#/definitions/nickname"
|
||||
"forward_ip": {
|
||||
"$ref": "#/definitions/forward_ip"
|
||||
},
|
||||
"email": {
|
||||
"$ref": "#/definitions/email"
|
||||
"forward_port": {
|
||||
"$ref": "#/definitions/forward_port"
|
||||
},
|
||||
"roles": {
|
||||
"$ref": "#/definitions/roles"
|
||||
"ssl_enabled": {
|
||||
"$ref": "#/definitions/ssl_enabled"
|
||||
},
|
||||
"is_disabled": {
|
||||
"$ref": "#/definitions/is_disabled"
|
||||
"ssl_forced": {
|
||||
"$ref": "#/definitions/ssl_forced"
|
||||
},
|
||||
"auth": {
|
||||
"type": "object",
|
||||
"description": "Auth Credentials",
|
||||
"example": {
|
||||
"type": "password",
|
||||
"secret": "bigredhorsebanana"
|
||||
}
|
||||
"ssl_provider": {
|
||||
"$ref": "#/definitions/ssl_provider"
|
||||
},
|
||||
"meta": {
|
||||
"$ref": "#/definitions/meta"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -122,8 +149,8 @@
|
||||
},
|
||||
{
|
||||
"title": "Update",
|
||||
"description": "Updates a existing User",
|
||||
"href": "/users/{definitions.identity.example}",
|
||||
"description": "Updates a existing Proxy Host",
|
||||
"href": "/nginx/proxy-hosts/{definitions.identity.example}",
|
||||
"access": "private",
|
||||
"method": "PUT",
|
||||
"rel": "update",
|
||||
@ -133,20 +160,26 @@
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"$ref": "#/definitions/name"
|
||||
"domain_names": {
|
||||
"$ref": "#/definitions/domain_names"
|
||||
},
|
||||
"nickname": {
|
||||
"$ref": "#/definitions/nickname"
|
||||
"forward_ip": {
|
||||
"$ref": "#/definitions/forward_ip"
|
||||
},
|
||||
"email": {
|
||||
"$ref": "#/definitions/email"
|
||||
"forward_port": {
|
||||
"$ref": "#/definitions/forward_port"
|
||||
},
|
||||
"roles": {
|
||||
"$ref": "#/definitions/roles"
|
||||
"ssl_enabled": {
|
||||
"$ref": "#/definitions/ssl_enabled"
|
||||
},
|
||||
"is_disabled": {
|
||||
"$ref": "#/definitions/is_disabled"
|
||||
"ssl_forced": {
|
||||
"$ref": "#/definitions/ssl_forced"
|
||||
},
|
||||
"ssl_provider": {
|
||||
"$ref": "#/definitions/ssl_provider"
|
||||
},
|
||||
"meta": {
|
||||
"$ref": "#/definitions/meta"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -158,8 +191,8 @@
|
||||
},
|
||||
{
|
||||
"title": "Delete",
|
||||
"description": "Deletes a existing User",
|
||||
"href": "/users/{definitions.identity.example}",
|
||||
"description": "Deletes a existing Proxy Host",
|
||||
"href": "/nginx/proxy-hosts/{definitions.identity.example}",
|
||||
"access": "private",
|
||||
"method": "DELETE",
|
||||
"rel": "delete",
|
||||
@ -170,34 +203,5 @@
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"$ref": "#/definitions/id"
|
||||
},
|
||||
"created_on": {
|
||||
"$ref": "#/definitions/created_on"
|
||||
},
|
||||
"modified_on": {
|
||||
"$ref": "#/definitions/modified_on"
|
||||
},
|
||||
"name": {
|
||||
"$ref": "#/definitions/name"
|
||||
},
|
||||
"nickname": {
|
||||
"$ref": "#/definitions/nickname"
|
||||
},
|
||||
"email": {
|
||||
"$ref": "#/definitions/email"
|
||||
},
|
||||
"avatar": {
|
||||
"$ref": "#/definitions/avatar"
|
||||
},
|
||||
"roles": {
|
||||
"$ref": "#/definitions/roles"
|
||||
},
|
||||
"is_disabled": {
|
||||
"$ref": "#/definitions/is_disabled"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -264,6 +264,25 @@ module.exports = {
|
||||
*/
|
||||
create: function (data) {
|
||||
return fetch('post', 'nginx/proxy-hosts', data);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Object} data
|
||||
* @param {Integer} data.id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
update: function (data) {
|
||||
let id = data.id;
|
||||
delete data.id;
|
||||
return fetch('put', 'nginx/proxy-hosts/' + id, data);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Integer} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
delete: function (id) {
|
||||
return fetch('delete', 'nginx/proxy-hosts/' + id);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -147,6 +147,19 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Proxy Host Delete Confirm
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showNginxProxyDeleteConfirm: function (model) {
|
||||
if (Cache.User.isAdmin() || Cache.User.canManage('proxy_hosts')) {
|
||||
require(['./main', './nginx/proxy/delete'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Nginx Redirection Hosts
|
||||
*/
|
||||
|
23
src/frontend/js/app/nginx/proxy/delete.ejs
Normal file
23
src/frontend/js/app/nginx/proxy/delete.ejs
Normal file
@ -0,0 +1,23 @@
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Delete Proxy Host</h5>
|
||||
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form>
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
Are you sure you want to delete the Proxy host for: <strong><%- domain_names.join(', ') %></strong>?
|
||||
<% if (ssl_enabled) { %>
|
||||
<br><br>
|
||||
The SSL certificates attached will be removed, this action cannot be recovered.
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary cancel" data-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-danger save">Yes I'm Sure</button>
|
||||
</div>
|
||||
</div>
|
38
src/frontend/js/app/nginx/proxy/delete.js
Normal file
38
src/frontend/js/app/nginx/proxy/delete.js
Normal file
@ -0,0 +1,38 @@
|
||||
'use strict';
|
||||
|
||||
const Mn = require('backbone.marionette');
|
||||
const template = require('./delete.ejs');
|
||||
const Controller = require('../../controller');
|
||||
const Api = require('../../api');
|
||||
const App = require('../../main');
|
||||
|
||||
require('jquery-serializejson');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
className: 'modal-dialog',
|
||||
|
||||
ui: {
|
||||
form: 'form',
|
||||
buttons: '.modal-footer button',
|
||||
cancel: 'button.cancel',
|
||||
save: 'button.save'
|
||||
},
|
||||
|
||||
events: {
|
||||
|
||||
'click @ui.save': function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
Api.Nginx.ProxyHosts.delete(this.model.get('id'))
|
||||
.then(() => {
|
||||
Controller.showNginxProxy();
|
||||
App.UI.closeModal();
|
||||
})
|
||||
.catch(err => {
|
||||
alert(err.message);
|
||||
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
@ -13,22 +13,23 @@
|
||||
<!-- Details -->
|
||||
<div role="tabpanel" class="tab-pane active" id="details">
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Domain Name <span class="form-required">*</span></label>
|
||||
<input name="domain_name" type="text" class="form-control" placeholder="example.com or *.example.com" value="<%- domain_name %>" pattern="(\*\.)?[a-z0-9\.]+" required title="Please enter a valid domain name. Domain wildcards are allowed: *.yourdomain.com">
|
||||
<label class="form-label">Domain Names <span class="form-required">*</span></label>
|
||||
<input type="text" name="domain_names" class="form-control" id="input-domains" value="<%- domain_names.join(',') %>" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-8 col-md-8">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Forward IP <span class="form-required">*</span></label>
|
||||
<input type="text" name="forward_ip" class="form-control" placeholder="000.000.000.000" autocomplete="off" maxlength="15" required>
|
||||
<input type="text" name="forward_ip" class="form-control text-monospace" placeholder="000.000.000.000" value="<%- forward_ip %>" autocomplete="off" maxlength="15" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4 col-md-4">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Forward Port <span class="form-required">*</span></label>
|
||||
<input name="forward_port" type="number" class="form-control" placeholder="80" value="<%- forward_port %>" required>
|
||||
<input name="forward_port" type="number" class="form-control text-monospace" placeholder="80" value="<%- forward_port %>" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -11,6 +11,7 @@ const ProxyHostModel = require('../../../models/proxy-host');
|
||||
|
||||
require('jquery-serializejson');
|
||||
require('jquery-mask-plugin');
|
||||
require('selectize');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
@ -18,7 +19,7 @@ module.exports = Mn.View.extend({
|
||||
|
||||
ui: {
|
||||
form: 'form',
|
||||
domain_name: 'input[name="domain_name"]',
|
||||
domain_names: 'input[name="domain_names"]',
|
||||
forward_ip: 'input[name="forward_ip"]',
|
||||
buttons: '.modal-footer button',
|
||||
cancel: 'button.cancel',
|
||||
@ -73,6 +74,10 @@ module.exports = Mn.View.extend({
|
||||
data[idx] = item;
|
||||
});
|
||||
|
||||
if (typeof data.domain_names === 'string' && data.domain_names) {
|
||||
data.domain_names = data.domain_names.split(',');
|
||||
}
|
||||
|
||||
// Process
|
||||
this.ui.buttons.prop('disabled', true).addClass('btn-disabled');
|
||||
let method = Api.Nginx.ProxyHosts.create;
|
||||
@ -118,9 +123,18 @@ module.exports = Mn.View.extend({
|
||||
this.ui.ssl_enabled.trigger('change');
|
||||
this.ui.ssl_provider.trigger('change');
|
||||
|
||||
this.ui.domain_name[0].oninvalid = function () {
|
||||
this.setCustomValidity('Please enter a valid domain name. Domain wildcards are allowed: *.yourdomain.com');
|
||||
};
|
||||
this.ui.domain_names.selectize({
|
||||
delimiter: ',',
|
||||
persist: false,
|
||||
maxOptions: 15,
|
||||
create: function (input) {
|
||||
return {
|
||||
value: input,
|
||||
text: input
|
||||
};
|
||||
},
|
||||
createFilter: /^(?:\*\.)?(?:[^.*]+\.?)+[^.]$/
|
||||
});
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
|
@ -1,32 +1,40 @@
|
||||
<td class="text-center">
|
||||
<div class="avatar d-block" style="background-image: url(<%- avatar || '/images/default-avatar.jpg' %>)">
|
||||
<span class="avatar-status <%- is_disabled ? 'bg-red' : 'bg-green' %>"></span>
|
||||
<div class="avatar d-block" style="background-image: url(<%- owner.avatar || '/images/default-avatar.jpg' %>)" title="Owned by <%- owner.name %>">
|
||||
<span class="avatar-status <%- owner.is_disabled ? 'bg-red' : 'bg-green' %>"></span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div><%- name %></div>
|
||||
<div>
|
||||
<% domain_names.map(function(host) {
|
||||
%>
|
||||
<span class="tag"><%- host %></span>
|
||||
<%
|
||||
});
|
||||
%>
|
||||
</div>
|
||||
<div class="small text-muted">
|
||||
Created: <%- formatDbDate(created_on, 'Do MMMM YYYY') %>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div><%- email %></div>
|
||||
<div class="text-monospace"><%- forward_ip %>:<%- forward_port %></div>
|
||||
</td>
|
||||
<td>
|
||||
<div><%- roles.join(', ') %></div>
|
||||
<div><%- ssl_enabled && ssl_provider ? ssl_provider : 'HTTP only' %></div>
|
||||
</td>
|
||||
<td>
|
||||
<div><%- access_list_id ? access_list.name : 'Public' %></div>
|
||||
</td>
|
||||
<% if (canManage) { %>
|
||||
<td class="text-center">
|
||||
<div class="item-action dropdown">
|
||||
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a href="#" class="edit-user dropdown-item"><i class="dropdown-icon fe fe-edit"></i> Edit Details</a>
|
||||
<a href="#" class="edit-permissions dropdown-item"><i class="dropdown-icon fe fe-shield"></i> Edit Permissions</a>
|
||||
<a href="#" class="set-password dropdown-item"><i class="dropdown-icon fe fe-lock"></i> Set Password</a>
|
||||
<% if (!isSelf()) { %>
|
||||
<a href="#" class="login dropdown-item"><i class="dropdown-icon fe fe-log-in"></i> Sign in as User</a>
|
||||
<a href="#" class="edit dropdown-item"><i class="dropdown-icon fe fe-edit"></i> Edit</a>
|
||||
<a href="#" class="logs dropdown-item"><i class="dropdown-icon fe fe-book"></i> Logs</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a href="#" class="delete-user dropdown-item"><i class="dropdown-icon fe fe-trash-2"></i> Delete User</a>
|
||||
<% } %>
|
||||
<a href="#" class="delete dropdown-item"><i class="dropdown-icon fe fe-trash-2"></i> Delete</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<% } %>
|
@ -4,7 +4,6 @@ const Mn = require('backbone.marionette');
|
||||
const Controller = require('../../../controller');
|
||||
const Api = require('../../../api');
|
||||
const Cache = require('../../../cache');
|
||||
const Tokens = require('../../../tokens');
|
||||
const template = require('./item.ejs');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
@ -12,58 +11,24 @@ module.exports = Mn.View.extend({
|
||||
tagName: 'tr',
|
||||
|
||||
ui: {
|
||||
edit: 'a.edit-user',
|
||||
permissions: 'a.edit-permissions',
|
||||
password: 'a.set-password',
|
||||
login: 'a.login',
|
||||
delete: 'a.delete-user'
|
||||
edit: 'a.edit',
|
||||
delete: 'a.delete'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click @ui.edit': function (e) {
|
||||
e.preventDefault();
|
||||
Controller.showUserForm(this.model);
|
||||
},
|
||||
|
||||
'click @ui.permissions': function (e) {
|
||||
e.preventDefault();
|
||||
Controller.showUserPermissions(this.model);
|
||||
},
|
||||
|
||||
'click @ui.password': function (e) {
|
||||
e.preventDefault();
|
||||
Controller.showUserPasswordForm(this.model);
|
||||
Controller.showNginxProxyForm(this.model);
|
||||
},
|
||||
|
||||
'click @ui.delete': function (e) {
|
||||
e.preventDefault();
|
||||
Controller.showUserDeleteConfirm(this.model);
|
||||
},
|
||||
|
||||
'click @ui.login': function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
if (Cache.User.get('id') !== this.model.get('id')) {
|
||||
this.ui.login.prop('disabled', true).addClass('btn-disabled');
|
||||
|
||||
Api.Users.loginAs(this.model.get('id'))
|
||||
.then(res => {
|
||||
Tokens.addToken(res.token, res.user.nickname || res.user.name);
|
||||
window.location = '/';
|
||||
window.location.reload();
|
||||
})
|
||||
.catch(err => {
|
||||
alert(err.message);
|
||||
this.ui.login.prop('disabled', false).removeClass('btn-disabled');
|
||||
});
|
||||
}
|
||||
Controller.showNginxProxyDeleteConfirm(this.model);
|
||||
}
|
||||
},
|
||||
|
||||
templateContext: {
|
||||
isSelf: function () {
|
||||
return Cache.User.get('id') === this.id;
|
||||
}
|
||||
canManage: Cache.User.canManage('proxy_hosts')
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
|
@ -1,9 +1,12 @@
|
||||
<thead>
|
||||
<th width="30"> </th>
|
||||
<th>Name</th>
|
||||
<th>Email</th>
|
||||
<th>Roles</th>
|
||||
<th>Source</th>
|
||||
<th>Destination</th>
|
||||
<th>SSL</th>
|
||||
<th>Access</th>
|
||||
<% if (canManage) { %>
|
||||
<th> </th>
|
||||
<% } %>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- items -->
|
||||
|
@ -1,8 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
const Mn = require('backbone.marionette');
|
||||
const ItemView = require('./item');
|
||||
const template = require('./main.ejs');
|
||||
const Mn = require('backbone.marionette');
|
||||
const ItemView = require('./item');
|
||||
const template = require('./main.ejs');
|
||||
const Cache = require('../../../cache');
|
||||
|
||||
const TableBody = Mn.CollectionView.extend({
|
||||
tagName: 'tbody',
|
||||
@ -21,6 +22,10 @@ module.exports = Mn.View.extend({
|
||||
}
|
||||
},
|
||||
|
||||
templateContext: {
|
||||
canManage: Cache.User.canManage('proxy_hosts')
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
this.showChildView('body', new TableBody({
|
||||
collection: this.collection
|
||||
|
@ -38,7 +38,7 @@ module.exports = Mn.View.extend({
|
||||
onRender: function () {
|
||||
let view = this;
|
||||
|
||||
Api.Nginx.ProxyHosts.getAll()
|
||||
Api.Nginx.ProxyHosts.getAll(['owner', 'access_list'])
|
||||
.then(response => {
|
||||
if (!view.isDestroyed()) {
|
||||
if (response && response.length) {
|
||||
|
@ -9,8 +9,7 @@ const model = Backbone.Model.extend({
|
||||
return {
|
||||
created_on: null,
|
||||
modified_on: null,
|
||||
owner: null,
|
||||
domain_name: '',
|
||||
domain_names: [],
|
||||
forward_ip: '',
|
||||
forward_port: null,
|
||||
access_list_id: null,
|
||||
@ -19,7 +18,10 @@ const model = Backbone.Model.extend({
|
||||
ssl_forced: false,
|
||||
caching_enabled: false,
|
||||
block_exploits: false,
|
||||
meta: []
|
||||
meta: [],
|
||||
// The following are expansions:
|
||||
owner: null,
|
||||
access_list: null
|
||||
};
|
||||
}
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user