Compare commits

...

7 Commits

Author SHA1 Message Date
Pavel Loginov 35bd366f55
Merge pull request #425 from e345/patch-1
Update action.py : Redirect Docker command output to prevent Python hang
2025-09-23 09:44:28 +03:00
e345 d7c07b75b1
Merge pull request #1 from e345/e345-patch-1
Update action.py : Redirect Docker command output to prevent Python hang
2025-09-22 15:42:31 +08:00
e345 b25ce00e12
Update action.py : Redirect Docker command output to prevent Python hang
When executing Docker commands via Python, such as `docker kill -s HUP <container>`, the process may hang because the command produces output that blocks the Python subprocess.  

This change appends `> /dev/null` to Docker commands to discard any standard output, ensuring that Python can execute the command without waiting indefinitely.  

Systemd commands remain unchanged, as they do not exhibit the same blocking behavior.
2025-09-22 15:41:50 +08:00
Pavel Loginov 90050a9bd0
Merge pull request #424 from e345/patch-1
Update action.py: correct docker kill signal option case
2025-09-22 09:07:08 +03:00
e345 6b7cfd1e86
Update action.py: correct docker kill signal option case
Previously, when handling the reload action, the code used `docker kill -S HUP`.
However, the Docker CLI only supports the lowercase `-s` option.
Using `-S` results in an error and prevents the signal from being sent to the container process.

This change:
- Corrects `kill -S HUP` to `kill -s HUP`
- Ensures that in Docker mode, the reload action can properly send the SIGHUP signal
- Keeps the systemd branch logic unchanged
2025-09-22 13:53:50 +08:00
Aidaho 3042fd7f24 v8.2.3: Add hotkeys info link in config template and refactor time_range handling in metrics module
- Added an informational link about hotkeys to the config.html template for improved usability.
- Refactored time_range logic in metric queries to use integers instead of strings for better consistency and readability.
2025-09-06 09:50:36 +03:00
Aidaho dccc2b3e20 v8.2.3: Add backend header hiding option and improve UDP listener updates
- Introduced `hide_backend_headers` option in Nginx configuration for enhanced security.
- Updated models, templates, and JavaScript to support `hide_backend_headers`.
- Improved UDP listener rendering logic by dynamically fetching and syncing listeners via AJAX.
- Added new database migration to update version to `8.2.3`.
2025-08-26 09:24:20 +03:00
18 changed files with 122 additions and 44 deletions

View File

