Pavel Loginov 2022-03-02 23:09:39 +03:00
parent 8a83efafe5
commit f696d8ed63
29 changed files with 1732 additions and 103 deletions

View File

@ -50,9 +50,13 @@ else:
servers = sql.get_dick_permit() servers = sql.get_dick_permit()
if serv is not None: if serv is not None:
cfg = configs_dir + serv + "-" + funct.get_data('config') + "."+file_format if service == 'nginx':
conf_file_name_short = config_file_name.split('/')[-1]
cfg = configs_dir + serv + "-" + conf_file_name_short + "-" + funct.get_data('config') + "." + file_format
else:
cfg = configs_dir + serv + "-" + funct.get_data('config') + "."+file_format
if serv is not None and form.getvalue('open') is not None: if serv is not None and form.getvalue('open') is not None and form.getvalue('new_config') is None:
funct.check_is_server_in_group(serv) funct.check_is_server_in_group(serv)
if service == 'keepalived': if service == 'keepalived':
error = funct.get_config(serv, cfg, keepalived=1) error = funct.get_config(serv, cfg, keepalived=1)
@ -82,6 +86,9 @@ if serv is not None and form.getvalue('open') is not None:
os.system("/bin/mv %s %s.old" % (cfg, cfg)) os.system("/bin/mv %s %s.old" % (cfg, cfg))
if form.getvalue('new_config') is not None:
config_read = ' '
if serv is not None and form.getvalue('config') is not None: if serv is not None and form.getvalue('config') is not None:
import sys import sys
funct.check_is_server_in_group(serv) funct.check_is_server_in_group(serv)

View File

