chg: [ail sync] add sync api (ping, version) + UI/client error handler

This commit is contained in:
Terrtia 2021-11-26 16:13:46 +01:00
parent 4f052673a7
commit 658cb73d4e
No known key found for this signature in database
GPG key ID: 1E1B1F50D84613D0
7 changed files with 363 additions and 54 deletions

View file

@ -9,7 +9,10 @@ import sys
import time import time
import uuid import uuid
import subprocess
from flask import escape from flask import escape
from pubsublogger import publisher
sys.path.append(os.path.join(os.environ['AIL_BIN'], 'lib/')) sys.path.append(os.path.join(os.environ['AIL_BIN'], 'lib/'))
import ConfigLoader import ConfigLoader
@ -27,6 +30,12 @@ r_serv_db = config_loader.get_redis_conn("ARDB_DB")
r_serv_sync = config_loader.get_redis_conn("ARDB_DB") r_serv_sync = config_loader.get_redis_conn("ARDB_DB")
config_loader = None config_loader = None
#### LOGS ####
# redis_logger = publisher
# redis_logger.port = 6380
# redis_logger.channel = 'AIL_SYNC'
##-- LOGS --##
def is_valid_uuid_v4(UUID): def is_valid_uuid_v4(UUID):
if not UUID: if not UUID:
return False return False
@ -50,6 +59,9 @@ def generate_sync_api_key():
def get_ail_uuid(): def get_ail_uuid():
return r_serv_db.get('ail:uuid') return r_serv_db.get('ail:uuid')
def get_sync_server_version():
return '0.1'
def is_valid_websocket_url(websocket_url): def is_valid_websocket_url(websocket_url):
regex_websocket_url = r'^(wss:\/\/)([0-9]{1,3}(?:\.[0-9]{1,3}){3}|(?=[^\/]{1,254}(?![^\/]))(?:(?=[a-zA-Z0-9-]{1,63}\.?)(?:xn--+)?[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*\.?)+[a-zA-Z]{2,63}):([0-9]{1,5})$' regex_websocket_url = r'^(wss:\/\/)([0-9]{1,3}(?:\.[0-9]{1,3}){3}|(?=[^\/]{1,254}(?![^\/]))(?:(?=[a-zA-Z0-9-]{1,63}\.?)(?:xn--+)?[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*\.?)+[a-zA-Z]{2,63}):([0-9]{1,5})$'
if re.match(regex_websocket_url, websocket_url): if re.match(regex_websocket_url, websocket_url):
@ -134,9 +146,6 @@ def refresh_ail_instance_connection(ail_uuid):
client_id = get_client_id_by_ail_uuid(ail_uuid) client_id = get_client_id_by_ail_uuid(ail_uuid)
launch_required = is_ail_instance_push_enabled(ail_uuid) launch_required = is_ail_instance_push_enabled(ail_uuid)
print(client_id)
print(launch_required)
# relaunch # relaunch
if client_id and launch_required: if client_id and launch_required:
send_command_to_manager('relaunch', client_id=client_id) send_command_to_manager('relaunch', client_id=client_id)
@ -193,7 +202,7 @@ class AIL2AILClientManager(object):
def launch_sync_client(self, ail_uuid): def launch_sync_client(self, ail_uuid):
dir_project = os.environ['AIL_HOME'] dir_project = os.environ['AIL_HOME']
client_id = self.get_new_sync_client_id() client_id = self.get_new_sync_client_id()
script_options = f'-a {ail_uuid} -m push -i {client_id}' script_options = f'-u {ail_uuid} -m push -i {client_id}'
screen.create_screen(AIL2AILClientManager.SCREEN_NAME) screen.create_screen(AIL2AILClientManager.SCREEN_NAME)
screen.launch_uniq_windows_script(AIL2AILClientManager.SCREEN_NAME, screen.launch_uniq_windows_script(AIL2AILClientManager.SCREEN_NAME,
client_id, dir_project, client_id, dir_project,
@ -226,14 +235,11 @@ class AIL2AILClientManager(object):
def get_manager_command(self): def get_manager_command(self):
res = r_cache.spop('ail_2_ail:client_manager:command') res = r_cache.spop('ail_2_ail:client_manager:command')
if res: if res:
print(res)
print(type(res))
return json.loads(res) return json.loads(res)
else: else:
return None return None
def execute_manager_command(self, command_dict): def execute_manager_command(self, command_dict):
print(command_dict)
command = command_dict.get('command') command = command_dict.get('command')
if command == 'launch': if command == 'launch':
ail_uuid = command_dict.get('ail_uuid') ail_uuid = command_dict.get('ail_uuid')
@ -357,6 +363,16 @@ def change_pull_push_state(ail_uuid, pull=False, push=False):
set_last_updated_sync_config() set_last_updated_sync_config()
refresh_ail_instance_connection(ail_uuid) refresh_ail_instance_connection(ail_uuid)
def get_ail_server_version(ail_uuid):
return r_serv_sync.hget(f'ail:instance:{ail_uuid}', 'version')
def get_ail_server_ping(ail_uuid):
res = r_serv_sync.hget(f'ail:instance:{ail_uuid}', 'ping')
return res == 'True'
def get_ail_server_error(ail_uuid):
return r_cache.hget(f'ail_2_ail:all_servers:metadata:{ail_uuid}', 'error')
# # TODO: HIDE ADD GLOBAL FILTER (ON BOTH SIDE) # # TODO: HIDE ADD GLOBAL FILTER (ON BOTH SIDE)
def get_ail_instance_metadata(ail_uuid, sync_queues=False): def get_ail_instance_metadata(ail_uuid, sync_queues=False):
dict_meta = {} dict_meta = {}
@ -365,6 +381,9 @@ def get_ail_instance_metadata(ail_uuid, sync_queues=False):
dict_meta['description'] = get_ail_instance_description(ail_uuid) dict_meta['description'] = get_ail_instance_description(ail_uuid)
dict_meta['pull'] = is_ail_instance_pull_enabled(ail_uuid) dict_meta['pull'] = is_ail_instance_pull_enabled(ail_uuid)
dict_meta['push'] = is_ail_instance_pull_enabled(ail_uuid) dict_meta['push'] = is_ail_instance_pull_enabled(ail_uuid)
dict_meta['ping'] = get_ail_server_ping(ail_uuid)
dict_meta['version'] = get_ail_server_version(ail_uuid)
dict_meta['error'] = get_ail_server_error(ail_uuid)
# # TODO: HIDE # # TODO: HIDE
dict_meta['api_key'] = get_ail_instance_key(ail_uuid) dict_meta['api_key'] = get_ail_instance_key(ail_uuid)
@ -421,8 +440,105 @@ def delete_ail_instance(ail_uuid):
refresh_ail_instance_connection(ail_uuid) refresh_ail_instance_connection(ail_uuid)
return ail_uuid return ail_uuid
## WEBSOCKET API - ERRORS ##
def set_ail_server_version(ail_uuid, version):
r_serv_sync.hset(f'ail:instance:{ail_uuid}', 'version', version)
def set_ail_server_ping(ail_uuid, pong):
r_serv_sync.hset(f'ail:instance:{ail_uuid}', 'ping', bool(pong))
def save_ail_server_error(ail_uuid, error_message):
r_cache.hset(f'ail_2_ail:all_servers:metadata:{ail_uuid}', 'error', error_message)
def clear_save_ail_server_error(ail_uuid):
r_cache.hdel(f'ail_2_ail:all_servers:metadata:{ail_uuid}', 'error')
def _get_remote_ail_server_response(ail_uuid, api_request):
websocket_client = os.path.join(os.environ['AIL_BIN'], 'core', 'ail_2_ail_client.py')
l_command = ['python', websocket_client, '-u', ail_uuid, '-m', 'api', '-a', api_request]
process = subprocess.Popen(l_command, stdout=subprocess.PIPE)
while process.poll() is None:
time.sleep(1)
if process.returncode == 0:
# Scrapy-Splash ERRORS
if process.stderr:
stderr = process.stderr.read().decode()
if stderr:
print(f'stderr: {stderr}')
if process.stdout:
output = process.stdout.read().decode()
#print(output)
if output:
try:
message = json.loads(output)
return message
except Exception as e:
print(e)
error = f'Error: {e}'
save_ail_server_error(ail_uuid, error)
return
# ERROR
else:
if process.stderr:
stderr = process.stderr.read().decode()
else:
stderr = ''
if process.stdout:
stdout = process.stdout.read().decode()
else:
stdout =''
if stderr or stdout:
error = f'-stderr-\n{stderr}\n-stdout-\n{stdout}'
print(error)
save_ail_server_error(ail_uuid, error)
return
def get_remote_ail_server_version(ail_uuid):
response = _get_remote_ail_server_response(ail_uuid, 'version')
if response:
version = response.get('version')
if version:
version = float(version)
if version >= 0.1:
set_ail_server_version(ail_uuid, version)
return version
# # TODO: CATCH WEBSOCKETS RESPONSE CODE
def ping_remote_ail_server(ail_uuid):
response = _get_remote_ail_server_response(ail_uuid, 'ping')
if response:
response = response.get('message', False)
pong = response == 'pong'
set_ail_server_ping(ail_uuid, pong)
return pong
## API ## ## API ##
def api_ping_remote_ail_server(json_dict):
ail_uuid = json_dict.get('uuid').replace(' ', '')
if not is_valid_uuid_v4(ail_uuid):
return {"status": "error", "reason": "Invalid ail uuid"}, 400
ail_uuid = sanityze_uuid(ail_uuid)
if not exists_ail_instance(ail_uuid):
return {"status": "error", "reason": "AIL server not found"}, 404
res = ping_remote_ail_server(ail_uuid)
return res, 200
def api_get_remote_ail_server_version(json_dict):
ail_uuid = json_dict.get('uuid').replace(' ', '')
if not is_valid_uuid_v4(ail_uuid):
return {"status": "error", "reason": "Invalid ail uuid"}, 400
ail_uuid = sanityze_uuid(ail_uuid)
if not exists_ail_instance(ail_uuid):
return {"status": "error", "reason": "AIL server not found"}, 404
res = get_remote_ail_server_version(ail_uuid)
return res, 200
def api_create_ail_instance(json_dict): def api_create_ail_instance(json_dict):
ail_uuid = json_dict.get('uuid').replace(' ', '') ail_uuid = json_dict.get('uuid').replace(' ', '')
if not is_valid_uuid_v4(ail_uuid): if not is_valid_uuid_v4(ail_uuid):
@ -755,7 +871,13 @@ if __name__ == '__main__':
# print(get_all_sync_queue()) # print(get_all_sync_queue())
# res = get_all_unregistred_queue_by_ail_instance(ail_uuid) # res = get_all_unregistred_queue_by_ail_instance(ail_uuid)
ail_uuid = 'd82d3e61-2438-4ede-93bf-37b6fd9d7510' ail_uuid = 'c3c2f3ef-ca53-4ff6-8317-51169b73f731'
res = get_client_id_by_ail_uuid(ail_uuid) ail_uuid = '03c51929-eeab-4d47-9dc0-c667f94c7d2d'
# res = ping_remote_ail_server(ail_uuid)
# print(res)
#
res = get_remote_ail_server_version(ail_uuid)
#res = _get_remote_ail_server_response(ail_uuid, 'pin')
print(res) print(res)

View file

@ -6,6 +6,7 @@ import json
import os import os
import sys import sys
import time import time
from pubsublogger import publisher
from urllib.parse import urljoin from urllib.parse import urljoin
import asyncio import asyncio
@ -19,6 +20,12 @@ sys.path.append(os.environ['AIL_BIN'])
################################## ##################################
from core import ail_2_ail from core import ail_2_ail
#### LOGS ####
redis_logger = publisher
redis_logger.port = 6380
redis_logger.channel = 'AIL_SYNC_client'
##-- LOGS --##
#################################################################### ####################################################################
class AIL2AILClient(object): class AIL2AILClient(object):
@ -29,18 +36,21 @@ class AIL2AILClient(object):
self.ail_uuid = ail_uuid self.ail_uuid = ail_uuid
self.sync_mode = sync_mode self.sync_mode = sync_mode
# # TODO:
self.ail_url = "wss://localhost:4443"
self.uri = f"{ail_url}/{sync_mode}/{ail_uuid}" self.uri = f"{ail_url}/{sync_mode}/{ail_uuid}"
#################################################################### ####################################################################
# # TODO: ADD TIMEOUT => 30s
async def api_request(websocket, ail_uuid):
res = await websocket.recv()
# API OUTPUT
sys.stdout.write(res)
# # TODO: ADD TIMEOUT # # TODO: ADD TIMEOUT
async def pull(websocket, ail_uuid): async def pull(websocket, ail_uuid):
while True: while True:
obj = await websocket.recv() obj = await websocket.recv()
print(obj) sys.stdout.write(res)
async def push(websocket, ail_uuid): async def push(websocket, ail_uuid):
@ -60,51 +70,100 @@ async def push(websocket, ail_uuid):
await asyncio.sleep(10) await asyncio.sleep(10)
async def ail_to_ail_client(ail_uuid, sync_mode, ail_key=None): async def ail_to_ail_client(ail_uuid, sync_mode, api, ail_key=None):
if not ail_2_ail.exists_ail_instance(ail_uuid):
print('AIL server not found')
return
if not ail_key: if not ail_key:
ail_key = ail_2_ail.get_ail_instance_key(ail_uuid) ail_key = ail_2_ail.get_ail_instance_key(ail_uuid)
ail_url = "wss://localhost:4443"
uri = f"{ail_url}/{sync_mode}/{ail_uuid}" # # TODO: raise exception
print(uri) ail_url = ail_2_ail.get_ail_instance_url(ail_uuid)
local_ail_uuid = ail_2_ail.get_ail_uuid()
async with websockets.connect( if sync_mode == 'api':
uri, uri = f"{ail_url}/{sync_mode}/{api}/{local_ail_uuid}"
ssl=ssl_context, else:
extra_headers={"Authorization": f"{ail_key}"} uri = f"{ail_url}/{sync_mode}/{local_ail_uuid}"
) as websocket: #print(uri)
if sync_mode == 'pull': ail_2_ail.clear_save_ail_server_error(ail_uuid)
await pull(websocket, ail_uuid)
try:
async with websockets.connect(
uri,
ssl=ssl_context,
extra_headers={"Authorization": f"{ail_key}"}
) as websocket:
if sync_mode == 'pull':
await pull(websocket, ail_uuid)
elif sync_mode == 'push':
await push(websocket, ail_uuid)
await websocket.close()
elif sync_mode == 'api':
await api_request(websocket, ail_uuid)
await websocket.close()
except websockets.exceptions.InvalidStatusCode as e:
status_code = e.status_code
error_message = ''
# success
if status_code == 1000:
print('connection closed')
elif status_code == 400:
error_message = 'BAD_REQUEST: Invalid path'
elif status_code == 401:
error_message = 'UNAUTHORIZED: Invalid Key'
elif status_code == 403:
error_message = 'FORBIDDEN: SYNC mode disabled'
else:
error_message = str(e)
if error_message:
sys.stderr.write(error_message)
redis_logger.warning(f'{error_message}: {ail_uuid}')
ail_2_ail.save_ail_server_error(ail_uuid, error_message)
except websockets.exceptions.InvalidURI as e:
error_message = f'Invalid AIL url: {e.uri}'
sys.stderr.write(error_message)
redis_logger.warning(f'{error_message}: {ail_uuid}')
ail_2_ail.save_ail_server_error(ail_uuid, error_message)
except ConnectionError as e:
error_message = str(e)
sys.stderr.write(error_message)
redis_logger.info(f'{error_message}: {ail_uuid}')
ail_2_ail.save_ail_server_error(ail_uuid, error_message)
except websockets.exceptions.ConnectionClosedOK as e:
print('connection closed')
# except Exception as e:
# print(e)
elif sync_mode == 'push':
await push(websocket, ail_uuid)
await websocket.close()
elif sync_mode == 'api':
await websocket.close()
##########################################################3
# # TODO:manual key
##########################################################
if __name__ == '__main__': if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Websocket SYNC Client') parser = argparse.ArgumentParser(description='Websocket SYNC Client')
parser.add_argument('-a', '--ail', help='AIL UUID', type=str, dest='ail_uuid', required=True, default=None) parser.add_argument('-u', '--uuid', help='AIL UUID', type=str, dest='ail_uuid', required=True, default=None)
parser.add_argument('-i', '--client_id', help='Client ID', type=str, dest='client_id', default=None) parser.add_argument('-i', '--client_id', help='Client ID', type=str, dest='client_id', default=None)
parser.add_argument('-m', '--mode', help='SYNC Mode, pull or push', type=str, dest='sync_mode', default='pull') parser.add_argument('-m', '--mode', help='SYNC Mode, pull, push or api', type=str, dest='sync_mode', default='pull')
parser.add_argument('-a', '--api', help='API, ping or version', type=str, dest='api', default=None)
#parser.add_argument('-k', '--key', type=str, default='', help='AIL Key') #parser.add_argument('-k', '--key', type=str, default='', help='AIL Key')
args = parser.parse_args() args = parser.parse_args()
ail_uuid = args.ail_uuid ail_uuid = args.ail_uuid
sync_mode = args.sync_mode sync_mode = args.sync_mode
api = args.api
if ail_uuid is None or sync_mode not in ['pull', 'push']: if ail_uuid is None or sync_mode not in ['api', 'pull', 'push']:
parser.print_help() parser.print_help()
sys.exit(0) sys.exit(0)
#ail_uuid = '03c51929-eeab-4d47-9dc0-c667f94c7d2d' if api:
#sync_mode = 'pull' if api not in ['ping', 'version']:
parser.print_help()
sys.exit(0)
# SELF SIGNED CERTIFICATES # SELF SIGNED CERTIFICATES
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
@ -112,4 +171,4 @@ if __name__ == '__main__':
ssl_context.verify_mode = ssl.CERT_NONE ssl_context.verify_mode = ssl.CERT_NONE
# SELF SIGNED CERTIFICATES # SELF SIGNED CERTIFICATES
asyncio.get_event_loop().run_until_complete(ail_to_ail_client(ail_uuid, sync_mode)) asyncio.get_event_loop().run_until_complete(ail_to_ail_client(ail_uuid, sync_mode, api))

View file

@ -22,7 +22,7 @@ from core import ail_2_ail
#### LOGS #### #### LOGS ####
redis_logger = publisher redis_logger = publisher
redis_logger.port = 6380 redis_logger.port = 6380
redis_logger.channel = 'AIL_SYNC' redis_logger.channel = 'AIL_SYNC_Server'
############################# #############################
@ -52,8 +52,13 @@ def unpack_path(path):
path = path.split('/') path = path.split('/')
if len(path) < 3: if len(path) < 3:
raise Exception('Invalid url path') raise Exception('Invalid url path')
if not len(path[-1]):
path = path[:-1]
dict_path['sync_mode'] = path[1] dict_path['sync_mode'] = path[1]
dict_path['ail_uuid'] = path[2] dict_path['ail_uuid'] = path[-1]
dict_path['api'] = path[2:-1]
return dict_path return dict_path
# # # # # # # # # # # # # #
@ -66,8 +71,12 @@ def unpack_path(path):
async def register(websocket): async def register(websocket):
ail_uuid = websocket.ail_uuid
remote_address = websocket.remote_address
redis_logger.info(f'Client Connected: {ail_uuid} {remote_address}')
print(f'Client Connected: {ail_uuid} {remote_address}')
CONNECTED_CLIENT.add(websocket) CONNECTED_CLIENT.add(websocket)
print(CONNECTED_CLIENT) #print(CONNECTED_CLIENT)
async def unregister(websocket): async def unregister(websocket):
CONNECTED_CLIENT.remove(websocket) CONNECTED_CLIENT.remove(websocket)
@ -108,6 +117,23 @@ async def push(websocket, ail_uuid):
ail_2_ail.add_ail_stream_to_sync_importer(ail_stream) ail_2_ail.add_ail_stream_to_sync_importer(ail_stream)
# API: server API
# # TODO: ADD TIMEOUT ???
async def api(websocket, ail_uuid, api):
api = api[0]
if api == 'ping':
message = {'message':'pong'}
message = json.dumps(message)
await websocket.send(message)
elif api == 'version':
sync_version = ail_2_ail.get_sync_server_version()
message = {'version': sync_version}
message = json.dumps(message)
await websocket.send(message)
# END API
return
async def ail_to_ail_serv(websocket, path): async def ail_to_ail_serv(websocket, path):
# # TODO: save in class # # TODO: save in class
@ -118,10 +144,9 @@ async def ail_to_ail_serv(websocket, path):
# # TODO: check if it works # # TODO: check if it works
# # DEBUG: # # DEBUG:
print(websocket.ail_key) # print(websocket.ail_uuid)
print(websocket.ail_uuid) # print(websocket.remote_address)
print(websocket.remote_address) # print(f'sync mode: {sync_mode}')
print(f'sync mode: {sync_mode}')
await register(websocket) await register(websocket)
try: try:
@ -135,7 +160,10 @@ async def ail_to_ail_serv(websocket, path):
await push(websocket, websocket.ail_uuid) await push(websocket, websocket.ail_uuid)
elif sync_mode == 'api': elif sync_mode == 'api':
await api(websocket, websocket.ail_uuid, path['api'])
await websocket.close() await websocket.close()
redis_logger.info(f'Connection closed: {ail_uuid} {remote_address}')
print(f'Connection closed: {ail_uuid} {remote_address}')
finally: finally:
await unregister(websocket) await unregister(websocket)
@ -151,11 +179,12 @@ class AIL_2_AIL_Protocol(websockets.WebSocketServerProtocol):
async def process_request(self, path, request_headers): async def process_request(self, path, request_headers):
print(self.remote_address) # DEBUG:
print(request_headers) # print(self.remote_address)
# print(request_headers)
# API TOKEN # API TOKEN
api_key = request_headers.get('Authorization', '') api_key = request_headers.get('Authorization', '')
print(api_key)
if api_key is None: if api_key is None:
redis_logger.warning(f'Missing token: {self.remote_address}') redis_logger.warning(f'Missing token: {self.remote_address}')
print(f'Missing token: {self.remote_address}') print(f'Missing token: {self.remote_address}')
@ -215,7 +244,7 @@ class AIL_2_AIL_Protocol(websockets.WebSocketServerProtocol):
return http.HTTPStatus.FORBIDDEN, [], b"SYNC mode disabled\n" return http.HTTPStatus.FORBIDDEN, [], b"SYNC mode disabled\n"
# # TODO: CHECK API # # TODO: CHECK API
elif dict_path[sync_mode] == 'api': elif dict_path['sync_mode'] == 'api':
pass pass
else: else:

View file

@ -53,9 +53,11 @@ def create_json_response(data, status_code):
@login_required @login_required
@login_admin @login_admin
def ail_2_ail_dashboard(): def ail_2_ail_dashboard():
ail_uuid = ail_2_ail.get_ail_uuid()
l_servers = ail_2_ail.get_all_running_sync_servers() l_servers = ail_2_ail.get_all_running_sync_servers()
l_servers = ail_2_ail.get_ail_instances_metadata(l_servers) l_servers = ail_2_ail.get_ail_instances_metadata(l_servers)
return render_template("ail_2_ail_dashboard.html", l_servers=l_servers) return render_template("ail_2_ail_dashboard.html", ail_uuid=ail_uuid,
l_servers=l_servers)
###################### ######################
# # # #
@ -80,6 +82,28 @@ def ail_server_view():
return render_template("view_ail_server.html", server_metadata=server_metadata, return render_template("view_ail_server.html", server_metadata=server_metadata,
bootstrap_label=bootstrap_label) bootstrap_label=bootstrap_label)
@ail_2_ail_sync.route('/settings/ail_2_ail/server/api/ping', methods=['GET'])
@login_required
@login_admin
def ail_server_api_ping():
ail_uuid = request.args.get('uuid')
input_dict = {"uuid": ail_uuid}
res = ail_2_ail.api_ping_remote_ail_server(input_dict)
if res[1] != 200:
return create_json_response(res[0], res[1])
return redirect(url_for('ail_2_ail_sync.ail_server_view', uuid=ail_uuid))
@ail_2_ail_sync.route('/settings/ail_2_ail/server/api/version', methods=['GET'])
@login_required
@login_admin
def ail_server_api_version():
ail_uuid = request.args.get('uuid')
input_dict = {"uuid": ail_uuid}
res = ail_2_ail.api_get_remote_ail_server_version(input_dict)
if res[1] != 200:
return create_json_response(res[0], res[1])
return redirect(url_for('ail_2_ail_sync.ail_server_view', uuid=ail_uuid))
@ail_2_ail_sync.route('/settings/ail_2_ail/server/add', methods=['GET', 'POST']) @ail_2_ail_sync.route('/settings/ail_2_ail/server/add', methods=['GET', 'POST'])
@login_required @login_required
@login_admin @login_admin

View file

@ -30,6 +30,20 @@
<div class="col-12 col-lg-10" id="core_content"> <div class="col-12 col-lg-10" id="core_content">
<div class="d-flex justify-content-center my-4">
<div class="card border-secondary" style="max-width: 40rem;">
<div class="card-header bg-inf">
<h5 class="card-title">AIL UUID:</h5>
</div>
<div class="card-body text-dark">
<div class="">
{{ ail_uuid }}
</div>
</div>
</div>
</div>
<h1>Connected Servers:</h3> <h1>Connected Servers:</h3>
<table id="table_servers" class="table table-striped border-primary"> <table id="table_servers" class="table table-striped border-primary">
@ -73,7 +87,7 @@
<script> <script>
$(document).ready(function(){ $(document).ready(function(){
$('#nav_sync').removeClass("text-muted"); $('#nav_sync').removeClass("text-muted");
$("#nav_ail_servers").addClass("active"); $("#nav_ail_servers").addClass("nav_ail_sync");
$('#table_servers').DataTable({ $('#table_servers').DataTable({
"aLengthMenu": [[5, 10, 15, -1], [5, 10, 15, "All"]], "aLengthMenu": [[5, 10, 15, -1], [5, 10, 15, "All"]],

View file

@ -41,8 +41,9 @@
<table id="table_servers" class="table table-striped border-primary"> <table id="table_servers" class="table table-striped border-primary">
<thead class="bg-dark text-white"> <thead class="bg-dark text-white">
<tr> <tr>
<th>uuid</th>
<th>url</th> <th>url</th>
<th></th>
<th>uuid</th>
<th>description</th> <th>description</th>
<th>sync queues</th> <th>sync queues</th>
</tr> </tr>
@ -50,12 +51,23 @@
<tbody style="font-size: 15px;"> <tbody style="font-size: 15px;">
{% for dict_server in l_servers %} {% for dict_server in l_servers %}
<tr class="border-color: blue;"> <tr class="border-color: blue;">
<td> <td>{{ dict_server['url']}}</td>
<td>
{% if dict_server['ping'] %}
<div style="color:Green;">
<i class="fas fa-check-circle fa-2x"></i>
</div>
{% else %}
<div style="color:Red;">
<i class="fas fa-times-circle fa-2x"></i>
</div>
{% endif %}
</td>
<td>
<a href="{{ url_for('ail_2_ail_sync.ail_server_view') }}?uuid={{ dict_server['uuid'] }}"> <a href="{{ url_for('ail_2_ail_sync.ail_server_view') }}?uuid={{ dict_server['uuid'] }}">
{{ dict_server['uuid']}} {{ dict_server['uuid']}}
</a> </a>
</td> </td>
<td>{{ dict_server['url']}}</td>
<td>{{ dict_server['description']}}</td> <td>{{ dict_server['description']}}</td>
<td class="text-center"> <td class="text-center">
{% for queue_uuid in dict_server['sync_queues'] %} {% for queue_uuid in dict_server['sync_queues'] %}

View file

@ -30,6 +30,19 @@
<div class="card"> <div class="card">
<div class="card-header bg-dark text-white"> <div class="card-header bg-dark text-white">
<span class="badge badge-pill badge-light flex-row-reverse float-right">
{% if server_metadata['ping'] %}
<div style="color:Green;">
<i class="fas fa-check-circle fa-2x"></i>
PING
</div>
{% else %}
<div style="color:Red;">
<i class="fas fa-times-circle fa-2x"></i>
ERROR
</div>
{% endif %}
</span>
<h5 class="card-title">{{server_metadata['uuid']}}</h5> <h5 class="card-title">{{server_metadata['uuid']}}</h5>
</div> </div>
<div class="card-body"> <div class="card-body">
@ -39,6 +52,22 @@
<td class="text-right"><b>URL</b></td> <td class="text-right"><b>URL</b></td>
<td> <td>
{{server_metadata['url']}} {{server_metadata['url']}}
{% if server_metadata['ping'] %}
<span style="color:Green;">
<i class="fas fa-check-circle"></i>
</span>
{% else %}
<span style="color:Red;">
<i class="fas fa-times-circle"></i>
</span>
{% endif %}
<div>
<a href="{{ url_for('ail_2_ail_sync.ail_server_api_ping') }}?uuid={{server_metadata['uuid']}}">
<button type="button" class="btn btn-primary px-1 py-0">
<i class="fas fa-redo-alt"></i> ping
</button>
</a>
</div>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -65,9 +94,29 @@
{{server_metadata['push']}} {{server_metadata['push']}}
</td> </td>
</tr> </tr>
<tr>
<td class="text-right"><b>Version</b></td>
<td>
{{server_metadata['version']}}
<a href="{{ url_for('ail_2_ail_sync.ail_server_api_version') }}?uuid={{server_metadata['uuid']}}">
<button type="button" class="btn btn-info px-1 py-0">
<i class="fas fa-redo-alt"></i>
</button>
</a>
</td>
</tr>
</tbody> </tbody>
</table> </table>
{% if server_metadata['error']%}
<pre class="bg-dark text-white">
----------------------------
- ERROR -
----------------------------
{{server_metadata['error']}}
</pre>
{% endif %}
<div class="my-4"> <div class="my-4">
<a href="{{ url_for('ail_2_ail_sync.ail_server_delete') }}?uuid={{server_metadata['uuid']}}"> <a href="{{ url_for('ail_2_ail_sync.ail_server_delete') }}?uuid={{server_metadata['uuid']}}">
<button type="button" class="btn btn-danger"> <button type="button" class="btn btn-danger">