@ -52,28 +52,26 @@ def select_metrics(serv, service, **kwargs):
else: else:
model = Metrics model = Metrics
# Get time range from kwargs time_range = kwargs.get('time_range', '30')
time_range = kwargs.get('time_range', '30') # Default to 30 minutes if not specified
# Create a base query # Create a base query
query = model.select().where(model.serv == serv) query = model.select().where(model.serv == serv)
# Add time-based filtering # Add time-based filtering
now = datetime.utcnow() now = datetime.utcnow()
if time_range == '1': if time_range == 1:
# Last 1 minute # Last 1 minute
query = query.where(model.date >= now - timedelta(minutes=1)) query = query.where(model.date >= now - timedelta(minutes=1))
elif time_range == '60': elif time_range == 60:
# Last 60 minutes # Last 60 minutes
query = query.where(model.date >= now - timedelta(minutes=60)) query = query.where(model.date >= now - timedelta(minutes=60))
elif time_range == '180': elif time_range == 180:
# Last 180 minutes # Last 180 minutes
query = query.where(model.date >= now - timedelta(minutes=180)) query = query.where(model.date >= now - timedelta(minutes=180))
elif time_range == '360': elif time_range == 360:
# Last 360 minutes # Last 360 minutes
query = query.where(model.date >= now - timedelta(minutes=360)) query = query.where(model.date >= now - timedelta(minutes=360))
elif time_range == '720': elif time_range == 720:
# Last 720 minutes # Last 720 minutes
query = query.where(model.date >= now - timedelta(minutes=720)) query = query.where(model.date >= now - timedelta(minutes=720))
else: else:
@ -84,14 +82,14 @@ def select_metrics(serv, service, **kwargs):
query = query.order_by(model.date.asc()) query = query.order_by(model.date.asc())
# For longer time ranges, we can sample the data to reduce the number of points # For longer time ranges, we can sample the data to reduce the number of points
# This is similar to the original SQL's "group by `date` div X" or "rowid % X = 0" # This is similar to the original SQL's "group by `date` div X" or "rowid % X = 0"
if mysql_enable == '1' and time_range not in ('1', '30'): if mysql_enable == '1' and time_range not in (1, 30):
# For MySQL, we can use the SQL function to sample data # For MySQL, we can use the SQL function to sample data
from peewee import fn from peewee import fn
sampling_rates = { sampling_rates = {
'60': 100, 60: 100,
'180': 200, 180: 200,
'360': 300, 360: 300,
'720': 500 720: 500
} }
if time_range in sampling_rates: if time_range in sampling_rates:
# Group by date div X to reduce data points # Group by date div X to reduce data points
@ -99,15 +97,15 @@ def select_metrics(serv, service, **kwargs):
(model.serv == serv) & (model.serv == serv) &
(model.date >= now - timedelta(minutes=int(time_range))) (model.date >= now - timedelta(minutes=int(time_range)))
).group_by(fn.DIV(model.date, sampling_rates[time_range])).order_by(model.date.asc()) ).group_by(fn.DIV(model.date, sampling_rates[time_range])).order_by(model.date.asc())
elif not mysql_enable == '1' and time_range not in ('1', '30'): elif not mysql_enable == '1' and time_range not in (1, 30):
# For SQLite, we need to fetch all data and then sample it in Python # For SQLite, we need to fetch all data and then sample it in Python
# This is less efficient but maintains compatibility # This is less efficient but maintains compatibility
results = list(query.dicts()) results = list(query.dicts())
sampling_rates = { sampling_rates = {
'60': 2, 60: 2,
'180': 5, 180: 5,
'360': 7, 360: 7,
'720': 9 720: 9
} }
if time_range in sampling_rates: if time_range in sampling_rates:
# Sample data by taking every Nth row # Sample data by taking every Nth row

View File

@ -0,0 +1,22 @@
from playhouse.migrate import *
from app.modules.db.db_model import connect, Version
migrator = connect(get_migrator=1)
def up():
"""Apply the migration."""
try:
Version.update(version='8.2.3').execute()
except Exception as e:
print(f"Error updating version: {str(e)}")
raise e
def down():
"""Roll back the migration."""
try:
Version.update(version='8.2.2').execute()
except Exception as e:
print(f"Error rolling back migration: {str(e)}")
raise e

View File

@ -56,8 +56,8 @@ def get_action_command(service: str, action: str, server_id: int) -> str:
if is_docker == '1': if is_docker == '1':
container_name = sql.get_setting(f'{service}_container_name') container_name = sql.get_setting(f'{service}_container_name')
if action == 'reload': if action == 'reload':
action = 'kill -S HUP' action = 'kill -s HUP'
commands = f"sudo docker {action} {container_name}" commands = f"sudo docker {action} {container_name} > /dev/null"
else: else:
service_name = service_common.get_correct_service_name(service, server_id) service_name = service_common.get_correct_service_name(service, server_id)
commands = f"sudo systemctl {action} {service_name}" commands = f"sudo systemctl {action} {service_name}"

View File