@ -29,8 +29,8 @@ def default_values():
{'param': 'lists_path', 'value': 'lists', 'section': 'main', {'param': 'lists_path', 'value': 'lists', 'section': 'main',
'desc': 'Path to the black and the wild list. The value of this paramer should be specified as a relative path beginning with $HOME_ROXY-WI', 'desc': 'Path to the black and the wild list. The value of this paramer should be specified as a relative path beginning with $HOME_ROXY-WI',
'group': '1'}, 'group': '1'},
{'param': 'haproxy_path_logs', 'value': '/var/log/haproxy/access.log', 'section': 'haproxy', {'param': 'haproxy_path_logs', 'value': '/var/log/haproxy/', 'section': 'haproxy',
'desc': 'The default local path for saving logs', 'group': '1'}, 'desc': 'The path for HAProxy logs', 'group': '1'},
{'param': 'syslog_server_enable', 'value': '0', 'section': 'logs', {'param': 'syslog_server_enable', 'value': '0', 'section': 'logs',
'desc': 'Enable getting logs from a syslog server; (0 - no, 1 - yes)', 'group': '1'}, 'desc': 'Enable getting logs from a syslog server; (0 - no, 1 - yes)', 'group': '1'},
{'param': 'syslog_server', 'value': '', 'section': 'logs', 'desc': 'IP address of the syslog_server', {'param': 'syslog_server', 'value': '', 'section': 'logs', 'desc': 'IP address of the syslog_server',
@ -57,8 +57,8 @@ def default_values():
'group': '1'}, 'group': '1'},
{'param': 'apache_log_path', 'value': '/var/log/'+apache_dir+'/', 'section': 'logs', 'desc': 'Path to Apache logs', {'param': 'apache_log_path', 'value': '/var/log/'+apache_dir+'/', 'section': 'logs', 'desc': 'Path to Apache logs',
'group': '1'}, 'group': '1'},
{'param': 'nginx_path_error_logs', 'value': '/var/log/nginx/error.log', 'section': 'nginx', {'param': 'nginx_path_logs', 'value': '/var/log/nginx/', 'section': 'nginx',
'desc': 'Nginx error log', 'group': '1'}, 'desc': 'The path for Nginx logs', 'group': '1'},
{'param': 'nginx_stats_user', 'value': 'admin', 'section': 'nginx', 'desc': 'Username for accessing Nginx stats page', {'param': 'nginx_stats_user', 'value': 'admin', 'section': 'nginx', 'desc': 'Username for accessing Nginx stats page',
'group': '1'}, 'group': '1'},
{'param': 'nginx_stats_password', 'value': 'password', 'section': 'nginx', {'param': 'nginx_stats_password', 'value': 'password', 'section': 'nginx',
@ -873,8 +873,30 @@ def update_db_v_5_4_2(**kwargs):
print("Updating... DB has been updated to version 5.4.2") print("Updating... DB has been updated to version 5.4.2")
def update_db_v_5_4_3(**kwargs):
query = Setting.update(param='nginx_path_logs', value='/var/log/nginx/').where(Setting.param == 'nginx_path_error_logs')
try:
query.execute()
except Exception as e:
print("An error occurred:", e)
else:
if kwargs.get('silent') != 1:
print("Updating... DB has been updated to version 5.4.3")
def update_db_v_5_4_3_1(**kwargs):
query = Setting.update( value='/etc/nginx/').where(Setting.param == 'nginx_dir')
try:
query.execute()
except Exception as e:
print("An error occurred:", e)
else:
if kwargs.get('silent') != 1:
print("Updating... DB has been updated to version 5.4.3-1")
def update_ver(): def update_ver():
query = Version.update(version='5.4.2.0') query = Version.update(version='5.4.3.0')
try: try:
query.execute() query.execute()
except: except:
@ -911,6 +933,8 @@ def update_all():
update_db_v_5_3_2() update_db_v_5_3_2()
update_db_v_5_3_2_2() update_db_v_5_3_2_2()
update_db_v_5_4_2() update_db_v_5_4_2()
update_db_v_5_4_3()
update_db_v_5_4_3_1()
update_ver() update_ver()
@ -944,6 +968,8 @@ def update_all_silent():
update_db_v_5_3_2(silent=1) update_db_v_5_3_2(silent=1)
update_db_v_5_3_2_2(silent=1) update_db_v_5_3_2_2(silent=1)
update_db_v_5_4_2(silent=1) update_db_v_5_4_2(silent=1)
update_db_v_5_4_3(silent=1)
update_db_v_5_4_3_1(silent=1)
update_ver() update_ver()

View File

@ -490,19 +490,29 @@ def diff_config(oldcfg, cfg, **kwargs):
except IOError: except IOError:
print('<center><div class="alert alert-danger">Can\'t read write change to log. %s</div></center>' % stderr) print('<center><div class="alert alert-danger">Can\'t read write change to log. %s</div></center>' % stderr)
pass pass
def get_remote_sections(server_ip: str, service: str) -> str:
import sql
remote_dir = service+'_dir'
config_dir = sql.get_setting(remote_dir)
config_dir = return_nice_path(config_dir)
if service == 'nginx':
section_name = 'server_name'
elif service == 'apache':
section_name = 'ServerName'
commands = ['sudo grep {} {}* -R |grep -v \'$server_name\|#\'|awk \'{{print $1, $3}}\''.format(section_name, config_dir)]
backends = ssh_command(server_ip, commands)
return backends
def get_sections(config, **kwargs): def get_sections(config, **kwargs):
return_config = list() return_config = list()
with open(config, 'r') as f: with open(config, 'r') as f:
for line in f: for line in f:
if kwargs.get('service') == 'nginx': if kwargs.get('service') == 'keepalived':
if 'server_name' in line:
line = line.split('server_name')[1]
line = line.split(';')[0]
line = line.strip()
return_config.append(line)
elif kwargs.get('service') == 'keepalived':
import re import re
ip_pattern = re.compile('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}') ip_pattern = re.compile('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}')
find_ip = re.findall(ip_pattern,line) find_ip = re.findall(ip_pattern,line)
@ -658,7 +668,7 @@ def get_stick_table(table):
def show_installation_output(error, output, service): def show_installation_output(error, output, service):
if error and "WARNING" not in error: if error and "DEPRECATION WARNING" not in error:
logging('localhost', error, haproxywi=1) logging('localhost', error, haproxywi=1)
print('error: '+error) print('error: '+error)
return False return False
@ -1168,9 +1178,35 @@ def show_log(stdout, **kwargs):
return out return out
def show_finding_in_config(stdout: str, **kwargs) -> str:
i = 0
out = ''
grep = ''
line_class = 'line'
if kwargs.get('grep'):
import re
grep = kwargs.get('grep')
grep = re.sub(r'[?|$|!|^|*|\]|\[|,| |]', r'', grep)
out += '<div class="line">--</div>'
for line in stdout:
i = i + 1
if kwargs.get('grep'):
line = line.replace(grep, '<span style="color: red; font-weight: bold;">'+grep+'</span>')
line_class = "line" if '--' in line else "line3"
out += '<div class="'+line_class+'">' + line + '</div>'
out += '<div class="line">--</div>'
return out
def show_haproxy_log(serv, rows=10, waf='0', grep=None, hour='00', minut='00', hour1='24', minut1='00', service='haproxy', **kwargs): def show_haproxy_log(serv, rows=10, waf='0', grep=None, hour='00', minut='00', hour1='24', minut1='00', service='haproxy', **kwargs):
import sql import sql
exgrep = form.getvalue('exgrep') exgrep = form.getvalue('exgrep')
log_file = form.getvalue('file')
date = hour+':'+minut date = hour+':'+minut
date1 = hour1+':'+minut1 date1 = hour1+':'+minut1
cmd = '' cmd = ''
@ -1189,11 +1225,11 @@ def show_haproxy_log(serv, rows=10, waf='0', grep=None, hour='00', minut='00', h
syslog_server_enable = sql.get_setting('syslog_server_enable') syslog_server_enable = sql.get_setting('syslog_server_enable')
if syslog_server_enable is None or syslog_server_enable == 0: if syslog_server_enable is None or syslog_server_enable == 0:
if service == 'nginx': if service == 'nginx':
local_path_logs = sql.get_setting('nginx_path_error_logs') local_path_logs = sql.get_setting('nginx_path_logs')
commands = ["sudo cat %s| awk '$2>\"%s:00\" && $2<\"%s:00\"' |tail -%s %s %s" % (local_path_logs, date, date1, rows, grep_act, exgrep_act)] commands = ["sudo cat %s/%s |tail -%s %s %s" % (local_path_logs, log_file, rows, grep_act, exgrep_act)]
else: else:
local_path_logs = sql.get_setting('haproxy_path_logs') local_path_logs = sql.get_setting('haproxy_path_logs')
commands = ["sudo cat %s| awk '$3>\"%s:00\" && $3<\"%s:00\"' |tail -%s %s %s" % (local_path_logs, date, date1, rows, grep_act, exgrep_act)] commands = ["sudo cat %s/%s| awk '$3>\"%s:00\" && $3<\"%s:00\"' |tail -%s %s %s" % (local_path_logs, log_file, date, date1, rows, grep_act, exgrep_act)]
syslog_server = serv syslog_server = serv
else: else:
commands = ["sudo cat /var/log/%s/syslog.log | sed '/ %s:00/,/ %s:00/! d' |tail -%s %s %s %s" % (serv, date, date1, rows, grep_act, grep, exgrep_act)] commands = ["sudo cat /var/log/%s/syslog.log | sed '/ %s:00/,/ %s:00/! d' |tail -%s %s %s %s" % (serv, date, date1, rows, grep_act, grep, exgrep_act)]
@ -1399,15 +1435,25 @@ def get_files(dir=get_config_var('configs', 'haproxy_save_configs_dir'), format=
def get_remote_files(server_ip: str, config_dir: str, file_format: str): def get_remote_files(server_ip: str, config_dir: str, file_format: str):
if 'nginx' not in config_dir: config_dir = return_nice_path(config_dir)
return 'error: The path must contain the name of the service. Check it in Roxy-WI settings' if file_format == 'conf':
if config_dir[-1] != '/': commands = ['ls ' + config_dir + '*/*.' + file_format]
config_dir += '/' else:
commands = ['ls ' + config_dir + '*.' + file_format] commands = ['ls ' + config_dir + '/*.' + file_format]
config_files = ssh_command(server_ip, commands) config_files = ssh_command(server_ip, commands)
return config_files return config_files
def return_nice_path(return_path: str) -> str:
if 'nginx' not in return_path and 'haproxy' not in return_path:
return 'error: The path must contain the name of the service. Check it in Roxy-WI settings'
if return_path[-1] != '/':
return_path += '/'
return return_path
def get_key(item): def get_key(item):
return item[0] return item[0]
@ -1693,6 +1739,26 @@ def get_system_info(server_ip: str) -> bool:
except Exception: except Exception:
pass pass
try:
if b['class'] == 'storage':
for p, pval in b.items():
if isinstance(pval, list):
for disks_info in pval:
for volume_info in disks_info['children']:
if isinstance(volume_info['logicalname'], list):
volume_name = volume_info['logicalname'][0]
mount_point = volume_info['logicalname'][1]
size = round(volume_info['capacity'] / 1073741824)
size = str(size) + 'Gb'
fs = volume_info['configuration']['mount.fstype']
state = volume_info['configuration']['state']
disks[volume_name] = {'mount_point': mount_point,
'size': size,
'fs': fs,
'state': state}
except Exception:
pass
try: try:
if b['class'] == 'bridge': if b['class'] == 'bridge':
if 'children' in b: if 'children' in b:
@ -1764,6 +1830,20 @@ def get_system_info(server_ip: str) -> bool:
try: try:
for q in y['children']: for q in y['children']:
for o in q['children']: for o in q['children']:
try:
volume_name = o['logicalname']
mount_point = ''
size = round(o['size'] / 1073741824)
size = str(size) + 'Gb'
fs = ''
state = ''
disks[volume_name] = {
'mount_point': mount_point,
'size': size,
'fs': fs,
'state': state}
except Exception:
pass
for w in o['children']: for w in o['children']:
try: try:
if isinstance(w['logicalname'], list): if isinstance(w['logicalname'], list):

View File

@ -39,6 +39,9 @@ except Exception:
if service == 'nginx': if service == 'nginx':
if funct.check_login(service=2): if funct.check_login(service=2):
title = "Nginx`s logs" title = "Nginx`s logs"
elif waf == '1':
if funct.check_login(service=1):
title = "WAF logs"
else: else:
if funct.check_login(service=1): if funct.check_login(service=1):
title = "HAProxy`s logs" title = "HAProxy`s logs"

View File

@ -479,36 +479,36 @@ if act == "overviewHapserverBackends":
if service == 'haproxy': if service == 'haproxy':
configs_dir = funct.get_config_var('configs', 'haproxy_save_configs_dir') configs_dir = funct.get_config_var('configs', 'haproxy_save_configs_dir')
format_file = 'cfg' format_file = 'cfg'
elif service == 'nginx':
configs_dir = funct.get_config_var('configs', 'nginx_save_configs_dir')
format_file = 'conf'
elif service == 'keepalived': elif service == 'keepalived':
configs_dir = funct.get_config_var('configs', 'kp_save_configs_dir') configs_dir = funct.get_config_var('configs', 'kp_save_configs_dir')
format_file = 'conf' format_file = 'conf'
try: if service != 'nginx':
sections = funct.get_sections(configs_dir + funct.get_files(dir=configs_dir, format=format_file)[0], service=service) try:
except Exception as e: sections = funct.get_sections(configs_dir + funct.get_files(dir=configs_dir, format=format_file)[0], service=service)
funct.logging('localhost', str(e), haproxywi=1) except Exception as e:
funct.logging('localhost', str(e), haproxywi=1)
try: try:
cfg = configs_dir + serv + "-" + funct.get_data('config') + '.' + format_file cfg = configs_dir + serv + "-" + funct.get_data('config') + '.' + format_file
except Exception as e: except Exception as e:
funct.logging('localhost', ' Cannot generate a cfg path ' + str(e), haproxywi=1) funct.logging('localhost', ' Cannot generate a cfg path ' + str(e), haproxywi=1)
try: try:
if service == 'nginx': if service == 'nginx':
error = funct.get_config(serv, cfg, nginx=1) error = funct.get_config(serv, cfg, nginx=1)
elif service == 'keepalived': elif service == 'keepalived':
error = funct.get_config(serv, cfg, keepalived=1) error = funct.get_config(serv, cfg, keepalived=1)
else: else:
error = funct.get_config(serv, cfg) error = funct.get_config(serv, cfg)
except Exception as e: except Exception as e:
funct.logging('localhost', ' Cannot download a config ' + str(e), haproxywi=1) funct.logging('localhost', ' Cannot download a config ' + str(e), haproxywi=1)
try: try:
sections = funct.get_sections(cfg, service=service) sections = funct.get_sections(cfg, service=service)
except Exception as e: except Exception as e:
funct.logging('localhost', ' Cannot get sections from config file ' + str(e), haproxywi=1) funct.logging('localhost', ' Cannot get sections from config file ' + str(e), haproxywi=1)
sections = 'Cannot get backends' sections = 'Cannot get backends'
else:
sections = funct.get_remote_sections(serv, service)
template = template.render(backends=sections, serv=serv, service=service) template = template.render(backends=sections, serv=serv, service=service)
print(template) print(template)
@ -1203,6 +1203,20 @@ if act == 'configShowFiles':
template = template.render(serv=serv, return_files=return_files, config_file_name=config_file_name, path_dir=nginx_config_dir) template = template.render(serv=serv, return_files=return_files, config_file_name=config_file_name, path_dir=nginx_config_dir)
print(template) print(template)
if act == 'showRemoteLogFiles':
service = form.getvalue('service')
log_path = sql.get_setting(service+'_path_logs')
return_files = funct.get_remote_files(serv, log_path, 'log')
if 'error: ' in return_files:
print(return_files)
sys.exit()
from jinja2 import Environment, FileSystemLoader
env = Environment(loader=FileSystemLoader('templates/'), autoescape=True)
template = env.get_template('ajax/show_log_files.html')
template = template.render(serv=serv, return_files=return_files, path_dir=log_path)
print(template)
if form.getvalue('master'): if form.getvalue('master'):
master = form.getvalue('master') master = form.getvalue('master')
slave = form.getvalue('slave') slave = form.getvalue('slave')
@ -3113,7 +3127,7 @@ if form.getvalue('awsvars') or form.getvalue('awseditvars'):
print('error: ' + stderr) print('error: ' + stderr)
else: else:
print('ok') print('ok')
if form.getvalue('dovars') or form.getvalue('doeditvars'): if form.getvalue('dovars') or form.getvalue('doeditvars'):
if form.getvalue('dovars'): if form.getvalue('dovars'):
dovars = form.getvalue('dovars') dovars = form.getvalue('dovars')
@ -3172,7 +3186,7 @@ if form.getvalue('dovalidate') or form.getvalue('doeditvalidate'):
print('error: ' + stderr) print('error: ' + stderr)
else: else:
print('ok') print('ok')
if form.getvalue('doworkspace'): if form.getvalue('doworkspace'):
workspace = form.getvalue('doworkspace') workspace = form.getvalue('doworkspace')
group = form.getvalue('do_create_group') group = form.getvalue('do_create_group')
@ -3335,7 +3349,7 @@ if form.getvalue('awseditworkspace'):
output, stderr = funct.subprocess_execute(cmd) output, stderr = funct.subprocess_execute(cmd)
except Exception as e: except Exception as e:
print('error: ' +str(e)) print('error: ' +str(e))
if stderr != '': if stderr != '':
stderr = stderr.strip() stderr = stderr.strip()
stderr = repr(stderr) stderr = repr(stderr)
@ -4022,3 +4036,19 @@ if act == 'updateSystemInfo':
print(template) print(template)
else: else:
print('error: Cannot update server info') print('error: Cannot update server info')
if act == 'findInConfigs':
server_ip = serv
server_ip = funct.is_ip_or_dns(server_ip)
finding_words = form.getvalue('words')
service = form.getvalue('service')
log_path = sql.get_setting(service + '_dir')
log_path = funct.return_nice_path(log_path)
commands = ['sudo grep "%s" %s* -C 2 -Rn' % (finding_words, log_path)]
return_find = funct.ssh_command(server_ip, commands, raw='1')
return_find = funct.show_finding_in_config(return_find, grep=finding_words)
if 'error: ' in return_find:
print(return_find)
sys.exit()
print(return_find)

View File

@ -10,12 +10,12 @@
- name: populate service facts - name: populate service facts
service_facts: service_facts:
- include: logs.yml
- include: configure.yml - include: configure.yml
- include: installation.yml - include: installation.yml
- include: logs.yml
- name: Add syn_flood tasks - name: Add syn_flood tasks
include: syn_flood.yml include: syn_flood.yml
when: (SYN_FLOOD is defined) and (SYN_FLOOD|length > 0) when: (SYN_FLOOD is defined) and (SYN_FLOOD|length > 0)

View File

@ -40,6 +40,18 @@
http_proxy: "{{PROXY}}" http_proxy: "{{PROXY}}"
https_proxy: "{{PROXY}}" https_proxy: "{{PROXY}}"
- name: install the el9 RPMS for HAProxy
yum:
name:
- yajl-devel
state: latest
when:
- ansible_facts['os_family'] == "RedHat" or ansible_facts['os_family'] == 'CentOS'
- ansible_facts['distribution_major_version'] == '8'
environment:
http_proxy: "{{PROXY}}"
https_proxy: "{{PROXY}}"
- name: install the common RPMS for HAProxy - name: install the common RPMS for HAProxy
yum: yum:
name: name:

View File

@ -6,11 +6,20 @@
{% if backends == 'Cannot get backends' %} {% if backends == 'Cannot get backends' %}
{{backends}} {{backends}}
{% else %} {% else %}
{% if service == 'nginx' or service == 'apache' %}
{% set backends = backends.split('\n') %}
{% endif %}
{% for b in backends %} {% for b in backends %}
{% if service == 'haproxy' %} {% if service == 'haproxy' %}
<a href="/app/sections.py?serv={{ serv}}&section={{b}}" title="Edit backend {{b}}" target="_blank" style="padding-right: 10px;"> <a href="/app/sections.py?serv={{ serv}}&section={{b}}" title="Edit backend {{b}}" target="_blank" style="padding-right: 10px;">
{{b}} {{b}}
</a> </a>
{% elif service == 'nginx' or service == 'apache' %}
{% set full_file = b.split(' ')[0] | replace ('/', '92') %}
{% set full_file = full_file.replace(':', '') %}
<a href="/app/config.py?service={{ service }}&serv={{serv}}&open=open&config_file_name={{full_file}}" title="Edit config {{b.split(' ')[0]}}" target="_blank" style="padding-right: 10px;">
{{b.split(' ')[1] | replace(';', '')}}
</a>
{% else %} {% else %}
{{b}} {{b}}
{% endif %} {% endif %}

View File

@ -1,11 +1,12 @@
{% from 'include/input_macros.html' import input %} {% from 'include/input_macros.html' import input %}
<form action="" method="post"> <div style="text-align: center;margin-top: 20px;">
<div style="text-align: center;margin-top: 20px;"> {% if 'cannot access' not in return_files %}
{% if config_file_name == '' %} {% if config_file_name == '' %}
<h4>Config files from {{serv}}</h4> <h4>Config files from {{serv}}</h4>
{% endif %} {% endif %}
<form action="" method="post">
<p> <p>
<select autofocus required name="config_file_name" id="config_file_name"> <select autofocus required name="config_file_name" id="config_file_name" style="width: 365px;">
<option disabled selected>Select a config file</option> <option disabled selected>Select a config file</option>
{% for file in return_files.split() %} {% for file in return_files.split() %}
{% if file == config_file_name %} {% if file == config_file_name %}
@ -18,7 +19,79 @@
{{ input('serv', type='hidden', value=serv) }} {{ input('serv', type='hidden', value=serv) }}
{{ input('open', type='hidden', value='open') }} {{ input('open', type='hidden', value='open') }}
<a class="ui-button ui-widget ui-corner-all" id="show" title="Compare" onclick="showConfig()">Open</a> <a class="ui-button ui-widget ui-corner-all" id="show" title="Open config" onclick="showConfig()">Open</a>
<a class="ui-button ui-widget ui-corner-all" title="Create a new config file" onclick="addNewConfig('{{serv}}')">Add</a>
<a class="ui-button ui-widget ui-corner-all" title="Lookup in config files" id="open_find_form">Find</a>
</p> </p>
</form>
<form action="" method="post" id="finding_words_from">
<p id="find_p" style="display: none;">
{{ input('words', type='text', style='height: 32.5px;') }}
<button type="submit" name="find" id="find_in_configs" value="Find" title="Find in configs">Find</button>
</p>
{% else %}
<div class="alert alert-warning">{{return_files}}</div>
{% endif %}
</form>
</div> </div>
</form>
<div id="add-new-config" style="display: none">
<div style="padding-top: 15px;">
Config file name: {{ input('new_config_name', type='text', placeholder='conf.d/config_name', title='Format: sub-directory/config_name') }}
{{ input('path_config_name', type='hidden', value=path_dir) }}
</div>
</div>
<script>
$(document).ready(function() {
$('#config_file_name').select2();
$('#finding_words_from').submit(function() {
if ($('#words').val() == '') {
toastr.warning('Enter words for seaching');
return false;
}
findInConfig();
return false;
});
$( "input[type=submit], button" ).button()
$('#open_find_form').on('click', function (){
if ($('#find_p').css('display') == 'none') {
$('#find_p').show();
} else {
$('#find_p').hide();
}
});
});
function addNewConfig(serv) {
$( "#add-new-config" ).dialog({
autoOpen: true,
resizable: false,
height: "auto",
width: 600,
modal: true,
title: "Create a new config file",
show: {
effect: "fade",
duration: 200
},
hide: {
effect: "fade",
duration: 200
},
buttons: {
"Create": function() {
let config_file_name = $('#new_config_name').val();
let path_dir = $('#path_config_name').val();
config_file_name = config_file_name.replaceAll('\/','92');
path_dir = path_dir.replaceAll('\/','92');
window.location.replace('config.py?service=nginx&serv='+serv+'&open=open&config_file_name='+path_dir+'92'+config_file_name+'.conf&new_config=1');
$( this ).dialog( "close" );
},
Cancel: function() {
$( this ).dialog( "close" );
}
}
});
}
$( "select" ).selectmenu();
$("#config_file_name").selectmenu("destroy");
</script>

View File

@ -0,0 +1,20 @@
<select autofocus required name="log_files" id="log_files" style="width: 200px;">
<option disabled selected>Select a log file</option>
{% for file in return_files.split() %}
{% if file == config_file_name %}
<option value="{{ file.split('/')[-1] }}" selected>{{ file.split('/')[-1] }}</option>
{% else %}
<option value="{{ file.split('/')[-1] }}">{{ file.split('/')[-1] }}</option>
{% endif %}
{% endfor %}
</select>
<script>
$(document).ready(function() {
$('#log_files').select2();
});
</script>
<style>
.select2-container .select2-selection--single {
height: 33px;
}
</style>

View File

@ -56,7 +56,7 @@
<td>{{ram_info.size}}Gb</td> <td>{{ram_info.size}}Gb</td>
</tr> </tr>
</table> </table>
<table class="overview-wi"> <table class="overview-wi" style="clear: both;">
<tr class="overviewHead"> <tr class="overviewHead">
<td class="padding10 first-collumn-wi" colspan=2> <td class="padding10 first-collumn-wi" colspan=2>
CPU CPU

View File

@ -1,14 +1,12 @@
<tr> <td class="padding20" style="width: 70%">Current group</td>
<td class="padding20" style="width: 70%">Current group</td> <td>
<td> <select id="newCurrentGroup" name="newCurrentGroup">
<select id="newCurrentGroup" name="newCurrentGroup" > {% for g in groups %}
{% for g in groups %} {% if g.user_group_id|string() == group|string() %}
{% if g.user_group_id|string() == group|string() %} <option value="{{ g.user_group_id }}" selected>{{ g.groups.name }}</option>
<option value="{{ g.user_group_id }}" selected>{{ g.groups.name }}</option> {% else %}
{% else %} <option value="{{ g.user_group_id }}">{{ g.groups.name }}</option>
<option value="{{ g.user_group_id }}">{{ g.groups.name }}</option> {% endif %}
{% endif %} {% endfor %}
{% endfor %} </select>
</select> </td>
</td>
</tr>

View File

@ -37,6 +37,8 @@
<script src="/inc/jquery-1.12.4.js"></script> <script src="/inc/jquery-1.12.4.js"></script>
<script src="/inc/jquery-ui.js"></script> <script src="/inc/jquery-ui.js"></script>
<script src="/inc/js.cookie.min.js"></script> <script src="/inc/js.cookie.min.js"></script>
<link href="/inc/select2.css" rel="stylesheet" />
<script src="/inc/select2.js"></script>
<script src="/inc/script.js"></script> <script src="/inc/script.js"></script>
<script src="/inc/nprogress.js"></script> <script src="/inc/nprogress.js"></script>
<link href="/inc/toastr.css" rel="stylesheet"/> <link href="/inc/toastr.css" rel="stylesheet"/>

View File

@ -1,9 +1,14 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
<link rel="stylesheet" href="/inc/codemirror/codemirror.css"> <link rel="stylesheet" href="/inc/codemirror/codemirror.css">
<link rel="stylesheet" href="/inc/codemirror/dialog.css">
<script src="/inc/codemirror/codemirror.js"></script> <script src="/inc/codemirror/codemirror.js"></script>
<script src="/inc/codemirror/nginx.js"></script> <script src="/inc/codemirror/nginx.js"></script>
<script src="/inc/codemirror/haproxy.js"></script> <script src="/inc/codemirror/haproxy.js"></script>
<script src="/inc/codemirror/dialog.js"></script>
<script src="/inc/codemirror/search.js"></script>
<script src="/inc/codemirror/searchcursor.js"></script>
<script src="/inc/codemirror/jump-to-line.js"></script>
<script src="/inc/configshow.js"></script> <script src="/inc/configshow.js"></script>
<center> <center>
{% if selects|length == 0 %} {% if selects|length == 0 %}
@ -86,10 +91,10 @@
} }
if (cur_url[1].split('&')[0] == 'service=haproxy' || cur_url[1].split('&')[0] == 'service=None') { if (cur_url[1].split('&')[0] == 'service=haproxy' || cur_url[1].split('&')[0] == 'service=None') {
var myCodeMirror = CodeMirror.fromTextArea(document.getElementById("config_text_area"), var myCodeMirror = CodeMirror.fromTextArea(document.getElementById("config_text_area"),
{mode: "haproxy", lineNumbers: true, autocapitalize: true, autocorrect: true, spellcheck: true}); {mode: "haproxy", lineNumbers: true, autocapitalize: true, autocorrect: true, spellcheck: true, autoCloseBrackets: true});
} else { } else {
var myCodeMirror = CodeMirror.fromTextArea(document.getElementById("config_text_area"), var myCodeMirror = CodeMirror.fromTextArea(document.getElementById("config_text_area"),
{mode: "nginx", lineNumbers: true, autocapitalize: true, autocorrect: true, spellcheck: true}); {mode: "nginx", lineNumbers: true, autocapitalize: true, autocorrect: true, spellcheck: true, autoCloseBrackets: true});
} }
</script> </script>
<style> <style>

View File

@ -12,12 +12,14 @@
Server Server
{% endif %} {% endif %}
</td> </td>
{% if select_id != 'viewlogs' and service != 'nginx' %} {% if waf != '1' %}
<td style="width: 5%;">WAF logs</td> <td style="width: 10%;">Log files</td>
{% endif %} {% endif %}
<td>Number rows</td> {% if select_id != 'viewlogs' and service != 'nginx' %}
<td class="padding10 help_cursor"><span title="Find in a log file(supports regular expressions)">Find<span></td> {% endif %}
<td class="padding10 help_cursor"><span title="Exclude from search in a log file(supports regular expressions)">Exclude<span></td> <td style="width: 10%;">Number rows</td>
<td class="help_cursor" style="width: 10%;"><span title="Find in a log file(supports regular expressions)">Find<span></td>
<td class="padding10 help_cursor" style="width: 10%;"><span title="Exclude from search in a log file(supports regular expressions)">Exclude<span></td>
<td style="width: 10%;"> <td style="width: 10%;">
<label for="time_range_out_hour" style="padding: 0">Time range:</label> <label for="time_range_out_hour" style="padding: 0">Time range:</label>
{{ input('time_range_out_hour', value=hour, class='time-range', readonly='readonly') }}:{{ input('time_range_out_minut', value=minut, class='time-range', readonly='readonly') }} {{ input('time_range_out_hour', value=hour, class='time-range', readonly='readonly') }}:{{ input('time_range_out_minut', value=minut, class='time-range', readonly='readonly') }}
@ -49,28 +51,26 @@
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</select> </select>
{% else %} {% else %}
{% include 'include/select.html' %} {% include 'include/select.html' %}
{% endif %} {% endif %}
</td>
{% if select_id != 'viewlogs' and service != 'nginx' %}
<td>
{{ checkbox('waf') }}
</td> </td>
{% if waf != '1' %}
<td id="remote_log_files"></td>
{% endif %} {% endif %}
<td class="padding10" style="width: 10%;"> <td class="padding10" style="width: 10%;">
{{ input('rows', type='number', value=rows, required='required', size='5') }} {{ input('rows', type='number', value=rows, required='required', style='width: 110px;') }}
</td> </td>
<td class="padding10" style="width: 10%;"> <td class="padding10" style="width: 10%;">
{{ input('grep', value=grep) }} {{ input('grep', value=grep, style='width: 110px;') }}
</td> </td>
<td class="padding10" style="width: 10%;"> <td class="padding10" style="width: 10%;">
{{ input('exgrep', value=exgrep) }} {{ input('exgrep', value=exgrep, style='width: 110px;') }}
</td> </td>
<td class="padding10" style="width: 10%;"> <td class="padding10" style="width: 10%;">
<div id="time-range"></div> <div id="time-range"></div>
</td> </td>
<td class="padding10 first-collumn" style="width: 10%;"> <td class="padding10 first-collumn" style="width: 1%;">
<button type="submit" name="Show log" value="Show" id="show_log_button">Show</button> <button type="submit" name="Show log" value="Show" id="show_log_button">Show</button>
</form> </form>
</td> </td>
@ -101,11 +101,22 @@
{% if waf == '1' %} {% if waf == '1' %}
$('#waf').prop('checked', true); $('#waf').prop('checked', true);
{% endif %} {% endif %}
showLog() {% if waf != '1' %}
showRemoteLogFiles()
$( "#serv" ).on('selectmenuchange',function() {
showRemoteLogFiles();
});
{% else %}
showLog()
{% endif %}
if (window.matchMedia('(max-width: 786px)').matches || window.matchMedia('(max-width: 1024px)').matches || window.matchMedia('(max-width: 667px)').matches) { if (window.matchMedia('(max-width: 786px)').matches || window.matchMedia('(max-width: 1024px)').matches || window.matchMedia('(max-width: 667px)').matches) {
$( "#serv" ).selectmenu({ $( "#serv" ).selectmenu({
width: 150 width: 150
}); });
$( "#log_files" ).selectmenu({
width: 150
});
} }
</script> </script>
{% endif %} {% endif %}

View File

@ -95,6 +95,7 @@
<td class="padding10">WAF mode</td> <td class="padding10">WAF mode</td>
<td>Metrics</td> <td>Metrics</td>
<td>Manage rules</td> <td>Manage rules</td>
<td>Log</td>
<td> <td>
<a onclick="showOverviewWaf(ip, hostnamea)" title="Refresh" style="float: right; margin-right: 25px;"> <a onclick="showOverviewWaf(ip, hostnamea)" title="Refresh" style="float: right; margin-right: 25px;">
<span class="service-reload"></span> <span class="service-reload"></span>

32
inc/codemirror/dialog.css Normal file
View File

@ -0,0 +1,32 @@
.CodeMirror-dialog {
position: absolute;
left: 0; right: 0;
background: inherit;
z-index: 15;
padding: .1em .8em;
overflow: hidden;
color: inherit;
}
.CodeMirror-dialog-top {
border-bottom: 1px solid #eee;
top: 0;
}
.CodeMirror-dialog-bottom {
border-top: 1px solid #eee;
bottom: 0;
}
.CodeMirror-dialog input {
border: none;
outline: none;
background: transparent;
width: 20em;
color: inherit;
font-family: monospace;
}
.CodeMirror-dialog button {
font-size: 70%;
}

163
inc/codemirror/dialog.js Normal file
View File

@ -0,0 +1,163 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
// Open simple dialogs on top of an editor. Relies on dialog.css.
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
function dialogDiv(cm, template, bottom) {
var wrap = cm.getWrapperElement();
var dialog;
dialog = wrap.appendChild(document.createElement("div"));
if (bottom)
dialog.className = "CodeMirror-dialog CodeMirror-dialog-bottom";
else
dialog.className = "CodeMirror-dialog CodeMirror-dialog-top";
if (typeof template == "string") {
dialog.innerHTML = template;
} else { // Assuming it's a detached DOM element.
dialog.appendChild(template);
}
CodeMirror.addClass(wrap, 'dialog-opened');
return dialog;
}
function closeNotification(cm, newVal) {
if (cm.state.currentNotificationClose)
cm.state.currentNotificationClose();
cm.state.currentNotificationClose = newVal;
}
CodeMirror.defineExtension("openDialog", function(template, callback, options) {
if (!options) options = {};
closeNotification(this, null);
var dialog = dialogDiv(this, template, options.bottom);
var closed = false, me = this;
function close(newVal) {
if (typeof newVal == 'string') {
inp.value = newVal;
} else {
if (closed) return;
closed = true;
CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');
dialog.parentNode.removeChild(dialog);
me.focus();
if (options.onClose) options.onClose(dialog);
}
}
var inp = dialog.getElementsByTagName("input")[0], button;
if (inp) {
inp.focus();
if (options.value) {
inp.value = options.value;
if (options.selectValueOnOpen !== false) {
inp.select();
}
}
if (options.onInput)
CodeMirror.on(inp, "input", function(e) { options.onInput(e, inp.value, close);});
if (options.onKeyUp)
CodeMirror.on(inp, "keyup", function(e) {options.onKeyUp(e, inp.value, close);});
CodeMirror.on(inp, "keydown", function(e) {
if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; }
if (e.keyCode == 27 || (options.closeOnEnter !== false && e.keyCode == 13)) {
inp.blur();
CodeMirror.e_stop(e);
close();
}
if (e.keyCode == 13) callback(inp.value, e);
});
if (options.closeOnBlur !== false) CodeMirror.on(dialog, "focusout", function (evt) {
if (evt.relatedTarget !== null) close();
});
} else if (button = dialog.getElementsByTagName("button")[0]) {
CodeMirror.on(button, "click", function() {
close();
me.focus();
});
if (options.closeOnBlur !== false) CodeMirror.on(button, "blur", close);
button.focus();
}
return close;
});
CodeMirror.defineExtension("openConfirm", function(template, callbacks, options) {
closeNotification(this, null);
var dialog = dialogDiv(this, template, options && options.bottom);
var buttons = dialog.getElementsByTagName("button");
var closed = false, me = this, blurring = 1;
function close() {
if (closed) return;
closed = true;
CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');
dialog.parentNode.removeChild(dialog);
me.focus();
}
buttons[0].focus();
for (var i = 0; i < buttons.length; ++i) {
var b = buttons[i];
(function(callback) {
CodeMirror.on(b, "click", function(e) {
CodeMirror.e_preventDefault(e);
close();
if (callback) callback(me);
});
})(callbacks[i]);
CodeMirror.on(b, "blur", function() {
--blurring;
setTimeout(function() { if (blurring <= 0) close(); }, 200);
});
CodeMirror.on(b, "focus", function() { ++blurring; });
}
});
/*
* openNotification
* Opens a notification, that can be closed with an optional timer
* (default 5000ms timer) and always closes on click.
*
* If a notification is opened while another is opened, it will close the
* currently opened one and open the new one immediately.
*/
CodeMirror.defineExtension("openNotification", function(template, options) {
closeNotification(this, close);
var dialog = dialogDiv(this, template, options && options.bottom);
var closed = false, doneTimer;
var duration = options && typeof options.duration !== "undefined" ? options.duration : 5000;
function close() {
if (closed) return;
closed = true;
clearTimeout(doneTimer);
CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');
dialog.parentNode.removeChild(dialog);
}
CodeMirror.on(dialog, 'click', function(e) {
CodeMirror.e_preventDefault(e);
close();
});
if (duration)
doneTimer = setTimeout(close, duration);
return close;
});
});

