mirror of
https://github.com/NginxProxyManager/nginx-proxy-manager.git
synced 2025-06-18 18:16:26 +00:00
Compare commits
32 Commits
v2.12.0
...
12abd5b407
Author | SHA1 | Date | |
---|---|---|---|
12abd5b407 | |||
25a26d6175 | |||
17246e418f | |||
f7d3ca0b07 | |||
a55de386e7 | |||
e9d4f5b827 | |||
1c1cee3836 | |||
eaf6335694 | |||
ffe05ebd41 | |||
2e9a4f1aed | |||
d17c85e4c8 | |||
dad8d0ca00 | |||
d7e0558a35 | |||
ee41bb5562 | |||
0cf6b9caa4 | |||
1d60c5ad12 | |||
f1946574ec | |||
57d90948d8 | |||
2e474d5589 | |||
9a03a247d9 | |||
68a9baf206 | |||
d92421d098 | |||
96c58b203e | |||
d499e2bfef | |||
2f9e062718 | |||
edbed1af90 | |||
8497022e41 | |||
fa2c814fcb | |||
d96a3987c0 | |||
0987d7bba9 | |||
b0b234ff7d | |||
10f61595e1 |
10
Jenkinsfile
vendored
10
Jenkinsfile
vendored
@ -43,7 +43,7 @@ pipeline {
|
|||||||
steps {
|
steps {
|
||||||
script {
|
script {
|
||||||
// Defaults to the Branch name, which is applies to all branches AND pr's
|
// Defaults to the Branch name, which is applies to all branches AND pr's
|
||||||
buildxPushTags = "-t docker.io/jc21/${IMAGE}:github-${BRANCH_LOWER}"
|
buildxPushTags = "-t docker.io/nginxproxymanager/${IMAGE}-dev:${BRANCH_LOWER}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -203,7 +203,13 @@ pipeline {
|
|||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
script {
|
script {
|
||||||
npmGithubPrComment("Docker Image for build ${BUILD_NUMBER} is available on [DockerHub](https://cloud.docker.com/repository/docker/jc21/${IMAGE}) as `jc21/${IMAGE}:github-${BRANCH_LOWER}`\n\n**Note:** ensure you backup your NPM instance before testing this PR image! Especially if this PR contains database changes.", true)
|
npmGithubPrComment("""Docker Image for build ${BUILD_NUMBER} is available on
|
||||||
|
[DockerHub](https://cloud.docker.com/repository/docker/nginxproxymanager/${IMAGE}-dev)
|
||||||
|
as `nginxproxymanager/${IMAGE}-dev:${BRANCH_LOWER}`
|
||||||
|
|
||||||
|
**Note:** ensure you backup your NPM instance before testing this image! Especially if there are database changes
|
||||||
|
**Note:** this is a different docker image namespace than the official image
|
||||||
|
""", true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="https://nginxproxymanager.com/github.png">
|
<img src="https://nginxproxymanager.com/github.png">
|
||||||
<br><br>
|
<br><br>
|
||||||
<img src="https://img.shields.io/badge/version-2.12.0-green.svg?style=for-the-badge">
|
<img src="https://img.shields.io/badge/version-2.12.1-green.svg?style=for-the-badge">
|
||||||
<a href="https://hub.docker.com/repository/docker/jc21/nginx-proxy-manager">
|
<a href="https://hub.docker.com/repository/docker/jc21/nginx-proxy-manager">
|
||||||
<img src="https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge">
|
<img src="https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge">
|
||||||
</a>
|
</a>
|
||||||
|
@ -29,6 +29,7 @@ if (config.debug()) {
|
|||||||
app.set('json spaces', 2);
|
app.set('json spaces', 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// CORS for everything
|
// CORS for everything
|
||||||
app.use(require('./lib/express/cors'));
|
app.use(require('./lib/express/cors'));
|
||||||
|
|
||||||
|
@ -9,6 +9,22 @@ function generateDbConfig() {
|
|||||||
if (cfg.engine === 'knex-native') {
|
if (cfg.engine === 'knex-native') {
|
||||||
return cfg.knex;
|
return cfg.knex;
|
||||||
}
|
}
|
||||||
|
if (cfg.engine === 'pg') {
|
||||||
|
|
||||||
|
return {
|
||||||
|
client: cfg.engine,
|
||||||
|
connection: {
|
||||||
|
host: cfg.host,
|
||||||
|
user: cfg.user,
|
||||||
|
password: cfg.password,
|
||||||
|
database: cfg.name,
|
||||||
|
port: cfg.port
|
||||||
|
},
|
||||||
|
migrations: {
|
||||||
|
tableName: 'migrations'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
client: cfg.engine,
|
client: cfg.engine,
|
||||||
connection: {
|
connection: {
|
||||||
|
@ -252,10 +252,14 @@ const internalAccessList = {
|
|||||||
let query = accessListModel
|
let query = accessListModel
|
||||||
.query()
|
.query()
|
||||||
.select('access_list.*', accessListModel.raw('COUNT(proxy_host.id) as proxy_host_count'))
|
.select('access_list.*', accessListModel.raw('COUNT(proxy_host.id) as proxy_host_count'))
|
||||||
.joinRaw('LEFT JOIN `proxy_host` ON `proxy_host`.`access_list_id` = `access_list`.`id` AND `proxy_host`.`is_deleted` = 0')
|
.leftJoin('proxy_host', function() {
|
||||||
|
this.on('proxy_host.access_list_id', '=', 'access_list.id')
|
||||||
|
.andOn('proxy_host.is_deleted', '=', 0);
|
||||||
|
})
|
||||||
.where('access_list.is_deleted', 0)
|
.where('access_list.is_deleted', 0)
|
||||||
.andWhere('access_list.id', data.id)
|
.andWhere('access_list.id', data.id)
|
||||||
.allowGraph('[owner,items,clients,proxy_hosts.[certificate,access_list.[clients,items]]]')
|
.allowGraph('[owner,items,clients,proxy_hosts.[certificate,access_list.[clients,items]]]')
|
||||||
|
.groupBy('access_list.id')
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
if (access_data.permission_visibility !== 'all') {
|
if (access_data.permission_visibility !== 'all') {
|
||||||
@ -373,7 +377,10 @@ const internalAccessList = {
|
|||||||
let query = accessListModel
|
let query = accessListModel
|
||||||
.query()
|
.query()
|
||||||
.select('access_list.*', accessListModel.raw('COUNT(proxy_host.id) as proxy_host_count'))
|
.select('access_list.*', accessListModel.raw('COUNT(proxy_host.id) as proxy_host_count'))
|
||||||
.joinRaw('LEFT JOIN `proxy_host` ON `proxy_host`.`access_list_id` = `access_list`.`id` AND `proxy_host`.`is_deleted` = 0')
|
.leftJoin('proxy_host', function() {
|
||||||
|
this.on('proxy_host.access_list_id', '=', 'access_list.id')
|
||||||
|
.andOn('proxy_host.is_deleted', '=', 0);
|
||||||
|
})
|
||||||
.where('access_list.is_deleted', 0)
|
.where('access_list.is_deleted', 0)
|
||||||
.groupBy('access_list.id')
|
.groupBy('access_list.id')
|
||||||
.allowGraph('[owner,items,clients]')
|
.allowGraph('[owner,items,clients]')
|
||||||
|
@ -22,9 +22,9 @@ const internalAuditLog = {
|
|||||||
.allowGraph('[user]');
|
.allowGraph('[user]');
|
||||||
|
|
||||||
// Query is used for searching
|
// Query is used for searching
|
||||||
if (typeof search_query === 'string') {
|
if (typeof search_query === 'string' && search_query.length > 0) {
|
||||||
query.where(function () {
|
query.where(function () {
|
||||||
this.where('meta', 'like', '%' + search_query + '%');
|
this.whereRaw('CAST(meta AS VARCHAR(65535)) like ? ESCAPE \'\'', '%' + search_query + '%');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -409,16 +409,16 @@ const internalDeadHost = {
|
|||||||
.where('is_deleted', 0)
|
.where('is_deleted', 0)
|
||||||
.groupBy('id')
|
.groupBy('id')
|
||||||
.allowGraph('[owner,certificate]')
|
.allowGraph('[owner,certificate]')
|
||||||
.orderBy('domain_names', 'ASC');
|
.orderByRaw('CAST(domain_names AS VARCHAR(65535)) ASC');
|
||||||
|
|
||||||
if (access_data.permission_visibility !== 'all') {
|
if (access_data.permission_visibility !== 'all') {
|
||||||
query.andWhere('owner_user_id', access.token.getUserId(1));
|
query.andWhere('owner_user_id', access.token.getUserId(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query is used for searching
|
// Query is used for searching
|
||||||
if (typeof search_query === 'string') {
|
if (typeof search_query === 'string' && search_query.length > 0) {
|
||||||
query.where(function () {
|
query.where(function () {
|
||||||
this.where('domain_names', 'like', '%' + search_query + '%');
|
this.whereRaw('CAST(domain_names AS VARCHAR(65535)) like ? ESCAPE \'\'', '%' + search_query + '%');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,15 +129,15 @@ const internalHost = {
|
|||||||
proxyHostModel
|
proxyHostModel
|
||||||
.query()
|
.query()
|
||||||
.where('is_deleted', 0)
|
.where('is_deleted', 0)
|
||||||
.andWhere('domain_names', 'like', '%' + hostname + '%'),
|
.whereRaw('CAST(domain_names AS VARCHAR(65535)) like ? ESCAPE \'\'', '%'+hostname + '%'),
|
||||||
redirectionHostModel
|
redirectionHostModel
|
||||||
.query()
|
.query()
|
||||||
.where('is_deleted', 0)
|
.where('is_deleted', 0)
|
||||||
.andWhere('domain_names', 'like', '%' + hostname + '%'),
|
.whereRaw('CAST(domain_names AS VARCHAR(65535)) like ? ESCAPE \'\'', '%'+hostname + '%'),
|
||||||
deadHostModel
|
deadHostModel
|
||||||
.query()
|
.query()
|
||||||
.where('is_deleted', 0)
|
.where('is_deleted', 0)
|
||||||
.andWhere('domain_names', 'like', '%' + hostname + '%')
|
.whereRaw('CAST(domain_names AS VARCHAR(65535)) like ? ESCAPE \'\'', '%'+hostname + '%'),
|
||||||
];
|
];
|
||||||
|
|
||||||
return Promise.all(promises)
|
return Promise.all(promises)
|
||||||
|
@ -181,7 +181,9 @@ const internalNginx = {
|
|||||||
* @param {Object} host
|
* @param {Object} host
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
generateConfig: (host_type, host) => {
|
generateConfig: (host_type, host_row) => {
|
||||||
|
// Prevent modifying the original object:
|
||||||
|
let host = JSON.parse(JSON.stringify(host_row));
|
||||||
const nice_host_type = internalNginx.getFileFriendlyHostType(host_type);
|
const nice_host_type = internalNginx.getFileFriendlyHostType(host_type);
|
||||||
|
|
||||||
if (config.debug()) {
|
if (config.debug()) {
|
||||||
|
@ -409,6 +409,7 @@ const internalProxyHost = {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
getAll: (access, expand, search_query) => {
|
getAll: (access, expand, search_query) => {
|
||||||
|
|
||||||
return access.can('proxy_hosts:list')
|
return access.can('proxy_hosts:list')
|
||||||
.then((access_data) => {
|
.then((access_data) => {
|
||||||
let query = proxyHostModel
|
let query = proxyHostModel
|
||||||
@ -416,16 +417,17 @@ const internalProxyHost = {
|
|||||||
.where('is_deleted', 0)
|
.where('is_deleted', 0)
|
||||||
.groupBy('id')
|
.groupBy('id')
|
||||||
.allowGraph('[owner,access_list,certificate]')
|
.allowGraph('[owner,access_list,certificate]')
|
||||||
.orderBy('domain_names', 'ASC');
|
.orderByRaw('CAST(domain_names AS VARCHAR(65535) ) ASC')
|
||||||
|
;
|
||||||
|
|
||||||
if (access_data.permission_visibility !== 'all') {
|
if (access_data.permission_visibility !== 'all') {
|
||||||
query.andWhere('owner_user_id', access.token.getUserId(1));
|
query.andWhere('owner_user_id', access.token.getUserId(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query is used for searching
|
// Query is used for searching
|
||||||
if (typeof search_query === 'string') {
|
if (typeof search_query === 'string' && search_query.length > 0) {
|
||||||
query.where(function () {
|
query.where(function () {
|
||||||
this.where('domain_names', 'like', '%' + search_query + '%');
|
this.whereRaw('CAST(domain_names AS VARCHAR(65535) ) like ? ESCAPE \'\'', '%'+search_query + '%');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -436,6 +438,7 @@ const internalProxyHost = {
|
|||||||
return query.then(utils.omitRows(omissions()));
|
return query.then(utils.omitRows(omissions()));
|
||||||
})
|
})
|
||||||
.then((rows) => {
|
.then((rows) => {
|
||||||
|
|
||||||
if (typeof expand !== 'undefined' && expand !== null && expand.indexOf('certificate') !== -1) {
|
if (typeof expand !== 'undefined' && expand !== null && expand.indexOf('certificate') !== -1) {
|
||||||
return internalHost.cleanAllRowsCertificateMeta(rows);
|
return internalHost.cleanAllRowsCertificateMeta(rows);
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,6 @@ const internalRedirectionHost = {
|
|||||||
*/
|
*/
|
||||||
create: (access, data) => {
|
create: (access, data) => {
|
||||||
let create_certificate = data.certificate_id === 'new';
|
let create_certificate = data.certificate_id === 'new';
|
||||||
|
|
||||||
if (create_certificate) {
|
if (create_certificate) {
|
||||||
delete data.certificate_id;
|
delete data.certificate_id;
|
||||||
}
|
}
|
||||||
@ -409,16 +408,16 @@ const internalRedirectionHost = {
|
|||||||
.where('is_deleted', 0)
|
.where('is_deleted', 0)
|
||||||
.groupBy('id')
|
.groupBy('id')
|
||||||
.allowGraph('[owner,certificate]')
|
.allowGraph('[owner,certificate]')
|
||||||
.orderBy('domain_names', 'ASC');
|
.orderByRaw('CAST(domain_names AS VARCHAR(65535) ) ASC');
|
||||||
|
|
||||||
if (access_data.permission_visibility !== 'all') {
|
if (access_data.permission_visibility !== 'all') {
|
||||||
query.andWhere('owner_user_id', access.token.getUserId(1));
|
query.andWhere('owner_user_id', access.token.getUserId(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query is used for searching
|
// Query is used for searching
|
||||||
if (typeof search_query === 'string') {
|
if (typeof search_query === 'string' && search_query.length > 0) {
|
||||||
query.where(function () {
|
query.where(function () {
|
||||||
this.where('domain_names', 'like', '%' + search_query + '%');
|
this.whereRaw('CAST(domain_names AS VARCHAR(65535) ) like ? ESCAPE \'\'', '%' + search_query + '%');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,16 +298,18 @@ const internalStream = {
|
|||||||
.where('is_deleted', 0)
|
.where('is_deleted', 0)
|
||||||
.groupBy('id')
|
.groupBy('id')
|
||||||
.allowGraph('[owner]')
|
.allowGraph('[owner]')
|
||||||
.orderBy('incoming_port', 'ASC');
|
//.orderBy('incoming_port', 'ASC')
|
||||||
|
.orderByRaw('CAST(incoming_port AS INTEGER) ASC')
|
||||||
|
;
|
||||||
|
|
||||||
if (access_data.permission_visibility !== 'all') {
|
if (access_data.permission_visibility !== 'all') {
|
||||||
query.andWhere('owner_user_id', access.token.getUserId(1));
|
query.andWhere('owner_user_id', access.token.getUserId(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query is used for searching
|
// Query is used for searching
|
||||||
if (typeof search_query === 'string') {
|
if (typeof search_query === 'string' && search_query.length > 0) {
|
||||||
query.where(function () {
|
query.where(function () {
|
||||||
this.where('incoming_port', 'like', '%' + search_query + '%');
|
this.whereRaw('CAST(incoming_port AS VARCHAR(65535)) like ? ESCAPE \'\'', '%' + search_query+ '%');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,6 +45,25 @@ const configure = () => {
|
|||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const envPostgresqlHost = process.env.DB_POSTGRESQL_HOST || null;
|
||||||
|
const envPostgresqlUser = process.env.DB_POSTGRESQL_USER || null;
|
||||||
|
const envPostgresqlName = process.env.DB_POSTGRESQL_NAME || null;
|
||||||
|
if (envPostgresqlHost && envPostgresqlUser && envPostgresqlName) {
|
||||||
|
// we have enough mysql creds to go with mysql
|
||||||
|
logger.info('Using POSTGRESQL configuration');
|
||||||
|
instance = {
|
||||||
|
database: {
|
||||||
|
engine: 'pg',
|
||||||
|
host: envPostgresqlHost,
|
||||||
|
port: process.env.DB_POSTGRESQL_PORT || 5432,
|
||||||
|
user: envPostgresqlUser,
|
||||||
|
password: process.env.DB_POSTGRESQL_PASSWORD,
|
||||||
|
name: envPostgresqlName,
|
||||||
|
},
|
||||||
|
keys: getKeys(),
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const envSqliteFile = process.env.DB_SQLITE_FILE || '/data/database.sqlite';
|
const envSqliteFile = process.env.DB_SQLITE_FILE || '/data/database.sqlite';
|
||||||
logger.info(`Using Sqlite: ${envSqliteFile}`);
|
logger.info(`Using Sqlite: ${envSqliteFile}`);
|
||||||
|
@ -17,6 +17,9 @@ const boolFields = [
|
|||||||
'preserve_path',
|
'preserve_path',
|
||||||
'ssl_forced',
|
'ssl_forced',
|
||||||
'block_exploits',
|
'block_exploits',
|
||||||
|
'hsts_enabled',
|
||||||
|
'hsts_subdomains',
|
||||||
|
'http2_support',
|
||||||
];
|
];
|
||||||
|
|
||||||
class RedirectionHost extends Model {
|
class RedirectionHost extends Model {
|
||||||
|
@ -21,8 +21,9 @@
|
|||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"mysql2": "^3.11.1",
|
"mysql2": "^3.11.1",
|
||||||
"node-rsa": "^1.0.8",
|
"node-rsa": "^1.0.8",
|
||||||
"objection": "3.0.1",
|
"objection": "^3.0.1",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
|
"pg": "^8.13.0",
|
||||||
"signale": "1.4.0",
|
"signale": "1.4.0",
|
||||||
"sqlite3": "5.1.6",
|
"sqlite3": "5.1.6",
|
||||||
"temp-write": "^4.0.0"
|
"temp-write": "^4.0.0"
|
||||||
|
@ -23,9 +23,7 @@
|
|||||||
"locations",
|
"locations",
|
||||||
"hsts_enabled",
|
"hsts_enabled",
|
||||||
"hsts_subdomains",
|
"hsts_subdomains",
|
||||||
"certificate",
|
"certificate"
|
||||||
"use_default_location",
|
|
||||||
"ipv6"
|
|
||||||
],
|
],
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -151,12 +149,6 @@
|
|||||||
"$ref": "./access-list-object.json"
|
"$ref": "./access-list-object.json"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
"use_default_location": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"ipv6": {
|
|
||||||
"type": "boolean"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
},
|
},
|
||||||
"forward_scheme": {
|
"forward_scheme": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["http", "https"]
|
"enum": ["auto", "http", "https"]
|
||||||
},
|
},
|
||||||
"forward_domain_name": {
|
"forward_domain_name": {
|
||||||
"description": "Domain Name",
|
"description": "Domain Name",
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
"value": {
|
"value": {
|
||||||
"description": "Value in almost any form",
|
"description": "Value in almost any form",
|
||||||
"example": "congratulations",
|
"example": "congratulations",
|
||||||
"oneOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"minLength": 1
|
"minLength": 1
|
||||||
@ -46,7 +46,10 @@
|
|||||||
},
|
},
|
||||||
"meta": {
|
"meta": {
|
||||||
"description": "Extra metadata",
|
"description": "Extra metadata",
|
||||||
"example": {},
|
"example": {
|
||||||
|
"redirect": "http://example.com",
|
||||||
|
"html": "<h1>404</h1>"
|
||||||
|
},
|
||||||
"type": "object"
|
"type": "object"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,8 +49,7 @@
|
|||||||
"minLength": 1
|
"minLength": 1
|
||||||
},
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"type": "string",
|
"type": "string"
|
||||||
"minLength": 1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,9 +94,7 @@
|
|||||||
"avatar": "",
|
"avatar": "",
|
||||||
"roles": ["admin"]
|
"roles": ["admin"]
|
||||||
},
|
},
|
||||||
"certificate": null,
|
"certificate": null
|
||||||
"use_default_location": true,
|
|
||||||
"ipv6": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -79,9 +79,7 @@
|
|||||||
"nickname": "Admin",
|
"nickname": "Admin",
|
||||||
"avatar": "",
|
"avatar": "",
|
||||||
"roles": ["admin"]
|
"roles": ["admin"]
|
||||||
},
|
}
|
||||||
"use_default_location": true,
|
|
||||||
"ipv6": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -129,9 +129,7 @@
|
|||||||
"roles": ["admin"]
|
"roles": ["admin"]
|
||||||
},
|
},
|
||||||
"certificate": null,
|
"certificate": null,
|
||||||
"access_list": null,
|
"access_list": null
|
||||||
"use_default_location": true,
|
|
||||||
"ipv6": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -114,9 +114,7 @@
|
|||||||
"avatar": "//www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?default=mm",
|
"avatar": "//www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?default=mm",
|
||||||
"roles": ["admin"]
|
"roles": ["admin"]
|
||||||
},
|
},
|
||||||
"access_list": null,
|
"access_list": null
|
||||||
"use_default_location": true,
|
|
||||||
"ipv6": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -114,9 +114,7 @@
|
|||||||
"avatar": "",
|
"avatar": "",
|
||||||
"roles": ["admin"]
|
"roles": ["admin"]
|
||||||
},
|
},
|
||||||
"certificate": null,
|
"certificate": null
|
||||||
"use_default_location": true,
|
|
||||||
"ipv6": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -99,9 +99,7 @@
|
|||||||
"nickname": "Admin",
|
"nickname": "Admin",
|
||||||
"avatar": "",
|
"avatar": "",
|
||||||
"roles": ["admin"]
|
"roles": ["admin"]
|
||||||
},
|
}
|
||||||
"use_default_location": true,
|
|
||||||
"ipv6": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -129,9 +129,7 @@
|
|||||||
"roles": ["admin"]
|
"roles": ["admin"]
|
||||||
},
|
},
|
||||||
"certificate": null,
|
"certificate": null,
|
||||||
"access_list": null,
|
"access_list": null
|
||||||
"use_default_location": true,
|
|
||||||
"ipv6": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -13,7 +13,8 @@
|
|||||||
"name": "settingID",
|
"name": "settingID",
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"minLength": 1
|
"minLength": 1,
|
||||||
|
"enum": ["default-site"]
|
||||||
},
|
},
|
||||||
"required": true,
|
"required": true,
|
||||||
"description": "Setting ID",
|
"description": "Setting ID",
|
||||||
@ -31,10 +32,21 @@
|
|||||||
"minProperties": 1,
|
"minProperties": 1,
|
||||||
"properties": {
|
"properties": {
|
||||||
"value": {
|
"value": {
|
||||||
"$ref": "../../../components/setting-object.json#/properties/value"
|
"type": "string",
|
||||||
|
"minLength": 1,
|
||||||
|
"enum": ["congratulations", "404", "444", "redirect", "html"]
|
||||||
},
|
},
|
||||||
"meta": {
|
"meta": {
|
||||||
"$ref": "../../../components/setting-object.json#/properties/meta"
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"redirect": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"html": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,11 +15,11 @@ const certbot = require('./lib/certbot');
|
|||||||
const setupDefaultUser = () => {
|
const setupDefaultUser = () => {
|
||||||
return userModel
|
return userModel
|
||||||
.query()
|
.query()
|
||||||
.select(userModel.raw('COUNT(`id`) as `count`'))
|
.select('id')
|
||||||
.where('is_deleted', 0)
|
.where('is_deleted', 0)
|
||||||
.first()
|
|
||||||
.then((row) => {
|
.then((row) => {
|
||||||
if (!row.count) {
|
if (row.length === 0) {
|
||||||
|
|
||||||
// Create a new user and set password
|
// Create a new user and set password
|
||||||
let email = process.env.INITIAL_ADMIN_EMAIL || 'admin@example.com';
|
let email = process.env.INITIAL_ADMIN_EMAIL || 'admin@example.com';
|
||||||
let password = process.env.INITIAL_ADMIN_PASSWORD || 'changeme';
|
let password = process.env.INITIAL_ADMIN_PASSWORD || 'changeme';
|
||||||
@ -77,11 +77,10 @@ const setupDefaultUser = () => {
|
|||||||
const setupDefaultSettings = () => {
|
const setupDefaultSettings = () => {
|
||||||
return settingModel
|
return settingModel
|
||||||
.query()
|
.query()
|
||||||
.select(settingModel.raw('COUNT(`id`) as `count`'))
|
.select('id')
|
||||||
.where({id: 'default-site'})
|
.where({id: 'default-site'})
|
||||||
.first()
|
|
||||||
.then((row) => {
|
.then((row) => {
|
||||||
if (!row.count) {
|
if (!row.length || !row[0].id) {
|
||||||
settingModel
|
settingModel
|
||||||
.query()
|
.query()
|
||||||
.insert({
|
.insert({
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
auth_basic "Authorization required";
|
auth_basic "Authorization required";
|
||||||
auth_basic_user_file /data/access/{{ access_list_id }};
|
auth_basic_user_file /data/access/{{ access_list_id }};
|
||||||
|
|
||||||
{% if access_list.pass_auth == 0 %}
|
{% if access_list.pass_auth == 0 or access_list.pass_auth == true %}
|
||||||
proxy_set_header Authorization "";
|
proxy_set_header Authorization "";
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
@ -17,7 +17,7 @@
|
|||||||
deny all;
|
deny all;
|
||||||
|
|
||||||
# Access checks must...
|
# Access checks must...
|
||||||
{% if access_list.satisfy_any == 1 %}
|
{% if access_list.satisfy_any == 1 or access_list.satisfy_any == true %}
|
||||||
satisfy any;
|
satisfy any;
|
||||||
{% else %}
|
{% else %}
|
||||||
satisfy all;
|
satisfy all;
|
||||||
|
@ -5,11 +5,16 @@
|
|||||||
#listen [::]:80;
|
#listen [::]:80;
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if certificate -%}
|
{% if certificate -%}
|
||||||
listen 443 ssl{% if http2_support == 1 or http2_support == true %} http2{% endif %};
|
listen 443 ssl;
|
||||||
{% if ipv6 -%}
|
{% if ipv6 -%}
|
||||||
listen [::]:443 ssl{% if http2_support == 1 or http2_support == true %} http2{% endif %};
|
listen [::]:443 ssl;
|
||||||
{% else -%}
|
{% else -%}
|
||||||
#listen [::]:443;
|
#listen [::]:443;
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
server_name {{ domain_names | join: " " }};
|
server_name {{ domain_names | join: " " }};
|
||||||
|
{% if http2_support == 1 or http2_support == true %}
|
||||||
|
http2 on;
|
||||||
|
{% else -%}
|
||||||
|
http2 off;
|
||||||
|
{% endif %}
|
@ -7,11 +7,7 @@
|
|||||||
proxy_set_header X-Forwarded-For $remote_addr;
|
proxy_set_header X-Forwarded-For $remote_addr;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
|
||||||
set $proxy_forward_scheme {{ forward_scheme }};
|
proxy_pass {{ forward_scheme }}://{{ forward_host }}:{{ forward_port }}{{ forward_path }};
|
||||||
set $proxy_server "{{ forward_host }}";
|
|
||||||
set $proxy_port {{ forward_port }};
|
|
||||||
|
|
||||||
proxy_pass $proxy_forward_scheme://$proxy_server:$proxy_port{{ forward_path }};
|
|
||||||
|
|
||||||
{% include "_access.conf" %}
|
{% include "_access.conf" %}
|
||||||
{% include "_assets.conf" %}
|
{% include "_assets.conf" %}
|
||||||
|
1491
backend/yarn.lock
1491
backend/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -26,6 +26,8 @@ WORKDIR /root
|
|||||||
|
|
||||||
COPY rootfs /
|
COPY rootfs /
|
||||||
COPY scripts/install-s6 /tmp/install-s6
|
COPY scripts/install-s6 /tmp/install-s6
|
||||||
|
RUN /tmp/install-s6 "${TARGETPLATFORM}" && rm -f /tmp/install-s6
|
||||||
|
RUN chmod 644 -R /root/.cache
|
||||||
RUN rm -f /etc/nginx/conf.d/production.conf \
|
RUN rm -f /etc/nginx/conf.d/production.conf \
|
||||||
&& chmod 644 /etc/logrotate.d/nginx-proxy-manager \
|
&& chmod 644 /etc/logrotate.d/nginx-proxy-manager \
|
||||||
&& /tmp/install-s6 "${TARGETPLATFORM}" \
|
&& /tmp/install-s6 "${TARGETPLATFORM}" \
|
||||||
|
29
docker/docker-compose.ci.postgresql.yml
Normal file
29
docker/docker-compose.ci.postgresql.yml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# WARNING: This is a CI docker-compose file used for building and testing of the entire app, it should not be used for production.
|
||||||
|
services:
|
||||||
|
|
||||||
|
fullstack:
|
||||||
|
environment:
|
||||||
|
DB_POSTGRESQL_HOST: 'db'
|
||||||
|
DB_POSTGRESQL_PORT: '5432'
|
||||||
|
DB_POSTGRESQL_USER: 'npm'
|
||||||
|
DB_POSTGRESQL_PASSWORD: 'npmpass'
|
||||||
|
DB_POSTGRESQL_NAME: 'npm'
|
||||||
|
depends_on:
|
||||||
|
- db-postgresql
|
||||||
|
|
||||||
|
db-postgresql:
|
||||||
|
image: postgres:14.2-alpine
|
||||||
|
environment:
|
||||||
|
POSTGRES_PASSWORD: "npmpass"
|
||||||
|
POSTGRES_USER: "npm"
|
||||||
|
POSTGRES_DB: "npm"
|
||||||
|
ports:
|
||||||
|
- 5432:5432
|
||||||
|
volumes:
|
||||||
|
- postgres_vol:/var/lib/postgresql/data
|
||||||
|
networks:
|
||||||
|
- fulltest
|
||||||
|
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres_vol:
|
89
docker/docker-compose.dev2.yml
Normal file
89
docker/docker-compose.dev2.yml
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
# WARNING: This is a DEVELOPMENT docker-compose file, it should not be used for production.
|
||||||
|
services:
|
||||||
|
|
||||||
|
npm1:
|
||||||
|
image: nginxproxymanager:dev
|
||||||
|
container_name: npm_core1
|
||||||
|
build:
|
||||||
|
context: ./
|
||||||
|
dockerfile: ./dev/Dockerfile
|
||||||
|
ports:
|
||||||
|
- 4080:80
|
||||||
|
- 4081:81
|
||||||
|
- 4443:443
|
||||||
|
networks:
|
||||||
|
- nginx_proxy_manager
|
||||||
|
environment:
|
||||||
|
PUID: 1000
|
||||||
|
PGID: 1000
|
||||||
|
FORCE_COLOR: 1
|
||||||
|
# specifically for dev:
|
||||||
|
DEBUG: 'true'
|
||||||
|
DEVELOPMENT: 'true'
|
||||||
|
LE_STAGING: 'true'
|
||||||
|
# db:
|
||||||
|
DB_POSTGRESQL_HOST: 'db1'
|
||||||
|
DB_POSTGRESQL_PORT: '5432'
|
||||||
|
DB_POSTGRESQL_USER: 'npm'
|
||||||
|
DB_POSTGRESQL_PASSWORD: 'npmpass'
|
||||||
|
DB_POSTGRESQL_NAME: 'npm'
|
||||||
|
# DB_SQLITE_FILE: "/data/database.sqlite"
|
||||||
|
# DISABLE_IPV6: "true"
|
||||||
|
volumes:
|
||||||
|
- npm_data1:/data
|
||||||
|
- le_data1:/etc/letsencrypt
|
||||||
|
- ../backend:/app
|
||||||
|
- ../frontend:/app/frontend
|
||||||
|
- ../global:/app/global
|
||||||
|
depends_on:
|
||||||
|
- db1
|
||||||
|
working_dir: /app
|
||||||
|
|
||||||
|
db1:
|
||||||
|
image: postgis/postgis:17-3.5-alpine
|
||||||
|
container_name: npm_db1
|
||||||
|
ports:
|
||||||
|
- 5432:5432
|
||||||
|
networks:
|
||||||
|
- nginx_proxy_manager
|
||||||
|
environment:
|
||||||
|
POSTGRES_PASSWORD: "npmpass"
|
||||||
|
POSTGRES_USER: "npm"
|
||||||
|
POSTGRES_DB: "npm"
|
||||||
|
volumes:
|
||||||
|
- db_data1:/var/lib/postgresql/data
|
||||||
|
pgadmin:
|
||||||
|
image: dpage/pgadmin4
|
||||||
|
environment:
|
||||||
|
PGADMIN_DEFAULT_EMAIL: "admin@example.com"
|
||||||
|
PGADMIN_DEFAULT_PASSWORD: "changeme"
|
||||||
|
ports:
|
||||||
|
- 5080:80
|
||||||
|
networks:
|
||||||
|
- nginx_proxy_manager
|
||||||
|
depends_on:
|
||||||
|
- db1
|
||||||
|
|
||||||
|
|
||||||
|
swagger1:
|
||||||
|
image: swaggerapi/swagger-ui:latest
|
||||||
|
container_name: npm_swagger1
|
||||||
|
ports:
|
||||||
|
- 5082:80
|
||||||
|
environment:
|
||||||
|
URL: "http://npm:81/api/schema"
|
||||||
|
PORT: '80'
|
||||||
|
depends_on:
|
||||||
|
- npm1
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
npm_data1:
|
||||||
|
name: npm_core_data
|
||||||
|
le_data1:
|
||||||
|
name: npm_le_data
|
||||||
|
db_data1:
|
||||||
|
name: npm_db_data1
|
||||||
|
|
||||||
|
networks:
|
||||||
|
nginx_proxy_manager:
|
||||||
|
name: npm_network
|
@ -137,5 +137,13 @@ Email: admin@example.com
|
|||||||
Password: changeme
|
Password: changeme
|
||||||
```
|
```
|
||||||
|
|
||||||
Immediately after logging in with this default user you will be asked to modify your details and change your password.
|
Immediately after logging in with this default user you will be asked to modify your details and change your password. You can change defaults with:
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
environment:
|
||||||
|
INITIAL_ADMIN_EMAIL: my@example.com
|
||||||
|
INITIAL_ADMIN_PASSWORD: mypassword1
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
@ -303,6 +303,14 @@
|
|||||||
"credentials": "dns_joker_username = <Dynamic DNS Authentication Username>\ndns_joker_password = <Dynamic DNS Authentication Password>\ndns_joker_domain = <Dynamic DNS Domain>",
|
"credentials": "dns_joker_username = <Dynamic DNS Authentication Username>\ndns_joker_password = <Dynamic DNS Authentication Password>\ndns_joker_domain = <Dynamic DNS Domain>",
|
||||||
"full_plugin_name": "dns-joker"
|
"full_plugin_name": "dns-joker"
|
||||||
},
|
},
|
||||||
|
"leaseweb": {
|
||||||
|
"name": "LeaseWeb",
|
||||||
|
"package_name": "certbot-dns-leaseweb",
|
||||||
|
"version": "~=1.0.1",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_leaseweb_api_token = 01234556789",
|
||||||
|
"full_plugin_name": "dns-leaseweb"
|
||||||
|
},
|
||||||
"linode": {
|
"linode": {
|
||||||
"name": "Linode",
|
"name": "Linode",
|
||||||
"package_name": "certbot-dns-linode",
|
"package_name": "certbot-dns-linode",
|
||||||
|
@ -9,7 +9,7 @@ describe('Full Certificate Provisions', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it.only('Should be able to create new http certificate', function() {
|
it('Should be able to create new http certificate', function() {
|
||||||
cy.task('backendApiPost', {
|
cy.task('backendApiPost', {
|
||||||
token: token,
|
token: token,
|
||||||
path: '/api/nginx/certificates',
|
path: '/api/nginx/certificates',
|
||||||
@ -35,7 +35,7 @@ describe('Full Certificate Provisions', () => {
|
|||||||
it('Should be able to create new DNS certificate with Powerdns', function() {
|
it('Should be able to create new DNS certificate with Powerdns', function() {
|
||||||
cy.task('backendApiPost', {
|
cy.task('backendApiPost', {
|
||||||
token: token,
|
token: token,
|
||||||
path: '/api/certificates',
|
path: '/api/nginx/certificates',
|
||||||
data: {
|
data: {
|
||||||
domain_names: [
|
domain_names: [
|
||||||
'website2.example.com'
|
'website2.example.com'
|
||||||
@ -45,7 +45,8 @@ describe('Full Certificate Provisions', () => {
|
|||||||
dns_challenge: true,
|
dns_challenge: true,
|
||||||
dns_provider: 'powerdns',
|
dns_provider: 'powerdns',
|
||||||
dns_provider_credentials: 'dns_powerdns_api_url = http://ns1.pdns:8081\r\ndns_powerdns_api_key = npm',
|
dns_provider_credentials: 'dns_powerdns_api_url = http://ns1.pdns:8081\r\ndns_powerdns_api_key = npm',
|
||||||
letsencrypt_agree: true
|
letsencrypt_agree: true,
|
||||||
|
propagation_seconds: 5,
|
||||||
},
|
},
|
||||||
provider: 'letsencrypt'
|
provider: 'letsencrypt'
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/// <reference types="cypress" />
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
describe('Hosts endpoints', () => {
|
describe('Proxy Hosts endpoints', () => {
|
||||||
let token;
|
let token;
|
||||||
|
|
||||||
before(() => {
|
before(() => {
|
124
test/cypress/e2e/api/Settings.cy.js
Normal file
124
test/cypress/e2e/api/Settings.cy.js
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
|
describe('Settings endpoints', () => {
|
||||||
|
let token;
|
||||||
|
|
||||||
|
before(() => {
|
||||||
|
cy.getToken().then((tok) => {
|
||||||
|
token = tok;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Get all settings', function() {
|
||||||
|
cy.task('backendApiGet', {
|
||||||
|
token: token,
|
||||||
|
path: '/api/settings',
|
||||||
|
}).then((data) => {
|
||||||
|
cy.validateSwaggerSchema('get', 200, '/settings', data);
|
||||||
|
expect(data.length).to.be.greaterThan(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Get default-site setting', function() {
|
||||||
|
cy.task('backendApiGet', {
|
||||||
|
token: token,
|
||||||
|
path: '/api/settings/default-site',
|
||||||
|
}).then((data) => {
|
||||||
|
cy.validateSwaggerSchema('get', 200, '/settings/{settingID}', data);
|
||||||
|
expect(data).to.have.property('id');
|
||||||
|
expect(data.id).to.be.equal('default-site');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Default Site congratulations', function() {
|
||||||
|
cy.task('backendApiPut', {
|
||||||
|
token: token,
|
||||||
|
path: '/api/settings/default-site',
|
||||||
|
data: {
|
||||||
|
value: 'congratulations',
|
||||||
|
},
|
||||||
|
}).then((data) => {
|
||||||
|
cy.validateSwaggerSchema('put', 200, '/settings/{settingID}', data);
|
||||||
|
expect(data).to.have.property('id');
|
||||||
|
expect(data.id).to.be.equal('default-site');
|
||||||
|
expect(data).to.have.property('value');
|
||||||
|
expect(data.value).to.be.equal('congratulations');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Default Site 404', function() {
|
||||||
|
cy.task('backendApiPut', {
|
||||||
|
token: token,
|
||||||
|
path: '/api/settings/default-site',
|
||||||
|
data: {
|
||||||
|
value: '404',
|
||||||
|
},
|
||||||
|
}).then((data) => {
|
||||||
|
cy.validateSwaggerSchema('put', 200, '/settings/{settingID}', data);
|
||||||
|
expect(data).to.have.property('id');
|
||||||
|
expect(data.id).to.be.equal('default-site');
|
||||||
|
expect(data).to.have.property('value');
|
||||||
|
expect(data.value).to.be.equal('404');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Default Site 444', function() {
|
||||||
|
cy.task('backendApiPut', {
|
||||||
|
token: token,
|
||||||
|
path: '/api/settings/default-site',
|
||||||
|
data: {
|
||||||
|
value: '444',
|
||||||
|
},
|
||||||
|
}).then((data) => {
|
||||||
|
cy.validateSwaggerSchema('put', 200, '/settings/{settingID}', data);
|
||||||
|
expect(data).to.have.property('id');
|
||||||
|
expect(data.id).to.be.equal('default-site');
|
||||||
|
expect(data).to.have.property('value');
|
||||||
|
expect(data.value).to.be.equal('444');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Default Site redirect', function() {
|
||||||
|
cy.task('backendApiPut', {
|
||||||
|
token: token,
|
||||||
|
path: '/api/settings/default-site',
|
||||||
|
data: {
|
||||||
|
value: 'redirect',
|
||||||
|
meta: {
|
||||||
|
redirect: 'https://www.google.com',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}).then((data) => {
|
||||||
|
cy.validateSwaggerSchema('put', 200, '/settings/{settingID}', data);
|
||||||
|
expect(data).to.have.property('id');
|
||||||
|
expect(data.id).to.be.equal('default-site');
|
||||||
|
expect(data).to.have.property('value');
|
||||||
|
expect(data.value).to.be.equal('redirect');
|
||||||
|
expect(data).to.have.property('meta');
|
||||||
|
expect(data.meta).to.have.property('redirect');
|
||||||
|
expect(data.meta.redirect).to.be.equal('https://www.google.com');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Default Site html', function() {
|
||||||
|
cy.task('backendApiPut', {
|
||||||
|
token: token,
|
||||||
|
path: '/api/settings/default-site',
|
||||||
|
data: {
|
||||||
|
value: 'html',
|
||||||
|
meta: {
|
||||||
|
html: '<p>hello world</p>'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}).then((data) => {
|
||||||
|
cy.validateSwaggerSchema('put', 200, '/settings/{settingID}', data);
|
||||||
|
expect(data).to.have.property('id');
|
||||||
|
expect(data.id).to.be.equal('default-site');
|
||||||
|
expect(data).to.have.property('value');
|
||||||
|
expect(data.value).to.be.equal('html');
|
||||||
|
expect(data).to.have.property('meta');
|
||||||
|
expect(data.meta).to.have.property('html');
|
||||||
|
expect(data.meta.html).to.be.equal('<p>hello world</p>');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -7,7 +7,7 @@ const BackendApi = function(config, token) {
|
|||||||
|
|
||||||
this.axios = axios.create({
|
this.axios = axios.create({
|
||||||
baseURL: config.baseUrl,
|
baseURL: config.baseUrl,
|
||||||
timeout: 60000,
|
timeout: 90000,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user