@ -135,7 +135,7 @@ location {{ location.location }} {
gzip_proxied any; gzip_proxied any;
{% endif %} {% endif %}
{% if nginx_proxy.security.hide_backend_headers %} {% if config.security.hide_backend_headers %}
# Hide backend headers # Hide backend headers
proxy_hide_header X-Powered-By; proxy_hide_header X-Powered-By;
proxy_hide_header Server; proxy_hide_header Server;

View File

@ -9,7 +9,6 @@ nginx_proxy:
# Security configurations # Security configurations
security: security:
hide_backend_headers: true # Hide backend server headers (e.g., X-Powered-By)
hsts_max_age: 15768000 # HSTS duration in seconds (6 months) hsts_max_age: 15768000 # HSTS duration in seconds (6 months)
content_security_policy: "default-src 'self'" # Content Security Policy rules content_security_policy: "default-src 'self'" # Content Security Policy rules

View File

@ -237,7 +237,14 @@ function getNginxFormData($form, form_name) {
let header = {action, name, value}; let header = {action, name, value};
headers.push(header); headers.push(header);
}); });
let location_config = {location, proxy_connect_timeout, proxy_read_timeout, proxy_send_timeout, headers, upstream}; let location_config = {
location,
proxy_connect_timeout,
proxy_read_timeout,
proxy_send_timeout,
headers,
upstream
};
indexed_array['locations'].push(location_config) indexed_array['locations'].push(location_config)
} else if (n['name'] === 'ssl_offloading') { } else if (n['name'] === 'ssl_offloading') {
if ($('input[name="ssl_offloading"]').is(':checked')) { if ($('input[name="ssl_offloading"]').is(':checked')) {
@ -261,7 +268,7 @@ function getNginxFormData($form, form_name) {
indexed_array[n['name']] = n['value']; indexed_array[n['name']] = n['value'];
} }
}); });
$('#name_alias_div p').each(function (){ $('#name_alias_div p').each(function () {
let name = $(this).children("input[name='name_alias']").val(); let name = $(this).children("input[name='name_alias']").val();
if (name === undefined || name === '') { if (name === undefined || name === '') {
return; return;
@ -270,14 +277,22 @@ function getNginxFormData($form, form_name) {
}); });
let hide_server_tokens = false; let hide_server_tokens = false;
let security_headers = false; let security_headers = false;
let hide_backend_headers = false;
if ($('#hide_server_tokens').is(':checked')) { if ($('#hide_server_tokens').is(':checked')) {
hide_server_tokens = true; hide_server_tokens = true;
} }
if ($('#security_headers').is(':checked')) { if ($('#security_headers').is(':checked')) {
security_headers = true; security_headers = true;
} }
indexed_array['security'] = {'hide_server_tokens': hide_server_tokens, 'security_headers': security_headers}; if ($('#hide_backend_headers').is(':checked')) {
$('#'+form_name+' span[name="add_servers"] p').each(function (){ hide_backend_headers = true;
}
indexed_array['security'] = {
'hide_server_tokens': hide_server_tokens,
'security_headers': security_headers,
'hide_backend_headers': hide_backend_headers
};
$('#' + form_name + ' span[name="add_servers"] p').each(function () {
let server = $(this).children("input[name='servers']").val(); let server = $(this).children("input[name='servers']").val();
if (server === undefined || server === '') { if (server === undefined || server === '') {
return; return;
@ -290,7 +305,8 @@ function getNginxFormData($form, form_name) {
}); });
let elementsForDelete = [ let elementsForDelete = [
'servers', 'server_port', 'max_fails', 'fail_timeout', 'proxy_connect_timeout', 'proxy_read_timeout', 'proxy_send_timeout', 'servers', 'server_port', 'max_fails', 'fail_timeout', 'proxy_connect_timeout', 'proxy_read_timeout', 'proxy_send_timeout',
'headers_res', 'header_name', 'header_value', 'upstream', 'server', 'name_alias', 'hide_server_tokens', 'security_headers' 'headers_res', 'header_name', 'header_value', 'upstream', 'server', 'name_alias', 'hide_server_tokens', 'security_headers',
'hide_backend_headers'
] ]
for (let element of elementsForDelete) { for (let element of elementsForDelete) {
delete indexed_array[element] delete indexed_array[element]

View File

@ -826,6 +826,11 @@ function openNginxSection(section) {
} else { } else {
$('#security_headers').prop("checked", false); $('#security_headers').prop("checked", false);
} }
if (data.security.hide_backend_headers) {
$('#hide_backend_headers').prop("checked", true);
} else {
$('#hide_backend_headers').prop("checked", false);
}
} }
if (data.hsts) { if (data.hsts) {
$('#hsts').prop("checked", true); $('#hsts').prop("checked", true);

View File

@ -615,3 +615,33 @@ function checkUdpBackendStatus(listener_id, backend_ip) {
} }
}); });
} }
function getUdpListeners() {
$.ajax({
url: api_prefix + "/udp/listeners",
success: function (data) {
let clusters = document.querySelectorAll('.up-pannel > [id^="listener-"]');
let clusterNumbers = Array.from(clusters).map(div =>
parseInt(div.id.replace('listener-', ''), 10)
);
if (data.status === 'failed') {
toastr.error(data.error);
return;
}
if (data.length > 0) {
const dataIds = data.map(item => item.id);
const idsToDelete = clusterNumbers.filter(id => !dataIds.includes(id));
for (const [key, value] of Object.entries(data)) {
if (!clusterNumbers.includes(value.id)) {
getUDPListener(value.id, true);
} else {
getUDPListener(value.id, false);
}
}
for (let id of idsToDelete) {
$('#listener-' + id).remove();
}
}
}
});
}

