mirror of
https://github.com/NginxProxyManager/nginx-proxy-manager.git
synced 2025-08-28 03:30:05 +00:00
Version 3 starter
This commit is contained in:
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"targets": {
|
||||
"browsers": [
|
||||
"Chrome >= 65"
|
||||
]
|
||||
},
|
||||
"debug": false,
|
||||
"modules": false,
|
||||
"useBuiltIns": "usage"
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
2
frontend/.env.development
Normal file
2
frontend/.env.development
Normal file
@@ -0,0 +1,2 @@
|
||||
PORT=9000
|
||||
IMAGE_INLINE_SIZE_LIMIT=20000
|
119
frontend/.eslintrc
Normal file
119
frontend/.eslintrc
Normal file
@@ -0,0 +1,119 @@
|
||||
{
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": [
|
||||
"@typescript-eslint",
|
||||
"prettier",
|
||||
"import",
|
||||
"react-hooks"
|
||||
],
|
||||
"extends": [
|
||||
"react-app",
|
||||
"eslint-config-prettier",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:prettier/recommended"
|
||||
],
|
||||
"env": {
|
||||
"jest": true,
|
||||
"browser": true,
|
||||
"commonjs": true
|
||||
},
|
||||
"rules": {
|
||||
"prettier/prettier": [
|
||||
"error"
|
||||
],
|
||||
"@typescript-eslint/ban-ts-comment": [
|
||||
"error",
|
||||
{
|
||||
"ts-ignore": "allow-with-description"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/consistent-type-definitions": [
|
||||
"error",
|
||||
"interface"
|
||||
],
|
||||
"@typescript-eslint/explicit-function-return-type": [
|
||||
"off"
|
||||
],
|
||||
"@typescript-eslint/explicit-module-boundary-types": [
|
||||
"off"
|
||||
],
|
||||
"@typescript-eslint/explicit-member-accessibility": [
|
||||
"off"
|
||||
],
|
||||
"@typescript-eslint/no-empty-function": [
|
||||
"off"
|
||||
],
|
||||
"@typescript-eslint/no-explicit-any": [
|
||||
"off"
|
||||
],
|
||||
"@typescript-eslint/no-non-null-assertion": [
|
||||
"off"
|
||||
],
|
||||
"@typescript-eslint/naming-convention": [
|
||||
"error",
|
||||
{
|
||||
"selector": "default",
|
||||
"format": [
|
||||
"camelCase",
|
||||
"PascalCase",
|
||||
"UPPER_CASE"
|
||||
],
|
||||
"leadingUnderscore": "allow",
|
||||
"trailingUnderscore": "allow"
|
||||
}
|
||||
],
|
||||
"react-hooks/rules-of-hooks": [
|
||||
"error"
|
||||
],
|
||||
"react-hooks/exhaustive-deps": [
|
||||
"warn",
|
||||
{
|
||||
"additionalHooks": "useAction|useReduxAction"
|
||||
}
|
||||
],
|
||||
"no-restricted-globals": [
|
||||
"off"
|
||||
],
|
||||
"import/extensions": 0, // We let webpack handle resolving file extensions
|
||||
"import/order": [
|
||||
"error",
|
||||
{
|
||||
"alphabetize": {
|
||||
"order": "asc",
|
||||
"caseInsensitive": true
|
||||
},
|
||||
"newlines-between": "always",
|
||||
"pathGroups": [
|
||||
{
|
||||
"pattern": "@(react)",
|
||||
"group": "external",
|
||||
"position": "before"
|
||||
},
|
||||
{
|
||||
"pattern": "@/@(fixtures|jest)/**",
|
||||
"group": "internal",
|
||||
"position": "before"
|
||||
},
|
||||
{
|
||||
"pattern": "@/**",
|
||||
"group": "internal"
|
||||
}
|
||||
],
|
||||
"pathGroupsExcludedImportTypes": [
|
||||
"builtin",
|
||||
"internal"
|
||||
],
|
||||
"groups": [
|
||||
"builtin",
|
||||
"external",
|
||||
"internal",
|
||||
[
|
||||
"parent",
|
||||
"sibling",
|
||||
"index"
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
8
frontend/.gitignore
vendored
8
frontend/.gitignore
vendored
@@ -1,4 +1,4 @@
|
||||
dist
|
||||
node_modules
|
||||
webpack_stats.html
|
||||
yarn-error.log
|
||||
.eslintcache
|
||||
coverage
|
||||
junit.xml
|
||||
eslint.xml
|
||||
|
11
frontend/.prettierrc
Normal file
11
frontend/.prettierrc
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"printWidth": 80,
|
||||
"tabWidth": 2,
|
||||
"useTabs": true,
|
||||
"semi": true,
|
||||
"singleQuote": false,
|
||||
"bracketSpacing": true,
|
||||
"jsxBracketSameLine": true,
|
||||
"trailingComma": "all",
|
||||
"proseWrap": "always"
|
||||
}
|
44
frontend/README.md
Normal file
44
frontend/README.md
Normal file
@@ -0,0 +1,44 @@
|
||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||
|
||||
## Available Scripts
|
||||
|
||||
In the project directory, you can run:
|
||||
|
||||
### `yarn start`
|
||||
|
||||
Runs the app in the development mode.<br />
|
||||
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
||||
|
||||
The page will reload if you make edits.<br />
|
||||
You will also see any lint errors in the console.
|
||||
|
||||
### `yarn test`
|
||||
|
||||
Launches the test runner in the interactive watch mode.<br />
|
||||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
||||
|
||||
### `yarn build`
|
||||
|
||||
Builds the app for production to the `build` folder.<br />
|
||||
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||
|
||||
The build is minified and the filenames include the hashes.<br />
|
||||
Your app is ready to be deployed!
|
||||
|
||||
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
||||
|
||||
### `yarn eject`
|
||||
|
||||
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
|
||||
|
||||
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
||||
|
||||
Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
|
||||
|
||||
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
|
||||
|
||||
## Learn More
|
||||
|
||||
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
||||
|
||||
To learn React, check out the [React documentation](https://reactjs.org/).
|
@@ -1 +0,0 @@
|
||||
../node_modules/tabler-ui/dist/assets/fonts/feather
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
4
frontend/globalSetup.js
Normal file
4
frontend/globalSetup.js
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = async () => {
|
||||
process.env.TZ = "Australia/Queensland";
|
||||
process.env.IMAGE_INLINE_SIZE_LIMIT = "20000";
|
||||
};
|
@@ -1,9 +0,0 @@
|
||||
<% var title = 'Nginx Proxy Manager' %>
|
||||
<%- include partials/header.ejs %>
|
||||
|
||||
<div id="app" class="page">
|
||||
<span class="loader"></span>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="/js/main.bundle.js?v=<%= version %>"></script>
|
||||
<%- include partials/footer.ejs %>
|
@@ -1,9 +0,0 @@
|
||||
<% var title = 'Login – Nginx Proxy Manager' %>
|
||||
<%- include partials/header.ejs %>
|
||||
|
||||
<div class="page" id="login" data-version="<%= version %>">
|
||||
<span class="loader"></span>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="/js/login.bundle.js?v=<%= version %>"></script>
|
||||
<%- include partials/footer.ejs %>
|
@@ -1,2 +0,0 @@
|
||||
</body>
|
||||
</html>
|
@@ -1,33 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<meta http-equiv="Content-Language" content="en">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="HandheldFriendly" content="True">
|
||||
<meta name="MobileOptimized" content="320">
|
||||
<title><%- title %></title>
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/images/favicons/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/images/favicons/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/images/favicons/favicon-16x16.png">
|
||||
<link rel="manifest" href="/images/favicons/site.webmanifest">
|
||||
<link rel="mask-icon" href="/images/favicons/safari-pinned-tab.svg" color="#5bbad5">
|
||||
<link rel="shortcut icon" href="/images/favicons/favicon.ico">
|
||||
<meta name="msapplication-TileColor" content="#333333">
|
||||
<meta name="msapplication-config" content="/images/favicons/browserconfig.xml">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
<link href="/css/main.css?v=<%= version %>" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<noscript>
|
||||
<div class="container no-js-warning">
|
||||
<div class="alert alert-warning text-center">
|
||||
<strong>Warning!</strong> This application requires Javascript and your browser doesn't support it.
|
||||
</div>
|
||||
</div>
|
||||
</noscript>
|
@@ -1 +0,0 @@
|
||||
./node_modules/tabler-ui/dist/assets/images
|
6
frontend/jest.eslint.js
Normal file
6
frontend/jest.eslint.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
displayName: "ESLint",
|
||||
runner: "jest-runner-eslint",
|
||||
testMatch: ["<rootDir>/src/**/*.(js|jsx|ts|tsx)"],
|
||||
watchPlugins: ["jest-runner-eslint/watch-fix"],
|
||||
};
|
@@ -1,694 +0,0 @@
|
||||
const $ = require('jquery');
|
||||
const _ = require('underscore');
|
||||
const Tokens = require('./tokens');
|
||||
|
||||
/**
|
||||
* @param {String} message
|
||||
* @param {*} debug
|
||||
* @param {Number} code
|
||||
* @constructor
|
||||
*/
|
||||
const ApiError = function (message, debug, code) {
|
||||
let temp = Error.call(this, message);
|
||||
temp.name = this.name = 'ApiError';
|
||||
this.stack = temp.stack;
|
||||
this.message = temp.message;
|
||||
this.debug = debug;
|
||||
this.code = code;
|
||||
};
|
||||
|
||||
ApiError.prototype = Object.create(Error.prototype, {
|
||||
constructor: {
|
||||
value: ApiError,
|
||||
writable: true,
|
||||
configurable: true
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {String} verb
|
||||
* @param {String} path
|
||||
* @param {Object} [data]
|
||||
* @param {Object} [options]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function fetch(verb, path, data, options) {
|
||||
options = options || {};
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
let api_url = '/api/';
|
||||
let url = api_url + path;
|
||||
let token = Tokens.getTopToken();
|
||||
|
||||
if ((typeof options.contentType === 'undefined' || options.contentType.match(/json/im)) && typeof data === 'object') {
|
||||
data = JSON.stringify(data);
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: url,
|
||||
data: typeof data === 'object' ? JSON.stringify(data) : data,
|
||||
type: verb,
|
||||
dataType: 'json',
|
||||
contentType: options.contentType || 'application/json; charset=UTF-8',
|
||||
processData: options.processData || true,
|
||||
crossDomain: true,
|
||||
timeout: options.timeout ? options.timeout : 180000,
|
||||
xhrFields: {
|
||||
withCredentials: true
|
||||
},
|
||||
|
||||
beforeSend: function (xhr) {
|
||||
xhr.setRequestHeader('Authorization', 'Bearer ' + (token ? token.t : null));
|
||||
},
|
||||
|
||||
success: function (data, textStatus, response) {
|
||||
let total = response.getResponseHeader('X-Dataset-Total');
|
||||
if (total !== null) {
|
||||
resolve({
|
||||
data: data,
|
||||
pagination: {
|
||||
total: parseInt(total, 10),
|
||||
offset: parseInt(response.getResponseHeader('X-Dataset-Offset'), 10),
|
||||
limit: parseInt(response.getResponseHeader('X-Dataset-Limit'), 10)
|
||||
}
|
||||
});
|
||||
} else {
|
||||
resolve(response);
|
||||
}
|
||||
},
|
||||
|
||||
error: function (xhr, status, error_thrown) {
|
||||
let code = 400;
|
||||
|
||||
if (typeof xhr.responseJSON !== 'undefined' && typeof xhr.responseJSON.error !== 'undefined' && typeof xhr.responseJSON.error.message !== 'undefined') {
|
||||
error_thrown = xhr.responseJSON.error.message;
|
||||
code = xhr.responseJSON.error.code || 500;
|
||||
}
|
||||
|
||||
reject(new ApiError(error_thrown, xhr.responseText, code));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Array} expand
|
||||
* @returns {String}
|
||||
*/
|
||||
function makeExpansionString(expand) {
|
||||
let items = [];
|
||||
_.forEach(expand, function (exp) {
|
||||
items.push(encodeURIComponent(exp));
|
||||
});
|
||||
|
||||
return items.join(',');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String} path
|
||||
* @param {Array} [expand]
|
||||
* @param {String} [query]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function getAllObjects(path, expand, query) {
|
||||
let params = [];
|
||||
|
||||
if (typeof expand === 'object' && expand !== null && expand.length) {
|
||||
params.push('expand=' + makeExpansionString(expand));
|
||||
}
|
||||
|
||||
if (typeof query === 'string') {
|
||||
params.push('query=' + query);
|
||||
}
|
||||
|
||||
return fetch('get', path + (params.length ? '?' + params.join('&') : ''));
|
||||
}
|
||||
|
||||
function FileUpload(path, fd) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let xhr = new XMLHttpRequest();
|
||||
let token = Tokens.getTopToken();
|
||||
|
||||
xhr.open('POST', '/api/' + path);
|
||||
xhr.overrideMimeType('text/plain');
|
||||
xhr.setRequestHeader('Authorization', 'Bearer ' + (token ? token.t : null));
|
||||
xhr.send(fd);
|
||||
|
||||
xhr.onreadystatechange = function () {
|
||||
if (this.readyState === XMLHttpRequest.DONE) {
|
||||
if (xhr.status !== 200 && xhr.status !== 201) {
|
||||
try {
|
||||
reject(new Error('Upload failed: ' + JSON.parse(xhr.responseText).error.message));
|
||||
} catch (err) {
|
||||
reject(new Error('Upload failed: ' + xhr.status));
|
||||
}
|
||||
} else {
|
||||
resolve(xhr.responseText);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
status: function () {
|
||||
return fetch('get', '');
|
||||
},
|
||||
|
||||
Tokens: {
|
||||
|
||||
/**
|
||||
* @param {String} identity
|
||||
* @param {String} secret
|
||||
* @param {Boolean} [wipe] Will wipe the stack before adding to it again if login was successful
|
||||
* @returns {Promise}
|
||||
*/
|
||||
login: function (identity, secret, wipe) {
|
||||
return fetch('post', 'tokens', {identity: identity, secret: secret})
|
||||
.then(response => {
|
||||
if (response.token) {
|
||||
if (wipe) {
|
||||
Tokens.clearTokens();
|
||||
}
|
||||
|
||||
// Set storage token
|
||||
Tokens.addToken(response.token);
|
||||
return response.token;
|
||||
} else {
|
||||
Tokens.clearTokens();
|
||||
throw(new Error('No token returned'));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @returns {Promise}
|
||||
*/
|
||||
refresh: function () {
|
||||
return fetch('get', 'tokens')
|
||||
.then(response => {
|
||||
if (response.token) {
|
||||
Tokens.setCurrentToken(response.token);
|
||||
return response.token;
|
||||
} else {
|
||||
Tokens.clearTokens();
|
||||
throw(new Error('No token returned'));
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
Users: {
|
||||
|
||||
/**
|
||||
* @param {Number|String} user_id
|
||||
* @param {Array} [expand]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getById: function (user_id, expand) {
|
||||
return fetch('get', 'users/' + user_id + (typeof expand === 'object' && expand.length ? '?expand=' + makeExpansionString(expand) : ''));
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Array} [expand]
|
||||
* @param {String} [query]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getAll: function (expand, query) {
|
||||
return getAllObjects('users', expand, query);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Object} data
|
||||
* @returns {Promise}
|
||||
*/
|
||||
create: function (data) {
|
||||
return fetch('post', 'users', data);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Object} data
|
||||
* @param {Number} data.id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
update: function (data) {
|
||||
let id = data.id;
|
||||
delete data.id;
|
||||
return fetch('put', 'users/' + id, data);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
delete: function (id) {
|
||||
return fetch('delete', 'users/' + id);
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Number} id
|
||||
* @param {Object} auth
|
||||
* @returns {Promise}
|
||||
*/
|
||||
setPassword: function (id, auth) {
|
||||
return fetch('put', 'users/' + id + '/auth', auth);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
loginAs: function (id) {
|
||||
return fetch('post', 'users/' + id + '/login');
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Number} id
|
||||
* @param {Object} perms
|
||||
* @returns {Promise}
|
||||
*/
|
||||
setPermissions: function (id, perms) {
|
||||
return fetch('put', 'users/' + id + '/permissions', perms);
|
||||
}
|
||||
},
|
||||
|
||||
Nginx: {
|
||||
|
||||
ProxyHosts: {
|
||||
/**
|
||||
* @param {Array} [expand]
|
||||
* @param {String} [query]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getAll: function (expand, query) {
|
||||
return getAllObjects('nginx/proxy-hosts', expand, query);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Object} data
|
||||
*/
|
||||
create: function (data) {
|
||||
return fetch('post', 'nginx/proxy-hosts', data);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Object} data
|
||||
* @param {Number} data.id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
update: function (data) {
|
||||
let id = data.id;
|
||||
delete data.id;
|
||||
return fetch('put', 'nginx/proxy-hosts/' + id, data);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
delete: function (id) {
|
||||
return fetch('delete', 'nginx/proxy-hosts/' + id);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
get: function (id) {
|
||||
return fetch('get', 'nginx/proxy-hosts/' + id);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
enable: function (id) {
|
||||
return fetch('post', 'nginx/proxy-hosts/' + id + '/enable');
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
disable: function (id) {
|
||||
return fetch('post', 'nginx/proxy-hosts/' + id + '/disable');
|
||||
}
|
||||
},
|
||||
|
||||
RedirectionHosts: {
|
||||
/**
|
||||
* @param {Array} [expand]
|
||||
* @param {String} [query]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getAll: function (expand, query) {
|
||||
return getAllObjects('nginx/redirection-hosts', expand, query);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Object} data
|
||||
*/
|
||||
create: function (data) {
|
||||
return fetch('post', 'nginx/redirection-hosts', data);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Object} data
|
||||
* @param {Number} data.id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
update: function (data) {
|
||||
let id = data.id;
|
||||
delete data.id;
|
||||
return fetch('put', 'nginx/redirection-hosts/' + id, data);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
delete: function (id) {
|
||||
return fetch('delete', 'nginx/redirection-hosts/' + id);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
get: function (id) {
|
||||
return fetch('get', 'nginx/redirection-hosts/' + id);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @param {FormData} form_data
|
||||
* @params {Promise}
|
||||
*/
|
||||
setCerts: function (id, form_data) {
|
||||
return FileUpload('nginx/redirection-hosts/' + id + '/certificates', form_data);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
enable: function (id) {
|
||||
return fetch('post', 'nginx/redirection-hosts/' + id + '/enable');
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
disable: function (id) {
|
||||
return fetch('post', 'nginx/redirection-hosts/' + id + '/disable');
|
||||
}
|
||||
},
|
||||
|
||||
Streams: {
|
||||
/**
|
||||
* @param {Array} [expand]
|
||||
* @param {String} [query]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getAll: function (expand, query) {
|
||||
return getAllObjects('nginx/streams', expand, query);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Object} data
|
||||
*/
|
||||
create: function (data) {
|
||||
return fetch('post', 'nginx/streams', data);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Object} data
|
||||
* @param {Number} data.id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
update: function (data) {
|
||||
let id = data.id;
|
||||
delete data.id;
|
||||
return fetch('put', 'nginx/streams/' + id, data);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
delete: function (id) {
|
||||
return fetch('delete', 'nginx/streams/' + id);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
get: function (id) {
|
||||
return fetch('get', 'nginx/streams/' + id);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
enable: function (id) {
|
||||
return fetch('post', 'nginx/streams/' + id + '/enable');
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
disable: function (id) {
|
||||
return fetch('post', 'nginx/streams/' + id + '/disable');
|
||||
}
|
||||
},
|
||||
|
||||
DeadHosts: {
|
||||
/**
|
||||
* @param {Array} [expand]
|
||||
* @param {String} [query]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getAll: function (expand, query) {
|
||||
return getAllObjects('nginx/dead-hosts', expand, query);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Object} data
|
||||
*/
|
||||
create: function (data) {
|
||||
return fetch('post', 'nginx/dead-hosts', data);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Object} data
|
||||
* @param {Number} data.id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
update: function (data) {
|
||||
let id = data.id;
|
||||
delete data.id;
|
||||
return fetch('put', 'nginx/dead-hosts/' + id, data);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
delete: function (id) {
|
||||
return fetch('delete', 'nginx/dead-hosts/' + id);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
get: function (id) {
|
||||
return fetch('get', 'nginx/dead-hosts/' + id);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @param {FormData} form_data
|
||||
* @params {Promise}
|
||||
*/
|
||||
setCerts: function (id, form_data) {
|
||||
return FileUpload('nginx/dead-hosts/' + id + '/certificates', form_data);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
enable: function (id) {
|
||||
return fetch('post', 'nginx/dead-hosts/' + id + '/enable');
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
disable: function (id) {
|
||||
return fetch('post', 'nginx/dead-hosts/' + id + '/disable');
|
||||
}
|
||||
},
|
||||
|
||||
AccessLists: {
|
||||
/**
|
||||
* @param {Array} [expand]
|
||||
* @param {String} [query]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getAll: function (expand, query) {
|
||||
return getAllObjects('nginx/access-lists', expand, query);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Object} data
|
||||
*/
|
||||
create: function (data) {
|
||||
return fetch('post', 'nginx/access-lists', data);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Object} data
|
||||
* @param {Number} data.id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
update: function (data) {
|
||||
let id = data.id;
|
||||
delete data.id;
|
||||
return fetch('put', 'nginx/access-lists/' + id, data);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
delete: function (id) {
|
||||
return fetch('delete', 'nginx/access-lists/' + id);
|
||||
}
|
||||
},
|
||||
|
||||
Certificates: {
|
||||
/**
|
||||
* @param {Array} [expand]
|
||||
* @param {String} [query]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getAll: function (expand, query) {
|
||||
return getAllObjects('nginx/certificates', expand, query);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Object} data
|
||||
*/
|
||||
create: function (data) {
|
||||
|
||||
const timeout = 180000 + (data && data.meta && data.meta.propagation_seconds ? Number(data.meta.propagation_seconds) * 1000 : 0);
|
||||
return fetch('post', 'nginx/certificates', data, {timeout});
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Object} data
|
||||
* @param {Number} data.id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
update: function (data) {
|
||||
let id = data.id;
|
||||
delete data.id;
|
||||
return fetch('put', 'nginx/certificates/' + id, data);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
delete: function (id) {
|
||||
return fetch('delete', 'nginx/certificates/' + id);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @param {FormData} form_data
|
||||
* @params {Promise}
|
||||
*/
|
||||
upload: function (id, form_data) {
|
||||
return FileUpload('nginx/certificates/' + id + '/upload', form_data);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {FormData} form_data
|
||||
* @params {Promise}
|
||||
*/
|
||||
validate: function (form_data) {
|
||||
return FileUpload('nginx/certificates/validate', form_data);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
renew: function (id, timeout = 180000) {
|
||||
return fetch('post', 'nginx/certificates/' + id + '/renew', undefined, {timeout});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
AuditLog: {
|
||||
/**
|
||||
* @param {Array} [expand]
|
||||
* @param {String} [query]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getAll: function (expand, query) {
|
||||
return getAllObjects('audit-log', expand, query);
|
||||
}
|
||||
},
|
||||
|
||||
Reports: {
|
||||
|
||||
/**
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getHostStats: function () {
|
||||
return fetch('get', 'reports/hosts');
|
||||
}
|
||||
},
|
||||
|
||||
Settings: {
|
||||
|
||||
/**
|
||||
* @param {String} setting_id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getById: function (setting_id) {
|
||||
return fetch('get', 'settings/' + setting_id);
|
||||
},
|
||||
|
||||
/**
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getAll: function () {
|
||||
return getAllObjects('settings');
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Object} data
|
||||
* @param {Number} data.id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
update: function (data) {
|
||||
let id = data.id;
|
||||
delete data.id;
|
||||
return fetch('put', 'settings/' + id, data);
|
||||
}
|
||||
}
|
||||
};
|
@@ -1,80 +0,0 @@
|
||||
<td class="text-center">
|
||||
<div class="avatar d-block" style="background-image: url(<%- user.avatar || '/images/default-avatar.jpg' %>)">
|
||||
<span class="avatar-status <%- user.is_disabled ? 'bg-red' : 'bg-green' %>"></span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>
|
||||
<% if (user.is_deleted) {
|
||||
%>
|
||||
<span class="mdi-format-strikethrough" title="Deleted"><%- user.name %></span>
|
||||
<%
|
||||
} else {
|
||||
%>
|
||||
<%- user.name %>
|
||||
<%
|
||||
}
|
||||
%>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>
|
||||
<%
|
||||
var items = [];
|
||||
switch (object_type) {
|
||||
case 'proxy-host':
|
||||
%> <span class="text-success"><i class="fe fe-zap"></i></span> <%
|
||||
items = meta.domain_names;
|
||||
break;
|
||||
case 'redirection-host':
|
||||
%> <span class="text-yellow"><i class="fe fe-shuffle"></i></span> <%
|
||||
items = meta.domain_names;
|
||||
break;
|
||||
case 'stream':
|
||||
%> <span class="text-blue"><i class="fe fe-radio"></i></span> <%
|
||||
items.push(meta.incoming_port);
|
||||
break;
|
||||
case 'dead-host':
|
||||
%> <span class="text-danger"><i class="fe fe-zap-off"></i></span> <%
|
||||
items = meta.domain_names;
|
||||
break;
|
||||
case 'access-list':
|
||||
%> <span class="text-teal"><i class="fe fe-lock"></i></span> <%
|
||||
items.push(meta.name);
|
||||
break;
|
||||
case 'user':
|
||||
%> <span class="text-teal"><i class="fe fe-user"></i></span> <%
|
||||
items.push(meta.name);
|
||||
break;
|
||||
case 'certificate':
|
||||
%> <span class="text-pink"><i class="fe fe-shield"></i></span> <%
|
||||
if (meta.provider === 'letsencrypt') {
|
||||
items = meta.domain_names;
|
||||
} else {
|
||||
items.push(meta.nice_name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
%> <%- i18n('audit-log', action, {name: i18n('audit-log', object_type)}) %>
|
||||
—
|
||||
<%
|
||||
if (items && items.length) {
|
||||
items.map(function(item) {
|
||||
%>
|
||||
<span class="tag"><%- item %></span>
|
||||
<%
|
||||
});
|
||||
} else {
|
||||
%>
|
||||
#<%- object_id %>
|
||||
<%
|
||||
}
|
||||
%>
|
||||
</div>
|
||||
<div class="small text-muted">
|
||||
<%- formatDbDate(created_on, 'Do MMMM YYYY, h:mm a') %>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<a href="#" class="meta btn btn-secondary btn-sm"><%- i18n('audit-log', 'view-meta') %></a>
|
||||
</td>
|
@@ -1,32 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const Controller = require('../../controller');
|
||||
const template = require('./item.ejs');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
tagName: 'tr',
|
||||
|
||||
ui: {
|
||||
meta: 'a.meta'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click @ui.meta': function (e) {
|
||||
e.preventDefault();
|
||||
Controller.showAuditMeta(this.model);
|
||||
}
|
||||
},
|
||||
|
||||
templateContext: {
|
||||
more: function() {
|
||||
switch (this.object_type) {
|
||||
case 'redirection-host':
|
||||
case 'stream':
|
||||
case 'proxy-host':
|
||||
return this.meta.domain_names.join(', ');
|
||||
}
|
||||
|
||||
return '#' + (this.object_id || '?');
|
||||
}
|
||||
}
|
||||
});
|
@@ -1,9 +0,0 @@
|
||||
<thead>
|
||||
<th width="30"> </th>
|
||||
<th>User</th>
|
||||
<th>Event</th>
|
||||
<th> </th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- items -->
|
||||
</tbody>
|
@@ -1,27 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const ItemView = require('./item');
|
||||
const template = require('./main.ejs');
|
||||
|
||||
const TableBody = Mn.CollectionView.extend({
|
||||
tagName: 'tbody',
|
||||
childView: ItemView
|
||||
});
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
tagName: 'table',
|
||||
className: 'table table-hover table-outline table-vcenter card-table',
|
||||
template: template,
|
||||
|
||||
regions: {
|
||||
body: {
|
||||
el: 'tbody',
|
||||
replaceElement: true
|
||||
}
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
this.showChildView('body', new TableBody({
|
||||
collection: this.collection
|
||||
}));
|
||||
}
|
||||
});
|
@@ -1,15 +0,0 @@
|
||||
<div class="card">
|
||||
<div class="card-status bg-teal"></div>
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><%- i18n('audit-log', 'title') %></h3>
|
||||
</div>
|
||||
<div class="card-body no-padding min-100">
|
||||
<div class="dimmer active">
|
||||
<div class="loader"></div>
|
||||
<div class="dimmer-content list-region">
|
||||
<!-- List Region -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
@@ -1,53 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const App = require('../main');
|
||||
const AuditLogModel = require('../../models/audit-log');
|
||||
const ListView = require('./list/main');
|
||||
const template = require('./main.ejs');
|
||||
const ErrorView = require('../error/main');
|
||||
const EmptyView = require('../empty/main');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
id: 'audit-log',
|
||||
template: template,
|
||||
|
||||
ui: {
|
||||
list_region: '.list-region',
|
||||
dimmer: '.dimmer'
|
||||
},
|
||||
|
||||
regions: {
|
||||
list_region: '@ui.list_region'
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
let view = this;
|
||||
|
||||
App.Api.AuditLog.getAll(['user'])
|
||||
.then(response => {
|
||||
if (!view.isDestroyed() && response && response.length) {
|
||||
view.showChildView('list_region', new ListView({
|
||||
collection: new AuditLogModel.Collection(response)
|
||||
}));
|
||||
} else {
|
||||
view.showChildView('list_region', new EmptyView({
|
||||
title: App.i18n('audit-log', 'empty'),
|
||||
subtitle: App.i18n('audit-log', 'empty-subtitle')
|
||||
}));
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
view.showChildView('list_region', new ErrorView({
|
||||
code: err.code,
|
||||
message: err.message,
|
||||
retry: function () {
|
||||
App.Controller.showAuditLog();
|
||||
}
|
||||
}));
|
||||
|
||||
console.error(err);
|
||||
})
|
||||
.then(() => {
|
||||
view.ui.dimmer.removeClass('active');
|
||||
});
|
||||
}
|
||||
});
|
@@ -1,27 +0,0 @@
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><%- i18n('audit-log', 'meta-title') %></h5>
|
||||
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="mb-2">
|
||||
<div class="tag tag-dark">
|
||||
<%- i18n('audit-log', action, {name: i18n('audit-log', object_type)}) %>
|
||||
<span class="tag-addon tag-orange">#<%- object_id %></span>
|
||||
</div>
|
||||
<div class="tag tag-dark">
|
||||
<%- i18n('audit-log', 'user') %>
|
||||
<span class="tag-addon tag-teal"><%- user.name %></span>
|
||||
</div>
|
||||
<div class="tag tag-dark">
|
||||
<%- i18n('audit-log', 'date') %>
|
||||
<span class="tag-addon tag-primary"><%- formatDbDate(created_on, 'Do MMMM YYYY, h:mm a') %></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<pre><%- JSON.stringify(meta, null, 2) %></pre>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary cancel" data-dismiss="modal"><%- i18n('str', 'close') %></button>
|
||||
</div>
|
||||
</div>
|
@@ -1,7 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const template = require('./meta.ejs');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
className: 'modal-dialog wide'
|
||||
});
|
@@ -1,10 +0,0 @@
|
||||
const UserModel = require('../models/user');
|
||||
|
||||
let cache = {
|
||||
User: new UserModel.Model(),
|
||||
locale: 'en',
|
||||
version: null
|
||||
};
|
||||
|
||||
module.exports = cache;
|
||||
|
@@ -1,434 +0,0 @@
|
||||
const Backbone = require('backbone');
|
||||
const Cache = require('./cache');
|
||||
const Tokens = require('./tokens');
|
||||
|
||||
module.exports = {
|
||||
|
||||
/**
|
||||
* @param {String} route
|
||||
* @param {Object} [options]
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
navigate: function (route, options) {
|
||||
options = options || {};
|
||||
Backbone.history.navigate(route.toString(), options);
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Login
|
||||
*/
|
||||
showLogin: function () {
|
||||
window.location = '/login';
|
||||
},
|
||||
|
||||
/**
|
||||
* Users
|
||||
*/
|
||||
showUsers: function () {
|
||||
let controller = this;
|
||||
if (Cache.User.isAdmin()) {
|
||||
require(['./main', './users/main'], (App, View) => {
|
||||
controller.navigate('/users');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
} else {
|
||||
this.showDashboard();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* User Form
|
||||
*
|
||||
* @param [model]
|
||||
*/
|
||||
showUserForm: function (model) {
|
||||
if (Cache.User.isAdmin()) {
|
||||
require(['./main', './user/form'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* User Permissions Form
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showUserPermissions: function (model) {
|
||||
if (Cache.User.isAdmin()) {
|
||||
require(['./main', './user/permissions'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* User Password Form
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showUserPasswordForm: function (model) {
|
||||
if (Cache.User.isAdmin() || model.get('id') === Cache.User.get('id')) {
|
||||
require(['./main', './user/password'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* User Delete Confirm
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showUserDeleteConfirm: function (model) {
|
||||
if (Cache.User.isAdmin() && model.get('id') !== Cache.User.get('id')) {
|
||||
require(['./main', './user/delete'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Dashboard
|
||||
*/
|
||||
showDashboard: function () {
|
||||
let controller = this;
|
||||
|
||||
require(['./main', './dashboard/main'], (App, View) => {
|
||||
controller.navigate('/');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Nginx Proxy Hosts
|
||||
*/
|
||||
showNginxProxy: function () {
|
||||
if (Cache.User.isAdmin() || Cache.User.canView('proxy_hosts')) {
|
||||
let controller = this;
|
||||
|
||||
require(['./main', './nginx/proxy/main'], (App, View) => {
|
||||
controller.navigate('/nginx/proxy');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Nginx Proxy Host Form
|
||||
*
|
||||
* @param [model]
|
||||
*/
|
||||
showNginxProxyForm: function (model) {
|
||||
if (Cache.User.isAdmin() || Cache.User.canManage('proxy_hosts')) {
|
||||
require(['./main', './nginx/proxy/form'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
showNginxRedirection: function () {
|
||||
if (Cache.User.isAdmin() || Cache.User.canView('redirection_hosts')) {
|
||||
let controller = this;
|
||||
|
||||
require(['./main', './nginx/redirection/main'], (App, View) => {
|
||||
controller.navigate('/nginx/redirection');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Nginx Redirection Host Form
|
||||
*
|
||||
* @param [model]
|
||||
*/
|
||||
showNginxRedirectionForm: function (model) {
|
||||
if (Cache.User.isAdmin() || Cache.User.canManage('redirection_hosts')) {
|
||||
require(['./main', './nginx/redirection/form'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Proxy Redirection Delete Confirm
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showNginxRedirectionDeleteConfirm: function (model) {
|
||||
if (Cache.User.isAdmin() || Cache.User.canManage('redirection_hosts')) {
|
||||
require(['./main', './nginx/redirection/delete'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Nginx Stream Hosts
|
||||
*/
|
||||
showNginxStream: function () {
|
||||
if (Cache.User.isAdmin() || Cache.User.canView('streams')) {
|
||||
let controller = this;
|
||||
|
||||
require(['./main', './nginx/stream/main'], (App, View) => {
|
||||
controller.navigate('/nginx/stream');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Stream Form
|
||||
*
|
||||
* @param [model]
|
||||
*/
|
||||
showNginxStreamForm: function (model) {
|
||||
if (Cache.User.isAdmin() || Cache.User.canManage('streams')) {
|
||||
require(['./main', './nginx/stream/form'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Stream Delete Confirm
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showNginxStreamDeleteConfirm: function (model) {
|
||||
if (Cache.User.isAdmin() || Cache.User.canManage('streams')) {
|
||||
require(['./main', './nginx/stream/delete'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Nginx Dead Hosts
|
||||
*/
|
||||
showNginxDead: function () {
|
||||
if (Cache.User.isAdmin() || Cache.User.canView('dead_hosts')) {
|
||||
let controller = this;
|
||||
|
||||
require(['./main', './nginx/dead/main'], (App, View) => {
|
||||
controller.navigate('/nginx/404');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Dead Host Form
|
||||
*
|
||||
* @param [model]
|
||||
*/
|
||||
showNginxDeadForm: function (model) {
|
||||
if (Cache.User.isAdmin() || Cache.User.canManage('dead_hosts')) {
|
||||
require(['./main', './nginx/dead/form'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Dead Host Delete Confirm
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showNginxDeadDeleteConfirm: function (model) {
|
||||
if (Cache.User.isAdmin() || Cache.User.canManage('dead_hosts')) {
|
||||
require(['./main', './nginx/dead/delete'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Help Dialog
|
||||
*
|
||||
* @param {String} title
|
||||
* @param {String} content
|
||||
*/
|
||||
showHelp: function (title, content) {
|
||||
require(['./main', './help/main'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({title: title, content: content}));
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Nginx Access
|
||||
*/
|
||||
showNginxAccess: function () {
|
||||
if (Cache.User.isAdmin() || Cache.User.canView('access_lists')) {
|
||||
let controller = this;
|
||||
|
||||
require(['./main', './nginx/access/main'], (App, View) => {
|
||||
controller.navigate('/nginx/access');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Nginx Access List Form
|
||||
*
|
||||
* @param [model]
|
||||
*/
|
||||
showNginxAccessListForm: function (model) {
|
||||
if (Cache.User.isAdmin() || Cache.User.canManage('access_lists')) {
|
||||
require(['./main', './nginx/access/form'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Access List Delete Confirm
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showNginxAccessListDeleteConfirm: function (model) {
|
||||
if (Cache.User.isAdmin() || Cache.User.canManage('access_lists')) {
|
||||
require(['./main', './nginx/access/delete'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Nginx Certificates
|
||||
*/
|
||||
showNginxCertificates: function () {
|
||||
if (Cache.User.isAdmin() || Cache.User.canView('certificates')) {
|
||||
let controller = this;
|
||||
|
||||
require(['./main', './nginx/certificates/main'], (App, View) => {
|
||||
controller.navigate('/nginx/certificates');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Nginx Certificate Form
|
||||
*
|
||||
* @param [model]
|
||||
*/
|
||||
showNginxCertificateForm: function (model) {
|
||||
if (Cache.User.isAdmin() || Cache.User.canManage('certificates')) {
|
||||
require(['./main', './nginx/certificates/form'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Certificate Renew
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showNginxCertificateRenew: function (model) {
|
||||
if (Cache.User.isAdmin() || Cache.User.canManage('certificates')) {
|
||||
require(['./main', './nginx/certificates/renew'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Certificate Delete Confirm
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showNginxCertificateDeleteConfirm: function (model) {
|
||||
if (Cache.User.isAdmin() || Cache.User.canManage('certificates')) {
|
||||
require(['./main', './nginx/certificates/delete'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Audit Log
|
||||
*/
|
||||
showAuditLog: function () {
|
||||
let controller = this;
|
||||
if (Cache.User.isAdmin()) {
|
||||
require(['./main', './audit-log/main'], (App, View) => {
|
||||
controller.navigate('/audit-log');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
} else {
|
||||
this.showDashboard();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Audit Log Metadata
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showAuditMeta: function (model) {
|
||||
if (Cache.User.isAdmin()) {
|
||||
require(['./main', './audit-log/meta'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Settings
|
||||
*/
|
||||
showSettings: function () {
|
||||
let controller = this;
|
||||
if (Cache.User.isAdmin()) {
|
||||
require(['./main', './settings/main'], (App, View) => {
|
||||
controller.navigate('/settings');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
} else {
|
||||
this.showDashboard();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Settings Item Form
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showSettingForm: function (model) {
|
||||
if (Cache.User.isAdmin()) {
|
||||
if (model.get('id') === 'default-site') {
|
||||
require(['./main', './settings/default-site/main'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Logout
|
||||
*/
|
||||
logout: function () {
|
||||
Tokens.dropTopToken();
|
||||
this.showLogin();
|
||||
}
|
||||
};
|
@@ -1,67 +0,0 @@
|
||||
<div class="page-header">
|
||||
<h1 class="page-title"><%- i18n('dashboard', 'title', {name: getUserName()}) %></h1>
|
||||
</div>
|
||||
|
||||
<% if (columns) { %>
|
||||
<div class="row">
|
||||
<% if (canShow('proxy_hosts')) { %>
|
||||
<div class="col-sm-<%- 24 / columns %> col-lg-<%- 12 / columns %>">
|
||||
<div class="card p-3">
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="stamp stamp-md bg-green mr-3">
|
||||
<i class="fe fe-zap"></i>
|
||||
</span>
|
||||
<div>
|
||||
<h4 class="m-0"><a href="/nginx/proxy"><%- getHostStat('proxy') %> <small><%- i18n('proxy-hosts', 'title') %></small></a></h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<% if (canShow('redirection_hosts')) { %>
|
||||
<div class="col-sm-<%- 24 / columns %> col-lg-<%- 12 / columns %>">
|
||||
<div class="card p-3">
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="stamp stamp-md bg-yellow mr-3">
|
||||
<i class="fe fe-shuffle"></i>
|
||||
</span>
|
||||
<div>
|
||||
<h4 class="m-0"><a href="/nginx/redirection"><%- getHostStat('redirection') %> <small><%- i18n('redirection-hosts', 'title') %></small></a></h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<% if (canShow('streams')) { %>
|
||||
<div class="col-sm-<%- 24 / columns %> col-lg-<%- 12 / columns %>">
|
||||
<div class="card p-3">
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="stamp stamp-md bg-blue mr-3">
|
||||
<i class="fe fe-radio"></i>
|
||||
</span>
|
||||
<div>
|
||||
<h4 class="m-0"><a href="/nginx/stream"><%- getHostStat('stream') %> <small> <%- i18n('streams', 'title') %></small></a></h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<% if (canShow('dead_hosts')) { %>
|
||||
<div class="col-sm-<%- 24 / columns %> col-lg-<%- 12 / columns %>">
|
||||
<div class="card p-3">
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="stamp stamp-md bg-red mr-3">
|
||||
<i class="fe fe-zap-off"></i>
|
||||
</span>
|
||||
<div>
|
||||
<h4 class="m-0"><a href="/nginx/404"><%- getHostStat('dead') %> <small><%- i18n('dead-hosts', 'title') %></small></a></h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
<% } %>
|
@@ -1,92 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const Cache = require('../cache');
|
||||
const Controller = require('../controller');
|
||||
const Api = require('../api');
|
||||
const Helpers = require('../../lib/helpers');
|
||||
const template = require('./main.ejs');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
id: 'dashboard',
|
||||
columns: 0,
|
||||
|
||||
stats: {},
|
||||
|
||||
ui: {
|
||||
links: 'a'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click @ui.links': function (e) {
|
||||
e.preventDefault();
|
||||
Controller.navigate($(e.currentTarget).attr('href'), true);
|
||||
}
|
||||
},
|
||||
|
||||
templateContext: function () {
|
||||
let view = this;
|
||||
|
||||
return {
|
||||
getUserName: function () {
|
||||
return Cache.User.get('nickname') || Cache.User.get('name');
|
||||
},
|
||||
|
||||
getHostStat: function (type) {
|
||||
if (view.stats && typeof view.stats.hosts !== 'undefined' && typeof view.stats.hosts[type] !== 'undefined') {
|
||||
return Helpers.niceNumber(view.stats.hosts[type]);
|
||||
}
|
||||
|
||||
return '-';
|
||||
},
|
||||
|
||||
canShow: function (perm) {
|
||||
return Cache.User.isAdmin() || Cache.User.canView(perm);
|
||||
},
|
||||
|
||||
columns: view.columns
|
||||
};
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
let view = this;
|
||||
|
||||
if (typeof view.stats.hosts === 'undefined') {
|
||||
Api.Reports.getHostStats()
|
||||
.then(response => {
|
||||
if (!view.isDestroyed()) {
|
||||
view.stats.hosts = response;
|
||||
view.render();
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Object} [model]
|
||||
*/
|
||||
preRender: function (model) {
|
||||
this.columns = 0;
|
||||
|
||||
// calculate the available columns based on permissions for the objects
|
||||
// and store as a variable
|
||||
//let view = this;
|
||||
let perms = ['proxy_hosts', 'redirection_hosts', 'streams', 'dead_hosts'];
|
||||
|
||||
perms.map(perm => {
|
||||
this.columns += Cache.User.isAdmin() || Cache.User.canView(perm) ? 1 : 0;
|
||||
});
|
||||
|
||||
// Prevent double rendering on initial calls
|
||||
if (typeof model !== 'undefined') {
|
||||
this.render();
|
||||
}
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
this.preRender();
|
||||
this.listenTo(Cache.User, 'change', this.preRender);
|
||||
}
|
||||
});
|
@@ -1,11 +0,0 @@
|
||||
<% if (title) { %>
|
||||
<h1 class="h2 mb-3"><%- title %></h1>
|
||||
<% }
|
||||
|
||||
if (subtitle) { %>
|
||||
<p class="h4 text-muted font-weight-normal mb-7"><%- subtitle %></p>
|
||||
<% }
|
||||
|
||||
if (link) { %>
|
||||
<a class="btn btn-<%- btn_color %>" href="#"><%- link %></a>
|
||||
<% } %>
|
@@ -1,33 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const template = require('./main.ejs');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
className: 'text-center m-7',
|
||||
template: template,
|
||||
|
||||
options: {
|
||||
btn_color: 'teal'
|
||||
},
|
||||
|
||||
ui: {
|
||||
action: 'a'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click @ui.action': function (e) {
|
||||
e.preventDefault();
|
||||
this.getOption('action')();
|
||||
}
|
||||
},
|
||||
|
||||
templateContext: function () {
|
||||
return {
|
||||
title: this.getOption('title'),
|
||||
subtitle: this.getOption('subtitle'),
|
||||
link: this.getOption('link'),
|
||||
action: typeof this.getOption('action') === 'function',
|
||||
btn_color: this.getOption('btn_color')
|
||||
};
|
||||
}
|
||||
|
||||
});
|
@@ -1,7 +0,0 @@
|
||||
<i class="fe fe-alert-triangle mr-2" aria-hidden="true"></i>
|
||||
<%= code ? '<strong>' + code + '</strong> — ' : '' %>
|
||||
<%- message %>
|
||||
|
||||
<% if (retry) { %>
|
||||
<br><br><a href="#" class="btn btn-sm btn-warning retry"><%- i18n('str', 'try-again') %></a>
|
||||
<% } %>
|
@@ -1,27 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const template = require('./main.ejs');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
className: 'alert alert-icon alert-warning m-5',
|
||||
|
||||
ui: {
|
||||
retry: 'a.retry'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click @ui.retry': function (e) {
|
||||
e.preventDefault();
|
||||
this.getOption('retry')();
|
||||
}
|
||||
},
|
||||
|
||||
templateContext: function () {
|
||||
return {
|
||||
message: this.getOption('message'),
|
||||
code: this.getOption('code'),
|
||||
retry: typeof this.getOption('retry') === 'function'
|
||||
};
|
||||
}
|
||||
|
||||
});
|
@@ -1,12 +0,0 @@
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><%- title %></h5>
|
||||
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<%= content %>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary cancel" data-dismiss="modal"><%- i18n('str', 'close') %></button>
|
||||
</div>
|
||||
</div>
|
@@ -1,16 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const template = require('./main.ejs');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
className: 'modal-dialog wide',
|
||||
|
||||
templateContext: function () {
|
||||
let content = this.getOption('content').split("\n");
|
||||
|
||||
return {
|
||||
title: this.getOption('title'),
|
||||
content: '<p>' + content.join('</p><p>') + '</p>'
|
||||
};
|
||||
}
|
||||
});
|
@@ -1,23 +0,0 @@
|
||||
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 + ')';
|
||||
};
|
@@ -1,155 +0,0 @@
|
||||
const _ = require('underscore');
|
||||
const Backbone = require('backbone');
|
||||
const Mn = require('../lib/marionette');
|
||||
const Cache = require('./cache');
|
||||
const Controller = require('./controller');
|
||||
const Router = require('./router');
|
||||
const Api = require('./api');
|
||||
const Tokens = require('./tokens');
|
||||
const UI = require('./ui/main');
|
||||
const i18n = require('./i18n');
|
||||
|
||||
const App = Mn.Application.extend({
|
||||
|
||||
Cache: Cache,
|
||||
Api: Api,
|
||||
UI: null,
|
||||
i18n: i18n,
|
||||
Controller: Controller,
|
||||
|
||||
region: {
|
||||
el: '#app',
|
||||
replaceElement: true
|
||||
},
|
||||
|
||||
onStart: function (app, options) {
|
||||
console.log(i18n('main', 'welcome'));
|
||||
|
||||
// Check if token is coming through
|
||||
if (this.getParam('token')) {
|
||||
Tokens.addToken(this.getParam('token'));
|
||||
}
|
||||
|
||||
// Check if we are still logged in by refreshing the token
|
||||
Api.status()
|
||||
.then(result => {
|
||||
Cache.version = [result.version.major, result.version.minor, result.version.revision].join('.');
|
||||
})
|
||||
.then(Api.Tokens.refresh)
|
||||
.then(this.bootstrap)
|
||||
.then(() => {
|
||||
console.info(i18n('main', 'logged-in', Cache.User.attributes));
|
||||
this.bootstrapTimer();
|
||||
this.refreshTokenTimer();
|
||||
|
||||
this.UI = new UI();
|
||||
this.UI.on('render', () => {
|
||||
new Router(options);
|
||||
Backbone.history.start({pushState: true});
|
||||
|
||||
// Ask the admin use to change their details
|
||||
if (Cache.User.get('email') === 'admin@example.com') {
|
||||
Controller.showUserForm(Cache.User);
|
||||
}
|
||||
});
|
||||
|
||||
this.getRegion().show(this.UI);
|
||||
})
|
||||
.catch(err => {
|
||||
console.warn('Not logged in:', err.message);
|
||||
Controller.showLogin();
|
||||
});
|
||||
},
|
||||
|
||||
History: {
|
||||
replace: function (data) {
|
||||
window.history.replaceState(_.extend(window.history.state || {}, data), document.title);
|
||||
},
|
||||
|
||||
get: function (attr) {
|
||||
return window.history.state ? window.history.state[attr] : undefined;
|
||||
}
|
||||
},
|
||||
|
||||
getParam: function (name) {
|
||||
name = name.replace(/[\[\]]/g, '\\$&');
|
||||
let url = window.location.href;
|
||||
let regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)');
|
||||
let results = regex.exec(url);
|
||||
|
||||
if (!results) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!results[2]) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return decodeURIComponent(results[2].replace(/\+/g, ' '));
|
||||
},
|
||||
|
||||
/**
|
||||
* Get user and other base info to start prime the cache and the application
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
bootstrap: function () {
|
||||
return Api.Users.getById('me', ['permissions'])
|
||||
.then(response => {
|
||||
Cache.User.set(response);
|
||||
Tokens.setCurrentName(response.nickname || response.name);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Bootstraps the user from time to time
|
||||
*/
|
||||
bootstrapTimer: function () {
|
||||
setTimeout(() => {
|
||||
Api.status()
|
||||
.then(result => {
|
||||
let version = [result.version.major, result.version.minor, result.version.revision].join('.');
|
||||
if (version !== Cache.version) {
|
||||
document.location.reload();
|
||||
}
|
||||
})
|
||||
.then(this.bootstrap)
|
||||
.then(() => {
|
||||
this.bootstrapTimer();
|
||||
})
|
||||
.catch(err => {
|
||||
if (err.message !== 'timeout' && err.code && err.code !== 400) {
|
||||
console.log(err);
|
||||
console.error(err.message);
|
||||
console.info('Not logged in?');
|
||||
Controller.showLogin();
|
||||
} else {
|
||||
this.bootstrapTimer();
|
||||
}
|
||||
});
|
||||
}, 30 * 1000); // 30 seconds
|
||||
},
|
||||
|
||||
refreshTokenTimer: function () {
|
||||
setTimeout(() => {
|
||||
return Api.Tokens.refresh()
|
||||
.then(this.bootstrap)
|
||||
.then(() => {
|
||||
this.refreshTokenTimer();
|
||||
})
|
||||
.catch(err => {
|
||||
if (err.message !== 'timeout' && err.code && err.code !== 400) {
|
||||
console.log(err);
|
||||
console.error(err.message);
|
||||
console.info('Not logged in?');
|
||||
Controller.showLogin();
|
||||
} else {
|
||||
this.refreshTokenTimer();
|
||||
}
|
||||
});
|
||||
}, 10 * 60 * 1000);
|
||||
}
|
||||
});
|
||||
|
||||
const app = new App();
|
||||
module.exports = app;
|
@@ -1,23 +0,0 @@
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><%- i18n('access-lists', '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('access-lists', 'delete-confirm') %>
|
||||
<% if (proxy_host_count) { %>
|
||||
<br><br>
|
||||
<%- i18n('access-lists', 'delete-has-hosts', {count: proxy_host_count}) %>
|
||||
<% } %>
|
||||
</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>
|
@@ -1,32 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const App = require('../../main');
|
||||
const template = require('./delete.ejs');
|
||||
|
||||
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.AccessLists.delete(this.model.get('id'))
|
||||
.then(() => {
|
||||
App.Controller.showNginxAccess();
|
||||
App.UI.closeModal();
|
||||
})
|
||||
.catch(err => {
|
||||
alert(err.message);
|
||||
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
@@ -1,108 +0,0 @@
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><%- i18n('access-lists', 'form-title', {id: id}) %></h5>
|
||||
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
|
||||
</div>
|
||||
<div class="modal-body has-tabs">
|
||||
<form>
|
||||
<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 show" aria-selected="true"><i class="fe fe-zap"></i> <%- i18n('access-lists', 'details') %></a></li>
|
||||
<li role="presentation" class="nav-item"><a href="#auth" aria-controls="tab4" role="tab" data-toggle="tab" class="nav-link" aria-selected="false"><i class="fe fe-users"></i> <%- i18n('access-lists', 'authorization') %></a></li>
|
||||
<li role="presentation" class="nav-item"><a href="#access" aria-controls="tab2" role="tab" data-toggle="tab" class="nav-link" aria-selected="false"><i class="fe fe-radio"></i> <%- i18n('access-lists', 'access') %></a></li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<!-- Details -->
|
||||
<div role="tabpanel" class="tab-pane active show" id="details">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('str', 'name') %> <span class="form-required">*</span></label>
|
||||
<input type="text" name="name" class="form-control" value="<%- name %>" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="satisfy_any" value="1"<%- typeof satisfy_any !== 'undefined' && satisfy_any ? ' checked' : '' %>>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%- i18n('access-lists', 'satisfy-any') %></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="pass_auth" value="1"<%- typeof pass_auth !== 'undefined' && pass_auth ? ' checked' : '' %>>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%- i18n('access-lists', 'pass-auth') %></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Authorization -->
|
||||
<div class="tab-pane" id="auth">
|
||||
<p>
|
||||
Basic Authorization via
|
||||
<a target="_blank" href="https://nginx.org/en/docs/http/ngx_http_auth_basic_module.html">
|
||||
Nginx HTTP Basic Authentication
|
||||
</a>
|
||||
</p>
|
||||
<div class="row">
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('str', 'username') %></label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('str', 'password') %></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="items"><!-- items --></div>
|
||||
<div class="btn-list justify-content-end">
|
||||
<button type="button" class="btn btn-teal auth_add"><%- i18n('access-lists', 'auth-add') %></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Access -->
|
||||
<div class="tab-pane" id="access">
|
||||
<p>
|
||||
IP Address Whitelist/Blacklist via
|
||||
<a target="_blank" href="https://nginx.org/en/docs/http/ngx_http_access_module.html">
|
||||
Nginx HTTP Access
|
||||
</a>
|
||||
</p>
|
||||
<div class="clients"><!-- clients --></div>
|
||||
<div class="row">
|
||||
<div class="col-sm-3 col-md-3">
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control disabled" value="deny" disabled>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-9 col-md-9">
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control disabled" value="all" disabled>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-muted">Note that the <code>allow</code> and <code>deny</code> directives will be applied in the order they are defined.</div>
|
||||
<div class="btn-list justify-content-end">
|
||||
<button type="button" class="btn btn-teal access_add"><%- i18n('access-lists', 'access-add') %></button>
|
||||
</div>
|
||||
</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-teal save"><%- i18n('str', 'save') %></button>
|
||||
</div>
|
||||
</div>
|
@@ -1,153 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const App = require('../../main');
|
||||
const AccessListModel = require('../../../models/access-list');
|
||||
const template = require('./form.ejs');
|
||||
const ItemView = require('./form/item');
|
||||
const ClientView = require('./form/client');
|
||||
|
||||
require('jquery-serializejson');
|
||||
|
||||
const ItemsView = Mn.CollectionView.extend({
|
||||
childView: ItemView
|
||||
});
|
||||
|
||||
const ClientsView = Mn.CollectionView.extend({
|
||||
childView: ClientView
|
||||
});
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
className: 'modal-dialog',
|
||||
|
||||
ui: {
|
||||
items_region: '.items',
|
||||
clients_region: '.clients',
|
||||
form: 'form',
|
||||
buttons: '.modal-footer button',
|
||||
cancel: 'button.cancel',
|
||||
save: 'button.save',
|
||||
access_add: 'button.access_add',
|
||||
auth_add: 'button.auth_add'
|
||||
},
|
||||
|
||||
regions: {
|
||||
items_region: '@ui.items_region',
|
||||
clients_region: '@ui.clients_region'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click @ui.save': function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
if (!this.ui.form[0].checkValidity()) {
|
||||
$('<input type="submit">').hide().appendTo(this.ui.form).click().remove();
|
||||
return;
|
||||
}
|
||||
|
||||
let view = this;
|
||||
let form_data = this.ui.form.serializeJSON();
|
||||
let items_data = [];
|
||||
let clients_data = [];
|
||||
|
||||
form_data.username.map(function (val, idx) {
|
||||
if (val.trim().length) {
|
||||
items_data.push({
|
||||
username: val.trim(),
|
||||
password: form_data.password[idx]
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
form_data.address.map(function (val, idx) {
|
||||
if (val.trim().length) {
|
||||
clients_data.push({
|
||||
address: val.trim(),
|
||||
directive: form_data.directive[idx]
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
if (!items_data.length && !clients_data.length) {
|
||||
alert('You must specify at least 1 Authorization or Access rule');
|
||||
return;
|
||||
}
|
||||
|
||||
let data = {
|
||||
name: form_data.name,
|
||||
satisfy_any: !!form_data.satisfy_any,
|
||||
pass_auth: !!form_data.pass_auth,
|
||||
items: items_data,
|
||||
clients: clients_data
|
||||
};
|
||||
|
||||
console.log(data);
|
||||
|
||||
let method = App.Api.Nginx.AccessLists.create;
|
||||
let is_new = true;
|
||||
|
||||
if (this.model.get('id')) {
|
||||
// edit
|
||||
is_new = false;
|
||||
method = App.Api.Nginx.AccessLists.update;
|
||||
data.id = this.model.get('id');
|
||||
}
|
||||
|
||||
this.ui.buttons.prop('disabled', true).addClass('btn-disabled');
|
||||
method(data)
|
||||
.then(result => {
|
||||
view.model.set(result);
|
||||
|
||||
App.UI.closeModal(function () {
|
||||
if (is_new) {
|
||||
App.Controller.showNginxAccess();
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
alert(err.message);
|
||||
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
||||
});
|
||||
},
|
||||
'click @ui.access_add': function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
let clients = this.model.get('clients');
|
||||
clients.push({});
|
||||
this.showChildView('clients_region', new ClientsView({
|
||||
collection: new Backbone.Collection(clients)
|
||||
}));
|
||||
},
|
||||
'click @ui.auth_add': function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
let items = this.model.get('items');
|
||||
items.push({});
|
||||
this.showChildView('items_region', new ItemsView({
|
||||
collection: new Backbone.Collection(items)
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
let items = this.model.get('items');
|
||||
let clients = this.model.get('clients');
|
||||
|
||||
// Ensure at least one field is shown initally
|
||||
if (!items.length) items.push({});
|
||||
if (!clients.length) clients.push({});
|
||||
|
||||
this.showChildView('items_region', new ItemsView({
|
||||
collection: new Backbone.Collection(items)
|
||||
}));
|
||||
|
||||
this.showChildView('clients_region', new ClientsView({
|
||||
collection: new Backbone.Collection(clients)
|
||||
}));
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
if (typeof options.model === 'undefined' || !options.model) {
|
||||
this.model = new AccessListModel.Model();
|
||||
}
|
||||
}
|
||||
});
|
@@ -1,13 +0,0 @@
|
||||
<div class="col-sm-3 col-md-3">
|
||||
<div class="form-group">
|
||||
<select name="directive[]" class="form-control custom-select" placeholder="http">
|
||||
<option value="allow" <%- typeof directive == 'undefined' || directive === 'allow' ? 'selected' : '' %>>allow</option>
|
||||
<option value="deny" <%- typeof directive !== 'undefined' && directive === 'deny' ? 'selected' : '' %>>deny</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-9 col-md-9">
|
||||
<div class="form-group">
|
||||
<input type="text" name="address[]" placeholder="IP / Subnet" class="form-control" value="<%- typeof address !== 'undefined' ? address : '' %>" value="">
|
||||
</div>
|
||||
</div>
|
@@ -1,7 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const template = require('./client.ejs');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
className: 'row'
|
||||
});
|
@@ -1,10 +0,0 @@
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<input type="text" name="username[]" class="form-control" value="<%- typeof username !== 'undefined' ? username : '' %>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<input type="password" name="password[]" class="form-control" placeholder="<%- typeof hint !== 'undefined' ? hint : '' %>" value="">
|
||||
</div>
|
||||
</div>
|
@@ -1,7 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const template = require('./item.ejs');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
className: 'row'
|
||||
});
|
@@ -1,42 +0,0 @@
|
||||
<td class="text-center">
|
||||
<div class="avatar d-block" style="background-image: url(<%- owner.avatar || '/images/default-avatar.jpg' %>)" title="Owned by <%- owner.name %>">
|
||||
<span class="avatar-status <%- owner.is_disabled ? 'bg-red' : 'bg-green' %>"></span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>
|
||||
<%- name %>
|
||||
</div>
|
||||
<div class="small text-muted">
|
||||
<%- i18n('str', 'created-on', {date: formatDbDate(created_on, 'Do MMMM YYYY')}) %>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<%- i18n('access-lists', 'item-count', {count: items.length || 0}) %>
|
||||
</td>
|
||||
<td>
|
||||
<%- i18n('access-lists', 'client-count', {count: clients.length || 0}) %>
|
||||
</td>
|
||||
<td>
|
||||
<% if (satisfy_any) { %>
|
||||
<%- i18n('str', 'any') %>
|
||||
<%} else { %>
|
||||
<%- i18n('str', 'all') %>
|
||||
<% } %>
|
||||
</td>
|
||||
<td>
|
||||
<%- i18n('access-lists', 'proxy-host-count', {count: proxy_host_count}) %>
|
||||
</td>
|
||||
<% if (canManage) { %>
|
||||
<td class="text-right">
|
||||
<div class="item-action dropdown">
|
||||
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<span class="dropdown-header"><%- i18n('audit-log', 'access-list') %> #<%- id %></span>
|
||||
<a href="#" class="edit dropdown-item"><i class="dropdown-icon fe fe-edit"></i> <%- i18n('str', 'edit') %></a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a href="#" class="delete dropdown-item"><i class="dropdown-icon fe fe-trash-2"></i> <%- i18n('str', 'delete') %></a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<% } %>
|
@@ -1,33 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const App = require('../../../main');
|
||||
const template = require('./item.ejs');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
tagName: 'tr',
|
||||
|
||||
ui: {
|
||||
edit: 'a.edit',
|
||||
delete: 'a.delete'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click @ui.edit': function (e) {
|
||||
e.preventDefault();
|
||||
App.Controller.showNginxAccessListForm(this.model);
|
||||
},
|
||||
|
||||
'click @ui.delete': function (e) {
|
||||
e.preventDefault();
|
||||
App.Controller.showNginxAccessListDeleteConfirm(this.model);
|
||||
}
|
||||
},
|
||||
|
||||
templateContext: {
|
||||
canManage: App.Cache.User.canManage('access_lists')
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
this.listenTo(this.model, 'change', this.render);
|
||||
}
|
||||
});
|
@@ -1,14 +0,0 @@
|
||||
<thead>
|
||||
<th width="30"> </th>
|
||||
<th><%- i18n('str', 'name') %></th>
|
||||
<th><%- i18n('access-lists', 'authorization') %></th>
|
||||
<th><%- i18n('access-lists', 'access') %></th>
|
||||
<th><%- i18n('access-lists', 'satisfy') %></th>
|
||||
<th><%- i18n('proxy-hosts', 'title') %></th>
|
||||
<% if (canManage) { %>
|
||||
<th> </th>
|
||||
<% } %>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- items -->
|
||||
</tbody>
|
@@ -1,32 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const App = require('../../../main');
|
||||
const ItemView = require('./item');
|
||||
const template = require('./main.ejs');
|
||||
|
||||
const TableBody = Mn.CollectionView.extend({
|
||||
tagName: 'tbody',
|
||||
childView: ItemView
|
||||
});
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
tagName: 'table',
|
||||
className: 'table table-hover table-outline table-vcenter card-table',
|
||||
template: template,
|
||||
|
||||
regions: {
|
||||
body: {
|
||||
el: 'tbody',
|
||||
replaceElement: true
|
||||
}
|
||||
},
|
||||
|
||||
templateContext: {
|
||||
canManage: App.Cache.User.canManage('access_lists')
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
this.showChildView('body', new TableBody({
|
||||
collection: this.collection
|
||||
}));
|
||||
}
|
||||
});
|
@@ -1,20 +0,0 @@
|
||||
<div class="card">
|
||||
<div class="card-status bg-teal"></div>
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><%- i18n('access-lists', 'title') %></h3>
|
||||
<div class="card-options">
|
||||
<a href="#" class="btn btn-outline-secondary btn-sm ml-2 help"><i class="fe fe-help-circle"></i></a>
|
||||
<% if (showAddButton) { %>
|
||||
<a href="#" class="btn btn-outline-teal btn-sm ml-2 add-item"><%- i18n('access-lists', 'add') %></a>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body no-padding min-100">
|
||||
<div class="dimmer active">
|
||||
<div class="loader"></div>
|
||||
<div class="dimmer-content list-region">
|
||||
<!-- List Region -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@@ -1,81 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const App = require('../../main');
|
||||
const AccessListModel = require('../../../models/access-list');
|
||||
const ListView = require('./list/main');
|
||||
const ErrorView = require('../../error/main');
|
||||
const EmptyView = require('../../empty/main');
|
||||
const template = require('./main.ejs');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
id: 'nginx-access',
|
||||
template: template,
|
||||
|
||||
ui: {
|
||||
list_region: '.list-region',
|
||||
add: '.add-item',
|
||||
help: '.help',
|
||||
dimmer: '.dimmer'
|
||||
},
|
||||
|
||||
regions: {
|
||||
list_region: '@ui.list_region'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click @ui.add': function (e) {
|
||||
e.preventDefault();
|
||||
App.Controller.showNginxAccessListForm();
|
||||
},
|
||||
|
||||
'click @ui.help': function (e) {
|
||||
e.preventDefault();
|
||||
App.Controller.showHelp(App.i18n('access-lists', 'help-title'), App.i18n('access-lists', 'help-content'));
|
||||
}
|
||||
},
|
||||
|
||||
templateContext: {
|
||||
showAddButton: App.Cache.User.canManage('access_lists')
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
let view = this;
|
||||
|
||||
App.Api.Nginx.AccessLists.getAll(['owner', 'items', 'clients'])
|
||||
.then(response => {
|
||||
if (!view.isDestroyed()) {
|
||||
if (response && response.length) {
|
||||
view.showChildView('list_region', new ListView({
|
||||
collection: new AccessListModel.Collection(response)
|
||||
}));
|
||||
} else {
|
||||
let manage = App.Cache.User.canManage('access_lists');
|
||||
|
||||
view.showChildView('list_region', new EmptyView({
|
||||
title: App.i18n('access-lists', 'empty'),
|
||||
subtitle: App.i18n('all-hosts', 'empty-subtitle', {manage: manage}),
|
||||
link: manage ? App.i18n('access-lists', 'add') : null,
|
||||
btn_color: 'teal',
|
||||
permission: 'access_lists',
|
||||
action: function () {
|
||||
App.Controller.showNginxAccessListForm();
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
view.showChildView('list_region', new ErrorView({
|
||||
code: err.code,
|
||||
message: err.message,
|
||||
retry: function () {
|
||||
App.Controller.showNginxAccess();
|
||||
}
|
||||
}));
|
||||
|
||||
console.error(err);
|
||||
})
|
||||
.then(() => {
|
||||
view.ui.dimmer.removeClass('active');
|
||||
});
|
||||
}
|
||||
});
|
@@ -1,18 +0,0 @@
|
||||
<div>
|
||||
<% if (id === 'new') { %>
|
||||
<div class="title">
|
||||
<i class="fe fe-shield text-success"></i> <%- i18n('all-hosts', 'new-cert') %>
|
||||
</div>
|
||||
<span class="description"><%- i18n('all-hosts', 'with-le') %></span>
|
||||
<% } else if (id > 0) { %>
|
||||
<div class="title">
|
||||
<i class="fe fe-shield text-pink"></i> <%- provider === 'other' ? nice_name : domain_names.join(', ') %>
|
||||
</div>
|
||||
<span class="description"><%- i18n('ssl', provider) %> – Expires: <%- formatDbDate(expires_on, 'Do MMMM YYYY, h:mm a') %></span>
|
||||
<% } else { %>
|
||||
<div class="title">
|
||||
<i class="fe fe-shield-off text-danger"></i> <%- i18n('all-hosts', 'none') %>
|
||||
</div>
|
||||
<span class="description"><%- i18n('all-hosts', 'no-ssl') %></span>
|
||||
<% } %>
|
||||
</div>
|
@@ -1,19 +0,0 @@
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><%- i18n('certificates', '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('certificates', 'delete-confirm') %>
|
||||
</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>
|
@@ -1,34 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const App = require('../../main');
|
||||
const template = require('./delete.ejs');
|
||||
|
||||
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();
|
||||
this.ui.save.addClass('btn-loading');
|
||||
this.ui.buttons.prop('disabled', true).addClass('btn-disabled');
|
||||
|
||||
App.Api.Nginx.Certificates.delete(this.model.get('id'))
|
||||
.then(() => {
|
||||
App.Controller.showNginxCertificates();
|
||||
App.UI.closeModal();
|
||||
})
|
||||
.catch(err => {
|
||||
alert(err.message);
|
||||
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
||||
this.ui.save.removeClass('btn-loading');
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
@@ -1,177 +0,0 @@
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><%- i18n('certificates', 'form-title', {provider: provider}) %></h5>
|
||||
<button type="button" class="close cancel non-loader-content" aria-label="Close" data-dismiss="modal"> </button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="alert alert-danger mb-0 rounded-0" id="le-error-info" role="alert"></div>
|
||||
<div class="text-center loader-content">
|
||||
<div class="loader mx-auto my-6"></div>
|
||||
<p><%- i18n('ssl', 'processing-info') %></p>
|
||||
</div>
|
||||
<form class="non-loader-content">
|
||||
<div class="row">
|
||||
<% if (provider === 'letsencrypt') { %>
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('all-hosts', 'domain-names') %> <span class="form-required">*</span></label>
|
||||
<input type="text" name="domain_names" class="form-control" id="input-domains" value="<%- domain_names.join(',') %>" required>
|
||||
<div class="text-blue"><i class="fe fe-alert-triangle"></i> <%- i18n('ssl', 'hosts-warning') %></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- DNS challenge -->
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="custom-switch-input"
|
||||
name="meta[dns_challenge]"
|
||||
value="1"
|
||||
<%- getUseDnsChallenge() ? 'checked' : '' %>
|
||||
>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%= i18n('ssl', 'dns-challenge') %></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<fieldset class="form-fieldset dns-challenge">
|
||||
<div class="text-red mb-4"><i class="fe fe-alert-triangle"></i> <%= i18n('ssl', 'certbot-warning') %></div>
|
||||
|
||||
<!-- Certbot DNS plugin selection -->
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('ssl', 'dns-provider') %> <span class="form-required">*</span></label>
|
||||
<select
|
||||
name="meta[dns_provider]"
|
||||
id="dns_provider"
|
||||
class="form-control custom-select"
|
||||
>
|
||||
<option
|
||||
value=""
|
||||
disabled
|
||||
hidden
|
||||
<%- getDnsProvider() === null ? 'selected' : '' %>
|
||||
>Please Choose...</option>
|
||||
<% _.each(dns_plugins, function(plugin_info, plugin_name){ %>
|
||||
<option
|
||||
value="<%- plugin_name %>"
|
||||
<%- getDnsProvider() === plugin_name ? 'selected' : '' %>
|
||||
><%- plugin_info.display_name %></option>
|
||||
<% }); %>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Certbot credentials file content -->
|
||||
<div class="row credentials-file-content">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('ssl', 'credentials-file-content') %> <span class="form-required">*</span></label>
|
||||
<textarea
|
||||
name="meta[dns_provider_credentials]"
|
||||
class="form-control text-monospace"
|
||||
id="dns_provider_credentials"
|
||||
><%- getDnsProviderCredentials() %></textarea>
|
||||
<div class="text-secondary small">
|
||||
<i class="fe fe-info"></i>
|
||||
<%= i18n('ssl', 'credentials-file-content-info') %>
|
||||
</div>
|
||||
<div class="text-red small">
|
||||
<i class="fe fe-alert-triangle"></i>
|
||||
<%= i18n('ssl', 'stored-as-plaintext-info') %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- DNS propagation delay -->
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group mb-0">
|
||||
<label class="form-label"><%- i18n('ssl', 'propagation-seconds') %></label>
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
name="meta[propagation_seconds]"
|
||||
class="form-control"
|
||||
id="propagation_seconds"
|
||||
value="<%- getPropagationSeconds() %>"
|
||||
>
|
||||
<div class="text-secondary small">
|
||||
<i class="fe fe-info"></i>
|
||||
<%= i18n('ssl', 'propagation-seconds-info') %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<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-description"><%= i18n('ssl', 'letsencrypt-agree', {url: 'https://letsencrypt.org/repository/'}) %> <span class="form-required">*</span></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<% } else if (provider === 'other') { %>
|
||||
<!-- Other -->
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="text-blue mb-4"><i class="fe fe-alert-triangle"></i> <%= i18n('ssl', 'passphrase-protection-support-info') %></div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('str', 'name') %> <span class="form-required">*</span></label>
|
||||
<input name="nice_name" type="text" class="form-control" placeholder="" value="<%- nice_name %>" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12 other-ssl">
|
||||
<div class="form-group">
|
||||
<div class="form-label"><%- i18n('certificates', 'other-certificate-key') %><span class="form-required">*</span></div>
|
||||
<div class="custom-file">
|
||||
<input type="file" class="custom-file-input" name="meta[other_certificate_key]" id="other_certificate_key" required>
|
||||
<label id="other_certificate_key_label" class="custom-file-label"><%- i18n('str', 'choose-file') %></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12 other-ssl">
|
||||
<div class="form-group">
|
||||
<div class="form-label"><%- i18n('certificates', 'other-certificate') %><span class="form-required">*</span></div>
|
||||
<div class="custom-file">
|
||||
<input type="file" class="custom-file-input" name="meta[other_certificate]" id="other_certificate">
|
||||
<label id="other_certificate_label" class="custom-file-label"><%- i18n('str', 'choose-file') %></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12 other-ssl">
|
||||
<div class="form-group">
|
||||
<div class="form-label"><%- i18n('certificates', 'other-intermediate-certificate') %></div>
|
||||
<div class="custom-file">
|
||||
<input type="file" class="custom-file-input" name="meta[other_intermediate_certificate]" id="other_intermediate_certificate">
|
||||
<label id="other_intermediate_certificate_label" class="custom-file-label"><%- i18n('str', 'choose-file') %></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% } %>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer non-loader-content">
|
||||
<button type="button" class="btn btn-secondary cancel" data-dismiss="modal"><%- i18n('str', 'cancel') %></button>
|
||||
<button type="button" class="btn btn-teal save"><%- i18n('str', 'save') %></button>
|
||||
</div>
|
||||
</div>
|
@@ -1,267 +0,0 @@
|
||||
const _ = require('underscore');
|
||||
const Mn = require('backbone.marionette');
|
||||
const App = require('../../main');
|
||||
const CertificateModel = require('../../../models/certificate');
|
||||
const template = require('./form.ejs');
|
||||
const i18n = require('../../i18n');
|
||||
const dns_providers = sortProvidersAlphabetically(require('../../../../../global/certbot-dns-plugins'));
|
||||
|
||||
require('jquery-serializejson');
|
||||
require('selectize');
|
||||
|
||||
function sortProvidersAlphabetically(obj) {
|
||||
return Object.entries(obj)
|
||||
.sort((a,b) => a[1].display_name.toLowerCase() > b[1].display_name.toLowerCase())
|
||||
.reduce((result, entry) => {
|
||||
result[entry[0]] = entry[1];
|
||||
return result;
|
||||
}, {});
|
||||
}
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
className: 'modal-dialog',
|
||||
max_file_size: 102400,
|
||||
|
||||
ui: {
|
||||
form: 'form',
|
||||
loader_content: '.loader-content',
|
||||
non_loader_content: '.non-loader-content',
|
||||
le_error_info: '#le-error-info',
|
||||
domain_names: 'input[name="domain_names"]',
|
||||
buttons: '.modal-footer button',
|
||||
cancel: 'button.cancel',
|
||||
save: 'button.save',
|
||||
other_certificate: '#other_certificate',
|
||||
other_certificate_label: '#other_certificate_label',
|
||||
other_certificate_key: '#other_certificate_key',
|
||||
dns_challenge_switch: 'input[name="meta[dns_challenge]"]',
|
||||
dns_challenge_content: '.dns-challenge',
|
||||
dns_provider: 'select[name="meta[dns_provider]"]',
|
||||
credentials_file_content: '.credentials-file-content',
|
||||
dns_provider_credentials: 'textarea[name="meta[dns_provider_credentials]"]',
|
||||
propagation_seconds: 'input[name="meta[propagation_seconds]"]',
|
||||
other_certificate_key_label: '#other_certificate_key_label',
|
||||
other_intermediate_certificate: '#other_intermediate_certificate',
|
||||
other_intermediate_certificate_label: '#other_intermediate_certificate_label'
|
||||
},
|
||||
|
||||
events: {
|
||||
'change @ui.dns_challenge_switch': function () {
|
||||
const checked = this.ui.dns_challenge_switch.prop('checked');
|
||||
if (checked) {
|
||||
this.ui.dns_provider.prop('required', 'required');
|
||||
const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value;
|
||||
if(selected_provider != '' && dns_providers[selected_provider].credentials !== false){
|
||||
this.ui.dns_provider_credentials.prop('required', 'required');
|
||||
}
|
||||
this.ui.dns_challenge_content.show();
|
||||
} else {
|
||||
this.ui.dns_provider.prop('required', false);
|
||||
this.ui.dns_provider_credentials.prop('required', false);
|
||||
this.ui.dns_challenge_content.hide();
|
||||
}
|
||||
},
|
||||
|
||||
'change @ui.dns_provider': function () {
|
||||
const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value;
|
||||
if (selected_provider != '' && dns_providers[selected_provider].credentials !== false) {
|
||||
this.ui.dns_provider_credentials.prop('required', 'required');
|
||||
this.ui.dns_provider_credentials[0].value = dns_providers[selected_provider].credentials;
|
||||
this.ui.credentials_file_content.show();
|
||||
} else {
|
||||
this.ui.dns_provider_credentials.prop('required', false);
|
||||
this.ui.credentials_file_content.hide();
|
||||
}
|
||||
},
|
||||
|
||||
'click @ui.save': function (e) {
|
||||
e.preventDefault();
|
||||
this.ui.le_error_info.hide();
|
||||
|
||||
if (!this.ui.form[0].checkValidity()) {
|
||||
$('<input type="submit">').hide().appendTo(this.ui.form).click().remove();
|
||||
$(this).removeClass('btn-loading');
|
||||
return;
|
||||
}
|
||||
|
||||
let data = this.ui.form.serializeJSON();
|
||||
data.provider = this.model.get('provider');
|
||||
let ssl_files = [];
|
||||
|
||||
if (data.provider === 'letsencrypt') {
|
||||
if (typeof data.meta === 'undefined') data.meta = {};
|
||||
|
||||
let domain_err = false;
|
||||
if (!data.meta.dns_challenge) {
|
||||
data.domain_names.split(',').map(function (name) {
|
||||
if (name.match(/\*/im)) {
|
||||
domain_err = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (domain_err) {
|
||||
alert(i18n('ssl', 'no-wildcard-without-dns'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Manipulate
|
||||
data.meta.letsencrypt_agree = data.meta.letsencrypt_agree == 1;
|
||||
data.meta.dns_challenge = data.meta.dns_challenge == 1;
|
||||
|
||||
if(!data.meta.dns_challenge){
|
||||
data.meta.dns_provider = undefined;
|
||||
data.meta.dns_provider_credentials = undefined;
|
||||
data.meta.propagation_seconds = undefined;
|
||||
} else {
|
||||
if(data.meta.propagation_seconds === '') data.meta.propagation_seconds = undefined;
|
||||
}
|
||||
|
||||
if (typeof data.domain_names === 'string' && data.domain_names) {
|
||||
data.domain_names = data.domain_names.split(',');
|
||||
}
|
||||
} else if (data.provider === 'other' && !this.model.hasSslFiles()) {
|
||||
// check files are attached
|
||||
if (!this.ui.other_certificate[0].files.length || !this.ui.other_certificate[0].files[0].size) {
|
||||
alert('Certificate file is not attached');
|
||||
return;
|
||||
} else {
|
||||
if (this.ui.other_certificate[0].files[0].size > this.max_file_size) {
|
||||
alert('Certificate file is too large (> 100kb)');
|
||||
return;
|
||||
}
|
||||
ssl_files.push({name: 'certificate', file: this.ui.other_certificate[0].files[0]});
|
||||
}
|
||||
|
||||
if (!this.ui.other_certificate_key[0].files.length || !this.ui.other_certificate_key[0].files[0].size) {
|
||||
alert('Certificate key file is not attached');
|
||||
return;
|
||||
} else {
|
||||
if (this.ui.other_certificate_key[0].files[0].size > this.max_file_size) {
|
||||
alert('Certificate key file is too large (> 100kb)');
|
||||
return;
|
||||
}
|
||||
ssl_files.push({name: 'certificate_key', file: this.ui.other_certificate_key[0].files[0]});
|
||||
}
|
||||
|
||||
if (this.ui.other_intermediate_certificate[0].files.length && this.ui.other_intermediate_certificate[0].files[0].size) {
|
||||
if (this.ui.other_intermediate_certificate[0].files[0].size > this.max_file_size) {
|
||||
alert('Intermediate Certificate file is too large (> 100kb)');
|
||||
return;
|
||||
}
|
||||
ssl_files.push({name: 'intermediate_certificate', file: this.ui.other_intermediate_certificate[0].files[0]});
|
||||
}
|
||||
}
|
||||
|
||||
this.ui.loader_content.show();
|
||||
this.ui.non_loader_content.hide();
|
||||
|
||||
// compile file data
|
||||
let form_data = new FormData();
|
||||
if (data.provider === 'other' && ssl_files.length) {
|
||||
ssl_files.map(function (file) {
|
||||
form_data.append(file.name, file.file);
|
||||
});
|
||||
}
|
||||
|
||||
new Promise(resolve => {
|
||||
if (data.provider === 'other') {
|
||||
resolve(App.Api.Nginx.Certificates.validate(form_data));
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
return App.Api.Nginx.Certificates.create(data);
|
||||
})
|
||||
.then(result => {
|
||||
this.model.set(result);
|
||||
|
||||
// Now upload the certs if we need to
|
||||
if (data.provider === 'other') {
|
||||
return App.Api.Nginx.Certificates.upload(this.model.get('id'), form_data)
|
||||
.then(result => {
|
||||
this.model.set('meta', _.assign({}, this.model.get('meta'), result));
|
||||
});
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
App.UI.closeModal(function () {
|
||||
App.Controller.showNginxCertificates();
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
let more_info = '';
|
||||
if (err.code === 500 && err.debug) {
|
||||
try{
|
||||
more_info = JSON.parse(err.debug).debug.stack.join("\n");
|
||||
} catch(e) {}
|
||||
}
|
||||
this.ui.le_error_info[0].innerHTML = `${err.message}${more_info !== '' ? `<pre class="mt-3">${more_info}</pre>`:''}`;
|
||||
this.ui.le_error_info.show();
|
||||
this.ui.le_error_info[0].scrollIntoView();
|
||||
this.ui.loader_content.hide();
|
||||
this.ui.non_loader_content.show();
|
||||
});
|
||||
},
|
||||
'change @ui.other_certificate_key': function(e){
|
||||
this.setFileName("other_certificate_key_label", e)
|
||||
},
|
||||
'change @ui.other_certificate': function(e){
|
||||
this.setFileName("other_certificate_label", e)
|
||||
},
|
||||
'change @ui.other_intermediate_certificate': function(e){
|
||||
this.setFileName("other_intermediate_certificate_label", e)
|
||||
}
|
||||
},
|
||||
setFileName(ui, e){
|
||||
this.getUI(ui).text(e.target.files[0].name)
|
||||
},
|
||||
templateContext: {
|
||||
getLetsencryptEmail: function () {
|
||||
return typeof this.meta.letsencrypt_email !== 'undefined' ? this.meta.letsencrypt_email : App.Cache.User.get('email');
|
||||
},
|
||||
getLetsencryptAgree: function () {
|
||||
return typeof this.meta.letsencrypt_agree !== 'undefined' ? this.meta.letsencrypt_agree : false;
|
||||
},
|
||||
getUseDnsChallenge: function () {
|
||||
return typeof this.meta.dns_challenge !== 'undefined' ? this.meta.dns_challenge : false;
|
||||
},
|
||||
getDnsProvider: function () {
|
||||
return typeof this.meta.dns_provider !== 'undefined' && this.meta.dns_provider != '' ? this.meta.dns_provider : null;
|
||||
},
|
||||
getDnsProviderCredentials: function () {
|
||||
return typeof this.meta.dns_provider_credentials !== 'undefined' ? this.meta.dns_provider_credentials : '';
|
||||
},
|
||||
getPropagationSeconds: function () {
|
||||
return typeof this.meta.propagation_seconds !== 'undefined' ? this.meta.propagation_seconds : '';
|
||||
},
|
||||
dns_plugins: dns_providers,
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
this.ui.domain_names.selectize({
|
||||
delimiter: ',',
|
||||
persist: false,
|
||||
maxOptions: 15,
|
||||
create: function (input) {
|
||||
return {
|
||||
value: input,
|
||||
text: input
|
||||
};
|
||||
},
|
||||
createFilter: /^(?:[^.]+\.?)+[^.]$/
|
||||
});
|
||||
this.ui.dns_challenge_content.hide();
|
||||
this.ui.credentials_file_content.hide();
|
||||
this.ui.loader_content.hide();
|
||||
this.ui.le_error_info.hide();
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
if (typeof options.model === 'undefined' || !options.model) {
|
||||
this.model = new CertificateModel.Model({provider: 'letsencrypt'});
|
||||
}
|
||||
}
|
||||
});
|
@@ -1,50 +0,0 @@
|
||||
<td class="text-center">
|
||||
<div class="avatar d-block" style="background-image: url(<%- owner.avatar || '/images/default-avatar.jpg' %>)" title="Owned by <%- owner.name %>">
|
||||
<span class="avatar-status <%- owner.is_disabled ? 'bg-red' : 'bg-green' %>"></span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="wrap">
|
||||
<%
|
||||
if (provider === 'letsencrypt') {
|
||||
domain_names.map(function(host) {
|
||||
if (host.indexOf('*') === -1) {
|
||||
%>
|
||||
<span class="tag host-link hover-pink" rel="https://<%- host %>"><%- host %></span>
|
||||
<%
|
||||
} else {
|
||||
%>
|
||||
<span class="tag"><%- host %></span>
|
||||
<%
|
||||
}
|
||||
});
|
||||
} else {
|
||||
%><%- nice_name %><%
|
||||
}
|
||||
%>
|
||||
</div>
|
||||
<div class="small text-muted">
|
||||
<%- i18n('str', 'created-on', {date: formatDbDate(created_on, 'Do MMMM YYYY')}) %>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<%- i18n('ssl', provider) %><% if (meta.dns_provider) { %> - <%- dns_providers[meta.dns_provider].display_name %><% } %>
|
||||
</td>
|
||||
<td class="<%- isExpired() ? 'text-danger' : '' %>">
|
||||
<%- formatDbDate(expires_on, 'Do MMMM YYYY, h:mm a') %>
|
||||
</td>
|
||||
<% if (canManage) { %>
|
||||
<td class="text-right">
|
||||
<div class="item-action dropdown">
|
||||
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<span class="dropdown-header"><%- i18n('audit-log', 'certificate') %> #<%- id %></span>
|
||||
<% if (provider === 'letsencrypt') { %>
|
||||
<a href="#" class="renew dropdown-item"><i class="dropdown-icon fe fe-refresh-cw"></i> <%- i18n('certificates', 'force-renew') %></a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<% } %>
|
||||
<a href="#" class="delete dropdown-item"><i class="dropdown-icon fe fe-trash-2"></i> <%- i18n('str', 'delete') %></a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<% } %>
|
@@ -1,46 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const moment = require('moment');
|
||||
const App = require('../../../main');
|
||||
const template = require('./item.ejs');
|
||||
const dns_providers = require('../../../../../../global/certbot-dns-plugins')
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
tagName: 'tr',
|
||||
|
||||
ui: {
|
||||
host_link: '.host-link',
|
||||
renew: 'a.renew',
|
||||
delete: 'a.delete'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click @ui.renew': function (e) {
|
||||
e.preventDefault();
|
||||
App.Controller.showNginxCertificateRenew(this.model);
|
||||
},
|
||||
|
||||
'click @ui.delete': function (e) {
|
||||
e.preventDefault();
|
||||
App.Controller.showNginxCertificateDeleteConfirm(this.model);
|
||||
},
|
||||
|
||||
'click @ui.host_link': function (e) {
|
||||
e.preventDefault();
|
||||
let win = window.open($(e.currentTarget).attr('rel'), '_blank');
|
||||
win.focus();
|
||||
}
|
||||
},
|
||||
|
||||
templateContext: {
|
||||
canManage: App.Cache.User.canManage('certificates'),
|
||||
isExpired: function () {
|
||||
return moment(this.expires_on).isBefore(moment());
|
||||
},
|
||||
dns_providers: dns_providers
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
this.listenTo(this.model, 'change', this.render);
|
||||
}
|
||||
});
|
@@ -1,12 +0,0 @@
|
||||
<thead>
|
||||
<th width="30"> </th>
|
||||
<th><%- i18n('str', 'name') %></th>
|
||||
<th><%- i18n('all-hosts', 'cert-provider') %></th>
|
||||
<th><%- i18n('str', 'expires') %></th>
|
||||
<% if (canManage) { %>
|
||||
<th> </th>
|
||||
<% } %>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- items -->
|
||||
</tbody>
|
@@ -1,32 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const App = require('../../../main');
|
||||
const ItemView = require('./item');
|
||||
const template = require('./main.ejs');
|
||||
|
||||
const TableBody = Mn.CollectionView.extend({
|
||||
tagName: 'tbody',
|
||||
childView: ItemView
|
||||
});
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
tagName: 'table',
|
||||
className: 'table table-hover table-outline table-vcenter card-table',
|
||||
template: template,
|
||||
|
||||
regions: {
|
||||
body: {
|
||||
el: 'tbody',
|
||||
replaceElement: true
|
||||
}
|
||||
},
|
||||
|
||||
templateContext: {
|
||||
canManage: App.Cache.User.canManage('certificates')
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
this.showChildView('body', new TableBody({
|
||||
collection: this.collection
|
||||
}));
|
||||
}
|
||||
});
|
@@ -1,28 +0,0 @@
|
||||
<div class="card">
|
||||
<div class="card-status bg-pink"></div>
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><%- i18n('certificates', 'title') %></h3>
|
||||
<div class="card-options">
|
||||
<a href="#" class="btn btn-outline-secondary btn-sm ml-2 help"><i class="fe fe-help-circle"></i></a>
|
||||
<% if (showAddButton) { %>
|
||||
<div class="dropdown">
|
||||
<button type="button" class="btn btn-outline-pink btn-sm ml-2 dropdown-toggle" data-toggle="dropdown">
|
||||
<%- i18n('certificates', 'add') %>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item add-item" data-cert="letsencrypt" href="#"><%- i18n('ssl', 'letsencrypt') %></a>
|
||||
<a class="dropdown-item add-item" data-cert="other" href="#"><%- i18n('ssl', 'other') %></a>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body no-padding min-100">
|
||||
<div class="dimmer active">
|
||||
<div class="loader"></div>
|
||||
<div class="dimmer-content list-region">
|
||||
<!-- List Region -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@@ -1,82 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const App = require('../../main');
|
||||
const CertificateModel = require('../../../models/certificate');
|
||||
const ListView = require('./list/main');
|
||||
const ErrorView = require('../../error/main');
|
||||
const EmptyView = require('../../empty/main');
|
||||
const template = require('./main.ejs');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
id: 'nginx-certificates',
|
||||
template: template,
|
||||
|
||||
ui: {
|
||||
list_region: '.list-region',
|
||||
add: '.add-item',
|
||||
help: '.help',
|
||||
dimmer: '.dimmer'
|
||||
},
|
||||
|
||||
regions: {
|
||||
list_region: '@ui.list_region'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click @ui.add': function (e) {
|
||||
e.preventDefault();
|
||||
let model = new CertificateModel.Model({provider: $(e.currentTarget).data('cert')});
|
||||
App.Controller.showNginxCertificateForm(model);
|
||||
},
|
||||
|
||||
'click @ui.help': function (e) {
|
||||
e.preventDefault();
|
||||
App.Controller.showHelp(App.i18n('certificates', 'help-title'), App.i18n('certificates', 'help-content'));
|
||||
}
|
||||
},
|
||||
|
||||
templateContext: {
|
||||
showAddButton: App.Cache.User.canManage('certificates')
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
let view = this;
|
||||
|
||||
App.Api.Nginx.Certificates.getAll(['owner'])
|
||||
.then(response => {
|
||||
if (!view.isDestroyed()) {
|
||||
if (response && response.length) {
|
||||
view.showChildView('list_region', new ListView({
|
||||
collection: new CertificateModel.Collection(response)
|
||||
}));
|
||||
} else {
|
||||
let manage = App.Cache.User.canManage('certificates');
|
||||
|
||||
view.showChildView('list_region', new EmptyView({
|
||||
title: App.i18n('certificates', 'empty'),
|
||||
subtitle: App.i18n('all-hosts', 'empty-subtitle', {manage: manage}),
|
||||
link: manage ? App.i18n('certificates', 'add') : null,
|
||||
btn_color: 'pink',
|
||||
permission: 'certificates',
|
||||
action: function () {
|
||||
App.Controller.showNginxCertificateForm();
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
view.showChildView('list_region', new ErrorView({
|
||||
code: err.code,
|
||||
message: err.message,
|
||||
retry: function () {
|
||||
App.Controller.showNginxCertificates();
|
||||
}
|
||||
}));
|
||||
|
||||
console.error(err);
|
||||
})
|
||||
.then(() => {
|
||||
view.ui.dimmer.removeClass('active');
|
||||
});
|
||||
}
|
||||
});
|
@@ -1,14 +0,0 @@
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><%- i18n('certificates', 'renew-title') %></h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="waiting text-center">
|
||||
<%= i18n('str', 'please-wait') %>
|
||||
</div>
|
||||
<div class="alert alert-danger error" role="alert"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary cancel" data-dismiss="modal" disabled><%- i18n('str', 'close') %></button>
|
||||
</div>
|
||||
</div>
|
@@ -1,31 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const App = require('../../main');
|
||||
const template = require('./renew.ejs');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
className: 'modal-dialog',
|
||||
|
||||
ui: {
|
||||
waiting: '.waiting',
|
||||
error: '.error',
|
||||
close: 'button.cancel'
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
this.ui.error.hide();
|
||||
|
||||
App.Api.Nginx.Certificates.renew(this.model.get('id'))
|
||||
.then((result) => {
|
||||
this.model.set(result);
|
||||
setTimeout(() => {
|
||||
App.UI.closeModal();
|
||||
}, 1000);
|
||||
})
|
||||
.catch((err) => {
|
||||
this.ui.waiting.hide();
|
||||
this.ui.error.text(err.message).show();
|
||||
this.ui.close.prop('disabled', false);
|
||||
});
|
||||
}
|
||||
});
|
@@ -1,23 +0,0 @@
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><%- i18n('dead-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('dead-hosts', 'delete-confirm', {domains: domain_names.join(', ')}) %>
|
||||
<% if (certificate_id) { %>
|
||||
<br><br>
|
||||
<%- i18n('ssl', '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>
|
@@ -1,32 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const App = require('../../main');
|
||||
const template = require('./delete.ejs');
|
||||
|
||||
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.DeadHosts.delete(this.model.get('id'))
|
||||
.then(() => {
|
||||
App.Controller.showNginxDead();
|
||||
App.UI.closeModal();
|
||||
})
|
||||
.catch(err => {
|
||||
alert(err.message);
|
||||
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
@@ -1,206 +0,0 @@
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><%- i18n('dead-hosts', 'form-title', {id: id}) %></h5>
|
||||
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
|
||||
</div>
|
||||
<div class="modal-body has-tabs">
|
||||
<div class="alert alert-danger mb-0 rounded-0" id="le-error-info" role="alert"></div>
|
||||
<form>
|
||||
<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> <%- 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> <%- i18n('str', 'ssl') %></a></li>
|
||||
<li role="presentation" class="nav-item"><a href="#advanced" aria-controls="tab3" role="tab" data-toggle="tab" class="nav-link"><i class="fe fe-settings"></i> <%- i18n('all-hosts', 'advanced') %></a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<!-- Details -->
|
||||
<div role="tabpanel" class="tab-pane active" id="details">
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('all-hosts', 'domain-names') %> <span class="form-required">*</span></label>
|
||||
<input type="text" name="domain_names" class="form-control" id="input-domains" value="<%- domain_names.join(',') %>" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SSL -->
|
||||
<div role="tabpanel" class="tab-pane" id="ssl-options">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('all-hosts', 'ssl-certificate') %></label>
|
||||
<select name="certificate_id" class="form-control custom-select" placeholder="<%- i18n('all-hosts', 'none') %>">
|
||||
<option selected value="0" data-data="{"id":0}" <%- certificate_id ? '' : 'selected' %>><%- i18n('all-hosts', 'none') %></option>
|
||||
<option selected value="new" data-data="{"id":"new"}"><%- i18n('all-hosts', 'new-cert') %></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="ssl_forced" value="1"<%- ssl_forced ? ' checked' : '' %><%- certificate_id ? '' : ' disabled' %>>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%- i18n('all-hosts', 'force-ssl') %></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="http2_support" value="1"<%- http2_support ? ' checked' : '' %><%- certificate_id ? '' : ' disabled' %>>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%- i18n('all-hosts', 'http2-support') %></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="hsts_enabled" value="1"<%- hsts_enabled ? ' checked' : '' %><%- certificate_id && ssl_forced ? '' : ' disabled' %>>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%- i18n('all-hosts', 'hsts-enabled') %> <a href="https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security" target="_blank"><i class="fe fe-help-circle"></i></a></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="hsts_subdomains" value="1"<%- hsts_subdomains ? ' checked' : '' %><%- certificate_id && ssl_forced && hsts_enabled ? '' : ' disabled' %>>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%- i18n('all-hosts', 'hsts-subdomains') %></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- DNS challenge -->
|
||||
<div class="col-sm-12 col-md-12 letsencrypt">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="custom-switch-input"
|
||||
name="meta[dns_challenge]"
|
||||
value="1"
|
||||
<%- getUseDnsChallenge() ? 'checked' : '' %>
|
||||
>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%= i18n('ssl', 'dns-challenge') %></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12 letsencrypt">
|
||||
<fieldset class="form-fieldset dns-challenge">
|
||||
<div class="text-red mb-4"><i class="fe fe-alert-triangle"></i> <%= i18n('ssl', 'certbot-warning') %></div>
|
||||
|
||||
<!-- Certbot DNS plugin selection -->
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('ssl', 'dns-provider') %> <span class="form-required">*</span></label>
|
||||
<select
|
||||
name="meta[dns_provider]"
|
||||
id="dns_provider"
|
||||
class="form-control custom-select"
|
||||
>
|
||||
<option
|
||||
value=""
|
||||
disabled
|
||||
hidden
|
||||
<%- getDnsProvider() === null ? 'selected' : '' %>
|
||||
>Please Choose...</option>
|
||||
<% _.each(dns_plugins, function(plugin_info, plugin_name){ %>
|
||||
<option
|
||||
value="<%- plugin_name %>"
|
||||
<%- getDnsProvider() === plugin_name ? 'selected' : '' %>
|
||||
><%- plugin_info.display_name %></option>
|
||||
<% }); %>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Certbot credentials file content -->
|
||||
<div class="row credentials-file-content">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('ssl', 'credentials-file-content') %> <span class="form-required">*</span></label>
|
||||
<textarea
|
||||
name="meta[dns_provider_credentials]"
|
||||
class="form-control text-monospace"
|
||||
id="dns_provider_credentials"
|
||||
><%- getDnsProviderCredentials() %></textarea>
|
||||
<div class="text-secondary small">
|
||||
<i class="fe fe-info"></i>
|
||||
<%= i18n('ssl', 'credentials-file-content-info') %>
|
||||
</div>
|
||||
<div class="text-red small">
|
||||
<i class="fe fe-alert-triangle"></i>
|
||||
<%= i18n('ssl', 'stored-as-plaintext-info') %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- DNS propagation delay -->
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group mb-0">
|
||||
<label class="form-label"><%- i18n('ssl', 'propagation-seconds') %></label>
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
name="meta[propagation_seconds]"
|
||||
class="form-control"
|
||||
id="propagation_seconds"
|
||||
value="<%- getPropagationSeconds() %>"
|
||||
>
|
||||
<div class="text-secondary small">
|
||||
<i class="fe fe-info"></i>
|
||||
<%= i18n('ssl', 'propagation-seconds-info') %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<!-- Lets encrypt -->
|
||||
<div class="col-sm-12 col-md-12 letsencrypt">
|
||||
<div class="form-group">
|
||||
<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 disabled>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12 letsencrypt">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="meta[letsencrypt_agree]" value="1" required disabled>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%= i18n('ssl', 'letsencrypt-agree', {url: 'https://letsencrypt.org/repository/'}) %> <span class="form-required">*</span></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Advanced -->
|
||||
<div role="tabpanel" class="tab-pane" id="advanced">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="form-group mb-0">
|
||||
<label class="form-label"><%- i18n('all-hosts', 'advanced-config') %></label>
|
||||
<textarea name="advanced_config" rows="8" class="form-control text-monospace" placeholder="# <%- i18n('all-hosts', 'advanced-warning') %>"><%- advanced_config %></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</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-teal save"><%- i18n('str', 'save') %></button>
|
||||
</div>
|
||||
</div>
|
@@ -1,286 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const App = require('../../main');
|
||||
const DeadHostModel = require('../../../models/dead-host');
|
||||
const template = require('./form.ejs');
|
||||
const certListItemTemplate = require('../certificates-list-item.ejs');
|
||||
const Helpers = require('../../../lib/helpers');
|
||||
const i18n = require('../../i18n');
|
||||
const dns_providers = require('../../../../../global/certbot-dns-plugins');
|
||||
|
||||
require('jquery-serializejson');
|
||||
require('selectize');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
className: 'modal-dialog',
|
||||
|
||||
ui: {
|
||||
form: 'form',
|
||||
domain_names: 'input[name="domain_names"]',
|
||||
buttons: '.modal-footer button',
|
||||
cancel: 'button.cancel',
|
||||
save: 'button.save',
|
||||
le_error_info: '#le-error-info',
|
||||
certificate_select: 'select[name="certificate_id"]',
|
||||
ssl_forced: 'input[name="ssl_forced"]',
|
||||
hsts_enabled: 'input[name="hsts_enabled"]',
|
||||
hsts_subdomains: 'input[name="hsts_subdomains"]',
|
||||
http2_support: 'input[name="http2_support"]',
|
||||
dns_challenge_switch: 'input[name="meta[dns_challenge]"]',
|
||||
dns_challenge_content: '.dns-challenge',
|
||||
dns_provider: 'select[name="meta[dns_provider]"]',
|
||||
credentials_file_content: '.credentials-file-content',
|
||||
dns_provider_credentials: 'textarea[name="meta[dns_provider_credentials]"]',
|
||||
propagation_seconds: 'input[name="meta[propagation_seconds]"]',
|
||||
letsencrypt: '.letsencrypt'
|
||||
},
|
||||
|
||||
events: {
|
||||
'change @ui.certificate_select': function () {
|
||||
let id = this.ui.certificate_select.val();
|
||||
if (id === 'new') {
|
||||
this.ui.letsencrypt.show().find('input').prop('disabled', false);
|
||||
this.ui.dns_challenge_content.hide();
|
||||
} else {
|
||||
this.ui.letsencrypt.hide().find('input').prop('disabled', true);
|
||||
}
|
||||
|
||||
|
||||
let enabled = id === 'new' || parseInt(id, 10) > 0;
|
||||
|
||||
let inputs = this.ui.ssl_forced.add(this.ui.http2_support);
|
||||
inputs
|
||||
.prop('disabled', !enabled)
|
||||
.parents('.form-group')
|
||||
.css('opacity', enabled ? 1 : 0.5);
|
||||
|
||||
if (!enabled) {
|
||||
inputs.prop('checked', false);
|
||||
}
|
||||
|
||||
inputs.trigger('change');
|
||||
},
|
||||
|
||||
'change @ui.ssl_forced': function () {
|
||||
let checked = this.ui.ssl_forced.prop('checked');
|
||||
this.ui.hsts_enabled
|
||||
.prop('disabled', !checked)
|
||||
.parents('.form-group')
|
||||
.css('opacity', checked ? 1 : 0.5);
|
||||
|
||||
if (!checked) {
|
||||
this.ui.hsts_enabled.prop('checked', false);
|
||||
}
|
||||
|
||||
this.ui.hsts_enabled.trigger('change');
|
||||
},
|
||||
|
||||
'change @ui.hsts_enabled': function () {
|
||||
let checked = this.ui.hsts_enabled.prop('checked');
|
||||
this.ui.hsts_subdomains
|
||||
.prop('disabled', !checked)
|
||||
.parents('.form-group')
|
||||
.css('opacity', checked ? 1 : 0.5);
|
||||
|
||||
if (!checked) {
|
||||
this.ui.hsts_subdomains.prop('checked', false);
|
||||
}
|
||||
},
|
||||
|
||||
'change @ui.dns_challenge_switch': function () {
|
||||
const checked = this.ui.dns_challenge_switch.prop('checked');
|
||||
if (checked) {
|
||||
this.ui.dns_provider.prop('required', 'required');
|
||||
const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value;
|
||||
if(selected_provider != '' && dns_providers[selected_provider].credentials !== false){
|
||||
this.ui.dns_provider_credentials.prop('required', 'required');
|
||||
}
|
||||
this.ui.dns_challenge_content.show();
|
||||
} else {
|
||||
this.ui.dns_provider.prop('required', false);
|
||||
this.ui.dns_provider_credentials.prop('required', false);
|
||||
this.ui.dns_challenge_content.hide();
|
||||
}
|
||||
},
|
||||
|
||||
'change @ui.dns_provider': function () {
|
||||
const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value;
|
||||
if (selected_provider != '' && dns_providers[selected_provider].credentials !== false) {
|
||||
this.ui.dns_provider_credentials.prop('required', 'required');
|
||||
this.ui.dns_provider_credentials[0].value = dns_providers[selected_provider].credentials;
|
||||
this.ui.credentials_file_content.show();
|
||||
} else {
|
||||
this.ui.dns_provider_credentials.prop('required', false);
|
||||
this.ui.credentials_file_content.hide();
|
||||
}
|
||||
},
|
||||
|
||||
'click @ui.save': function (e) {
|
||||
e.preventDefault();
|
||||
this.ui.le_error_info.hide();
|
||||
|
||||
if (!this.ui.form[0].checkValidity()) {
|
||||
$('<input type="submit">').hide().appendTo(this.ui.form).click().remove();
|
||||
return;
|
||||
}
|
||||
|
||||
let view = this;
|
||||
let data = this.ui.form.serializeJSON();
|
||||
|
||||
// Manipulate
|
||||
data.hsts_enabled = !!data.hsts_enabled;
|
||||
data.hsts_subdomains = !!data.hsts_subdomains;
|
||||
data.http2_support = !!data.http2_support;
|
||||
data.ssl_forced = !!data.ssl_forced;
|
||||
|
||||
if (typeof data.meta === 'undefined') data.meta = {};
|
||||
data.meta.letsencrypt_agree = data.meta.letsencrypt_agree == 1;
|
||||
data.meta.dns_challenge = data.meta.dns_challenge == 1;
|
||||
|
||||
if(!data.meta.dns_challenge){
|
||||
data.meta.dns_provider = undefined;
|
||||
data.meta.dns_provider_credentials = undefined;
|
||||
data.meta.propagation_seconds = undefined;
|
||||
} else {
|
||||
if(data.meta.propagation_seconds === '') data.meta.propagation_seconds = undefined;
|
||||
}
|
||||
|
||||
if (typeof data.domain_names === 'string' && data.domain_names) {
|
||||
data.domain_names = data.domain_names.split(',');
|
||||
}
|
||||
|
||||
// Check for any domain names containing wildcards, which are not allowed with letsencrypt
|
||||
if (data.certificate_id === 'new') {
|
||||
let domain_err = false;
|
||||
if (!data.meta.dns_challenge) {
|
||||
data.domain_names.map(function (name) {
|
||||
if (name.match(/\*/im)) {
|
||||
domain_err = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (domain_err) {
|
||||
alert(i18n('ssl', 'no-wildcard-without-dns'));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
data.certificate_id = parseInt(data.certificate_id, 10);
|
||||
}
|
||||
|
||||
let method = App.Api.Nginx.DeadHosts.create;
|
||||
let is_new = true;
|
||||
|
||||
if (this.model.get('id')) {
|
||||
// edit
|
||||
is_new = false;
|
||||
method = App.Api.Nginx.DeadHosts.update;
|
||||
data.id = this.model.get('id');
|
||||
}
|
||||
|
||||
this.ui.buttons.prop('disabled', true).addClass('btn-disabled');
|
||||
this.ui.save.addClass('btn-loading');
|
||||
|
||||
method(data)
|
||||
.then(result => {
|
||||
view.model.set(result);
|
||||
|
||||
App.UI.closeModal(function () {
|
||||
if (is_new) {
|
||||
App.Controller.showNginxDead();
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
let more_info = '';
|
||||
if(err.code === 500 && err.debug){
|
||||
try{
|
||||
more_info = JSON.parse(err.debug).debug.stack.join("\n");
|
||||
} catch(e) {}
|
||||
}
|
||||
this.ui.le_error_info[0].innerHTML = `${err.message}${more_info !== '' ? `<pre class="mt-3">${more_info}</pre>`:''}`;
|
||||
this.ui.le_error_info.show();
|
||||
this.ui.le_error_info[0].scrollIntoView();
|
||||
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
||||
this.ui.save.removeClass('btn-loading');
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
templateContext: {
|
||||
getLetsencryptEmail: function () {
|
||||
return App.Cache.User.get('email');
|
||||
},
|
||||
getUseDnsChallenge: function () {
|
||||
return typeof this.meta.dns_challenge !== 'undefined' ? this.meta.dns_challenge : false;
|
||||
},
|
||||
getDnsProvider: function () {
|
||||
return typeof this.meta.dns_provider !== 'undefined' && this.meta.dns_provider != '' ? this.meta.dns_provider : null;
|
||||
},
|
||||
getDnsProviderCredentials: function () {
|
||||
return typeof this.meta.dns_provider_credentials !== 'undefined' ? this.meta.dns_provider_credentials : '';
|
||||
},
|
||||
getPropagationSeconds: function () {
|
||||
return typeof this.meta.propagation_seconds !== 'undefined' ? this.meta.propagation_seconds : '';
|
||||
},
|
||||
dns_plugins: dns_providers,
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
let view = this;
|
||||
|
||||
// Domain names
|
||||
this.ui.domain_names.selectize({
|
||||
delimiter: ',',
|
||||
persist: false,
|
||||
maxOptions: 15,
|
||||
create: function (input) {
|
||||
return {
|
||||
value: input,
|
||||
text: input
|
||||
};
|
||||
},
|
||||
createFilter: /^(?:\*\.)?(?:[^.*]+\.?)+[^.]$/
|
||||
});
|
||||
|
||||
// Certificates
|
||||
this.ui.le_error_info.hide();
|
||||
this.ui.dns_challenge_content.hide();
|
||||
this.ui.credentials_file_content.hide();
|
||||
this.ui.letsencrypt.hide();
|
||||
this.ui.certificate_select.selectize({
|
||||
valueField: 'id',
|
||||
labelField: 'nice_name',
|
||||
searchField: ['nice_name', 'domain_names'],
|
||||
create: false,
|
||||
preload: true,
|
||||
allowEmptyOption: true,
|
||||
render: {
|
||||
option: function (item) {
|
||||
item.i18n = App.i18n;
|
||||
item.formatDbDate = Helpers.formatDbDate;
|
||||
return certListItemTemplate(item);
|
||||
}
|
||||
},
|
||||
load: function (query, callback) {
|
||||
App.Api.Nginx.Certificates.getAll()
|
||||
.then(rows => {
|
||||
callback(rows);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
callback();
|
||||
});
|
||||
},
|
||||
onLoad: function () {
|
||||
view.ui.certificate_select[0].selectize.setValue(view.model.get('certificate_id'));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
if (typeof options.model === 'undefined' || !options.model) {
|
||||
this.model = new DeadHostModel.Model();
|
||||
}
|
||||
}
|
||||
});
|
@@ -1,54 +0,0 @@
|
||||
<td class="text-center">
|
||||
<div class="avatar d-block" style="background-image: url(<%- owner.avatar || '/images/default-avatar.jpg' %>)" title="Owned by <%- owner.name %>">
|
||||
<span class="avatar-status <%- owner.is_disabled ? 'bg-red' : 'bg-green' %>"></span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>
|
||||
<% domain_names.map(function(host) {
|
||||
if (host.indexOf('*') === -1) {
|
||||
%>
|
||||
<span class="tag host-link hover-red" rel="http<%- certificate_id ? 's' : '' %>://<%- host %>"><%- host %></span>
|
||||
<%
|
||||
} else {
|
||||
%>
|
||||
<span class="tag"><%- host %></span>
|
||||
<%
|
||||
}
|
||||
});
|
||||
%>
|
||||
</div>
|
||||
<div class="small text-muted">
|
||||
<%- i18n('str', 'created-on', {date: formatDbDate(created_on, 'Do MMMM YYYY')}) %>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div><%- certificate ? i18n('ssl', certificate.provider) : i18n('ssl', 'none') %></div>
|
||||
</td>
|
||||
<td>
|
||||
<%
|
||||
var o = isOnline();
|
||||
if (!enabled) { %>
|
||||
<span class="status-icon bg-warning"></span> <%- i18n('str', 'disabled') %>
|
||||
<% } else if (o === true) { %>
|
||||
<span class="status-icon bg-success"></span> <%- i18n('str', 'online') %>
|
||||
<% } else if (o === false) { %>
|
||||
<span title="<%- getOfflineError() %>"><span class="status-icon bg-danger"></span> <%- i18n('str', 'offline') %></span>
|
||||
<% } else { %>
|
||||
<span class="status-icon bg-warning"></span> <%- i18n('str', 'unknown') %>
|
||||
<% } %>
|
||||
</td>
|
||||
<% if (canManage) { %>
|
||||
<td class="text-right">
|
||||
<div class="item-action dropdown">
|
||||
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<span class="dropdown-header"><%- i18n('audit-log', 'dead-host') %> #<%- id %></span>
|
||||
<a href="#" class="edit dropdown-item"><i class="dropdown-icon fe fe-edit"></i> <%- i18n('str', 'edit') %></a>
|
||||
<a href="#" class="able dropdown-item"><i class="dropdown-icon fe fe-power"></i> <%- i18n('str', enabled ? 'disable' : 'enable') %></a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a href="#" class="delete dropdown-item"><i class="dropdown-icon fe fe-trash-2"></i> <%- i18n('str', 'delete') %></a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<% } %>
|
@@ -1,61 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const App = require('../../../main');
|
||||
const template = require('./item.ejs');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
tagName: 'tr',
|
||||
|
||||
ui: {
|
||||
able: 'a.able',
|
||||
edit: 'a.edit',
|
||||
delete: 'a.delete',
|
||||
host_link: '.host-link'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click @ui.able': function (e) {
|
||||
e.preventDefault();
|
||||
let id = this.model.get('id');
|
||||
App.Api.Nginx.DeadHosts[this.model.get('enabled') ? 'disable' : 'enable'](id)
|
||||
.then(() => {
|
||||
return App.Api.Nginx.DeadHosts.get(id)
|
||||
.then(row => {
|
||||
this.model.set(row);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
'click @ui.edit': function (e) {
|
||||
e.preventDefault();
|
||||
App.Controller.showNginxDeadForm(this.model);
|
||||
},
|
||||
|
||||
'click @ui.delete': function (e) {
|
||||
e.preventDefault();
|
||||
App.Controller.showNginxDeadDeleteConfirm(this.model);
|
||||
},
|
||||
|
||||
'click @ui.host_link': function (e) {
|
||||
e.preventDefault();
|
||||
let win = window.open($(e.currentTarget).attr('rel'), '_blank');
|
||||
win.focus();
|
||||
}
|
||||
},
|
||||
|
||||
templateContext: {
|
||||
canManage: App.Cache.User.canManage('dead_hosts'),
|
||||
|
||||
isOnline: function () {
|
||||
return typeof this.meta.nginx_online === 'undefined' ? null : this.meta.nginx_online;
|
||||
},
|
||||
|
||||
getOfflineError: function () {
|
||||
return this.meta.nginx_err || '';
|
||||
}
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
this.listenTo(this.model, 'change', this.render);
|
||||
}
|
||||
});
|
@@ -1,12 +0,0 @@
|
||||
<thead>
|
||||
<th width="30"> </th>
|
||||
<th><%- i18n('str', 'source') %></th>
|
||||
<th><%- i18n('str', 'ssl') %></th>
|
||||
<th><%- i18n('str', 'status') %></th>
|
||||
<% if (canManage) { %>
|
||||
<th> </th>
|
||||
<% } %>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- items -->
|
||||
</tbody>
|
@@ -1,32 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const App = require('../../../main');
|
||||
const ItemView = require('./item');
|
||||
const template = require('./main.ejs');
|
||||
|
||||
const TableBody = Mn.CollectionView.extend({
|
||||
tagName: 'tbody',
|
||||
childView: ItemView
|
||||
});
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
tagName: 'table',
|
||||
className: 'table table-hover table-outline table-vcenter card-table',
|
||||
template: template,
|
||||
|
||||
regions: {
|
||||
body: {
|
||||
el: 'tbody',
|
||||
replaceElement: true
|
||||
}
|
||||
},
|
||||
|
||||
templateContext: {
|
||||
canManage: App.Cache.User.canManage('dead_hosts')
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
this.showChildView('body', new TableBody({
|
||||
collection: this.collection
|
||||
}));
|
||||
}
|
||||
});
|
@@ -1,20 +0,0 @@
|
||||
<div class="card">
|
||||
<div class="card-status bg-danger"></div>
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><%- i18n('dead-hosts', 'title') %></h3>
|
||||
<div class="card-options">
|
||||
<a href="#" class="btn btn-outline-secondary btn-sm ml-2 help"><i class="fe fe-help-circle"></i></a>
|
||||
<% if (showAddButton) { %>
|
||||
<a href="#" class="btn btn-outline-danger btn-sm ml-2 add-item"><%- i18n('dead-hosts', 'add') %></a>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body no-padding min-100">
|
||||
<div class="dimmer active">
|
||||
<div class="loader"></div>
|
||||
<div class="dimmer-content list-region">
|
||||
<!-- List Region -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@@ -1,81 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const App = require('../../main');
|
||||
const DeadHostModel = require('../../../models/dead-host');
|
||||
const ListView = require('./list/main');
|
||||
const ErrorView = require('../../error/main');
|
||||
const EmptyView = require('../../empty/main');
|
||||
const template = require('./main.ejs');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
id: 'nginx-dead',
|
||||
template: template,
|
||||
|
||||
ui: {
|
||||
list_region: '.list-region',
|
||||
add: '.add-item',
|
||||
help: '.help',
|
||||
dimmer: '.dimmer'
|
||||
},
|
||||
|
||||
regions: {
|
||||
list_region: '@ui.list_region'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click @ui.add': function (e) {
|
||||
e.preventDefault();
|
||||
App.Controller.showNginxDeadForm();
|
||||
},
|
||||
|
||||
'click @ui.help': function (e) {
|
||||
e.preventDefault();
|
||||
App.Controller.showHelp(App.i18n('dead-hosts', 'help-title'), App.i18n('dead-hosts', 'help-content'));
|
||||
}
|
||||
},
|
||||
|
||||
templateContext: {
|
||||
showAddButton: App.Cache.User.canManage('dead_hosts')
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
let view = this;
|
||||
|
||||
App.Api.Nginx.DeadHosts.getAll(['owner', 'certificate'])
|
||||
.then(response => {
|
||||
if (!view.isDestroyed()) {
|
||||
if (response && response.length) {
|
||||
view.showChildView('list_region', new ListView({
|
||||
collection: new DeadHostModel.Collection(response)
|
||||
}));
|
||||
} else {
|
||||
let manage = App.Cache.User.canManage('dead_hosts');
|
||||
|
||||
view.showChildView('list_region', new EmptyView({
|
||||
title: App.i18n('dead-hosts', 'empty'),
|
||||
subtitle: App.i18n('all-hosts', 'empty-subtitle', {manage: manage}),
|
||||
link: manage ? App.i18n('dead-hosts', 'add') : null,
|
||||
btn_color: 'danger',
|
||||
permission: 'dead_hosts',
|
||||
action: function () {
|
||||
App.Controller.showNginxDeadForm();
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
view.showChildView('list_region', new ErrorView({
|
||||
code: err.code,
|
||||
message: err.message,
|
||||
retry: function () {
|
||||
App.Controller.showNginxDead();
|
||||
}
|
||||
}));
|
||||
|
||||
console.error(err);
|
||||
})
|
||||
.then(() => {
|
||||
view.ui.dimmer.removeClass('active');
|
||||
});
|
||||
}
|
||||
});
|
@@ -1,13 +0,0 @@
|
||||
<div>
|
||||
<% if (id > 0) { %>
|
||||
<div class="title">
|
||||
<i class="fe fe-lock text-teal"></i> <%- name %>
|
||||
</div>
|
||||
<span class="description"><%- i18n('access-lists', 'item-count', {count: items.length || 0}) %>, <%- i18n('access-lists', 'client-count', {count: clients.length || 0}) %> – Created: <%- formatDbDate(created_on, 'Do MMMM YYYY, h:mm a') %></span>
|
||||
<% } else { %>
|
||||
<div class="title">
|
||||
<i class="fe fe-unlock text-yellow"></i> <%- i18n('access-lists', 'public') %>
|
||||
</div>
|
||||
<span class="description"><%- i18n('access-lists', 'public-sub') %></span>
|
||||
<% } %>
|
||||
</div>
|
@@ -1,23 +0,0 @@
|
||||
<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(', ')}) %>
|
||||
<% if (certificate_id) { %>
|
||||
<br><br>
|
||||
<%- i18n('ssl', '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>
|
@@ -1,32 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const App = require('../../main');
|
||||
const template = require('./delete.ejs');
|
||||
|
||||
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,280 +0,0 @@
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<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>
|
||||
</div>
|
||||
<div class="modal-body has-tabs">
|
||||
<div class="alert alert-danger mb-0 rounded-0" id="le-error-info" role="alert"></div>
|
||||
<form>
|
||||
<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> <%- i18n('all-hosts', 'details') %></a></li>
|
||||
<li role="presentation" class="nav-item"><a href="#locations" aria-controls="tab4" role="tab" data-toggle="tab" class="nav-link"><i class="fe fe-layers"></i> <%- i18n('all-hosts', 'locations') %></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>
|
||||
<li role="presentation" class="nav-item"><a href="#advanced" aria-controls="tab3" role="tab" data-toggle="tab" class="nav-link"><i class="fe fe-settings"></i> <%- i18n('all-hosts', 'advanced') %></a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
|
||||
<!-- Locations -->
|
||||
<div class="tab-pane" id="locations">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<button type="button" class="btn btn-secondary add_location"><%- i18n('locations', 'new_location') %></button>
|
||||
<div class="locations_container mt-3"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Details -->
|
||||
<div role="tabpanel" class="tab-pane active" id="details">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('all-hosts', 'domain-names') %> <span class="form-required">*</span></label>
|
||||
<input type="text" name="domain_names" class="form-control" id="input-domains" value="<%- domain_names.join(',') %>" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3 col-md-3">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('proxy-hosts', 'forward-scheme') %><span class="form-required">*</span></label>
|
||||
<select name="forward_scheme" class="form-control custom-select" placeholder="http">
|
||||
<option value="http" <%- forward_scheme === 'http' ? 'selected' : '' %>>http</option>
|
||||
<option value="https" <%- forward_scheme === 'https' ? 'selected' : '' %>>https</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-5 col-md-5">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('proxy-hosts', 'forward-host') %><span class="form-required">*</span></label>
|
||||
<input type="text" name="forward_host" class="form-control text-monospace" placeholder="" value="<%- forward_host %>" autocomplete="off" maxlength="255" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4 col-md-4">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('proxy-hosts', 'forward-port') %> <span class="form-required">*</span></label>
|
||||
<input name="forward_port" type="number" class="form-control text-monospace" placeholder="80" value="<%- forward_port %>" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="caching_enabled" value="1"<%- caching_enabled ? ' checked' : '' %>>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%- i18n('all-hosts', 'caching-enabled') %></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="block_exploits" value="1"<%- block_exploits ? ' checked' : '' %>>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%- i18n('all-hosts', 'block-exploits') %></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="allow_websocket_upgrade" value="1"<%- allow_websocket_upgrade ? ' checked' : '' %>>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%- i18n('proxy-hosts', 'allow-websocket-upgrade') %></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('proxy-hosts', 'access-list') %></label>
|
||||
<select name="access_list_id" class="form-control custom-select" placeholder="<%- i18n('access-lists', 'public') %>">
|
||||
<option selected value="0" data-data="{"id":0}" <%- access_list_id ? '' : 'selected' %>><%- i18n('access-lists', 'public') %></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SSL -->
|
||||
<div role="tabpanel" class="tab-pane" id="ssl-options">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('all-hosts', 'ssl-certificate') %></label>
|
||||
<select name="certificate_id" class="form-control custom-select" placeholder="<%- i18n('all-hosts', 'none') %>">
|
||||
<option selected value="0" data-data="{"id":0}" <%- certificate_id ? '' : 'selected' %>><%- i18n('all-hosts', 'none') %></option>
|
||||
<option selected value="new" data-data="{"id":"new"}"><%- i18n('all-hosts', 'new-cert') %></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="ssl_forced" value="1"<%- ssl_forced ? ' checked' : '' %><%- certificate_id ? '' : ' disabled' %>>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%- i18n('all-hosts', 'force-ssl') %></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="http2_support" value="1"<%- http2_support ? ' checked' : '' %><%- certificate_id ? '' : ' disabled' %>>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%- i18n('all-hosts', 'http2-support') %></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="hsts_enabled" value="1"<%- hsts_enabled ? ' checked' : '' %><%- certificate_id && ssl_forced ? '' : ' disabled' %>>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%- i18n('all-hosts', 'hsts-enabled') %> <a href="https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security" target="_blank"><i class="fe fe-help-circle"></i></a></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="hsts_subdomains" value="1"<%- hsts_subdomains ? ' checked' : '' %><%- certificate_id && ssl_forced && hsts_enabled ? '' : ' disabled' %>>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%- i18n('all-hosts', 'hsts-subdomains') %></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- DNS challenge -->
|
||||
<div class="col-sm-12 col-md-12 letsencrypt">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="custom-switch-input"
|
||||
name="meta[dns_challenge]"
|
||||
value="1"
|
||||
<%- getUseDnsChallenge() ? 'checked' : '' %>
|
||||
>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%= i18n('ssl', 'dns-challenge') %></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12 letsencrypt">
|
||||
<fieldset class="form-fieldset dns-challenge">
|
||||
<div class="text-red mb-4"><i class="fe fe-alert-triangle"></i> <%= i18n('ssl', 'certbot-warning') %></div>
|
||||
|
||||
<!-- Certbot DNS plugin selection -->
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('ssl', 'dns-provider') %> <span class="form-required">*</span></label>
|
||||
<select
|
||||
name="meta[dns_provider]"
|
||||
id="dns_provider"
|
||||
class="form-control custom-select"
|
||||
>
|
||||
<option
|
||||
value=""
|
||||
disabled
|
||||
hidden
|
||||
<%- getDnsProvider() === null ? 'selected' : '' %>
|
||||
>Please Choose...</option>
|
||||
<% _.each(dns_plugins, function(plugin_info, plugin_name){ %>
|
||||
<option
|
||||
value="<%- plugin_name %>"
|
||||
<%- getDnsProvider() === plugin_name ? 'selected' : '' %>
|
||||
><%- plugin_info.display_name %></option>
|
||||
<% }); %>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Certbot credentials file content -->
|
||||
<div class="row credentials-file-content">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('ssl', 'credentials-file-content') %> <span class="form-required">*</span></label>
|
||||
<textarea
|
||||
name="meta[dns_provider_credentials]"
|
||||
class="form-control text-monospace"
|
||||
id="dns_provider_credentials"
|
||||
><%- getDnsProviderCredentials() %></textarea>
|
||||
<div class="text-secondary small">
|
||||
<i class="fe fe-info"></i>
|
||||
<%= i18n('ssl', 'credentials-file-content-info') %>
|
||||
</div>
|
||||
<div class="text-red small">
|
||||
<i class="fe fe-alert-triangle"></i>
|
||||
<%= i18n('ssl', 'stored-as-plaintext-info') %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- DNS propagation delay -->
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group mb-0">
|
||||
<label class="form-label"><%- i18n('ssl', 'propagation-seconds') %></label>
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
name="meta[propagation_seconds]"
|
||||
class="form-control"
|
||||
id="propagation_seconds"
|
||||
value="<%- getPropagationSeconds() %>"
|
||||
>
|
||||
<div class="text-secondary small">
|
||||
<i class="fe fe-info"></i>
|
||||
<%= i18n('ssl', 'propagation-seconds-info') %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<!-- Lets encrypt -->
|
||||
<div class="col-sm-12 col-md-12 letsencrypt">
|
||||
<div class="form-group">
|
||||
<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 disabled>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12 letsencrypt">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="meta[letsencrypt_agree]" value="1" required disabled>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%= i18n('ssl', 'letsencrypt-agree', {url: 'https://letsencrypt.org/repository/'}) %> <span class="form-required">*</span></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Advanced -->
|
||||
<div role="tabpanel" class="tab-pane" id="advanced">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<p>Nginx variables available to you are:</p>
|
||||
<ul class="text-monospace">
|
||||
<li>$server # Host/IP</li>
|
||||
<li>$port # Port Number</li>
|
||||
<li>$forward_scheme # http or https</li>
|
||||
</ul>
|
||||
<div class="form-group mb-0">
|
||||
<label class="form-label"><%- i18n('all-hosts', 'advanced-config') %></label>
|
||||
<textarea name="advanced_config" rows="8" class="form-control text-monospace" placeholder="# <%- i18n('all-hosts', 'advanced-warning') %>"><%- advanced_config %></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</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-teal save"><%- i18n('str', 'save') %></button>
|
||||
</div>
|
||||
</div>
|
@@ -1,369 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const App = require('../../main');
|
||||
const ProxyHostModel = require('../../../models/proxy-host');
|
||||
const ProxyLocationModel = require('../../../models/proxy-host-location');
|
||||
const template = require('./form.ejs');
|
||||
const certListItemTemplate = require('../certificates-list-item.ejs');
|
||||
const accessListItemTemplate = require('./access-list-item.ejs');
|
||||
const CustomLocation = require('./location');
|
||||
const Helpers = require('../../../lib/helpers');
|
||||
const i18n = require('../../i18n');
|
||||
const dns_providers = require('../../../../../global/certbot-dns-plugins');
|
||||
|
||||
|
||||
require('jquery-serializejson');
|
||||
require('selectize');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
className: 'modal-dialog',
|
||||
|
||||
locationsCollection: new ProxyLocationModel.Collection(),
|
||||
|
||||
ui: {
|
||||
form: 'form',
|
||||
domain_names: 'input[name="domain_names"]',
|
||||
forward_host: 'input[name="forward_host"]',
|
||||
buttons: '.modal-footer button',
|
||||
cancel: 'button.cancel',
|
||||
save: 'button.save',
|
||||
add_location_btn: 'button.add_location',
|
||||
locations_container: '.locations_container',
|
||||
le_error_info: '#le-error-info',
|
||||
certificate_select: 'select[name="certificate_id"]',
|
||||
access_list_select: 'select[name="access_list_id"]',
|
||||
ssl_forced: 'input[name="ssl_forced"]',
|
||||
hsts_enabled: 'input[name="hsts_enabled"]',
|
||||
hsts_subdomains: 'input[name="hsts_subdomains"]',
|
||||
http2_support: 'input[name="http2_support"]',
|
||||
dns_challenge_switch: 'input[name="meta[dns_challenge]"]',
|
||||
dns_challenge_content: '.dns-challenge',
|
||||
dns_provider: 'select[name="meta[dns_provider]"]',
|
||||
credentials_file_content: '.credentials-file-content',
|
||||
dns_provider_credentials: 'textarea[name="meta[dns_provider_credentials]"]',
|
||||
propagation_seconds: 'input[name="meta[propagation_seconds]"]',
|
||||
forward_scheme: 'select[name="forward_scheme"]',
|
||||
letsencrypt: '.letsencrypt'
|
||||
},
|
||||
|
||||
regions: {
|
||||
locations_regions: '@ui.locations_container'
|
||||
},
|
||||
|
||||
events: {
|
||||
'change @ui.certificate_select': function () {
|
||||
let id = this.ui.certificate_select.val();
|
||||
if (id === 'new') {
|
||||
this.ui.letsencrypt.show().find('input').prop('disabled', false);
|
||||
this.ui.dns_challenge_content.hide();
|
||||
} else {
|
||||
this.ui.letsencrypt.hide().find('input').prop('disabled', true);
|
||||
}
|
||||
|
||||
let enabled = id === 'new' || parseInt(id, 10) > 0;
|
||||
|
||||
let inputs = this.ui.ssl_forced.add(this.ui.http2_support);
|
||||
inputs
|
||||
.prop('disabled', !enabled)
|
||||
.parents('.form-group')
|
||||
.css('opacity', enabled ? 1 : 0.5);
|
||||
|
||||
if (!enabled) {
|
||||
inputs.prop('checked', false);
|
||||
}
|
||||
|
||||
inputs.trigger('change');
|
||||
},
|
||||
|
||||
'change @ui.ssl_forced': function () {
|
||||
let checked = this.ui.ssl_forced.prop('checked');
|
||||
this.ui.hsts_enabled
|
||||
.prop('disabled', !checked)
|
||||
.parents('.form-group')
|
||||
.css('opacity', checked ? 1 : 0.5);
|
||||
|
||||
if (!checked) {
|
||||
this.ui.hsts_enabled.prop('checked', false);
|
||||
}
|
||||
|
||||
this.ui.hsts_enabled.trigger('change');
|
||||
},
|
||||
|
||||
'change @ui.hsts_enabled': function () {
|
||||
let checked = this.ui.hsts_enabled.prop('checked');
|
||||
this.ui.hsts_subdomains
|
||||
.prop('disabled', !checked)
|
||||
.parents('.form-group')
|
||||
.css('opacity', checked ? 1 : 0.5);
|
||||
|
||||
if (!checked) {
|
||||
this.ui.hsts_subdomains.prop('checked', false);
|
||||
}
|
||||
},
|
||||
|
||||
'change @ui.dns_challenge_switch': function () {
|
||||
const checked = this.ui.dns_challenge_switch.prop('checked');
|
||||
if (checked) {
|
||||
this.ui.dns_provider.prop('required', 'required');
|
||||
const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value;
|
||||
if(selected_provider != '' && dns_providers[selected_provider].credentials !== false){
|
||||
this.ui.dns_provider_credentials.prop('required', 'required');
|
||||
}
|
||||
this.ui.dns_challenge_content.show();
|
||||
} else {
|
||||
this.ui.dns_provider.prop('required', false);
|
||||
this.ui.dns_provider_credentials.prop('required', false);
|
||||
this.ui.dns_challenge_content.hide();
|
||||
}
|
||||
},
|
||||
|
||||
'change @ui.dns_provider': function () {
|
||||
const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value;
|
||||
if (selected_provider != '' && dns_providers[selected_provider].credentials !== false) {
|
||||
this.ui.dns_provider_credentials.prop('required', 'required');
|
||||
this.ui.dns_provider_credentials[0].value = dns_providers[selected_provider].credentials;
|
||||
this.ui.credentials_file_content.show();
|
||||
} else {
|
||||
this.ui.dns_provider_credentials.prop('required', false);
|
||||
this.ui.credentials_file_content.hide();
|
||||
}
|
||||
},
|
||||
|
||||
'click @ui.add_location_btn': function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
const model = new ProxyLocationModel.Model();
|
||||
this.locationsCollection.add(model);
|
||||
},
|
||||
|
||||
'click @ui.save': function (e) {
|
||||
e.preventDefault();
|
||||
this.ui.le_error_info.hide();
|
||||
|
||||
if (!this.ui.form[0].checkValidity()) {
|
||||
$('<input type="submit">').hide().appendTo(this.ui.form).click().remove();
|
||||
return;
|
||||
}
|
||||
|
||||
let view = this;
|
||||
let data = this.ui.form.serializeJSON();
|
||||
|
||||
// Add locations
|
||||
data.locations = [];
|
||||
this.locationsCollection.models.forEach((location) => {
|
||||
data.locations.push(location.toJSON());
|
||||
});
|
||||
|
||||
// Serialize collects path from custom locations
|
||||
// This field must be removed from root object
|
||||
delete data.path;
|
||||
|
||||
// Manipulate
|
||||
data.forward_port = parseInt(data.forward_port, 10);
|
||||
data.block_exploits = !!data.block_exploits;
|
||||
data.caching_enabled = !!data.caching_enabled;
|
||||
data.allow_websocket_upgrade = !!data.allow_websocket_upgrade;
|
||||
data.http2_support = !!data.http2_support;
|
||||
data.hsts_enabled = !!data.hsts_enabled;
|
||||
data.hsts_subdomains = !!data.hsts_subdomains;
|
||||
data.ssl_forced = !!data.ssl_forced;
|
||||
|
||||
if (typeof data.meta === 'undefined') data.meta = {};
|
||||
data.meta.letsencrypt_agree = data.meta.letsencrypt_agree == 1;
|
||||
data.meta.dns_challenge = data.meta.dns_challenge == 1;
|
||||
|
||||
if(!data.meta.dns_challenge){
|
||||
data.meta.dns_provider = undefined;
|
||||
data.meta.dns_provider_credentials = undefined;
|
||||
data.meta.propagation_seconds = undefined;
|
||||
} else {
|
||||
if(data.meta.propagation_seconds === '') data.meta.propagation_seconds = undefined;
|
||||
}
|
||||
|
||||
if (typeof data.domain_names === 'string' && data.domain_names) {
|
||||
data.domain_names = data.domain_names.split(',');
|
||||
}
|
||||
|
||||
// Check for any domain names containing wildcards, which are not allowed with letsencrypt
|
||||
if (data.certificate_id === 'new') {
|
||||
let domain_err = false;
|
||||
if (!data.meta.dns_challenge) {
|
||||
data.domain_names.map(function (name) {
|
||||
if (name.match(/\*/im)) {
|
||||
domain_err = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (domain_err) {
|
||||
alert(i18n('ssl', 'no-wildcard-without-dns'));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
data.certificate_id = parseInt(data.certificate_id, 10);
|
||||
}
|
||||
|
||||
let method = App.Api.Nginx.ProxyHosts.create;
|
||||
let is_new = true;
|
||||
|
||||
if (this.model.get('id')) {
|
||||
// edit
|
||||
is_new = false;
|
||||
method = App.Api.Nginx.ProxyHosts.update;
|
||||
data.id = this.model.get('id');
|
||||
}
|
||||
|
||||
this.ui.buttons.prop('disabled', true).addClass('btn-disabled');
|
||||
this.ui.save.addClass('btn-loading');
|
||||
|
||||
method(data)
|
||||
.then(result => {
|
||||
view.model.set(result);
|
||||
|
||||
App.UI.closeModal(function () {
|
||||
if (is_new) {
|
||||
App.Controller.showNginxProxy();
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
let more_info = '';
|
||||
if(err.code === 500 && err.debug){
|
||||
try{
|
||||
more_info = JSON.parse(err.debug).debug.stack.join("\n");
|
||||
} catch(e) {}
|
||||
}
|
||||
this.ui.le_error_info[0].innerHTML = `${err.message}${more_info !== '' ? `<pre class="mt-3">${more_info}</pre>`:''}`;
|
||||
this.ui.le_error_info.show();
|
||||
this.ui.le_error_info[0].scrollIntoView();
|
||||
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
||||
this.ui.save.removeClass('btn-loading');
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
templateContext: {
|
||||
getLetsencryptEmail: function () {
|
||||
return App.Cache.User.get('email');
|
||||
},
|
||||
getUseDnsChallenge: function () {
|
||||
return typeof this.meta.dns_challenge !== 'undefined' ? this.meta.dns_challenge : false;
|
||||
},
|
||||
getDnsProvider: function () {
|
||||
return typeof this.meta.dns_provider !== 'undefined' && this.meta.dns_provider != '' ? this.meta.dns_provider : null;
|
||||
},
|
||||
getDnsProviderCredentials: function () {
|
||||
return typeof this.meta.dns_provider_credentials !== 'undefined' ? this.meta.dns_provider_credentials : '';
|
||||
},
|
||||
getPropagationSeconds: function () {
|
||||
return typeof this.meta.propagation_seconds !== 'undefined' ? this.meta.propagation_seconds : '';
|
||||
},
|
||||
dns_plugins: dns_providers,
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
let view = this;
|
||||
|
||||
this.ui.ssl_forced.trigger('change');
|
||||
this.ui.hsts_enabled.trigger('change');
|
||||
|
||||
// Domain names
|
||||
this.ui.domain_names.selectize({
|
||||
delimiter: ',',
|
||||
persist: false,
|
||||
maxOptions: 15,
|
||||
create: function (input) {
|
||||
return {
|
||||
value: input,
|
||||
text: input
|
||||
};
|
||||
},
|
||||
createFilter: /^(?:\.)?(?:[^.*]+\.?)+[^.]$/
|
||||
});
|
||||
|
||||
// Access Lists
|
||||
this.ui.access_list_select.selectize({
|
||||
valueField: 'id',
|
||||
labelField: 'name',
|
||||
searchField: ['name'],
|
||||
create: false,
|
||||
preload: true,
|
||||
allowEmptyOption: true,
|
||||
render: {
|
||||
option: function (item) {
|
||||
item.i18n = App.i18n;
|
||||
item.formatDbDate = Helpers.formatDbDate;
|
||||
return accessListItemTemplate(item);
|
||||
}
|
||||
},
|
||||
load: function (query, callback) {
|
||||
App.Api.Nginx.AccessLists.getAll(['items', 'clients'])
|
||||
.then(rows => {
|
||||
callback(rows);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
callback();
|
||||
});
|
||||
},
|
||||
onLoad: function () {
|
||||
view.ui.access_list_select[0].selectize.setValue(view.model.get('access_list_id'));
|
||||
}
|
||||
});
|
||||
|
||||
// Certificates
|
||||
this.ui.le_error_info.hide();
|
||||
this.ui.dns_challenge_content.hide();
|
||||
this.ui.credentials_file_content.hide();
|
||||
this.ui.letsencrypt.hide();
|
||||
this.ui.certificate_select.selectize({
|
||||
valueField: 'id',
|
||||
labelField: 'nice_name',
|
||||
searchField: ['nice_name', 'domain_names'],
|
||||
create: false,
|
||||
preload: true,
|
||||
allowEmptyOption: true,
|
||||
render: {
|
||||
option: function (item) {
|
||||
item.i18n = App.i18n;
|
||||
item.formatDbDate = Helpers.formatDbDate;
|
||||
return certListItemTemplate(item);
|
||||
}
|
||||
},
|
||||
load: function (query, callback) {
|
||||
App.Api.Nginx.Certificates.getAll()
|
||||
.then(rows => {
|
||||
callback(rows);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
callback();
|
||||
});
|
||||
},
|
||||
onLoad: function () {
|
||||
view.ui.certificate_select[0].selectize.setValue(view.model.get('certificate_id'));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
if (typeof options.model === 'undefined' || !options.model) {
|
||||
this.model = new ProxyHostModel.Model();
|
||||
}
|
||||
|
||||
this.locationsCollection = new ProxyLocationModel.Collection();
|
||||
|
||||
// Custom locations
|
||||
this.showChildView('locations_regions', new CustomLocation.LocationCollectionView({
|
||||
collection: this.locationsCollection
|
||||
}));
|
||||
|
||||
// Check wether there are any location defined
|
||||
if (options.model && Array.isArray(options.model.attributes.locations)) {
|
||||
options.model.attributes.locations.forEach((location) => {
|
||||
let m = new ProxyLocationModel.Model(location);
|
||||
this.locationsCollection.add(m);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
@@ -1,60 +0,0 @@
|
||||
<td class="text-center">
|
||||
<div class="avatar d-block" style="background-image: url(<%- owner.avatar || '/images/default-avatar.jpg' %>)" title="Owned by <%- owner.name %>">
|
||||
<span class="avatar-status <%- owner.is_disabled ? 'bg-red' : 'bg-green' %>"></span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="wrap">
|
||||
<% domain_names.map(function(host) {
|
||||
if (host.indexOf('*') === -1) {
|
||||
%>
|
||||
<span class="tag host-link hover-green" rel="http<%- certificate_id ? 's' : '' %>://<%- host %>"><%- host %></span>
|
||||
<%
|
||||
} else {
|
||||
%>
|
||||
<span class="tag"><%- host %></span>
|
||||
<%
|
||||
}
|
||||
});
|
||||
%>
|
||||
</div>
|
||||
<div class="small text-muted">
|
||||
<%- i18n('str', 'created-on', {date: formatDbDate(created_on, 'Do MMMM YYYY')}) %>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="text-monospace"><%- forward_scheme %>://<%- forward_host %>:<%- forward_port %></div>
|
||||
</td>
|
||||
<td>
|
||||
<div><%- certificate && certificate_id ? i18n('ssl', certificate.provider) : i18n('ssl', 'none') %></div>
|
||||
</td>
|
||||
<td>
|
||||
<div><%- access_list_id ? access_list.name : i18n('str', 'public') %></div>
|
||||
</td>
|
||||
<td>
|
||||
<%
|
||||
var o = isOnline();
|
||||
if (!enabled) { %>
|
||||
<span class="status-icon bg-warning"></span> <%- i18n('str', 'disabled') %>
|
||||
<% } else if (o === true) { %>
|
||||
<span class="status-icon bg-success"></span> <%- i18n('str', 'online') %>
|
||||
<% } else if (o === false) { %>
|
||||
<span title="<%- getOfflineError() %>"><span class="status-icon bg-danger"></span> <%- i18n('str', 'offline') %></span>
|
||||
<% } else { %>
|
||||
<span class="status-icon bg-warning"></span> <%- i18n('str', 'unknown') %>
|
||||
<% } %>
|
||||
</td>
|
||||
<% if (canManage) { %>
|
||||
<td class="text-right">
|
||||
<div class="item-action dropdown">
|
||||
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<span class="dropdown-header"><%- i18n('audit-log', 'proxy-host') %> #<%- id %></span>
|
||||
<a href="#" class="edit dropdown-item"><i class="dropdown-icon fe fe-edit"></i> <%- i18n('str', 'edit') %></a>
|
||||
<a href="#" class="able dropdown-item"><i class="dropdown-icon fe fe-power"></i> <%- i18n('str', enabled ? 'disable' : 'enable') %></a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a href="#" class="delete dropdown-item"><i class="dropdown-icon fe fe-trash-2"></i> <%- i18n('str', 'delete') %></a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<% } %>
|
@@ -1,61 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const App = require('../../../main');
|
||||
const template = require('./item.ejs');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
tagName: 'tr',
|
||||
|
||||
ui: {
|
||||
able: 'a.able',
|
||||
edit: 'a.edit',
|
||||
delete: 'a.delete',
|
||||
host_link: '.host-link'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click @ui.able': function (e) {
|
||||
e.preventDefault();
|
||||
let id = this.model.get('id');
|
||||
App.Api.Nginx.ProxyHosts[this.model.get('enabled') ? 'disable' : 'enable'](id)
|
||||
.then(() => {
|
||||
return App.Api.Nginx.ProxyHosts.get(id)
|
||||
.then(row => {
|
||||
this.model.set(row);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
'click @ui.edit': function (e) {
|
||||
e.preventDefault();
|
||||
App.Controller.showNginxProxyForm(this.model);
|
||||
},
|
||||
|
||||
'click @ui.delete': function (e) {
|
||||
e.preventDefault();
|
||||
App.Controller.showNginxProxyDeleteConfirm(this.model);
|
||||
},
|
||||
|
||||
'click @ui.host_link': function (e) {
|
||||
e.preventDefault();
|
||||
let win = window.open($(e.currentTarget).attr('rel'), '_blank');
|
||||
win.focus();
|
||||
}
|
||||
},
|
||||
|
||||
templateContext: {
|
||||
canManage: App.Cache.User.canManage('proxy_hosts'),
|
||||
|
||||
isOnline: function () {
|
||||
return typeof this.meta.nginx_online === 'undefined' ? null : this.meta.nginx_online;
|
||||
},
|
||||
|
||||
getOfflineError: function () {
|
||||
return this.meta.nginx_err || '';
|
||||
}
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
this.listenTo(this.model, 'change', this.render);
|
||||
}
|
||||
});
|
@@ -1,14 +0,0 @@
|
||||
<thead>
|
||||
<th width="30"> </th>
|
||||
<th><%- i18n('str', 'source') %></th>
|
||||
<th><%- i18n('str', 'destination') %></th>
|
||||
<th><%- i18n('str', 'ssl') %></th>
|
||||
<th><%- i18n('str', 'access') %></th>
|
||||
<th><%- i18n('str', 'status') %></th>
|
||||
<% if (canManage) { %>
|
||||
<th> </th>
|
||||
<% } %>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- items -->
|
||||
</tbody>
|
@@ -1,32 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const App = require('../../../main');
|
||||
const ItemView = require('./item');
|
||||
const template = require('./main.ejs');
|
||||
|
||||
const TableBody = Mn.CollectionView.extend({
|
||||
tagName: 'tbody',
|
||||
childView: ItemView
|
||||
});
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
tagName: 'table',
|
||||
className: 'table table-hover table-outline table-vcenter card-table',
|
||||
template: template,
|
||||
|
||||
regions: {
|
||||
body: {
|
||||
el: 'tbody',
|
||||
replaceElement: true
|
||||
}
|
||||
},
|
||||
|
||||
templateContext: {
|
||||
canManage: App.Cache.User.canManage('proxy_hosts')
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
this.showChildView('body', new TableBody({
|
||||
collection: this.collection
|
||||
}));
|
||||
}
|
||||
});
|
@@ -1,64 +0,0 @@
|
||||
<div class="location-block card">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('locations', 'location_label') %> <span class="form-required">*</span></label>
|
||||
<div class="row gutter-xs">
|
||||
<div class="col">
|
||||
<div class="input-group">
|
||||
<span class="input-group-prepend">
|
||||
<span class="input-group-text">location</span>
|
||||
</span>
|
||||
<input type="text" name="path" class="form-control model" value="<%- path %>" placeholder="<%- i18n('locations', 'path') %>" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="selectgroup">
|
||||
<label class="selectgroup-item">
|
||||
<input type="checkbox" class="selectgroup-input">
|
||||
<span class="selectgroup-button">
|
||||
<i class="fe fe-settings"></i>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3 col-md-3">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('proxy-hosts', 'forward-scheme') %><span class="form-required">*</span></label>
|
||||
<select name="forward_scheme" class="form-control custom-select model" placeholder="http">
|
||||
<option value="http" <%- forward_scheme === 'http' ? 'selected' : '' %>>http</option>
|
||||
<option value="https" <%- forward_scheme === 'https' ? 'selected' : '' %>>https</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-5 col-md-5">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('proxy-hosts', 'forward-host') %><span class="form-required">*</span></label>
|
||||
<input type="text" name="forward_host" class="form-control text-monospace model" placeholder="" value="<%- forward_host %>" autocomplete="off" maxlength="200" required>
|
||||
<span style="font-size: 9px;"><%- i18n('proxy-hosts', 'custom-forward-host-help') %></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4 col-md-4">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('proxy-hosts', 'forward-port') %> <span class="form-required">*</span></label>
|
||||
<input name="forward_port" type="number" class="form-control text-monospace model" placeholder="80" value="<%- forward_port %>" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row config">
|
||||
<div class="col-md-12">
|
||||
<div class="form-group">
|
||||
<textarea name="advanced_config" rows="8" class="form-control text-monospace model" placeholder="# <%- i18n('all-hosts', 'advanced-warning') %>"><%- advanced_config %></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="#" class="card-link location-delete">
|
||||
<i class="fa fa-trash"></i> <%- i18n('locations', 'delete') %>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
@@ -1,54 +0,0 @@
|
||||
const locationItemTemplate = require('./location-item.ejs');
|
||||
const Mn = require('backbone.marionette');
|
||||
const App = require('../../main');
|
||||
|
||||
const LocationView = Mn.View.extend({
|
||||
template: locationItemTemplate,
|
||||
className: 'location_block',
|
||||
|
||||
ui: {
|
||||
toggle: 'input[type="checkbox"]',
|
||||
config: '.config',
|
||||
delete: '.location-delete'
|
||||
},
|
||||
|
||||
events: {
|
||||
'change @ui.toggle': function(el) {
|
||||
if (el.target.checked) {
|
||||
this.ui.config.show();
|
||||
} else {
|
||||
this.ui.config.hide();
|
||||
}
|
||||
},
|
||||
|
||||
'change .model': function (e) {
|
||||
const map = {};
|
||||
map[e.target.name] = e.target.value;
|
||||
this.model.set(map);
|
||||
},
|
||||
|
||||
'click @ui.delete': function () {
|
||||
this.model.destroy();
|
||||
}
|
||||
},
|
||||
|
||||
onRender: function() {
|
||||
$(this.ui.config).hide();
|
||||
},
|
||||
|
||||
templateContext: function() {
|
||||
return {
|
||||
i18n: App.i18n
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const LocationCollectionView = Mn.CollectionView.extend({
|
||||
className: 'locations_container',
|
||||
childView: LocationView
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
LocationCollectionView,
|
||||
LocationView
|
||||
}
|
@@ -1,20 +0,0 @@
|
||||
<div class="card">
|
||||
<div class="card-status bg-success"></div>
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><%- i18n('proxy-hosts', 'title') %></h3>
|
||||
<div class="card-options">
|
||||
<a href="#" class="btn btn-outline-secondary btn-sm ml-2 help"><i class="fe fe-help-circle"></i></a>
|
||||
<% if (showAddButton) { %>
|
||||
<a href="#" class="btn btn-outline-success btn-sm ml-2 add-item"><%- i18n('proxy-hosts', 'add') %></a>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body no-padding min-100">
|
||||
<div class="dimmer active">
|
||||
<div class="loader"></div>
|
||||
<div class="dimmer-content list-region">
|
||||
<!-- List Region -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@@ -1,81 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const App = require('../../main');
|
||||
const ProxyHostModel = require('../../../models/proxy-host');
|
||||
const ListView = require('./list/main');
|
||||
const ErrorView = require('../../error/main');
|
||||
const EmptyView = require('../../empty/main');
|
||||
const template = require('./main.ejs');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
id: 'nginx-proxy',
|
||||
template: template,
|
||||
|
||||
ui: {
|
||||
list_region: '.list-region',
|
||||
add: '.add-item',
|
||||
help: '.help',
|
||||
dimmer: '.dimmer'
|
||||
},
|
||||
|
||||
regions: {
|
||||
list_region: '@ui.list_region'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click @ui.add': function (e) {
|
||||
e.preventDefault();
|
||||
App.Controller.showNginxProxyForm();
|
||||
},
|
||||
|
||||
'click @ui.help': function (e) {
|
||||
e.preventDefault();
|
||||
App.Controller.showHelp(App.i18n('proxy-hosts', 'help-title'), App.i18n('proxy-hosts', 'help-content'));
|
||||
}
|
||||
},
|
||||
|
||||
templateContext: {
|
||||
showAddButton: App.Cache.User.canManage('proxy_hosts')
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
let view = this;
|
||||
|
||||
App.Api.Nginx.ProxyHosts.getAll(['owner', 'access_list', 'certificate'])
|
||||
.then(response => {
|
||||
if (!view.isDestroyed()) {
|
||||
if (response && response.length) {
|
||||
view.showChildView('list_region', new ListView({
|
||||
collection: new ProxyHostModel.Collection(response)
|
||||
}));
|
||||
} else {
|
||||
let manage = App.Cache.User.canManage('proxy_hosts');
|
||||
|
||||
view.showChildView('list_region', new EmptyView({
|
||||
title: App.i18n('proxy-hosts', 'empty'),
|
||||
subtitle: App.i18n('all-hosts', 'empty-subtitle', {manage: manage}),
|
||||
link: manage ? App.i18n('proxy-hosts', 'add') : null,
|
||||
btn_color: 'success',
|
||||
permission: 'proxy_hosts',
|
||||
action: function () {
|
||||
App.Controller.showNginxProxyForm();
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
view.showChildView('list_region', new ErrorView({
|
||||
code: err.code,
|
||||
message: err.message,
|
||||
retry: function () {
|
||||
App.Controller.showNginxProxy();
|
||||
}
|
||||
}));
|
||||
|
||||
console.error(err);
|
||||
})
|
||||
.then(() => {
|
||||
view.ui.dimmer.removeClass('active');
|
||||
});
|
||||
}
|
||||
});
|
@@ -1,23 +0,0 @@
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><%- i18n('redirection-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('redirection-hosts', 'delete-confirm', {domains: domain_names.join(', ')}) %>
|
||||
<% if (certificate_id) { %>
|
||||
<br><br>
|
||||
<%- i18n('ssl', '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>
|
@@ -1,32 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const App = require('../../main');
|
||||
const template = require('./delete.ejs');
|
||||
|
||||
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.RedirectionHosts.delete(this.model.get('id'))
|
||||
.then(() => {
|
||||
App.Controller.showNginxRedirection();
|
||||
App.UI.closeModal();
|
||||
})
|
||||
.catch(err => {
|
||||
alert(err.message);
|
||||
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
@@ -1,253 +0,0 @@
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><%- i18n('redirection-hosts', 'form-title', {id: id}) %></h5>
|
||||
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
|
||||
</div>
|
||||
<div class="modal-body has-tabs">
|
||||
<div class="alert alert-danger mb-0 rounded-0" id="le-error-info" role="alert"></div>
|
||||
<form>
|
||||
<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> <%- 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> <%- i18n('str', 'ssl') %></a></li>
|
||||
<li role="presentation" class="nav-item"><a href="#advanced" aria-controls="tab3" role="tab" data-toggle="tab" class="nav-link"><i class="fe fe-settings"></i> <%- i18n('all-hosts', 'advanced') %></a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<!-- Details -->
|
||||
<div role="tabpanel" class="tab-pane active" id="details">
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('all-hosts', 'domain-names') %> <span class="form-required">*</span></label>
|
||||
<input type="text" name="domain_names" class="form-control" id="input-domains" value="<%- domain_names.join(',') %>" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3 col-md-3">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('redirection-hosts', 'forward-scheme') %><span class="form-required">*</span></label>
|
||||
<select name="forward_scheme" class="form-control custom-select" placeholder="$scheme">
|
||||
<option value="$scheme" <%- forward_scheme === '$scheme' ? 'selected' : '' %>>auto</option>
|
||||
<option value="http" <%- forward_scheme === 'http' ? 'selected' : '' %>>http</option>
|
||||
<option value="https" <%- forward_scheme === 'https' ? 'selected' : '' %>>https</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-9 col-md-9">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('redirection-hosts', 'forward-domain') %><span class="form-required">*</span></label>
|
||||
<input type="text" name="forward_domain_name" class="form-control text-monospace" placeholder="" value="<%- forward_domain_name %>" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('redirection-hosts', 'forward-http-status-code') %><span class="form-required">*</span></label>
|
||||
<select name="forward_http_code" class="form-control custom-select" placeholder="301">
|
||||
<option value="300" <%- forward_http_code == '300' ? 'selected' : '' %>>300 Multiple choices</option>
|
||||
<option value="301" <%- forward_http_code == '301' ? 'selected' : '' %>>301 Moved permanently</option>
|
||||
<option value="302" <%- forward_http_code == '302' ? 'selected' : '' %>>302 Found</option>
|
||||
<option value="303" <%- forward_http_code == '303' ? 'selected' : '' %>>303 See other</option>
|
||||
<option value="307" <%- forward_http_code == '307' ? 'selected' : '' %>>307 Temporary redirect</option>
|
||||
<option value="308" <%- forward_http_code == '308' ? 'selected' : '' %>>308 Permanent redirect</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="preserve_path" value="1"<%- preserve_path ? ' checked' : '' %>>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%- i18n('redirection-hosts', 'preserve-path') %></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="block_exploits" value="1"<%- block_exploits ? ' checked' : '' %>>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%- i18n('all-hosts', 'block-exploits') %></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SSL -->
|
||||
<div role="tabpanel" class="tab-pane" id="ssl-options">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('all-hosts', 'ssl-certificate') %></label>
|
||||
<select name="certificate_id" class="form-control custom-select" placeholder="<%- i18n('all-hosts', 'none') %>">
|
||||
<option selected value="0" data-data="{"id":0}" <%- certificate_id ? '' : 'selected' %>><%- i18n('all-hosts', 'none') %></option>
|
||||
<option selected value="new" data-data="{"id":"new"}"><%- i18n('all-hosts', 'new-cert') %></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="ssl_forced" value="1"<%- ssl_forced ? ' checked' : '' %><%- certificate_id ? '' : ' disabled' %>>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%- i18n('all-hosts', 'force-ssl') %></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="http2_support" value="1"<%- http2_support ? ' checked' : '' %><%- certificate_id ? '' : ' disabled' %>>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%- i18n('all-hosts', 'http2-support') %></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="hsts_enabled" value="1"<%- hsts_enabled ? ' checked' : '' %><%- certificate_id && ssl_forced ? '' : ' disabled' %>>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%- i18n('all-hosts', 'hsts-enabled') %> <a href="https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security" target="_blank"><i class="fe fe-help-circle"></i></a></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="hsts_subdomains" value="1"<%- hsts_subdomains ? ' checked' : '' %><%- certificate_id && ssl_forced && hsts_enabled ? '' : ' disabled' %>>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%- i18n('all-hosts', 'hsts-subdomains') %></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- DNS challenge -->
|
||||
<div class="col-sm-12 col-md-12 letsencrypt">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="custom-switch-input"
|
||||
name="meta[dns_challenge]"
|
||||
value="1"
|
||||
<%- getUseDnsChallenge() ? 'checked' : '' %>
|
||||
>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%= i18n('ssl', 'dns-challenge') %></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12 letsencrypt">
|
||||
<fieldset class="form-fieldset dns-challenge">
|
||||
<div class="text-red mb-4"><i class="fe fe-alert-triangle"></i> <%= i18n('ssl', 'certbot-warning') %></div>
|
||||
|
||||
<!-- Certbot DNS plugin selection -->
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('ssl', 'dns-provider') %> <span class="form-required">*</span></label>
|
||||
<select
|
||||
name="meta[dns_provider]"
|
||||
id="dns_provider"
|
||||
class="form-control custom-select"
|
||||
>
|
||||
<option
|
||||
value=""
|
||||
disabled
|
||||
hidden
|
||||
<%- getDnsProvider() === null ? 'selected' : '' %>
|
||||
>Please Choose...</option>
|
||||
<% _.each(dns_plugins, function(plugin_info, plugin_name){ %>
|
||||
<option
|
||||
value="<%- plugin_name %>"
|
||||
<%- getDnsProvider() === plugin_name ? 'selected' : '' %>
|
||||
><%- plugin_info.display_name %></option>
|
||||
<% }); %>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Certbot credentials file content -->
|
||||
<div class="row credentials-file-content">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('ssl', 'credentials-file-content') %> <span class="form-required">*</span></label>
|
||||
<textarea
|
||||
name="meta[dns_provider_credentials]"
|
||||
class="form-control text-monospace"
|
||||
id="dns_provider_credentials"
|
||||
><%- getDnsProviderCredentials() %></textarea>
|
||||
<div class="text-secondary small">
|
||||
<i class="fe fe-info"></i>
|
||||
<%= i18n('ssl', 'credentials-file-content-info') %>
|
||||
</div>
|
||||
<div class="text-red small">
|
||||
<i class="fe fe-alert-triangle"></i>
|
||||
<%= i18n('ssl', 'stored-as-plaintext-info') %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- DNS propagation delay -->
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group mb-0">
|
||||
<label class="form-label"><%- i18n('ssl', 'propagation-seconds') %></label>
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
name="meta[propagation_seconds]"
|
||||
class="form-control"
|
||||
id="propagation_seconds"
|
||||
value="<%- getPropagationSeconds() %>"
|
||||
>
|
||||
<div class="text-secondary small">
|
||||
<i class="fe fe-info"></i>
|
||||
<%= i18n('ssl', 'propagation-seconds-info') %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<!-- Lets encrypt -->
|
||||
<div class="col-sm-12 col-md-12 letsencrypt">
|
||||
<div class="form-group">
|
||||
<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 disabled>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12 letsencrypt">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="meta[letsencrypt_agree]" value="1" required disabled>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%= i18n('ssl', 'letsencrypt-agree', {url: 'https://letsencrypt.org/repository/'}) %> <span class="form-required">*</span></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Advanced -->
|
||||
<div role="tabpanel" class="tab-pane" id="advanced">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="form-group mb-0">
|
||||
<label class="form-label"><%- i18n('all-hosts', 'advanced-config') %></label>
|
||||
<textarea name="advanced_config" rows="8" class="form-control text-monospace" placeholder="# <%- i18n('all-hosts', 'advanced-warning') %>"><%- advanced_config %></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</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-teal save"><%- i18n('str', 'save') %></button>
|
||||
</div>
|
||||
</div>
|
@@ -1,288 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const App = require('../../main');
|
||||
const RedirectionHostModel = require('../../../models/redirection-host');
|
||||
const template = require('./form.ejs');
|
||||
const certListItemTemplate = require('../certificates-list-item.ejs');
|
||||
const Helpers = require('../../../lib/helpers');
|
||||
const i18n = require('../../i18n');
|
||||
const dns_providers = require('../../../../../global/certbot-dns-plugins');
|
||||
|
||||
|
||||
require('jquery-serializejson');
|
||||
require('selectize');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
className: 'modal-dialog',
|
||||
|
||||
ui: {
|
||||
form: 'form',
|
||||
domain_names: 'input[name="domain_names"]',
|
||||
buttons: '.modal-footer button',
|
||||
cancel: 'button.cancel',
|
||||
save: 'button.save',
|
||||
le_error_info: '#le-error-info',
|
||||
certificate_select: 'select[name="certificate_id"]',
|
||||
ssl_forced: 'input[name="ssl_forced"]',
|
||||
hsts_enabled: 'input[name="hsts_enabled"]',
|
||||
hsts_subdomains: 'input[name="hsts_subdomains"]',
|
||||
http2_support: 'input[name="http2_support"]',
|
||||
dns_challenge_switch: 'input[name="meta[dns_challenge]"]',
|
||||
dns_challenge_content: '.dns-challenge',
|
||||
dns_provider: 'select[name="meta[dns_provider]"]',
|
||||
credentials_file_content: '.credentials-file-content',
|
||||
dns_provider_credentials: 'textarea[name="meta[dns_provider_credentials]"]',
|
||||
propagation_seconds: 'input[name="meta[propagation_seconds]"]',
|
||||
letsencrypt: '.letsencrypt'
|
||||
},
|
||||
|
||||
events: {
|
||||
'change @ui.certificate_select': function () {
|
||||
let id = this.ui.certificate_select.val();
|
||||
if (id === 'new') {
|
||||
this.ui.letsencrypt.show().find('input').prop('disabled', false);
|
||||
this.ui.dns_challenge_content.hide();
|
||||
} else {
|
||||
this.ui.letsencrypt.hide().find('input').prop('disabled', true);
|
||||
}
|
||||
|
||||
let enabled = id === 'new' || parseInt(id, 10) > 0;
|
||||
|
||||
let inputs = this.ui.ssl_forced.add(this.ui.http2_support);
|
||||
inputs
|
||||
.prop('disabled', !enabled)
|
||||
.parents('.form-group')
|
||||
.css('opacity', enabled ? 1 : 0.5);
|
||||
|
||||
if (!enabled) {
|
||||
inputs.prop('checked', false);
|
||||
}
|
||||
|
||||
inputs.trigger('change');
|
||||
},
|
||||
|
||||
'change @ui.ssl_forced': function () {
|
||||
let checked = this.ui.ssl_forced.prop('checked');
|
||||
this.ui.hsts_enabled
|
||||
.prop('disabled', !checked)
|
||||
.parents('.form-group')
|
||||
.css('opacity', checked ? 1 : 0.5);
|
||||
|
||||
if (!checked) {
|
||||
this.ui.hsts_enabled.prop('checked', false);
|
||||
}
|
||||
|
||||
this.ui.hsts_enabled.trigger('change');
|
||||
},
|
||||
|
||||
'change @ui.hsts_enabled': function () {
|
||||
let checked = this.ui.hsts_enabled.prop('checked');
|
||||
this.ui.hsts_subdomains
|
||||
.prop('disabled', !checked)
|
||||
.parents('.form-group')
|
||||
.css('opacity', checked ? 1 : 0.5);
|
||||
|
||||
if (!checked) {
|
||||
this.ui.hsts_subdomains.prop('checked', false);
|
||||
}
|
||||
},
|
||||
|
||||
'change @ui.dns_challenge_switch': function () {
|
||||
const checked = this.ui.dns_challenge_switch.prop('checked');
|
||||
if (checked) {
|
||||
this.ui.dns_provider.prop('required', 'required');
|
||||
const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value;
|
||||
if(selected_provider != '' && dns_providers[selected_provider].credentials !== false){
|
||||
this.ui.dns_provider_credentials.prop('required', 'required');
|
||||
}
|
||||
this.ui.dns_challenge_content.show();
|
||||
} else {
|
||||
this.ui.dns_provider.prop('required', false);
|
||||
this.ui.dns_provider_credentials.prop('required', false);
|
||||
this.ui.dns_challenge_content.hide();
|
||||
}
|
||||
},
|
||||
|
||||
'change @ui.dns_provider': function () {
|
||||
const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value;
|
||||
if (selected_provider != '' && dns_providers[selected_provider].credentials !== false) {
|
||||
this.ui.dns_provider_credentials.prop('required', 'required');
|
||||
this.ui.dns_provider_credentials[0].value = dns_providers[selected_provider].credentials;
|
||||
this.ui.credentials_file_content.show();
|
||||
} else {
|
||||
this.ui.dns_provider_credentials.prop('required', false);
|
||||
this.ui.credentials_file_content.hide();
|
||||
}
|
||||
},
|
||||
|
||||
'click @ui.save': function (e) {
|
||||
e.preventDefault();
|
||||
this.ui.le_error_info.hide();
|
||||
|
||||
if (!this.ui.form[0].checkValidity()) {
|
||||
$('<input type="submit">').hide().appendTo(this.ui.form).click().remove();
|
||||
return;
|
||||
}
|
||||
|
||||
let view = this;
|
||||
let data = this.ui.form.serializeJSON();
|
||||
|
||||
// Manipulate
|
||||
data.block_exploits = !!data.block_exploits;
|
||||
data.preserve_path = !!data.preserve_path;
|
||||
data.http2_support = !!data.http2_support;
|
||||
data.hsts_enabled = !!data.hsts_enabled;
|
||||
data.hsts_subdomains = !!data.hsts_subdomains;
|
||||
data.ssl_forced = !!data.ssl_forced;
|
||||
|
||||
if (typeof data.meta === 'undefined') data.meta = {};
|
||||
data.meta.letsencrypt_agree = data.meta.letsencrypt_agree == 1;
|
||||
data.meta.dns_challenge = data.meta.dns_challenge == 1;
|
||||
|
||||
if(!data.meta.dns_challenge){
|
||||
data.meta.dns_provider = undefined;
|
||||
data.meta.dns_provider_credentials = undefined;
|
||||
data.meta.propagation_seconds = undefined;
|
||||
} else {
|
||||
if(data.meta.propagation_seconds === '') data.meta.propagation_seconds = undefined;
|
||||
}
|
||||
|
||||
if (typeof data.domain_names === 'string' && data.domain_names) {
|
||||
data.domain_names = data.domain_names.split(',');
|
||||
}
|
||||
|
||||
// Check for any domain names containing wildcards, which are not allowed with letsencrypt
|
||||
if (data.certificate_id === 'new') {
|
||||
let domain_err = false;
|
||||
if (!data.meta.dns_challenge) {
|
||||
data.domain_names.map(function (name) {
|
||||
if (name.match(/\*/im)) {
|
||||
domain_err = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (domain_err) {
|
||||
alert(i18n('ssl', 'no-wildcard-without-dns'));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
data.certificate_id = parseInt(data.certificate_id, 10);
|
||||
}
|
||||
|
||||
let method = App.Api.Nginx.RedirectionHosts.create;
|
||||
let is_new = true;
|
||||
|
||||
if (this.model.get('id')) {
|
||||
// edit
|
||||
is_new = false;
|
||||
method = App.Api.Nginx.RedirectionHosts.update;
|
||||
data.id = this.model.get('id');
|
||||
}
|
||||
|
||||
this.ui.buttons.prop('disabled', true).addClass('btn-disabled');
|
||||
this.ui.save.addClass('btn-loading');
|
||||
|
||||
method(data)
|
||||
.then(result => {
|
||||
view.model.set(result);
|
||||
|
||||
App.UI.closeModal(function () {
|
||||
if (is_new) {
|
||||
App.Controller.showNginxRedirection();
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
let more_info = '';
|
||||
if(err.code === 500 && err.debug){
|
||||
try{
|
||||
more_info = JSON.parse(err.debug).debug.stack.join("\n");
|
||||
} catch(e) {}
|
||||
}
|
||||
this.ui.le_error_info[0].innerHTML = `${err.message}${more_info !== '' ? `<pre class="mt-3">${more_info}</pre>`:''}`;
|
||||
this.ui.le_error_info.show();
|
||||
this.ui.le_error_info[0].scrollIntoView();
|
||||
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
||||
this.ui.save.removeClass('btn-loading');
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
templateContext: {
|
||||
getLetsencryptEmail: function () {
|
||||
return App.Cache.User.get('email');
|
||||
},
|
||||
getUseDnsChallenge: function () {
|
||||
return typeof this.meta.dns_challenge !== 'undefined' ? this.meta.dns_challenge : false;
|
||||
},
|
||||
getDnsProvider: function () {
|
||||
return typeof this.meta.dns_provider !== 'undefined' && this.meta.dns_provider != '' ? this.meta.dns_provider : null;
|
||||
},
|
||||
getDnsProviderCredentials: function () {
|
||||
return typeof this.meta.dns_provider_credentials !== 'undefined' ? this.meta.dns_provider_credentials : '';
|
||||
},
|
||||
getPropagationSeconds: function () {
|
||||
return typeof this.meta.propagation_seconds !== 'undefined' ? this.meta.propagation_seconds : '';
|
||||
},
|
||||
dns_plugins: dns_providers,
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
let view = this;
|
||||
|
||||
// Domain names
|
||||
this.ui.domain_names.selectize({
|
||||
delimiter: ',',
|
||||
persist: false,
|
||||
maxOptions: 15,
|
||||
create: function (input) {
|
||||
return {
|
||||
value: input,
|
||||
text: input
|
||||
};
|
||||
},
|
||||
createFilter: /^(?:\*\.)?(?:[^.*]+\.?)+[^.]$/
|
||||
});
|
||||
|
||||
// Certificates
|
||||
this.ui.le_error_info.hide();
|
||||
this.ui.dns_challenge_content.hide();
|
||||
this.ui.credentials_file_content.hide();
|
||||
this.ui.letsencrypt.hide();
|
||||
this.ui.certificate_select.selectize({
|
||||
valueField: 'id',
|
||||
labelField: 'nice_name',
|
||||
searchField: ['nice_name', 'domain_names'],
|
||||
create: false,
|
||||
preload: true,
|
||||
allowEmptyOption: true,
|
||||
render: {
|
||||
option: function (item) {
|
||||
item.i18n = App.i18n;
|
||||
item.formatDbDate = Helpers.formatDbDate;
|
||||
return certListItemTemplate(item);
|
||||
}
|
||||
},
|
||||
load: function (query, callback) {
|
||||
App.Api.Nginx.Certificates.getAll()
|
||||
.then(rows => {
|
||||
callback(rows);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
callback();
|
||||
});
|
||||
},
|
||||
onLoad: function () {
|
||||
view.ui.certificate_select[0].selectize.setValue(view.model.get('certificate_id'));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
if (typeof options.model === 'undefined' || !options.model) {
|
||||
this.model = new RedirectionHostModel.Model();
|
||||
}
|
||||
}
|
||||
});
|
@@ -1,63 +0,0 @@
|
||||
<td class="text-center">
|
||||
<div class="avatar d-block" style="background-image: url(<%- owner.avatar || '/images/default-avatar.jpg' %>)" title="Owned by <%- owner.name %>">
|
||||
<span class="avatar-status <%- owner.is_disabled ? 'bg-red' : 'bg-green' %>"></span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>
|
||||
<% domain_names.map(function(host) {
|
||||
if (host.indexOf('*') === -1) {
|
||||
%>
|
||||
<span class="tag host-link hover-yellow" rel="http<%- certificate_id ? 's' : '' %>://<%- host %>"><%- host %></span>
|
||||
<%
|
||||
} else {
|
||||
%>
|
||||
<span class="tag"><%- host %></span>
|
||||
<%
|
||||
}
|
||||
});
|
||||
%>
|
||||
</div>
|
||||
<div class="small text-muted">
|
||||
<%- i18n('str', 'created-on', {date: formatDbDate(created_on, 'Do MMMM YYYY')}) %>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="text-monospace"><%- forward_http_code %></div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="text-monospace"><%- forward_scheme == '$scheme' ? 'auto' : forward_scheme %></div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="text-monospace"><%- forward_domain_name %></div>
|
||||
</td>
|
||||
<td>
|
||||
<div><%- certificate ? i18n('ssl', certificate.provider) : i18n('ssl', 'none') %></div>
|
||||
</td>
|
||||
<td>
|
||||
<%
|
||||
var o = isOnline();
|
||||
if (!enabled) { %>
|
||||
<span class="status-icon bg-warning"></span> <%- i18n('str', 'disabled') %>
|
||||
<% } else if (o === true) { %>
|
||||
<span class="status-icon bg-success"></span> <%- i18n('str', 'online') %>
|
||||
<% } else if (o === false) { %>
|
||||
<span title="<%- getOfflineError() %>"><span class="status-icon bg-danger"></span> <%- i18n('str', 'offline') %></span>
|
||||
<% } else { %>
|
||||
<span class="status-icon bg-warning"></span> <%- i18n('str', 'unknown') %>
|
||||
<% } %>
|
||||
</td>
|
||||
<% if (canManage) { %>
|
||||
<td class="text-right">
|
||||
<div class="item-action dropdown">
|
||||
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<span class="dropdown-header"><%- i18n('audit-log', 'redirection-host') %> #<%- id %></span>
|
||||
<a href="#" class="edit dropdown-item"><i class="dropdown-icon fe fe-edit"></i> <%- i18n('str', 'edit') %></a>
|
||||
<a href="#" class="able dropdown-item"><i class="dropdown-icon fe fe-power"></i> <%- i18n('str', enabled ? 'disable' : 'enable') %></a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a href="#" class="delete dropdown-item"><i class="dropdown-icon fe fe-trash-2"></i> <%- i18n('str', 'delete') %></a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<% } %>
|
@@ -1,61 +0,0 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const App = require('../../../main');
|
||||
const template = require('./item.ejs');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
tagName: 'tr',
|
||||
|
||||
ui: {
|
||||
able: 'a.able',
|
||||
edit: 'a.edit',
|
||||
delete: 'a.delete',
|
||||
host_link: '.host-link'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click @ui.able': function (e) {
|
||||
e.preventDefault();
|
||||
let id = this.model.get('id');
|
||||
App.Api.Nginx.RedirectionHosts[this.model.get('enabled') ? 'disable' : 'enable'](id)
|
||||
.then(() => {
|
||||
return App.Api.Nginx.RedirectionHosts.get(id)
|
||||
.then(row => {
|
||||
this.model.set(row);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
'click @ui.edit': function (e) {
|
||||
e.preventDefault();
|
||||
App.Controller.showNginxRedirectionForm(this.model);
|
||||
},
|
||||
|
||||
'click @ui.delete': function (e) {
|
||||
e.preventDefault();
|
||||
App.Controller.showNginxRedirectionDeleteConfirm(this.model);
|
||||
},
|
||||
|
||||
'click @ui.host_link': function (e) {
|
||||
e.preventDefault();
|
||||
let win = window.open($(e.currentTarget).attr('rel'), '_blank');
|
||||
win.focus();
|
||||
}
|
||||
},
|
||||
|
||||
templateContext: {
|
||||
canManage: App.Cache.User.canManage('redirection_hosts'),
|
||||
|
||||
isOnline: function () {
|
||||
return typeof this.meta.nginx_online === 'undefined' ? null : this.meta.nginx_online;
|
||||
},
|
||||
|
||||
getOfflineError: function () {
|
||||
return this.meta.nginx_err || '';
|
||||
}
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
this.listenTo(this.model, 'change', this.render);
|
||||
}
|
||||
});
|
@@ -1,15 +0,0 @@
|
||||
<thead>
|
||||
<th width="30"> </th>
|
||||
<th><%- i18n('str', 'source') %></th>
|
||||
<th><%- i18n('redirection-hosts', 'forward-http-status-code') %></th>
|
||||
<th><%- i18n('redirection-hosts', 'forward-scheme') %></th>
|
||||
<th><%- i18n('str', 'destination') %></th>
|
||||
<th><%- i18n('str', 'ssl') %></th>
|
||||
<th><%- i18n('str', 'status') %></th>
|
||||
<% if (canManage) { %>
|
||||
<th> </th>
|
||||
<% } %>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- items -->
|
||||
</tbody>
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user