Aidaho 2024-10-02 11:51:26 +03:00
parent 9c961cf6cf
commit 292ca51cc7
48 changed files with 4186 additions and 2127 deletions

View File

@ -4,12 +4,14 @@ from flask_pydantic import validate
from app import app from app import app
from app.api.routes import bp from app.api.routes import bp
from app.views.install.views import InstallView from app.views.install.views import InstallView, InstallGetStatus
from app.views.server.views import ServerView, ServerGroupView, ServerGroupsView, ServersView, ServerIPView from app.views.server.views import ServerView, ServerGroupView, ServerGroupsView, ServersView, ServerIPView
from app.views.server.cred_views import CredView, CredsView from app.views.server.cred_views import CredView, CredsView
from app.views.server.backup_vews import BackupView, S3BackupView, GitBackupView from app.views.server.backup_vews import BackupView, S3BackupView, GitBackupView
from app.views.service.views import (ServiceView, ServiceActionView, ServiceBackendView, ServiceConfigView, from app.views.service.views import (ServiceView, ServiceActionView, ServiceBackendView, ServiceConfigView,
ServiceConfigVersionsView, ServiceConfigList) ServiceConfigVersionsView, ServiceConfigList)
from app.views.service.haproxy_section_views import ListenSectionView, UserListSectionView, PeersSectionView, \
GlobalSectionView, DefaultsSectionView
from app.views.ha.views import HAView, HAVIPView, HAVIPsView from app.views.ha.views import HAView, HAVIPView, HAVIPsView
from app.views.user.views import UserView, UserGroupView, UserRoles from app.views.user.views import UserView, UserGroupView, UserRoles
from app.views.udp.views import UDPListener, UDPListeners, UDPListenerActionView from app.views.udp.views import UDPListener, UDPListeners, UDPListenerActionView
@ -56,12 +58,22 @@ bp.add_url_rule('/ha/<service>/<int:cluster_id>/vips', view_func=HAVIPsView.as_v
register_api(UDPListener, 'udp_listener', '/<service>/listener', 'listener_id') register_api(UDPListener, 'udp_listener', '/<service>/listener', 'listener_id')
bp.add_url_rule('/<service>/listener/<int:listener_id>/<any(start, stop, reload, restart):action>', view_func=UDPListenerActionView.as_view('listener_action'), methods=['GET']) bp.add_url_rule('/<service>/listener/<int:listener_id>/<any(start, stop, reload, restart):action>', view_func=UDPListenerActionView.as_view('listener_action'), methods=['GET'])
bp.add_url_rule('/<service>/listeners', view_func=UDPListeners.as_view('listeners'), methods=['GET']) bp.add_url_rule('/<service>/listeners', view_func=UDPListeners.as_view('listeners'), methods=['GET'])
bp.add_url_rule('/service/<service>/<int:server_id>/install', view_func=InstallGetStatus.as_view('install_status'), methods=['GET'])
bp.add_url_rule('/service/<service>/<server_id>/install', view_func=InstallGetStatus.as_view('install_status_ip'), methods=['GET'])
register_api_id_ip(ServiceView, 'service', '/status', ['GET']) register_api_id_ip(ServiceView, 'service', '/status', ['GET'])
register_api_id_ip(ServiceBackendView, 'service_backend', '/backend', ['GET']) register_api_id_ip(ServiceBackendView, 'service_backend', '/backend', ['GET'])
register_api_id_ip(ServiceConfigView, 'config_view', '/config') register_api_id_ip(ServiceConfigView, 'config_view', '/config')
register_api_id_ip(ServiceConfigVersionsView, 'config_version', '/versions', methods=['GET', 'DELETE']) register_api_id_ip(ServiceConfigVersionsView, 'config_version', '/versions', methods=['GET', 'DELETE'])
register_api_id_ip(ListenSectionView, 'haproxy_section_post', '/section/<any(listen, frontend, backend):section_type>', methods=['POST'])
register_api_id_ip(ListenSectionView, 'haproxy_section', '/section/<any(listen, frontend, backend):section_type>/<section_name>', methods=['GET', 'PUT', 'DELETE'])
register_api_id_ip(UserListSectionView, 'haproxy_userlist_post', '/section/userlist', methods=['POST'])
register_api_id_ip(UserListSectionView, 'haproxy_userlist', '/section/userlist/<section_name>', methods=['GET', 'PUT', 'DELETE'])
register_api_id_ip(PeersSectionView, 'haproxy_peers_post', '/section/peers', methods=['POST'])
register_api_id_ip(PeersSectionView, 'haproxy_peers', '/section/peers/<section_name>', methods=['GET', 'PUT', 'DELETE'])
register_api_id_ip(GlobalSectionView, 'haproxy_global', '/section/global', methods=['GET', 'PUT'])
register_api_id_ip(DefaultsSectionView, 'haproxy_defaults', '/section/defaults', methods=['GET', 'PUT'])
bp.add_url_rule('/service/<any(nginx, apache):service>/<server_id>/config/list', view_func=ServiceConfigList.as_view('config_list_ip'), methods=['GET']) bp.add_url_rule('/service/<any(nginx, apache):service>/<server_id>/config/list', view_func=ServiceConfigList.as_view('config_list_ip'), methods=['GET'])
bp.add_url_rule('/service/<any(nginx, apache):service>/<int:server_id>/config/list', view_func=ServiceConfigList.as_view('config_list'), methods=['GET']) bp.add_url_rule('/service/<any(nginx, apache):service>/<int:server_id>/config/list', view_func=ServiceConfigList.as_view('config_list'), methods=['GET'])
register_api_id_ip(CheckerView, 'checker', '/tools') register_api_id_ip(CheckerView, 'checker', '/tools')

View File

@ -623,7 +623,7 @@ def update_db_v_8_0_2_1():
def update_ver(): def update_ver():
try: try:
Version.update(version='8.0.2').execute() Version.update(version='8.1.0').execute()
except Exception: except Exception:
print('Cannot update version') print('Cannot update version')

View File

@ -1,6 +1,6 @@
import os import os
from flask import render_template, request from flask import render_template
import app.modules.db.sql as sql import app.modules.db.sql as sql
import app.modules.db.add as add_sql import app.modules.db.add as add_sql
@ -17,34 +17,6 @@ import app.modules.roxy_wi_tools as roxy_wi_tools
get_config = roxy_wi_tools.GetConfigVar() get_config = roxy_wi_tools.GetConfigVar()
def save_to_haproxy_config(config: str, server_ip: str, name: str) -> str:
roxywi_common.check_is_server_in_group(server_ip)
cfg = config_common.generate_config_path('haproxy', server_ip)
config_mod.get_config(server_ip, cfg)
try:
with open(cfg, "a") as conf:
conf.write(config)
except IOError as e:
raise Exception(f"error: Cannot read import config file {e}")
try:
output = config_mod.master_slave_upload_and_restart(server_ip, cfg, "save", 'haproxy')
except Exception as e:
raise Exception(e)
try:
roxywi_common.logging(server_ip, f"Add has been added a new {name}")
except Exception:
pass
if output:
return output
else:
return name
def save_nginx_config(config_add: str, server_ip: str, config_name: str) -> str: def save_nginx_config(config_add: str, server_ip: str, config_name: str) -> str:
roxywi_common.check_is_server_in_group(server_ip) roxywi_common.check_is_server_in_group(server_ip)
sub_folder = 'conf.d' if 'upstream' in config_name else 'sites-enabled' sub_folder = 'conf.d' if 'upstream' in config_name else 'sites-enabled'
@ -73,83 +45,6 @@ def save_nginx_config(config_add: str, server_ip: str, config_name: str) -> str:
return config_name return config_name
def get_userlists(config):
return_config = ''
with open(config, 'r') as f:
for line in f:
if line.startswith('userlist'):
line = line.strip()
return_config += line + ','
return return_config
def show_userlist(server_ip: str) -> str:
configs_dir = get_config.get_config_var('configs', 'haproxy_save_configs_dir')
format_file = 'cfg'
try:
sections = get_userlists(configs_dir + roxywi_common.get_files(configs_dir, format_file)[0])
except Exception as e:
roxywi_common.logging('Roxy-WI server', str(e), roxywi=1)
try:
cfg = config_common.generate_config_path('haproxy', server_ip)
except Exception as e:
roxywi_common.logging('Roxy-WI server', f' Cannot generate a cfg path {e}', roxywi=1)
try:
config_mod.get_config(server_ip, cfg)
except Exception as e:
roxywi_common.logging('Roxy-WI server', f' Cannot download a config {e}', roxywi=1)
try:
sections = get_userlists(cfg)
except Exception as e:
roxywi_common.logging('Roxy-WI server', f' Cannot get Userlists from the config file {e}', roxywi=1)
sections = 'error: Cannot get Userlists'
return sections
def add_userlist() -> str:
generate = request.form.get('generateconfig')
server_ip = request.form.get('serv')
name = f"userlist {request.form.get('new_userlist')}\n"
new_userlist_groups = ""
if request.form.get('userlist-group') is not None:
groups = request.form.getlist('userlist-group')
for group in groups:
if group == '':
continue
new_userlist_groups += f" group {group}\n"
new_users_list = ""
if request.form.get('userlist-user') is not None:
users = request.form.getlist('userlist-user')
passwords = request.form.getlist('userlist-password')
userlist_user_group = request.form.getlist('userlist-user-group')
i = 0
for user in users:
if user == '':
continue
try:
group = f' groups {userlist_user_group[i]}'
except Exception:
group = ''
new_users_list += f" user {user} insecure-password {passwords[i]} {group}\n"
i += 1
config_add = "\n" + name + new_userlist_groups + new_users_list
if generate:
return config_add
else:
try:
return save_to_haproxy_config(config_add, server_ip, name)
except Exception as e:
return str(e)
def get_bwlist(color: str, group: str, list_name: str) -> str: def get_bwlist(color: str, group: str, list_name: str) -> str:
lib_path = get_config.get_config_var('main', 'lib_path') lib_path = get_config.get_config_var('main', 'lib_path')
list_path = f"{lib_path}/lists/{group}/{color}/{list_name}" list_path = f"{lib_path}/lists/{group}/{color}/{list_name}"
@ -411,15 +306,6 @@ def get_saved_option(group: str, term: str) -> dict:
return a return a
def update_saved_option(option, option_id) -> bool:
try:
add_sql.update_options(option, option_id)
except Exception as e:
raise Exception(e)
else:
return True
def create_saved_server(server: str, group: str, desc: str) -> str: def create_saved_server(server: str, group: str, desc: str) -> str:
if add_sql.insert_new_saved_server(server, desc, group): if add_sql.insert_new_saved_server(server, desc, group):
return render_template('ajax/new_saved_servers.html', server=add_sql.select_saved_servers(server=server)) return render_template('ajax/new_saved_servers.html', server=add_sql.select_saved_servers(server=server))

View File

@ -1,5 +1,8 @@
from app.modules.db.db_model import SavedServer, Option from typing import Union
from app.modules.db.db_model import SavedServer, Option, HaproxySection
from app.modules.db.common import out_error from app.modules.db.common import out_error
from app.modules.roxywi.class_models import HaproxyConfigRequest, HaproxyGlobalRequest, HaproxyDefaultsRequest
from app.modules.roxywi.exception import RoxywiResourceNotFound from app.modules.roxywi.exception import RoxywiResourceNotFound
@ -26,9 +29,6 @@ def delete_option(option_id):
Option.delete().where(Option.id == option_id).execute() Option.delete().where(Option.id == option_id).execute()
except Exception as e: except Exception as e:
out_error(e) out_error(e)
return False
else:
return True
def insert_new_saved_server(server, description, group): def insert_new_saved_server(server, description, group):
@ -91,3 +91,53 @@ def select_saved_servers(**kwargs):
out_error(e) out_error(e)
else: else:
return query_res return query_res
def insert_new_section(server_id: int, section_type: str, section_name: str, body: HaproxyConfigRequest):
try:
HaproxySection.insert(
server_id=server_id,
type=section_type,
name=section_name,
config=body.model_dump(mode='json')
).execute()
except Exception as e:
out_error(e)
def insert_or_update_new_section(server_id: int, section_type: str, section_name: str, body: Union[HaproxyGlobalRequest, HaproxyDefaultsRequest]):
try:
HaproxySection.insert(
server_id=server_id,
type=section_type,
name=section_name,
config=body.model_dump(mode='json')
).on_conflict('replace').execute()
except Exception as e:
out_error(e)
def update_section(server_id: int, section_type: str, section_name: str, body: HaproxyConfigRequest):
try:
HaproxySection.update(
config=body.model_dump(mode='json')
).where(
(HaproxySection.server_id == server_id) & (HaproxySection.type == section_type) & (HaproxySection.name == section_name)
).execute()
except HaproxySection.DoesNotExist:
raise RoxywiResourceNotFound
except Exception as e:
out_error(e)
def get_section(server_id: int, section_type: str, section_name: str) -> HaproxySection:
try:
return HaproxySection.get(
(HaproxySection.server_id == server_id)
& (HaproxySection.type == section_type)
& (HaproxySection.name == section_name)
)
except HaproxySection.DoesNotExist:
raise RoxywiResourceNotFound
except Exception as e:
out_error(e)

View File

@ -9,6 +9,11 @@ import app.modules.roxy_wi_tools as roxy_wi_tools
get_config = roxy_wi_tools.GetConfigVar() get_config = roxy_wi_tools.GetConfigVar()
mysql_enable = get_config.get_config_var('mysql', 'enable') mysql_enable = get_config.get_config_var('mysql', 'enable')
if mysql_enable == '1':
from playhouse.mysql_ext import JSONField
else:
from playhouse.sqlite_ext import JSONField
class ReconnectMySQLDatabase(ReconnectMixin, MySQLDatabase): class ReconnectMySQLDatabase(ReconnectMixin, MySQLDatabase):
pass pass
@ -767,15 +772,27 @@ class UDPBalancer(BaseModel):
constraints = [SQL('UNIQUE (vip, port)')] constraints = [SQL('UNIQUE (vip, port)')]
class HaproxySection(BaseModel):
id = AutoField
server_id = ForeignKeyField(Server, on_delete='Cascade')
type = CharField()
name = CharField()
config = JSONField()
class Meta:
table_name = 'haproxy_sections'
constraints = [SQL('UNIQUE (server_id, type, name)')]
def create_tables(): def create_tables():
conn = connect() conn = connect()
with conn: with conn:
conn.create_tables( conn.create_tables(
[User, Server, Role, Telegram, Slack, ApiToken, Groups, UserGroups, ConfigVersion, Setting, [User, Server, Role, Telegram, Slack, ApiToken, Groups, UserGroups, ConfigVersion, Setting, RoxyTool, Alerts,
Cred, Backup, Metrics, WafMetrics, Version, Option, SavedServer, Waf, ActionHistory, PortScannerSettings, Cred, Backup, Metrics, WafMetrics, Version, Option, SavedServer, Waf, ActionHistory, PortScannerSettings,
PortScannerPorts, PortScannerHistory, ServiceSetting, MetricsHttpStatus, SMON, WafRules, Alerts, GeoipCodes, PortScannerPorts, PortScannerHistory, ServiceSetting, MetricsHttpStatus, SMON, WafRules, GeoipCodes,
NginxMetrics, SystemInfo, Services, UserName, GitSetting, CheckerSetting, ApacheMetrics, WafNginx, ServiceStatus, NginxMetrics, SystemInfo, Services, UserName, GitSetting, CheckerSetting, ApacheMetrics, WafNginx, ServiceStatus,
KeepaliveRestart, PD, SmonHistory, SmonAgent, SmonTcpCheck, SmonHttpCheck, SmonPingCheck, SmonDnsCheck, S3Backup, RoxyTool, KeepaliveRestart, PD, SmonHistory, SmonAgent, SmonTcpCheck, SmonHttpCheck, SmonPingCheck, SmonDnsCheck, S3Backup,
SmonStatusPage, SmonStatusPageCheck, HaCluster, HaClusterSlave, HaClusterVip, HaClusterVirt, HaClusterService, SmonStatusPage, SmonStatusPageCheck, HaCluster, HaClusterSlave, HaClusterVip, HaClusterVirt, HaClusterService,
HaClusterRouter, MM, UDPBalancer] HaClusterRouter, MM, UDPBalancer, HaproxySection]
) )

View File

@ -290,3 +290,157 @@ class SSLCertUploadRequest(BaseModel):
class SavedServerRequest(BaseModel): class SavedServerRequest(BaseModel):
server: EscapedString server: EscapedString
description: Optional[EscapedString] = None description: Optional[EscapedString] = None
class HaproxyBinds(BaseModel):
ip: Optional[str] = None
port: Annotated[int, Gt(1), Le(65535)]
class HaproxyHeaders(BaseModel):
path: Literal['http-request', 'http-response']
name: str
method: Literal['add-header', 'set-header', 'del-header', 'replace-header', 'pass-header']
value: Optional[str] = None
class HaproxyAcls(BaseModel):
acl_if: int
acl_then: int
acl_then_value: Optional[str] = None
acl_value: str
class HaproxyBackendServer(BaseModel):
server: Union[IPvAnyAddress, DomainName]
port: Annotated[int, Gt(1), Le(65535)]
port_check: Annotated[int, Gt(1), Le(65535)]
maxconn: Optional[int] = 2000
send_proxy: Optional[bool] = 0
backup: Optional[bool] = 0
class HaproxyCookie(BaseModel):
dynamic: str
dynamicKey: str
domain: Optional[str] = None
name: Optional[str] = None
nocache: Optional[str] = None
postonly: Optional[str] = None
prefix: Optional[str] = None
rewrite: Optional[str] = None
class HaproxyHealthCheck(BaseModel):
check: str
domain: Optional[str] = None
path: str
class HaproxySSL(BaseModel):
cert: str
ssl_check_backend: Optional[bool] = 1
class HaproxyServersCheck(BaseModel):
check_enabled: Optional[bool] = 1
fall: Optional[int] = 5
rise: Optional[int] = 2
inter: Optional[int] = 2000
class HaproxyCircuitBreaking(BaseModel):
observe: Literal['layer7', 'layer4']
error_limit: int
on_error: Literal['mark-down', 'fastinter', 'fail-check', 'sudden-death']
class HaproxyConfigRequest(BaseModel):
balance: Optional[Literal['roundrobin', 'source', 'leastconn', 'first', 'rdp-cookie', 'uri', 'uri whole', 'static-rr']] = None
mode: Literal['tcp', 'http'] = 'http'
type: Literal['listen', 'frontend', 'backend']
name: EscapedString
option: Optional[str] = None
maxconn: Optional[int] = 2000
waf: Optional[bool] = 0
binds: List[HaproxyBinds]
headers: List[HaproxyHeaders] = None
acls: List[HaproxyAcls] = None
backend_servers: List[HaproxyBackendServer] = None
blacklist: Optional[str] = ''
whitelist: Optional[str] = ''
ssl: Optional[HaproxySSL] = None
cache: Optional[bool] = 0
compression: Optional[bool] = 0
cookie: Optional[HaproxyCookie] = None
health_check: Optional[HaproxyHealthCheck] = None
servers_check: Optional[HaproxyServersCheck] = None
ssl_offloading: Optional[bool] = 0
redispatch: Optional[bool] = 0
forward_for: Optional[bool] = 0
slow_attack: Optional[bool] = 0
ddos: Optional[bool] = 0
antibot: Optional[bool] = 0
backends: Optional[str] = None
circuit_breaking: Optional[HaproxyCircuitBreaking] = None
class HaproxyUserListUser(BaseModel):
user: str
password: str
group: Optional[str] = ''
class HaproxyUserListRequest(BaseModel):
name: EscapedString
type: Literal['userlist']
userlist_users: Optional[List[HaproxyUserListUser]] = ''
userlist_groups: Optional[List[str]] = ''
class HaproxyPeers(BaseModel):
name: str
ip: Union[IPvAnyAddress, DomainName]
port: Annotated[int, Gt(1), Le(65535)]
class HaproxyPeersRequest(BaseModel):
name: EscapedString
type: Literal['peers']
peers: List[HaproxyPeers]
class GenerateConfigRequest(BaseModel):
generate: Optional[bool] = 0
class HaproxyGlobalRequest(BaseModel):
log: Optional[List[str]] = ['127.0.0.1 local', '127.0.0.1 local1 notice']
chroot: Optional[str] = '/var/lib/haproxy'
pidfile: Optional[str] = '/var/run/haproxy.pid'
maxconn: Optional[int] = 5000
user: Optional[str] = 'haproxy'
group: Optional[str] = 'haproxy'
daemon: Optional[bool] = 1
socket: Optional[List[str]] = ['*:1999 level admin', '/var/run/haproxy.sock mode 600 level admin', '/var/lib/haproxy/stats']
type: Optional[Literal['global']] = 'global'
option: Optional[str] = ''
class HaproxyDefaultsTimeout(BaseModel):
http_request: Optional[int] = 10
queue: Optional[int] = 60
connect: Optional[int] = 10
client: Optional[int] = 60
server: Optional[int] = 60
check: Optional[int] = 10
http_keep_alive: Optional[int] = 10
class HaproxyDefaultsRequest(BaseModel):
log: Optional[str] = 'global'
retries: Optional[int] = 3
timeout: Optional[HaproxyDefaultsTimeout] = HaproxyDefaultsTimeout().model_dump(mode='json')
option: Optional[str] = ''
maxconn: Optional[int] = 5000
type: Optional[Literal['defaults']] = 'defaults'

View File

@ -7,6 +7,7 @@ import ansible
import ansible_runner import ansible_runner
import app.modules.db.sql as sql import app.modules.db.sql as sql
import app.modules.db.add as add_sql
import app.modules.db.ha_cluster as ha_sql import app.modules.db.ha_cluster as ha_sql
import app.modules.db.server as server_sql import app.modules.db.server as server_sql
import app.modules.db.service as service_sql import app.modules.db.service as service_sql
@ -16,7 +17,8 @@ import app.modules.common.common as common
import app.modules.server.server as server_mod import app.modules.server.server as server_mod
import app.modules.roxywi.common as roxywi_common import app.modules.roxywi.common as roxywi_common
from app.modules.server.ssh import return_ssh_keys_path from app.modules.server.ssh import return_ssh_keys_path
from app.modules.roxywi.class_models import ServiceInstall, HAClusterRequest from app.modules.roxywi.class_models import ServiceInstall, HAClusterRequest, HaproxyGlobalRequest, \
HaproxyDefaultsRequest, HaproxyConfigRequest
def generate_udp_inv(listener_id: int, action: str) -> object: def generate_udp_inv(listener_id: int, action: str) -> object:
@ -157,6 +159,33 @@ def generate_haproxy_inv(json_data: ServiceInstall, installed_service: str) -> o
return inv, server_ips return inv, server_ips
def generate_haproxy_section_inv(json_data: dict, cfg: str) -> dict:
cert_path = sql.get_setting('cert_path')
haproxy_dir = sql.get_setting('haproxy_dir')
inv = {"server": {"hosts": {}}}
inv['server']['hosts']['localhost'] = {
"config": json_data,
"cert_path": cert_path,
"haproxy_dir": haproxy_dir,
"cfg": cfg,
"action": 'create'
}
return inv
def generate_haproxy_section_inv_for_del(cfg: str, section_type: str, section_name: str) -> dict:
config = {'type': section_type, 'name': section_name}
inv = {"server": {"hosts": {}}}
inv['server']['hosts']['localhost'] = {
"config": config,
"cfg": cfg,
"action": 'delete'
}
return inv
def generate_service_inv(json_data: ServiceInstall, installed_service: str) -> object: def generate_service_inv(json_data: ServiceInstall, installed_service: str) -> object:
inv = {"server": {"hosts": {}}} inv = {"server": {"hosts": {}}}
server_ips = [] server_ips = []
@ -297,6 +326,55 @@ def run_ansible(inv: dict, server_ips: list, ansible_role: str) -> dict:
return result.stats return result.stats
def run_ansible_locally(inv: dict, ansible_role: str) -> dict:
inventory_path = '/var/www/haproxy-wi/app/scripts/ansible/inventory'
inventory = f'{inventory_path}/{ansible_role}.json'
# proxy = sql.get_setting('proxy')
# proxy_serv = ''
envvars = {
'ANSIBLE_DISPLAY_OK_HOSTS': 'no',
'ANSIBLE_SHOW_CUSTOM_STATS': 'no',
'ANSIBLE_DISPLAY_SKIPPED_HOSTS': "no",
'ANSIBLE_DEPRECATION_WARNINGS': "no",
'ANSIBLE_HOST_KEY_CHECKING': "no",
'ACTION_WARNINGS': "no",
'LOCALHOST_WARNING': "no",
'COMMAND_WARNINGS': "no",
'AWX_DISPLAY': False,
'ANSIBLE_PYTHON_INTERPRETER': '/usr/bin/python3'
}
kwargs = {
'private_data_dir': '/var/www/haproxy-wi/app/scripts/ansible/',
'inventory': inventory,
'envvars': envvars,
'playbook': f'/var/www/haproxy-wi/app/scripts/ansible/roles/{ansible_role}.yml',
}
if os.path.isfile(inventory):
os.remove(inventory)
if not os.path.isdir(inventory_path):
os.makedirs(inventory_path)
try:
with open(inventory, 'a') as invent:
invent.write(str(inv))
except Exception as e:
roxywi_common.handle_exceptions(e, 'Roxy-WI server', 'Cannot save inventory file', roxywi=1)
try:
result = ansible_runner.run(**kwargs)
except Exception as e:
roxywi_common.handle_exceptions(e, 'Roxy-WI server', 'Cannot run Ansible', roxywi=1)
os.remove(inventory)
if result.rc != 0:
raise Exception('Something wrong with installation, check <a href="/app/logs/internal?log_file=roxy-wi.error.log" target="_blank" class="link">Apache logs</a> for details')
return result.stats
def service_actions_after_install(server_ips: str, service: str, json_data) -> None: def service_actions_after_install(server_ips: str, service: str, json_data) -> None:
is_docker = None is_docker = None
update_functions = { update_functions = {
@ -319,6 +397,35 @@ def service_actions_after_install(server_ips: str, service: str, json_data) -> N
service_sql.insert_or_update_service_setting(server_id, service, 'dockerized', '1') service_sql.insert_or_update_service_setting(server_id, service, 'dockerized', '1')
service_sql.insert_or_update_service_setting(server_id, service, 'restart', '1') service_sql.insert_or_update_service_setting(server_id, service, 'restart', '1')
if service == 'haproxy':
try:
_create_default_config_in_db(server_id)
except Exception:
pass
def _create_default_config_in_db(server_id: int) -> None:
hap_sock_p = sql.get_setting('haproxy_sock_port')
stats_port = sql.get_setting('haproxy_stats_port')
stats_user = sql.get_setting('haproxy_stats_user')
stats_password = sql.get_setting('haproxy_stats_password')
config = HaproxyGlobalRequest(
socket=[f'*:{hap_sock_p} level admin', '/var/run/haproxy.sock mode 600 level admin', '/var/lib/haproxy/stats']
)
add_sql.insert_or_update_new_section(server_id, 'global', 'global', config)
add_sql.insert_or_update_new_section(server_id, 'default', 'defaults', HaproxyDefaultsRequest())
option = (
'http-request use-service prometheus-exporter if { path /metrics }\r\nstats enable\r\nstats uri /stats\r\n'
f'stats realm HAProxy-04\ Statistics\r\nstats auth {stats_user}:{stats_password}\r\nstats admin if TRUE'
)
stats_config = HaproxyConfigRequest(
binds=[{'ip': '', 'port': stats_port}],
option=option,
type='listen',
name='stats',
)
add_sql.insert_new_section(server_id, 'listen', 'stats', stats_config)
def install_service(service: str, json_data: Union[str, ServiceInstall, HAClusterRequest], cluster_id: int = None) -> dict: def install_service(service: str, json_data: Union[str, ServiceInstall, HAClusterRequest], cluster_id: int = None) -> dict:
generate_functions = { generate_functions = {

View File

@ -6,17 +6,34 @@ from flask_pydantic import validate
from app.modules.roxywi.class_models import SSLCertUploadRequest, DataStrResponse, SavedServerRequest, BaseResponse from app.modules.roxywi.class_models import SSLCertUploadRequest, DataStrResponse, SavedServerRequest, BaseResponse
from app.routes.add import bp from app.routes.add import bp
import app.modules.db.sql as sql
import app.modules.db.add as add_sql import app.modules.db.add as add_sql
import app.modules.db.server as server_sql
from app.middleware import check_services, get_user_params from app.middleware import check_services, get_user_params
import app.modules.config.add as add_mod import app.modules.config.add as add_mod
import app.modules.common.common as common import app.modules.common.common as common
import app.modules.roxywi.auth as roxywi_auth import app.modules.roxywi.auth as roxywi_auth
import app.modules.roxywi.common as roxywi_common import app.modules.roxywi.common as roxywi_common
import app.modules.roxy_wi_tools as roxy_wi_tools import app.modules.roxy_wi_tools as roxy_wi_tools
from app.views.service.haproxy_section_views import (GlobalSectionView, DefaultsSectionView, ListenSectionView,
UserListSectionView, PeersSectionView)
get_config = roxy_wi_tools.GetConfigVar() get_config = roxy_wi_tools.GetConfigVar()
def register_api_id_ip(view, endpoint, url: str = '', methods: list = ['GET', 'POST']):
for point in ('_id', '_ip'):
view_func = view.as_view(f'{endpoint}_{point}')
pk = 'int:' if point == '_id' else ''
bp.add_url_rule(f'/<any(haproxy, nginx, apache, keepalived):service>/<{pk}server_id>{url}', view_func=view_func, methods=methods)
register_api_id_ip(ListenSectionView, 'haproxy_section_post_a', '/section/<any(listen, frontend, backend):section_type>', methods=['POST'])
register_api_id_ip(ListenSectionView, 'haproxy_section_a', '/section/<any(listen, frontend, backend):section_type>/<section_name>', methods=['GET', 'PUT', 'DELETE'])
register_api_id_ip(UserListSectionView, 'haproxy_userlist_post_a', '/section/userlist', methods=['POST'])
register_api_id_ip(UserListSectionView, 'haproxy_userlist_a', '/section/userlist/<section_name>', methods=['GET', 'PUT', 'DELETE'])
register_api_id_ip(PeersSectionView, 'haproxy_peers_post_a', '/section/peers', methods=['POST'])
register_api_id_ip(PeersSectionView, 'haproxy_peers_a', '/section/peers/<section_name>', methods=['GET', 'PUT', 'DELETE'])
register_api_id_ip(GlobalSectionView, 'haproxy_global_a', '/section/global', methods=['GET', 'PUT'])
register_api_id_ip(DefaultsSectionView, 'haproxy_defaults_a', '/section/defaults', methods=['GET', 'PUT'])
@bp.before_request @bp.before_request
@jwt_required() @jwt_required()
@ -72,325 +89,11 @@ def add(service):
return redirect(url_for('index')) return redirect(url_for('index'))
@bp.route('/haproxy/add', methods=['POST']) @bp.route('/haproxy/get_section_html')
@get_user_params() @get_user_params()
def add_haproxy(): def get_section_html():
""" lang = g.user_params['lang']
Add HAProxy sections return render_template('ajax/config_show_add_sections.html', lang=lang)
:return: Generated section or output of adding status
"""
roxywi_auth.page_for_admin(level=3)
haproxy_dir = sql.get_setting('haproxy_dir')
generate = request.form.get('generateconfig')
server_ip = request.form.get('serv')
port = request.form.getlist('port')
bind = ""
ip = ""
force_close = request.form.get('force_close')
balance = ""
mode = f" mode {request.form.get('mode')}\n"
maxconn = ""
options_split = ""
ssl = ""
ssl_check = ""
backend = ""
headers = ""
acl = ""
servers_split = ""
new_listener = request.form.get('listener')
new_frontend = request.form.get('frontend')
new_backend = request.form.get('new_backend')
if request.form.get('balance') is not None:
balance = " balance " + request.form.get('balance') + "\n"
if request.form.get('health_check') is not None:
health_check = request.form.get('health_check')
if health_check == 'option httpchk' and request.form.get('checks_http_domain') is not None:
health_check = health_check + ' GET ' + request.form.get(
'checks_http_path') + ' "HTTP/1.0\\r\\nHost: ' + request.form.get('checks_http_domain') + '"'
balance += f" {health_check}\n"
if request.form.get('ip') is not None:
ip = request.form.getlist('ip')
if new_listener is not None:
name = f"listen {new_listener}"
end_name = new_listener
elif new_frontend is not None:
name = f"frontend {new_frontend}"
end_name = new_frontend
server_ip = request.form.get('serv2')
elif new_backend is not None:
name = f"backend {new_backend}"
end_name = new_backend
server_ip = request.form.get('serv3')
else:
return 'error: The name cannot be empty'
if request.form.get('backends') != '' and request.form.get('backends'):
backend = f" default_backend {request.form.get('backends')}\n"
if request.form.get('maxconn'):
maxconn = f" maxconn {request.form.get('maxconn')}\n"
if request.form.get('ssl') == "https" and request.form.get('mode') != "tcp":
cert_path = sql.get_setting('cert_path')
if request.form.get('cert') is not None:
ssl = f"ssl crt {cert_path}{request.form.get('cert')}"
if request.form.get('ssl-dis-check') is None:
if request.form.get('ssl-check') == "ssl-check":
ssl_check = " ssl verify none"
else:
ssl_check = " ssl verify"
if ip or port:
if type(port) is list:
i = 0
for _p in port:
if ip[i] == 'IsEmptY':
if ip[i] == 'IsEmptY' and port[i] == 'IsEmptY':
i += 1
continue
else:
port_value = port[i]
bind += f" bind *:{port_value} {ssl}\n"
else:
if port[i] == 'IsEmptY':
return 'error: IP cannot be bind without a port'
else:
port_value = port[i]
bind += f" bind {ip[i]}:{port_value} {ssl}\n"
i += 1
if request.form.get('default-check') == "1":
if request.form.get('check-servers') == "1":
check = f" check inter {request.form.get('inter')} rise {request.form.get('rise')} fall {request.form.get('fall')}{ssl_check}"
else:
check = ""
else:
if request.form.get('check-servers') != "1":
check = ""
else:
check = f" check{ssl_check}"
if request.form.get('option') is not None:
options = request.form.get('option')
i = options.split("\n")
for j in i:
options_split += f" {j}\n"
if force_close == "1":
options_split += " option http-server-close\n"
elif force_close == "2":
options_split += " option forceclose\n"
elif force_close == "3":
options_split += " option http-pretend-keepalive\n"
if request.form.get('whitelist'):
options_split += " tcp-request connection accept if { src -f " + haproxy_dir + "/white/" + request.form.get(
'whitelist') + " }\n"
if request.form.get('blacklist'):
options_split += " tcp-request connection reject if { src -f " + haproxy_dir + "/black/" + request.form.get(
'blacklist') + " }\n"
if request.form.get('cookie'):
cookie = f" cookie {request.form.get('cookie_name')}"
if request.form.get('cookie_domain'):
cookie += f" domain {request.form.get('cookie_domain')}"
if request.form.get('rewrite'):
rewrite = request.form.get('rewrite')
else:
rewrite = ""
if request.form.get('prefix'):
prefix = request.form.get('prefix')
else:
prefix = ""
if request.form.get('nocache'):
nocache = request.form.get('nocache')
else:
nocache = ""
if request.form.get('postonly'):
postonly = request.form.get('postonly')
else:
postonly = ""
if request.form.get('dynamic'):
dynamic = request.form.get('dynamic')
else:
dynamic = ""
cookie += f" {rewrite} {prefix} {nocache} {postonly} {dynamic}\n"
options_split += cookie
if request.form.get('dynamic'):
options_split += f" dynamic-cookie-key {request.form.get('dynamic-cookie-key')}\n"
if request.form.get('headers_res'):
headers_res = request.form.getlist('headers_res')
headers_method = request.form.getlist('headers_method')
header_name = request.form.getlist('header_name')
header_value = request.form.getlist('header_value')
i = 0
for _h in headers_method:
if headers_method[i] != 'del-header':
headers += f' {headers_res[i]} {headers_method[i]} {header_name[i]} {header_value[i]}\n'
else:
headers += f' {headers_res[i]} {headers_method[i]} {header_name[i]}\n'
i += 1
if request.form.get('acl_if'):
acl_if = request.form.getlist('acl_if')
acl_value = request.form.getlist('acl_value')
acl_then = request.form.getlist('acl_then')
acl_then_values = request.form.getlist('acl_then_value')
i = 0
for a in acl_if:
acl_then_value = '' if acl_then_values[i] == 'IsEmptY' else acl_then_values[i]
try:
if a == '1':
acl_if_word = 'hdr_beg(host) -i '
if request.form.get('ssl') == "https" and request.form.get('mode') != "tcp":
acl_if_word = 'ssl_fc_sni -i '
if request.form.get('mode') == "tcp":
acl_if_word = 'req.ssl_sni -i '
elif a == '2':
acl_if_word = 'hdr_end(host) -i '
if request.form.get('ssl') == "https" and request.form.get('mode') != "tcp":
acl_if_word = 'ssl_fc_sni -i '
if request.form.get('mode') == "tcp":
acl_if_word = 'req.ssl_sni -i '
elif a == '3':
acl_if_word = 'path_beg -i '
elif a == '4':
acl_if_word = 'path_end -i '
elif a == '6':
acl_if_word = 'src ip '
else:
acl_if_word = ''
if acl_then[i] == '5':
acl += ' use_backend '
elif acl_then[i] == '2':
acl += ' http-request redirect location '
elif acl_then[i] == '3':
acl += ' http-request allow'
acl_then_value = ''
elif acl_then[i] == '4':
acl += ' http-request deny'
acl_then_value = ''
elif acl_then[i] == '6':
acl += f' acl return_{acl_value[i]} {acl_if_word} {acl_value[i]}\n'
acl += f' http-request return if return_{acl_value[i]}\n'
elif acl_then[i] == '7':
acl += f' acl set_header_{acl_value[i]} {acl_if_word} {acl_value[i]}\n'
acl += f' http-request set-header if set_header_{acl_value[i]}\n'
if acl_then[i] in ('2', '3', '4', '5'):
acl += acl_then_value + ' if { ' + acl_if_word + acl_value[i] + ' } \n'
except Exception:
acl = ''
i += 1
if request.form.get('circuit_breaking') == "1":
observe = 'observe ' + request.form.get('circuit_breaking_observe')
error_limit = ' error-limit ' + request.form.get('circuit_breaking_error_limit')
circuit_breaking_on_error = ' on-error ' + request.form.get('circuit_breaking_on_error')
default_server = ' default-server ' + observe + error_limit + circuit_breaking_on_error + '\n'
servers_split += default_server
if request.form.get('servers'):
servers = request.form.getlist('servers')
server_port = request.form.getlist('server_port')
send_proxy = request.form.getlist('send_proxy')
backup = request.form.getlist('backup')
server_maxconn = request.form.getlist('server_maxconn')
port_check = request.form.getlist('port_check')
i = 0
for server in servers:
if server == '':
continue
if request.form.get('template') is None:
try:
if send_proxy[i] == '1':
send_proxy_param = 'send-proxy'
else:
send_proxy_param = ''
except Exception:
send_proxy_param = ''
try:
if backup[i] == '1':
backup_param = 'backup'
else:
backup_param = ''
except Exception:
backup_param = ''
try:
maxconn_val = server_maxconn[i]
except Exception:
maxconn_val = '200'
try:
port_check_val = port_check[i]
if port_check_val == '':
port_check_val = server_port[i]
except Exception:
port_check_val = server_port[i]
servers_split += " server {0} {0}:{1}{2} port {6} maxconn {5} {3} {4} \n".format(
server, server_port[i], check, send_proxy_param, backup_param, maxconn_val, port_check_val
)
else:
servers_split += " server-template {0} {1} {2}:{3} {4} \n".format(
request.form.get('prefix'), request.form.get('template-number'), server, server_port[i], check
)
i += 1
compression = request.form.get("compression")
cache = request.form.get("cache")
compression_s = ""
cache_s = ""
cache_set = ""
filter_com = ""
if compression == "1" or cache == "2":
filter_com = " filter compression\n"
if cache == "2":
cache_s = f" http-request cache-use {end_name}\n http-response cache-store {end_name}\n"
cache_set = f"cache {end_name}\n total-max-size 4\n max-age 240\n"
if compression == "1":
compression_s = " compression algo gzip\n compression type text/html text/plain text/css\n"
waf = ""
if request.form.get('waf'):
waf = f" filter spoe engine modsecurity config {haproxy_dir}/waf.conf\n"
waf += " http-request deny if { var(txn.modsec.code) -m int gt 0 }\n"
config_add = f"\n{name}\n{bind}{mode}{maxconn}{balance}{options_split}{cache_s}{filter_com}{compression_s}" \
f"{waf}{headers}{acl}{backend}{servers_split}\n{cache_set}\n"
if generate:
return config_add
else:
try:
return add_mod.save_to_haproxy_config(config_add, server_ip, name)
except Exception as e:
return f'error: Cannot add to config: {e}'
@bp.post('/haproxy/userlist')
@get_user_params()
def add_userlist():
"""
Add HAProxy section userlist
:return: Output of adding status
"""
roxywi_auth.page_for_admin(level=3)
return add_mod.add_userlist()
@bp.post('/haproxy/bwlist/create') @bp.post('/haproxy/bwlist/create')
@ -441,42 +144,6 @@ def get_bwlists(color, group):
return add_mod.get_bwlists_for_autocomplete(color, group) return add_mod.get_bwlists_for_autocomplete(color, group)
@bp.route('/haproxy/userlist/<server_ip>')
def show_userlist(server_ip):
server_ip = common.is_ip_or_dns(server_ip)
return add_mod.show_userlist(server_ip)
@bp.post('/haproxy/peers')
def add_peers():
roxywi_auth.page_for_admin(level=3)
generate = request.form.get('generateconfig')
server_ip = request.form.get('serv')
servers_split = ''
name = "peers " + request.form.get('peers-name') + "\n"
servers = request.form.getlist('servers')
server_port = request.form.getlist('server_port')
servers_name = request.form.getlist('servers_name')
i = 0
for server in servers:
if server == '':
continue
servers_split += " peer {0} {1}:{2} \n".format(servers_name[i], server, server_port[i])
i += 1
config_add = "\n" + name + servers_split
if generate:
return config_add, 200
else:
try:
return add_mod.save_to_haproxy_config(config_add, server_ip, name)
except Exception as e:
return f'error: Cannot add to config: {e}'
@bp.route('/option/get/<group>') @bp.route('/option/get/<group>')
def get_option(group): def get_option(group):
term = request.args.get('term') term = request.args.get('term')
@ -554,9 +221,14 @@ def delete_saved_server(server_id):
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot delete server') return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot delete server')
@bp.route('/certs/<int:server_ip>')
@bp.route('/certs/<server_ip>') @bp.route('/certs/<server_ip>')
def get_certs(server_ip): def get_certs(server_ip):
server_ip = common.is_ip_or_dns(server_ip) if isinstance(server_ip, str):
server_ip = common.is_ip_or_dns(server_ip)
elif isinstance(server_ip, int):
server = server_sql.get_server_by_id(server_ip)
server_ip = server.ip
return add_mod.get_ssl_certs(server_ip) return add_mod.get_ssl_certs(server_ip)

View File

@ -33,6 +33,7 @@ def before_request():
@bp.route('/<service>/show', methods=['POST']) @bp.route('/<service>/show', methods=['POST'])
@check_services @check_services
@get_user_params()
def show_config(service): def show_config(service):
config_file_name = request.json.get('config_file_name') config_file_name = request.json.get('config_file_name')
configver = request.json.get('configver') configver = request.json.get('configver')

View File

@ -1,3 +1,4 @@
# BEGIN Roxy-WI MANAGED global do not edit it directly
global global
log 127.0.0.2 local0 log 127.0.0.2 local0
log 127.0.0.1 local1 notice log 127.0.0.1 local1 notice
@ -11,7 +12,9 @@ global
stats socket *:{{SOCK_PORT}} level admin stats socket *:{{SOCK_PORT}} level admin
stats socket /var/run/haproxy.sock mode 600 level admin stats socket /var/run/haproxy.sock mode 600 level admin
server-state-file {{STAT_FILE}} server-state-file {{STAT_FILE}}
# END Roxy-WI MANAGED global do not edit it directly
# BEGIN Roxy-WI MANAGED defaults do not edit it directly
defaults defaults
mode http mode http
log global log global
@ -29,7 +32,9 @@ defaults
timeout http-keep-alive 10s timeout http-keep-alive 10s
timeout check 10s timeout check 10s
maxconn 3000 maxconn 3000
# END Roxy-WI MANAGED defaults do not edit it directly
# BEGIN Roxy-WI MANAGED listen stats do not edit it directly
listen stats listen stats
bind *:{{STAT_PORT}} bind *:{{STAT_PORT}}
http-request use-service prometheus-exporter if { path /metrics } http-request use-service prometheus-exporter if { path /metrics }
@ -38,6 +43,7 @@ listen stats
stats realm HAProxy-04\ Statistics stats realm HAProxy-04\ Statistics
stats auth {{STATS_USER}}:{{STATS_PASS}} stats auth {{STATS_USER}}:{{STATS_PASS}}
stats admin if TRUE stats admin if TRUE
# END Roxy-WI MANAGED listen stats do not edit it directly
{% if M_OR_S != 'None' %} {% if M_OR_S != 'None' %}
peers default_peers peers default_peers

View File

@ -0,0 +1,9 @@
---
- name: Create HAProxy section
hosts: localhost
connection: local
become: yes
become_method: sudo
gather_facts: yes
roles:
- role: haproxy_section

View File

@ -0,0 +1,37 @@
acl_if:
1: 'hdr_beg(host) -i'
2: 'hdr_end(host) -i'
3: 'path_beg -i'
4: 'path_end -i'
6: 'src ip'
acl_then:
2: 'http-request redirect location'
3: 'http-request allow'
4: 'http-request deny'
5: 'use_backend'
forward_for: 'option forwardfor if-none'
redispatch: 'option redispatch'
slow_attack: |
option http-buffer-request
timeout http-request 10s
ddos: |
# Start config for DDOS atack protect
stick-table type ip size 1m expire 1m store gpc0,http_req_rate(10s),http_err_rate(10s)
tcp-request connection track-sc1 src
tcp-request connection reject if { sc1_get_gpc0 gt 0 }
# Abuser means more than 100reqs/10s
ssl_offloading: |
http-request set-header X-Forwarded-Port %[dst_port]
http-request add-header X-Forwarded-Proto https if { ssl_fc }
redirect scheme https if !{ ssl_fc }
compression: |
compression algo gzip
compression type text/html text/plain text/css
antibot: |
# Start config for Antibot protection
http-request track-sc0 src table per_ip_rates
http-request track-sc1 url32+src table per_ip_and_url_rates unless { path_end .css .js .png .jpeg .gif }
acl exceeds_limit sc_gpc0_rate(0) gt 15
http-request sc-inc-gpc0(0) if { sc_http_req_rate(1) eq 1 } !exceeds_limit
http-request deny if exceeds_limit
# End config for Antibot

View File

@ -0,0 +1,50 @@
- name: Create HAProxy section
blockinfile:
block: "{{ lookup('template', 'section.j2') }}"
dest: "{{ cfg }}"
marker: "# {mark} Roxy-WI MANAGED {{ config.type }} {{ config.name }} do not edit it directly"
when: config.type in ('listen', 'frontend', 'backend') and action == 'create'
- name: Create Userlist
blockinfile:
block: "{{ lookup('template', 'userlist.j2') }}"
dest: "{{ cfg }}"
marker: "# {mark} Roxy-WI MANAGED {{ config.type }} {{ config.name }} do not edit it directly"
when: config.type == 'userlist' and action == 'create'
- name: Create Peers
blockinfile:
block: "{{ lookup('template', 'peers.j2') }}"
dest: "{{ cfg }}"
marker: "# {mark} Roxy-WI MANAGED {{ config.type }} {{ config.name }} do not edit it directly"
when: config.type == 'peers' and action == 'create'
- name: Create Global
blockinfile:
block: "{{ lookup('template', 'global.j2') }}"
dest: "{{ cfg }}"
marker: "# {mark} Roxy-WI MANAGED {{ config.type }} do not edit it directly"
when: config.type == 'global' and action == 'create'
- name: Create Defaults
blockinfile:
block: "{{ lookup('template', 'defaults.j2') }}"
dest: "{{ cfg }}"
marker: "# {mark} Roxy-WI MANAGED {{ config.type }} do not edit it directly"
when: config.type == 'defaults' and action == 'create'
- name: Delete section
blockinfile:
block: ""
dest: "{{ cfg }}"
marker: "# {mark} Roxy-WI MANAGED {{ config.type }} {{ config.name }} do not edit it directly"
when: action == 'delete' and config.name
- name: Delete Global or Defaults
blockinfile:
block: ""
dest: "{{ cfg }}"
marker: "# {mark} Roxy-WI MANAGED {{ config.type }} do not edit it directly"
when:
- action == 'delete'
- config.type == 'global' or config.type == 'defaults

View File

@ -0,0 +1,16 @@
defaults
log {{ config.log }}
retries {{ config.retries }}
maxconn {{ config.maxconn }}
{% if config.timeout != '' -%}
{% for key, value in config.timeout.items() -%}
timeout {{ key.replace('_', '-') }} {{ value }}s
{% endfor %}
{% endif %}
{% if config.option != '' -%}
{% for o in config.option.split('\\r\\n') -%}
{{ o }}
{% endfor -%}
{% endif -%}

View File

@ -0,0 +1,28 @@
global
{% if config.log != '' -%}
{% for log in config.log -%}
log {{ log }}
{% endfor -%}
{% endif %}
chroot {{ config.chroot }}
pidfile {{ config.pidfile }}
maxconn {{ config.maxconn }}
user {{ config.user }}
group {{ config.group }}
{% if config.daemon -%}
daemon
{% endif -%}
{% if config.socket != '' -%}
{% for o in config.socket -%}
stats socket {{ o }}
{% endfor -%}
{% endif -%}
{% if config.option != '' -%}
{% for o in config.option.split('\\r\\n') -%}
{{ o }}
{% endfor -%}
{% endif -%}

View File

@ -0,0 +1,5 @@
peers {{ config.name }}
{% for peer in config.peers -%}
peer {{ peer.name }} {{ peer.ip }}:{{ peer.port }}
{% endfor -%}

View File

@ -0,0 +1,148 @@
{{ config.type }} {{ config.name }}
{% for bind in config.binds -%}
bind {{ bind.ip }}:{{ bind.port }} {% if config.ssl != 'None' and config.mode == 'http' and config.ssl.cert %} ssl crt {{cert_path}}/{{ config.ssl.cert }}{% endif %}
{% endfor %}
mode {{ config.mode }}
{% if config.balance != 'None' -%}
balance {{ config.balance }}
{% endif -%}
maxconn {{ config.maxconn }}
{% if config.health_check != 'None' -%}
{% if config.health_check.check == 'httpchk' and config.health_check.domain -%}
option {{ config.health_check.check }} GET {{ config.health_check.path }} "HTTP/1.0\\r\\nHost: {{ config.health_check.domain }}
{% else -%}
option {{ config.health_check.check }}
{% endif -%}
{% endif %}
{% if config.headers != 'None' -%}
{%- for header in config.headers -%}
{{ header.path }} {{ header.method }} {{ header.name }} {{ header.value }}
{% endfor %}
{% endif %}
{% if config.whitelist -%}
acl white_list_{{ whitelist }} src -f {{ haproxy_dir }}/white/{{ config.whitelist }}
tcp-request content accept if white_list_{{ whitelist }}
tcp-request content reject
{% endif %}
{% if config.blacklist -%}
tcp-request connection reject if { src -f {{ haproxy_dir }}/white/{{ config.blacklist }} }
{% endif %}
{% if config.acls -%}
{% for acl in config.acls -%}
{% if acl.acl_if in (1, 2) -%}
{% if config.mode == 'tcp' -%}
{% set acl_then_hdr = 'ssl_fc_sni -i' -%}
{% elif config.mode == 'http' and config.ssl == 'None' -%}
{% set acl_then_hdr = 'hdr_beg(host) -i' -%}
{% else -%}
{% set acl_then_hdr = 'ssl_fc_sni -i' -%}
{% endif %}
{{ acl_then[acl.acl_then] }} {{ acl.acl_then_value }} if { {{ acl_then_hdr }} {{ acl.acl_value }} }
{% elif acl.acl_if in (3, 4, 5) -%}
{{ acl_then[acl.acl_then] }} {{ acl.acl_then_value }} if { {{ acl_if[acl.acl_if] }} {{ acl.acl_value }} }
{% elif acl.acl_if == 6 -%}
acl return_{{ acl.acl_value }} {{ acl_if[acl.acl_if] }} {{ acl.acl_value }}
http-request return if return_{{ acl.acl_value }}
{% elif acl.acl_if == 7 -%}
acl set_header_{{ acl.acl_value }} {{ acl_if[acl.acl_if] }} {{ acl.acl_value }}
http-request set_header_ if set_header_{{ acl.acl_value }}
{% endif -%}
{% endfor -%}
{% endif -%}
{% if config.slow_attack -%}
{{ slow_attack }}
{% endif -%}
{% if config.forward_for -%}
{{ forward_for }}
{% endif -%}
{% if config.redispatch -%}
{{ redispatch }}
{% endif -%}
{% if config.ssl_offloading -%}
{{ ssl_offloading }}
{% endif -%}
{% if config.ddos -%}
{{ ddos }}
acl abuse sc1_http_req_rate({{ config.name }}) ge 100
acl flag_abuser sc1_inc_gpc0({{ config.name }})
tcp-request content reject if abuse flag_abuser
# End config for DDOS
{% endif -%}
{% if config.waf -%}
filter spoe engine modsecurity config {{ haproxy_dir }}/waf.conf
http-request deny if { var(txn.modsec.code) -m int gt 0 }
{% endif -%}
{% if config.compression -%}
{{ compression }}
{% endif -%}
{% if config.antibot -%}
{{ antibot }}
{% endif -%}
{% if config.cookie != 'None' -%}
cookie {{ config.cookie.name }} {% if config.cookie.domain != 'None' %} {{ config.cookie.domain }}{% endif %} {{ config.cookie.rewrite }} {{ config.cookie.prefix }} {{ config.cookie.nocache }} {{ config.cookie.postonly }} {{ config.cookie.dynamic }}
{% if config.cookie.dynamicKey -%}
dynamic-cookie-key {{ config.cookie.dynamicKey }}
{% endif -%}
{% endif -%}
{% if config.ssl != 'None' and config.mode == 'http' -%}
{%- if config.ssl.ssl_check_backend -%}
{%- set ssl_check_option = 'ssl verify' -%}
{%- else -%}
{%- set ssl_check_option = 'ssl verify none' -%}
{%- endif -%}
{% else -%}
{%- set ssl_check_option = '' -%}
{% endif -%}
{% if config.servers_check != 'None' and config.servers_check.check_enabled -%}
{%- set check_option = ' check inter ' + config.servers_check.inter|string() + ' rise ' + config.servers_check.rise|string() + ' fall ' + config.servers_check.fall|string() %}
{% else -%}
{% if config.servers_check != 'None' and config.servers_check.check_enabled == 'None' -%}
{% set check_option = '' -%}
{% endif -%}
{% endif -%}
{% if config.option != '' -%}
{% for o in config.option.split('\\r\\n') -%}
{{ o }}
{% endfor -%}
{% endif -%}
{% if config.cache -%}
http-request cache-use {{ config.name }}
http-response cache-store {{ config.name }}
{% endif -%}
{% if config.circuit_breaking != 'None' -%}
default-server observe {{ config.circuit_breaking.observe }} error-limit {{ config.circuit_breaking.error_limit }} on-error {{ config.circuit_breaking.on_error }}
{% endif -%}
{% for backend in config.backend_servers -%}
server {{ backend.server }} {{ backend.server }}:{{ backend.port }} port {{ backend.port_check }} {{ check_option }} {{ ssl_check_option }} maxconn {{ backend.maxconn }}{% if backend.send_proxy %} send-proxy{% endif %}{% if backend.backup %} backup {% endif %}
{% endfor -%}
{% if config.backends and config.backends != 'None' -%}
use_backend {{ config.backends }}
{% endif %}
{% if config.cache %}
cache {{ config.name }}
total-max-size 4
max-age 240
{% endif %}

View File

@ -0,0 +1,11 @@
userlist {{ config.name }}
{% if config.userlist_groups -%}
{% for group in config.userlist_groups %}
group {{ group }}
{% endfor %}
{% endif %}
{% for user in config.userlist_users -%}
user {{ user.user }} insecure-password {{ user.password }}{% if user.group %} groups {{ user.group }}{% endif %}
{% endfor -%}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,624 @@
function openSection(section) {
let section_type = section.split(' ')[0];
let section_name = section.split(' ')[1];
let url = '/add/haproxy/' + $('#serv').val() + '/section/' + section_type + '/' + section_name;
clearEditSection();
if (section === 'global' || section === 'defaults') {
url = '/add/haproxy/' + $('#serv').val() + '/section/' + section_type;
section_type = section;
section_name = section;
}
$.ajax({
url: url,
contentType: "application/json; charset=utf-8",
statusCode: {
404: function (xhr) {
window.open('/config/section/haproxy/' + $('#serv').val() + '/' + section, '_blank').focus();
}
},
async: false,
success: function (data) {
$('.advance-show-button').click(function () {
$('.advance').fadeIn();
$('.advance-show-button').css('display', 'none');
$('.advance-hide-button').css('display', 'block');
return false;
});
$('.advance-hide-button').click(function () {
$('.advance').fadeOut();
$('.advance-show-button').css('display', 'block');
$('.advance-hide-button').css('display', 'none');
return false;
});
let section_id = '#add-' + section_type;
$.getScript(awesome);
$('#edit-' + section_type).show();
$('#edit-' + section_type + ' caption').hide();
$('#' + section_type + '-add-buttons').hide();
let i = 0;
Object.keys(data.config).forEach(function (key) {
if ($(section_id + ' *[name="' + key + '"]').prop("tagName") === 'SELECT') {
$(section_id + ' select[name="' + key + '"]').val(data.config[key]).change();
} else if ($(section_id + ' *[name="' + key + '"]').prop("tagName") === 'TEXTAREA') {
$(section_id + ' select[name="' + key + '"]').val(data.config['option']).change();
} else {
if ($(section_id + ' *[name="' + key + '"]').prop('type') === 'checkbox') {
if (data.config[key]) {
$(section_id + ' input[name="' + key + '"]').prop("checked", true);
}
} else {
$(section_id + ' input[name="' + key + '"]').val(data.config[key]);
}
}
});
if (data.config.option) {
$(section_id + ' textarea[name="option"]').val(data.config.option);
}
if (section_type === 'global') {
if (data.config.daemon > 0) {
$('#global-daemon').prop("checked", true);
} else {
$('#global-daemon').prop("checked", false);
}
if (data.config.log) {
let logs = '';
for (let log of data.config.log) {
logs += log + '\n';
}
$(section_id + ' textarea[name="log"]').val(logs);
}
if (data.config.socket) {
let sockets = '';
for (let socket of data.config.socket) {
sockets += socket + '\n';
}
$(section_id + ' textarea[name="socket"]').val(sockets);
}
}
if (section_type === 'defaults') {
if (data.config.timeout) {
Object.keys(data.config.timeout).forEach(function (key) {
$(section_id + ' input[name="'+key+'"]').val(data.config.timeout[key]);
});
}
}
if (section_type === 'listen' || section_type === 'frontend') {
if (data.config.binds.length > 0) {
for (let i = 1; i < data.config.binds.length; i++) {
make_actions_for_adding_bind('#' + section_type + '_bind');
}
}
for (let bind of data.config.binds) {
$(section_id + ' input[name="ip"]').get(i).value = bind.ip;
$(section_id + ' input[name="port"]').get(i).value = bind.port;
i++;
}
}
if (section_type === 'listen' || section_type === 'backend') {
if (data.config.backend_servers) {
if (data.config.backend_servers.length > 3) {
for (let i = 2; i < data.config.backend_servers.length; i++) {
$("[name=add_servers]").append(add_server_var);
}
}
let serv_ports = $('.send_proxy');
for (let i = 0; i <= serv_ports.length; i++) {
var uniqId = makeid(3);
$(serv_ports[i]).append('<label for="' + uniqId + '" class="send_proxy_label" title="Set send-proxy for this server" data-help="The Send-proxy parameter enforces the use of the PROXY protocol over any connection established to this server. The PROXY protocol informs the other end about the layer 3/4 addresses of the incoming connection so that it can know the client\'s address or the public address it accessed to, whatever the upper-layer protocol.">send-proxy</label><input type="checkbox" name="send_proxy" value="1" id="' + uniqId + '">');
var uniqId = makeid(3);
$(serv_ports[i]).append('<label for="' + uniqId + '" class="send_proxy_label" title="Set this server as backup server" data-help="When all servers in a farm are down, we want to redirect traffic to a backup server which delivers either sorry pages or a degraded mode of the application.\n' +
'This can be done easily in HAProxy by adding the keyword backup on the server line. If multiple backup servers are configured, only the first active one is used.">backup</label><input type="checkbox" name="backup" value="1" id="' + uniqId + '">');
}
i = 0;
if (data.config.backend_servers.length > 0) {
for (let bind of data.config.backend_servers) {
$(section_id + ' input[name="servers"]').get(i).value = bind.server;
$(section_id + ' input[name="server_port"]').get(i).value = bind.port;
$(section_id + ' input[name="port_check"]').get(i).value = bind.port_check;
$(section_id + ' input[name="server_maxconn"]').get(i).value = bind.maxconn;
if (bind.send_proxy) {
let check_id = $(section_id + ' input[name="send_proxy"]').get(i).id;
$('#' + check_id).prop("checked", true);
}
if (bind.backup) {
let backup_id = $(section_id + ' input[name="backup"]').get(i).id;
$('#' + backup_id).prop("checked", true);
}
i++;
}
}
}
}
if (section_type === 'listen' || section_type === 'frontend' || section_type === 'backend') {
$("#options-" + section_type + "-show").click(function () {
if ($("#options-" + section_type + "-show").is(':checked')) {
$("#options-" + section_type + "-show-div").show("fast");
} else {
$("#options-" + section_type + "-show-div").hide("fast");
}
});
if (data.config.headers) {
if (data.config.headers.length > 0) {
i = 0;
$("#add_" + section_type + "_header").on("click", function () {
$("#" + section_type + "_header_div").show();
$("#" + section_type + "_add_header").show();
$("#add_" + section_type + "_header").hide();
});
$('#add_' + section_type + '_header').trigger('click');
for (let header of data.config.headers) {
make_actions_for_adding_header('#' + section_type + '_header_div')
let headers_res_id = $(section_id + ' select[name="headers_res"]').get(i).id;
$('#' + headers_res_id).val(header.path).change();
let headers_method_id = $(section_id + ' select[name="headers_method"]').get(i).id;
$('#' + headers_method_id).val(header.method).change();
$(section_id + ' input[name="header_name"]').get(i).value = header.name;
$(section_id + ' input[name="header_value"]').get(i).value = header.value;
i++;
}
}
}
if (data.config.option) {
$("#options-" + section_type + "-show").trigger('click');
}
if (data.config.acls) {
if (data.config.acls.length > 0) {
i = 0;
$("#add_" + section_type + "_acl").on("click", function () {
$("#" + section_type + "_acl").show();
$("#" + section_type + "_add_acl").show();
$("#add_" + section_type + "_acl").hide();
});
$("#add_" + section_type + "_acl").trigger('click');
for (let acl of data.config.acls) {
make_actions_for_adding_acl_rule('#' + section_type + '_acl');
let acl_if_id = $(section_id + ' select[name="acl_if"]').get(i).id;
$('#' + acl_if_id).val(acl.acl_if).change();
let acl_then_id = $(section_id + ' select[name="acl_then"]').get(i).id;
$('#' + acl_then_id).val(acl.acl_then).change();
$(section_id + ' input[name="acl_value"]').get(i).value = acl.acl_value;
$(section_id + ' input[name="acl_then_value"]').get(i).value = acl.acl_then_value;
i++;
}
}
}
}
if (section_type === 'userlist') {
if (data.config.userlist_groups.length > 0) {
i = 0;
for (let c of data.config.userlist_groups) {
$(section_id + ' input[name="userlist-group"]').get(i).value = c;
$('#userlist-groups').append(add_userlist_group_var);
i++;
}
}
if (data.config.userlist_users.length > 0) {
i = 0;
for (let c of data.config.userlist_users) {
$(section_id + ' input[name="userlist-user"]').get(i).value = c.user;
$(section_id + ' input[name="userlist-password"]').get(i).value = c.password;
$(section_id + ' input[name="userlist-user-group"]').get(i).value = c.group;
$('#userlist-users').append(add_userlist_var);
i++;
}
}
}
if (section_type === 'peers') {
$('[name=add-peer-input]').click(function () {
$("[name=add_peers]").append(add_peer_var);
});
if (data.config.peers.length > 0) {
i = 0;
for (let c of data.config.peers) {
$(section_id + ' input[name="servers_name"]').get(i).value = c.name;
$(section_id + ' input[name="servers"]').get(i).value = c.ip;
$(section_id + ' input[name="server_port"]').get(i).value = c.port;
i++;
if (i > 1) {
$("[name=add_peers]").append(add_peer_var);
}
}
}
}
$("select").selectmenu();
$("select").selectmenu('refresh');
$("input[type=checkbox]").checkboxradio();
$("input[type=checkbox]").checkboxradio('refresh');
$(section_id + ' select[name="server"]').val(data.server_id).change();
$(section_id + ' select[name="server"]').selectmenu('disable').parent().parent().hide();
$(section_id + ' input[name="name"]').prop("readonly", true).parent().parent().hide();
$("#edit-section").dialog({
resizable: false,
height: "auto",
width: 1100,
modal: true,
title: edit_word,
close: function () {
$('#edit-' + section_type).hide();
},
buttons: [{
text: edit_word,
click: function () {
editProxy('add-' + section_type, $(this));
}
}, {
text: delete_word,
click: function () {
delete_section(section_type, section_name, $('#serv').val());
$(this).dialog("close");
$('#edit-' + section_type).hide();
}
}, {
text: cancel_word,
click: function () {
$(this).dialog("close");
$('#edit-' + section_type).hide();
}
}]
});
}
});
}
function delete_section(section_type, section_name, server_id) {
$.ajax({
url: '/add/haproxy/' + server_id + '/section/' + section_type + '/' + section_name,
contentType: "application/json; charset=utf-8",
method: "DELETE",
statusCode: {
204: function (xhr) {
showConfig();
},
404: function (xhr) {
showConfig();
}
},
success: function (data) {
if (data) {
if (data.status === "failed") {
toastr.error(data.error);
}
}
}
})
}
function clearEditSection() {
$('#edit-section').empty();
$.ajax({
url: "/add/haproxy/get_section_html",
method: "GET",
async: false,
success: function(data) {
$('#edit-section').html(data);
$.getScript('/static/js/add.js');
},
})
}
function addProxy(form_name, generate=false) {
let frm = $('#'+form_name);
let serv = '';
let name_id = '';
if (form_name === 'add-listen') {
serv = '#serv'
name_id = '#listener'
} else if (form_name === 'add-frontend') {
serv = '#serv2'
name_id = '#new_frontend'
} else if (form_name === 'add-backend') {
serv = '#serv3'
name_id = '#new_backend'
} else if (form_name === 'add-userlist') {
serv = '#userlist_serv'
name_id = '#new_userlist'
} else {
serv = '#peers_serv'
name_id = '#peers-name'
}
if(!checkIsServerFiled(serv)) return false;
if(!checkIsServerFiled(name_id, 'The name cannot be empty')) return false;
let json_data = getFormData(frm, form_name);
let section_type = form_name.split('-')[1]
let q_generate = '';
if (generate) {
q_generate = '?generate=1';
}
$.ajax({
url: '/add/haproxy/' + $(serv).val() + '/section/' + section_type + q_generate,
data: JSON.stringify(json_data),
type: frm.attr('method'),
contentType: "application/json; charset=utf-8",
success: function( data ) {
if (data.status === 'failed') {
toastr.error(data.error)
} else if (data === '') {
toastr.clear();
toastr.error('error: Something went wrong. Check configuration');
} else {
if (generate) {
$('#dialog-confirm-body').text(data.data);
let generated_title = translate_div.attr('data-generated_config');
$("#dialog-confirm-cert").dialog({
resizable: false,
height: "auto",
width: 650,
modal: true,
title: generated_title,
buttons: {
Ok: function () {
$(this).dialog("close");
}
}
});
} else {
toastr.clear();
data.data = data.data.replace(/\n/g, "<br>");
if (returnNiceCheckingConfig(data.data) === 0) {
toastr.info('Section has been added. Do not forget to restart the server');
let ip = frm.find('select[name=serv]').val();
localStorage.setItem('restart', ip);
resetProxySettings();
}
}
}
}
});
}
function editProxy(form_name, dialog_id, generate=false) {
let frm = $('#'+form_name);
let name_id = '#' +form_name + ' input[name="name"]';
if(!checkIsServerFiled(name_id, 'The name cannot be empty')) return false;
let json_data = getFormData(frm, form_name);
let section_type = form_name.split('-')[1]
let url = '/add/haproxy/' + $('#serv').val() + '/section/' + section_type + '/' + $(name_id).val();
if (section_type === 'defaults' || section_type === 'global') {
url = '/add/haproxy/' + $('#serv').val() + '/section/' + section_type;
}
$.ajax({
url: url,
data: JSON.stringify(json_data),
type: 'PUT',
contentType: "application/json; charset=utf-8",
success: function( data ) {
if (data.status === 'failed') {
toastr.error(data.error)
} else if (data === '') {
toastr.clear();
toastr.error('error: Something went wrong. Check configuration');
} else {
toastr.clear();
data.data = data.data.replace(/\n/g, "<br>");
if (returnNiceCheckingConfig(data.data) === 0) {
toastr.info('Section has been updated. Do not forget to restart the server');
let ip = frm.find('select[name=serv]').val();
localStorage.setItem('restart', ip);
showConfig();
$(dialog_id).dialog( "close" );
}
}
}
});
}
function getFormData($form, form_name) {
let section_type = form_name.split('-')[1]
let unindexed_array = $form.serializeArray();
let indexed_array = {};
indexed_array['acls'] = [];
indexed_array['binds'] = [];
indexed_array['headers'] = [];
indexed_array['backend_servers'] = [];
indexed_array['type'] = section_type;
indexed_array['userlist_users'] = [];
indexed_array['userlist_groups'] = [];
indexed_array['peers'] = [];
indexed_array['daemon'] = 0;
$.map(unindexed_array, function (n, i) {
if (n['name'] === 'cookie') {
if ($('input[name="cookie"]').is(':checked')) {
let name = $('input[name="cookie_name"]').val();
let domain = $('input[name="cookie_domain"]').val();
let dynamic = $('input[name="dynamic"]').val();
let dynamicKey = $('input[name="dynamic-cookie-key"]').val();
let nocache = $('input[name="nocache"]').val();
let postonly = $('input[name="postonly"]').val();
let rewrite = $('select[name="rewrite"] option:selected').val();
let prefix = $('input[name="prefix"]').val();
indexed_array['cookie'] = {name, domain, dynamic, dynamicKey, nocache, postonly, rewrite, prefix}
}
} else if (n['name'] === 'whitelist_checkbox') {
if ($('input[name="whitelist_checkbox"]').is(':checked')) {
indexed_array['whitelist'] = $('input[name="whitelist"]').val();
}
} else if (n['name'] === 'ssl') {
if ($('input[name="ssl"]').is(':checked')) {
let cert = $('input[name="cert"]').val();
let ssl_check_backend = 1;
if ($('input[name="ssl-check"]').is(':checked')) {
ssl_check_backend = 0;
} else {
ssl_check_backend = 1;
}
indexed_array['ssl'] = {cert, ssl_check_backend};
}
} else if (n['name'] === 'health_check') {
let check = n['value'];
if (check === undefined || check === '' || check === '-------') {
return;
}
let path = $('input[name="checks_http_path"]').val();
let domain = $('input[name="checks_http_domain"]').val();
indexed_array['health_check'] = {check, path, domain}
} else if (n['name'] === 'blacklist_checkbox') {
if ($('input[name="blacklist_checkbox"]').is(':checked')) {
indexed_array['blacklist'] = $('input[name="blacklist"]').val();
}
} else if (n['name'] === 'ssl_offloading') {
if ($('input[name="ssl_offloading"]').is(':checked')) {
indexed_array['ssl_offloading'] = 1;
}
} else if (n['name'] === 'forward_for') {
if ($('input[name="forward_for"]').is(':checked')) {
indexed_array['forward_for'] = 1;
}
} else if (n['name'] === 'redispatch') {
if ($('input[name="redispatch"]').is(':checked')) {
indexed_array['redispatch'] = 1;
}
} else if (n['name'] === 'slow_attack') {
if ($('input[name="slow_attack"]').is(':checked')) {
indexed_array['slow_attack'] = 1;
}
} else if (n['name'] === 'ddos') {
if ($('input[name="ddos"]').is(':checked')) {
indexed_array['ddos'] = 1;
}
} else if (n['name'] === 'antibot') {
if ($('input[name="antibot"]').is(':checked')) {
indexed_array['antibot'] = 1;
}
} else if (n['name'] === 'cache') {
if ($('input[name="cache"]').is(':checked')) {
indexed_array['cache'] = 1;
}
} else if (n['name'] === 'circuit_breaking') {
if ($('input[name="circuit_breaking"]').is(':checked')) {
let observe = $('select[name="circuit_breaking_observe"] option:selected').val();
let error_limit = $('input[name="circuit_breaking_error_limit"]').val();
let on_error = $('select[name="circuit_breaking_on_error"] option:selected').val();
indexed_array['circuit_breaking'] = {observe, error_limit, on_error};
}
} else if (n['name'] === 'check-servers') {
if ($('input[name="check-servers"]').is(':checked')) {
let check_enabled = 1;
let inter = $('select[name="inter"] option:selected').val();
let rise = $('select[name="rise"] option:selected').val();
let fall = $('select[name="fall"] option:selected').val();
indexed_array['servers_check'] = {check_enabled, inter, rise, fall};
} else {
let check_enabled = 0;
indexed_array['servers_check'] = {check_enabled}
}
} else if (n['name'] === 'template') {
if ($('input[name="template"]').is(':checked')) {
let prefix = $('input[name="template-prefix"]').val();
let count = $('input[name="template-number"]').val();
let servers = $('input[name="servers"]').val();
let port = $('input[name="server_port"]').val();
indexed_array['servers_template'] = {prefix, count, servers, port};
}
} else if (n['name'] === 'log' && section_type === 'global') {
indexed_array['log'] = [];
for (let l of n['value'].split('\r\n')) {
if (l != '') {
indexed_array['log'].push(l);
}
}
} else if (n['name'] === 'socket') {
indexed_array['socket'] = [];
for (let l of n['value'].split('\r\n')) {
if (l != '') {
indexed_array['socket'].push(l);
}
}
} else if (n['name'] === 'daemon') {
if ($('input[name="daemon"]').is(':checked')) {
indexed_array['daemon'] = 1;
}
} else {
indexed_array[n['name']] = n['value'];
}
});
$('#' +section_type+ '_acl p').each(function () {
let acl_if = $(this).children().children("select[name='acl_if'] option:selected").val();
if (acl_if === undefined || acl_if === '' || acl_if === "Select if") {
return;
}
let acl_value = $(this).children("input[name='acl_value']").val();
let acl_then = $(this).children().children("select[name='acl_then'] option:selected").val();
let acl_then_value = $(this).children("input[name='acl_then_value']").val();
indexed_array['acls'].push({acl_if, acl_value, acl_then, acl_then_value});
});
$('#' +section_type+ '_bind p').each(function () {
let ip = $(this).children("input[name='ip']").val();
let port = $(this).children("input[name='port']").val();
if (port === undefined || port === '') {
return;
}
indexed_array['binds'].push({ip, port});
});
$('#' +section_type+ '_header_div p').each(function () {
let path = $(this).children().children("select[name='headers_res'] option:selected").val();
let method = $(this).children().children("select[name='headers_method'] option:selected").val();
let name = $(this).children("input[name='header_name']").val();
let value = $(this).children("input[name='header_value']").val();
if (path === undefined || path === '' || path === '------') {
return;
}
if (name === undefined || name === '') {
return;
}
indexed_array['headers'].push({path, method, name, value});
});
$('#userlist-groups p').each(function (){
let group = $(this).children("input[name='userlist-group']").val();
if (group === undefined || group === '') {
return;
}
indexed_array['userlist_groups'].push(group);
});
$('#userlist-users p').each(function (){
let user = $(this).children("input[name='userlist-user']").val();
let password = $(this).children("input[name='userlist-password']").val();
let group = $(this).children("input[name='userlist-user-group']").val();
if (user === undefined || user === '' || password === undefined || password === '') {
return;
}
indexed_array['userlist_users'].push({user, password, group});
});
$('#add_peers p').each(function (){
let name = $(this).children("input[name='servers_name']").val();
let ip = $(this).children("input[name='servers']").val();
let port = $(this).children("input[name='server_port']").val();
if (name === undefined || name === '' || ip === undefined || ip === '' || port === undefined || port === '') {
return;
}
indexed_array['peers'].push({name, ip, port});
});
$('#'+form_name+' span[name="add_servers"] p').each(function (){
let server = $(this).children("input[name='servers']").val();
if (server === undefined || server === '') {
return;
}
let port = $(this).children("input[name='server_port']").val();
let port_check = $(this).children("input[name='port_check']").val();
let maxconn = $(this).children("input[name='server_maxconn']").val();
let send_proxy = 0;
let backup = 0;
if ($(this).children().children('input[name="send_proxy"]').is(':checked')) {
send_proxy = 1;
}
if ($(this).children().children('input[name="backup"]').is(':checked')) {
backup = 1;
}
let test_var = {server, port, port_check, maxconn, send_proxy, backup};
indexed_array['backend_servers'].push(test_var);
});
if (section_type === 'defaults') {
indexed_array['timeout'] = {};
$("input[id^='defaults-timeout']").each(function (i, el) {
indexed_array['timeout'][el.name] = el.value;
})
}
let elementsForDelete = ['acl_if', 'acl_value', 'acl_then', 'acl_then_value', 'servers', 'port', 'port_check',
'backup', 'send_proxy', 'server_port', 'ip', 'headers_method', 'headers_res', 'header_name', 'header_value', 'server_maxconn',
'options', 'options1', 'options2', 'cookie_domain', 'cookie_name', 'dynamic', 'dynamic-cookie-key', 'nocache', 'postonly',
'rewrite', 'prefix', 'saved-options', 'blacklist_checkbox', 'whitelist_checkbox', 'circuit_breaking_error_limit',
'circuit_breaking_observe', 'circuit_breaking_on_error', 'check-servers', 'checks_http_domain', 'checks_http_path',
'options-listen-show', 'cert', 'ssl-check', 'template', 'template-number', 'fall', 'inner', 'rise', 'template-prefix',
'saved-options1', 'userlist-group', 'userlist-password', 'userlist-user', 'userlist-user-group', 'servers_name',
'servers', 'server_port', 'serv', 'check', 'client', 'connect', 'queue', 'server', 'http_keep_alive', 'http_request']
for (let element of elementsForDelete) {
delete indexed_array[element]
}
return indexed_array;
}

File diff suppressed because one or more lines are too long

2
app/static/js/jquery-3.7.1.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1152,6 +1152,7 @@ function returnNiceCheckingConfig(data) {
if (alert_error) { if (alert_error) {
toastr.error(server_name + '<pre style="padding: 0; margin: 0;">' + alert_error + '</pre>'); toastr.error(server_name + '<pre style="padding: 0; margin: 0;">' + alert_error + '</pre>');
toastr.info('Config not applied'); toastr.info('Config not applied');
return 1;
} else if (alert_warning) { } else if (alert_warning) {
toastr.warning(server_name + '<pre style="padding: 0; margin: 0;">' + alert_warning + '</pre>'); toastr.warning(server_name + '<pre style="padding: 0; margin: 0;">' + alert_warning + '</pre>');
toastr.success('<b>' + server_name + ' Configuration file is valid</b>'); toastr.success('<b>' + server_name + ' Configuration file is valid</b>');
@ -1165,6 +1166,7 @@ function returnNiceCheckingConfig(data) {
} else if (server_name2) { } else if (server_name2) {
toastr.success('<b>' + server_name2 + ' Configuration file is valid</b>'); toastr.success('<b>' + server_name2 + ' Configuration file is valid</b>');
} }
return 0;
} }
function show_version() { function show_version() {
NProgress.configure({showSpinner: false}); NProgress.configure({showSpinner: false});
@ -1346,9 +1348,8 @@ function showPassword(input) {
} }
} }
function removeData() { function removeData() {
let chart;
for (let i = 0; i < charts.length; i++) { for (let i = 0; i < charts.length; i++) {
chart = charts[i]; let chart = charts[i];
chart.destroy(); chart.destroy();
} }
} }

View File

@ -27,3 +27,14 @@ const csrf_token = Cookies.get('csrf_access_token');
// API prefix // API prefix
const api_prefix = '/api' const api_prefix = '/api'
// Add page
const add_server_var = '<br /><input name="servers" title="Backend IP" size=14 placeholder="xxx.xxx.xxx.xxx" class="form-control second-server" style="margin: 2px 0 4px 0;">: ' +
'<input name="server_port" required title="Backend port" size=3 placeholder="yyy" class="form-control second-server add_server_number" type="number"> ' +
'Port check: <input name="port_check" required title="Maxconn. Default 200" size=5 value="200" class="form-control add_server_number" type="number">' +
' maxconn: <input name="server_maxconn" required title="Maxconn. Default 200" size=5 value="200" class="form-control add_server_number" type="number">'
const add_userlist_group_var = '<p><input name="userlist-group" title="User`s group" placeholder="group_name" class="form-control"></p>'
const add_userlist_var = '<p><input name="userlist-user" title="User name" placeholder="user_name" class="form-control"> <input name="userlist-password" required title="User password. By default it insecure-password" placeholder="password" class="form-control"> <input name="userlist-user-group" title="User`s group" placeholder="user`s group" class="form-control"></p>'
const add_peer_var = '<p><input name="servers_name" required title="Peer name" size=14 placeholder="haproxyN" class="form-control">: ' +
'<input name="servers" title="Backend IP" size=14 placeholder="xxx.xxx.xxx.xxx" class="form-control second-server">: ' +
'<input name="server_port" required title="Backend port" size=3 placeholder="yyy" class="form-control second-server add_server_number" type="number"></p>'

View File

@ -3,22 +3,19 @@
{% block h2 %}{{lang.menu_links.add_proxy.title}}{% endblock %} {% block h2 %}{{lang.menu_links.add_proxy.title}}{% endblock %}
{% block content %} {% block content %}
{% from 'include/input_macros.html' import input, checkbox, select %} {% from 'include/input_macros.html' import input, checkbox, select %}
{% set balance_params = dict() %}
{% set balance_params = {'roundrobin':'roundrobin','source':'source','leastconn':'leastconn','first':'first', {% set balance_params = {'roundrobin':'roundrobin','source':'source','leastconn':'leastconn','first':'first',
'rdp-cookie':'rdp-cookie', 'uri':'uri', 'uri whole':'uri whole', 'static-rr': 'static-rr', 'rdp-cookie':'rdp-cookie', 'uri':'uri', 'uri whole':'uri whole', 'static-rr': 'static-rr',
'url_param userid':'url_param userid'} %} 'url_param userid':'url_param userid'} %}
{% set checks = dict() %} {% set checks = {'':'Choose a custom health check','tcp-check':'Check a TCP Port', 'ssl-hello-chk':'Check a SSL Port',
{% set checks = {'':'Choose a custom health check','option tcp-check':'Check a TCP Port', 'httpchk':'Check a HTTP service', 'ldap-check':'Check a LDAP service', 'mysql-check':'Check a MySql Service',
'option ssl-hello-chk':'Check a SSL Port','option httpchk':'Check a HTTP service', 'pgsql-check':'Check a PgSQL Service', 'redis-check': 'Check a Redis Service', 'smtpchk':'Check a SMTP service'} %}
'option ldap-check':'Check a LDAP service', 'option mysql-check':'Check a MySql Service',
'option pgsql-check':'Check a PgSQL Service', 'option redis-check': 'Check a Redis Service',
'option smtpchk':'Check a SMTP service'} %}
{% set observe = {'layer7':'layer7', 'layer4': 'layer4'} %} {% set observe = {'layer7':'layer7', 'layer4': 'layer4'} %}
{% set on_error = {'mark-down':'mark-down', 'fastinter': 'fastinter', 'fail-check':'fail-check', {% set on_error = {'mark-down':'mark-down', 'fastinter': 'fastinter', 'fail-check':'fail-check',
'sudden-death':'sudden-death'} %} 'sudden-death':'sudden-death'} %}
{% set header_res = {'http-response': 'response', 'http-request': 'request'} %} {% set header_res = {'http-response': 'response', 'http-request': 'request'} %}
{% set header_params = {'add-header': 'add-header', 'set-header': 'set-header', 'del-header': 'del-header'} %} {% set header_params = {'add-header': 'add-header', 'set-header': 'set-header', 'del-header': 'del-header'} %}
{% set if_values = {'1':'Host name starts with','2':'Host name ends with','3':'Path starts with','4':'Path ends with', '6': 'Src ip'} %} {% set if_values = {'1':'Host name starts with','2':'Host name ends with','3':'Path starts with','4':'Path ends with', '6': 'Src ip'} %}
{% set force_close = {'0':'Off','1':'Server only','2':'Force close','3':'Pretend keep alive'} %}
<script src="/static/js/add.js"></script> <script src="/static/js/add.js"></script>
<div id="tabs"> <div id="tabs">
@ -36,677 +33,21 @@
<li><a href="#maps" title="{{lang.words.add|title()}} {{lang.words.proxy}}: {{lang.words.create|title()}} {{lang.words.and}} {{lang.words.upload}} {{lang.words.maps}} - Roxy-WI">{{lang.words.maps|title()}}</a></li> <li><a href="#maps" title="{{lang.words.add|title()}} {{lang.words.proxy}}: {{lang.words.create|title()}} {{lang.words.and}} {{lang.words.upload}} {{lang.words.maps}} - Roxy-WI">{{lang.words.maps|title()}}</a></li>
</ul> </ul>
<ul id='browse_histroy'></ul> <ul id='browse_histroy'></ul>
{% include 'include/add_proxy.html' %} {% include 'include/add/add_proxy.html' %}
<div id="listen"> <div id="listen">
<form name="add-listener" id="add-listener" action="/add/haproxy/add" method="post"> {% include 'include/add/listen.html' %}
<table class="add-table">
<caption><h3>{{lang.words.add|title()}} {{lang.words.listener}}</h3></caption>
<tr>
<td class="addName">{{lang.words.select|title()}} {{lang.words.w_a}} {{lang.words.server}}: </td>
<td class="addOption">
{{ select('serv', values=g.user_params['servers'], is_servers='true') }}
<div class="tooltip tooltipTop"><b>{{lang.words.note|title()}}:</b> {{lang.phrases.master_slave}}</div>
</td>
<td rowspan="5" class="add-note addName alert-info">
{{lang.add_page.desc.listener_desc1}}
<br /><br />
{{lang.add_page.desc.listener_desc2}}
<br /><br />
{{lang.add_page.desc.listener_desc3}}
</td>
</tr>
<tr>
<td class="addName">{{lang.words.name|title()}}:</td>
<td class="addOption">
{{ input('name', name='listener', title=lang.words.name|title() + ' ' +lang.words.listener, placeholder="web_80", required='required') }}
</td>
</tr>
<tr>
<td class="addName">IP and {{lang.words.port|title()}}:</td>
<td class="addOption">
{{ input('ip', placeholder="Any", size='15') }}<b>:</b>
{{ input('listen-port', name='port', title=lang.add_page.desc.port_for_bind + ' ' + lang.words.listener, placeholder="8080", size='5', required='required') }}
<div id="listener_bind" style="display: none"></div>
<a class="link add-server" id="add_bind_listener" title="{{lang.add_page.desc.bind_ip_pair}}"></a>
<div class="tooltip tooltipTop">
{{lang.add_page.desc.ip_port}}
</div>
</td>
</tr>
<tr class="advance">
<td class="addName">{{lang.words.mode|title()}}: </td>
<td class="addOption">
{% set values = dict() %}
{% set values = {'http':'http','tcp':'tcp'} %}
{{ select('listen-mode-select', name='mode', values=values, selected='http', required='required', class='force_close') }}
<span id="https-listen-span">
<label for="https-listen" style="margin-top: 5px;" title="{{lang.words.enable|title()}} SSL Offloading" data-help="{{lang.add_page.desc.ssl_offloading}}">SSL Offloading</label>
<input type="checkbox" id="https-listen" name="ssl" value="https" >
</span>
<div id="https-hide-listen" style="display: none;">
<br /><span class="tooltip tooltipTop">{{lang.words.enter2|title()}} {{lang.words.name}} {{lang.words.of}} pem {{lang.words.file2}}, {{lang.add_page.desc.press_down}}:</span><br />
{{ input('path-cert-listen', name="cert", placeholder="some_cert.pem", size='39') }}<br />
<label for="ssl-dis-check-listen" style="margin-top: 5px;">{{lang.add_page.buttons.disable_ssl_check}}</label><input type="checkbox" id="ssl-dis-check-listen" name="ssl-dis-check" value="ssl-dis-check">
<label for="ssl-check-listen" style="margin-top: 5px;">{{lang.add_page.buttons.disable_ssl_verify}}</label><input type="checkbox" id="ssl-check-listen" name="ssl-check" value="ssl-check" checked>
</div>
</td>
</tr>
<tr class="advance">
<td class="addName">Maxconn: </td>
<td class="addOption">
{{ input('maxconn', value='2000', type="number", title=lang.add_page.desc.maxconn_fix, size='5', required='required') }}
<div class="tooltip tooltipTop">{{lang.add_page.desc.maxconn_desc}}: 2000</div>
</td>
</tr>
<tr class="advance">
<td class="addName">{{lang.words.balance|title()}}: </td>
<td class="addOption">
{{ select('balance', values=balance_params, selected='roundrobin', required='required', class='force_close') }}
</td>
</tr>
<tr class="advance">
<td class="addName">{{lang.words.health|title()}} {{lang.words.check}}: </td>
<td class="addOption">
{{ select('listener_checks', name='health_check', values=checks, selected='', class='force_close') }}
<span id="listener_checks_note" class="tooltip tooltipTop"></span>
<br />
<span id="listener_checks_http" style="display: none;">
URI path for checking: {{ input('listener_checks_http_path', name='checks_http_path', value='/', title="URI for checking e.g. /check") }}
Domain name: {{ input('listener_checks_http_domain', name='checks_http_domain', placeholder='domain.com', title="Domain name for checking e.g. domain.com") }}
</span>
</td>
</tr>
<tr class="advance">
<td class="addName">{{lang.words.headers|title()}}: </td>
<td class="addOption">
<span title="{{lang.words.add|title()}} {{lang.words.headers}}" id="add_listener_header" class="link add-server"></span>
<div id="listener_header_div" style="display: none;">
<p style="border-bottom: 1px solid #ddd; padding-bottom: 10px;" id="listener_header_p">
{{ select('listener_header_res_method', name='headers_res', values=header_res, first='------', class='force_close') }}
{{ select('listener_header_method', name='headers_method', values=header_params, selected='add-header', class='force_close') }}
<b class="padding10">{{lang.words.name}}</b>
{{ input('listener_header_name', name="header_name") }}
<b class="padding10">{{lang.words.value}}</b>
{{ input('listener_header_value', name="header_value", placeholder='Leave blank if using del-header') }}
<span class="minus minus-style" onclick="deleteId('listener_header_p')" title="{{lang.words.delete|title()}}"></span>
</p>
</div>
<span>
<a class="link add-server" id="listener_add_header" title="{{lang.words.add|title()}} {{lang.words.headers}}" style="display: none;"></a>
</span>
</td>
</tr>
<tr class="advance">
<td class="addName" title="Access control list">ACL: </td>
<td class="addOption">
<span title="{{lang.words.add|title()}} ACL" id="add_listener_acl" class="link add-server"></span>
<div id="listener_acl" style="display: none;">
<p style="border-bottom: 1px solid #ddd; padding-bottom: 10px;" id="listener_acl_rule">
<b class="padding10">{{lang.words.if|title()}}</b>
{{ select('listener_acl_if', name='acl_if', values=if_values, first='Select if', class='force_close', disabled=false) }}
<b class="padding10">{{lang.words.value}}</b>
{{ input('listener_acl_value', name="acl_value") }}
<b class="padding10">{{lang.words.then}}</b>
{% set values = dict() %}
{% set values = {'2':'Redirect to','3':'Allow','4':'Deny', '6': 'Return', '7': 'Set-header'} %}
{{ select('listener_acl_then', name='acl_then', values=values, first='Select then', class='force_close', disabled=false) }}
<b class="padding10">{{lang.words.value}}</b>
{{ input('listener_acl_then_value', name='acl_then_value', title="Required if \'then\' is \'Use backend\' or \'Redirect\', \'Return\', or \'Set-header\'") }}
<span class="minus minus-style" onclick="deleteId('listener_acl_rule')" title="{{lang.words.delete|title()}} {{lang.words.this}} ACL"></span>
</p>
</div>
<span>
<a class="link add-server" id="listener_add_acl" title="{{lang.words.add|title()}} ACL" style="cursor: pointer; display: none;"></a>
</span>
</td>
</tr>
<tr class="advance">
<td class="addName"><span title="Cache support start 1.8 and latter" class="help_cursor">Web {{lang.words.acceleration}}:</span></td>
<td class="addOption">
<span class="controlgroup">
{{ checkbox('compression', title=lang.add_page.desc.http_compression, value='1', desc=lang.words.compression|title()) }}
{{ checkbox('cache', title=lang.words.enable|title()+' '+lang.words.cache, value='2', desc=lang.words.cache|title()) }}
{{ checkbox('ssl_offloading', title=lang.add_page.desc.http_https, desc='HTTP->HTTPS') }}
</span>
</td>
</tr>
<tr class="advance">
<td class="addName"><span title="Web application firewall" class="help_cursor">WAF:</span></td>
<td class="addOption">
<span class="controlgroup">
{{ checkbox('slow_atack', title=lang.add_page.desc.slow_attack,
desc='Slow attack') }}
{{ checkbox('ddos', title='DDOS attack protect', desc='DDOS') }}
{{ checkbox('whitelist_checkbox', title=lang.words.enable|title()+' '+ lang.words.whitelist, desc=lang.words.whitelist|title()) }}
{{ checkbox('blacklist_checkbox', title=lang.words.enable|title()+' '+ lang.words.blacklist, desc=lang.words.blacklist|title()) }}
{{ checkbox('waf', title='Web application firewall', desc='WAF', value='1') }}
{{ checkbox('antibot', title=lang.add_page.desc.antibot, desc='Antibot', value='1') }}
</span>
<div id="blacklist-hide" style="display: none;">
<br /><span class="tooltip tooltipTop">{{lang.words.enter2|title()}} {{lang.words.w_a}} {{lang.words.blacklist}} {{lang.words.name}}, {{lang.add_page.desc.press_down}}:</span><br />
{{ input('blacklist-hide-input', size='39', name="blacklist", placeholder="blacklist.lst") }}
</div>
<div id="whitelist-hide" style="display: none;">
<br /><span class="tooltip tooltipTop">{{lang.words.enter2|title()}} {{lang.words.w_a}} {{lang.words.whitelist}} {{lang.words.name}}, {{lang.add_page.desc.press_down}}:</span><br />
{{ input('whitelist-hide-input', size='39', name="whitelist", placeholder="whitelist.lst") }}
</div>
</td>
</tr>
<tr class="advance">
<td class="addName">{{lang.words.options|title()}}:</td>
<td class="addOption">
<span class="controlgroup">
{{ checkbox('forward_for', title=lang.add_page.desc.forward_for, desc='Forward for') }}
{{ checkbox('redispatch', title=lang.add_page.desc.redispatch, desc='Redispatch') }}
{% set values = dict() %}
{% set values = {'Off':'Off','Server only':'Server only','Force close':'Force close','Pretend keep alive':'Pretend keep alive'} %}
{{ select('force_close', values=values, first='Force HTTP close', title=lang.add_page.desc.force_close, class='force_close') }}
{{ checkbox('cookie', title=lang.add_page.desc.cookie, desc=lang.words.set|title()+' cookie', value='1') }}
{{ checkbox('options-listen-show', title=lang.add_page.buttons.set_options_m, desc=lang.add_page.buttons.set_options) }}
</span>
<br>
<span id="cookie_div" style="display: none;">
<input type="text" placeholder="name" name="cookie_name" id="cookie_name" class="form-control"><br><br>
<input type="text" placeholder="domain" name="cookie_domain" class="form-control"><br><br>
<span class="controlgroup">
{% set values = dict() %}
{% set values = {'None':'None','rewrite':'rewrite','indirect':'indirect','insert':'insert'} %}
{{ select('rewrite', values=values, first='rewrite/indirect/insert', class='force_close') }}
{{ checkbox('prefix', title=lang.add_page.desc.c_prefix, desc='prefix', value='prefix') }}
{{ checkbox('nocache', title=lang.add_page.desc.c_nocache, desc='nocache', value='nocache') }}
{{ checkbox('postonly', title=lang.add_page.desc.c_postonly, desc='postonly', value='postonly') }}
{{ checkbox('dynamic', title=lang.add_page.desc.c_dynamic, desc='dynamic', value='dynamic') }}
<span id="dynamic_div" style="display: none;">
dynamic-cookie-key: {{ input('dynamic-cookie-key', placeholder="your-custom-key") }}
</span>
</span>
</span>
<div id="options-listen-show-div" style="display: none;">
<div class="tooltip">
<span style="padding-right: 10px;">{{lang.words.start2|title()}} {{lang.words.typing}} {{lang.words.options}}: </span>
{{ input('options') }}
<span style="padding-left: 10px;">
{{lang.add_page.desc.press_down}}. <a href="http://cbonte.github.io/haproxy-dconv/1.7/configuration.html" target="_blanck" style="color: #23527c" title="HAProxy docs">{{lang.words.read|title()}} {{lang.words.more}} {{lang.words.about}} {{lang.words.options}}</a>
</span>
<br />
<span style="padding-right: 10px;">{{lang.words.start2|title()}} {{lang.words.typing}} {{lang.words.saved}} {{lang.words.options}}: </span>
{{ input('saved-options') }}
{{lang.add_page.desc.saved_options}}
</div>
<textarea name="option" title="Options thru" id="optionsInput" cols=80 rows=5 placeholder="acl test hdr_beg(host) -i some_host"></textarea>
</div>
</td>
</tr>
<tr>
<td class="addName">{{lang.words.servers|title()}}:</td>
<td class="addOption">
{% include 'include/add_servers.html' %}
<br>
<br>
{{ checkbox('template-listen', name='template', title=lang.add_page.desc.server_template, value='template', desc=lang.add_page.desc.server_template) }}
</td>
</tr>
<tr class="advance">
<td class="addName">{{lang.words.check|title()}}:</td>
<td class="addOption">
<div>
<label for="controlgroup-listen-show" style="margin-top: 5px;" title="Set custom check parameters">{{lang.words.custom|title()}} {{lang.words.check}} {{lang.words.params}}</label>
<input type="checkbox" id="controlgroup-listen-show" name="default-check" value="1">
<span class="tooltip tooltipTop">{{lang.add_page.desc.def_check}}: inter 2000 rise 2 fall 5</span>
</div>
<div class="controlgroup" id="controlgroup-listen" style="display: none;">
<label for="check-servers-listen" title="Ebable servers check">{{lang.words.check|title()}}</label>
<input type="checkbox" id="check-servers-listen" name="check-servers" checked value="1">
{% set values = dict() %}
{% set values = {'1000':'1000','2000':'2000','3000':'3000'} %}
{{ select('inter-listen', values=values, first='inter', class='force_close') }}
{% set values = dict() %}
{% set values = {'1':'1','2':'2','3':'3'} %}
{{ select('rise-listen', name='rise', values=values, first='rise', class='force_close') }}
{% set values = dict() %}
{% set values = {'4':'4','5':'5','6':'6'} %}
{{ select('fall-listen', name='fall', values=values, first='fall', class='force_close') }}
</div>
<div style="display: block">
{{ checkbox('circuit_breaking_listen', name="circuit_breaking", desc='Circuit Breaking', title=lang.add_page.desc.circuit_breaking, value='1') }}
</div>
<div id="circuit_breaking_listen_div" style="display: none">
Observe:
{{ select('circuit_breaking_observe', values=observe, class='force_close') }}
error-limit: {{ input('circuit_breaking_error_limit', type='number', value='50', style='width: 50px;') }}
on-error:
{{ select('circuit_breaking_on_error', values=on_error, class='force_close') }}
<div class="tooltip tooltipTop">{{lang.words.read|title()}} {{lang.words.more}} {{lang.words.about}} Circuit Breaking <a href="https://roxy-wi.org/description/circuit-breaking" title="Circuit Breaking" target="_blank">{{lang.words.here}}</a></div>
</div>
</td>
</tr>
<tr class="advance-show">
<td class="addOption" colspan="2">
<button title="{{lang.add_page.buttons.show_full_settings}}" class="row-down advance-show-button">{{lang.words.show|title()}} {{lang.words.advanced}} {{lang.words.settings}}</button>
<button title="{{lang.add_page.buttons.hide_full_settings}}" class="row-up advance-hide-button" style="display: none">{{lang.words.hide|title()}} {{lang.words.advanced}} {{lang.words.settings}}</button>
</td>
</tr>
<tr>
<td class="addButton">
<a class="ui-button ui-widget ui-corner-all" title="{{lang.words.add|title()}} {{lang.words.listener|title()}}" onclick="addProxy('add-listener')">{{lang.words.add|title()}} {{lang.words.linstener|title()}}</a>
</td>
<td class="addButton">
<a class="ui-button ui-widget ui-corner-all" title="{{lang.words.generate|title()}} {{lang.words.and}} {{lang.words.display}} {{lang.words.config}}" onclick="generateConfig('add-listener')">{{lang.words.generate|title()}} {{lang.words.config}}</a>
</td>
</tr>
</table>
</form>
</div> </div>
<!-- Second tabs --> <!-- Second tabs -->
<div id="frontend"> <div id="frontend">
<form name="add-frontend" id="add-frontend" action="/add/haproxy/add" method="post"> {% include 'include/add/frontend.html' %}
<table>
<caption><h3>{{lang.words.add|title()}} {{lang.words.frontend}}</h3></caption>
<tr>
<td class="addName">{{lang.words.select|title()}} {{lang.words.w_a}} {{lang.words.server}}: </td>
<td class="addOption">
{{ select('serv2', values=g.user_params['servers'], is_servers='true') }}
<div class="tooltip tooltipTop"><b>{{lang.words.note|title()}}:</b> {{lang.phrases.master_slave}}</div>
</td>
<td rowspan="5" class="add-note addName alert-info">
{{lang.add_page.desc.front_desc1}}
<br /><br />
{{lang.add_page.desc.listener_desc2}}
<br /><br />
{{lang.add_page.desc.listener_desc1}}
</td>
</tr>
<tr>
<td class="addName">{{lang.words.name|title()}}:</td>
<td class="addOption">
<input type="text" name="frontend" id="new_frontend" required title="{{lang.words.name|title()}} {{lang.words.frontend}}" placeholder="web_80" class="form-control">
</td>
</tr>
<tr>
<td class="addName">IP and {{lang.words.port|title()}}:</td>
<td class="addOption">
<input type="text" name="ip" id="ip1" size="15" placeholder="Any" class="form-control"><b>:</b>
<input type="text" name="port" size="5" required title="{{lang.add_page.desc.port_for_bind}} {{lang.words.frontend}}" placeholder="8080" class="form-control">
<div id="frontend_bind" style="display: none"></div>
<a class="link add-server" id="add_bind_frontend" title="{{lang.add_page.desc.bind_ip_pair}}"></a>
<div class="tooltip tooltipTop">
{{lang.add_page.desc.ip_port}}
</div>
</td>
</tr>
<tr class="advance">
<td class="addName">{{lang.words.mode|title()}}: </td>
<td class="addOption">
{% set values = dict() %}
{% set values = {'http':'http','tcp':'tcp'} %}
{{ select('frontend-mode-select', name='mode', values=values, selected='http', required='required', class='force_close') }}
<span id="https-frontend-span">
{{ checkbox('https-frontend', title=lang.add_page.desc.ssl_offloading, desc='SSL Offloading') }}
</span>
<div id="https-hide-frontend" style="display: none;">
<br /><span class="tooltip tooltipTop">{{lang.words.enter2|title()}} {{lang.words.name}} {{lang.words.of}} pem {{lang.words.file2}}, {{lang.add_page.desc.press_down}}:</span><br />
{{ input('path-cert-frontend', name="cert", placeholder="some_cert.pem", size='39') }}
</div>
</td>
</tr>
<tr class="advance">
<td class="addName">Maxconn: </td>
<td class="addOption">
{{ input('maxconn', value='2000', type="number", title=lang.add_page.desc.maxconn_fix, size='5', required='required') }}
<div class="tooltip tooltipTop">{{lang.add_page.desc.maxconn_desc}}: 2000</div>
</td>
</tr>
<tr class="advance">
<td class="addName">{{lang.words.headers|title()}}: </td>
<td class="addOption">
<span title="{{lang.words.add|title()}} {{lang.words.headers}}" id="add_frontend_header" class="link add-server"></span>
<div id="frontend_header_div" style="display: none;">
<p style="border-bottom: 1px solid #ddd; padding-bottom: 10px;" id="frontend_header_p">
{{ select('frontend_header_res_method', name='headers_res', values=header_res, first='------', class='force_close') }}
{{ select('frontend_header_method', name='headers_method', values=header_params, selected='add-header', class='force_close') }}
<b class="padding10">{{lang.words.name}}</b>
{{ input('frontend_header_name', name="header_name") }}
<b class="padding10">{{lang.words.value}}</b>
{{ input('frontend_header_value', name="header_value", placeholder='Leave blank if using del-header') }}
<span class="minus minus-style" onclick="deleteId('frontend_header_p')" title="{{lang.words.delete|title()}}"></span>
</p>
</div>
<span>
<a class="link add-server" id="frontend_add_header" title="{{lang.words.add|title()}} {{lang.words.headers}}" style="display: none;"></a>
</span>
</td>
</tr>
<tr class="advance">
<td class="addName" title="Access control list">ACL: </td>
<td class="addOption">
<span title="{{lang.words.add|title()}} ACL" id="add_frontend_acl" class="link add-server"></span>
<div id="frontend_acl" style="display: none;">
<p id="frontend_acl_rule" style="border-bottom: 1px solid #ddd; padding-bottom: 10px;">
<b class="padding10">if</b>
{{ select('frontend_acl_if', name='acl_if', values=if_values, first='Select if', class='force_close', disabled=false) }}
<b class="padding10">value</b>
{{ input('frontend_acl_value', name='acl_value') }}
<b class="padding10">then</b>
{% set values = dict() %}
{% set values = {'5':'Use backend','2':'Redirect to','3':'Allow','4':'Deny', '6': 'Return', '7': 'Set-header'} %}
{{ select('frontend_acl_then', name='acl_then', values=values, first='Select then', class='force_close', disabled=false) }}
<b class="padding10">value</b>
{{ input('frontend_acl_then_value', name='acl_then_value', title="Required if \'then\' is \'Use backend\' or \'Redirect\', \'Return\', or \'Set-header\'") }}
<span class="minus minus-style" onclick="deleteId('frontend_acl_rule')" title="{{lang.words.delete|title()}}"></span>
</p>
</div>
<span>
<a class="link add-server" id="frontend_add_acl" title="{{lang.words.add|title()}} ACL" style="cursor: pointer; display: none;"></a>
</span>
</td>
</tr>
<tr class="advance">
<td class="addName"><span title="Cache support start 1.8 and latter" class="help_cursor">Web {{lang.words.acceleration}}:</span></td>
<td class="addOption">
<span class="controlgroup">
{{ checkbox('compression2', name="compression", title=lang.add_page.desc.http_compression, value='1', desc=lang.words.compression|title()) }}
{{ checkbox('cache2', name="cache", title=lang.words.enable|title()+' '+lang.words.cache, value='2', desc=lang.words.cache|title()) }}
{{ checkbox('ssl_offloading1', title=lang.add_page.desc.http_https, desc='HTTP->HTTPS') }}
</span>
</td>
</tr>
<tr class="advance">
<td class="addName"><span title="Web application firewall" class="help_cursor">WAF:</span></td>
<td class="addOption">
<span class="controlgroup">
{{ checkbox('slow_atack1', title=lang.add_page.desc.slow_attack,
desc='Slow attack') }}
{{ checkbox('ddos1', title='DDOS attack protect', desc='DDOS') }}
{{ checkbox('whitelist_checkbox1', title=lang.words.enable|title()+' '+ lang.words.whitelist, desc=lang.words.whitelist|title()) }}
{{ checkbox('blacklist_checkbox1', title=lang.words.enable|title()+' '+ lang.words.blacklist, desc=lang.words.blacklist|title()) }}
{{ checkbox('waf2', name='waf', title='Web application firewall', desc='WAF', value='1') }}
{{ checkbox('antibot1', title=lang.add_page.desc.antibot, desc='Antibot', value='1') }}
</span>
<div id="blacklist-hide1" style="display: none;">
<br /><span class="tooltip tooltipTop">{{lang.words.enter2|title()}} {{lang.words.w_a}} {{lang.words.blacklist}} {{lang.words.name}}, {{lang.add_page.desc.press_down}}:</span><br />
{{ input('blacklist-hide-input1', size='39', name="blacklist", placeholder="blacklist.lst") }}
</div>
<div id="whitelist-hide1" style="display: none;">
<br /><span class="tooltip tooltipTop">{{lang.words.enter2|title()}} {{lang.words.w_a}} {{lang.words.whitelist}} {{lang.words.name}}, {{lang.add_page.desc.press_down}}:</span><br />
{{ input('whitelist-hide-input1', size='39', name="whitelist", placeholder="whitelist.lst") }}
</div>
</td>
</tr>
<tr class="advance">
<td class="addName">{{lang.words.options|title()}}:</td>
<td class="addOption">
<span class="controlgroup">
{{ checkbox('forward_for1', title=lang.add_page.desc.forward_for,
desc='Forward for') }}
{% set values = dict() %}
{% set values = {'Off':'Off','Server only':'Server only','Force close':'Force close','Pretend keep alive':'Pretend keep alive'} %}
{{ select('force_close', values=values, first='Force HTTP close', title=lang.add_page.desc.force_close, class='force_close') }}
{{ checkbox('options-frontend-show', title=lang.add_page.buttons.set_options_m, desc=lang.add_page.buttons.set_options) }}
</span>
<div id="options-frontend-show-div" style="display: none;">
<div style="font-size: 12px; padding-bottom: 10px;">
<span style="padding-right: 10px;">{{lang.words.start2|title()}} {{lang.words.typing}} {{lang.words.options}}: </span>
{{ input('options1') }}
<span style="padding-left: 10px;">
{{lang.add_page.desc.press_down}}. <a href="http://cbonte.github.io/haproxy-dconv/1.7/configuration.html" target="_blanck" style="color: #23527c" title="HAProxy docs">{{lang.words.read|title()}} {{lang.words.more}} {{lang.words.about}} {{lang.words.options}}</a>
</span>
<br />
<span style="padding-right: 10px;">{{lang.words.start2|title()}} {{lang.words.typing}} {{lang.words.saved}} {{lang.words.options}}:</span>
{{ input('saved-options1') }}
{{lang.add_page.desc.saved_options}}
</div>
<textarea name="option" title="Options thru" cols=80 rows=5 id="optionsInput1" placeholder="acl test hdr_beg(host) -i some_host"></textarea>
</div>
</td>
</tr>
<tr>
<td class="addName">{{lang.words.default_backend}}</td>
<td class="addOption">
{{ input('backends', name='backends', placeholder="some_backend", size='30', title=lang.add_page.desc.no_def_backend) }}
<div class="tooltip tooltipTop">
<b>{{lang.words.note|title()}}</b>: {{lang.add_page.desc.def_backend}}, <span title="{{lang.words.create|title()}} {{lang.words.backend}}" class="redirectBackend link">{{lang.add_page.desc.def_backend_exit}}</span>.
</div>
</td>
</tr>
<tr class="advance-show">
<td class="addOption" colspan="2">
<button title="{{lang.add_page.buttons.show_full_settings}}" class="row-down advance-show-button">{{lang.words.show|title()}} {{lang.words.advanced}} {{lang.words.settings}}</button>
<button title="{{lang.add_page.buttons.hide_full_settings}}" class="row-up advance-hide-button" style="display: none">{{lang.words.hide|title()}} {{lang.words.advanced}} {{lang.words.settings}}</button>
</td>
</tr>
<tr>
<td class="addButton">
<a class="ui-button ui-widget ui-corner-all" title="Add Frontend" onclick="addProxy('add-frontend')">Add Frontend</a>
</td>
<td class="addButton">
<a class="ui-button ui-widget ui-corner-all" title="{{lang.words.generate|title()}} {{lang.words.and}} {{lang.words.display}} {{lang.words.config}}" onclick="generateConfig('add-frontend')">{{lang.words.generate|title()}} {{lang.words.config}}</a>
</td>
</tr>
</table>
</form>
</div> </div>
<!-- Third tabs --> <!-- Third tabs -->
<div id="backend"> <div id="backend">
<form name="add-backend" id="add-backend" action="/add/haproxy/add" method="post"> {% include 'include/add/backend.html' %}
<table>
<caption><h3>{{lang.words.add|title()}} {{lang.words.backend}}</h3></caption>
<tr>
<td class="addName">{{lang.words.select|title()}} {{lang.words.w_a}} {{lang.words.server}}: </td>
<td class="addOption">
{{ select('serv3', values=g.user_params['servers'], is_servers='true') }}
<div class="tooltip tooltipTop"><b>{{lang.words.note|title()}}:</b> {{lang.phrases.master_slave}}</div>
</td>
<td rowspan="4" class="add-note addName alert-info">
{{lang.add_page.desc.back_des1}}
<br /><br />
{{lang.add_page.desc.listener_desc2}}
<br /><br />
{{lang.add_page.desc.listener_desc3}}
</td>
</tr>
<tr>
<td class="addName">{{lang.words.name|title()}}:</td>
<td class="addOption">
{{ input('new_backend', title=lang.words.name|title() + ' ' +lang.words.backend, placeholder="web_80", required='required') }}
</td>
</tr>
<tr class="advance">
<td class="addName">{{lang.words.mode|title()}}: </td>
<td class="addOption">
{% set values = dict() %}
{% set values = {'http':'http','tcp':'tcp'} %}
{{ select('backend-mode-select', name='mode', values=values, selected='http', required='required', class='force_close') }}
<span id="https-backend-span">
<label for="https-backend" style="margin-top: 5px;">Is SSL enabled on frontend?</label>
<input type="checkbox" id="https-backend" name="ssl" value="https">
</span>
<div id="https-hide-backend" style="display: none;">
<label for="ssl-dis-check-backend" style="margin-top: 5px;">{{lang.add_page.buttons.disable_ssl_check}}</label><input type="checkbox" id="ssl-dis-check-backend" name="ssl-dis-check" value="ssl-dis-check">
<label for="ssl-check-backend" style="margin-top: 5px;">{{lang.add_page.buttons.disable_ssl_verify}}</label><input type="checkbox" id="ssl-check-backend" name="ssl-check" value="ssl-check" checked>
</div>
</td>
</tr>
<tr class="advance">
<td class="addName">{{lang.words.balance|title()}}: </td>
<td class="addOption">
{{ select('balance', values=balance_params, selected='roundrobin', required='required', class='force_close') }}
</td>
</tr>
<tr class="advance">
<td class="addName">{{lang.words.health|title()}} {{lang.words.check}}: </td>
<td class="addOption">
{{ select('backend_checks', name='health_check', values=checks, selected='', class='force_close') }}
<span id="backend_checks_note" class="tooltip tooltipTop"></span>
<br />
<span id="backend_checks_http" style="display: none;">
URI path for checking: {{ input('backend_checks_http_path', name='checks_http_path', value='/', title="URI for checking e.g. /check") }}
Domain name: {{ input('backend_checks_http_domain', name='checks_http_domain', placeholder='domain.com', title="Domain name for checking e.g. domain.com") }}
</span>
</td>
</tr>
<tr class="advance">
<td class="addName">{{lang.words.headers|title()}}: </td>
<td class="addOption">
<span title="{{lang.words.add|title()}} {{lang.words.headers}}" id="add_backend_header" class="link add-server"></span>
<div id="backend_header_div" style="display: none;">
<p style="border-bottom: 1px solid #ddd; padding-bottom: 10px;" id="backend_header_p">
{{ select('backend_header_res_method', name='headers_res', values=header_res, first='------', class='force_close') }}
{{ select('backend_header_method', name='headers_method', values=header_params, selected='add-header', class='force_close') }}
<b class="padding10">{{lang.words.name}}</b>
{{ input('backend_header_name', name="header_name") }}
<b class="padding10">{{lang.words.value}}</b>
{{ input('lbackend_header_value', name="header_value", placeholder='Leave blank if using del-header') }}
<span class="minus minus-style" onclick="deleteId('listener_header_p')" title="{{lang.words.delete|title()}}"></span>
</p>
</div>
<span>
<a class="link add-server" id="backend_add_header" title="{{lang.words.add|title()}} {{lang.words.headers}}" style="display: none;"></a>
</span>
</td>
</tr>
<tr class="advance">
<td class="addName" title="Access control list">ACL: </td>
<td class="addOption">
<span title="{{lang.words.add|title()}} ACL" id="add_backend_acl" class="link add-server"></span>
<div id="backend_acl" style="display: none;">
<p id="backend_acl_rule" style="border-bottom: 1px solid #ddd; padding-bottom: 10px;">
<b class="padding10">if</b>
{{ select('backend_acl_if', name='acl_if', values=if_values, first='Select if', class='force_close', disabled=false) }}
<b class="padding10">value</b>
{{ input('backend_acl_value', name="acl_value") }}
<b class="padding10">then</b>
{% set values = dict() %}
{% set values = {'2':'Redirect to','3':'Allow','4':'Deny', '6': 'Return', '7': 'Set-header'} %}
{{ select('backend_acl_then', name='acl_then', values=values, first='Select then', class='force_close', disabled=false) }}
<b class="padding10">value</b>
{{ input('backend_acl_then_value', name='acl_then_value', title="Required if \'then\' is \'Use backend\', \'Redirect\', \'Return\', or \'Set-header\'") }}
<span class="minus minus-style" onclick="deleteId('backend_acl_rule')" title="{{lang.words.delete|title()}} {{lang.words.this}} {{lang.words.rule}}"></span>
</p>
</div>
<span>
<a class="link add-server" id="backend_add_acl" title="{{lang.words.add|title()}} ACL" style="cursor: pointer; display: none;"></a>
</span>
</td>
</tr>
<tr class="advance">
<td class="addName"><span title="Cache support start 1.8 and latter" class="help_cursor">Web {{lang.words.acceleration}}:</span></td>
<td class="addOption">
<span class="controlgroup">
{{ checkbox('compression3', name="compression", title=lang.add_page.desc.http_compression, value='1', desc=lang.words.compression|title()) }}
{{ checkbox('cache3', name="cache", title=lang.words.enable|title()+' '+lang.words.cache, value='2', desc=lang.words.cache|title()) }}
{{ checkbox('ssl_offloading2', title=lang.add_page.desc.http_https, desc='SSL Offloading') }}
</span>
</td>
</tr>
<tr class="advance">
<td class="addName">{{lang.words.options|title()}}:</td>
<td class="addOption">
<span class="controlgroup">
{{ checkbox('forward_for2', title=lang.add_page.desc.forward_for, desc='Forward for') }}
{{ checkbox('redispatch2', title=lang.add_page.desc.redispatch, desc='Redispatch') }}
{% set values = dict() %}
{% set values = {'Off':'Off','Server only':'Server only','Force close':'Force close','Pretend keep alive':'Pretend keep alive'} %}
{{ select('force_close', values=values, first='Force HTTP close', title=lang.add_page.desc.force_close, class='force_close') }}
{{ checkbox('cookie2', title=lang.add_page.desc.cookie, desc=lang.words.set|title()+' cookie', value='1') }}
{{ checkbox('options-backend-show', title=lang.add_page.buttons.set_options_m, desc=lang.add_page.buttons.set_options) }}
</span>
<br>
<span id="cookie_div2" style="display: none;">
<input type="text" placeholder="name" name="cookie_name" id="cookie_name2" class="form-control"><br><br>
<input type="text" placeholder="domain" name="cookie_domain" class="form-control"><br><br>
<span class="controlgroup">
{% set values = dict() %}
{% set values = {'None':'None','rewrite':'rewrite','indirect':'indirect','insert':'insert'} %}
{{ select('rewrite2', values=values, first='rewrite/indirect/insert', class='force_close') }}
{{ checkbox('prefix2', name='prefix', title=lang.add_page.desc.c_prefix, desc='prefix', value='prefix') }}
{{ checkbox('nocache2', name='nocache', title=lang.add_page.desc.c_nocache, desc='nocache', value='nocache') }}
{{ checkbox('postonly2', name='postonly', title=lang.add_page.desc.c_postonly, desc='postonly', value='postonly') }}
{{ checkbox('dynamic2', name='dynamic', title=lang.add_page.desc.c_dynamic, desc='dynamic', value='dynamic') }}
<span id="dynamic_div2" style="display: none;">
dynamic-cookie-key: {{ input('dynamic-cookie-key2', name='dynamic-cookie-key', placeholder="your-custom-key") }}
</span>
</span>
</span>
<div id="options-backend-show-div" style="display: none;">
<div style="font-size: 12px; padding-bottom: 10px;">
<span style="padding-right: 10px;">{{lang.words.start2|title()}} {{lang.words.typing}} {{lang.words.options}}: </span>
{{ input('options2') }}
<span style="padding-left: 10px;">
{{lang.add_page.desc.press_down}}. <a href="http://cbonte.github.io/haproxy-dconv/1.7/configuration.html" target="_blanck" style="color: #23527c" title="HAproxy docs">{{lang.words.read|title()}} {{lang.words.more}} {{lang.words.about}} {{lang.words.options}}</a>
</span>
<br />
<span style="padding-right: 10px;">{{lang.words.start2|title()}} {{lang.words.typing}} {{lang.words.saved}} {{lang.words.options}}: </span>
<input type="text" id="saved-options2" class="form-control">
{{lang.add_page.desc.saved_options}}
</div>
<textarea name="option" title="Options thru" cols=80 rows=5 id="optionsInput2" placeholder="acl test hdr_beg(host) -i some_host"></textarea>
</div>
</td>
</tr>
<tr>
<td class="addName">{{lang.words.servers|title()}}:</td>
<td class="addOption">
{% include 'include/add_servers.html' %}
<br>
<br>
{{ checkbox('template-backend', name='template', title=lang.add_page.desc.server_template, value='template', desc=lang.add_page.desc.server_template) }}
</td>
</tr>
<tr class="advance">
<td class="addName">{{lang.words.check|title()}}:</td>
<td class="addOption">
<div>
<label for="controlgroup-backend-show" style="margin-top: 5px;" title="Set custom check parameters">{{lang.words.custom|title()}} {{lang.words.check}} {{lang.words.params}}</label>
<input type="checkbox" id="controlgroup-backend-show" name="default-check">
<span class="tooltip tooltipTop">{{lang.add_page.desc.def_check}}: inter 2000 rise 2 fall 5</span>
</div>
<div class="controlgroup" id="controlgroup-backend" style="display: none;">
<label for="check-servers-backend" title="Ebable servers check">{{lang.words.check|title()}}</label>
<input type="checkbox" id="check-servers-backend" name="check-servers" checked value="1">
{% set values = dict() %}
{% set values = {'1000':'1000','2000':'2000','3000':'3000'} %}
{{ select('inter-backend', values=values, first='inter', class='force_close') }}
{% set values = dict() %}
{% set values = {'1':'1','2':'2','3':'3'} %}
{{ select('rise-backend', name='rise', values=values, first='rise', class='force_close') }}
{% set values = dict() %}
{% set values = {'4':'4','5':'5','6':'6'} %}
{{ select('fall-backend', name='fall', values=values, first='fall', class='force_close') }}
</div>
<div style="display: block">
{{ checkbox('circuit_breaking_backend', name="circuit_breaking", desc='Circuit Breaking', title=lang.add_page.desc.circuit_breaking, value='1') }}
</div>
<div id="circuit_breaking_backend_div" style="display: none">
Observe:
{{ select('circuit_breaking_observe', values=observe, class='force_close') }}
error-limit: {{ input('circuit_breaking_error_limit', type='number', value='50', style='width: 50px;') }}
on-error:
{{ select('circuit_breaking_on_error', values=on_error, class='force_close') }}
<div class="tooltip tooltipTop">{{lang.words.read|title()}} {{lang.words.more}} {{lang.words.about}} Circuit Breaking <a href="https://roxy-wi.org/description/circuit-breaking" title="Circuit Breaking" target="_blank">{{lang.words.here}}</a></div>
</div>
</td>
</tr>
<tr class="advance-show">
<td class="addOption" colspan="2">
<button title="{{lang.add_page.buttons.show_full_settings}}" class="row-down advance-show-button">{{lang.words.show|title()}} {{lang.words.advanced}} {{lang.words.settings}}</button>
<button title="{{lang.add_page.buttons.hide_full_settings}}" class="row-up advance-hide-button" style="display: none">{{lang.words.hide|title()}} {{lang.words.advanced}} {{lang.words.settings}}</button>
</td>
</tr>
<tr>
<td class="addButton">
<a class="ui-button ui-widget ui-corner-all" title="{{lang.words.add|title()}} {{lang.words.backend|title()}}" onclick="addProxy('add-backend')">{{lang.words.add|title()}} {{lang.words.backend|title()}}</a>
</td>
<td class="addButton">
<a class="ui-button ui-widget ui-corner-all" title="{{lang.words.generate|title()}} {{lang.words.and}} {{lang.words.display}} {{lang.words.config}}" onclick="generateConfig('add-backend')">{{lang.words.generate|title()}} {{lang.words.config}}</a>
</td>
</tr>
</table>
</form>
</div> </div>
<div id="ssl"> <div id="ssl">
@ -873,77 +214,7 @@
</div> </div>
</div> </div>
<div id="userlist"> <div id="userlist">
<form name="add-userlist" id="add-userlist" action="/add/haproxy/userlist" method="post"> {% include 'include/add/userlist.html' %}
<table>
<caption><h3>{{lang.words.add|title()}} {{lang.words.userlists}}</h3></caption>
<tr>
<td class="addName">{{lang.words.select|title()}} {{lang.words.w_a}} {{lang.words.server}}: </td>
<td class="addOption">
{{ select('userlist_serv', name='serv', values=g.user_params['servers'], is_servers='true') }}
<div class="tooltip tooltipTop"><b>{{lang.words.note|title()}}:</b> {{lang.phrases.master_slave}}</div>
</td>
<td rowspan="4" class="add-note addName alert-info">{{lang.add_page.desc.userlist_desc}}</td>
</tr>
<tr>
<td class="addName">{{lang.words.name|title()}}:</td>
<td class="addOption">
{{ input('new_userlist', required='required', title=lang.add_page.desc.userlist_name, placeholder="basic-auth-list") }}
</td>
</tr>
<tr>
<td class="addName">{{lang.words.groups|title()}}:</td>
<td class="addOption">
<span id="userlist-groups">
{{ input('userlist-group', title=lang.add_page.desc.userlist_user_grp, placeholder="group_name") }}
</span>
<span>
<span class="link add-server" id="add-userlist-group" title="Add extra group" style="cursor: pointer;"></span>
</span>
<div class="tooltip tooltipTop">{{lang.add_page.desc.userlist_group}}</div>
</td>
</tr>
<tr>
<td class="addName">{{lang.words.user|title()}}:</td>
<td class="addOption">
<span id="userlist-users">
{{ input('userlist-user', required='required', title=lang.words.username|title(), placeholder="user_name") }}
{{ input('userlist-password', required='required', title=lang.add_page.desc.userlist_pass, placeholder="password") }}
{{ input('userlist-user-group', title=lang.add_page.desc.userlist_user_grp, placeholder="group") }}
</span>
<span>
<span class="link add-server" id="add-userlist-user" title="Add extra user" style="cursor: pointer;"></span>
</span>
<div class="tooltip tooltipTop">{{lang.add_page.desc.userlist_user}}</div>
</td>
</tr>
<tr>
<td class="addButton">
<a class="ui-button ui-widget ui-corner-all" title="Add Userlist" onclick="addProxy('add-userlist')">{{lang.words.add|title()}} {{lang.words.userlist|title()}}</a>
</td>
<td class="addButton">
<a class="ui-button ui-widget ui-corner-all" title="{{lang.words.generate|title()}} {{lang.words.and}} {{lang.words.display}} {{lang.words.config}}" onclick="generateConfig('add-userlist')">{{lang.words.generate|title()}} {{lang.words.config}}</a>
</td>
</tr>
</table>
</form>
<br />
<table>
<caption><h3>{{lang.words.existing|title()}} {{lang.words.userlists}}</h3></caption>
<tr>
<td class="addName">{{lang.words.select|title()}} {{lang.words.w_a}} {{lang.words.server}}: </td>
<td class="addOption">
{{ select('existing_userlist_serv', name='serv', values=g.user_params['servers'], is_servers='true') }}
<div class="tooltip tooltipTop"><b>{{lang.words.note|title()}}:</b> {{lang.phrases.master_slave}}</div>
</td>
<td>
<a class="ui-button ui-widget ui-corner-all" title="Add Userlist" onclick="showUserlists()">{{lang.words.show|title()}} {{lang.words.userlists}}</a>
</td>
</tr>
<tr id="existing_userlist_tr" style="display: none;">
<td class="addName">{{lang.words.existing|title()}} {{lang.words.userlists}}:</td>
<td class="addOption" id="existing_userlist_ajax"></td>
</tr>
</table>
<div class="add-note addName alert-info" style="width: inherit; margin-right: 15px;"> <div class="add-note addName alert-info" style="width: inherit; margin-right: 15px;">
{{lang.add_page.desc.userlist}} {{lang.add_page.desc.userlist}}
</div> </div>
@ -956,52 +227,7 @@
</div> </div>
</div> </div>
<div id="peers"> <div id="peers">
<form name="add-peers" id="add-peers" action="/add/haproxy/peers" method="post"> {% include 'include/add/peers.html' %}
<table>
<caption><h3>{{lang.words.add|title()}} Peer</h3></caption>
<tr>
<td class="addName">{{lang.words.select|title()}} {{lang.words.w_a}} {{lang.words.server}}: </td>
<td class="addOption">
{{ select('peers_serv', name='serv', values=g.user_params['servers'], is_servers='true') }}
<div class="tooltip tooltipTop"><b>{{lang.words.note|title()}}:</b> {{lang.phrases.master_slave}}</div>
</td>
<td rowspan="4" class="add-note addName alert-info">
{{lang.add_page.desc.peers}}
</td>
</tr>
<tr>
<td class="addName">Peers {{lang.words.name}}:</td>
<td class="addOption">
{{ input('peers-name', title="Peers "+lang.words.name, placeholder="peers name") }}
</td>
</tr>
<tr>
<td class="addName">Peers {{lang.words.servers}}:</td>
<td class="addOption">
<span name="add_peers">
<input name="servers_name" required title="Peer {{lang.words.name}}" size=14 placeholder="haproxy1" class="form-control">:
<input name="servers" required title="Peer IP" size=14 placeholder="xxx.xxx.xxx.xxx" class="form-control">:
<input name="server_port" required title="Peer {{lang.words.port}}" size=3 placeholder="yyy" class="form-control add_server_number" type="number">
<br />
<input name="servers_name" required title="Peer {{lang.words.name}}" size=14 placeholder="haproxy2" class="form-control">:
<input name="servers" title="Peer IP" size=14 placeholder="xxx.xxx.xxx.xxx" class="form-control second-server"><span class="second-server">:</span>
<input name="server_port" title="Peer {{lang.words.port}}" size=3 placeholder="yyy" class="form-control second-server add_server_number" type="number">
</span>
<span>
<a class="link add-server backend_server" name="add-peer-input" title="{{lang.words.add|title()}} peer {{lang.words.server}}" style="cursor: pointer;"></a>
</span>
</td>
</tr>
<tr>
<td class="addButton">
<a class="ui-button ui-widget ui-corner-all" title="{{lang.words.add|title()}} peer" onclick="addProxy('add-peers')">{{lang.words.add|title()}} peer</a>
</td>
<td class="addButton">
<a class="ui-button ui-widget ui-corner-all" title="{{lang.words.generate|title()}} {{lang.words.and}} {{lang.words.display}} {{lang.words.config}}" onclick="generateConfig('add-peers')">{{lang.words.generate|title()}} {{lang.words.config}}</a>
</td>
</tr>
</table>
</form>
<div class="alert addName alert-info" style="width: inherit; margin-right: 15px;"> <div class="alert addName alert-info" style="width: inherit; margin-right: 15px;">
{{lang.add_page.desc.peers_master}} {{lang.add_page.desc.peers_master}}
</div> </div>

View File

@ -1,4 +1,5 @@
{% import 'languages/'+lang|default('en')+'.html' as lang %} {% import 'languages/'+lang|default('en')+'.html' as lang %}
{% from 'include/input_macros.html' import input, checkbox, select %}
<div class="left-space"> <div class="left-space">
<h4>{{lang.words.config|title()}} {% if config_file_name != 'undefined' %}{{config_file_name.replace('92', '/')}}{%endif%} {{lang.words.from}} {{serv}} ({{hostname}})</h4> <h4>{{lang.words.config|title()}} {% if config_file_name != 'undefined' %}{{config_file_name.replace('92', '/')}}{%endif%} {{lang.words.from}} {{serv}} ({{hostname}})</h4>
<p class="accordion-expand-holder"> <p class="accordion-expand-holder">
@ -206,12 +207,16 @@
</span><br /> </span><br />
{% endif %} {% endif %}
{% else %} {% else %}
{% if line.startswith('# BEGIN') or line.startswith('# END') %}
{% continue %}
{% endif %}
{% if line.startswith('global') %} {% if line.startswith('global') %}
<span class="param">{{ line }} <span class="param">{{ line }}
{% if role %} {% if role %}
{% if service != 'keepalived' %} {% if service != 'keepalived' %}
<span class="accordion-link"> <span class="accordion-link" onclick="openSection('{{ line| trim }}')">
<a href="/config/section/haproxy/{{serv}}/{{ line }}">{{lang.words.edit|title()}}</a> {{lang.words.edit|title()}}/{{lang.words.delete|title()}}
</span>
</span> </span>
{% endif %} {% endif %}
{% endif %} {% endif %}
@ -221,8 +226,8 @@
{% if line.startswith('defaults') %} {% if line.startswith('defaults') %}
</div><span class="param">{{ line }} </div><span class="param">{{ line }}
{% if role %} {% if role %}
<span class="accordion-link"> <span class="accordion-link" onclick="openSection('{{ line| trim }}')">
<a href="/config/section/haproxy/{{serv}}/{{ line }}">{{lang.words.edit|title()}}</a> {{lang.words.edit|title()}}/{{lang.words.delete|title()}}
</span> </span>
{% endif %} {% endif %}
</span><div> </span><div>
@ -231,8 +236,8 @@
{%- if line.startswith('listen') -%} {%- if line.startswith('listen') -%}
</div><span class="param">{{- line -}} </div><span class="param">{{- line -}}
{% if role %} {% if role %}
<span class="accordion-link"> <span class="accordion-link" onclick="openSection('{{ line| trim }}')">
<a href="/config/section/haproxy/{{serv}}/{{ line }}" target="_blank">{{lang.words.edit|title()}}/{{lang.words.delete|title()}}</a> {{lang.words.edit|title()}}/{{lang.words.delete|title()}}
</span> </span>
{% endif %} {% endif %}
{%- set backend = line.split(' ') -%} {%- set backend = line.split(' ') -%}
@ -241,15 +246,15 @@
</span> </span>
{%- set backend = backend|join('_') -%} {%- set backend = backend|join('_') -%}
{%- do section_name.update({i: backend}) -%} {%- do section_name.update({i: backend}) -%}
<span id="{{-section_name[i]|replace('\n', '')-}}" class="accordion-link" target="_blank"></span> <span id="{{-section_name[i]|replace('\n', '')-}}" class="accordion-link"></span>
</span><div> </span><div>
{% continue %} {% continue %}
{%- endif -%} {%- endif -%}
{%- if line.startswith('frontend') -%} {%- if line.startswith('frontend') -%}
</div><span class="param">{{ line }} </div><span class="param">{{ line }}
{% if role %} {% if role %}
<span class="accordion-link"> <span class="accordion-link" onclick="openSection('{{ line| trim }}')">
<a href="/config/section/haproxy/{{serv}}/{{ line }}">{{lang.words.edit|title()}}/{{lang.words.delete|title()}}</a> {{lang.words.edit|title()}}/{{lang.words.delete|title()}}
</span> </span>
{% endif %} {% endif %}
{% set backend = line.split(' ') %} {% set backend = line.split(' ') %}
@ -265,8 +270,8 @@
{% if line.startswith('backend') %} {% if line.startswith('backend') %}
</div><span class="param">{{ line }} </div><span class="param">{{ line }}
{% if role %} {% if role %}
<span class="accordion-link"> <span class="accordion-link" onclick="openSection('{{ line| trim }}')">
<a href="/config/section/haproxy/{{serv}}/{{ line }}">{{lang.words.edit|title()}}/{{lang.words.delete|title()}}</a> {{lang.words.edit|title()}}/{{lang.words.delete|title()}}
</span> </span>
{% endif %} {% endif %}
{% set backend = line.split(' ') %} {% set backend = line.split(' ') %}
@ -289,8 +294,8 @@
{% if line.startswith('peers') %} {% if line.startswith('peers') %}
</div><span class="param">{{ line }} </div><span class="param">{{ line }}
{% if role %} {% if role %}
<span class="accordion-link"> <span class="accordion-link" onclick="openSection('{{ line| trim }}')">
<a href="/config/section/haproxy/{{serv}}/{{ line }}">{{lang.words.edit|title()}}/{{lang.words.delete|title()}}</a> {{lang.words.edit|title()}}/{{lang.words.delete|title()}}
</span> </span>
{% endif %} {% endif %}
</span><div> </span><div>
@ -309,8 +314,8 @@
{% if line.startswith('userlist') %} {% if line.startswith('userlist') %}
</div><span class="param">{{ line }} </div><span class="param">{{ line }}
{% if role %} {% if role %}
<span class="accordion-link"> <span class="accordion-link" onclick="openSection('{{ line| trim }}')">
<a href="/config/section/haproxy/{{serv}}/{{ line }}">{{lang.words.edit|title()}}/{{lang.words.delete|title()}}</a> {{lang.words.edit|title()}}/{{lang.words.delete|title()}}
</span> </span>
{% endif %} {% endif %}
</span><div> </span><div>
@ -406,3 +411,4 @@
$("#expand_link").click(); $("#expand_link").click();
} }
</script> </script>
<div id="edit-section" style="display: none;"></div>

View File

@ -0,0 +1,36 @@
{% import 'languages/'+lang|default('en')+'.html' as lang %}
{% from 'include/input_macros.html' import input, checkbox, select %}
{% set balance_params = {'roundrobin':'roundrobin','source':'source','leastconn':'leastconn','first':'first',
'rdp-cookie':'rdp-cookie', 'uri':'uri', 'uri whole':'uri whole', 'static-rr': 'static-rr',
'url_param userid':'url_param userid'} %}
{% set checks = {'':'Choose a custom health check','tcp-check':'Check a TCP Port', 'ssl-hello-chk':'Check a SSL Port',
'httpchk':'Check a HTTP service', 'ldap-check':'Check a LDAP service', 'mysql-check':'Check a MySql Service',
'pgsql-check':'Check a PgSQL Service', 'redis-check': 'Check a Redis Service', 'smtpchk':'Check a SMTP service'} %}
{% set observe = {'layer7':'layer7', 'layer4': 'layer4'} %}
{% set on_error = {'mark-down':'mark-down', 'fastinter': 'fastinter', 'fail-check':'fail-check',
'sudden-death':'sudden-death'} %}
{% set header_res = {'http-response': 'response', 'http-request': 'request'} %}
{% set header_params = {'add-header': 'add-header', 'set-header': 'set-header', 'del-header': 'del-header'} %}
{% set if_values = {'1':'Host name starts with','2':'Host name ends with','3':'Path starts with','4':'Path ends with', '6': 'Src ip'} %}
{% set force_close = {'0':'Off','1':'Server only','2':'Force close','3':'Pretend keep alive'} %}
<div id="edit-listen" style="display: none;">
{% include 'include/add/listen.html' %}
</div>
<div id="edit-frontend" style="display: none;">
{% include 'include/add/frontend.html' %}
</div>
<div id="edit-backend" style="display: none;">
{% include 'include/add/backend.html' %}
</div>
<div id="edit-userlist" style="display: none;">
{% include 'include/add/userlist.html' %}
</div>
<div id="edit-peers" style="display: none;">
{% include 'include/add/peers.html' %}
</div>
<div id="edit-global" style="display: none;">
{% include 'include/add/global.html' %}
</div>
<div id="edit-defaults" style="display: none;">
{% include 'include/add/defaults.html' %}
</div>

View File

@ -25,6 +25,8 @@
<script src="/static/js/codemirror/mode/haproxy.js"></script> <script src="/static/js/codemirror/mode/haproxy.js"></script>
<script src="/static/js/codemirror/keymap/sublime.js"></script> <script src="/static/js/codemirror/keymap/sublime.js"></script>
<script src="/static/js/configshow.js"></script> <script src="/static/js/configshow.js"></script>
<script src="/static/js/add.js"></script>
<script src="/static/js/edit_config.js"></script>
{% if is_serv_protected and g.user_params['role'] > 2 %} {% if is_serv_protected and g.user_params['role'] > 2 %}
<meta http-equiv="refresh" content="0; url=/service"> <meta http-equiv="refresh" content="0; url=/service">
{% else %} {% else %}
@ -129,7 +131,7 @@
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter", "breakpoints"], gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter", "breakpoints"],
highlightSelectionMatches: {showToken: /\w/, annotateScrollbar: true} highlightSelectionMatches: {showToken: /\w/, annotateScrollbar: true}
}); });
} else { } else if (cur_url[6] === 'edit') {
var myCodeMirror = CodeMirror.fromTextArea(document.getElementById("config_text_area"), var myCodeMirror = CodeMirror.fromTextArea(document.getElementById("config_text_area"),
{ {
mode: "nginx", mode: "nginx",

View File

@ -4,25 +4,29 @@
} }
</style> </style>
<span name="add_servers"> <span name="add_servers">
<input name="prefix" class="prefix form-control" title="servers prefix" size="3" placeholder="web" style="display: none;"> <p>
<input name="template-prefix" class="prefix form-control" title="servers prefix" size="3" placeholder="web" style="display: none;">
<input name="template-number" class="prefix form-control" title="servers num" value="3" type="number" style="width: 35px; display: none;"> <input name="template-number" class="prefix form-control" title="servers num" value="3" type="number" style="width: 35px; display: none;">
<input name="servers" required title="{{lang.words.backend|title()}} IP" size=14 placeholder="xxx.xxx.xxx.xxx" class="form-control">: <input name="servers" required title="{{lang.words.backend|title()}} IP" size=14 placeholder="xxx.xxx.xxx.xxx" class="form-control">:
<input name="server_port" required title="{{lang.words.backend|title()}} {{lang.words.port}}" size=8 placeholder="yyy" class="form-control add_server_number" type="number"> <input name="server_port" required title="{{lang.words.backend|title()}} {{lang.words.port}}" size=8 placeholder="yyy" class="form-control add_server_number" type="number">
<span name="port_check_text">{{lang.phrases.port_check}}:</span> <input name="port_check" required title="{{lang.words.port|title()}} {{lang.words.for}} {{lang.words.checking}}" data-help="{{lang.add_page.desc.port_check}}" size=8 class="form-control add_server_number" type="number"> <span name="port_check_text">{{lang.phrases.port_check}}:</span> <input name="port_check" required title="{{lang.words.port|title()}} {{lang.words.for}} {{lang.words.checking}}" data-help="{{lang.add_page.desc.port_check}}" size=8 class="form-control add_server_number" type="number">
<span name="maxconn_name">maxconn:</span> <input name="server_maxconn" required title="Maxconn. Default 200" data-help="{{lang.add_page.desc.maxconn}}" size=8 value="200" class="form-control add_server_number" type="number"> <span name="maxconn_name">maxconn:</span> <input name="server_maxconn" required title="Maxconn. Default 200" data-help="{{lang.add_page.desc.maxconn}}" size=8 value="200" class="form-control add_server_number" type="number">
<span class="send_proxy"></span> <span class="send_proxy"></span>
<br /> </p>
<p>
<input name="servers" title="{{lang.words.backend|title()}} IP" size=14 placeholder="xxx.xxx.xxx.xxx" class="form-control second-server"><span class="second-server">:</span> <input name="servers" title="{{lang.words.backend|title()}} IP" size=14 placeholder="xxx.xxx.xxx.xxx" class="form-control second-server"><span class="second-server">:</span>
<input name="server_port" title="{{lang.words.backend|title()}} {{lang.words.port}}" size=8 placeholder="yyy" class="form-control second-server add_server_number" type="number"> <input name="server_port" title="{{lang.words.backend|title()}} {{lang.words.port}}" size=8 placeholder="yyy" class="form-control second-server add_server_number" type="number">
<span name="port_check_text">{{lang.phrases.port_check}}:</span> <input name="port_check" required title="{{lang.words.port|title()}} {{lang.words.for}} {{lang.words.checking}}" data-help="{{lang.add_page.desc.port_check}}" size=8 class="form-control add_server_number" type="number"> <span name="port_check_text">{{lang.phrases.port_check}}:</span> <input name="port_check" required title="{{lang.words.port|title()}} {{lang.words.for}} {{lang.words.checking}}" data-help="{{lang.add_page.desc.port_check}}" size=8 class="form-control add_server_number" type="number">
<span name="maxconn_name">maxconn:</span> <input name="server_maxconn" required title="Maxconn. Default 200" data-help="{{lang.add_page.desc.maxconn}}" size=8 value="200" class="form-control add_server_number" type="number"> <span name="maxconn_name">maxconn:</span> <input name="server_maxconn" required title="Maxconn. Default 200" data-help="{{lang.add_page.desc.maxconn}}" size=8 value="200" class="form-control add_server_number" type="number">
<span class="send_proxy"></span> <span class="send_proxy"></span>
<br /> </p>
<p>
<input name="servers" title="Backend IP" size=14 placeholder="xxx.xxx.xxx.xxx" class="form-control second-server"><span class="second-server">:</span> <input name="servers" title="Backend IP" size=14 placeholder="xxx.xxx.xxx.xxx" class="form-control second-server"><span class="second-server">:</span>
<input name="server_port" title="{{lang.words.backend|title()}} {{lang.words.port}}" size=3 placeholder="yyy" class="form-control second-server add_server_number" type="number"> <input name="server_port" title="{{lang.words.backend|title()}} {{lang.words.port}}" size=3 placeholder="yyy" class="form-control second-server add_server_number" type="number">
<span name="port_check_text">{{lang.phrases.port_check}}:</span> <input name="port_check" required title="{{lang.words.port|title()}} {{lang.words.for}} {{lang.words.checking}}" data-help="{{lang.add_page.desc.port_check}}" size=8 class="form-control add_server_number" type="number"> <span name="port_check_text">{{lang.phrases.port_check}}:</span> <input name="port_check" required title="{{lang.words.port|title()}} {{lang.words.for}} {{lang.words.checking}}" data-help="{{lang.add_page.desc.port_check}}" size=8 class="form-control add_server_number" type="number">
<span name="maxconn_name">maxconn:</span> <input name="server_maxconn" required title="Maxconn. Default 200" data-help="{{lang.add_page.desc.maxconn}}" size=8 value="200" class="form-control add_server_number" type="number"> <span name="maxconn_name">maxconn:</span> <input name="server_maxconn" required title="Maxconn. Default 200" data-help="{{lang.add_page.desc.maxconn}}" size=8 value="200" class="form-control add_server_number" type="number">
<span class="send_proxy"></span> <span class="send_proxy"></span>
</p>
</span> </span>
<span> <span>
<a class="link add-server backend_server" name="add-server-input" title="{{lang.words.add|title()}} {{lang.words.backend}} {{lang.words.server}}" style="cursor: pointer;"></a> <a class="link add-server backend_server" name="add-server-input" title="{{lang.words.add|title()}} {{lang.words.backend}} {{lang.words.server}}" style="cursor: pointer;"></a>

View File

@ -0,0 +1,208 @@
<form name="add-backend" id="add-backend" action="/add/haproxy/add" method="post">
<table>
<caption><h3>{{lang.words.add|title()}} {{lang.words.backend}}</h3></caption>
<tr>
<td class="addName">{{lang.words.select|title()}} {{lang.words.w_a}} {{lang.words.server}}: </td>
<td class="addOption">
{{ select('serv3', name='server', values=g.user_params['servers'], is_servers='true', by_id=1) }}
<div class="tooltip tooltipTop"><b>{{lang.words.note|title()}}:</b> {{lang.phrases.master_slave}}</div>
</td>
<td rowspan="4" class="add-note addName alert-info">
{{lang.add_page.desc.back_des1}}
<br /><br />
{{lang.add_page.desc.listener_desc2}}
<br /><br />
{{lang.add_page.desc.listener_desc3}}
</td>
</tr>
<tr>
<td class="addName">{{lang.words.name|title()}}:</td>
<td class="addOption">
{{ input('new_backend', name='name', title=lang.words.name|title() + ' ' +lang.words.backend, placeholder="web_80", required='required') }}
</td>
</tr>
<tr class="advance">
<td class="addName">{{lang.words.mode|title()}}: </td>
<td class="addOption">
{% set values = dict() %}
{% set values = {'http':'http','tcp':'tcp'} %}
{{ select('backend-mode-select', name='mode', values=values, selected='http', required='required', class='force_close') }}
<span id="https-backend-span">
<label for="https-backend" style="margin-top: 5px;">Is SSL enabled on frontend?</label>
<input type="checkbox" name="ssl" id="https-backend" value="https">
</span>
<div id="https-hide-backend" style="display: none;">
<label for="ssl-check-backend" style="margin-top: 5px;">{{lang.add_page.buttons.disable_ssl_verify}}</label><input type="checkbox" id="ssl-check-backend" name="ssl-check" value="ssl-check" checked>
</div>
</td>
</tr>
<tr class="advance">
<td class="addName">{{lang.words.balance|title()}}: </td>
<td class="addOption">
{{ select('backend-balance', name="balance", values=balance_params, selected='roundrobin', required='required', class='force_close') }}
</td>
</tr>
<tr class="advance">
<td class="addName">{{lang.words.health|title()}} {{lang.words.check}}: </td>
<td class="addOption">
{{ select('backend_checks', name='health_check', values=checks, selected='', class='force_close') }}
<span id="backend_checks_note" class="tooltip tooltipTop"></span>
<br />
<span id="backend_checks_http" style="display: none;">
URI path for checking: {{ input('backend_checks_http_path', name='checks_http_path', value='/', title="URI for checking e.g. /check") }}
Domain name: {{ input('backend_checks_http_domain', name='checks_http_domain', placeholder='domain.com', title="Domain name for checking e.g. domain.com") }}
</span>
</td>
</tr>
<tr class="advance">
<td class="addName">{{lang.words.headers|title()}}: </td>
<td class="addOption">
<span title="{{lang.words.add|title()}} {{lang.words.headers}}" id="add_backend_header" class="link add-server"></span>
<div id="backend_header_div" style="display: none;">
<p style="border-bottom: 1px solid #ddd; padding-bottom: 10px;" id="backend_header_p">
{{ select('backend_header_res_method', name='headers_res', values=header_res, first='------', class='force_close') }}
{{ select('backend_header_method', name='headers_method', values=header_params, selected='add-header', class='force_close') }}
<b class="padding10">{{lang.words.name}}</b>
{{ input('backend_header_name', name="header_name") }}
<b class="padding10">{{lang.words.value}}</b>
{{ input('backend_header_value', name="header_value", placeholder='Leave blank if using del-header') }}
<span class="minus minus-style" onclick="deleteId('backend_header_p')" title="{{lang.words.delete|title()}}"></span>
</p>
</div>
<span>
<a class="link add-server" id="backend_add_header" title="{{lang.words.add|title()}} {{lang.words.headers}}" style="display: none;"></a>
</span>
</td>
</tr>
<tr class="advance">
<td class="addName" title="Access control list">ACL: </td>
<td class="addOption">
<span title="{{lang.words.add|title()}} ACL" id="add_backend_acl" class="link add-server"></span>
<div id="backend_acl" style="display: none;">
<p id="backend_acl_rule" style="border-bottom: 1px solid #ddd; padding-bottom: 10px;">
<b class="padding10">{{lang.words.if|title()}}</b>
{{ select('backend_acl_if', name='acl_if', values=if_values, first='Select if', class='force_close', disabled=false) }}
<b class="padding10">{{ lang.words.value }}</b>
{{ input('backend_acl_value', name="acl_value") }}
<b class="padding10">{{ lang.words.then }}</b>
{% set values = dict() %}
{% set values = {'2':'Redirect to','3':'Allow','4':'Deny', '6': 'Return', '7': 'Set-header'} %}
{{ select('backend_acl_then', name='acl_then', values=values, first='Select then', class='force_close', disabled=false) }}
<b class="padding10">{{ lang.words.value }}</b>
{{ input('backend_acl_then_value', name='acl_then_value', title="Required if \'then\' is \'Use backend\', \'Redirect\', \'Return\', or \'Set-header\'") }}
<span class="minus minus-style" onclick="deleteId('backend_acl_rule')" title="{{lang.words.delete|title()}} {{lang.words.this}} {{lang.words.rule}}"></span>
</p>
</div>
<span>
<a class="link add-server" id="backend_add_acl" title="{{lang.words.add|title()}} ACL" style="cursor: pointer; display: none;"></a>
</span>
</td>
</tr>
<tr class="advance">
<td class="addName"><span title="Cache support start 1.8 and latter" class="help_cursor">Web {{lang.words.acceleration}}:</span></td>
<td class="addOption">
<span class="controlgroup">
{{ checkbox('compression3', name="compression", title=lang.add_page.desc.http_compression, value='1', desc=lang.words.compression|title()) }}
{{ checkbox('cache3', name="cache", title=lang.words.enable|title()+' '+lang.words.cache, value='2', desc=lang.words.cache|title()) }}
{{ checkbox('ssl_offloading2', name='ssl_offloading', title=lang.add_page.desc.http_https, desc='SSL Offloading') }}
</span>
</td>
</tr>
<tr class="advance">
<td class="addName">{{lang.words.options|title()}}:</td>
<td class="addOption">
<span class="controlgroup">
{{ checkbox('forward_for2', name='forward_for', title=lang.add_page.desc.forward_for, desc='Forward for') }}
{{ checkbox('redispatch2', name='redispatch', title=lang.add_page.desc.redispatch, desc='Redispatch') }}
{{ checkbox('backend_cookie', name='cookie', title=lang.add_page.desc.cookie, desc=lang.words.set|title()+' cookie', value='1') }}
{{ checkbox('options-backend-show', title=lang.add_page.buttons.set_options_m, desc=lang.add_page.buttons.set_options) }}
</span>
<br>
<span id="backend_cookie_div" style="display: none;">
<input type="text" placeholder="name" name="cookie_name" id="backend_cookie_name" class="form-control"><br><br>
<input type="text" placeholder="domain" name="cookie_domain" class="form-control"><br><br>
<span class="controlgroup">
{% set values = dict() %}
{% set values = {'None':'None','rewrite':'rewrite','indirect':'indirect','insert':'insert'} %}
{{ select('backend_rewrite', name='rewrite', values=values, first='rewrite/indirect/insert', class='force_close') }}
{{ checkbox('backend_prefix', name='prefix', title=lang.add_page.desc.c_prefix, desc='prefix', value='prefix') }}
{{ checkbox('backend_nocache', name='nocache', title=lang.add_page.desc.c_nocache, desc='nocache', value='nocache') }}
{{ checkbox('backend_postonly', name='postonly', title=lang.add_page.desc.c_postonly, desc='postonly', value='postonly') }}
{{ checkbox('backend_dynamic', name='dynamic', title=lang.add_page.desc.c_dynamic, desc='dynamic', value='dynamic') }}
<span id="backend_dynamic_div" style="display: none;">
dynamic-cookie-key: {{ input('backend_dynamic-cookie-key', name='dynamic-cookie-key', placeholder="your-custom-key") }}
</span>
</span>
</span>
<div id="options-backend-show-div" style="display: none;">
<div style="font-size: 12px; padding-bottom: 10px;">
<span style="padding-right: 10px;">{{lang.words.start2|title()}} {{lang.words.typing}} {{lang.words.options}}: </span>
{{ input('options2') }}
<span style="padding-left: 10px;">
{{lang.add_page.desc.press_down}}. <a href="http://cbonte.github.io/haproxy-dconv/1.7/configuration.html" target="_blanck" style="color: #23527c" title="HAproxy docs">{{lang.words.read|title()}} {{lang.words.more}} {{lang.words.about}} {{lang.words.options}}</a>
</span>
<br />
<span style="padding-right: 10px;">{{lang.words.start2|title()}} {{lang.words.typing}} {{lang.words.saved}} {{lang.words.options}}: </span>
<input type="text" id="saved-options2" class="form-control">
{{lang.add_page.desc.saved_options}}
</div>
<textarea name="option" title="Options thru" cols=80 rows=5 id="optionsInput2" placeholder="acl test hdr_beg(host) -i some_host"></textarea>
</div>
</td>
</tr>
<tr>
<td class="addName">{{lang.words.servers|title()}}:</td>
<td class="addOption">
{% include 'include/add/add_servers.html' %}
<br>
<br>
{{ checkbox('template-backend', name='template', title=lang.add_page.desc.server_template, value='template', desc=lang.add_page.desc.server_template) }}
</td>
</tr>
<tr class="advance">
<td class="addName">{{lang.words.check|title()}}:</td>
<td class="addOption">
<div>
<label for="controlgroup-backend-show" style="margin-top: 5px;" title="Set custom check parameters">{{lang.words.custom|title()}} {{lang.words.check}} {{lang.words.params}}</label>
<input type="checkbox" id="controlgroup-backend-show" name="default-check">
<span class="tooltip tooltipTop">{{lang.add_page.desc.def_check}}: inter 2000 rise 2 fall 5</span>
</div>
<div class="controlgroup" id="controlgroup-backend" style="display: none;">
<label for="check-servers-backend" title="Enable servers check">{{lang.words.check|title()}}</label>
<input type="checkbox" id="check-servers-backend" name="check-servers" checked value="1">
{% set values = {'1000':'1000','2000':'2000','3000':'3000'} %}
{{ select('inter-backend', name='inter', values=values, first='2000', class='force_close') }}
{% set values = {'1':'1','2':'2','3':'3'} %}
{{ select('rise-backend', name='rise', values=values, first='2', class='force_close') }}
{% set values = {'4':'4','5':'5','6':'6'} %}
{{ select('fall-backend', name='fall', values=values, first='5', class='force_close') }}
</div>
<div style="display: block">
{{ checkbox('backend_circuit_breaking', name="circuit_breaking", desc='Circuit Breaking', title=lang.add_page.desc.circuit_breaking, value='1') }}
</div>
<div id="backend_circuit_breaking" style="display: none">
Observe:
{{ select('circuit_breaking_observe', values=observe, class='force_close') }}
error-limit: {{ input('circuit_breaking_error_limit', type='number', value='50', style='width: 50px;') }}
on-error:
{{ select('circuit_breaking_on_error', values=on_error, class='force_close') }}
<div class="tooltip tooltipTop">{{lang.words.read|title()}} {{lang.words.more}} {{lang.words.about}} Circuit Breaking <a href="https://roxy-wi.org/description/circuit-breaking" title="Circuit Breaking" target="_blank">{{lang.words.here}}</a></div>
</div>
</td>
</tr>
<tr class="advance-show">
<td class="addOption" colspan="2">
<button title="{{lang.add_page.buttons.show_full_settings}}" class="row-down advance-show-button">{{lang.words.show|title()}} {{lang.words.advanced}} {{lang.words.settings}}</button>
<button title="{{lang.add_page.buttons.hide_full_settings}}" class="row-up advance-hide-button" style="display: none">{{lang.words.hide|title()}} {{lang.words.advanced}} {{lang.words.settings}}</button>
</td>
</tr>
<tr id="backend-add-buttons">
<td class="addButton">
<a class="ui-button ui-widget ui-corner-all" id="backend-add-button" title="{{lang.words.add|title()}} {{lang.words.backend|title()}}" onclick="addProxy('add-backend')">{{lang.words.add|title()}} {{lang.words.backend|title()}}</a>
</td>
<td class="addButton">
<a class="ui-button ui-widget ui-corner-all" id="backend-generate-button" title="{{lang.words.generate|title()}} {{lang.words.and}} {{lang.words.display}} {{lang.words.config}}" onclick="addProxy('add-backend', 'true')">{{lang.words.generate|title()}} {{lang.words.config}}</a>
</td>
</tr>
</table>
</form>

View File

@ -0,0 +1,83 @@
<form name="add-defaults" id="add-defaults" action="/add/haproxy/defaults" method="post">
<table>
<tr>
<td class="addOption">
{{ select('defaults_serv', name='server', values=g.user_params['servers'], is_servers='true', by_id=1) }}
<div class="tooltip tooltipTop"><b>{{lang.words.note|title()}}:</b> {{lang.phrases.master_slave}}</div>
</td>
</tr>
<tr>
<td class="addOption">
{{ input('defaults-name', name='name', title="defaults "+lang.words.name, value="name") }}
</td>
</tr>
<tr>
<td class="addName">{{lang.words.log|title()}}:</td>
<td class="addOption">
{{ input('defaults-log', name='log', placeholder="global") }}
</td>
</tr>
<tr>
<td class="addName">{{lang.words.retries|title()}}:</td>
<td class="addOption">
{{ input('defaults-retries', name='retries', type='number') }}
<div class="tooltip tooltipTop">Set the number of retries to perform on a server after a failure</div>
</td>
</tr>
<tr>
<td class="addName">Maxconn:</td>
<td class="addOption">
{{ input('defaults-maxconn', name='maxconn', type='number') }}
<div class="tooltip tooltipTop">Sets the maximum per-process number of concurrent connections to number.</div>
</td>
</tr>
<tr>
<td class="addName">Timeout http request:</td>
<td class="addOption">
{{ input('defaults-timeout_http_request', name='http_request', placeholder="10", type="number") }}{{ lang.words.seconds2 }}
</td>
</tr>
<tr>
<td class="addName">Timeout queue:</td>
<td class="addOption">
{{ input('defaults-timeout_queue', name='queue', placeholder="60", type="number") }}{{ lang.words.seconds2 }}
</td>
</tr>
<tr>
<td class="addName">Timeout connect:</td>
<td class="addOption">
{{ input('defaults-timeout_connect', name='connect', placeholder="10", type="number") }}{{ lang.words.seconds2 }}
</td>
</tr>
<tr>
<td class="addName">Timeout client:</td>
<td class="addOption">
{{ input('defaults-timeout_client', name='client', placeholder="60", type="number") }}{{ lang.words.seconds2 }}
</td>
</tr>
<tr>
<td class="addName">Timeout server:</td>
<td class="addOption">
{{ input('defaults-timeout_server', name='server', placeholder="60", type="number") }}{{ lang.words.seconds2 }}
</td>
</tr>
<tr>
<td class="addName">Timeout check:</td>
<td class="addOption">
{{ input('defaults-timeout_check', name='check', placeholder="10", type="number") }}{{ lang.words.seconds2 }}
</td>
</tr>
<tr>
<td class="addName">Timeout http keep alive:</td>
<td class="addOption">
{{ input('defaults-timeout_http_keep_alive', name='http_keep_alive', placeholder="10", type="number") }}{{ lang.words.seconds2 }}
</td>
</tr>
<tr>
<td class="addName">{{ lang.add_page.buttons.set_options }}:</td>
<td class="addOption">
<textarea name="option" title="Options thru" cols=80 rows=5 placeholder="ssl-default-bind-options ssl-min-ver TLSv1.0 no-tls-tickets"></textarea>
</td>
</tr>
</table>
</form>

View File

@ -0,0 +1,184 @@
<form name="add-frontend" id="add-frontend" action="/add/haproxy/add" method="post">
<table>
<caption><h3>{{lang.words.add|title()}} {{lang.words.frontend}}</h3></caption>
<tr>
<td class="addName">{{lang.words.select|title()}} {{lang.words.w_a}} {{lang.words.server}}: </td>
<td class="addOption">
{{ select('serv2', name='server', values=g.user_params['servers'], is_servers='true', by_id=1) }}
<div class="tooltip tooltipTop"><b>{{lang.words.note|title()}}:</b> {{lang.phrases.master_slave}}</div>
</td>
<td rowspan="5" class="add-note addName alert-info">
{{lang.add_page.desc.front_desc1}}
<br /><br />
{{lang.add_page.desc.listener_desc2}}
<br /><br />
{{lang.add_page.desc.listener_desc1}}
</td>
</tr>
<tr>
<td class="addName">{{lang.words.name|title()}}:</td>
<td class="addOption">
<input type="text" name="name" id="new_frontend" required title="{{lang.words.name|title()}} {{lang.words.frontend}}" placeholder="web_80" class="form-control">
</td>
</tr>
<tr>
<td class="addName">IP and {{lang.words.port|title()}}:</td>
<td class="addOption">
<div id="frontend_bind">
<p>
<input type="text" name="ip" id="ip1" size="15" placeholder="Any" class="form-control"><b>:</b>
<input type="text" name="port" size="5" required title="{{lang.add_page.desc.port_for_bind}} {{lang.words.frontend}}" placeholder="8080" class="form-control">
</p>
</div>
<a class="link add-server" id="add_bind_frontend" title="{{lang.add_page.desc.bind_ip_pair}}"></a>
<div class="tooltip tooltipTop">
{{lang.add_page.desc.ip_port}}
</div>
</td>
</tr>
<tr class="advance">
<td class="addName">{{lang.words.mode|title()}}: </td>
<td class="addOption">
{% set values = dict() %}
{% set values = {'http':'http','tcp':'tcp'} %}
{{ select('frontend-mode-select', name='mode', values=values, selected='http', required='required', class='force_close') }}
<span id="https-frontend-span">
{{ checkbox('https-frontend', name='ssl', title=lang.add_page.desc.ssl_offloading, desc='SSL Offloading') }}
</span>
<div id="https-hide-frontend" style="display: none;">
<br /><span class="tooltip tooltipTop">{{lang.words.enter2|title()}} {{lang.words.name}} {{lang.words.of}} pem {{lang.words.file2}}, {{lang.add_page.desc.press_down}}:</span><br />
{{ input('path-cert-frontend', name="cert", placeholder="some_cert.pem", size='39') }}
</div>
</td>
</tr>
<tr class="advance">
<td class="addName">Maxconn: </td>
<td class="addOption">
{{ input('maxconn', value='2000', type="number", title=lang.add_page.desc.maxconn_fix, size='5', required='required') }}
<div class="tooltip tooltipTop">{{lang.add_page.desc.maxconn_desc}}: 2000</div>
</td>
</tr>
<tr class="advance">
<td class="addName">{{lang.words.headers|title()}}: </td>
<td class="addOption">
<span title="{{lang.words.add|title()}} {{lang.words.headers}}" id="add_frontend_header" class="link add-server"></span>
<div id="frontend_header_div" style="display: none;">
<p style="border-bottom: 1px solid #ddd; padding-bottom: 10px;" id="frontend_header_p">
{{ select('frontend_header_res_method', name='headers_res', values=header_res, first='------', class='force_close') }}
{{ select('frontend_header_method', name='headers_method', values=header_params, selected='add-header', class='force_close') }}
<b class="padding10">{{lang.words.name}}</b>
{{ input('frontend_header_name', name="header_name") }}
<b class="padding10">{{lang.words.value}}</b>
{{ input('frontend_header_value', name="header_value", placeholder='Leave blank if using del-header') }}
<span class="minus minus-style" onclick="deleteId('frontend_header_p')" title="{{lang.words.delete|title()}}"></span>
</p>
</div>
<span>
<a class="link add-server" id="frontend_add_header" title="{{lang.words.add|title()}} {{lang.words.headers}}" style="display: none;"></a>
</span>
</td>
</tr>
<tr class="advance">
<td class="addName" title="Access control list">ACL: </td>
<td class="addOption">
<span title="{{lang.words.add|title()}} ACL" id="add_frontend_acl" class="link add-server"></span>
<div id="frontend_acl" style="display: none;">
<p id="frontend_acl_rule" style="border-bottom: 1px solid #ddd; padding-bottom: 10px;">
<b class="padding10">{{lang.words.if|title()}}</b>
{{ select('frontend_acl_if', name='acl_if', values=if_values, first='Select if', class='force_close', disabled=false) }}
<b class="padding10">{{ lang.words.value }}</b>
{{ input('frontend_acl_value', name='acl_value') }}
<b class="padding10">{{ lang.words.then }}</b>
{% set values = dict() %}
{% set values = {'5':'Use backend','2':'Redirect to','3':'Allow','4':'Deny', '6': 'Return', '7': 'Set-header'} %}
{{ select('frontend_acl_then', name='acl_then', values=values, first='Select then', class='force_close', disabled=false) }}
<b class="padding10">{{ lang.words.value }}</b>
{{ input('frontend_acl_then_value', name='acl_then_value', title="Required if \'then\' is \'Use backend\' or \'Redirect\', \'Return\', or \'Set-header\'") }}
<span class="minus minus-style" onclick="deleteId('frontend_acl_rule')" title="{{lang.words.delete|title()}}"></span>
</p>
</div>
<span>
<a class="link add-server" id="frontend_add_acl" title="{{lang.words.add|title()}} ACL" style="cursor: pointer; display: none;"></a>
</span>
</td>
</tr>
<tr class="advance">
<td class="addName"><span title="Cache support start 1.8 and latter" class="help_cursor">Web {{lang.words.acceleration}}:</span></td>
<td class="addOption">
<span class="controlgroup">
{{ checkbox('compression2', name="compression", title=lang.add_page.desc.http_compression, value='1', desc=lang.words.compression|title()) }}
{{ checkbox('cache2', name="cache", title=lang.words.enable|title()+' '+lang.words.cache, value='2', desc=lang.words.cache|title()) }}
{{ checkbox('ssl_offloading1', name='ssl_offloading', title=lang.add_page.desc.http_https, desc='HTTP->HTTPS') }}
</span>
</td>
</tr>
<tr class="advance">
<td class="addName"><span title="Web application firewall" class="help_cursor">WAF:</span></td>
<td class="addOption">
<span class="controlgroup">
{{ checkbox('slow_attack1', title=lang.add_page.desc.slow_attack,
desc='Slow attack') }}
{{ checkbox('ddos1', title='DDOS attack protect', desc='DDOS') }}
{{ checkbox('frontend_whitelist_checkbox', name='whitelist_checkbox', title=lang.words.enable|title()+' '+ lang.words.whitelist, desc=lang.words.whitelist|title()) }}
{{ checkbox('frontend_blacklist_checkbox', name='blacklist_checkbox', title=lang.words.enable|title()+' '+ lang.words.blacklist, desc=lang.words.blacklist|title()) }}
{{ checkbox('waf2', name='waf', title='Web application firewall', desc='WAF', value='1') }}
{{ checkbox('antibot1', name='antibot', title=lang.add_page.desc.antibot, desc='Antibot', value='1') }}
</span>
<div id="frontend_blacklist-hide" style="display: none;">
<br /><span class="tooltip tooltipTop">{{lang.words.enter2|title()}} {{lang.words.w_a}} {{lang.words.blacklist}} {{lang.words.name}}, {{lang.add_page.desc.press_down}}:</span><br />
{{ input('frontend_blacklist-hide-input', size='39', name="blacklist", placeholder="blacklist.lst") }}
</div>
<div id="frontend_whitelist-hide" style="display: none;">
<br /><span class="tooltip tooltipTop">{{lang.words.enter2|title()}} {{lang.words.w_a}} {{lang.words.whitelist}} {{lang.words.name}}, {{lang.add_page.desc.press_down}}:</span><br />
{{ input('frontend_whitelist-hide-input', size='39', name="whitelist", placeholder="whitelist.lst") }}
</div>
</td>
</tr>
<tr class="advance">
<td class="addName">{{lang.words.options|title()}}:</td>
<td class="addOption">
<span class="controlgroup">
{{ checkbox('forward_for1', name='forward_for', title=lang.add_page.desc.forward_for, desc='Forward for') }}
{{ checkbox('options-frontend-show', title=lang.add_page.buttons.set_options_m, desc=lang.add_page.buttons.set_options) }}
</span>
<div id="options-frontend-show-div" style="display: none;">
<div style="font-size: 12px; padding-bottom: 10px;">
<span style="padding-right: 10px;">{{lang.words.start2|title()}} {{lang.words.typing}} {{lang.words.options}}: </span>
{{ input('options1') }}
<span style="padding-left: 10px;">
{{lang.add_page.desc.press_down}}. <a href="http://cbonte.github.io/haproxy-dconv/1.7/configuration.html" target="_blanck" style="color: #23527c" title="HAProxy docs">{{lang.words.read|title()}} {{lang.words.more}} {{lang.words.about}} {{lang.words.options}}</a>
</span>
<br />
<span style="padding-right: 10px;">{{lang.words.start2|title()}} {{lang.words.typing}} {{lang.words.saved}} {{lang.words.options}}:</span>
{{ input('saved-options1') }}
{{lang.add_page.desc.saved_options}}
</div>
<textarea name="option" title="Options thru" cols=80 rows=5 id="optionsInput1" placeholder="acl test hdr_beg(host) -i some_host"></textarea>
</div>
</td>
</tr>
<tr>
<td class="addName">{{lang.words.default_backend}}</td>
<td class="addOption">
{{ input('backends', name='backends', placeholder="some_backend", size='30', title=lang.add_page.desc.no_def_backend) }}
<div class="tooltip tooltipTop">
<b>{{lang.words.note|title()}}</b>: {{lang.add_page.desc.def_backend}}, <span title="{{lang.words.create|title()}} {{lang.words.backend}}" class="redirectBackend link">{{lang.add_page.desc.def_backend_exit}}</span>.
</div>
</td>
</tr>
<tr class="advance-show">
<td class="addOption" colspan="2">
<button title="{{lang.add_page.buttons.show_full_settings}}" class="row-down advance-show-button">{{lang.words.show|title()}} {{lang.words.advanced}} {{lang.words.settings}}</button>
<button title="{{lang.add_page.buttons.hide_full_settings}}" class="row-up advance-hide-button" style="display: none">{{lang.words.hide|title()}} {{lang.words.advanced}} {{lang.words.settings}}</button>
</td>
</tr>
<tr id="frontend-add-buttons">
<td class="addButton">
<a class="ui-button ui-widget ui-corner-all" id="frontend-add-button" title="{{lang.words.add|title()}} {{lang.words.frontend|title()}}" onclick="addProxy('add-frontend')">{{lang.words.add|title()}} {{lang.words.frontend|title()}}</a>
</td>
<td class="addButton">
<a class="ui-button ui-widget ui-corner-all" id="frontend-generate-button" title="{{lang.words.generate|title()}} {{lang.words.and}} {{lang.words.display}} {{lang.words.config}}" onclick="addProxy('add-frontend', 'true')">{{lang.words.generate|title()}} {{lang.words.config}}</a>
</td>
</tr>
</table>
</form>

View File

@ -0,0 +1,79 @@
<form name="add-global" id="add-global" action="/add/haproxy/global" method="post">
<table>
<tr>
{# <td class="addName">{{lang.words.select|title()}} {{lang.words.w_a}} {{lang.words.server}}: </td>#}
<td class="addOption">
{{ select('global_serv', name='server', values=g.user_params['servers'], is_servers='true', by_id=1) }}
<div class="tooltip tooltipTop"><b>{{lang.words.note|title()}}:</b> {{lang.phrases.master_slave}}</div>
</td>
</tr>
<tr>
{# <td class="addName">global {{lang.words.name}}:</td>#}
<td class="addOption">
{{ input('global-name', name='name', title="global "+lang.words.name, value="name") }}
</td>
</tr>
<tr>
<td class="addName">{{lang.words.log|title()}}:</td>
<td class="addOption">
<textarea name="log" cols=60 rows=3></textarea>
<div class="tooltip tooltipTop">Adds a global syslog server. Several global servers can be defined. They will receive logs for starts and exits</div>
</td>
</tr>
<tr>
<td class="addName">Chroot:</td>
<td class="addOption">
{{ input('global-chroot', name='chroot', placeholder="/var/lib/haproxy") }}
</td>
</tr>
<tr>
<td class="addName">Pidfile:</td>
<td class="addOption">
{{ input('global-pidfile', name='pidfile', placeholder="/var/run/haproxy.pid") }}
</td>
</tr>
<tr>
<td class="addName">Maxconn:</td>
<td class="addOption">
{{ input('global-maxconn', name='maxconn', placeholder="5000", type="number") }}
<div class="tooltip tooltipTop">Sets the maximum per-process number of concurrent connections to number.</div>
</td>
</tr>
<tr>
<td class="addName">{{lang.words.user|title()}}:</td>
<td class="addOption">
{{ input('global-user', name='user', placeholder="haproxy") }}
</td>
</tr>
<tr>
<td class="addName">{{lang.words.group|title()}}:</td>
<td class="addOption">
{{ input('global-group', name='group', placeholder="haproxy") }}
</td>
</tr>
<tr>
<td class="addName">Stats socket:</td>
<td class="addOption">
<textarea name="socket" title="Binds a UNIX socket" cols=60 rows=4 placeholder="*:1999 level admin"></textarea>
<div class="tooltip tooltipTop">
Binds a UNIX socket to path or a TCPv4/v6 address to address:port.
Connections to this socket will return various statistics outputs and even allow some commands to
be issued to change some runtime settings.
</div>
</td>
</tr>
<tr>
<td class="addName">Daemon:</td>
<td class="addOption">
{{ checkbox('global-daemon', name='daemon') }}
<div class="tooltip tooltipTop">Makes the process fork into background. This is the recommended mode of operation.</div>
</td>
</tr>
<tr>
<td class="addName">{{ lang.add_page.buttons.set_options }}:</td>
<td class="addOption">
<textarea name="option" title="Options thru" cols=80 rows=5 placeholder="ssl-default-bind-options ssl-min-ver TLSv1.0 no-tls-tickets"></textarea>
</td>
</tr>
</table>
</form>

View File

@ -0,0 +1,254 @@
<form name="add-listen" id="add-listen" action="/add/haproxy/add" method="post">
<table>
<caption><h3>{{lang.words.add|title()}} {{lang.words.listener}}</h3></caption>
<tr>
<td class="addName">{{lang.words.select|title()}} {{lang.words.w_a}} {{lang.words.server}}: </td>
<td class="addOption">
{{ select('serv', name='server', values=g.user_params['servers'], is_servers='true', by_id=1) }}
<div class="tooltip tooltipTop"><b>{{lang.words.note|title()}}:</b> {{lang.phrases.master_slave}}</div>
</td>
<td rowspan="5" class="add-note addName alert-info">
{{lang.add_page.desc.listener_desc1}}
<br /><br />
{{lang.add_page.desc.listener_desc2}}
<br /><br />
{{lang.add_page.desc.listener_desc3}}
</td>
</tr>
<tr>
<td class="addName">{{lang.words.name|title()}}:</td>
<td class="addOption">
{{ input('listener', name='name', title=lang.words.name|title() + ' ' +lang.words.listener, placeholder="web_80", required='required') }}
</td>
</tr>
<tr>
<td class="addName">IP and {{lang.words.port|title()}}:</td>
<td class="addOption">
<div id="listen_bind">
<p>
{{ input('ip', placeholder="Any", size='15') }}<b>:</b>
{{ input('listen-port', name='port', title=lang.add_page.desc.port_for_bind + ' ' + lang.words.listener, placeholder="8080", size='5', required='required') }}
</p>
</div>
<a class="link add-server" id="add_bind_listen" title="{{lang.add_page.desc.bind_ip_pair}}"></a>
<div class="tooltip tooltipTop">
{{lang.add_page.desc.ip_port}}
</div>
</td>
</tr>
<tr class="advance">
<td class="addName">{{lang.words.mode|title()}}: </td>
<td class="addOption">
{% set values = dict() %}
{% set values = {'http':'http','tcp':'tcp'} %}
{{ select('listen-mode-select', name='mode', values=values, selected='http', required='required', class='force_close') }}
<span id="https-listen-span">
<label for="https-listen" style="margin-top: 5px;" title="{{lang.words.enable|title()}} SSL Offloading" data-help="{{lang.add_page.desc.ssl_offloading}}">SSL Offloading</label>
<input type="checkbox" id="https-listen" name="ssl" value="https" >
</span>
<div id="https-hide-listen" style="display: none;">
<br /><span class="tooltip tooltipTop">{{lang.words.enter2|title()}} {{lang.words.name}} {{lang.words.of}} pem {{lang.words.file2}}, {{lang.add_page.desc.press_down}}:</span><br />
{{ input('path-cert-listen', name="cert", placeholder="some_cert.pem", size='39') }}<br />
<label for="ssl-check-listen" style="margin-top: 5px;">{{lang.add_page.buttons.disable_ssl_verify}}</label><input type="checkbox" id="ssl-check-listen" name="ssl-check" value="ssl-check" checked>
</div>
</td>
</tr>
<tr class="advance">
<td class="addName">Maxconn: </td>
<td class="addOption">
{{ input('listen_maxconn', name='maxconn', value='2000', type="number", title=lang.add_page.desc.maxconn_fix, size='5', required='required') }}
<div class="tooltip tooltipTop">{{lang.add_page.desc.maxconn_desc}}: 2000</div>
</td>
</tr>
<tr class="advance">
<td class="addName">{{lang.words.balance|title()}}: </td>
<td class="addOption">
{{ select('balance', values=balance_params, selected='roundrobin', required='required', class='force_close') }}
</td>
</tr>
<tr class="advance">
<td class="addName">{{lang.words.health|title()}} {{lang.words.check}}: </td>
<td class="addOption">
{{ select('listen_checks', name='health_check', values=checks, selected='', class='force_close') }}
<span id="listen_checks_note" class="tooltip tooltipTop"></span>
<br />
<span id="listen_checks_http" style="display: none;">
URI path for checking: {{ input('listen_checks_http_path', name='checks_http_path', value='/', title="URI for checking e.g. /check") }}
Domain name: {{ input('listen_checks_http_domain', name='checks_http_domain', placeholder='domain.com', title="Domain name for checking e.g. domain.com") }}
</span>
</td>
</tr>
<tr class="advance">
<td class="addName">{{lang.words.headers|title()}}: </td>
<td class="addOption">
<span title="{{lang.words.add|title()}} {{lang.words.headers}}" id="add_listen_header" class="link add-server"></span>
<div id="listen_header_div" style="display: none;">
<p style="border-bottom: 1px solid #ddd; padding-bottom: 10px;" id="listen_header_p">
{{ select('listen_header_res_method', name='headers_res', values=header_res, first='------', class='force_close') }}
{{ select('listen_header_method', name='headers_method', values=header_params, selected='add-header', class='force_close') }}
<b class="padding10">{{lang.words.name}}</b>
{{ input('listen_header_name', name="header_name") }}
<b class="padding10">{{lang.words.value}}</b>
{{ input('listen_header_value', name="header_value", placeholder='Leave blank if using del-header') }}
<span class="minus minus-style" onclick="deleteId('listen_header_p')" title="{{lang.words.delete|title()}}"></span>
</p>
</div>
<span>
<a class="link add-server" id="listen_add_header" title="{{lang.words.add|title()}} {{lang.words.headers}}" style="display: none;"></a>
</span>
</td>
</tr>
<tr class="advance">
<td class="addName" title="Access control list">ACL: </td>
<td class="addOption">
<span title="{{lang.words.add|title()}} ACL" id="add_listen_acl" class="link add-server"></span>
<div id="listen_acl" style="display: none;">
<p style="border-bottom: 1px solid #ddd; padding-bottom: 10px;" id="listen_acl_rule">
<b class="padding10">{{lang.words.if|title()}}</b>
{{ select('listen_acl_if', name='acl_if', values=if_values, first='Select if', class='force_close', disabled=false) }}
<b class="padding10">{{lang.words.value}}</b>
{{ input('listen_acl_value', name="acl_value") }}
<b class="padding10">{{lang.words.then}}</b>
{% set values = dict() %}
{% set values = {'2':'Redirect to','3':'Allow','4':'Deny', '6': 'Return', '7': 'Set-header'} %}
{{ select('listen_acl_then', name='acl_then', values=values, first='Select then', class='force_close', disabled=false) }}
<b class="padding10">{{lang.words.value}}</b>
{{ input('listen_acl_then_value', name='acl_then_value', title="Required if \'then\' is \'Use backend\' or \'Redirect\', \'Return\', or \'Set-header\'") }}
<span class="minus minus-style" onclick="deleteId('listen_acl_rule')" title="{{lang.words.delete|title()}} {{lang.words.this}} ACL"></span>
</p>
</div>
<span>
<a class="link add-server" id="listen_add_acl" title="{{lang.words.add|title()}} ACL" style="cursor: pointer; display: none;"></a>
</span>
</td>
</tr>
<tr class="advance">
<td class="addName"><span title="Cache support start 1.8 and latter" class="help_cursor">Web {{lang.words.acceleration}}:</span></td>
<td class="addOption">
<span class="controlgroup">
{{ checkbox('compression', title=lang.add_page.desc.http_compression, value='1', desc=lang.words.compression|title()) }}
{{ checkbox('cache', title=lang.words.enable|title()+' '+lang.words.cache, value='2', desc=lang.words.cache|title()) }}
{{ checkbox('ssl_offloading', title=lang.add_page.desc.http_https, desc='HTTP->HTTPS') }}
</span>
</td>
</tr>
<tr class="advance">
<td class="addName"><span title="Web application firewall" class="help_cursor">WAF:</span></td>
<td class="addOption">
<span class="controlgroup">
{{ checkbox('slow_attack', title=lang.add_page.desc.slow_attack,
desc='Slow attack') }}
{{ checkbox('ddos', title='DDOS attack protect', desc='DDOS') }}
{{ checkbox('listen_whitelist_checkbox', name='whitelist_checkbox', title=lang.words.enable|title()+' '+ lang.words.whitelist, desc=lang.words.whitelist|title()) }}
{{ checkbox('listen_blacklist_checkbox', name='blacklist_checkbox', title=lang.words.enable|title()+' '+ lang.words.blacklist, desc=lang.words.blacklist|title()) }}
{{ checkbox('waf', title='Web application firewall', desc='WAF', value='1') }}
{{ checkbox('antibot', title=lang.add_page.desc.antibot, desc='Antibot', value='1') }}
</span>
<div id="listen_blacklist-hide" style="display: none;">
<br /><span class="tooltip tooltipTop">{{lang.words.enter2|title()}} {{lang.words.w_a}} {{lang.words.blacklist}} {{lang.words.name}}, {{lang.add_page.desc.press_down}}:</span><br />
{{ input('listen_blacklist-hide-input', size='39', name="blacklist", placeholder="blacklist.lst") }}
</div>
<div id="listen_whitelist-hide" style="display: none;">
<br /><span class="tooltip tooltipTop">{{lang.words.enter2|title()}} {{lang.words.w_a}} {{lang.words.whitelist}} {{lang.words.name}}, {{lang.add_page.desc.press_down}}:</span><br />
{{ input('listen_whitelist-hide-input', size='39', name="whitelist", placeholder="whitelist.lst") }}
</div>
</td>
</tr>
<tr class="advance">
<td class="addName">{{lang.words.options|title()}}:</td>
<td class="addOption">
<span class="controlgroup">
{{ checkbox('forward_for', title=lang.add_page.desc.forward_for, desc='Forward for') }}
{{ checkbox('redispatch', title=lang.add_page.desc.redispatch, desc='Redispatch') }}
{{ checkbox('listen_cookie', name='cookie', title=lang.add_page.desc.cookie, desc=lang.words.set|title()+' cookie', value='1') }}
{{ checkbox('options-listen-show', title=lang.add_page.buttons.set_options_m, desc=lang.add_page.buttons.set_options) }}
</span>
<br>
<span id="listen_cookie_div" style="display: none;">
<input type="text" placeholder="name" name="cookie_name" id="listen_cookie_name" class="form-control"><br><br>
<input type="text" placeholder="domain" name="cookie_domain" class="form-control"><br><br>
<span class="controlgroup">
{% set values = dict() %}
{% set values = {'None':'None','rewrite':'rewrite','indirect':'indirect','insert':'insert'} %}
{{ select('listen_rewrite', name='rewrite', values=values, first='rewrite/indirect/insert', class='force_close') }}
{{ checkbox('listen_prefix', name='prefix', title=lang.add_page.desc.c_prefix, desc='prefix', value='prefix') }}
{{ checkbox('listen_nocache', name='nocache', title=lang.add_page.desc.c_nocache, desc='nocache', value='nocache') }}
{{ checkbox('listen_postonly', name='postonly', title=lang.add_page.desc.c_postonly, desc='postonly', value='postonly') }}
{{ checkbox('listen_dynamic', name='dynamic', title=lang.add_page.desc.c_dynamic, desc='dynamic', value='dynamic') }}
<span id="listen_dynamic_div" style="display: none;">
dynamic-cookie-key: {{ input('listen_dynamic-cookie-key', name='dynamic-cookie-key', placeholder="your-custom-key") }}
</span>
</span>
</span>
<div id="options-listen-show-div" style="display: none;">
<div class="tooltip">
<span style="padding-right: 10px;">{{lang.words.start2|title()}} {{lang.words.typing}} {{lang.words.options}}: </span>
{{ input('options') }}
<span style="padding-left: 10px;">
{{lang.add_page.desc.press_down}}. <a href="http://cbonte.github.io/haproxy-dconv/1.7/configuration.html" target="_blanck" style="color: #23527c" title="HAProxy docs">{{lang.words.read|title()}} {{lang.words.more}} {{lang.words.about}} {{lang.words.options}}</a>
</span>
<br />
<span style="padding-right: 10px;">{{lang.words.start2|title()}} {{lang.words.typing}} {{lang.words.saved}} {{lang.words.options}}: </span>
{{ input('saved-options') }}
{{lang.add_page.desc.saved_options}}
</div>
<textarea name="option" title="Options thru" id="optionsInput" cols=80 rows=5 placeholder="acl test hdr_beg(host) -i some_host"></textarea>
</div>
</td>
</tr>
<tr>
<td class="addName">{{lang.words.servers|title()}}:</td>
<td class="addOption">
{% include 'include/add/add_servers.html' %}
<br>
<br>
{{ checkbox('template-listen', name='template', title=lang.add_page.desc.server_template, value='template', desc=lang.add_page.desc.server_template) }}
</td>
</tr>
<tr class="advance">
<td class="addName">{{lang.words.check|title()}}:</td>
<td class="addOption">
<div>
<label for="controlgroup-listen-show" style="margin-top: 5px;" title="Set custom check parameters">{{lang.words.custom|title()}} {{lang.words.check}} {{lang.words.params}}</label>
<input type="checkbox" id="controlgroup-listen-show" name="default-check" value="1">
<span class="tooltip tooltipTop">{{lang.add_page.desc.def_check}}: inter 2000 rise 2 fall 5</span>
</div>
<div class="controlgroup" id="controlgroup-listen" style="display: none;">
<label for="check-servers-listen" title="Ebable servers check">{{lang.words.check|title()}}</label>
<input type="checkbox" id="check-servers-listen" name="check-servers" checked value="1">
{% set values = {'1000':'1000','2000':'2000','3000':'3000'} %}
{{ select('inter-listen', name='inter', values=values, first='1000', class='force_close') }}
{% set values = {'1':'1','2':'2','3':'3'} %}
{{ select('rise-listen', name='rise', values=values, first='2', class='force_close') }}
{% set values = {'4':'4','5':'5','6':'6'} %}
{{ select('fall-listen', name='fall', values=values, first='5', class='force_close') }}
</div>
<div style="display: block">
{{ checkbox('listen_circuit_breaking', name="circuit_breaking", desc='Circuit Breaking', title=lang.add_page.desc.circuit_breaking, value='1') }}
</div>
<div id="listen_circuit_breaking_div" style="display: none">
Observe:
{{ select('listen_circuit_breaking_observe', name='circuit_breaking_observe', values=observe, class='force_close') }}
error-limit: {{ input('listen_circuit_breaking_error_limit', name='circuit_breaking_error_limit', type='number', value='50', style='width: 50px;') }}
on-error:
{{ select('listen_circuit_breaking_on_error', name='circuit_breaking_on_error', values=on_error, class='force_close') }}
<div class="tooltip tooltipTop">{{lang.words.read|title()}} {{lang.words.more}} {{lang.words.about}} Circuit Breaking <a href="https://roxy-wi.org/description/circuit-breaking" title="Circuit Breaking" target="_blank">{{lang.words.here}}</a></div>
</div>
</td>
</tr>
<tr class="advance-show">
<td class="addOption" colspan="2">
<button title="{{lang.add_page.buttons.show_full_settings}}" class="row-down advance-show-button">{{lang.words.show|title()}} {{lang.words.advanced}} {{lang.words.settings}}</button>
<button title="{{lang.add_page.buttons.hide_full_settings}}" class="row-up advance-hide-button" style="display: none">{{lang.words.hide|title()}} {{lang.words.advanced}} {{lang.words.settings}}</button>
</td>
</tr>
<tr id="listen-add-buttons">
<td class="addButton">
<a class="ui-button ui-widget ui-corner-all" id="listen-add-button" title="{{lang.words.add|title()}} {{lang.words.listener|title()}}" onclick="addProxy('add-listen')">{{lang.words.add|title()}} {{lang.words.linstener|title()}}</a>
</td>
<td class="addButton">
<a class="ui-button ui-widget ui-corner-all" id="listen-generate-button" title="{{lang.words.generate|title()}} {{lang.words.and}} {{lang.words.display}} {{lang.words.config}}" onclick="addProxy('add-listen', 'true')">{{lang.words.generate|title()}} {{lang.words.config}}</a>
</td>
</tr>
</table>
</form>

View File

@ -0,0 +1,49 @@
<form name="add-peers" id="add-peers" action="/add/haproxy/peers" method="post">
<table>
<caption><h3>{{lang.words.add|title()}} Peer</h3></caption>
<tr>
<td class="addName">{{lang.words.select|title()}} {{lang.words.w_a}} {{lang.words.server}}: </td>
<td class="addOption">
{{ select('peers_serv', name='server', values=g.user_params['servers'], is_servers='true', by_id=1) }}
<div class="tooltip tooltipTop"><b>{{lang.words.note|title()}}:</b> {{lang.phrases.master_slave}}</div>
</td>
<td rowspan="4" class="add-note addName alert-info">
{{lang.add_page.desc.peers}}
</td>
</tr>
<tr>
<td class="addName">Peers {{lang.words.name}}:</td>
<td class="addOption">
{{ input('peers-name', name='name', title="Peers "+lang.words.name, placeholder="peers name") }}
</td>
</tr>
<tr>
<td class="addName">Peers {{lang.words.servers}}:</td>
<td class="addOption">
<span name="add_peers" id="add_peers">
<p>
<input name="servers_name" required title="Peer {{lang.words.name}}" size=14 placeholder="haproxy1" class="form-control">:
<input name="servers" required title="Peer IP" size=14 placeholder="xxx.xxx.xxx.xxx" class="form-control">:
<input name="server_port" required title="Peer {{lang.words.port}}" size=3 placeholder="yyy" class="form-control add_server_number" type="number">
</p>
<p>
<input name="servers_name" required title="Peer {{lang.words.name}}" size=14 placeholder="haproxy2" class="form-control">:
<input name="servers" title="Peer IP" size=14 placeholder="xxx.xxx.xxx.xxx" class="form-control second-server"><span class="second-server">:</span>
<input name="server_port" title="Peer {{lang.words.port}}" size=3 placeholder="yyy" class="form-control second-server add_server_number" type="number">
</p>
</span>
<span>
<a class="link add-server backend_server" name="add-peer-input" title="{{lang.words.add|title()}} peer {{lang.words.server}}" style="cursor: pointer;"></a>
</span>
</td>
</tr>
<tr id="peers-add-buttons">
<td class="addButton">
<a class="ui-button ui-widget ui-corner-all" id="peers-add-button" title="{{lang.words.add|title()}} peer" onclick="addProxy('add-peers')">{{lang.words.add|title()}} peer</a>
</td>
<td class="addButton">
<a class="ui-button ui-widget ui-corner-all" id="peers-generate-button" title="{{lang.words.generate|title()}} {{lang.words.and}} {{lang.words.display}} {{lang.words.config}}" onclick="addProxy('add-peers', 'true')">{{lang.words.generate|title()}} {{lang.words.config}}</a>
</td>
</tr>
</table>
</form>

View File

@ -0,0 +1,57 @@
<form name="add-userlist" id="add-userlist" action="/add/haproxy/userlist" method="post">
<table>
<caption><h3>{{lang.words.add|title()}} {{lang.words.userlists}}</h3></caption>
<tr>
<td class="addName">{{lang.words.select|title()}} {{lang.words.w_a}} {{lang.words.server}}: </td>
<td class="addOption">
{{ select('userlist_serv', name='server', values=g.user_params['servers'], is_servers='true') }}
<div class="tooltip tooltipTop"><b>{{lang.words.note|title()}}:</b> {{lang.phrases.master_slave}}</div>
</td>
<td rowspan="4" class="add-note addName alert-info">{{lang.add_page.desc.userlist_desc}}</td>
</tr>
<tr>
<td class="addName">{{lang.words.name|title()}}:</td>
<td class="addOption">
{{ input('new_userlist', name='name', required='required', title=lang.add_page.desc.userlist_name, placeholder="basic-auth-list") }}
</td>
</tr>
<tr>
<td class="addName">{{lang.words.groups|title()}}:</td>
<td class="addOption">
<span id="userlist-groups">
<p>
{{ input('userlist-group', title=lang.add_page.desc.userlist_user_grp, placeholder="group_name") }}
</p>
</span>
<span>
<span class="link add-server" id="add-userlist-group" title="Add extra group" style="cursor: pointer;"></span>
</span>
<div class="tooltip tooltipTop">{{lang.add_page.desc.userlist_group}}</div>
</td>
</tr>
<tr>
<td class="addName">{{lang.words.user|title()}}:</td>
<td class="addOption">
<span id="userlist-users">
<p>
{{ input('userlist-user', required='required', title=lang.words.username|title(), placeholder="user_name") }}
{{ input('userlist-password', required='required', title=lang.add_page.desc.userlist_pass, placeholder="password") }}
{{ input('userlist-user-group', title=lang.add_page.desc.userlist_user_grp, placeholder="group") }}
</p>
</span>
<span>
<span class="link add-server" id="add-userlist-user" title="Add extra user" style="cursor: pointer;"></span>
</span>
<div class="tooltip tooltipTop">{{lang.add_page.desc.userlist_user}}</div>
</td>
</tr>
<tr id="userlist-add-buttons">
<td class="addButton">
<a class="ui-button ui-widget ui-corner-all" id="userlist-add-button" title="Add Userlist" onclick="addProxy('add-userlist')">{{lang.words.add|title()}} {{lang.words.userlist|title()}}</a>
</td>
<td class="addButton">
<a class="ui-button ui-widget ui-corner-all" id="userlist-generate-button" title="{{lang.words.generate|title()}} {{lang.words.and}} {{lang.words.display}} {{lang.words.config}}" onclick="addProxy('add-userlist', 'true')">{{lang.words.generate|title()}} {{lang.words.config}}</a>
</td>
</tr>
</table>
</form>

View File

@ -35,7 +35,7 @@
<link href="{{ url_for('static', filename='css/nprogress.css') }}" rel="stylesheet"> <link href="{{ url_for('static', filename='css/nprogress.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/jquery-ui.min.css') }}" rel="stylesheet"> <link href="{{ url_for('static', filename='css/jquery-ui.min.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/jquery-ui.structure.min.css') }}" rel="stylesheet"> <link href="{{ url_for('static', filename='css/jquery-ui.structure.min.css') }}" rel="stylesheet">
<script src="/static/js/jquery-3.6.0.min.js"></script> <script src="/static/js/jquery-3.7.1.min.js"></script>
<script src="/static/js/jquery-ui.min.js"></script> <script src="/static/js/jquery-ui.min.js"></script>
<script src="/static/js/js.cookie.min.js"></script> <script src="/static/js/js.cookie.min.js"></script>
<script src="/static/js/reconnecting-websocket.js"></script> <script src="/static/js/reconnecting-websocket.js"></script>

View File

@ -951,6 +951,7 @@
"listeners": "listeners", "listeners": "listeners",
"weight": "weight", "weight": "weight",
"where": "where", "where": "where",
"shared": "shared" "shared": "shared",
"retries": "retries",
} }
%} %}

View File

@ -951,6 +951,7 @@
"listeners": "les auditeurs", "listeners": "les auditeurs",
"weight": "poids", "weight": "poids",
"where": "où", "where": "où",
"shared": "commun" "shared": "commun",
"retries": "tentatives",
} }
%} %}

