This commit is contained in:
Jamie Curnow
2018-07-18 08:35:49 +10:00
parent c5450eaa1a
commit c629deb56c
34 changed files with 710 additions and 295 deletions

View File

@ -26,7 +26,7 @@ const internalDeadHost = {
.where('is_deleted', 0)
.groupBy('id')
.omit(['is_deleted'])
.orderBy('domain_name', 'ASC');
.orderBy('domain_names', 'ASC');
if (access_data.permission_visibility !== 'all') {
query.andWhere('owner_user_id', access.token.get('attrs').id);
@ -35,7 +35,7 @@ const internalDeadHost = {
// Query is used for searching
if (typeof search_query === 'string') {
query.where(function () {
this.where('domain_name', 'like', '%' + search_query + '%');
this.where('domain_names', 'like', '%' + search_query + '%');
});
}

View 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;

View File

@ -3,6 +3,7 @@
const _ = require('lodash');
const error = require('../lib/error');
const proxyHostModel = require('../models/proxy_host');
const internalHost = require('./host');
function omissions () {
return ['is_deleted'];
@ -16,60 +17,39 @@ const internalProxyHost = {
* @returns {Promise}
*/
create: (access, data) => {
let auth = data.auth || null;
delete data.auth;
data.avatar = data.avatar || '';
data.roles = data.roles || [];
if (typeof data.is_disabled !== 'undefined') {
data.is_disabled = data.is_disabled ? 1 : 0;
}
return access.can('proxy_hosts:create', data)
.then(() => {
data.avatar = gravatar.url(data.email, {default: 'mm'});
.then(access_data => {
// Get a list of the domain names and check each of them against existing records
let domain_name_check_promises = [];
return userModel
data.domain_names.map(function (domain_name) {
domain_name_check_promises.push(internalHost.isHostnameTaken(domain_name));
});
return Promise.all(domain_name_check_promises)
.then(check_results => {
check_results.map(function (result) {
if (result.is_taken) {
throw new error.ValidationError(result.hostname + ' is already in use');
}
});
});
})
.then(() => {
// At this point the domains should have been checked
data.owner_user_id = access.token.get('attrs').id;
if (typeof data.meta === 'undefined') {
data.meta = {};
}
return proxyHostModel
.query()
.omit(omissions())
.insertAndFetch(data);
})
.then(user => {
if (auth) {
return authModel
.query()
.insert({
user_id: user.id,
type: auth.type,
secret: auth.secret,
meta: {}
})
.then(() => {
return user;
});
} else {
return user;
}
})
.then(user => {
// Create permissions row as well
let is_admin = data.roles.indexOf('admin') !== -1;
return userPermissionModel
.query()
.insert({
user_id: user.id,
visibility: is_admin ? 'all' : 'user',
proxy_hosts: 'manage',
redirection_hosts: 'manage',
dead_hosts: 'manage',
streams: 'manage',
access_lists: 'manage'
})
.then(() => {
return internalProxyHost.get(access, {id: user.id, expand: ['permissions']});
});
.then(row => {
return _.omit(row, omissions());
});
},
@ -82,63 +62,49 @@ const internalProxyHost = {
* @return {Promise}
*/
update: (access, data) => {
if (typeof data.is_disabled !== 'undefined') {
data.is_disabled = data.is_disabled ? 1 : 0;
}
return access.can('proxy_hosts:update', data.id)
.then(() => {
.then(access_data => {
// Get a list of the domain names and check each of them against existing records
let domain_name_check_promises = [];
// Make sure that the user being updated doesn't change their email to another user that is already using it
// 1. get user we want to update
return internalProxyHost.get(access, {id: data.id})
.then(user => {
// 2. if email is to be changed, find other users with that email
if (typeof data.email !== 'undefined') {
data.email = data.email.toLowerCase().trim();
if (user.email !== data.email) {
return internalProxyHost.isEmailAvailable(data.email, data.id)
.then(available => {
if (!available) {
throw new error.ValidationError('Email address already in use - ' + data.email);
}
return user;
});
}
}
// No change to email:
return user;
if (typeof data.domain_names !== 'undefined') {
data.domain_names.map(function (domain_name) {
domain_name_check_promises.push(internalHost.isHostnameTaken(domain_name, 'proxy', data.id));
});
})
.then(user => {
if (user.id !== data.id) {
// Sanity check that something crazy hasn't happened
throw new error.InternalValidationError('User could not be updated, IDs do not match: ' + user.id + ' !== ' + data.id);
return Promise.all(domain_name_check_promises)
.then(check_results => {
check_results.map(function (result) {
if (result.is_taken) {
throw new error.ValidationError(result.hostname + ' is already in use');
}
});
});
}
data.avatar = gravatar.url(data.email || user.email, {default: 'mm'});
return userModel
.query()
.omit(omissions())
.patchAndFetchById(user.id, data)
.then(saved_user => {
return _.omit(saved_user, omissions());
});
})
.then(() => {
return internalProxyHost.get(access, {id: data.id});
})
.then(row => {
if (row.id !== data.id) {
// Sanity check that something crazy hasn't happened
throw new error.InternalValidationError('Proxy Host could not be updated, IDs do not match: ' + row.id + ' !== ' + data.id);
}
return proxyHostModel
.query()
.omit(omissions())
.patchAndFetchById(row.id, data)
.then(saved_row => {
return _.omit(saved_row, omissions());
});
});
},
/**
* @param {Access} access
* @param {Object} [data]
* @param {Integer} [data.id] Defaults to the token user
* @param {Object} data
* @param {Integer} data.id
* @param {Array} [data.expand]
* @param {Array} [data.omit]
* @return {Promise}
@ -153,14 +119,18 @@ const internalProxyHost = {
}
return access.can('proxy_hosts:get', data.id)
.then(() => {
let query = userModel
.then(access_data => {
let query = proxyHostModel
.query()
.where('is_deleted', 0)
.andWhere('id', data.id)
.allowEager('[permissions]')
.first();
if (access_data.permission_visibility !== 'all') {
query.andWhere('owner_user_id', access.token.get('attrs').id);
}
// Custom omissions
if (typeof data.omit !== 'undefined' && data.omit !== null) {
query.omit(data.omit);
@ -193,19 +163,14 @@ const internalProxyHost = {
.then(() => {
return internalProxyHost.get(access, {id: data.id});
})
.then(user => {
if (!user) {
.then(row => {
if (!row) {
throw new error.ItemNotFoundError(data.id);
}
// Make sure user can't delete themselves
if (user.id === access.token.get('attrs').id) {
throw new error.PermissionError('You cannot delete yourself.');
}
return userModel
return proxyHostModel
.query()
.where('id', user.id)
.where('id', row.id)
.patch({
is_deleted: 1
});
@ -231,7 +196,8 @@ const internalProxyHost = {
.where('is_deleted', 0)
.groupBy('id')
.omit(['is_deleted'])
.orderBy('domain_name', 'ASC');
.allowEager('[owner,access_list]')
.orderBy('domain_names', 'ASC');
if (access_data.permission_visibility !== 'all') {
query.andWhere('owner_user_id', access.token.get('attrs').id);
@ -240,7 +206,7 @@ const internalProxyHost = {
// Query is used for searching
if (typeof search_query === 'string') {
query.where(function () {
this.where('domain_name', 'like', '%' + search_query + '%');
this.where('domain_names', 'like', '%' + search_query + '%');
});
}

View File

@ -26,7 +26,7 @@ const internalProxyHost = {
.where('is_deleted', 0)
.groupBy('id')
.omit(['is_deleted'])
.orderBy('domain_name', 'ASC');
.orderBy('domain_names', 'ASC');
if (access_data.permission_visibility !== 'all') {
query.andWhere('owner_user_id', access.token.get('attrs').id);
@ -35,7 +35,7 @@ const internalProxyHost = {
// Query is used for searching
if (typeof search_query === 'string') {
query.where(function () {
this.where('domain_name', 'like', '%' + search_query + '%');
this.where('domain_names', 'like', '%' + search_query + '%');
});
}

View File

@ -290,6 +290,7 @@ const internalUser = {
.where('is_deleted', 0)
.groupBy('id')
.omit(['is_deleted'])
.allowEager('[permissions]')
.orderBy('name', 'ASC');
// Query is used for searching

View File

@ -301,8 +301,8 @@ module.exports = function (token_string) {
});
})
.catch(err => {
//logger.error(err.message);
//logger.error(err.errors);
logger.error(err.message);
logger.error(err.errors);
throw new error.PermissionError('Permission Denied', err);
});

View 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"]
}
}
}
}
]
}

View 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"]
}
}
}
}
]
}

View 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"]
}
}
}
}
]
}

View 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"]
}
}
}
}
]
}

View File

@ -67,7 +67,7 @@ exports.up = function (knex/*, Promise*/) {
table.dateTime('modified_on').notNull();
table.integer('owner_user_id').notNull().unsigned();
table.integer('is_deleted').notNull().unsigned().defaultTo(0);
table.string('domain_name').notNull();
table.json('domain_names').notNull();
table.string('forward_ip').notNull();
table.integer('forward_port').notNull().unsigned();
table.integer('access_list_id').notNull().unsigned().defaultTo(0);
@ -88,7 +88,7 @@ exports.up = function (knex/*, Promise*/) {
table.dateTime('modified_on').notNull();
table.integer('owner_user_id').notNull().unsigned();
table.integer('is_deleted').notNull().unsigned().defaultTo(0);
table.string('domain_name').notNull();
table.json('domain_names').notNull();
table.string('forward_domain_name').notNull();
table.integer('preserve_path').notNull().unsigned().defaultTo(0);
table.integer('ssl_enabled').notNull().unsigned().defaultTo(0);
@ -106,7 +106,7 @@ exports.up = function (knex/*, Promise*/) {
table.dateTime('modified_on').notNull();
table.integer('owner_user_id').notNull().unsigned();
table.integer('is_deleted').notNull().unsigned().defaultTo(0);
table.string('domain_name').notNull();
table.json('domain_names').notNull();
table.integer('ssl_enabled').notNull().unsigned().defaultTo(0);
table.string('ssl_provider').notNull().defaultTo('');
table.json('meta').notNull();

View 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;

View 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;

View File

@ -27,6 +27,10 @@ class DeadHost extends Model {
return 'dead_host';
}
static get jsonAttributes () {
return ['domain_names', 'meta'];
}
static get relationMappings () {
return {
owner: {
@ -38,7 +42,7 @@ class DeadHost extends Model {
},
modify: function (qb) {
qb.where('user.is_deleted', 0);
qb.omit(['created_on', 'modified_on', 'is_deleted', 'email', 'roles']);
qb.omit(['id', 'created_on', 'modified_on', 'is_deleted', 'email', 'roles']);
}
}
};

View File

@ -3,9 +3,10 @@
'use strict';
const db = require('../db');
const Model = require('objection').Model;
const User = require('./user');
const db = require('../db');
const Model = require('objection').Model;
const User = require('./user');
const AccessList = require('./access_list');
Model.knex(db);
@ -13,10 +14,14 @@ class ProxyHost extends Model {
$beforeInsert () {
this.created_on = Model.raw('NOW()');
this.modified_on = Model.raw('NOW()');
this.domain_names.sort();
}
$beforeUpdate () {
this.modified_on = Model.raw('NOW()');
if (typeof this.domain_names !== 'undefined') {
this.domain_names.sort();
}
}
static get name () {
@ -27,9 +32,13 @@ class ProxyHost extends Model {
return 'proxy_host';
}
static get jsonAttributes () {
return ['domain_names', 'meta'];
}
static get relationMappings () {
return {
owner: {
owner: {
relation: Model.HasOneRelation,
modelClass: User,
join: {
@ -38,7 +47,19 @@ class ProxyHost extends Model {
},
modify: function (qb) {
qb.where('user.is_deleted', 0);
qb.omit(['created_on', 'modified_on', 'is_deleted', 'email', 'roles']);
qb.omit(['id', 'created_on', 'modified_on', 'is_deleted', 'email', 'roles']);
}
},
access_list: {
relation: Model.HasOneRelation,
modelClass: AccessList,
join: {
from: 'proxy_host.access_list_id',
to: 'access_list.id'
},
modify: function (qb) {
qb.where('access_list.is_deleted', 0);
qb.omit(['id', 'created_on', 'modified_on', 'is_deleted']);
}
}
};

View File

@ -27,6 +27,10 @@ class RedirectionHost extends Model {
return 'redirection_host';
}
static get jsonAttributes () {
return ['domain_names', 'meta'];
}
static get relationMappings () {
return {
owner: {
@ -38,7 +42,7 @@ class RedirectionHost extends Model {
},
modify: function (qb) {
qb.where('user.is_deleted', 0);
qb.omit(['created_on', 'modified_on', 'is_deleted', 'email', 'roles']);
qb.omit(['id', 'created_on', 'modified_on', 'is_deleted', 'email', 'roles']);
}
}
};

View File

@ -27,6 +27,10 @@ class Stream extends Model {
return 'stream';
}
static get jsonAttributes () {
return ['meta'];
}
static get relationMappings () {
return {
owner: {
@ -38,7 +42,7 @@ class Stream extends Model {
},
modify: function (qb) {
qb.where('user.is_deleted', 0);
qb.omit(['created_on', 'modified_on', 'is_deleted', 'email', 'roles']);
qb.omit(['id', 'created_on', 'modified_on', 'is_deleted', 'email', 'roles']);
}
}
};

View File

@ -104,7 +104,7 @@ router
})
.then(data => {
return internalProxyHost.get(res.locals.access, {
id: data.host_id,
id: parseInt(data.host_id, 10),
expand: data.expand
});
})
@ -123,7 +123,7 @@ router
.put((req, res, next) => {
apiValidator({$ref: 'endpoints/proxy-hosts#/links/2/schema'}, req.body)
.then(payload => {
payload.id = req.params.host_id;
payload.id = parseInt(req.params.host_id, 10);
return internalProxyHost.update(res.locals.access, payload);
})
.then(result => {
@ -139,7 +139,7 @@ router
* Update and existing proxy-host
*/
.delete((req, res, next) => {
internalProxyHost.delete(res.locals.access, {id: req.params.host_id})
internalProxyHost.delete(res.locals.access, {id: parseInt(req.params.host_id, 10)})
.then(result => {
res.status(200)
.send(result);

View File

@ -134,6 +134,31 @@
"type": "string",
"minLength": 8,
"maxLength": 255
},
"domain_names": {
"description": "Domain Names separated by a comma",
"example": "*.jc21.com,blog.jc21.com",
"type": "array",
"maxItems": 15,
"uniqueItems": true,
"items": {
"type": "string",
"pattern": "^(?:\\*\\.)?(?:[^.*]+\\.?)+[^.]$"
}
},
"ssl_enabled": {
"description": "Is SSL Enabled",
"example": true,
"type": "boolean"
},
"ssl_forced": {
"description": "Is SSL Forced",
"example": false,
"type": "boolean"
},
"ssl_provider": {
"type": "string",
"pattern": "^(letsencrypt|other)$"
}
}
}

View File

@ -1,7 +1,7 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "endpoints/proxy-hosts",
"title": "Users",
"title": "Proxy Hosts",
"description": "Endpoints relating to Proxy Hosts",
"stability": "stable",
"type": "object",
@ -15,49 +15,78 @@
"modified_on": {
"$ref": "../definitions.json#/definitions/modified_on"
},
"name": {
"description": "Name",
"example": "Jamie Curnow",
"domain_names": {
"$ref": "../definitions.json#/definitions/domain_names"
},
"forward_ip": {
"type": "string",
"minLength": 2,
"maxLength": 100
"format": "ipv4"
},
"nickname": {
"description": "Nickname",
"example": "Jamie",
"type": "string",
"minLength": 2,
"maxLength": 50
"forward_port": {
"type": "integer",
"minimum": 1,
"maximum": 65535
},
"email": {
"$ref": "../definitions.json#/definitions/email"
"ssl_enabled": {
"$ref": "../definitions.json#/definitions/ssl_enabled"
},
"avatar": {
"description": "Avatar",
"example": "http://somewhere.jpg",
"type": "string",
"minLength": 2,
"maxLength": 150,
"readOnly": true
"ssl_forced": {
"$ref": "../definitions.json#/definitions/ssl_forced"
},
"roles": {
"description": "Roles",
"example": [
"admin"
],
"type": "array"
"ssl_provider": {
"$ref": "../definitions.json#/definitions/ssl_provider"
},
"is_disabled": {
"description": "Is Disabled",
"example": false,
"type": "boolean"
"meta": {
"type": "object",
"additionalProperties": false,
"properties": {
"letsencrypt_email": {
"type": "string",
"format": "email"
},
"letsencrypt_agree": {
"type": "boolean"
}
}
}
},
"properties": {
"id": {
"$ref": "#/definitions/id"
},
"created_on": {
"$ref": "#/definitions/created_on"
},
"modified_on": {
"$ref": "#/definitions/modified_on"
},
"domain_names": {
"$ref": "#/definitions/domain_names"
},
"forward_ip": {
"$ref": "#/definitions/forward_ip"
},
"forward_port": {
"$ref": "#/definitions/forward_port"
},
"ssl_enabled": {
"$ref": "#/definitions/ssl_enabled"
},
"ssl_forced": {
"$ref": "#/definitions/ssl_forced"
},
"ssl_provider": {
"$ref": "#/definitions/ssl_provider"
},
"meta": {
"$ref": "#/definitions/meta"
}
},
"links": [
{
"title": "List",
"description": "Returns a list of Users",
"href": "/users",
"description": "Returns a list of Proxy Hosts",
"href": "/nginx/proxy-hosts",
"access": "private",
"method": "GET",
"rel": "self",
@ -73,8 +102,8 @@
},
{
"title": "Create",
"description": "Creates a new User",
"href": "/users",
"description": "Creates a new Proxy Host",
"href": "/nginx/proxy-hosts",
"access": "private",
"method": "POST",
"rel": "create",
@ -84,33 +113,31 @@
"schema": {
"type": "object",
"required": [
"name",
"nickname",
"email"
"domain_names",
"forward_ip",
"forward_port"
],
"properties": {
"name": {
"$ref": "#/definitions/name"
"domain_names": {
"$ref": "#/definitions/domain_names"
},
"nickname": {
"$ref": "#/definitions/nickname"
"forward_ip": {
"$ref": "#/definitions/forward_ip"
},
"email": {
"$ref": "#/definitions/email"
"forward_port": {
"$ref": "#/definitions/forward_port"
},
"roles": {
"$ref": "#/definitions/roles"
"ssl_enabled": {
"$ref": "#/definitions/ssl_enabled"
},
"is_disabled": {
"$ref": "#/definitions/is_disabled"
"ssl_forced": {
"$ref": "#/definitions/ssl_forced"
},
"auth": {
"type": "object",
"description": "Auth Credentials",
"example": {
"type": "password",
"secret": "bigredhorsebanana"
}
"ssl_provider": {
"$ref": "#/definitions/ssl_provider"
},
"meta": {
"$ref": "#/definitions/meta"
}
}
},
@ -122,8 +149,8 @@
},
{
"title": "Update",
"description": "Updates a existing User",
"href": "/users/{definitions.identity.example}",
"description": "Updates a existing Proxy Host",
"href": "/nginx/proxy-hosts/{definitions.identity.example}",
"access": "private",
"method": "PUT",
"rel": "update",
@ -133,20 +160,26 @@
"schema": {
"type": "object",
"properties": {
"name": {
"$ref": "#/definitions/name"
"domain_names": {
"$ref": "#/definitions/domain_names"
},
"nickname": {
"$ref": "#/definitions/nickname"
"forward_ip": {
"$ref": "#/definitions/forward_ip"
},
"email": {
"$ref": "#/definitions/email"
"forward_port": {
"$ref": "#/definitions/forward_port"
},
"roles": {
"$ref": "#/definitions/roles"
"ssl_enabled": {
"$ref": "#/definitions/ssl_enabled"
},
"is_disabled": {
"$ref": "#/definitions/is_disabled"
"ssl_forced": {
"$ref": "#/definitions/ssl_forced"
},
"ssl_provider": {
"$ref": "#/definitions/ssl_provider"
},
"meta": {
"$ref": "#/definitions/meta"
}
}
},
@ -158,8 +191,8 @@
},
{
"title": "Delete",
"description": "Deletes a existing User",
"href": "/users/{definitions.identity.example}",
"description": "Deletes a existing Proxy Host",
"href": "/nginx/proxy-hosts/{definitions.identity.example}",
"access": "private",
"method": "DELETE",
"rel": "delete",
@ -170,34 +203,5 @@
"type": "boolean"
}
}
],
"properties": {
"id": {
"$ref": "#/definitions/id"
},
"created_on": {
"$ref": "#/definitions/created_on"
},
"modified_on": {
"$ref": "#/definitions/modified_on"
},
"name": {
"$ref": "#/definitions/name"
},
"nickname": {
"$ref": "#/definitions/nickname"
},
"email": {
"$ref": "#/definitions/email"
},
"avatar": {
"$ref": "#/definitions/avatar"
},
"roles": {
"$ref": "#/definitions/roles"
},
"is_disabled": {
"$ref": "#/definitions/is_disabled"
}
}
]
}