274
inc/codemirror/docs.css Normal file
View File

@ -0,0 +1,274 @@
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
src: local('Source Sans Pro'), local('SourceSansPro-Regular'), url(//themes.googleusercontent.com/static/fonts/sourcesanspro/v5/ODelI1aHBYDBqgeIAH2zlBM0YzuT7MdOe03otPbuUS0.woff) format('woff');
}
body, html { margin: 0; padding: 0; height: 100%; }
section, article { display: block; padding: 0; }
body {
background: #f8f8f8;
font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif;
line-height: 1.5;
}
p { margin-top: 0; }
h2, h3, h1 {
font-weight: normal;
margin-bottom: .7em;
}
h1 { font-size: 140%; }
h2 { font-size: 120%; }
h3 { font-size: 110%; }
article > h2:first-child, section:first-child > h2 { margin-top: 0; }
#nav h1 {
margin-right: 12px;
margin-top: 0;
margin-bottom: 2px;
color: #d30707;
letter-spacing: .5px;
}
a, a:visited, a:link, .quasilink {
color: #A21313;
}
em {
padding-right: 2px;
}
.quasilink {
cursor: pointer;
}
article {
max-width: 700px;
margin: 0 0 0 160px;
border-left: 2px solid #E30808;
border-right: 1px solid #ddd;
padding: 30px 50px 100px 50px;
background: white;
z-index: 2;
position: relative;
min-height: 100%;
box-sizing: border-box;
-moz-box-sizing: border-box;
}
#nav {
position: fixed;
padding-top: 30px;
max-height: 100%;
box-sizing: -moz-border-box;
box-sizing: border-box;
overflow-y: auto;
left: 0; right: none;
width: 160px;
text-align: right;
z-index: 1;
}
@media screen and (min-width: 1000px) {
article {
margin: 0 auto;
}
#nav {
right: 50%;
width: auto;
border-right: 349px solid transparent;
}
}
#nav ul {
display: block;
margin: 0; padding: 0;
margin-bottom: 32px;
}
#nav a {
text-decoration: none;
}
#nav li {
display: block;
margin-bottom: 4px;
}
#nav li ul {
font-size: 80%;
margin-bottom: 0;
display: none;
}
#nav li.active ul {
display: block;
}
#nav li li a {
padding-right: 20px;
display: inline-block;
}
#nav ul a {
color: black;
padding: 0 7px 1px 11px;
}
#nav ul a.active, #nav ul a:hover {
border-bottom: 1px solid #E30808;
margin-bottom: -1px;
color: #E30808;
}
#logo {
border: 0;
margin-right: 12px;
margin-bottom: 25px;
}
section {
border-top: 1px solid #E30808;
margin: 1.5em 0;
}
section.first {
border: none;
margin-top: 0;
}
#demo {
position: relative;
}
#demolist {
position: absolute;
right: 5px;
top: 5px;
z-index: 25;
}
.yinyang {
position: absolute;
top: -10px;
left: 0; right: 0;
margin: auto;
display: block;
height: 120px;
}
.actions {
margin: 1em 0 0;
min-height: 100px;
position: relative;
}
.actionspicture {
pointer-events: none;
position: absolute;
height: 100px;
top: 0; left: 0; right: 0;
}
.actionlink {
pointer-events: auto;
font-family: arial;
font-size: 80%;
font-weight: bold;
position: absolute;
top: 0; bottom: 0;
line-height: 1;
height: 1em;
margin: auto;
}
.actionlink.download {
color: white;
right: 50%;
margin-right: 13px;
text-shadow: -1px 1px 3px #b00, -1px -1px 3px #b00, 1px 0px 3px #b00;
}
.actionlink.fund {
color: #b00;
left: 50%;
margin-left: 15px;
}
.actionlink:hover {
text-decoration: underline;
}
.actionlink a {
color: inherit;
}
.actionsleft {
float: left;
}
.actionsright {
float: right;
text-align: right;
}
@media screen and (max-width: 800px) {
.actions {
padding-top: 120px;
}
.actionsleft, .actionsright {
float: none;
text-align: left;
margin-bottom: 1em;
}
}
th {
text-decoration: underline;
font-weight: normal;
text-align: left;
}
#features ul {
list-style: none;
margin: 0 0 1em;
padding: 0 0 0 1.2em;
}
#features li:before {
content: "-";
width: 1em;
display: inline-block;
padding: 0;
margin: 0;
margin-left: -1em;
}
.rel {
margin-bottom: 0;
}
.rel-note {
margin-top: 0;
color: #555;
}
pre {
padding-left: 15px;
border-left: 2px solid #ddd;
}
code {
padding: 0 2px;
}
strong {
text-decoration: underline;
font-weight: normal;
}
.field {
border: 1px solid #A21313;
}

