mirror of
https://github.com/xiaoxinpro/nginx-proxy-manager-zh.git
synced 2025-01-22 21:08:13 -05:00
Merge branch 'v2-rewrite' of github.com:jc21/nginx-proxy-manager into v2-rewrite
This commit is contained in:
commit
2daa471a19
@ -12,6 +12,8 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ./data/letsencrypt:/etc/letsencrypt
|
- ./data/letsencrypt:/etc/letsencrypt
|
||||||
- .:/srv/app
|
- .:/srv/app
|
||||||
|
- ~/.yarnrc:/root/.yarnrc
|
||||||
|
- ~/.npmrc:/root/.npmrc
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
links:
|
links:
|
||||||
|
@ -26,7 +26,7 @@ const internalDeadHost = {
|
|||||||
.where('is_deleted', 0)
|
.where('is_deleted', 0)
|
||||||
.groupBy('id')
|
.groupBy('id')
|
||||||
.omit(['is_deleted'])
|
.omit(['is_deleted'])
|
||||||
.orderBy('domain_name', 'ASC');
|
.orderBy('domain_names', 'ASC');
|
||||||
|
|
||||||
if (access_data.permission_visibility !== 'all') {
|
if (access_data.permission_visibility !== 'all') {
|
||||||
query.andWhere('owner_user_id', access.token.get('attrs').id);
|
query.andWhere('owner_user_id', access.token.get('attrs').id);
|
||||||
@ -35,7 +35,7 @@ const internalDeadHost = {
|
|||||||
// Query is used for searching
|
// Query is used for searching
|
||||||
if (typeof search_query === 'string') {
|
if (typeof search_query === 'string') {
|
||||||
query.where(function () {
|
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 _ = require('lodash');
|
||||||
const error = require('../lib/error');
|
const error = require('../lib/error');
|
||||||
const proxyHostModel = require('../models/proxy_host');
|
const proxyHostModel = require('../models/proxy_host');
|
||||||
|
const internalHost = require('./host');
|
||||||
|
|
||||||
function omissions () {
|
function omissions () {
|
||||||
return ['is_deleted'];
|
return ['is_deleted'];
|
||||||
@ -16,60 +17,39 @@ const internalProxyHost = {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
create: (access, data) => {
|
create: (access, data) => {
|
||||||
let auth = data.auth || null;
|
return access.can('proxy_hosts:create', data)
|
||||||
delete data.auth;
|
.then(access_data => {
|
||||||
|
// Get a list of the domain names and check each of them against existing records
|
||||||
|
let domain_name_check_promises = [];
|
||||||
|
|
||||||
data.avatar = data.avatar || '';
|
data.domain_names.map(function (domain_name) {
|
||||||
data.roles = data.roles || [];
|
domain_name_check_promises.push(internalHost.isHostnameTaken(domain_name));
|
||||||
|
});
|
||||||
|
|
||||||
if (typeof data.is_disabled !== 'undefined') {
|
return Promise.all(domain_name_check_promises)
|
||||||
data.is_disabled = data.is_disabled ? 1 : 0;
|
.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 access.can('proxy_hosts:create', data)
|
return proxyHostModel
|
||||||
.then(() => {
|
|
||||||
data.avatar = gravatar.url(data.email, {default: 'mm'});
|
|
||||||
|
|
||||||
return userModel
|
|
||||||
.query()
|
.query()
|
||||||
.omit(omissions())
|
.omit(omissions())
|
||||||
.insertAndFetch(data);
|
.insertAndFetch(data);
|
||||||
})
|
})
|
||||||
.then(user => {
|
.then(row => {
|
||||||
if (auth) {
|
return _.omit(row, omissions());
|
||||||
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']});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -82,63 +62,49 @@ const internalProxyHost = {
|
|||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
update: (access, data) => {
|
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)
|
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
|
if (typeof data.domain_names !== 'undefined') {
|
||||||
// 1. get user we want to update
|
data.domain_names.map(function (domain_name) {
|
||||||
return internalProxyHost.get(access, {id: data.id})
|
domain_name_check_promises.push(internalHost.isHostnameTaken(domain_name, 'proxy', data.id));
|
||||||
.then(user => {
|
});
|
||||||
|
|
||||||
// 2. if email is to be changed, find other users with that email
|
return Promise.all(domain_name_check_promises)
|
||||||
if (typeof data.email !== 'undefined') {
|
.then(check_results => {
|
||||||
data.email = data.email.toLowerCase().trim();
|
check_results.map(function (result) {
|
||||||
|
if (result.is_taken) {
|
||||||
if (user.email !== data.email) {
|
throw new error.ValidationError(result.hostname + ' is already in use');
|
||||||
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;
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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(() => {
|
.then(() => {
|
||||||
return internalProxyHost.get(access, {id: data.id});
|
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 {Access} access
|
||||||
* @param {Object} [data]
|
* @param {Object} data
|
||||||
* @param {Integer} [data.id] Defaults to the token user
|
* @param {Integer} data.id
|
||||||
* @param {Array} [data.expand]
|
* @param {Array} [data.expand]
|
||||||
* @param {Array} [data.omit]
|
* @param {Array} [data.omit]
|
||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
@ -153,14 +119,18 @@ const internalProxyHost = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return access.can('proxy_hosts:get', data.id)
|
return access.can('proxy_hosts:get', data.id)
|
||||||
.then(() => {
|
.then(access_data => {
|
||||||
let query = userModel
|
let query = proxyHostModel
|
||||||
.query()
|
.query()
|
||||||
.where('is_deleted', 0)
|
.where('is_deleted', 0)
|
||||||
.andWhere('id', data.id)
|
.andWhere('id', data.id)
|
||||||
.allowEager('[permissions]')
|
.allowEager('[permissions]')
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
|
if (access_data.permission_visibility !== 'all') {
|
||||||
|
query.andWhere('owner_user_id', access.token.get('attrs').id);
|
||||||
|
}
|
||||||
|
|
||||||
// Custom omissions
|
// Custom omissions
|
||||||
if (typeof data.omit !== 'undefined' && data.omit !== null) {
|
if (typeof data.omit !== 'undefined' && data.omit !== null) {
|
||||||
query.omit(data.omit);
|
query.omit(data.omit);
|
||||||
@ -193,19 +163,14 @@ const internalProxyHost = {
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
return internalProxyHost.get(access, {id: data.id});
|
return internalProxyHost.get(access, {id: data.id});
|
||||||
})
|
})
|
||||||
.then(user => {
|
.then(row => {
|
||||||
if (!user) {
|
if (!row) {
|
||||||
throw new error.ItemNotFoundError(data.id);
|
throw new error.ItemNotFoundError(data.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure user can't delete themselves
|
return proxyHostModel
|
||||||
if (user.id === access.token.get('attrs').id) {
|
|
||||||
throw new error.PermissionError('You cannot delete yourself.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return userModel
|
|
||||||
.query()
|
.query()
|
||||||
.where('id', user.id)
|
.where('id', row.id)
|
||||||
.patch({
|
.patch({
|
||||||
is_deleted: 1
|
is_deleted: 1
|
||||||
});
|
});
|
||||||
@ -231,7 +196,8 @@ const internalProxyHost = {
|
|||||||
.where('is_deleted', 0)
|
.where('is_deleted', 0)
|
||||||
.groupBy('id')
|
.groupBy('id')
|
||||||
.omit(['is_deleted'])
|
.omit(['is_deleted'])
|
||||||
.orderBy('domain_name', 'ASC');
|
.allowEager('[owner,access_list]')
|
||||||
|
.orderBy('domain_names', 'ASC');
|
||||||
|
|
||||||
if (access_data.permission_visibility !== 'all') {
|
if (access_data.permission_visibility !== 'all') {
|
||||||
query.andWhere('owner_user_id', access.token.get('attrs').id);
|
query.andWhere('owner_user_id', access.token.get('attrs').id);
|
||||||
@ -240,7 +206,7 @@ const internalProxyHost = {
|
|||||||
// Query is used for searching
|
// Query is used for searching
|
||||||
if (typeof search_query === 'string') {
|
if (typeof search_query === 'string') {
|
||||||
query.where(function () {
|
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)
|
.where('is_deleted', 0)
|
||||||
.groupBy('id')
|
.groupBy('id')
|
||||||
.omit(['is_deleted'])
|
.omit(['is_deleted'])
|
||||||
.orderBy('domain_name', 'ASC');
|
.orderBy('domain_names', 'ASC');
|
||||||
|
|
||||||
if (access_data.permission_visibility !== 'all') {
|
if (access_data.permission_visibility !== 'all') {
|
||||||
query.andWhere('owner_user_id', access.token.get('attrs').id);
|
query.andWhere('owner_user_id', access.token.get('attrs').id);
|
||||||
@ -35,7 +35,7 @@ const internalProxyHost = {
|
|||||||
// Query is used for searching
|
// Query is used for searching
|
||||||
if (typeof search_query === 'string') {
|
if (typeof search_query === 'string') {
|
||||||
query.where(function () {
|
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)
|
.where('is_deleted', 0)
|
||||||
.groupBy('id')
|
.groupBy('id')
|
||||||
.omit(['is_deleted'])
|
.omit(['is_deleted'])
|
||||||
|
.allowEager('[permissions]')
|
||||||
.orderBy('name', 'ASC');
|
.orderBy('name', 'ASC');
|
||||||
|
|
||||||
// Query is used for searching
|
// Query is used for searching
|
||||||
|
@ -301,8 +301,8 @@ module.exports = function (token_string) {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
//logger.error(err.message);
|
logger.error(err.message);
|
||||||
//logger.error(err.errors);
|
logger.error(err.errors);
|
||||||
|
|
||||||
throw new error.PermissionError('Permission Denied', err);
|
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.dateTime('modified_on').notNull();
|
||||||
table.integer('owner_user_id').notNull().unsigned();
|
table.integer('owner_user_id').notNull().unsigned();
|
||||||
table.integer('is_deleted').notNull().unsigned().defaultTo(0);
|
table.integer('is_deleted').notNull().unsigned().defaultTo(0);
|
||||||
table.string('domain_name').notNull();
|
table.json('domain_names').notNull();
|
||||||
table.string('forward_ip').notNull();
|
table.string('forward_ip').notNull();
|
||||||
table.integer('forward_port').notNull().unsigned();
|
table.integer('forward_port').notNull().unsigned();
|
||||||
table.integer('access_list_id').notNull().unsigned().defaultTo(0);
|
table.integer('access_list_id').notNull().unsigned().defaultTo(0);
|
||||||
@ -88,7 +88,7 @@ exports.up = function (knex/*, Promise*/) {
|
|||||||
table.dateTime('modified_on').notNull();
|
table.dateTime('modified_on').notNull();
|
||||||
table.integer('owner_user_id').notNull().unsigned();
|
table.integer('owner_user_id').notNull().unsigned();
|
||||||
table.integer('is_deleted').notNull().unsigned().defaultTo(0);
|
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.string('forward_domain_name').notNull();
|
||||||
table.integer('preserve_path').notNull().unsigned().defaultTo(0);
|
table.integer('preserve_path').notNull().unsigned().defaultTo(0);
|
||||||
table.integer('ssl_enabled').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.dateTime('modified_on').notNull();
|
||||||
table.integer('owner_user_id').notNull().unsigned();
|
table.integer('owner_user_id').notNull().unsigned();
|
||||||
table.integer('is_deleted').notNull().unsigned().defaultTo(0);
|
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.integer('ssl_enabled').notNull().unsigned().defaultTo(0);
|
||||||
table.string('ssl_provider').notNull().defaultTo('');
|
table.string('ssl_provider').notNull().defaultTo('');
|
||||||
table.json('meta').notNull();
|
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';
|
return 'dead_host';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static get jsonAttributes () {
|
||||||
|
return ['domain_names', 'meta'];
|
||||||
|
}
|
||||||
|
|
||||||
static get relationMappings () {
|
static get relationMappings () {
|
||||||
return {
|
return {
|
||||||
owner: {
|
owner: {
|
||||||
@ -38,7 +42,7 @@ class DeadHost extends Model {
|
|||||||
},
|
},
|
||||||
modify: function (qb) {
|
modify: function (qb) {
|
||||||
qb.where('user.is_deleted', 0);
|
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']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
const db = require('../db');
|
const db = require('../db');
|
||||||
const Model = require('objection').Model;
|
const Model = require('objection').Model;
|
||||||
const User = require('./user');
|
const User = require('./user');
|
||||||
|
const AccessList = require('./access_list');
|
||||||
|
|
||||||
Model.knex(db);
|
Model.knex(db);
|
||||||
|
|
||||||
@ -13,10 +14,14 @@ class ProxyHost extends Model {
|
|||||||
$beforeInsert () {
|
$beforeInsert () {
|
||||||
this.created_on = Model.raw('NOW()');
|
this.created_on = Model.raw('NOW()');
|
||||||
this.modified_on = Model.raw('NOW()');
|
this.modified_on = Model.raw('NOW()');
|
||||||
|
this.domain_names.sort();
|
||||||
}
|
}
|
||||||
|
|
||||||
$beforeUpdate () {
|
$beforeUpdate () {
|
||||||
this.modified_on = Model.raw('NOW()');
|
this.modified_on = Model.raw('NOW()');
|
||||||
|
if (typeof this.domain_names !== 'undefined') {
|
||||||
|
this.domain_names.sort();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get name () {
|
static get name () {
|
||||||
@ -27,6 +32,10 @@ class ProxyHost extends Model {
|
|||||||
return 'proxy_host';
|
return 'proxy_host';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static get jsonAttributes () {
|
||||||
|
return ['domain_names', 'meta'];
|
||||||
|
}
|
||||||
|
|
||||||
static get relationMappings () {
|
static get relationMappings () {
|
||||||
return {
|
return {
|
||||||
owner: {
|
owner: {
|
||||||
@ -38,7 +47,19 @@ class ProxyHost extends Model {
|
|||||||
},
|
},
|
||||||
modify: function (qb) {
|
modify: function (qb) {
|
||||||
qb.where('user.is_deleted', 0);
|
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';
|
return 'redirection_host';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static get jsonAttributes () {
|
||||||
|
return ['domain_names', 'meta'];
|
||||||
|
}
|
||||||
|
|
||||||
static get relationMappings () {
|
static get relationMappings () {
|
||||||
return {
|
return {
|
||||||
owner: {
|
owner: {
|
||||||
@ -38,7 +42,7 @@ class RedirectionHost extends Model {
|
|||||||
},
|
},
|
||||||
modify: function (qb) {
|
modify: function (qb) {
|
||||||
qb.where('user.is_deleted', 0);
|
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';
|
return 'stream';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static get jsonAttributes () {
|
||||||
|
return ['meta'];
|
||||||
|
}
|
||||||
|
|
||||||
static get relationMappings () {
|
static get relationMappings () {
|
||||||
return {
|
return {
|
||||||
owner: {
|
owner: {
|
||||||
@ -38,7 +42,7 @@ class Stream extends Model {
|
|||||||
},
|
},
|
||||||
modify: function (qb) {
|
modify: function (qb) {
|
||||||
qb.where('user.is_deleted', 0);
|
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 => {
|
.then(data => {
|
||||||
return internalProxyHost.get(res.locals.access, {
|
return internalProxyHost.get(res.locals.access, {
|
||||||
id: data.host_id,
|
id: parseInt(data.host_id, 10),
|
||||||
expand: data.expand
|
expand: data.expand
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
@ -123,7 +123,7 @@ router
|
|||||||
.put((req, res, next) => {
|
.put((req, res, next) => {
|
||||||
apiValidator({$ref: 'endpoints/proxy-hosts#/links/2/schema'}, req.body)
|
apiValidator({$ref: 'endpoints/proxy-hosts#/links/2/schema'}, req.body)
|
||||||
.then(payload => {
|
.then(payload => {
|
||||||
payload.id = req.params.host_id;
|
payload.id = parseInt(req.params.host_id, 10);
|
||||||
return internalProxyHost.update(res.locals.access, payload);
|
return internalProxyHost.update(res.locals.access, payload);
|
||||||
})
|
})
|
||||||
.then(result => {
|
.then(result => {
|
||||||
@ -139,7 +139,7 @@ router
|
|||||||
* Update and existing proxy-host
|
* Update and existing proxy-host
|
||||||
*/
|
*/
|
||||||
.delete((req, res, next) => {
|
.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 => {
|
.then(result => {
|
||||||
res.status(200)
|
res.status(200)
|
||||||
.send(result);
|
.send(result);
|
||||||
|
@ -134,6 +134,31 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"minLength": 8,
|
"minLength": 8,
|
||||||
"maxLength": 255
|
"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#",
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
"$id": "endpoints/proxy-hosts",
|
"$id": "endpoints/proxy-hosts",
|
||||||
"title": "Users",
|
"title": "Proxy Hosts",
|
||||||
"description": "Endpoints relating to Proxy Hosts",
|
"description": "Endpoints relating to Proxy Hosts",
|
||||||
"stability": "stable",
|
"stability": "stable",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
@ -15,49 +15,78 @@
|
|||||||
"modified_on": {
|
"modified_on": {
|
||||||
"$ref": "../definitions.json#/definitions/modified_on"
|
"$ref": "../definitions.json#/definitions/modified_on"
|
||||||
},
|
},
|
||||||
"name": {
|
"domain_names": {
|
||||||
"description": "Name",
|
"$ref": "../definitions.json#/definitions/domain_names"
|
||||||
"example": "Jamie Curnow",
|
},
|
||||||
|
"forward_ip": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"minLength": 2,
|
"format": "ipv4"
|
||||||
"maxLength": 100
|
|
||||||
},
|
},
|
||||||
"nickname": {
|
"forward_port": {
|
||||||
"description": "Nickname",
|
"type": "integer",
|
||||||
"example": "Jamie",
|
"minimum": 1,
|
||||||
|
"maximum": 65535
|
||||||
|
},
|
||||||
|
"ssl_enabled": {
|
||||||
|
"$ref": "../definitions.json#/definitions/ssl_enabled"
|
||||||
|
},
|
||||||
|
"ssl_forced": {
|
||||||
|
"$ref": "../definitions.json#/definitions/ssl_forced"
|
||||||
|
},
|
||||||
|
"ssl_provider": {
|
||||||
|
"$ref": "../definitions.json#/definitions/ssl_provider"
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"letsencrypt_email": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"minLength": 2,
|
"format": "email"
|
||||||
"maxLength": 50
|
|
||||||
},
|
},
|
||||||
"email": {
|
"letsencrypt_agree": {
|
||||||
"$ref": "../definitions.json#/definitions/email"
|
|
||||||
},
|
|
||||||
"avatar": {
|
|
||||||
"description": "Avatar",
|
|
||||||
"example": "http://somewhere.jpg",
|
|
||||||
"type": "string",
|
|
||||||
"minLength": 2,
|
|
||||||
"maxLength": 150,
|
|
||||||
"readOnly": true
|
|
||||||
},
|
|
||||||
"roles": {
|
|
||||||
"description": "Roles",
|
|
||||||
"example": [
|
|
||||||
"admin"
|
|
||||||
],
|
|
||||||
"type": "array"
|
|
||||||
},
|
|
||||||
"is_disabled": {
|
|
||||||
"description": "Is Disabled",
|
|
||||||
"example": false,
|
|
||||||
"type": "boolean"
|
"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": [
|
"links": [
|
||||||
{
|
{
|
||||||
"title": "List",
|
"title": "List",
|
||||||
"description": "Returns a list of Users",
|
"description": "Returns a list of Proxy Hosts",
|
||||||
"href": "/users",
|
"href": "/nginx/proxy-hosts",
|
||||||
"access": "private",
|
"access": "private",
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"rel": "self",
|
"rel": "self",
|
||||||
@ -73,8 +102,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Create",
|
"title": "Create",
|
||||||
"description": "Creates a new User",
|
"description": "Creates a new Proxy Host",
|
||||||
"href": "/users",
|
"href": "/nginx/proxy-hosts",
|
||||||
"access": "private",
|
"access": "private",
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"rel": "create",
|
"rel": "create",
|
||||||
@ -84,33 +113,31 @@
|
|||||||
"schema": {
|
"schema": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
"name",
|
"domain_names",
|
||||||
"nickname",
|
"forward_ip",
|
||||||
"email"
|
"forward_port"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {
|
"domain_names": {
|
||||||
"$ref": "#/definitions/name"
|
"$ref": "#/definitions/domain_names"
|
||||||
},
|
},
|
||||||
"nickname": {
|
"forward_ip": {
|
||||||
"$ref": "#/definitions/nickname"
|
"$ref": "#/definitions/forward_ip"
|
||||||
},
|
},
|
||||||
"email": {
|
"forward_port": {
|
||||||
"$ref": "#/definitions/email"
|
"$ref": "#/definitions/forward_port"
|
||||||
},
|
},
|
||||||
"roles": {
|
"ssl_enabled": {
|
||||||
"$ref": "#/definitions/roles"
|
"$ref": "#/definitions/ssl_enabled"
|
||||||
},
|
},
|
||||||
"is_disabled": {
|
"ssl_forced": {
|
||||||
"$ref": "#/definitions/is_disabled"
|
"$ref": "#/definitions/ssl_forced"
|
||||||
},
|
},
|
||||||
"auth": {
|
"ssl_provider": {
|
||||||
"type": "object",
|
"$ref": "#/definitions/ssl_provider"
|
||||||
"description": "Auth Credentials",
|
},
|
||||||
"example": {
|
"meta": {
|
||||||
"type": "password",
|
"$ref": "#/definitions/meta"
|
||||||
"secret": "bigredhorsebanana"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -122,8 +149,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Update",
|
"title": "Update",
|
||||||
"description": "Updates a existing User",
|
"description": "Updates a existing Proxy Host",
|
||||||
"href": "/users/{definitions.identity.example}",
|
"href": "/nginx/proxy-hosts/{definitions.identity.example}",
|
||||||
"access": "private",
|
"access": "private",
|
||||||
"method": "PUT",
|
"method": "PUT",
|
||||||
"rel": "update",
|
"rel": "update",
|
||||||
@ -133,20 +160,26 @@
|
|||||||
"schema": {
|
"schema": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {
|
"domain_names": {
|
||||||
"$ref": "#/definitions/name"
|
"$ref": "#/definitions/domain_names"
|
||||||
},
|
},
|
||||||
"nickname": {
|
"forward_ip": {
|
||||||
"$ref": "#/definitions/nickname"
|
"$ref": "#/definitions/forward_ip"
|
||||||
},
|
},
|
||||||
"email": {
|
"forward_port": {
|
||||||
"$ref": "#/definitions/email"
|
"$ref": "#/definitions/forward_port"
|
||||||
},
|
},
|
||||||
"roles": {
|
"ssl_enabled": {
|
||||||
"$ref": "#/definitions/roles"
|
"$ref": "#/definitions/ssl_enabled"
|
||||||
},
|
},
|
||||||
"is_disabled": {
|
"ssl_forced": {
|
||||||
"$ref": "#/definitions/is_disabled"
|
"$ref": "#/definitions/ssl_forced"
|
||||||
|
},
|
||||||
|
"ssl_provider": {
|
||||||
|
"$ref": "#/definitions/ssl_provider"
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"$ref": "#/definitions/meta"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -158,8 +191,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Delete",
|
"title": "Delete",
|
||||||
"description": "Deletes a existing User",
|
"description": "Deletes a existing Proxy Host",
|
||||||
"href": "/users/{definitions.identity.example}",
|
"href": "/nginx/proxy-hosts/{definitions.identity.example}",
|
||||||
"access": "private",
|
"access": "private",
|
||||||
"method": "DELETE",
|
"method": "DELETE",
|
||||||
"rel": "delete",
|
"rel": "delete",
|
||||||
@ -170,34 +203,5 @@
|
|||||||
"type": "boolean"
|
"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) {
|
create: function (data) {
|
||||||
return fetch('post', 'nginx/proxy-hosts', 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);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h3 class="card-title">Audit Log</h3>
|
<h3 class="card-title"><%- i18n('audit-log', 'title') %></h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body no-padding min-100">
|
<div class="card-body no-padding min-100">
|
||||||
<div class="dimmer active">
|
<div class="dimmer active">
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const Mn = require('backbone.marionette');
|
const Mn = require('backbone.marionette');
|
||||||
|
const App = require('../main');
|
||||||
const AuditLogModel = require('../../models/audit-log');
|
const AuditLogModel = require('../../models/audit-log');
|
||||||
const Api = require('../api');
|
|
||||||
const Controller = require('../controller');
|
|
||||||
const ListView = require('./list/main');
|
const ListView = require('./list/main');
|
||||||
const template = require('./main.ejs');
|
const template = require('./main.ejs');
|
||||||
const ErrorView = require('../error/main');
|
const ErrorView = require('../error/main');
|
||||||
@ -25,7 +24,7 @@ module.exports = Mn.View.extend({
|
|||||||
onRender: function () {
|
onRender: function () {
|
||||||
let view = this;
|
let view = this;
|
||||||
|
|
||||||
Api.AuditLog.getAll()
|
App.Api.AuditLog.getAll()
|
||||||
.then(response => {
|
.then(response => {
|
||||||
if (!view.isDestroyed() && response && response.length) {
|
if (!view.isDestroyed() && response && response.length) {
|
||||||
view.showChildView('list_region', new ListView({
|
view.showChildView('list_region', new ListView({
|
||||||
@ -33,8 +32,8 @@ module.exports = Mn.View.extend({
|
|||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
view.showChildView('list_region', new EmptyView({
|
view.showChildView('list_region', new EmptyView({
|
||||||
title: 'There are no logs.',
|
title: App.i18n('audit-log', 'empty'),
|
||||||
subtitle: 'As soon as you or another user changes something, history of those events will show up here.'
|
subtitle: App.i18n('audit-log', 'empty-subtitle')
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -43,7 +42,7 @@ module.exports = Mn.View.extend({
|
|||||||
code: err.code,
|
code: err.code,
|
||||||
message: err.message,
|
message: err.message,
|
||||||
retry: function () {
|
retry: function () {
|
||||||
Controller.showAuditLog();
|
App.Controller.showAuditLog();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -3,7 +3,9 @@
|
|||||||
const UserModel = require('../models/user');
|
const UserModel = require('../models/user');
|
||||||
|
|
||||||
let cache = {
|
let cache = {
|
||||||
User: new UserModel.Model()
|
User: new UserModel.Model(),
|
||||||
|
locale: 'en',
|
||||||
|
version: null
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = cache;
|
module.exports = cache;
|
||||||
|
@ -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
|
* Nginx Redirection Hosts
|
||||||
*/
|
*/
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h1 class="page-title">Hi <%- getUserName() %></h1>
|
<h1 class="page-title"><%- i18n('dashboard', 'title', {name: getUserName()}) %></h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<% if (columns) { %>
|
<% if (columns) { %>
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<i class="fe fe-zap"></i>
|
<i class="fe fe-zap"></i>
|
||||||
</span>
|
</span>
|
||||||
<div>
|
<div>
|
||||||
<h4 class="m-0"><a href="/nginx/proxy"><%- getHostStat('proxy') %> <small>Proxy Hosts</small></a></h4>
|
<h4 class="m-0"><a href="/nginx/proxy"><%- getHostStat('proxy') %> <small><%- i18n('proxy-hosts', 'title') %></small></a></h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -27,7 +27,7 @@
|
|||||||
<i class="fe fe-shuffle"></i>
|
<i class="fe fe-shuffle"></i>
|
||||||
</span>
|
</span>
|
||||||
<div>
|
<div>
|
||||||
<h4 class="m-0"><a href="/nginx/redirection"><%- getHostStat('redirection') %> <small>Redirection Hosts</small></a></h4>
|
<h4 class="m-0"><a href="/nginx/redirection"><%- getHostStat('redirection') %> <small><%- i18n('redirection-hosts', 'title') %></small></a></h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -42,7 +42,7 @@
|
|||||||
<i class="fe fe-radio"></i>
|
<i class="fe fe-radio"></i>
|
||||||
</span>
|
</span>
|
||||||
<div>
|
<div>
|
||||||
<h4 class="m-0"><a href="/nginx/stream"><%- getHostStat('stream') %> <small> Streams</small></a></h4>
|
<h4 class="m-0"><a href="/nginx/stream"><%- getHostStat('stream') %> <small> <%- i18n('streams', 'title') %></small></a></h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -57,7 +57,7 @@
|
|||||||
<i class="fe fe-zap-off"></i>
|
<i class="fe fe-zap-off"></i>
|
||||||
</span>
|
</span>
|
||||||
<div>
|
<div>
|
||||||
<h4 class="m-0"><a href="/nginx/404"><%- getHostStat('dead') %> <small>404 Hosts</small></a></h4>
|
<h4 class="m-0"><a href="/nginx/404"><%- getHostStat('dead') %> <small><%- i18n('dead-hosts', 'title') %></small></a></h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,5 +3,5 @@
|
|||||||
<%- message %>
|
<%- message %>
|
||||||
|
|
||||||
<% if (retry) { %>
|
<% if (retry) { %>
|
||||||
<br><br><a href="#" class="btn btn-sm btn-warning retry">Try again</a>
|
<br><br><a href="#" class="btn btn-sm btn-warning retry"><%- i18n('str', 'try-again') %></a>
|
||||||
<% } %>
|
<% } %>
|
||||||
|
25
src/frontend/js/app/i18n.js
Normal file
25
src/frontend/js/app/i18n.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const Cache = ('./cache');
|
||||||
|
const messages = require('../i18n/messages.json');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {String} namespace
|
||||||
|
* @param {String} key
|
||||||
|
* @param {Object} [data]
|
||||||
|
*/
|
||||||
|
module.exports = function (namespace, key, data) {
|
||||||
|
let locale = Cache.locale;
|
||||||
|
// check that the locale exists
|
||||||
|
if (typeof messages[locale] === 'undefined') {
|
||||||
|
locale = 'en';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof messages[locale][namespace] !== 'undefined' && typeof messages[locale][namespace][key] !== 'undefined') {
|
||||||
|
return messages[locale][namespace][key](data);
|
||||||
|
} else if (locale !== 'en' && typeof messages['en'][namespace] !== 'undefined' && typeof messages['en'][namespace][key] !== 'undefined') {
|
||||||
|
return messages['en'][namespace][key](data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return '(MISSING: ' + namespace + '/' + key + ')';
|
||||||
|
};
|
@ -9,14 +9,15 @@ const Router = require('./router');
|
|||||||
const Api = require('./api');
|
const Api = require('./api');
|
||||||
const Tokens = require('./tokens');
|
const Tokens = require('./tokens');
|
||||||
const UI = require('./ui/main');
|
const UI = require('./ui/main');
|
||||||
|
const i18n = require('./i18n');
|
||||||
|
|
||||||
const App = Mn.Application.extend({
|
const App = Mn.Application.extend({
|
||||||
|
|
||||||
Cache: Cache,
|
Cache: Cache,
|
||||||
Api: Api,
|
Api: Api,
|
||||||
UI: null,
|
UI: null,
|
||||||
|
i18n: i18n,
|
||||||
Controller: Controller,
|
Controller: Controller,
|
||||||
version: null,
|
|
||||||
|
|
||||||
region: {
|
region: {
|
||||||
el: '#app',
|
el: '#app',
|
||||||
@ -24,7 +25,7 @@ const App = Mn.Application.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
onStart: function (app, options) {
|
onStart: function (app, options) {
|
||||||
console.log('Welcome to Nginx Proxy Manager');
|
console.log(i18n('main', 'welcome'));
|
||||||
|
|
||||||
// Check if token is coming through
|
// Check if token is coming through
|
||||||
if (this.getParam('token')) {
|
if (this.getParam('token')) {
|
||||||
@ -34,12 +35,12 @@ const App = Mn.Application.extend({
|
|||||||
// Check if we are still logged in by refreshing the token
|
// Check if we are still logged in by refreshing the token
|
||||||
Api.status()
|
Api.status()
|
||||||
.then(result => {
|
.then(result => {
|
||||||
this.version = [result.version.major, result.version.minor, result.version.revision].join('.');
|
Cache.version = [result.version.major, result.version.minor, result.version.revision].join('.');
|
||||||
})
|
})
|
||||||
.then(Api.Tokens.refresh)
|
.then(Api.Tokens.refresh)
|
||||||
.then(this.bootstrap)
|
.then(this.bootstrap)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
console.info('You are logged in');
|
console.info(i18n('main', 'logged-in', Cache.User.attributes));
|
||||||
this.bootstrapTimer();
|
this.bootstrapTimer();
|
||||||
this.refreshTokenTimer();
|
this.refreshTokenTimer();
|
||||||
|
|
||||||
@ -60,7 +61,6 @@ const App = Mn.Application.extend({
|
|||||||
console.warn('Not logged in:', err.message);
|
console.warn('Not logged in:', err.message);
|
||||||
Controller.showLogin();
|
Controller.showLogin();
|
||||||
});
|
});
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
History: {
|
History: {
|
||||||
@ -86,7 +86,7 @@ const App = Mn.Application.extend({
|
|||||||
let ErrorView = Mn.View.extend({
|
let ErrorView = Mn.View.extend({
|
||||||
tagName: 'section',
|
tagName: 'section',
|
||||||
id: 'error',
|
id: 'error',
|
||||||
template: _.template('Error loading stuff. Please reload the app.')
|
template: _.template(i18n('main', 'unknown-error'))
|
||||||
});
|
});
|
||||||
|
|
||||||
this.getRegion().show(new ErrorView());
|
this.getRegion().show(new ErrorView());
|
||||||
@ -130,7 +130,7 @@ const App = Mn.Application.extend({
|
|||||||
Api.status()
|
Api.status()
|
||||||
.then(result => {
|
.then(result => {
|
||||||
let version = [result.version.major, result.version.minor, result.version.revision].join('.');
|
let version = [result.version.major, result.version.minor, result.version.revision].join('.');
|
||||||
if (version !== this.version) {
|
if (version !== Cache.version) {
|
||||||
document.location.reload();
|
document.location.reload();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
24
src/frontend/js/app/nginx/proxy/delete.ejs
Normal file
24
src/frontend/js/app/nginx/proxy/delete.ejs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title"><%- i18n('proxy-hosts', 'delete') %></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">
|
||||||
|
<%= i18n('proxy-hosts', 'delete-confirm', {domains: domain_names.join(', ')}) %>
|
||||||
|
Are you sure you want to delete the Proxy host for: <strong><%- domain_names.join(', ') %></strong>?
|
||||||
|
<% if (ssl_enabled) { %>
|
||||||
|
<br><br>
|
||||||
|
<%- i18n('proxy-hosts', 'delete-ssl') %>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary cancel" data-dismiss="modal"><%- i18n('str', 'cancel') %></button>
|
||||||
|
<button type="button" class="btn btn-danger save"><%- i18n('str', 'sure') %></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
36
src/frontend/js/app/nginx/proxy/delete.js
Normal file
36
src/frontend/js/app/nginx/proxy/delete.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const Mn = require('backbone.marionette');
|
||||||
|
const App = require('../../main');
|
||||||
|
const template = require('./delete.ejs');
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
App.Api.Nginx.ProxyHosts.delete(this.model.get('id'))
|
||||||
|
.then(() => {
|
||||||
|
App.Controller.showNginxProxy();
|
||||||
|
App.UI.closeModal();
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
alert(err.message);
|
||||||
|
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
@ -1,34 +1,35 @@
|
|||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title"><% if (typeof id !== 'undefined') { %>Edit<% } else { %>New<% } %> Proxy Host</h5>
|
<h5 class="modal-title"><%- i18n('proxy-hosts', 'form-title', {id: id}) %></h5>
|
||||||
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
|
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body has-tabs">
|
<div class="modal-body has-tabs">
|
||||||
<form>
|
<form>
|
||||||
<ul class="nav nav-tabs" role="tablist">
|
<ul class="nav nav-tabs" role="tablist">
|
||||||
<li role="presentation" class="nav-item"><a href="#details" aria-controls="tab1" role="tab" data-toggle="tab" class="nav-link active"><i class="fe fe-zap"></i> Details</a></li>
|
<li role="presentation" class="nav-item"><a href="#details" aria-controls="tab1" role="tab" data-toggle="tab" class="nav-link active"><i class="fe fe-zap"></i> <%- i18n('all-hosts', 'details') %></a></li>
|
||||||
<li role="presentation" class="nav-item"><a href="#ssl-options" aria-controls="tab2" role="tab" data-toggle="tab" class="nav-link"><i class="fe fe-shield"></i> SSL</a></li>
|
<li role="presentation" class="nav-item"><a href="#ssl-options" aria-controls="tab2" role="tab" data-toggle="tab" class="nav-link"><i class="fe fe-shield"></i> <%- i18n('str', 'ssl') %></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<!-- Details -->
|
<!-- Details -->
|
||||||
<div role="tabpanel" class="tab-pane active" id="details">
|
<div role="tabpanel" class="tab-pane active" id="details">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
||||||
<div class="col-sm-12 col-md-12">
|
<div class="col-sm-12 col-md-12">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">Domain Name <span class="form-required">*</span></label>
|
<label class="form-label"><%- i18n('all-hosts', 'domain-names') %> <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">
|
<input type="text" name="domain_names" class="form-control" id="input-domains" value="<%- domain_names.join(',') %>" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-8 col-md-8">
|
<div class="col-sm-8 col-md-8">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">Forward IP <span class="form-required">*</span></label>
|
<label class="form-label"><%- i18n('proxy-hosts', '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>
|
</div>
|
||||||
<div class="col-sm-4 col-md-4">
|
<div class="col-sm-4 col-md-4">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">Forward Port <span class="form-required">*</span></label>
|
<label class="form-label"><%- i18n('proxy-hosts', '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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -42,7 +43,7 @@
|
|||||||
<label class="custom-switch">
|
<label class="custom-switch">
|
||||||
<input type="checkbox" class="custom-switch-input" name="ssl_enabled" value="1"<%- ssl_enabled ? ' checked' : '' %>>
|
<input type="checkbox" class="custom-switch-input" name="ssl_enabled" value="1"<%- ssl_enabled ? ' checked' : '' %>>
|
||||||
<span class="custom-switch-indicator"></span>
|
<span class="custom-switch-indicator"></span>
|
||||||
<span class="custom-switch-description">Enable SSL</span>
|
<span class="custom-switch-description"><%- i18n('all-hosts', 'enable-ssl') %></span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -51,21 +52,21 @@
|
|||||||
<label class="custom-switch">
|
<label class="custom-switch">
|
||||||
<input type="checkbox" class="custom-switch-input" name="ssl_forced" value="1"<%- ssl_forced ? ' checked' : '' %><%- ssl_enabled ? '' : ' disabled' %>>
|
<input type="checkbox" class="custom-switch-input" name="ssl_forced" value="1"<%- ssl_forced ? ' checked' : '' %><%- ssl_enabled ? '' : ' disabled' %>>
|
||||||
<span class="custom-switch-indicator"></span>
|
<span class="custom-switch-indicator"></span>
|
||||||
<span class="custom-switch-description">Force SSL</span>
|
<span class="custom-switch-description"><%- i18n('all-hosts', 'force-ssl') %></span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-12 col-md-12">
|
<div class="col-sm-12 col-md-12">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">Certificate Provider</label>
|
<label class="form-label"><%- i18n('all-hosts', 'cert-provider') %></label>
|
||||||
<div class="selectgroup w-100">
|
<div class="selectgroup w-100">
|
||||||
<label class="selectgroup-item">
|
<label class="selectgroup-item">
|
||||||
<input type="radio" name="ssl_provider" value="letsencrypt" class="selectgroup-input"<%- ssl_provider !== 'other' ? ' checked' : '' %>>
|
<input type="radio" name="ssl_provider" value="letsencrypt" class="selectgroup-input"<%- ssl_provider !== 'other' ? ' checked' : '' %>>
|
||||||
<span class="selectgroup-button">Let's Encrypt</span>
|
<span class="selectgroup-button"><%- i18n('ssl', 'letsencrypt') %></span>
|
||||||
</label>
|
</label>
|
||||||
<label class="selectgroup-item">
|
<label class="selectgroup-item">
|
||||||
<input type="radio" name="ssl_provider" value="other" class="selectgroup-input"<%- ssl_provider === 'other' ? ' checked' : '' %>>
|
<input type="radio" name="ssl_provider" value="other" class="selectgroup-input"<%- ssl_provider === 'other' ? ' checked' : '' %>>
|
||||||
<span class="selectgroup-button">Other</span>
|
<span class="selectgroup-button"><%- i18n('ssl', 'other') %></span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -74,7 +75,7 @@
|
|||||||
<!-- Lets encrypt -->
|
<!-- Lets encrypt -->
|
||||||
<div class="col-sm-12 col-md-12 letsencrypt-ssl">
|
<div class="col-sm-12 col-md-12 letsencrypt-ssl">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">Email Address for Let's Encrypt <span class="form-required">*</span></label>
|
<label class="form-label"><%- i18n('ssl', 'letsencrypt-email') %> <span class="form-required">*</span></label>
|
||||||
<input name="meta[letsencrypt_email]" type="email" class="form-control" placeholder="" value="<%- getLetsencryptEmail() %>" required>
|
<input name="meta[letsencrypt_email]" type="email" class="form-control" placeholder="" value="<%- getLetsencryptEmail() %>" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -83,7 +84,7 @@
|
|||||||
<label class="custom-switch">
|
<label class="custom-switch">
|
||||||
<input type="checkbox" class="custom-switch-input" name="meta[letsencrypt_agree]" value="1" required<%- getLetsencryptAgree() ? ' checked' : '' %>>
|
<input type="checkbox" class="custom-switch-input" name="meta[letsencrypt_agree]" value="1" required<%- getLetsencryptAgree() ? ' checked' : '' %>>
|
||||||
<span class="custom-switch-indicator"></span>
|
<span class="custom-switch-indicator"></span>
|
||||||
<span class="custom-switch-description">I Agree to the <a href="https://letsencrypt.org/repository/" target="_blank">Let's Encrypt Terms of Service</a> <span class="form-required">*</span></span>
|
<span class="custom-switch-description"><%= i18n('ssl', 'letsencrypt-agree', {url: 'https://letsencrypt.org/repository/'}) %> <span class="form-required">*</span></span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -91,19 +92,19 @@
|
|||||||
<!-- Other -->
|
<!-- Other -->
|
||||||
<div class="col-sm-12 col-md-12 other-ssl">
|
<div class="col-sm-12 col-md-12 other-ssl">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="form-label">Certificate</div>
|
<div class="form-label"><%- i18n('all-hosts', 'other-certificate') %></div>
|
||||||
<div class="custom-file">
|
<div class="custom-file">
|
||||||
<input type="file" class="custom-file-input" name="meta[other_ssl_certificate]">
|
<input type="file" class="custom-file-input" name="meta[other_ssl_certificate]">
|
||||||
<label class="custom-file-label">Choose file</label>
|
<label class="custom-file-label"><%- i18n('str', 'choose-file') %></label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-12 col-md-12 other-ssl">
|
<div class="col-sm-12 col-md-12 other-ssl">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="form-label">Certificate Key</div>
|
<div class="form-label"><%- i18n('all-hosts', 'other-certificate-key') %></div>
|
||||||
<div class="custom-file">
|
<div class="custom-file">
|
||||||
<input type="file" class="custom-file-input" name="meta[other_ssl_certificate_key]">
|
<input type="file" class="custom-file-input" name="meta[other_ssl_certificate_key]">
|
||||||
<label class="custom-file-label">Choose file</label>
|
<label class="custom-file-label"><%- i18n('str', 'choose-file') %></label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -115,7 +116,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary cancel" data-dismiss="modal">Cancel</button>
|
<button type="button" class="btn btn-secondary cancel" data-dismiss="modal"><%- i18n('str', 'cancel') %></button>
|
||||||
<button type="button" class="btn btn-teal save">Save</button>
|
<button type="button" class="btn btn-teal save"><%- i18n('str', 'save') %></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,15 +2,13 @@
|
|||||||
|
|
||||||
const _ = require('underscore');
|
const _ = require('underscore');
|
||||||
const Mn = require('backbone.marionette');
|
const Mn = require('backbone.marionette');
|
||||||
const template = require('./form.ejs');
|
|
||||||
const Controller = require('../../controller');
|
|
||||||
const Cache = require('../../cache');
|
|
||||||
const Api = require('../../api');
|
|
||||||
const App = require('../../main');
|
const App = require('../../main');
|
||||||
const ProxyHostModel = require('../../../models/proxy-host');
|
const ProxyHostModel = require('../../../models/proxy-host');
|
||||||
|
const template = require('./form.ejs');
|
||||||
|
|
||||||
require('jquery-serializejson');
|
require('jquery-serializejson');
|
||||||
require('jquery-mask-plugin');
|
require('jquery-mask-plugin');
|
||||||
|
require('selectize');
|
||||||
|
|
||||||
module.exports = Mn.View.extend({
|
module.exports = Mn.View.extend({
|
||||||
template: template,
|
template: template,
|
||||||
@ -18,7 +16,7 @@ module.exports = Mn.View.extend({
|
|||||||
|
|
||||||
ui: {
|
ui: {
|
||||||
form: 'form',
|
form: 'form',
|
||||||
domain_name: 'input[name="domain_name"]',
|
domain_names: 'input[name="domain_names"]',
|
||||||
forward_ip: 'input[name="forward_ip"]',
|
forward_ip: 'input[name="forward_ip"]',
|
||||||
buttons: '.modal-footer button',
|
buttons: '.modal-footer button',
|
||||||
cancel: 'button.cancel',
|
cancel: 'button.cancel',
|
||||||
@ -73,13 +71,17 @@ module.exports = Mn.View.extend({
|
|||||||
data[idx] = item;
|
data[idx] = item;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (typeof data.domain_names === 'string' && data.domain_names) {
|
||||||
|
data.domain_names = data.domain_names.split(',');
|
||||||
|
}
|
||||||
|
|
||||||
// Process
|
// Process
|
||||||
this.ui.buttons.prop('disabled', true).addClass('btn-disabled');
|
this.ui.buttons.prop('disabled', true).addClass('btn-disabled');
|
||||||
let method = Api.Nginx.ProxyHosts.create;
|
let method = App.Api.Nginx.ProxyHosts.create;
|
||||||
|
|
||||||
if (this.model.get('id')) {
|
if (this.model.get('id')) {
|
||||||
// edit
|
// edit
|
||||||
method = Api.Nginx.ProxyHosts.update;
|
method = App.Api.Nginx.ProxyHosts.update;
|
||||||
data.id = this.model.get('id');
|
data.id = this.model.get('id');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,8 +89,8 @@ module.exports = Mn.View.extend({
|
|||||||
.then(result => {
|
.then(result => {
|
||||||
view.model.set(result);
|
view.model.set(result);
|
||||||
App.UI.closeModal(function () {
|
App.UI.closeModal(function () {
|
||||||
if (method === Api.Nginx.ProxyHosts.create) {
|
if (method === App.Api.Nginx.ProxyHosts.create) {
|
||||||
Controller.showNginxProxy();
|
App.Controller.showNginxProxy();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
@ -101,7 +103,7 @@ module.exports = Mn.View.extend({
|
|||||||
|
|
||||||
templateContext: {
|
templateContext: {
|
||||||
getLetsencryptEmail: function () {
|
getLetsencryptEmail: function () {
|
||||||
return typeof this.meta.letsencrypt_email !== 'undefined' ? this.meta.letsencrypt_email : Cache.User.get('email');
|
return typeof this.meta.letsencrypt_email !== 'undefined' ? this.meta.letsencrypt_email : App.Cache.User.get('email');
|
||||||
},
|
},
|
||||||
|
|
||||||
getLetsencryptAgree: function () {
|
getLetsencryptAgree: function () {
|
||||||
@ -118,10 +120,19 @@ module.exports = Mn.View.extend({
|
|||||||
this.ui.ssl_enabled.trigger('change');
|
this.ui.ssl_enabled.trigger('change');
|
||||||
this.ui.ssl_provider.trigger('change');
|
this.ui.ssl_provider.trigger('change');
|
||||||
|
|
||||||
this.ui.domain_name[0].oninvalid = function () {
|
this.ui.domain_names.selectize({
|
||||||
this.setCustomValidity('Please enter a valid domain name. Domain wildcards are allowed: *.yourdomain.com');
|
delimiter: ',',
|
||||||
|
persist: false,
|
||||||
|
maxOptions: 15,
|
||||||
|
create: function (input) {
|
||||||
|
return {
|
||||||
|
value: input,
|
||||||
|
text: input
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
createFilter: /^(?:\*\.)?(?:[^.*]+\.?)+[^.]$/
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
initialize: function (options) {
|
initialize: function (options) {
|
||||||
if (typeof options.model === 'undefined' || !options.model) {
|
if (typeof options.model === 'undefined' || !options.model) {
|
||||||
|
@ -1,32 +1,40 @@
|
|||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<div class="avatar d-block" style="background-image: url(<%- avatar || '/images/default-avatar.jpg' %>)">
|
<div class="avatar d-block" style="background-image: url(<%- owner.avatar || '/images/default-avatar.jpg' %>)" title="Owned by <%- owner.name %>">
|
||||||
<span class="avatar-status <%- is_disabled ? 'bg-red' : 'bg-green' %>"></span>
|
<span class="avatar-status <%- owner.is_disabled ? 'bg-red' : 'bg-green' %>"></span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div><%- name %></div>
|
<div>
|
||||||
|
<% domain_names.map(function(host) {
|
||||||
|
%>
|
||||||
|
<span class="tag"><%- host %></span>
|
||||||
|
<%
|
||||||
|
});
|
||||||
|
%>
|
||||||
|
</div>
|
||||||
<div class="small text-muted">
|
<div class="small text-muted">
|
||||||
Created: <%- formatDbDate(created_on, 'Do MMMM YYYY') %>
|
<%- i18n('str', 'created-on', {date: formatDbDate(created_on, 'Do MMMM YYYY')}) %>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div><%- email %></div>
|
<div class="text-monospace"><%- forward_ip %>:<%- forward_port %></div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div><%- roles.join(', ') %></div>
|
<div><%- ssl_enabled && ssl_provider ? i18n('ssl', ssl_provider) : i18n('ssl', 'none') %></div>
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
<div><%- access_list_id ? access_list.name : i18n('str', 'public') %></div>
|
||||||
|
</td>
|
||||||
|
<% if (canManage) { %>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<div class="item-action dropdown">
|
<div class="item-action dropdown">
|
||||||
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
|
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
|
||||||
<div class="dropdown-menu dropdown-menu-right">
|
<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 dropdown-item"><i class="dropdown-icon fe fe-edit"></i> <%- i18n('str', 'edit') %></a>
|
||||||
<a href="#" class="edit-permissions dropdown-item"><i class="dropdown-icon fe fe-shield"></i> Edit Permissions</a>
|
<a href="#" class="logs dropdown-item"><i class="dropdown-icon fe fe-book"></i> <%- i18n('str', 'logs') %></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>
|
|
||||||
<div class="dropdown-divider"></div>
|
<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> <%- i18n('str', 'delete') %></a>
|
||||||
<% } %>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
<% } %>
|
@ -1,10 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const Mn = require('backbone.marionette');
|
const Mn = require('backbone.marionette');
|
||||||
const Controller = require('../../../controller');
|
const App = require('../../../main');
|
||||||
const Api = require('../../../api');
|
|
||||||
const Cache = require('../../../cache');
|
|
||||||
const Tokens = require('../../../tokens');
|
|
||||||
const template = require('./item.ejs');
|
const template = require('./item.ejs');
|
||||||
|
|
||||||
module.exports = Mn.View.extend({
|
module.exports = Mn.View.extend({
|
||||||
@ -12,58 +9,24 @@ module.exports = Mn.View.extend({
|
|||||||
tagName: 'tr',
|
tagName: 'tr',
|
||||||
|
|
||||||
ui: {
|
ui: {
|
||||||
edit: 'a.edit-user',
|
edit: 'a.edit',
|
||||||
permissions: 'a.edit-permissions',
|
delete: 'a.delete'
|
||||||
password: 'a.set-password',
|
|
||||||
login: 'a.login',
|
|
||||||
delete: 'a.delete-user'
|
|
||||||
},
|
},
|
||||||
|
|
||||||
events: {
|
events: {
|
||||||
'click @ui.edit': function (e) {
|
'click @ui.edit': function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
Controller.showUserForm(this.model);
|
App.Controller.showNginxProxyForm(this.model);
|
||||||
},
|
|
||||||
|
|
||||||
'click @ui.permissions': function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
Controller.showUserPermissions(this.model);
|
|
||||||
},
|
|
||||||
|
|
||||||
'click @ui.password': function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
Controller.showUserPasswordForm(this.model);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
'click @ui.delete': function (e) {
|
'click @ui.delete': function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
Controller.showUserDeleteConfirm(this.model);
|
App.Controller.showNginxProxyDeleteConfirm(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');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
templateContext: {
|
templateContext: {
|
||||||
isSelf: function () {
|
canManage: App.Cache.User.canManage('proxy_hosts')
|
||||||
return Cache.User.get('id') === this.id;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
initialize: function () {
|
initialize: function () {
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<th width="30"> </th>
|
<th width="30"> </th>
|
||||||
<th>Name</th>
|
<th><%- i18n('str', 'source') %></th>
|
||||||
<th>Email</th>
|
<th><%- i18n('str', 'destination') %></th>
|
||||||
<th>Roles</th>
|
<th><%- i18n('str', 'ssl') %></th>
|
||||||
|
<th><%- i18n('str', 'access') %></th>
|
||||||
|
<% if (canManage) { %>
|
||||||
<th> </th>
|
<th> </th>
|
||||||
|
<% } %>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<!-- items -->
|
<!-- items -->
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const Mn = require('backbone.marionette');
|
const Mn = require('backbone.marionette');
|
||||||
|
const App = require('../../../main');
|
||||||
const ItemView = require('./item');
|
const ItemView = require('./item');
|
||||||
const template = require('./main.ejs');
|
const template = require('./main.ejs');
|
||||||
|
|
||||||
@ -21,6 +22,10 @@ module.exports = Mn.View.extend({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
templateContext: {
|
||||||
|
canManage: App.Cache.User.canManage('proxy_hosts')
|
||||||
|
},
|
||||||
|
|
||||||
onRender: function () {
|
onRender: function () {
|
||||||
this.showChildView('body', new TableBody({
|
this.showChildView('body', new TableBody({
|
||||||
collection: this.collection
|
collection: this.collection
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-status bg-success"></div>
|
<div class="card-status bg-success"></div>
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h3 class="card-title">Proxy Hosts</h3>
|
<h3 class="card-title"><%- i18n('proxy-hosts', 'title') %></h3>
|
||||||
<div class="card-options">
|
<div class="card-options">
|
||||||
<% if (showAddButton) { %>
|
<% if (showAddButton) { %>
|
||||||
<a href="#" class="btn btn-outline-success btn-sm ml-2 add-item">Add Proxy Host</a>
|
<a href="#" class="btn btn-outline-success btn-sm ml-2 add-item"><%- i18n('proxy-hosts', 'add') %></a>
|
||||||
<% } %>
|
<% } %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const Mn = require('backbone.marionette');
|
const Mn = require('backbone.marionette');
|
||||||
|
const App = require('../../main');
|
||||||
const ProxyHostModel = require('../../../models/proxy-host');
|
const ProxyHostModel = require('../../../models/proxy-host');
|
||||||
const Api = require('../../api');
|
|
||||||
const Cache = require('../../cache');
|
|
||||||
const Controller = require('../../controller');
|
|
||||||
const ListView = require('./list/main');
|
const ListView = require('./list/main');
|
||||||
const ErrorView = require('../../error/main');
|
const ErrorView = require('../../error/main');
|
||||||
const template = require('./main.ejs');
|
|
||||||
const EmptyView = require('../../empty/main');
|
const EmptyView = require('../../empty/main');
|
||||||
|
const template = require('./main.ejs');
|
||||||
|
|
||||||
module.exports = Mn.View.extend({
|
module.exports = Mn.View.extend({
|
||||||
id: 'nginx-proxy',
|
id: 'nginx-proxy',
|
||||||
@ -27,18 +25,18 @@ module.exports = Mn.View.extend({
|
|||||||
events: {
|
events: {
|
||||||
'click @ui.add': function (e) {
|
'click @ui.add': function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
Controller.showNginxProxyForm();
|
App.Controller.showNginxProxyForm();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
templateContext: {
|
templateContext: {
|
||||||
showAddButton: Cache.User.canManage('proxy_hosts')
|
showAddButton: App.Cache.User.canManage('proxy_hosts')
|
||||||
},
|
},
|
||||||
|
|
||||||
onRender: function () {
|
onRender: function () {
|
||||||
let view = this;
|
let view = this;
|
||||||
|
|
||||||
Api.Nginx.ProxyHosts.getAll()
|
App.Api.Nginx.ProxyHosts.getAll(['owner', 'access_list'])
|
||||||
.then(response => {
|
.then(response => {
|
||||||
if (!view.isDestroyed()) {
|
if (!view.isDestroyed()) {
|
||||||
if (response && response.length) {
|
if (response && response.length) {
|
||||||
@ -46,16 +44,16 @@ module.exports = Mn.View.extend({
|
|||||||
collection: new ProxyHostModel.Collection(response)
|
collection: new ProxyHostModel.Collection(response)
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
let manage = Cache.User.canManage('proxy_hosts');
|
let manage = App.Cache.User.canManage('proxy_hosts');
|
||||||
|
|
||||||
view.showChildView('list_region', new EmptyView({
|
view.showChildView('list_region', new EmptyView({
|
||||||
title: 'There are no Proxy Hosts',
|
title: App.i18n('proxy-hosts', 'empty'),
|
||||||
subtitle: manage ? 'Why don\'t you create one?' : 'And you don\'t have permission to create one.',
|
subtitle: App.i18n('all-hosts', 'empty-subtitle', {manage: manage}),
|
||||||
link: manage ? 'Add Proxy Host' : null,
|
link: manage ? App.i18n('proxy-hosts', 'add') : null,
|
||||||
btn_color: 'success',
|
btn_color: 'success',
|
||||||
permission: 'proxy_hosts',
|
permission: 'proxy_hosts',
|
||||||
action: function () {
|
action: function () {
|
||||||
Controller.showNginxProxyForm();
|
App.Controller.showNginxProxyForm();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@ -66,7 +64,7 @@ module.exports = Mn.View.extend({
|
|||||||
code: err.code,
|
code: err.code,
|
||||||
message: err.message,
|
message: err.message,
|
||||||
retry: function () {
|
retry: function () {
|
||||||
Controller.showNginxProxy();
|
App.Controller.showNginxProxy();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -3,12 +3,14 @@
|
|||||||
<div class="row align-items-center">
|
<div class="row align-items-center">
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
<ul class="list-inline list-inline-dots mb-0">
|
<ul class="list-inline list-inline-dots mb-0">
|
||||||
<li class="list-inline-item"><a href="https://github.com/jc21/docker-registry-ui?utm_source=docker-registry-ui">Fork me on Github</a></li>
|
<li class="list-inline-item"><a href="https://github.com/jc21/nginx-proxy-manager?utm_source=nginx-proxy-manager"><%- i18n('footer', 'fork-me') %></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-lg-auto mt-3 mt-lg-0 text-center">
|
<div class="col-12 col-lg-auto mt-3 mt-lg-0 text-center">
|
||||||
v<%- getVersion() %> © 2018 <a href="https://jc21.com?utm_source=nginx-proxy-manager" target="_blank">jc21.com</a>. Theme by <a href="https://tabler.github.io/?utm_source=nginx-proxy-manager" target="_blank">Tabler</a>
|
<%- i18n('main', 'version', {version: getVersion()}) %>
|
||||||
|
<%= i18n('footer', 'copy', {url: 'https://jc21.com?utm_source=nginx-proxy-manager'}) %>
|
||||||
|
<%= i18n('footer', 'theme', {url: 'https://tabler.github.io/?utm_source=nginx-proxy-manager'}) %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
const Mn = require('backbone.marionette');
|
const Mn = require('backbone.marionette');
|
||||||
const template = require('./main.ejs');
|
const template = require('./main.ejs');
|
||||||
const App = require('../../main');
|
const Cache = require('../../cache');
|
||||||
|
|
||||||
module.exports = Mn.View.extend({
|
module.exports = Mn.View.extend({
|
||||||
className: 'container',
|
className: 'container',
|
||||||
@ -10,7 +10,7 @@ module.exports = Mn.View.extend({
|
|||||||
|
|
||||||
templateContext: {
|
templateContext: {
|
||||||
getVersion: function () {
|
getVersion: function () {
|
||||||
return App.version;
|
return Cache.version || '0.0.0';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<a class="navbar-brand" href="/">
|
<a class="navbar-brand" href="/">
|
||||||
<img src="/images/favicons/favicon-32x32.png" border="0"> Nginx Proxy Manager
|
<img src="/images/favicons/favicon-32x32.png" border="0"> <%- i18n('main', 'app') %>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="d-flex order-lg-2 ml-auto">
|
<div class="d-flex order-lg-2 ml-auto">
|
||||||
@ -9,16 +9,16 @@
|
|||||||
<a href="#" class="nav-link pr-0 leading-none" data-toggle="dropdown">
|
<a href="#" class="nav-link pr-0 leading-none" data-toggle="dropdown">
|
||||||
<span class="avatar" style="background-image: url(<%- getUserField('avatar', '/images/default-avatar.jpg') %>)"></span>
|
<span class="avatar" style="background-image: url(<%- getUserField('avatar', '/images/default-avatar.jpg') %>)"></span>
|
||||||
<span class="ml-2 d-none d-lg-block">
|
<span class="ml-2 d-none d-lg-block">
|
||||||
<span class="text-default"><%- getUserField('nickname', null) || getUserField('name', 'Unknown User') %></span>
|
<span class="text-default"><%- getUserField('nickname', null) || getUserField('name', i18n('main', 'unknown-user')) %></span>
|
||||||
<small class="text-muted d-block mt-1"><%- getRole() %></small>
|
<small class="text-muted d-block mt-1"><%- getRole() %></small>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
<div class="dropdown-menu dropdown-menu-right dropdown-menu-arrow">
|
<div class="dropdown-menu dropdown-menu-right dropdown-menu-arrow">
|
||||||
<a class="dropdown-item edit-details" href="#">
|
<a class="dropdown-item edit-details" href="#">
|
||||||
<i class="dropdown-icon fe fe-user"></i> Edit Details
|
<i class="dropdown-icon fe fe-user"></i> <%- i18n('users', 'edit-details') %>
|
||||||
</a>
|
</a>
|
||||||
<a class="dropdown-item change-password" href="#">
|
<a class="dropdown-item change-password" href="#">
|
||||||
<i class="dropdown-icon fe fe-lock"></i> Change Password
|
<i class="dropdown-icon fe fe-lock"></i> <%- i18n('users', 'change-password') %>
|
||||||
</a>
|
</a>
|
||||||
<div class="dropdown-divider"></div>
|
<div class="dropdown-divider"></div>
|
||||||
<a class="dropdown-item logout" href="/logout">
|
<a class="dropdown-item logout" href="/logout">
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
const $ = require('jquery');
|
const $ = require('jquery');
|
||||||
const Mn = require('backbone.marionette');
|
const Mn = require('backbone.marionette');
|
||||||
|
const i18n = require('../../i18n');
|
||||||
const Cache = require('../../cache');
|
const Cache = require('../../cache');
|
||||||
const Controller = require('../../controller');
|
const Controller = require('../../controller');
|
||||||
const Tokens = require('../../tokens');
|
const Tokens = require('../../tokens');
|
||||||
@ -50,15 +51,15 @@ module.exports = Mn.View.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
getRole: function () {
|
getRole: function () {
|
||||||
return Cache.User.isAdmin() ? 'Administrator' : 'Apache Helicopter';
|
return i18n('roles', Cache.User.isAdmin() ? 'admin' : 'user');
|
||||||
},
|
},
|
||||||
|
|
||||||
getLogoutText: function () {
|
getLogoutText: function () {
|
||||||
if (Tokens.getTokenCount() > 1) {
|
if (Tokens.getTokenCount() > 1) {
|
||||||
return 'Sign back in as ' + Tokens.getNextTokenName();
|
return i18n('main', 'sign-in-as', {name: Tokens.getNextTokenName()});
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'Sign out';
|
return i18n('str', 'sign-out');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -3,39 +3,39 @@
|
|||||||
<div class="col-lg order-lg-first">
|
<div class="col-lg order-lg-first">
|
||||||
<ul class="nav nav-tabs border-0 flex-column flex-lg-row">
|
<ul class="nav nav-tabs border-0 flex-column flex-lg-row">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a href="/" class="nav-link"><i class="fe fe-home"></i> Dashboard</a>
|
<a href="/" class="nav-link"><i class="fe fe-home"></i> <%- i18n('menu', 'dashboard') %></a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<a href="#" class="nav-link" data-toggle="dropdown"><i class="fe fe-monitor"></i> Hosts</a>
|
<a href="#" class="nav-link" data-toggle="dropdown"><i class="fe fe-monitor"></i> <%- i18n('menu', 'hosts') %></a>
|
||||||
<div class="dropdown-menu dropdown-menu-arrow">
|
<div class="dropdown-menu dropdown-menu-arrow">
|
||||||
<% if (canShow('proxy_hosts')) { %>
|
<% if (canShow('proxy_hosts')) { %>
|
||||||
<a href="/nginx/proxy" class="dropdown-item ">Proxy Hosts</a>
|
<a href="/nginx/proxy" class="dropdown-item "><%- i18n('proxy-hosts', 'title') %></a>
|
||||||
<% } %>
|
<% } %>
|
||||||
|
|
||||||
<% if (canShow('redirection_hosts')) { %>
|
<% if (canShow('redirection_hosts')) { %>
|
||||||
<a href="/nginx/redirection" class="dropdown-item ">Redirections</a>
|
<a href="/nginx/redirection" class="dropdown-item "><%- i18n('redirection-hosts', 'title') %></a>
|
||||||
<% } %>
|
<% } %>
|
||||||
|
|
||||||
<% if (canShow('streams')) { %>
|
<% if (canShow('streams')) { %>
|
||||||
<a href="/nginx/stream" class="dropdown-item ">Streams</a>
|
<a href="/nginx/stream" class="dropdown-item "><%- i18n('streams', 'title') %></a>
|
||||||
<% } %>
|
<% } %>
|
||||||
|
|
||||||
<% if (canShow('dead_hosts')) { %>
|
<% if (canShow('dead_hosts')) { %>
|
||||||
<a href="/nginx/404" class="dropdown-item ">404 Hosts</a>
|
<a href="/nginx/404" class="dropdown-item "><%- i18n('dead-hosts', 'title') %></a>
|
||||||
<% } %>
|
<% } %>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<% if (canShow('access_lists')) { %>
|
<% if (canShow('access_lists')) { %>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a href="/nginx/access" class="nav-link"><i class="fe fe-lock"></i> Access Lists</a>
|
<a href="/nginx/access" class="nav-link"><i class="fe fe-lock"></i> <%- i18n('access-lists', 'title') %></a>
|
||||||
</li>
|
</li>
|
||||||
<% } %>
|
<% } %>
|
||||||
<% if (isAdmin()) { %>
|
<% if (isAdmin()) { %>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a href="/users" class="nav-link"><i class="fe fe-users"></i> Users</a>
|
<a href="/users" class="nav-link"><i class="fe fe-users"></i> <%- i18n('users', 'title') %></a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a href="/audit-log" class="nav-link"><i class="fe fe-book-open"></i> Audit Log</a>
|
<a href="/audit-log" class="nav-link"><i class="fe fe-book-open"></i> <%- i18n('audit-log', 'title') %></a>
|
||||||
</li>
|
</li>
|
||||||
<% } %>
|
<% } %>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title">Delete <%- name %></h5>
|
<h5 class="modal-title"><%- i18n('users', 'delete', {name: name}) %></h5>
|
||||||
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
|
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form>
|
<form>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12 col-md-12">
|
<div class="col-sm-12 col-md-12">
|
||||||
Are you sure you want to delete <strong><%- name %></strong>?
|
<%= i18n('users', 'delete-confirm', {name: name}) %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary cancel" data-dismiss="modal">Cancel</button>
|
<button type="button" class="btn btn-secondary cancel" data-dismiss="modal"><%- i18n('str', 'cancel') %></button>
|
||||||
<button type="button" class="btn btn-danger save">Yes I'm Sure</button>
|
<button type="button" class="btn btn-danger save"><%- i18n('str', 'sure') %></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
const Mn = require('backbone.marionette');
|
const Mn = require('backbone.marionette');
|
||||||
const template = require('./delete.ejs');
|
const template = require('./delete.ejs');
|
||||||
const Controller = require('../controller');
|
|
||||||
const Api = require('../api');
|
|
||||||
const App = require('../main');
|
const App = require('../main');
|
||||||
|
|
||||||
require('jquery-serializejson');
|
require('jquery-serializejson');
|
||||||
@ -24,9 +22,9 @@ module.exports = Mn.View.extend({
|
|||||||
'click @ui.save': function (e) {
|
'click @ui.save': function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
Api.Users.delete(this.model.get('id'))
|
App.Api.Users.delete(this.model.get('id'))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
Controller.showUsers();
|
App.Controller.showUsers();
|
||||||
App.UI.closeModal();
|
App.UI.closeModal();
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title"><% if (typeof id !== 'undefined') { %>Edit<% } else { %>New<% } %> User</h5>
|
<h5 class="modal-title"><%- i18n('users', 'form-title', {id: id}) %></h5>
|
||||||
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
|
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
@ -8,33 +8,33 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-6 col-md-6">
|
<div class="col-sm-6 col-md-6">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">Full Name <span class="form-required">*</span></label>
|
<label class="form-label"><%- i18n('users', 'full-name') %> <span class="form-required">*</span></label>
|
||||||
<input name="name" type="text" class="form-control" placeholder="Joe Citizen" value="<%- name %>" required>
|
<input name="name" type="text" class="form-control" placeholder="Joe Citizen" value="<%- name %>" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-6 col-md-6">
|
<div class="col-sm-6 col-md-6">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">Nickname</label>
|
<label class="form-label"><%- i18n('users', 'nickname') %></label>
|
||||||
<input name="nickname" type="text" class="form-control" placeholder="Joe" value="<%- nickname %>">
|
<input name="nickname" type="text" class="form-control" placeholder="Joe" value="<%- nickname %>">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-12 col-md-12">
|
<div class="col-sm-12 col-md-12">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">Email <span class="form-required">*</span></label>
|
<label class="form-label"><%- i18n('str', 'email') %> <span class="form-required">*</span></label>
|
||||||
<input name="email" type="email" class="form-control" placeholder="joe@example.com" value="<%- email %>" required>
|
<input name="email" type="email" class="form-control" placeholder="joe@example.com" value="<%- email %>" required>
|
||||||
<div class="invalid-feedback secret-error"></div>
|
<div class="invalid-feedback secret-error"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<% if (isAdmin()) { %>
|
<% if (isAdmin()) { %>
|
||||||
<div class="col-sm-12 col-md-12">
|
<div class="col-sm-12 col-md-12">
|
||||||
<div class="form-label">Roles</div>
|
<div class="form-label"><%- i18n('roles', 'title') %></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-6 col-md-6">
|
<div class="col-sm-6 col-md-6">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="custom-switch">
|
<label class="custom-switch">
|
||||||
<input type="checkbox" class="custom-switch-input" name="is_admin" value="1"<%- isAdmin() ? ' checked' : '' %><%- isSelf() ? ' disabled' : '' %>>
|
<input type="checkbox" class="custom-switch-input" name="is_admin" value="1"<%- isAdmin() ? ' checked' : '' %><%- isSelf() ? ' disabled' : '' %>>
|
||||||
<span class="custom-switch-indicator"></span>
|
<span class="custom-switch-indicator"></span>
|
||||||
<span class="custom-switch-description">Administrator</span>
|
<span class="custom-switch-description"><%- i18n('roles', 'admin') %></span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -43,7 +43,7 @@
|
|||||||
<label class="custom-switch">
|
<label class="custom-switch">
|
||||||
<input type="checkbox" class="custom-switch-input" name="is_disabled" value="1"<%- is_disabled ? ' checked' : '' %><%- isSelf() ? ' disabled' : '' %>>
|
<input type="checkbox" class="custom-switch-input" name="is_disabled" value="1"<%- is_disabled ? ' checked' : '' %><%- isSelf() ? ' disabled' : '' %>>
|
||||||
<span class="custom-switch-indicator"></span>
|
<span class="custom-switch-indicator"></span>
|
||||||
<span class="custom-switch-description">Disabled</span>
|
<span class="custom-switch-description"><%- i18n('str', 'disabled') %></span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -52,7 +52,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary cancel" data-dismiss="modal">Cancel</button>
|
<button type="button" class="btn btn-secondary cancel" data-dismiss="modal"><%- i18n('str', 'cancel') %></button>
|
||||||
<button type="button" class="btn btn-teal save">Save</button>
|
<button type="button" class="btn btn-teal save"><%- i18n('str', 'save') %></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const Mn = require('backbone.marionette');
|
const Mn = require('backbone.marionette');
|
||||||
const template = require('./form.ejs');
|
|
||||||
const Controller = require('../controller');
|
|
||||||
const Cache = require('../cache');
|
|
||||||
const Api = require('../api');
|
|
||||||
const App = require('../main');
|
const App = require('../main');
|
||||||
const UserModel = require('../../models/user');
|
const UserModel = require('../../models/user');
|
||||||
|
const template = require('./form.ejs');
|
||||||
|
|
||||||
require('jquery-serializejson');
|
require('jquery-serializejson');
|
||||||
|
|
||||||
@ -34,45 +31,45 @@ module.exports = Mn.View.extend({
|
|||||||
|
|
||||||
// admin@example.com is not allowed
|
// admin@example.com is not allowed
|
||||||
if (data.email === 'admin@example.com') {
|
if (data.email === 'admin@example.com') {
|
||||||
this.ui.error.text('Default email address must be changed').show();
|
this.ui.error.text(App.i18n('users', 'default_error')).show();
|
||||||
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manipulate
|
// Manipulate
|
||||||
data.roles = [];
|
data.roles = [];
|
||||||
if ((this.model.get('id') === Cache.User.get('id') && this.model.isAdmin()) || (typeof data.is_admin !== 'undefined' && data.is_admin)) {
|
if ((this.model.get('id') === App.Cache.User.get('id') && this.model.isAdmin()) || (typeof data.is_admin !== 'undefined' && data.is_admin)) {
|
||||||
data.roles.push('admin');
|
data.roles.push('admin');
|
||||||
delete data.is_admin;
|
delete data.is_admin;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.is_disabled = typeof data.is_disabled !== 'undefined' ? !!data.is_disabled : false;
|
data.is_disabled = typeof data.is_disabled !== 'undefined' ? !!data.is_disabled : false;
|
||||||
this.ui.buttons.prop('disabled', true).addClass('btn-disabled');
|
this.ui.buttons.prop('disabled', true).addClass('btn-disabled');
|
||||||
let method = Api.Users.create;
|
let method = App.Api.Users.create;
|
||||||
|
|
||||||
if (this.model.get('id')) {
|
if (this.model.get('id')) {
|
||||||
// edit
|
// edit
|
||||||
method = Api.Users.update;
|
method = App.Api.Users.update;
|
||||||
data.id = this.model.get('id');
|
data.id = this.model.get('id');
|
||||||
}
|
}
|
||||||
|
|
||||||
method(data)
|
method(data)
|
||||||
.then(result => {
|
.then(result => {
|
||||||
if (result.id === Cache.User.get('id')) {
|
if (result.id === App.Cache.User.get('id')) {
|
||||||
Cache.User.set(result);
|
App.Cache.User.set(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (view.model.get('id') !== Cache.User.get('id')) {
|
if (view.model.get('id') !== App.Cache.User.get('id')) {
|
||||||
Controller.showUsers();
|
App.Controller.showUsers();
|
||||||
}
|
}
|
||||||
|
|
||||||
view.model.set(result);
|
view.model.set(result);
|
||||||
App.UI.closeModal(function () {
|
App.App.UI.closeModal(function () {
|
||||||
if (method === Api.Users.create) {
|
if (method === App.Api.Users.create) {
|
||||||
// Show permissions dialog immediately
|
// Show permissions dialog immediately
|
||||||
Controller.showUserPermissions(view.model);
|
App.Controller.showUserPermissions(view.model);
|
||||||
} else if (show_password) {
|
} else if (show_password) {
|
||||||
Controller.showUserPasswordForm(view.model);
|
App.Controller.showUserPasswordForm(view.model);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
@ -88,7 +85,7 @@ module.exports = Mn.View.extend({
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
isSelf: function () {
|
isSelf: function () {
|
||||||
return view.model.get('id') === Cache.User.get('id');
|
return view.model.get('id') === App.Cache.User.get('id');
|
||||||
},
|
},
|
||||||
|
|
||||||
isAdmin: function () {
|
isAdmin: function () {
|
||||||
|
@ -1,30 +1,30 @@
|
|||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title">Change Password <%- isSelf() ? '' : 'for' + name %></h5>
|
<h5 class="modal-title"><%- i18n('users', 'form-title', {self: isSelf(), name: name}) %></h5>
|
||||||
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
|
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form>
|
<form>
|
||||||
<% if (isSelf()) { %>
|
<% if (isSelf()) { %>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">Current Password</label>
|
<label class="form-label"><%- i18n('users', 'current-password') %></label>
|
||||||
<input type="password" name="current_password" class="form-control" placeholder="" minlength="8" required>
|
<input type="password" name="current_password" class="form-control" placeholder="" minlength="8" required>
|
||||||
</div>
|
</div>
|
||||||
<% } %>
|
<% } %>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">New Password</label>
|
<label class="form-label"><%- i18n('users', 'new-password') %></label>
|
||||||
<input type="password" name="new_password1" class="form-control" placeholder="" minlength="8" required>
|
<input type="password" name="new_password1" class="form-control" placeholder="" minlength="8" required>
|
||||||
<div class="invalid-feedback secret-error"></div>
|
<div class="invalid-feedback secret-error"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">Confirm Password</label>
|
<label class="form-label"><%- i18n('users', 'confirm-password') %></label>
|
||||||
<input type="password" name="new_password2" class="form-control" placeholder="" minlength="8" required>
|
<input type="password" name="new_password2" class="form-control" placeholder="" minlength="8" required>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary cancel" data-dismiss="modal">Cancel</button>
|
<button type="button" class="btn btn-secondary cancel" data-dismiss="modal"><%- i18n('str', 'cancel') %></button>
|
||||||
<button type="button" class="btn btn-teal save">Save</button>
|
<button type="button" class="btn btn-teal save"><%- i18n('str', 'save') %></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const Mn = require('backbone.marionette');
|
const Mn = require('backbone.marionette');
|
||||||
const template = require('./password.ejs');
|
|
||||||
const Controller = require('../controller');
|
|
||||||
const Api = require('../api');
|
|
||||||
const App = require('../main');
|
const App = require('../main');
|
||||||
const Cache = require('../cache');
|
const template = require('./password.ejs');
|
||||||
|
|
||||||
require('jquery-serializejson');
|
require('jquery-serializejson');
|
||||||
|
|
||||||
@ -39,10 +36,10 @@ module.exports = Mn.View.extend({
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.ui.buttons.prop('disabled', true).addClass('btn-disabled');
|
this.ui.buttons.prop('disabled', true).addClass('btn-disabled');
|
||||||
Api.Users.setPassword(this.model.get('id'), data)
|
App.Api.Users.setPassword(this.model.get('id'), data)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
App.UI.closeModal();
|
App.UI.closeModal();
|
||||||
Controller.showUsers();
|
App.Controller.showUsers();
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
this.ui.error.text(err.message).show();
|
this.ui.error.text(err.message).show();
|
||||||
@ -52,7 +49,7 @@ module.exports = Mn.View.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
isSelf: function () {
|
isSelf: function () {
|
||||||
return Cache.User.get('id') === this.model.get('id');
|
return App.Cache.User.get('id') === this.model.get('id');
|
||||||
},
|
},
|
||||||
|
|
||||||
templateContext: function () {
|
templateContext: function () {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title">Permissions for <%- name %></h5>
|
<h5 class="modal-title"><%- i18n('users', 'permissions-title', {name: name}) %></h5>
|
||||||
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
|
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
@ -11,130 +11,58 @@
|
|||||||
<% if (isAdmin()) { %>
|
<% if (isAdmin()) { %>
|
||||||
<div class="alert alert-icon alert-secondary" role="alert">
|
<div class="alert alert-icon alert-secondary" role="alert">
|
||||||
<i class="fe fe-alert-triangle mr-2" aria-hidden="true"></i>
|
<i class="fe fe-alert-triangle mr-2" aria-hidden="true"></i>
|
||||||
This user is an Administrator and some items cannot be altered
|
<%- i18n('users', 'admin-perms') %>
|
||||||
</div>
|
</div>
|
||||||
<% } %>
|
<% } %>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">Item Visibility</label>
|
<label class="form-label"><%- i18n('users', 'perms-visibility') %></label>
|
||||||
<div class="selectgroup w-100">
|
<div class="selectgroup w-100">
|
||||||
<label class="selectgroup-item">
|
<label class="selectgroup-item">
|
||||||
<input type="radio" name="visibility" value="user" class="selectgroup-input"<%- getPerm('visibility') !== 'all' ? ' checked' : '' %>>
|
<input type="radio" name="visibility" value="user" class="selectgroup-input"<%- getPerm('visibility') !== 'all' ? ' checked' : '' %>>
|
||||||
<span class="selectgroup-button">Created Items Only</span>
|
<span class="selectgroup-button"><%- i18n('users', 'perms-visibility-user') %></span>
|
||||||
</label>
|
</label>
|
||||||
<label class="selectgroup-item">
|
<label class="selectgroup-item">
|
||||||
<input type="radio" name="visibility" value="all" class="selectgroup-input"<%- getPerm('visibility') === 'all' ? ' checked' : '' %>>
|
<input type="radio" name="visibility" value="all" class="selectgroup-input"<%- getPerm('visibility') === 'all' ? ' checked' : '' %>>
|
||||||
<span class="selectgroup-button">All Items</span>
|
<span class="selectgroup-button"><%- i18n('users', 'perms-visibility-all') %></span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<%
|
||||||
|
var list = ['proxy-hosts', 'redirection-hosts', 'dead-hosts', 'streams', 'access-lists'];
|
||||||
|
list.map(function(item) {
|
||||||
|
var perm = item.replace('-', '_');
|
||||||
|
%>
|
||||||
<div class="col-sm-12 col-md-12">
|
<div class="col-sm-12 col-md-12">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">Proxy Hosts</label>
|
<label class="form-label"><%- i18n(item, 'title') %></label>
|
||||||
<div class="selectgroup w-100">
|
<div class="selectgroup w-100">
|
||||||
<label class="selectgroup-item">
|
<label class="selectgroup-item">
|
||||||
<input type="radio" name="proxy_hosts" value="manage" class="selectgroup-input" <%- getPermProps('proxy_hosts', 'manage', true) %>>
|
<input type="radio" name="<%- perm %>" value="manage" class="selectgroup-input" <%- getPermProps(perm, 'manage', true) %>>
|
||||||
<span class="selectgroup-button">Manage</span>
|
<span class="selectgroup-button"><%- i18n('users', 'perm-manage') %></span>
|
||||||
</label>
|
</label>
|
||||||
<label class="selectgroup-item">
|
<label class="selectgroup-item">
|
||||||
<input type="radio" name="proxy_hosts" value="view" class="selectgroup-input" <%- getPermProps('proxy_hosts', 'view') %>>
|
<input type="radio" name="<%- perm %>" value="view" class="selectgroup-input" <%- getPermProps(perm, 'view') %>>
|
||||||
<span class="selectgroup-button">View Only</span>
|
<span class="selectgroup-button"><%- i18n('users', 'perm-view') %></span>
|
||||||
</label>
|
</label>
|
||||||
<label class="selectgroup-item">
|
<label class="selectgroup-item">
|
||||||
<input type="radio" name="proxy_hosts" value="hidden" class="selectgroup-input" <%- getPermProps('proxy_hosts', 'hidden') %>>
|
<input type="radio" name="<%- perm %>" value="hidden" class="selectgroup-input" <%- getPermProps(perm, 'hidden') %>>
|
||||||
<span class="selectgroup-button">Hidden</span>
|
<span class="selectgroup-button"><%- i18n('users', 'perm-hidden') %></span>
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-sm-12 col-md-12">
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="form-label">Redirection Hosts</label>
|
|
||||||
<div class="selectgroup w-100">
|
|
||||||
<label class="selectgroup-item">
|
|
||||||
<input type="radio" name="redirection_hosts" value="manage" class="selectgroup-input" <%- getPermProps('redirection_hosts', 'manage', true) %>>
|
|
||||||
<span class="selectgroup-button">Manage</span>
|
|
||||||
</label>
|
|
||||||
<label class="selectgroup-item">
|
|
||||||
<input type="radio" name="redirection_hosts" value="view" class="selectgroup-input" <%- getPermProps('redirection_hosts', 'view') %>>
|
|
||||||
<span class="selectgroup-button">View Only</span>
|
|
||||||
</label>
|
|
||||||
<label class="selectgroup-item">
|
|
||||||
<input type="radio" name="redirection_hosts" value="hidden" class="selectgroup-input" <%- getPermProps('redirection_hosts', 'hidden') %>>
|
|
||||||
<span class="selectgroup-button">Hidden</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-sm-12 col-md-12">
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="form-label">404 Hosts</label>
|
|
||||||
<div class="selectgroup w-100">
|
|
||||||
<label class="selectgroup-item">
|
|
||||||
<input type="radio" name="dead_hosts" value="manage" class="selectgroup-input" <%- getPermProps('dead_hosts', 'manage', true) %>>
|
|
||||||
<span class="selectgroup-button">Manage</span>
|
|
||||||
</label>
|
|
||||||
<label class="selectgroup-item">
|
|
||||||
<input type="radio" name="dead_hosts" value="view" class="selectgroup-input" <%- getPermProps('dead_hosts', 'view') %>>
|
|
||||||
<span class="selectgroup-button">View Only</span>
|
|
||||||
</label>
|
|
||||||
<label class="selectgroup-item">
|
|
||||||
<input type="radio" name="dead_hosts" value="hidden" class="selectgroup-input" <%- getPermProps('dead_hosts', 'hidden') %>>
|
|
||||||
<span class="selectgroup-button">Hidden</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-sm-12 col-md-12">
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="form-label">Streams</label>
|
|
||||||
<div class="selectgroup w-100">
|
|
||||||
<label class="selectgroup-item">
|
|
||||||
<input type="radio" name="streams" value="manage" class="selectgroup-input" <%- getPermProps('streams', 'manage', true) %>>
|
|
||||||
<span class="selectgroup-button">Manage</span>
|
|
||||||
</label>
|
|
||||||
<label class="selectgroup-item">
|
|
||||||
<input type="radio" name="streams" value="view" class="selectgroup-input" <%- getPermProps('streams', 'view') %>>
|
|
||||||
<span class="selectgroup-button">View Only</span>
|
|
||||||
</label>
|
|
||||||
<label class="selectgroup-item">
|
|
||||||
<input type="radio" name="streams" value="hidden" class="selectgroup-input" <%- getPermProps('streams', 'hidden') %>>
|
|
||||||
<span class="selectgroup-button">Hidden</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-sm-12 col-md-12">
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="form-label">Access Lists</label>
|
|
||||||
<div class="selectgroup w-100">
|
|
||||||
<label class="selectgroup-item">
|
|
||||||
<input type="radio" name="access_lists" value="manage" class="selectgroup-input" <%- getPermProps('access_lists', 'manage', true) %>>
|
|
||||||
<span class="selectgroup-button">Manage</span>
|
|
||||||
</label>
|
|
||||||
<label class="selectgroup-item">
|
|
||||||
<input type="radio" name="access_lists" value="view" class="selectgroup-input" <%- getPermProps('access_lists', 'view') %>>
|
|
||||||
<span class="selectgroup-button">View Only</span>
|
|
||||||
</label>
|
|
||||||
<label class="selectgroup-item">
|
|
||||||
<input type="radio" name="access_lists" value="hidden" class="selectgroup-input" <%- getPermProps('access_lists', 'hidden') %>>
|
|
||||||
<span class="selectgroup-button">Hidden</span>
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<%
|
||||||
|
});
|
||||||
|
%>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary cancel" data-dismiss="modal">Cancel</button>
|
<button type="button" class="btn btn-secondary cancel" data-dismiss="modal"><%- i18n('str', 'cancel') %></button>
|
||||||
<button type="button" class="btn btn-teal save">Save</button>
|
<button type="button" class="btn btn-teal save"><%- i18n('str', 'save') %></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const Mn = require('backbone.marionette');
|
const Mn = require('backbone.marionette');
|
||||||
const template = require('./permissions.ejs');
|
|
||||||
const Controller = require('../controller');
|
|
||||||
const Cache = require('../cache');
|
|
||||||
const Api = require('../api');
|
|
||||||
const App = require('../main');
|
const App = require('../main');
|
||||||
const UserModel = require('../../models/user');
|
const UserModel = require('../../models/user');
|
||||||
|
const template = require('./permissions.ejs');
|
||||||
|
|
||||||
require('jquery-serializejson');
|
require('jquery-serializejson');
|
||||||
|
|
||||||
@ -44,10 +41,10 @@ module.exports = Mn.View.extend({
|
|||||||
|
|
||||||
this.ui.buttons.prop('disabled', true).addClass('btn-disabled');
|
this.ui.buttons.prop('disabled', true).addClass('btn-disabled');
|
||||||
|
|
||||||
Api.Users.setPermissions(view.model.get('id'), data)
|
App.Api.Users.setPermissions(view.model.get('id'), data)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (view.model.get('id') === Cache.User.get('id')) {
|
if (view.model.get('id') === App.Cache.User.get('id')) {
|
||||||
Cache.User.set({permissions: data});
|
App.Cache.User.set({permissions: data});
|
||||||
}
|
}
|
||||||
|
|
||||||
view.model.set({permissions: data});
|
view.model.set({permissions: data});
|
||||||
|
@ -6,26 +6,36 @@
|
|||||||
<td>
|
<td>
|
||||||
<div><%- name %></div>
|
<div><%- name %></div>
|
||||||
<div class="small text-muted">
|
<div class="small text-muted">
|
||||||
Created: <%- formatDbDate(created_on, 'Do MMMM YYYY') %>
|
<%- i18n('str', 'created-on', {date: formatDbDate(created_on, 'Do MMMM YYYY')}) %>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div><%- email %></div>
|
<div><%- email %></div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div><%- roles.join(', ') %></div>
|
<div>
|
||||||
|
<%
|
||||||
|
var r = [];
|
||||||
|
roles.map(function(role) {
|
||||||
|
if (role) {
|
||||||
|
r.push(i18n('roles', role));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
%>
|
||||||
|
<%- r.join(', ') %>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<div class="item-action dropdown">
|
<div class="item-action dropdown">
|
||||||
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
|
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
|
||||||
<div class="dropdown-menu dropdown-menu-right">
|
<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-user dropdown-item"><i class="dropdown-icon fe fe-edit"></i> <%- i18n('users', 'edit-details') %></a>
|
||||||
<a href="#" class="edit-permissions dropdown-item"><i class="dropdown-icon fe fe-shield"></i> Edit Permissions</a>
|
<a href="#" class="edit-permissions dropdown-item"><i class="dropdown-icon fe fe-shield"></i> <%- i18n('users', 'edit-permissions') %></a>
|
||||||
<a href="#" class="set-password dropdown-item"><i class="dropdown-icon fe fe-lock"></i> Set Password</a>
|
<a href="#" class="set-password dropdown-item"><i class="dropdown-icon fe fe-lock"></i> <%- i18n('users', 'change-password') %></a>
|
||||||
<% if (!isSelf()) { %>
|
<% 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="login dropdown-item"><i class="dropdown-icon fe fe-log-in"></i> <%- i18n('users', 'sign-in-as') %></a>
|
||||||
<div class="dropdown-divider"></div>
|
<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-user dropdown-item"><i class="dropdown-icon fe fe-trash-2"></i> <%- i18n('users', 'delete') %></a>
|
||||||
<% } %>
|
<% } %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const Mn = require('backbone.marionette');
|
const Mn = require('backbone.marionette');
|
||||||
const Controller = require('../../controller');
|
const App = require('../../main');
|
||||||
const Api = require('../../api');
|
|
||||||
const Cache = require('../../cache');
|
|
||||||
const Tokens = require('../../tokens');
|
const Tokens = require('../../tokens');
|
||||||
const template = require('./item.ejs');
|
const template = require('./item.ejs');
|
||||||
|
|
||||||
@ -22,31 +20,31 @@ module.exports = Mn.View.extend({
|
|||||||
events: {
|
events: {
|
||||||
'click @ui.edit': function (e) {
|
'click @ui.edit': function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
Controller.showUserForm(this.model);
|
App.Controller.showUserForm(this.model);
|
||||||
},
|
},
|
||||||
|
|
||||||
'click @ui.permissions': function (e) {
|
'click @ui.permissions': function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
Controller.showUserPermissions(this.model);
|
App.Controller.showUserPermissions(this.model);
|
||||||
},
|
},
|
||||||
|
|
||||||
'click @ui.password': function (e) {
|
'click @ui.password': function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
Controller.showUserPasswordForm(this.model);
|
App.Controller.showUserPasswordForm(this.model);
|
||||||
},
|
},
|
||||||
|
|
||||||
'click @ui.delete': function (e) {
|
'click @ui.delete': function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
Controller.showUserDeleteConfirm(this.model);
|
App.Controller.showUserDeleteConfirm(this.model);
|
||||||
},
|
},
|
||||||
|
|
||||||
'click @ui.login': function (e) {
|
'click @ui.login': function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
if (Cache.User.get('id') !== this.model.get('id')) {
|
if (App.Cache.User.get('id') !== this.model.get('id')) {
|
||||||
this.ui.login.prop('disabled', true).addClass('btn-disabled');
|
this.ui.login.prop('disabled', true).addClass('btn-disabled');
|
||||||
|
|
||||||
Api.Users.loginAs(this.model.get('id'))
|
App.Api.Users.loginAs(this.model.get('id'))
|
||||||
.then(res => {
|
.then(res => {
|
||||||
Tokens.addToken(res.token, res.user.nickname || res.user.name);
|
Tokens.addToken(res.token, res.user.nickname || res.user.name);
|
||||||
window.location = '/';
|
window.location = '/';
|
||||||
@ -62,7 +60,7 @@ module.exports = Mn.View.extend({
|
|||||||
|
|
||||||
templateContext: {
|
templateContext: {
|
||||||
isSelf: function () {
|
isSelf: function () {
|
||||||
return Cache.User.get('id') === this.id;
|
return App.Cache.User.get('id') === this.id;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<th width="30"> </th>
|
<th width="30"> </th>
|
||||||
<th>Name</th>
|
<th><%- i18n('str', 'name') %></th>
|
||||||
<th>Email</th>
|
<th><%- i18n('str', 'email') %></th>
|
||||||
<th>Roles</th>
|
<th><%- i18n('str', 'roles') %></th>
|
||||||
<th> </th>
|
<th> </th>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h3 class="card-title">Users</h3>
|
<h3 class="card-title"><%- i18n('users', 'title') %></h3>
|
||||||
<div class="card-options">
|
<div class="card-options">
|
||||||
<a href="#" class="btn btn-outline-teal btn-sm ml-2 add-user">Add User</a>
|
<a href="#" class="btn btn-outline-teal btn-sm ml-2 add-user"><%- i18n('users', 'add') %></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body no-padding min-100">
|
<div class="card-body no-padding min-100">
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const Mn = require('backbone.marionette');
|
const Mn = require('backbone.marionette');
|
||||||
|
const App = require('../main');
|
||||||
const UserModel = require('../../models/user');
|
const UserModel = require('../../models/user');
|
||||||
const Api = require('../api');
|
|
||||||
const Controller = require('../controller');
|
|
||||||
const ListView = require('./list/main');
|
const ListView = require('./list/main');
|
||||||
const template = require('./main.ejs');
|
|
||||||
const ErrorView = require('../error/main');
|
const ErrorView = require('../error/main');
|
||||||
|
const template = require('./main.ejs');
|
||||||
|
|
||||||
module.exports = Mn.View.extend({
|
module.exports = Mn.View.extend({
|
||||||
id: 'users',
|
id: 'users',
|
||||||
@ -25,14 +24,14 @@ module.exports = Mn.View.extend({
|
|||||||
events: {
|
events: {
|
||||||
'click @ui.add': function (e) {
|
'click @ui.add': function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
Controller.showUserForm(new UserModel.Model());
|
App.Controller.showUserForm(new UserModel.Model());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onRender: function () {
|
onRender: function () {
|
||||||
let view = this;
|
let view = this;
|
||||||
|
|
||||||
Api.Users.getAll(['permissions'])
|
App.Api.Users.getAll(['permissions'])
|
||||||
.then(response => {
|
.then(response => {
|
||||||
if (!view.isDestroyed() && response && response.length) {
|
if (!view.isDestroyed() && response && response.length) {
|
||||||
view.showChildView('list_region', new ListView({
|
view.showChildView('list_region', new ListView({
|
||||||
@ -44,7 +43,9 @@ module.exports = Mn.View.extend({
|
|||||||
view.showChildView('list_region', new ErrorView({
|
view.showChildView('list_region', new ErrorView({
|
||||||
code: err.code,
|
code: err.code,
|
||||||
message: err.message,
|
message: err.message,
|
||||||
retry: function () { Controller.showUsers(); }
|
retry: function () {
|
||||||
|
App.Controller.showUsers();
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
128
src/frontend/js/i18n/messages.json
Normal file
128
src/frontend/js/i18n/messages.json
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
{
|
||||||
|
"en": {
|
||||||
|
"str": {
|
||||||
|
"email-address": "Email address",
|
||||||
|
"password": "Password",
|
||||||
|
"sign-in": "Sign in",
|
||||||
|
"sign-out": "Sign out",
|
||||||
|
"try-again": "Try again",
|
||||||
|
"name": "Name",
|
||||||
|
"email": "Email",
|
||||||
|
"roles": "Roles",
|
||||||
|
"created-on": "Created: {date}",
|
||||||
|
"save": "Save",
|
||||||
|
"cancel": "Cancel",
|
||||||
|
"sure": "Yes I'm Sure",
|
||||||
|
"disabled": "Disabled",
|
||||||
|
"choose-file": "Choose file",
|
||||||
|
"source": "Source",
|
||||||
|
"destination": "Destination",
|
||||||
|
"ssl": "SSL",
|
||||||
|
"access": "Access",
|
||||||
|
"public": "Public",
|
||||||
|
"edit": "Edit",
|
||||||
|
"delete": "Delete",
|
||||||
|
"logs": "Logs"
|
||||||
|
},
|
||||||
|
"login": {
|
||||||
|
"title": "Login to your account"
|
||||||
|
},
|
||||||
|
"main": {
|
||||||
|
"app": "Nginx Proxy Manager",
|
||||||
|
"version": "v{version}",
|
||||||
|
"welcome": "Welcome to Nginx Proxy Manager",
|
||||||
|
"logged-in": "You are logged in as {name}",
|
||||||
|
"unknown-error": "Error loading stuff. Please reload the app.",
|
||||||
|
"unknown-user": "Unknown User",
|
||||||
|
"sign-in-as": "Sign back in as {name}"
|
||||||
|
},
|
||||||
|
"roles": {
|
||||||
|
"title": "Roles",
|
||||||
|
"admin": "Administrator",
|
||||||
|
"user": "Apache Helicopter"
|
||||||
|
},
|
||||||
|
"menu": {
|
||||||
|
"dashboard": "Dashboard",
|
||||||
|
"hosts": "Hosts"
|
||||||
|
},
|
||||||
|
"footer": {
|
||||||
|
"fork-me": "Fork me on Github",
|
||||||
|
"copy": "© 2018 <a href=\"{url}\" target=\"_blank\">jc21.com</a>.",
|
||||||
|
"theme": "Theme by <a href=\"{url}\" target=\"_blank\">Tabler</a>"
|
||||||
|
},
|
||||||
|
"dashboard": {
|
||||||
|
"title": "Hi {name}"
|
||||||
|
},
|
||||||
|
"all-hosts": {
|
||||||
|
"empty-subtitle": "{manage, select, true{Why don't you create one?} other{And you don't have permission to create one.}}",
|
||||||
|
"details": "Details",
|
||||||
|
"enable-ssl": "Enable SSL",
|
||||||
|
"force-ssl": "Force SSL",
|
||||||
|
"domain-names": "Domain Names",
|
||||||
|
"cert-provider": "Certificate Provider",
|
||||||
|
"other-certificate": "Certificate",
|
||||||
|
"other-certificate-key": "Certificate Key"
|
||||||
|
},
|
||||||
|
"ssl": {
|
||||||
|
"letsencrypt": "Let's Encrypt",
|
||||||
|
"other": "Other",
|
||||||
|
"none": "HTTP only",
|
||||||
|
"letsencrypt-email": "Email Address for Let's Encrypt",
|
||||||
|
"letsencrypt-agree": "I Agree to the <a href=\"{url}\" target=\"_blank\">Let's Encrypt Terms of Service</a>"
|
||||||
|
},
|
||||||
|
"proxy-hosts": {
|
||||||
|
"title": "Proxy Hosts",
|
||||||
|
"empty": "There are no Proxy Hosts",
|
||||||
|
"add": "Add Proxy Host",
|
||||||
|
"form-title": "{id, select, undefined{New} other{Edit}} Proxy Host",
|
||||||
|
"forward-ip": "Forward IP",
|
||||||
|
"forward-port": "Forward Port",
|
||||||
|
"delete": "Delete Proxy Host",
|
||||||
|
"delete-confirm": "Are you sure you want to delete the Proxy host for: <strong>{domains}</strong>?",
|
||||||
|
"delete-ssl": "The SSL certificates attached will be removed, this action cannot be recovered."
|
||||||
|
},
|
||||||
|
"redirection-hosts": {
|
||||||
|
"title": "Redirection Hosts"
|
||||||
|
},
|
||||||
|
"dead-hosts": {
|
||||||
|
"title": "404 Hosts"
|
||||||
|
},
|
||||||
|
"streams": {
|
||||||
|
"title": "Streams"
|
||||||
|
},
|
||||||
|
"access-lists": {
|
||||||
|
"title": "Access Lists"
|
||||||
|
},
|
||||||
|
"users": {
|
||||||
|
"title": "Users",
|
||||||
|
"default_error": "Default email address must be changed",
|
||||||
|
"add": "Add User",
|
||||||
|
"nickname": "Nickname",
|
||||||
|
"full-name": "Full Name",
|
||||||
|
"edit-details": "Edit Details",
|
||||||
|
"change-password": "Change Password",
|
||||||
|
"edit-permissions": "Edit Permissions",
|
||||||
|
"sign-in-as": "Sign in as User",
|
||||||
|
"form-title": "{id, select, undefined{New} other{Edit}} User",
|
||||||
|
"delete": "Delete {name, select, undefined{User} other{{name}}}",
|
||||||
|
"delete-confirm": "Are you sure you want to delete <strong>{name}</strong>?",
|
||||||
|
"password-title": "Change Password{self, select, false{ for {name}} other{}}",
|
||||||
|
"current-password": "Current Password",
|
||||||
|
"new-password": "New Password",
|
||||||
|
"confirm-password": "Confirm Password",
|
||||||
|
"permissions-title": "Permissions for {name}",
|
||||||
|
"admin-perms": "This user is an Administrator and some items cannot be altered",
|
||||||
|
"perms-visibility": "Item Visibility",
|
||||||
|
"perms-visibility-user": "Created Items Only",
|
||||||
|
"perms-visibility-all": "All Items",
|
||||||
|
"perm-manage": "Manage",
|
||||||
|
"perm-view": "View Only",
|
||||||
|
"perm-hidden": "Hidden"
|
||||||
|
},
|
||||||
|
"audit-log": {
|
||||||
|
"title": "Audit Log",
|
||||||
|
"empty": "There are no logs.",
|
||||||
|
"empty-subtitle": "As soon as you or another user changes something, history of those events will show up here."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const numeral = require('numeral');
|
const numeral = require('numeral');
|
||||||
const moment = require('moment');
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
||||||
@ -11,26 +10,5 @@ module.exports = {
|
|||||||
*/
|
*/
|
||||||
niceNumber: function (number) {
|
niceNumber: function (number) {
|
||||||
return numeral(number).format('0,0');
|
return numeral(number).format('0,0');
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {String|Integer} date
|
|
||||||
* @returns {String}
|
|
||||||
*/
|
|
||||||
shortTime: function (date) {
|
|
||||||
let shorttime = '';
|
|
||||||
|
|
||||||
if (typeof date === 'number') {
|
|
||||||
shorttime = moment.unix(date).format('H:mm A');
|
|
||||||
} else {
|
|
||||||
shorttime = moment(date).format('H:mm A');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return shorttime;
|
|
||||||
},
|
|
||||||
|
|
||||||
replaceSlackLinks: function (content) {
|
|
||||||
return content.replace(/<(http[^|>]+)\|([^>]+)>/gi, '<a href="$1" target="_blank">$2</a>');
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
const _ = require('underscore');
|
const _ = require('underscore');
|
||||||
const Mn = require('backbone.marionette');
|
const Mn = require('backbone.marionette');
|
||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
const numeral = require('numeral');
|
const i18n = require('../app/i18n');
|
||||||
|
|
||||||
let render = Mn.Renderer.render;
|
let render = Mn.Renderer.render;
|
||||||
|
|
||||||
@ -11,29 +11,7 @@ Mn.Renderer.render = function (template, data, view) {
|
|||||||
|
|
||||||
data = _.clone(data);
|
data = _.clone(data);
|
||||||
|
|
||||||
/**
|
data.i18n = i18n;
|
||||||
* @param {Integer} number
|
|
||||||
* @returns {String}
|
|
||||||
*/
|
|
||||||
data.niceNumber = function (number) {
|
|
||||||
return numeral(number).format('0,0');
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Integer} seconds
|
|
||||||
* @returns {String}
|
|
||||||
*/
|
|
||||||
data.secondsToTime = function (seconds) {
|
|
||||||
let sec_num = parseInt(seconds, 10);
|
|
||||||
let minutes = Math.floor(sec_num / 60);
|
|
||||||
let sec = sec_num - (minutes * 60);
|
|
||||||
|
|
||||||
if (sec < 10) {
|
|
||||||
sec = '0' + sec;
|
|
||||||
}
|
|
||||||
|
|
||||||
return minutes + ':' + sec;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {String} date
|
* @param {String} date
|
||||||
@ -47,68 +25,6 @@ Mn.Renderer.render = function (template, data, view) {
|
|||||||
return moment(date).format(format);
|
return moment(date).format(format);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {String} date
|
|
||||||
* @returns {String}
|
|
||||||
*/
|
|
||||||
data.shortDate = function (date) {
|
|
||||||
let shortdate = data.formatDbDate(date, 'YYYY-MM-DD');
|
|
||||||
|
|
||||||
return moment().format('YYYY-MM-DD') === shortdate ? 'Today' : shortdate;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {String} date
|
|
||||||
* @returns {String}
|
|
||||||
*/
|
|
||||||
data.shortTime = function (date) {
|
|
||||||
return data.formatDbDate(date, 'H:mm A');
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {String} string
|
|
||||||
* @returns {String}
|
|
||||||
*/
|
|
||||||
data.escape = function (string) {
|
|
||||||
let entityMap = {
|
|
||||||
'&': '&',
|
|
||||||
'<': '<',
|
|
||||||
'>': '>',
|
|
||||||
'"': '"',
|
|
||||||
'\'': ''',
|
|
||||||
'/': '/'
|
|
||||||
};
|
|
||||||
|
|
||||||
return String(string).replace(/[&<>"'\/]/g, function (s) {
|
|
||||||
return entityMap[s];
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {String} string
|
|
||||||
* @param {Integer} length
|
|
||||||
* @returns {String}
|
|
||||||
*/
|
|
||||||
data.trim = function (string, length) {
|
|
||||||
if (string.length > length) {
|
|
||||||
let trimmedString = string.substr(0, length);
|
|
||||||
return trimmedString.substr(0, Math.min(trimmedString.length, trimmedString.lastIndexOf(' '))) + '...';
|
|
||||||
}
|
|
||||||
|
|
||||||
return string;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {String} name
|
|
||||||
* @returns {String}
|
|
||||||
*/
|
|
||||||
data.niceVarName = function (name) {
|
|
||||||
return name.replace('_', ' ')
|
|
||||||
.replace(/^(.)|\s+(.)/g, function ($1) {
|
|
||||||
return $1.toUpperCase();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return render.call(this, template, data, view);
|
return render.call(this, template, data, view);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3,23 +3,23 @@
|
|||||||
<div class="col col-login mx-auto">
|
<div class="col col-login mx-auto">
|
||||||
<form class="card" action="" method="post">
|
<form class="card" action="" method="post">
|
||||||
<div class="card-body p-6">
|
<div class="card-body p-6">
|
||||||
<div class="card-title">Login to your account</div>
|
<div class="card-title"><%- i18n('login', 'title') %></div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">Email address</label>
|
<label class="form-label"><%- i18n('str', 'email-address') %></label>
|
||||||
<input name="identity" type="email" class="form-control" placeholder="Enter email" required>
|
<input name="identity" type="email" class="form-control" placeholder="<%- i18n('str', 'email-address') %>" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">Password</label>
|
<label class="form-label"><%- i18n('str', 'password') %></label>
|
||||||
<input name="secret" type="password" class="form-control" placeholder="Password" required>
|
<input name="secret" type="password" class="form-control" placeholder="<%- i18n('str', 'password') %>" required>
|
||||||
<div class="invalid-feedback secret-error"></div>
|
<div class="invalid-feedback secret-error"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-footer">
|
<div class="form-footer">
|
||||||
<button type="submit" class="btn btn-teal btn-block">Sign in</button>
|
<button type="submit" class="btn btn-teal btn-block"><%- i18n('str', 'sign-in') %></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<div class="text-center text-muted">
|
<div class="text-center text-muted">
|
||||||
Nginx Proxy Manager v<%- getVersion() %>
|
<%- i18n('main', 'app') %> <%- i18n('main', 'version', {version: getVersion()}) %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,6 +4,7 @@ const $ = require('jquery');
|
|||||||
const Mn = require('backbone.marionette');
|
const Mn = require('backbone.marionette');
|
||||||
const template = require('./login.ejs');
|
const template = require('./login.ejs');
|
||||||
const Api = require('../../app/api');
|
const Api = require('../../app/api');
|
||||||
|
const i18n = require('../../app/i18n');
|
||||||
|
|
||||||
module.exports = Mn.View.extend({
|
module.exports = Mn.View.extend({
|
||||||
template: template,
|
template: template,
|
||||||
@ -35,6 +36,7 @@ module.exports = Mn.View.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
templateContext: {
|
templateContext: {
|
||||||
|
i18n: i18n,
|
||||||
getVersion: function () {
|
getVersion: function () {
|
||||||
return $('#login').data('version');
|
return $('#login').data('version');
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ const model = Backbone.Model.extend({
|
|||||||
|
|
||||||
defaults: function () {
|
defaults: function () {
|
||||||
return {
|
return {
|
||||||
|
id: 0,
|
||||||
created_on: null,
|
created_on: null,
|
||||||
modified_on: null,
|
modified_on: null,
|
||||||
owner: null,
|
owner: null,
|
||||||
|
@ -7,10 +7,10 @@ const model = Backbone.Model.extend({
|
|||||||
|
|
||||||
defaults: function () {
|
defaults: function () {
|
||||||
return {
|
return {
|
||||||
|
id: 0,
|
||||||
created_on: null,
|
created_on: null,
|
||||||
modified_on: null,
|
modified_on: null,
|
||||||
owner: null,
|
domain_names: [],
|
||||||
domain_name: '',
|
|
||||||
forward_ip: '',
|
forward_ip: '',
|
||||||
forward_port: null,
|
forward_port: null,
|
||||||
access_list_id: null,
|
access_list_id: null,
|
||||||
@ -19,7 +19,10 @@ const model = Backbone.Model.extend({
|
|||||||
ssl_forced: false,
|
ssl_forced: false,
|
||||||
caching_enabled: false,
|
caching_enabled: false,
|
||||||
block_exploits: false,
|
block_exploits: false,
|
||||||
meta: []
|
meta: [],
|
||||||
|
// The following are expansions:
|
||||||
|
owner: null,
|
||||||
|
access_list: null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -7,6 +7,7 @@ const model = Backbone.Model.extend({
|
|||||||
|
|
||||||
defaults: function () {
|
defaults: function () {
|
||||||
return {
|
return {
|
||||||
|
id: 0,
|
||||||
created_on: null,
|
created_on: null,
|
||||||
modified_on: null,
|
modified_on: null,
|
||||||
owner: null,
|
owner: null,
|
||||||
|
@ -7,6 +7,7 @@ const model = Backbone.Model.extend({
|
|||||||
|
|
||||||
defaults: function () {
|
defaults: function () {
|
||||||
return {
|
return {
|
||||||
|
id: 0,
|
||||||
created_on: null,
|
created_on: null,
|
||||||
modified_on: null,
|
modified_on: null,
|
||||||
owner: null,
|
owner: null,
|
||||||
|
@ -8,6 +8,7 @@ const model = Backbone.Model.extend({
|
|||||||
|
|
||||||
defaults: function () {
|
defaults: function () {
|
||||||
return {
|
return {
|
||||||
|
id: 0,
|
||||||
name: '',
|
name: '',
|
||||||
nickname: '',
|
nickname: '',
|
||||||
email: '',
|
email: '',
|
||||||
|
@ -45,6 +45,19 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// other:
|
// other:
|
||||||
|
{
|
||||||
|
type: 'javascript/auto', // <= Set the module.type explicitly
|
||||||
|
test: /\bmessages\.json$/,
|
||||||
|
loader: 'messageformat-loader',
|
||||||
|
options: {
|
||||||
|
biDiSupport: false,
|
||||||
|
disablePluralKeyChecks: false,
|
||||||
|
formatters: null,
|
||||||
|
intlSupport: false,
|
||||||
|
locale: ['en'/*, 'es'*/],
|
||||||
|
strictNumberSign: false
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
test: /\.js$/,
|
test: /\.js$/,
|
||||||
exclude: /node_modules/,
|
exclude: /node_modules/,
|
||||||
|
Loading…
Reference in New Issue
Block a user