diff --git a/app/api/routes/routes.py b/app/api/routes/routes.py
index 268c9315..047a6cf2 100644
--- a/app/api/routes/routes.py
+++ b/app/api/routes/routes.py
@@ -55,7 +55,7 @@ bp.add_url_rule('/ha///vips', view_func=HAVIPsView.as_v
register_api_id_ip(ServiceView, 'service', '/status', ['GET'])
register_api_id_ip(ServiceBackendView, 'service_backend', '/backend', ['GET'])
register_api_id_ip(ServiceConfigView, 'config_view')
-register_api_id_ip(ServiceConfigVersionsView, 'config_version', '/versions', methods=['GET'])
+register_api_id_ip(ServiceConfigVersionsView, 'config_version', '/versions', methods=['GET', 'DELETE'])
register_api_id_ip(CheckerView, 'checker', '/tools')
register_api_id_ip(InstallView, 'install', '/install', methods=['POST'])
register_api_id_ip(ServiceActionView, 'service_action', '/', methods=['GET'])
diff --git a/app/modules/db/metric.py b/app/modules/db/metric.py
index 9337741e..fbef0f24 100644
--- a/app/modules/db/metric.py
+++ b/app/modules/db/metric.py
@@ -288,7 +288,7 @@ def select_table_metrics(group_id):
avg_cur_1h, avg_cur_24h, avg_cur_3d, max_con_1h, max_con_24h, max_con_3d from
(select servers.ip from servers where haproxy_metrics = 1 ) as ip,
- (select servers.ip, servers.hostname as hostname from servers left join metrics as metr on servers.ip = metr.serv where servers.metrics = 1 %s) as hostname,
+ (select servers.ip, servers.hostname as hostname from servers left join metrics as metr on servers.ip = metr.serv where servers.haproxy_metrics = 1 %s) as hostname,
(select servers.ip,round(avg(metr.sess_rate), 1) as avg_sess_1h from servers
left join metrics as metr on metr.serv = servers.ip
@@ -383,7 +383,7 @@ def select_table_metrics(group_id):
avg_cur_24h, avg_cur_3d, max_con_1h, max_con_24h, max_con_3d from
(select servers.ip from servers where haproxy_metrics = 1 ) as ip,
- (select servers.ip, servers.hostname as hostname from servers left join metrics as metr on servers.ip = metr.serv where servers.metrics = 1 %s) as hostname,
+ (select servers.ip, servers.hostname as hostname from servers left join metrics as metr on servers.ip = metr.serv where servers.haproxy_metrics = 1 %s) as hostname,
(select servers.ip,round(avg(metr.sess_rate), 1) as avg_sess_1h from servers
left join metrics as metr on metr.serv = servers.ip
diff --git a/app/modules/roxywi/class_models.py b/app/modules/roxywi/class_models.py
index 4c2a976d..69e4c1cc 100644
--- a/app/modules/roxywi/class_models.py
+++ b/app/modules/roxywi/class_models.py
@@ -181,6 +181,10 @@ class ConfigFileNameQuery(BaseModel):
version: Optional[str] = None
+class VersionsForDelete(BaseModel):
+ versions: List[str]
+
+
class ConfigRequest(BaseModel):
action: Literal['save', 'test', 'reload', 'restart']
file_name: Optional[str] = None
diff --git a/app/routes/config/routes.py b/app/routes/config/routes.py
index c9a18730..3a72e01c 100644
--- a/app/routes/config/routes.py
+++ b/app/routes/config/routes.py
@@ -17,10 +17,11 @@ import app.modules.config.common as config_common
import app.modules.config.section as section_mod
import app.modules.service.haproxy as service_haproxy
import app.modules.server.server as server_mod
-from app.views.service.views import ServiceConfigView
+from app.views.service.views import ServiceConfigView, ServiceConfigVersionsView
from app.modules.roxywi.class_models import DataStrResponse
bp.add_url_rule('//', view_func=ServiceConfigView.as_view('config_view_ip'), methods=['POST'])
+bp.add_url_rule('///versions', view_func=ServiceConfigVersionsView.as_view('config_version'), methods=['DELETE'])
@bp.before_request
@@ -136,48 +137,16 @@ def config(service, serv, edit, config_file_name, new):
return render_template('config.html', **kwargs)
-@bp.route('/versions/', defaults={'server_ip': None}, methods=['GET', 'POST'])
-@bp.route('/versions//', methods=['GET', 'POST'])
+@bp.route('/versions/', defaults={'server_ip': None}, methods=['GET'])
+@bp.route('/versions//', methods=['GET'])
@check_services
@get_user_params(disable=1)
def versions(service, server_ip):
roxywi_auth.page_for_admin(level=3)
- after_save = ''
- file = set()
- stderr = ''
- file_format = config_common.get_file_format(service)
-
- if request.form.get('del'):
- after_save = 1
- for get in request.form.getlist('do_delete'):
- if file_format in get and server_ip in get:
- try:
- if config_sql.delete_config_version(service, get):
- try:
- os.remove(get)
- except OSError as e:
- if 'No such file or directory' in str(e):
- pass
- else:
- config_dir = config_common.get_config_dir('haproxy')
- os.remove(os.path.join(config_dir, get))
- try:
- file.add(get + "\n")
- roxywi_common.logging(
- server_ip, f"Version of config has been deleted: {get}", login=1, keep_history=1,
- service=service
- )
- except Exception:
- pass
- except OSError as e:
- stderr = "Error: %s - %s." % (e.filename, e.strerror)
kwargs = {
'serv': server_ip,
- 'aftersave': after_save,
- 'file': file,
'service': service,
- 'stderr': stderr,
'lang': g.user_params['lang']
}
return render_template('delver.html', **kwargs)
@@ -193,48 +162,48 @@ def list_of_version(service):
return config_mod.list_of_versions(server_ip, service, config_ver, for_delver)
-@bp.route('/versions///', defaults={'save': None}, methods=['GET', 'POST'])
-@bp.route('/versions////save', defaults={'save': 1}, methods=['GET', 'POST'])
+@bp.route('/versions///', methods=['GET'])
@check_services
@get_user_params(disable=1)
-def show_version(service, server_ip, configver, save):
+def show_version(service, server_ip, configver):
roxywi_auth.page_for_admin(level=3)
- service_desc = service_sql.select_service(service)
- config_dir = config_common.get_config_dir('haproxy')
- configver = config_dir + configver
- aftersave = 0
- stderr = ''
-
- if save:
- aftersave = 1
- save_action = request.form.get('save')
- try:
- roxywi_common.logging(
- server_ip, f"Version of config has been uploaded {configver}", login=1, keep_history=1, service=service
- )
- except Exception:
- pass
-
- if service == 'keepalived':
- stderr = config_mod.upload_and_restart(server_ip, configver, save_action, service)
- elif service in ('nginx', 'apache'):
- config_file_name = config_sql.select_remote_path_from_version(server_ip=server_ip, service=service, local_path=configver)
- stderr = config_mod.master_slave_upload_and_restart(server_ip, configver, save_action, service_desc.slug, config_file_name=config_file_name)
- else:
- stderr = config_mod.master_slave_upload_and_restart(server_ip, configver, save_action, service)
kwargs = {
'serv': server_ip,
- 'aftersave': aftersave,
- 'configver': configver,
'service': service,
- 'stderr': stderr,
'lang': g.user_params['lang']
}
return render_template('configver.html', **kwargs)
+@bp.route('/versions////save', methods=['POST'])
+@check_services
+@get_user_params()
+def save_version(service, server_ip, configver):
+ roxywi_auth.page_for_admin(level=3)
+ config_dir = config_common.get_config_dir('haproxy')
+ configver = config_dir + configver
+ service_desc = service_sql.select_service(service)
+ save_action = request.json.get('action')
+ try:
+ roxywi_common.logging(
+ server_ip, f"Version of config has been uploaded {configver}", login=1, keep_history=1, service=service
+ )
+ except Exception:
+ pass
+
+ if service == 'keepalived':
+ stderr = config_mod.upload_and_restart(server_ip, configver, save_action, service)
+ elif service in ('nginx', 'apache'):
+ config_file_name = config_sql.select_remote_path_from_version(server_ip=server_ip, service=service, local_path=configver)
+ stderr = config_mod.master_slave_upload_and_restart(server_ip, configver, save_action, service_desc.slug, config_file_name=config_file_name)
+ else:
+ stderr = config_mod.master_slave_upload_and_restart(server_ip, configver, save_action, service)
+
+ return DataStrResponse(data=stderr).model_dump(mode='json'), 201
+
+
@bp.route('/section/haproxy/')
@get_user_params()
def haproxy_section(server_ip):
diff --git a/app/static/js/add.js b/app/static/js/add.js
index b59f0799..3f8752ba 100644
--- a/app/static/js/add.js
+++ b/app/static/js/add.js
@@ -959,7 +959,7 @@ $( function() {
});
let add_server_var = '
: ' +
' ' +
- 'port check: ' +
+ 'Port check: ' +
' maxconn: '
$('[name=add-server-input]').click(function () {
$("[name=add_servers]").append(add_server_var);
diff --git a/app/static/js/configshow.js b/app/static/js/configshow.js
index 8645a1dd..a4cc8e18 100644
--- a/app/static/js/configshow.js
+++ b/app/static/js/configshow.js
@@ -82,4 +82,73 @@ $( function() {
});
e.preventDefault();
});
+
+ $("#save_version").on("click", ":submit", function (e) {
+ let frm = $('#save_version');
+ let unindexed_array = frm.serializeArray();
+ let indexed_array = {};
+ $.map(unindexed_array, function (n, i) {
+ if (n['value'] != 'undefined') {
+ indexed_array[n['name']] = n['value'];
+ }
+ });
+ indexed_array['action'] = $(this).val();
+ $.ajax({
+ url: frm.attr('action'),
+ dataType: 'json',
+ data: JSON.stringify(indexed_array),
+ type: frm.attr('method'),
+ contentType: "application/json; charset=UTF-8",
+ success: function (data) {
+ toastr.clear();
+ data.data = data.data.replace(/\n/g, "
");
+ returnNiceCheckingConfig(data.data);
+ if (data.status === 'failed') {
+ toastr.warning(data.error)
+ }
+ }
+ });
+ e.preventDefault();
+ });
+ $("#delete_versions_form").on("click", ":submit", function (e) {
+ let frm = $('#delete_versions_form');
+ let unindexed_array = frm.serializeArray();
+ let indexed_array = {};
+ indexed_array['versions'] = [];
+ $.map(unindexed_array, function (n, i) {
+ if (n['value'] != 'undefined') {
+ if (n['name'] === 'versions') {
+ indexed_array['versions'].push(n['value']);
+ } else {
+ indexed_array[n['name']] = n['value'];
+ }
+ }
+ });
+ indexed_array['action'] = $(this).val();
+ $.ajax({
+ url: frm.attr('action'),
+ dataType: 'json',
+ data: JSON.stringify(indexed_array),
+ type: frm.attr('method'),
+ contentType: "application/json; charset=UTF-8",
+ statusCode: {
+ 204: function (xhr) {
+ toastr.success('The versions of configs have been deleted');
+ showListOfVersion(1);
+ },
+ 404: function (xhr) {
+ toastr.success('The versions of configs have been deleted');
+ showListOfVersion(1);
+ }
+ },
+ success: function (data) {
+ if (data) {
+ if (data.status === "failed") {
+ toastr.error(data);
+ }
+ }
+ }
+ });
+ e.preventDefault();
+ });
})
diff --git a/app/templates/ajax/config_show.html b/app/templates/ajax/config_show.html
index b152585f..97b8a6bb 100644
--- a/app/templates/ajax/config_show.html
+++ b/app/templates/ajax/config_show.html
@@ -368,7 +368,7 @@
{% if role <= 3 %}
{% if not is_serv_protected or role <= 2 %}
-
- {% if not aftersave %}
- {% if stderr %}
- {% include 'include/errors.html' %}
- {% endif %}
- {% endif %}
- {% if aftersave %}
- {{lang.phrases.version_has_been_uploaded}}: {{ configver }}
- {% if 'is valid' not in stderr %}
- {% include 'include/errors.html' %}
- {% else %}
- {{lang.words.config|title()}} {{lang.words.is}} {{lang.words.valid}}
- {% endif %}
- {% endif %}
{% endblock %}
diff --git a/app/templates/delver.html b/app/templates/delver.html
index 894b8b21..3702a127 100644
--- a/app/templates/delver.html
+++ b/app/templates/delver.html
@@ -18,25 +18,11 @@
{% endif %}
- {% if aftersave %}
- {{lang.phrases.files_been_deleted}}:
- {% if stderr %}
- {% include 'include/errors.html' %}
- {% else %}
-
- {% for f in file %}
- {{f}}
- {% endfor %}
-
- {% endif %}
- {% endif %}
- {% if not aftersave %}
{{lang.phrases.work_with_prev}} {{ service[0]|upper}}{{service[1:] }}. {{lang.phrases.roll_back}}
- {% endif %}
- {% if serv and not aftersave %}
+ {% if serv %}
{% for select in g.user_params['servers'] %}
{% if select.2 == serv %}
diff --git a/app/views/service/views.py b/app/views/service/views.py
index e6b03d9a..f557a747 100644
--- a/app/views/service/views.py
+++ b/app/views/service/views.py
@@ -1,3 +1,4 @@
+import os
from typing import Union, Literal
from flask.views import MethodView
@@ -6,6 +7,7 @@ from flask import jsonify, g
from flask_jwt_extended import jwt_required
import app.modules.db.sql as sql
+import app.modules.db.config as config_sql
import app.modules.db.server as server_sql
import app.modules.db.service as service_sql
import app.modules.roxywi.common as roxywi_common
@@ -16,7 +18,8 @@ import app.modules.service.action as service_action
import app.modules.service.common as service_common
from app.middleware import get_user_params, page_for_admin, check_group, check_services
from app.modules.roxywi.exception import RoxywiResourceNotFound
-from app.modules.roxywi.class_models import BaseResponse, ErrorResponse, DataResponse, DataStrResponse, ConfigFileNameQuery, ConfigRequest
+from app.modules.roxywi.class_models import BaseResponse, ErrorResponse, DataResponse, DataStrResponse, \
+ ConfigFileNameQuery, ConfigRequest, VersionsForDelete
from app.modules.common.common_classes import SupportClass
@@ -404,10 +407,10 @@ class ServiceConfigView(MethodView):
class ServiceConfigVersionsView(MethodView):
- methods = ['GET', 'POST']
+ methods = ['GET', 'POST', 'DELETE']
decorators = [jwt_required(), get_user_params(), check_services, page_for_admin(level=4), check_group()]
- def get(self, service, server_id: Union[int, str]):
+ def get(self, service: str, server_id: Union[int, str]):
"""
This endpoint returns a list of configuration file versions for a specified service on a specific server.
---
@@ -439,3 +442,74 @@ class ServiceConfigVersionsView(MethodView):
file_format = config_common.get_file_format(service)
files = roxywi_common.get_files(config_dir, file_format, server_ip)
return DataResponse(data=files).model_dump(mode='json')
+
+ @validate(body=VersionsForDelete)
+ def delete(self, service: str, server_id: Union[int, str], body: VersionsForDelete):
+ """
+ This endpoint deletes specified configuration file versions for a particular service on a specific server.
+ ---
+ tags:
+ - Service config
+ parameters:
+ - in: path
+ name: service
+ type: string
+ enum: [haproxy, nginx, apache, keepalived]
+ required: true
+ description: The type of service (haproxy, nginx, apache, keepalived)
+ - in: path
+ name: server_id
+ type: string
+ required: true
+ description: The ID or IP of the server
+ - in: body
+ name: body
+ description: JSON array of paths to version files to be deleted
+ schema:
+ type: array
+ items:
+ type: string
+ description: Path to the version file
+ required: true
+ responses:
+ 204:
+ description: 'Successful operation, specified configuration file versions are deleted'
+ 400:
+ description: 'Invalid service type or server ID'
+ 404:
+ description: 'Service or server not found'
+ 500:
+ description: 'Internal server error'
+ """
+ file = set()
+ file_format = config_common.get_file_format(service)
+
+ try:
+ server_ip = SupportClass(False).return_server_ip_or_id(server_id)
+ except Exception as e:
+ return roxywi_common.handler_exceptions_for_json_data(e ,'')
+
+ for get in body.versions:
+ if file_format in get and server_ip in get:
+ try:
+ if config_sql.delete_config_version(service, get):
+ try:
+ os.remove(get)
+ except OSError as e:
+ if 'No such file or directory' in str(e):
+ pass
+ else:
+ config_dir = config_common.get_config_dir('haproxy')
+ os.remove(os.path.join(config_dir, get))
+ try:
+ file.add(get + "\n")
+ roxywi_common.logging(
+ server_ip, f"Version of config has been deleted: {get}", login=1, keep_history=1,
+ service=service
+ )
+ except Exception:
+ pass
+ except OSError as e:
+ return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot delete config version')
+
+ return BaseResponse().model_dump(mode='json'), 204