View File

@ -88,6 +88,7 @@
<button type="submit" value="reload" name="save" class="btn btn-default">{{lang.phrases.save_and_reload}}</button> <button type="submit" value="reload" name="save" class="btn btn-default">{{lang.phrases.save_and_reload}}</button>
{% if service != 'keepalived' %} {% if service != 'keepalived' %}
<div class="alert alert-info" style="margin-left: -0px;"><b>{{lang.words.note|title()}}:</b> {{lang.phrases.master_slave}}</div> <div class="alert alert-info" style="margin-left: -0px;"><b>{{lang.words.note|title()}}:</b> {{lang.phrases.master_slave}}</div>
<div class="alert alert-info" style="margin-left: -0px;">Read more about <a href="https://roxy-wi.org/description/hotkeys" target="_blank">Hotkeys</a></div>
{% endif %} {% endif %}
</p> </p>
</form> </form>

View File

@ -120,6 +120,7 @@ Timeouts: Timeouts (proxy_connect_timeout).
{% set enable_sec_headers = lang.words.enable|title() + ' ' + lang.words.security + ' ' + lang.words.headers %} {% set enable_sec_headers = lang.words.enable|title() + ' ' + lang.words.security + ' ' + lang.words.headers %}
<p>{{ checkbox('hide_server_tokens', title=lang.add_page.desc.hide_server_tokens, desc=lang.phrases.hide_server_tokens, checked='checked') }}</p> <p>{{ checkbox('hide_server_tokens', title=lang.add_page.desc.hide_server_tokens, desc=lang.phrases.hide_server_tokens, checked='checked') }}</p>
<p>{{ checkbox('security_headers', title=lang.add_page.desc.security_headers, desc=lang.phrases.security_headers, checked='checked') }}</p> <p>{{ checkbox('security_headers', title=lang.add_page.desc.security_headers, desc=lang.phrases.security_headers, checked='checked') }}</p>
<p>{{ checkbox('hide_backend_headers', title=lang.add_page.desc.hide_backend_headers, desc=lang.phrases.hide_backend_headers, checked='checked') }}</p>
</div> </div>
</td> </td>
</tr> </tr>

View File