View File

@ -1,6 +1,6 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others // CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE // Distributed under an MIT license: https://codemirror.net/LICENSE
// Modified for HAProxy by HAProxy-WI // Modified for HAProxy by Roxy-WI
(function(mod) { (function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror")); mod(require("../../lib/codemirror"));

View File

@ -0,0 +1,53 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
// Defines jumpToLine command. Uses dialog.js if present.
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"), require("../dialog/dialog"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror", "../dialog/dialog"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
// default search panel location
CodeMirror.defineOption("search", {bottom: false});
function dialog(cm, text, shortText, deflt, f) {
if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true, bottom: cm.options.search.bottom});
else f(prompt(shortText, deflt));
}
function getJumpDialog(cm) {
return cm.phrase("Jump to line:") + ' <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">' + cm.phrase("(Use line:column or scroll% syntax)") + '</span>';
}
function interpretLine(cm, string) {
var num = Number(string)
if (/^[-+]/.test(string)) return cm.getCursor().line + num
else return num - 1
}
CodeMirror.commands.jumpToLine = function(cm) {
var cur = cm.getCursor();
dialog(cm, getJumpDialog(cm), cm.phrase("Jump to line:"), (cur.line + 1) + ":" + cur.ch, function(posStr) {
if (!posStr) return;
var match;
if (match = /^\s*([\+\-]?\d+)\s*\:\s*(\d+)\s*$/.exec(posStr)) {
cm.setCursor(interpretLine(cm, match[1]), Number(match[2]))
} else if (match = /^\s*([\+\-]?\d+(\.\d+)?)\%\s*/.exec(posStr)) {
var line = Math.round(cm.lineCount() * Number(match[1]) / 100);
if (/^[-+]/.test(match[1])) line = cur.line + line + 1;
cm.setCursor(line - 1, cur.ch);
} else if (match = /^\s*\:?\s*([\+\-]?\d+)\s*/.exec(posStr)) {
cm.setCursor(interpretLine(cm, match[1]), cur.ch);
}
});
};
CodeMirror.keyMap["default"]["Alt-G"] = "jumpToLine";
});