View File

@ -951,6 +951,7 @@
"listeners": "ouvintes", "listeners": "ouvintes",
"weight": "peso", "weight": "peso",
"where": "onde", "where": "onde",
"shared": "partilhado" "shared": "partilhado",
"retries": "novas tentativas",
} }
%} %}

View File

@ -951,6 +951,7 @@
"listeners": "слушатели", "listeners": "слушатели",
"weight": "вес", "weight": "вес",
"where": "где", "where": "где",
"shared": "общий" "shared": "общий",
"retries": "повторные попытки",
} }
%} %}

View File

@ -11,6 +11,70 @@ import app.modules.service.installation as service_mod
from app.middleware import get_user_params, check_services, page_for_admin, check_group from app.middleware import get_user_params, check_services, page_for_admin, check_group
from app.modules.common.common_classes import SupportClass from app.modules.common.common_classes import SupportClass
from app.modules.roxywi.class_models import BaseResponse, ServiceInstall from app.modules.roxywi.class_models import BaseResponse, ServiceInstall
from app.views.service.views import ServiceView
class InstallGetStatus(ServiceView):
methods = ['GET']
decorators = [jwt_required(), get_user_params(), check_services, page_for_admin(level=3), check_group()]
def get(self, service: Literal['haproxy', 'nginx', 'apache', 'keepalived'], server_id: Union[int, str]):
"""
This endpoint retrieves information about a specific service.
---
tags:
- Service Installation
parameters:
- in: path
name: service
type: 'string'
required: true
description: The type of service (haproxy, nginx, apache, keepalived)
- in: path
name: server_id
type: 'integer'
required: true
description: The ID or IP of the server
responses:
200:
description: Successful operation
schema:
type: 'object'
properties:
CurrConns:
type: 'string'
description: 'Current connections to HAProxy (only for HAProxy service)'
Maxconn:
type: 'string'
description: 'Maximum connections to HAProxy (only for HAProxy service)'
MaxconnReached:
type: 'string'
description: 'Max connections reached (only for HAProxy service)'
Memmax_MB:
type: 'string'
description: 'Maximum memory in MB (only for HAProxy service)'
PoolAlloc_MB:
type: 'string'
description: 'Memory pool allocated in MB (only for HAProxy service)'
PoolUsed_MB:
type: 'string'
description: 'Memory pool used in MB (only for HAProxy service)'
Uptime:
type: 'string'
description: 'Time the service has been active'
Version:
type: 'string'
description: 'Version of the service'
Process:
type: 'string'
description: 'Number of processes launched by the service'
Status:
type: 'string'
description: 'Status of the service'
default:
description: Unexpected error
"""
return super().get(service=service, server_id=server_id)
class InstallView(MethodView): class InstallView(MethodView):

View File

@ -597,7 +597,8 @@ class ServerIPView(MethodView):
methods = ["GET"] methods = ["GET"]
decorators = [jwt_required(), get_user_params(), page_for_admin(level=3), check_group()] decorators = [jwt_required(), get_user_params(), page_for_admin(level=3), check_group()]
def get(self, server_id: int): @staticmethod
def get(server_id: int):
""" """
Retrieves IPs associated with a certain server. Retrieves IPs associated with a certain server.
--- ---

File diff suppressed because it is too large Load Diff