Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# nginx ui

![Image of Nginx UI](https://i.ibb.co/XXcfsDp/Bildschirmfoto-2020-06-20-um-18-40-27.png)

We use nginx in our company lab environment. It often happens that my
colleagues have developed an application that is now deployed in our Stage
or Prod environment. To make this application accessible nginx has to be
Expand Down Expand Up @@ -28,3 +30,16 @@ services:
- nginx:/etc/nginx
```

## UI

![Image of Nginx UI](https://i.ibb.co/qNgBRrt/Bildschirmfoto-2020-06-21-um-10-01-46.png)

With the menu item Main Config the Nginx specific configuration files
can be extracted and updated. These are dynamically read from the Nginx
directory. If a file has been added manually, it is immediately integrated
into the Nginx UI Main Config menu item.

![Image of Nginx UI](https://i.ibb.co/j85XKM6/Bildschirmfoto-2020-06-21-um-10-01-58.png)

Adding a domain opens an exclusive editing window for the configuration
file. This can be applied, deleted and enabled/disabled.
100 changes: 86 additions & 14 deletions app/api/endpoints.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,41 @@
import datetime
import io
import os

import flask

from app.api import api


@api.route('/config/<name>', methods=['GET'])
def get_config(name):
def get_config(name: str):
"""
Reads the file with the corresponding name that was passed.

:param name: Configuration file name
:type name: str

:return: Rendered HTML document with content of the configuration file.
:rtype: str
"""
nginx_path = flask.current_app.config['NGINX_PATH']

with io.open(os.path.join(nginx_path, name), 'r') as f:
_file = f.read()

return flask.render_template('config.html', name=name, file=_file)
return flask.render_template('config.html', name=name, file=_file), 200


@api.route('/config/<name>', methods=['POST'])
def post_config(name):
def post_config(name: str):
"""
Accepts the customized configuration and saves it in the configuration file with the supplied name.

:param name: Configuration file name
:type name: str

:return:
:rtype: werkzeug.wrappers.Response
"""
content = flask.request.get_json()
nginx_path = flask.current_app.config['NGINX_PATH']

Expand All @@ -30,6 +47,12 @@ def post_config(name):

@api.route('/domains', methods=['GET'])
def get_domains():
"""
Reads all files from the configuration file directory and checks the state of the site configuration.

:return: Rendered HTML document with the domains
:rtype: str
"""
config_path = flask.current_app.config['CONFIG_PATH']
sites_available = []
sites_enabled = []
Expand All @@ -55,11 +78,23 @@ def get_domains():
'time': time
})

return flask.render_template('domains.html', sites_available=sites_available, sites_enabled=sites_enabled)
# sort sites by name
sites_available = sorted(sites_available, key=lambda _: _['name'])
return flask.render_template('domains.html', sites_available=sites_available, sites_enabled=sites_enabled), 200


@api.route('/domain/<name>', methods=['GET'])
def get_domain(name):
def get_domain(name: str):
"""
Takes the name of the domain configuration file and
returns a rendered HTML with the current configuration of the domain.

:param name: The domain name that corresponds to the name of the file.
:type name: str

:return: Rendered HTML document with the domain
:rtype: str
"""
config_path = flask.current_app.config['CONFIG_PATH']
_file = ''
enabled = True
Expand All @@ -78,23 +113,44 @@ def get_domain(name):

break

return flask.render_template('domain.html', name=name, file=_file, enabled=enabled)
return flask.render_template('domain.html', name=name, file=_file, enabled=enabled), 200


@api.route('/domain/<name>', methods=['POST'])
def post_domain(name):
def post_domain(name: str):
"""
Creates the configuration file of the domain.

:param name: The domain name that corresponds to the name of the file.
:type name: str

:return: Returns a status about the success or failure of the action.
"""
config_path = flask.current_app.config['CONFIG_PATH']
new_domain = flask.render_template('new_domain.j2', name=name)
name = name + '.conf.disabled'

with io.open(os.path.join(config_path, name), 'w') as f:
f.write(new_domain)
try:
with io.open(os.path.join(config_path, name), 'w') as f:
f.write(new_domain)

response = flask.jsonify({'success': True}), 201
except Exception as ex:
response = flask.jsonify({'success': False, 'error_msg': ex}), 500

return flask.jsonify({'success': True}), 201
return response


@api.route('/domain/<name>', methods=['DELETE'])
def delete_domain(name):
def delete_domain(name: str):
"""
Deletes the configuration file of the corresponding domain.

:param name: The domain name that corresponds to the name of the file.
:type name: str

:return: Returns a status about the success or failure of the action.
"""
config_path = flask.current_app.config['CONFIG_PATH']
removed = False

Expand All @@ -113,7 +169,15 @@ def delete_domain(name):


@api.route('/domain/<name>', methods=['PUT'])
def put_domain(name):
def put_domain(name: str):
"""
Updates the configuration file with the corresponding domain name.

:param name: The domain name that corresponds to the name of the file.
:type name: str

:return: Returns a status about the success or failure of the action.
"""
content = flask.request.get_json()
config_path = flask.current_app.config['CONFIG_PATH']

Expand All @@ -128,7 +192,15 @@ def put_domain(name):


@api.route('/domain/<name>/enable', methods=['POST'])
def enable_domain(name):
def enable_domain(name: str):
"""
Activates the domain in Nginx so that the configuration is applied.

:param name: The domain name that corresponds to the name of the file.
:type name: str

:return: Returns a status about the success or failure of the action.
"""
content = flask.request.get_json()
config_path = flask.current_app.config['CONFIG_PATH']

Expand Down
14 changes: 14 additions & 0 deletions app/static/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,18 @@ textarea {

#main-container {
margin-top: 5em;
}
#domain {
display: none;
}

@media only screen and (max-width: 666px) {
[class*="mobile hidden"],
[class*="tablet only"]:not(.mobile),
[class*="computer only"]:not(.mobile),
[class*="large monitor only"]:not(.mobile),
[class*="widescreen monitor only"]:not(.mobile),
[class*="or lower hidden"] {
display: none !important;
}
}
47 changes: 12 additions & 35 deletions app/static/custom.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,30 @@ $(document).ready(function() {
$('.config.item').click(function() {
var name = $(this).html();
load_config(name);

$('.green.highlighted').removeClass('green highlighted');
$('#edit_config').addClass('green highlighted');
});

$('#domains').click(function() {
$.when(load_domains()).then(function() {
$('.green.highlighted').removeClass('green highlighted');
$('#domains').addClass('green highlighted');
});
$('#domains').click(function() { load_domains() });

});
load_domains();

});

function load_domains() {
fetch_html('api/domains');
$.when(fetch_html('api/domains')).then(function() {
$('#domain').hide();
$('#domain_cards').fadeIn();
});
}

function add_domain() {
var name = $('#add_domain').val();
$('#add_domain').val('');

$.ajax({
type: 'POST',
url: '/api/domain/' + name,
statusCode: {
201: function() {
$.when(load_domains()).then(function() {
fetch_domain(name);
});
}
201: function() { fetch_domain(name) }
}
});

Expand All @@ -51,11 +44,7 @@ function enable_domain(name, enable) {
enable: enable
}),
statusCode: {
200: function() {
$.when(load_domains()).then(function() {
fetch_domain(name);
});
}
200: function() { fetch_domain(name); }
}
});

Expand All @@ -74,20 +63,7 @@ function update_domain(name) {
file: _file
}),
statusCode: {
200: function() {

setTimeout(function(){

$.when(load_domains()).then(function() {

setTimeout(function() {
fetch_domain(name);
}, 50);

});
}, 450);

}
200: function() { setTimeout(function(){ fetch_domain(name) }, 400) }
}
});

Expand All @@ -98,7 +74,8 @@ function fetch_domain(name) {
fetch('api/domain/' + name)
.then(function(response) {
response.text().then(function(text) {
$('#domain').html(text);
$('#domain').html(text).fadeIn();
$('#domain_cards').hide();
});
})
.catch(function(error) {
Expand Down
2 changes: 1 addition & 1 deletion app/static/custom.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 19 additions & 8 deletions app/templates/config.html
Original file line number Diff line number Diff line change
@@ -1,22 +1,33 @@
<div class="column">
<div class="ui top attached segment">
<div class="ui two column grid">

<div class="column">
<h1 class="ui header">{{ name }}</h1>
</div>

<div class="column">
<button class="ui right floated labeled icon button" onclick="update_config('{{ name }}')" tabindex="0">
<i class="play icon"></i>
Apply
</button>
</div>

</div>
</div>

<div class="ui attached segment">
<div class="ui form">

<div class="field">
<label><h1>{{ name }}</h1></label>

<div class="ui basic segment">
<div class="ui inverted dimmer" id="dimmer">
<div class="ui huge loader"></div>
</div>
<textarea class="code" rows="25" id="file-content" autofocus>{{ file }}</textarea>
</div>
</div>

<button class="ui right floated labeled icon button" onclick="update_config('{{ name }}')" tabindex="0">
<i class="play icon"></i>
Apply
</button>
</div>

</div>

</div>
Loading