@ -345,6 +345,7 @@
"check_interval_title": "Check interval. In seconds.", "check_interval_title": "Check interval. In seconds.",
"hide_server_tokens": "Hide server tokens", "hide_server_tokens": "Hide server tokens",
"security_headers": "Enable security headers", "security_headers": "Enable security headers",
"hide_backend_headers": "Hide backend server headers",
} }
%} %}
{% set roles = { {% set roles = {
@ -358,6 +359,7 @@
"desc": { "desc": {
"security_headers": "Enable security headers (X-Content-Type-Options, etc.)", "security_headers": "Enable security headers (X-Content-Type-Options, etc.)",
"hide_server_tokens": "Hide Nginx version in headers", "hide_server_tokens": "Hide Nginx version in headers",
"hide_backend_headers": "Hide backend server headers (e.g., X-Powered-By)",
"port_check": "A basic TCP-layer health check tries to connect to the server's TCP port. The check is valid when the server answers with a SYN/ACK packet.", "port_check": "A basic TCP-layer health check tries to connect to the server's TCP port. The check is valid when the server answers with a SYN/ACK packet.",
"maxconn": "The total number of connections allowed, process-wide. This stops the process from accepting too many connections at once, which safeguards it from running out of memory.", "maxconn": "The total number of connections allowed, process-wide. This stops the process from accepting too many connections at once, which safeguards it from running out of memory.",
"server_template": "Create the list of servers from the template", "server_template": "Create the list of servers from the template",

View File

@ -332,6 +332,7 @@
"check_interval_title": "Intervalo de comprobación. En segundos.", "check_interval_title": "Intervalo de comprobación. En segundos.",
"hide_server_tokens": "Ocultar tokens del servidor", "hide_server_tokens": "Ocultar tokens del servidor",
"security_headers": "Habilitar encabezados de seguridad", "security_headers": "Habilitar encabezados de seguridad",
"hide_backend_headers": "Ocultar los encabezados del servidor backend",
} }
%} %}
{% set roles = { {% set roles = {
@ -345,6 +346,7 @@
"desc": { "desc": {
"security_headers": "Habilitar encabezados de seguridad (X-Content-Type-Options, etc.)", "security_headers": "Habilitar encabezados de seguridad (X-Content-Type-Options, etc.)",
"hide_server_tokens": "Ocultar la versión de Nginx en los encabezados", "hide_server_tokens": "Ocultar la versión de Nginx en los encabezados",
"hide_backend_headers": "Ocultar los encabezados del servidor backend (por ejemplo, X-Powered-By)",
"port_check": "Una verificación básica a nivel TCP intenta conectarse al puerto TCP del servidor. La verificación es válida cuando el servidor responde con un paquete SYN/ACK.", "port_check": "Una verificación básica a nivel TCP intenta conectarse al puerto TCP del servidor. La verificación es válida cuando el servidor responde con un paquete SYN/ACK.",
"maxconn": "Número total de conexiones permitidas a nivel de proceso. Esto evita aceptar demasiadas conexiones a la vez, protegiendo de quedarse sin memoria.", "maxconn": "Número total de conexiones permitidas a nivel de proceso. Esto evita aceptar demasiadas conexiones a la vez, protegiendo de quedarse sin memoria.",
"server_template": "Crear la lista de servidores desde la plantilla", "server_template": "Crear la lista de servidores desde la plantilla",

View File

@ -332,6 +332,7 @@
"check_interval_title": "Vérifiez l'intervalle. En secondes.", "check_interval_title": "Vérifiez l'intervalle. En secondes.",
"hide_server_tokens": "Masquer les jetons du serveur", "hide_server_tokens": "Masquer les jetons du serveur",
"security_headers": "Activer les en-têtes de sécurité", "security_headers": "Activer les en-têtes de sécurité",
"hide_backend_headers": "Masquer les en-têtes du serveur principal",
} }
%} %}
{% set roles = { {% set roles = {
@ -345,6 +346,7 @@
"desc": { "desc": {
"security_headers": "Activer les en-têtes de sécurité (X-Content-Type-Options, etc.)", "security_headers": "Activer les en-têtes de sécurité (X-Content-Type-Options, etc.)",
"hide_server_tokens": "Masquer la version Nginx dans les en-têtes", "hide_server_tokens": "Masquer la version Nginx dans les en-têtes",
"hide_backend_headers": "Masquer les en-têtes du serveur principal (par exemple, X-Powered-By)",
"port_check": "Un contrôle de base de la couche TCP tente de se connecter au port TCP du serveur. Le contrôle est valide lorsque le serveur répond par un paquet SYN/ACK.", "port_check": "Un contrôle de base de la couche TCP tente de se connecter au port TCP du serveur. Le contrôle est valide lorsque le serveur répond par un paquet SYN/ACK.",
"maxconn": "Le nombre total de connexions autorisées, à l\'échelle du processus. Cela empêche le processus d\'accepter trop de connexions à la fois, ce qui lui évite de manquer de mémoire.", "maxconn": "Le nombre total de connexions autorisées, à l\'échelle du processus. Cela empêche le processus d\'accepter trop de connexions à la fois, ce qui lui évite de manquer de mémoire.",
"server_template": "Créez la liste des serveurs à partir du modèle", "server_template": "Créez la liste des serveurs à partir du modèle",

View File

@ -332,6 +332,7 @@
"check_interval_title": "Verifique o intervalo. Em segundos.", "check_interval_title": "Verifique o intervalo. Em segundos.",
"hide_server_tokens": "Ocultar tokens do servidor", "hide_server_tokens": "Ocultar tokens do servidor",
"security_headers": "Habilitar cabeçalhos de segurança", "security_headers": "Habilitar cabeçalhos de segurança",
"hide_backend_headers": "Ocultar cabeçalhos do servidor de backend",
} }
%} %}
{% set roles = { {% set roles = {
@ -345,6 +346,7 @@
"desc": { "desc": {
"security_headers": "Habilitar cabeçalhos de segurança (X-Content-Type-Options, etc.)", "security_headers": "Habilitar cabeçalhos de segurança (X-Content-Type-Options, etc.)",
"hide_server_tokens": "Ocultar versão do Nginx nos cabeçalhos", "hide_server_tokens": "Ocultar versão do Nginx nos cabeçalhos",
"hide_backend_headers": "Ocultar cabeçalhos do servidor de backend (por exemplo, X-Powered-By)",
"port_check": "a verificação de integridade básica do nivel TCP tenta se conectar à porta TCP do servidor. A verificação é válida quando o servidor responde com um pacote SYN/ACK", "port_check": "a verificação de integridade básica do nivel TCP tenta se conectar à porta TCP do servidor. A verificação é válida quando o servidor responde com um pacote SYN/ACK",
"maxconn": "número total de conexões permitidas. Isso impede que o processo aceite muitas conexões de uma só vez, o que o protege de ficar sem memória.", "maxconn": "número total de conexões permitidas. Isso impede que o processo aceite muitas conexões de uma só vez, o que o protege de ficar sem memória.",
"server_template": "Criar uma lista de servidores de um modelo", "server_template": "Criar uma lista de servidores de um modelo",

View File

@ -345,6 +345,7 @@
"check_interval_title": "Интервал проверки. В секундах.", "check_interval_title": "Интервал проверки. В секундах.",
"hide_server_tokens": "Скрыть токены сервера", "hide_server_tokens": "Скрыть токены сервера",
"security_headers": "Включить заголовки безопасности", "security_headers": "Включить заголовки безопасности",
"hide_backend_headers": "Скрыть заголовки внутреннего сервера",
} }
%} %}
{% set roles = { {% set roles = {
@ -358,6 +359,7 @@
"desc": { "desc": {
"security_headers": "Включить заголовки безопасности (X-Content-Type-Options и т. д.)", "security_headers": "Включить заголовки безопасности (X-Content-Type-Options и т. д.)",
"hide_server_tokens": "Скрыть версию Nginx в заголовках", "hide_server_tokens": "Скрыть версию Nginx в заголовках",
"hide_backend_headers": "Скрыть заголовки внутреннего сервера (например, X-Powered-By)",
"port_check": "Базовая проверка работоспособности на уровне TCP пытается подключиться к TCP-порту сервера. Проверка действительна, когда сервер отвечает пакетом SYN/ACK.", "port_check": "Базовая проверка работоспособности на уровне TCP пытается подключиться к TCP-порту сервера. Проверка действительна, когда сервер отвечает пакетом SYN/ACK.",
"maxconn": "Общее количество разрешенных подключений для всего процесса. Это не позволяет процессу одновременно принимать слишком много подключений, что защищает его от нехватки памяти.", "maxconn": "Общее количество разрешенных подключений для всего процесса. Это не позволяет процессу одновременно принимать слишком много подключений, что защищает его от нехватки памяти.",
"server_template": "Создать список серверов из шаблона", "server_template": "Создать список серверов из шаблона",

View File

@ -332,6 +332,7 @@
"check_interval_title": "检查间隔(以秒为单位)。", "check_interval_title": "检查间隔(以秒为单位)。",
"hide_server_tokens": "隐藏服务器令牌", "hide_server_tokens": "隐藏服务器令牌",
"security_headers": "启用安全标头", "security_headers": "启用安全标头",
"hide_backend_headers": "隐藏后端服务器标头",
} }
%} %}
{% set roles = { {% set roles = {
@ -345,6 +346,7 @@
"desc": { "desc": {
"security_headers": "启用安全标头X-Content-Type-Options 等)", "security_headers": "启用安全标头X-Content-Type-Options 等)",
"hide_server_tokens": "在标头中隐藏 Nginx 版本", "hide_server_tokens": "在标头中隐藏 Nginx 版本",
"hide_backend_headers": "隐藏后端服务器标头(例如 X-Powered-By",
"port_check": "基本的 TCP 层健康检查尝试连接到服务器的 TCP 端口。当服务器返回 SYN/ACK 数据包时,检查被认为是成功的。", "port_check": "基本的 TCP 层健康检查尝试连接到服务器的 TCP 端口。当服务器返回 SYN/ACK 数据包时,检查被认为是成功的。",
"maxconn": "进程允许的总连接数。这可以防止进程接受过多连接,从而防止内存耗尽。", "maxconn": "进程允许的总连接数。这可以防止进程接受过多连接,从而防止内存耗尽。",
"server_template": "根据模板创建服务器列表。", "server_template": "根据模板创建服务器列表。",

View File

@ -22,11 +22,7 @@
{% if g.user_params['role'] <= 3 %} {% if g.user_params['role'] <= 3 %}
<div class="add-button add-button-big" title="{{lang.words.create|title()}} UDP {{ lang.words.listener }}" onclick="createUDPListener();">+ {{lang.words.create|title()}} UDP {{ lang.words.listener }}</div> <div class="add-button add-button-big" title="{{lang.words.create|title()}} UDP {{ lang.words.listener }}" onclick="createUDPListener();">+ {{lang.words.create|title()}} UDP {{ lang.words.listener }}</div>
{% endif %} {% endif %}
<div class="up-pannel" class="sortable"> <div class="up-pannel" class="sortable"></div>
{% for listener in listeners %}
<div id="listener-{{listener.id}}" class="div-server-hapwi animated-background"></div>
{% endfor %}
</div>
<div id="create-udp-step-1" style="display: none;"> <div id="create-udp-step-1" style="display: none;">
<table class="overview" id="create-udp-step-1-overview" <table class="overview" id="create-udp-step-1-overview"
title="{{lang.words.create|title()}} UDP {{ lang.words.listener }}" title="{{lang.words.create|title()}} UDP {{ lang.words.listener }}"
@ -179,9 +175,8 @@
<p><span class="ui-icon ui-icon-alert" style="float:left; margin:3px 12px 20px 0;"></span>{{lang.phrases.are_you_sure}}</p> <p><span class="ui-icon ui-icon-alert" style="float:left; margin:3px 12px 20px 0;"></span>{{lang.phrases.are_you_sure}}</p>
</div> </div>
<script> <script>
{% for listener in listeners %} getUdpListeners();
getUDPListener('{{listener.id}}'); setInterval(getUdpListeners, 60000);
{% endfor %}
</script> </script>
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View File

@ -105,7 +105,6 @@ class UDPListener(MethodView):
else: else:
if not listener_id: if not listener_id:
kwargs = { kwargs = {
'listeners': udp_sql.select_listeners(g.user_params['group_id']),
'lang': g.user_params['lang'], 'lang': g.user_params['lang'],
'clusters': ha_sql.select_clusters(g.user_params['group_id']), 'clusters': ha_sql.select_clusters(g.user_params['group_id']),
'is_needed_tool': common.is_tool('ansible'), 'is_needed_tool': common.is_tool('ansible'),