155
inc/codemirror/modsec.js Normal file
View File

@ -0,0 +1,155 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
// Modified for Modsec by Roxy-WI
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.defineMode("modsec", function(config) {
function words(str) {
var obj = {}, words = str.split(" ");
for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
return obj;
}
var keywords = words(
/* hapDirectiveControl */ "SecRule" +
/* hapDirective */ " SecMarker SecAction"
);
var keywords_block = words(
/* hapDirectiveBlock */ "REQUEST_FILENAME REQUEST_HEADERS REQUEST_PROTOCOL REQUEST_BASENAME REQUEST_HEADERS_NAMES HEADER_NAME ARGS_NAMES ARGS REQUEST_LINE QUERY_STRING REQUEST_BODY"
);
var keywords_important = words(
/*hapDirectiveImportant */ "User-Agent TX tx msg setvar anomaly_score none warning_anomaly_score severity GET HEAD POST PROPFIND OPTIONS phase"
);
var indentUnit = config.indentUnit, type;
function ret(style, tp) {type = tp; return style;}
function tokenBase(stream, state) {
stream.eatWhile(/[\w\$_]/);
var cur = stream.current();
if (keywords.propertyIsEnumerable(cur)) {
return "keyword";
}
else if (keywords_block.propertyIsEnumerable(cur)) {
return "variable-2";
}
else if (keywords_important.propertyIsEnumerable(cur)) {
return "string-2";
}
var ch = stream.next();
if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("meta", stream.current());}
else if (ch == "/" && stream.eat("*")) {
state.tokenize = tokenCComment;
return tokenCComment(stream, state);
}
else if (ch == "<" && stream.eat("!")) {
state.tokenize = tokenSGMLComment;
return tokenSGMLComment(stream, state);
}
else if (ch == "=") ret(null, "compare");
else if ((ch == "~" || ch == "|") && stream.eat("=")) return ret(null, "compare");
else if (ch == "\"" || ch == "'") {
state.tokenize = tokenString(ch);
return state.tokenize(stream, state);
}
else if (ch == "#") {
stream.skipToEnd();
return ret("comment", "comment");
}
else if (ch == "!") {
stream.match(/^\s*\w*/);
return ret("keyword", "important");
}
else if (/\d/.test(ch)) {
stream.eatWhile(/[\w.%]/);
return ret("number", "unit");
}
else if (/[,.+>*\/]/.test(ch)) {
return ret(null, "select-op");
}
else if (/[;{}:\[\]]/.test(ch)) {
return ret(null, ch);
}
else {
stream.eatWhile(/[\w\\\-]/);
return ret("variable", "variable");
}
}
function tokenCComment(stream, state) {
var maybeEnd = false, ch;
while ((ch = stream.next()) != null) {
if (maybeEnd && ch == "/") {
state.tokenize = tokenBase;
break;
}
maybeEnd = (ch == "*");
}
return ret("comment", "comment");
}
function tokenSGMLComment(stream, state) {
var dashes = 0, ch;
while ((ch = stream.next()) != null) {
if (dashes >= 2 && ch == ">") {
state.tokenize = tokenBase;
break;
}
dashes = (ch == "-") ? dashes + 1 : 0;
}
return ret("comment", "comment");
}
function tokenString(quote) {
return function(stream, state) {
var escaped = false, ch;
while ((ch = stream.next()) != null) {
if (ch == quote && !escaped)
break;
escaped = !escaped && ch == "\\";
}
if (!escaped) state.tokenize = tokenBase;
return ret("string", "string");
};
}
return {
startState: function(base) {
return {tokenize: tokenBase,
baseIndent: base || 0,
stack: []};
},
token: function(stream, state) {
if (stream.eatSpace()) return null;
type = null;
var style = state.tokenize(stream, state);
var context = state.stack[state.stack.length-1];
if (type == "hash" && context == "rule") style = "atom";
else if (style == "variable") {
if (context == "rule") style = "number";
else if (!context || context == "@media{") style = "tag";
}
if (context == "rule" && /^[\{\};]$/.test(type))
state.stack.pop();
if (type == "{") {
if (context == "@media") state.stack[state.stack.length-1] = "@media{";
else state.stack.push("{");
}
else if (type == "}") state.stack.pop();
else if (type == "@media") state.stack.push("@media");
else if (context == "{" && type != "comment") state.stack.push("rule");
return style;
},
indent: function(state, textAfter) {
var n = state.stack.length;
if (/^\}/.test(textAfter))
n -= state.stack[state.stack.length-1] == "rule" ? 2 : 1;
return state.baseIndent + n * indentUnit;
},
electricChars: "}"
};
});
CodeMirror.defineMIME("text/x-modsec-conf", "modsec");
});

292
inc/codemirror/search.js Normal file
View File

@ -0,0 +1,292 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
// Define search commands. Depends on dialog.js or another
// implementation of the openDialog method.
// Replace works a little oddly -- it will do the replace on the next
// Ctrl-G (or whatever is bound to findNext) press. You prevent a
// replace by making sure the match is no longer selected when hitting
// Ctrl-G.
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"), require("./searchcursor"), require("../dialog/dialog"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror", "./searchcursor", "../dialog/dialog"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
// default search panel location
CodeMirror.defineOption("search", {bottom: false});
function searchOverlay(query, caseInsensitive) {
if (typeof query == "string")
query = new RegExp(query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"), caseInsensitive ? "gi" : "g");
else if (!query.global)
query = new RegExp(query.source, query.ignoreCase ? "gi" : "g");
return {token: function(stream) {
query.lastIndex = stream.pos;
var match = query.exec(stream.string);
if (match && match.index == stream.pos) {
stream.pos += match[0].length || 1;
return "searching";
} else if (match) {
stream.pos = match.index;
} else {
stream.skipToEnd();
}
}};
}
function SearchState() {
this.posFrom = this.posTo = this.lastQuery = this.query = null;
this.overlay = null;
}
function getSearchState(cm) {
return cm.state.search || (cm.state.search = new SearchState());
}
function queryCaseInsensitive(query) {
return typeof query == "string" && query == query.toLowerCase();
}
function getSearchCursor(cm, query, pos) {
// Heuristic: if the query string is all lowercase, do a case insensitive search.
return cm.getSearchCursor(query, pos, {caseFold: queryCaseInsensitive(query), multiline: true});
}
function persistentDialog(cm, text, deflt, onEnter, onKeyDown) {
cm.openDialog(text, onEnter, {
value: deflt,
selectValueOnOpen: true,
closeOnEnter: false,
onClose: function() { clearSearch(cm); },
onKeyDown: onKeyDown,
bottom: cm.options.search.bottom
});
}
function dialog(cm, text, shortText, deflt, f) {
if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true, bottom: cm.options.search.bottom});
else f(prompt(shortText, deflt));
}
function confirmDialog(cm, text, shortText, fs) {
if (cm.openConfirm) cm.openConfirm(text, fs);
else if (confirm(shortText)) fs[0]();
}
function parseString(string) {
return string.replace(/\\([nrt\\])/g, function(match, ch) {
if (ch == "n") return "\n"
if (ch == "r") return "\r"
if (ch == "t") return "\t"
if (ch == "\\") return "\\"
return match
})
}
function parseQuery(query) {
var isRE = query.match(/^\/(.*)\/([a-z]*)$/);
if (isRE) {
try { query = new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i"); }
catch(e) {} // Not a regular expression after all, do a string search
} else {
query = parseString(query)
}
if (typeof query == "string" ? query == "" : query.test(""))
query = /x^/;
return query;
}
function startSearch(cm, state, query) {
state.queryText = query;
state.query = parseQuery(query);
cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query));
state.overlay = searchOverlay(state.query, queryCaseInsensitive(state.query));
cm.addOverlay(state.overlay);
if (cm.showMatchesOnScrollbar) {
if (state.annotate) { state.annotate.clear(); state.annotate = null; }
state.annotate = cm.showMatchesOnScrollbar(state.query, queryCaseInsensitive(state.query));
}
}
function doSearch(cm, rev, persistent, immediate) {
var state = getSearchState(cm);
if (state.query) return findNext(cm, rev);
var q = cm.getSelection() || state.lastQuery;
if (q instanceof RegExp && q.source == "x^") q = null
if (persistent && cm.openDialog) {
var hiding = null
var searchNext = function(query, event) {
CodeMirror.e_stop(event);
if (!query) return;
if (query != state.queryText) {
startSearch(cm, state, query);
state.posFrom = state.posTo = cm.getCursor();
}
if (hiding) hiding.style.opacity = 1
findNext(cm, event.shiftKey, function(_, to) {
var dialog
if (to.line < 3 && document.querySelector &&
(dialog = cm.display.wrapper.querySelector(".CodeMirror-dialog")) &&
dialog.getBoundingClientRect().bottom - 4 > cm.cursorCoords(to, "window").top)
(hiding = dialog).style.opacity = .4
})
};
persistentDialog(cm, getQueryDialog(cm), q, searchNext, function(event, query) {
var keyName = CodeMirror.keyName(event)
var extra = cm.getOption('extraKeys'), cmd = (extra && extra[keyName]) || CodeMirror.keyMap[cm.getOption("keyMap")][keyName]
if (cmd == "findNext" || cmd == "findPrev" ||
cmd == "findPersistentNext" || cmd == "findPersistentPrev") {
CodeMirror.e_stop(event);
startSearch(cm, getSearchState(cm), query);
cm.execCommand(cmd);
} else if (cmd == "find" || cmd == "findPersistent") {
CodeMirror.e_stop(event);
searchNext(query, event);
}
});
if (immediate && q) {
startSearch(cm, state, q);
findNext(cm, rev);
}
} else {
dialog(cm, getQueryDialog(cm), "Search for:", q, function(query) {
if (query && !state.query) cm.operation(function() {
startSearch(cm, state, query);
state.posFrom = state.posTo = cm.getCursor();
findNext(cm, rev);
});
});
}
}
function findNext(cm, rev, callback) {cm.operation(function() {
var state = getSearchState(cm);
var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);
if (!cursor.find(rev)) {
cursor = getSearchCursor(cm, state.query, rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0));
if (!cursor.find(rev)) return;
}
cm.setSelection(cursor.from(), cursor.to());
cm.scrollIntoView({from: cursor.from(), to: cursor.to()}, 20);
state.posFrom = cursor.from(); state.posTo = cursor.to();
if (callback) callback(cursor.from(), cursor.to())
});}
function clearSearch(cm) {cm.operation(function() {
var state = getSearchState(cm);
state.lastQuery = state.query;
if (!state.query) return;
state.query = state.queryText = null;
cm.removeOverlay(state.overlay);
if (state.annotate) { state.annotate.clear(); state.annotate = null; }
});}
function el(tag, attrs) {
var element = tag ? document.createElement(tag) : document.createDocumentFragment();
for (var key in attrs) {
element[key] = attrs[key];
}
for (var i = 2; i < arguments.length; i++) {
var child = arguments[i]
element.appendChild(typeof child == "string" ? document.createTextNode(child) : child);
}
return element;
}
function getQueryDialog(cm) {
return el("", null,
el("span", {className: "CodeMirror-search-label"}, cm.phrase("Search:")), " ",
el("input", {type: "text", "style": "width: 10em", className: "CodeMirror-search-field"}), " ",
el("span", {style: "color: #888", className: "CodeMirror-search-hint"},
cm.phrase("(Use /re/ syntax for regexp search)")));
}
function getReplaceQueryDialog(cm) {
return el("", null, " ",
el("input", {type: "text", "style": "width: 10em", className: "CodeMirror-search-field"}), " ",
el("span", {style: "color: #888", className: "CodeMirror-search-hint"},
cm.phrase("(Use /re/ syntax for regexp search)")));
}
function getReplacementQueryDialog(cm) {
return el("", null,
el("span", {className: "CodeMirror-search-label"}, cm.phrase("With:")), " ",
el("input", {type: "text", "style": "width: 10em", className: "CodeMirror-search-field"}));
}
function getDoReplaceConfirm(cm) {
return el("", null,
el("span", {className: "CodeMirror-search-label"}, cm.phrase("Replace?")), " ",
el("button", {}, cm.phrase("Yes")), " ",
el("button", {}, cm.phrase("No")), " ",
el("button", {}, cm.phrase("All")), " ",
el("button", {}, cm.phrase("Stop")));
}
function replaceAll(cm, query, text) {
cm.operation(function() {
for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
if (typeof query != "string") {
var match = cm.getRange(cursor.from(), cursor.to()).match(query);
cursor.replace(text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
} else cursor.replace(text);
}
});
}
function replace(cm, all) {
if (cm.getOption("readOnly")) return;
var query = cm.getSelection() || getSearchState(cm).lastQuery;
var dialogText = all ? cm.phrase("Replace all:") : cm.phrase("Replace:")
var fragment = el("", null,
el("span", {className: "CodeMirror-search-label"}, dialogText),
getReplaceQueryDialog(cm))
dialog(cm, fragment, dialogText, query, function(query) {
if (!query) return;
query = parseQuery(query);
dialog(cm, getReplacementQueryDialog(cm), cm.phrase("Replace with:"), "", function(text) {
text = parseString(text)
if (all) {
replaceAll(cm, query, text)
} else {
clearSearch(cm);
var cursor = getSearchCursor(cm, query, cm.getCursor("from"));
var advance = function() {
var start = cursor.from(), match;
if (!(match = cursor.findNext())) {
cursor = getSearchCursor(cm, query);
if (!(match = cursor.findNext()) ||
(start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
}
cm.setSelection(cursor.from(), cursor.to());
cm.scrollIntoView({from: cursor.from(), to: cursor.to()});
confirmDialog(cm, getDoReplaceConfirm(cm), cm.phrase("Replace?"),
[function() {doReplace(match);}, advance,
function() {replaceAll(cm, query, text)}]);
};
var doReplace = function(match) {
cursor.replace(typeof query == "string" ? text :
text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
advance();
};
advance();
}
});
});
}
CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};
CodeMirror.commands.findPersistent = function(cm) {clearSearch(cm); doSearch(cm, false, true);};
CodeMirror.commands.findPersistentNext = function(cm) {doSearch(cm, false, true, true);};
CodeMirror.commands.findPersistentPrev = function(cm) {doSearch(cm, true, true, true);};
CodeMirror.commands.findNext = doSearch;
CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};
CodeMirror.commands.clearSearch = clearSearch;
CodeMirror.commands.replace = replace;
CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);};
});

View File

@ -0,0 +1,305 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"))
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod)
else // Plain browser env
mod(CodeMirror)
})(function(CodeMirror) {
"use strict"
var Pos = CodeMirror.Pos
function regexpFlags(regexp) {
var flags = regexp.flags
return flags != null ? flags : (regexp.ignoreCase ? "i" : "")
+ (regexp.global ? "g" : "")
+ (regexp.multiline ? "m" : "")
}
function ensureFlags(regexp, flags) {
var current = regexpFlags(regexp), target = current
for (var i = 0; i < flags.length; i++) if (target.indexOf(flags.charAt(i)) == -1)
target += flags.charAt(i)
return current == target ? regexp : new RegExp(regexp.source, target)
}
function maybeMultiline(regexp) {
return /\\s|\\n|\n|\\W|\\D|\[\^/.test(regexp.source)
}
function searchRegexpForward(doc, regexp, start) {
regexp = ensureFlags(regexp, "g")
for (var line = start.line, ch = start.ch, last = doc.lastLine(); line <= last; line++, ch = 0) {
regexp.lastIndex = ch
var string = doc.getLine(line), match = regexp.exec(string)
if (match)
return {from: Pos(line, match.index),
to: Pos(line, match.index + match[0].length),
match: match}
}
}
function searchRegexpForwardMultiline(doc, regexp, start) {
if (!maybeMultiline(regexp)) return searchRegexpForward(doc, regexp, start)
regexp = ensureFlags(regexp, "gm")
var string, chunk = 1
for (var line = start.line, last = doc.lastLine(); line <= last;) {
// This grows the search buffer in exponentially-sized chunks
// between matches, so that nearby matches are fast and don't
// require concatenating the whole document (in case we're
// searching for something that has tons of matches), but at the
// same time, the amount of retries is limited.
for (var i = 0; i < chunk; i++) {
if (line > last) break
var curLine = doc.getLine(line++)
string = string == null ? curLine : string + "\n" + curLine
}
chunk = chunk * 2
regexp.lastIndex = start.ch
var match = regexp.exec(string)
if (match) {
var before = string.slice(0, match.index).split("\n"), inside = match[0].split("\n")
var startLine = start.line + before.length - 1, startCh = before[before.length - 1].length
return {from: Pos(startLine, startCh),
to: Pos(startLine + inside.length - 1,
inside.length == 1 ? startCh + inside[0].length : inside[inside.length - 1].length),
match: match}
}
}
}
function lastMatchIn(string, regexp, endMargin) {
var match, from = 0
while (from <= string.length) {
regexp.lastIndex = from
var newMatch = regexp.exec(string)
if (!newMatch) break
var end = newMatch.index + newMatch[0].length
if (end > string.length - endMargin) break
if (!match || end > match.index + match[0].length)
match = newMatch
from = newMatch.index + 1
}
return match
}
function searchRegexpBackward(doc, regexp, start) {
regexp = ensureFlags(regexp, "g")
for (var line = start.line, ch = start.ch, first = doc.firstLine(); line >= first; line--, ch = -1) {
var string = doc.getLine(line)
var match = lastMatchIn(string, regexp, ch < 0 ? 0 : string.length - ch)
if (match)
return {from: Pos(line, match.index),
to: Pos(line, match.index + match[0].length),
match: match}
}
}
function searchRegexpBackwardMultiline(doc, regexp, start) {
if (!maybeMultiline(regexp)) return searchRegexpBackward(doc, regexp, start)
regexp = ensureFlags(regexp, "gm")
var string, chunkSize = 1, endMargin = doc.getLine(start.line).length - start.ch
for (var line = start.line, first = doc.firstLine(); line >= first;) {
for (var i = 0; i < chunkSize && line >= first; i++) {
var curLine = doc.getLine(line--)
string = string == null ? curLine : curLine + "\n" + string
}
chunkSize *= 2
var match = lastMatchIn(string, regexp, endMargin)
if (match) {
var before = string.slice(0, match.index).split("\n"), inside = match[0].split("\n")
var startLine = line + before.length, startCh = before[before.length - 1].length
return {from: Pos(startLine, startCh),
to: Pos(startLine + inside.length - 1,
inside.length == 1 ? startCh + inside[0].length : inside[inside.length - 1].length),
match: match}
}
}
}
var doFold, noFold
if (String.prototype.normalize) {
doFold = function(str) { return str.normalize("NFD").toLowerCase() }
noFold = function(str) { return str.normalize("NFD") }
} else {
doFold = function(str) { return str.toLowerCase() }
noFold = function(str) { return str }
}
// Maps a position in a case-folded line back to a position in the original line
// (compensating for codepoints increasing in number during folding)
function adjustPos(orig, folded, pos, foldFunc) {
if (orig.length == folded.length) return pos
for (var min = 0, max = pos + Math.max(0, orig.length - folded.length);;) {
if (min == max) return min
var mid = (min + max) >> 1
var len = foldFunc(orig.slice(0, mid)).length
if (len == pos) return mid
else if (len > pos) max = mid
else min = mid + 1
}
}
function searchStringForward(doc, query, start, caseFold) {
// Empty string would match anything and never progress, so we
// define it to match nothing instead.
if (!query.length) return null
var fold = caseFold ? doFold : noFold
var lines = fold(query).split(/\r|\n\r?/)
search: for (var line = start.line, ch = start.ch, last = doc.lastLine() + 1 - lines.length; line <= last; line++, ch = 0) {
var orig = doc.getLine(line).slice(ch), string = fold(orig)
if (lines.length == 1) {
var found = string.indexOf(lines[0])
if (found == -1) continue search
var start = adjustPos(orig, string, found, fold) + ch
return {from: Pos(line, adjustPos(orig, string, found, fold) + ch),
to: Pos(line, adjustPos(orig, string, found + lines[0].length, fold) + ch)}
} else {
var cutFrom = string.length - lines[0].length
if (string.slice(cutFrom) != lines[0]) continue search
for (var i = 1; i < lines.length - 1; i++)
if (fold(doc.getLine(line + i)) != lines[i]) continue search
var end = doc.getLine(line + lines.length - 1), endString = fold(end), lastLine = lines[lines.length - 1]
if (endString.slice(0, lastLine.length) != lastLine) continue search
return {from: Pos(line, adjustPos(orig, string, cutFrom, fold) + ch),
to: Pos(line + lines.length - 1, adjustPos(end, endString, lastLine.length, fold))}
}
}
}
function searchStringBackward(doc, query, start, caseFold) {
if (!query.length) return null
var fold = caseFold ? doFold : noFold
var lines = fold(query).split(/\r|\n\r?/)
search: for (var line = start.line, ch = start.ch, first = doc.firstLine() - 1 + lines.length; line >= first; line--, ch = -1) {
var orig = doc.getLine(line)
if (ch > -1) orig = orig.slice(0, ch)
var string = fold(orig)
if (lines.length == 1) {
var found = string.lastIndexOf(lines[0])
if (found == -1) continue search
return {from: Pos(line, adjustPos(orig, string, found, fold)),
to: Pos(line, adjustPos(orig, string, found + lines[0].length, fold))}
} else {
var lastLine = lines[lines.length - 1]
if (string.slice(0, lastLine.length) != lastLine) continue search
for (var i = 1, start = line - lines.length + 1; i < lines.length - 1; i++)
if (fold(doc.getLine(start + i)) != lines[i]) continue search
var top = doc.getLine(line + 1 - lines.length), topString = fold(top)
if (topString.slice(topString.length - lines[0].length) != lines[0]) continue search
return {from: Pos(line + 1 - lines.length, adjustPos(top, topString, top.length - lines[0].length, fold)),
to: Pos(line, adjustPos(orig, string, lastLine.length, fold))}
}
}
}
function SearchCursor(doc, query, pos, options) {
this.atOccurrence = false
this.afterEmptyMatch = false
this.doc = doc
pos = pos ? doc.clipPos(pos) : Pos(0, 0)
this.pos = {from: pos, to: pos}
var caseFold
if (typeof options == "object") {
caseFold = options.caseFold
} else { // Backwards compat for when caseFold was the 4th argument
caseFold = options
options = null
}
if (typeof query == "string") {
if (caseFold == null) caseFold = false
this.matches = function(reverse, pos) {
return (reverse ? searchStringBackward : searchStringForward)(doc, query, pos, caseFold)
}
} else {
query = ensureFlags(query, "gm")
if (!options || options.multiline !== false)
this.matches = function(reverse, pos) {
return (reverse ? searchRegexpBackwardMultiline : searchRegexpForwardMultiline)(doc, query, pos)
}
else
this.matches = function(reverse, pos) {
return (reverse ? searchRegexpBackward : searchRegexpForward)(doc, query, pos)
}
}
}
SearchCursor.prototype = {
findNext: function() {return this.find(false)},
findPrevious: function() {return this.find(true)},
find: function(reverse) {
var head = this.doc.clipPos(reverse ? this.pos.from : this.pos.to);
if (this.afterEmptyMatch && this.atOccurrence) {
// do not return the same 0 width match twice
head = Pos(head.line, head.ch)
if (reverse) {
head.ch--;
if (head.ch < 0) {
head.line--;
head.ch = (this.doc.getLine(head.line) || "").length;
}
} else {
head.ch++;
if (head.ch > (this.doc.getLine(head.line) || "").length) {
head.ch = 0;
head.line++;
}
}
if (CodeMirror.cmpPos(head, this.doc.clipPos(head)) != 0) {
return this.atOccurrence = false
}
}
var result = this.matches(reverse, head)
this.afterEmptyMatch = result && CodeMirror.cmpPos(result.from, result.to) == 0
if (result) {
this.pos = result
this.atOccurrence = true
return this.pos.match || true
} else {
var end = Pos(reverse ? this.doc.firstLine() : this.doc.lastLine() + 1, 0)
this.pos = {from: end, to: end}
return this.atOccurrence = false
}
},
from: function() {if (this.atOccurrence) return this.pos.from},
to: function() {if (this.atOccurrence) return this.pos.to},
replace: function(newText, origin) {
if (!this.atOccurrence) return
var lines = CodeMirror.splitLines(newText)
this.doc.replaceRange(lines, this.pos.from, this.pos.to, origin)
this.pos.to = Pos(this.pos.from.line + lines.length - 1,
lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0))
}
}
CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) {
return new SearchCursor(this.doc, query, pos, caseFold)
})
CodeMirror.defineDocExtension("getSearchCursor", function(query, pos, caseFold) {
return new SearchCursor(this, query, pos, caseFold)
})
CodeMirror.defineExtension("selectMatches", function(query, caseFold) {
var ranges = []
var cur = this.getSearchCursor(query, this.getCursor("from"), caseFold)
while (cur.findNext()) {
if (CodeMirror.cmpPos(cur.to(), this.getCursor("to")) > 0) break
ranges.push({anchor: cur.from(), head: cur.to()})
}
if (ranges.length)
this.setSelections(ranges, 0)
})
});

View File

@ -293,9 +293,11 @@ function openVersions() {
win.focus(); win.focus();
} }
function showLog() { function showLog() {
var waf = 0; var waf = findGetParameter('waf');
if ($('#waf').is(':checked')) { var file = $('#log_files').val();
waf = '1'; if (file === null) {
toastr.warning('Select a log file first')
return false;
} }
var rows = $('#rows').val() var rows = $('#rows').val()
var grep = $('#grep').val() var grep = $('#grep').val()
@ -321,15 +323,60 @@ function showLog() {
hour1: hour1, hour1: hour1,
minut1: minut1, minut1: minut1,
service: service, service: service,
file: file,
token: $('#token').val() token: $('#token').val()
}, },
type: "POST", type: "POST",
success: function( data ) { success: function( data ) {
if (data.indexOf('Internal error:') == '-1') { toastr.clear();
$("#ajax").html(data);
window.history.pushState("Logs", "Logs", cur_url[0] + "?service=" + service + "&serv=" + $("#serv").val() +
'&rows=' + rows +
'&exgrep=' + exgrep +
'&grep=' + grep +
'&hour=' + hour +
'&minut=' + minut +
'&hour1=' + hour1 +
'&minut1=' + minut1 +
'&file=' + file +
'&waf=' + waf);
}
} );
}
function showRemoteLogFiles() {
let serv = $('#serv').val();
if (serv === undefined || serv === null) {
toastr.warning('Select a server firts');
return false;
}
var rows = $('#rows').val()
var grep = $('#grep').val()
var exgrep = $('#exgrep').val()
var hour = $('#time_range_out_hour').val()
var minut = $('#time_range_out_minut').val()
var hour1 = $('#time_range_out_hour1').val()
var minut1 = $('#time_range_out_minut1').val()
var service = $('#service').val()
if (service == 'None') {
service = 'haproxy';
}
$.ajax( {
url: "options.py",
data: {
serv: $("#serv").val(),
act: "showRemoteLogFiles",
service: service,
token: $('#token').val()
},
type: "POST",
success: function( data ) {
if (data.indexOf('error:') != '-1' || data.indexOf('ls: cannot access') != '-1') {
toastr.error(data); toastr.error(data);
} else { } else {
toastr.clear(); toastr.clear();
$("#ajax").html(data); $("#remote_log_files").html(data);
$.getScript('/inc/configshow.js');
window.history.pushState("Logs", "Logs", cur_url[0] + "?service=" + service + "&serv=" + $("#serv").val() + window.history.pushState("Logs", "Logs", cur_url[0] + "?service=" + service + "&serv=" + $("#serv").val() +
'&rows=' + rows + '&rows=' + rows +
'&exgrep=' + exgrep + '&exgrep=' + exgrep +
@ -338,10 +385,11 @@ function showLog() {
'&minut=' + minut + '&minut=' + minut +
'&hour1=' + hour1 + '&hour1=' + hour1 +
'&minut1=' + minut1 + '&minut1=' + minut1 +
'&waf=' + waf); '&waf=0');
} }
} }
} ); } );
} }
function clearAllAjaxFields() { function clearAllAjaxFields() {
$("#ajax").empty(); $("#ajax").empty();
@ -429,6 +477,12 @@ function showCompareConfigs() {
function showConfig() { function showConfig() {
var service = $('#service').val(); var service = $('#service').val();
var config_file_name = encodeURI($('#config_file_name').val()); var config_file_name = encodeURI($('#config_file_name').val());
if (service == 'nginx') {
if ($('#config_file_name').val() === undefined || $('#config_file_name').val() === null) {
toastr.warning('Select a config file firts');
return false;
}
}
clearAllAjaxFields(); clearAllAjaxFields();
$.ajax( { $.ajax( {
url: "options.py", url: "options.py",
@ -470,7 +524,6 @@ function showConfigFiles() {
} else { } else {
toastr.clear(); toastr.clear();
$("#ajax-config_file_name").html(data); $("#ajax-config_file_name").html(data);
$( "select" ).selectmenu();
window.history.pushState("Show config", "Show config", cur_url[0] + "?service=" + service + "&serv=" + $("#serv").val() + "&showConfigFiles"); window.history.pushState("Show config", "Show config", cur_url[0] + "?service=" + service + "&serv=" + $("#serv").val() + "&showConfigFiles");
} }
} }
@ -497,7 +550,6 @@ function showConfigFilesForEditing() {
} else { } else {
toastr.clear(); toastr.clear();
$("#ajax-config_file_name").html(data); $("#ajax-config_file_name").html(data);
$("select").selectmenu();
} }
} }
}); });
@ -1298,3 +1350,25 @@ function changeUserPasswordItOwn(d) {
} ); } );
} }
} }
function findInConfig() {
clearAllAjaxFields();
$.ajax( {
url: "options.py",
data: {
serv: $("#serv").val(),
act: "findInConfigs",
service: $("#service").val(),
words: $('#words').val(),
token: $('#token').val()
},
type: "POST",
success: function( data ) {
if (data.indexOf('error:') != '-1') {
toastr.error(data);
} else {
toastr.clear();
$("#ajax").html(data);
}
}
} );
}

1
inc/select2.css Normal file

File diff suppressed because one or more lines are too long

1
inc/select2.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -307,7 +307,7 @@ $( function() {
type: "POST", type: "POST",
success: function( data ) { success: function( data ) {
data = data.replace(/^\s+|\s+$/g,''); data = data.replace(/^\s+|\s+$/g,'');
if(data.indexOf('bash') != '-1' || data.indexOf('such') != '-1') { if(data.indexOf('bash') != '-1' || data.indexOf('such') != '-1' || data.indexOf('command not found') != '-1') {
$('#cur_nginx_ver').text('Nginx has not installed'); $('#cur_nginx_ver').text('Nginx has not installed');
$('#nginx_install').text('Install'); $('#nginx_install').text('Install');
$('#nginx_install').attr('title', 'Install Nginx'); $('#nginx_install').attr('title', 'Install Nginx');

View File

@ -43,6 +43,8 @@
<script src="/inc/toastr.js"></script> <script src="/inc/toastr.js"></script>
<script defer src="/inc/ion.sound.min.js"></script> <script defer src="/inc/ion.sound.min.js"></script>
<script src="/inc/provisioning.js"></script> <script src="/inc/provisioning.js"></script>
<link href="/inc/select2.css" rel="stylesheet" />
<script src="/inc/select2.js"></script>
<meta http-equiv="refresh" content="0; url=/app/overview.py" /> <meta http-equiv="refresh" content="0; url=/app/overview.py" />
</head> </head>
<body style="background-color: #239dee;"> <body style="background-color: #239dee;">