async def show_queues(request): """Shows all installed modules""" try: config.read_config() except: return PlainTextResponse( 'Configuration is being updated. Try again in a minute.') processing_suspended = False processing_halt_file = Path(config.mercure['processing_folder'] + '/' + mercure_names.HALT) if processing_halt_file.exists(): processing_suspended = True routing_suspended = False routing_halt_file = Path(config.mercure['outgoing_folder'] + '/' + mercure_names.HALT) if routing_halt_file.exists(): routing_suspended = True template = "queue.html" context = { "request": request, "mercure_version": mercure_defs.VERSION, "page": "queue", "processing_suspended": processing_suspended, "routing_suspended": routing_suspended } context.update(get_user_information(request)) return templates.TemplateResponse(template, context)
async def show_modules(request): """Shows all installed modules""" try: config.read_config() except: return PlainTextResponse( "Configuration is being updated. Try again in a minute.") used_modules = {} for rule in config.mercure["rules"]: used_module = config.mercure["rules"][rule].get( "processing_module", "NONE") used_modules[used_module] = rule template = "modules.html" context = { "request": request, "mercure_version": mercure_defs.VERSION, "page": "modules", "modules": config.mercure["modules"], "used_modules": used_modules } context.update(get_user_information(request)) return templates.TemplateResponse(template, context)
async def configuration_edit(request): """Shows a configuration editor""" # Check for existence of lock file cfg_file = Path(config.configuration_filename) cfg_lock = Path(cfg_file.parent / cfg_file.stem).with_suffix( mercure_names.LOCK) if cfg_lock.exists(): return PlainTextResponse( 'Configuration is being updated. Try again in a minute.') try: with open(cfg_file, "r") as json_file: config_content = json.load(json_file) except: return PlainTextResponse('Error reading configuration file.') config_content = json.dumps(config_content, indent=4, sort_keys=False) template = "configuration_edit.html" context = { "request": request, "mercure_version": mercure_defs.VERSION, "page": "configuration", "config_content": config_content } context.update(get_user_information(request)) return templates.TemplateResponse(template, context)
async def settings_edit(request): """Shows the settings for the current user. Renders the same template as the normal user edit, but with parameter own_settings=True.""" try: users.read_users() except: return PlainTextResponse( 'Configuration is being updated. Try again in a minute.') own_name = request.user.display_name template = "users_edit.html" context = { "request": request, "mercure_version": mercure_defs.VERSION, "page": "settings", "edituser": own_name, "edituser_info": users.users_list[own_name], "own_settings": "True", "change_password": users.users_list[own_name].get("change_password", "False") } context.update(get_user_information(request)) return templates.TemplateResponse(template, context)
async def show_users(request): """Shows all available users.""" try: users.read_users() except: return PlainTextResponse("Configuration is being updated. Try again in a minute.") template = "users.html" context = {"request": request, "mercure_version": mercure_defs.VERSION, "page": "users", "users": users.users_list} context.update(get_user_information(request)) return templates.TemplateResponse(template, context)
async def show_rules(request): """Show all defined routing rules. Can be executed by all logged-in users.""" try: config.read_config() except: return PlainTextResponse("Configuration is being updated. Try again in a minute.") template = "rules.html" context = {"request": request, "mercure_version": mercure_defs.VERSION, "page": "rules", "rules": config.mercure["rules"]} context.update(get_user_information(request)) return templates.TemplateResponse(template, context)
async def homepage(request): """Renders the index page that shows information about the system status.""" used_space = 0 free_space = 0 total_space = 0 try: disk_total, disk_used, disk_free = shutil.disk_usage( config.mercure["incoming_folder"]) if (disk_total == 0): disk_total = 1 used_space = 100 * disk_used / disk_total free_space = (disk_free // (2**30)) total_space = (disk_total // (2**30)) except: used_space = -1 free_space = "N/A" disk_total = "N/A" service_status = {} for service in services.services_list: running_status = "False" if (services.services_list[service].get("systemd_service", "")): if (await async_run("systemctl is-active " + services.services_list[service] ["systemd_service"]))[0] == 0: running_status = "True" service_status[service] = { "id": service, "name": services.services_list[service]["name"], "running": running_status } template = "index.html" context = { "request": request, "mercure_version": mercure_defs.VERSION, "page": "homepage", "used_space": used_space, "free_space": free_space, "total_space": total_space, "service_status": service_status } context.update(get_user_information(request)) return templates.TemplateResponse(template, context)
async def users_edit(request): """Shows the settings for a given user.""" try: users.read_users() except: return PlainTextResponse("Configuration is being updated. Try again in a minute.") edituser = request.path_params["user"] if not edituser in users.users_list: return RedirectResponse(url="/users", status_code=303) template = "users_edit.html" context = {"request": request, "mercure_version": mercure_defs.VERSION, "page": "users", "edituser": edituser, "edituser_info": users.users_list[edituser]} context.update(get_user_information(request)) return templates.TemplateResponse(template, context)
async def targets_edit(request): """Shows the edit page for the given target.""" try: config.read_config() except: return PlainTextResponse("Configuration is being updated. Try again in a minute.") edittarget = request.path_params["target"] if not edittarget in config.mercure["targets"]: return RedirectResponse(url="/targets", status_code=303) template = "targets_edit.html" context = {"request": request, "mercure_version": mercure_defs.VERSION, "page": "targets", "targets": config.mercure["targets"], "edittarget": edittarget} context.update(get_user_information(request)) return templates.TemplateResponse(template, context)
async def show_targets(request): """Shows all configured targets.""" try: config.read_config() except: return PlainTextResponse("Configuration is being updated. Try again in a minute.") used_targets = {} for rule in config.mercure["rules"]: used_target = config.mercure["rules"][rule].get("target", "NONE") used_targets[used_target] = rule template = "targets.html" context = {"request": request, "mercure_version": mercure_defs.VERSION, "page": "targets", "targets": config.mercure["targets"], "used_targets": used_targets} context.update(get_user_information(request)) return templates.TemplateResponse(template, context)
async def edit_module(request): """Shows all installed modules""" module = request.path_params["module"] try: config.read_config() except: return PlainTextResponse( "Configuration is being updated. Try again in a minute.") template = "modules_edit.html" context = { "request": request, "mercure_version": mercure_defs.VERSION, "page": "modules", "module": config.mercure["modules"][module], "module_name": module } context.update(get_user_information(request)) return templates.TemplateResponse(template, context)
async def configuration(request): """Shows the current configuration of the mercure appliance.""" try: config.read_config() except: return PlainTextResponse('Error reading configuration file.') template = "configuration.html" config_edited = int(request.query_params.get("edited", 0)) os_info = distro.linux_distribution() os_string = f"{os_info[0]} Version {os_info[1]} ({os_info[2]})" context = { "request": request, "mercure_version": mercure_defs.VERSION, "page": "configuration", "config": config.mercure, "os_string": os_string, "config_edited": config_edited } context.update(get_user_information(request)) return templates.TemplateResponse(template, context)
async def rules_edit(request): """Shows the edit page for the given routing rule.""" try: config.read_config() except: return PlainTextResponse("Configuration is being updated. Try again in a minute.") rule = request.path_params["rule"] template = "rules_edit.html" context = { "request": request, "mercure_version": mercure_defs.VERSION, "page": "rules", "rules": config.mercure["rules"], "targets": config.mercure["targets"], "modules": config.mercure["modules"], "rule": rule, "alltags": tagslist.alltags, "sortedtags": tagslist.sortedtags, } context.update(get_user_information(request)) return templates.TemplateResponse(template, context)
async def show_log(request): """Render the log for the given service. The time range can be specified via URL parameters.""" requested_service = request.path_params["service"] # Get optional start and end dates from the URL. Make sure # that the date format is clean try: start_date = request.query_params.get("from", "") start_time = request.query_params.get("from_time", "") datetime.datetime.strptime(start_date, '%Y-%m-%d') start_date_cmd = ' --since "' + start_date if start_date and start_time: datetime.datetime.strptime(start_time, '%H:%M') start_date_cmd = start_date_cmd + " " + start_time start_date_cmd = start_date_cmd + '"' except: start_date = "" start_time = "" start_date_cmd = "" try: end_date = request.query_params.get("to", "") end_time = request.query_params.get("to_time", "") datetime.datetime.strptime(end_date, '%Y-%m-%d') end_date_cmd = ' --until "' + end_date if end_date and end_time: datetime.datetime.strptime(end_time, '%H:%M') end_date_cmd = end_date_cmd + " " + end_time end_date_cmd = end_date_cmd + '"' except: end_date = "" end_time = "" end_date_cmd = "" service_logs = {} for service in services.services_list: service_logs[service] = { "id": service, "name": services.services_list[service]["name"], "systemd": services.services_list[service]["systemd_service"] } if (not requested_service in service_logs) or ( not services.services_list[requested_service]["systemd_service"]): return PlainTextResponse( 'Service does not exist or is incorrectly configured.') run_result = await async_run( 'journalctl -n 1000 -u ' + services.services_list[requested_service]["systemd_service"] + start_date_cmd + end_date_cmd) log_content = "" if run_result[0] == 0: log_content = html.escape(str(run_result[1].decode())) line_list = log_content.split('\n') if len(line_list) and (not line_list[-1]): del line_list[-1] log_content = '<br />'.join(line_list) else: log_content = "Error reading log information." if start_date or end_date: log_content = log_content + "<br /><br />Are the From/To settings valid?" template = "logs.html" context = { "request": request, "mercure_version": mercure_defs.VERSION, "page": "logs", "service_logs": service_logs, "log_id": requested_service, "log_content": log_content, "start_date": start_date, "start_time": start_time, "end_date": end_date, "end_time": end_time } context.update(get_user_information(request)) return templates.TemplateResponse(template, context)
async def show_log(request): """Render the log for the given service. The time range can be specified via URL parameters.""" requested_service = request.path_params["service"] # Get optional start and end dates from the URL. Make sure # that the date format is clean. try: start_date = request.query_params.get("from", "") start_time = request.query_params.get("from_time", "00:00") start_timestamp = f"{start_date} {start_time}" start_obj = datetime.datetime.strptime(start_timestamp, "%Y-%m-%d %H:%M") except ValueError: start_obj = None start_timestamp = "" try: end_date = request.query_params.get("to", "") # Make sure end time includes the day-of, unless otherwise specified end_time = request.query_params.get("to_time", "23:59") end_timestamp = f"{end_date} {end_time}" datetime.datetime.strptime(end_timestamp, "%Y-%m-%d %H:%M") except ValueError: end_timestamp = "" service_logs = {} for service in services.services_list: service_logs[service] = { "id": service, "name": services.services_list[service]["name"], "systemd": services.services_list[service].get("systemd_service", ""), "docker": services.services_list[service].get("docker_service", "") } if requested_service not in service_logs: return PlainTextResponse("Service does not exist.") if "systemd_service" not in services.services_list[requested_service] and \ "docker_service" not in services.services_list[requested_service]: return PlainTextResponse("Service incorrectly configured.") return_code = -1 raw_logs = "" if "systemd_service" in services.services_list[requested_service]: start_date_cmd = "" end_date_cmd = "" if start_timestamp: start_date_cmd = f'--since {start_timestamp}' if end_timestamp: end_date_cmd = f'--until {end_timestamp}' run_result = await async_run( f'journalctl -n 1000 -u ' f'{services.services_list[requested_service]["systemd_service"]} ' f'{start_date_cmd} {end_date_cmd}') return_code = run_result[0] raw_logs = run_result[1] elif "docker_service" in services.services_list[requested_service]: client = docker.from_env() try: container = client.containers.get( services.services_list[requested_service]["docker_service"]) container.reload() raw_logs = container.logs(since=start_obj) return_code = 0 except (docker.errors.NotFound, docker.errors.APIError): return_code = 1 if return_code == 0: log_content = html.escape(str(raw_logs.decode())) line_list = log_content.split("\n") if len(line_list) and (not line_list[-1]): del line_list[-1] log_content = "<br />".join(line_list) else: log_content = "Error reading log information." if start_date or end_date: log_content = log_content + "<br /><br />Are the From/To settings valid?" template = "logs.html" context = { "request": request, "mercure_version": mercure_defs.VERSION, "page": "logs", "service_logs": service_logs, "log_id": requested_service, "log_content": log_content, "start_date": start_date, "start_time": start_time, "end_date": end_date, "end_time": end_time, } context.update(get_user_information(request)) return templates.TemplateResponse(template, context)
async def homepage(request): """Renders the index page that shows information about the system status.""" used_space = 0 free_space = 0 total_space = 0 try: disk_total, disk_used, disk_free = shutil.disk_usage( config.mercure["incoming_folder"]) if disk_total == 0: disk_total = 1 used_space = 100 * disk_used / disk_total free_space = disk_free // (2**30) total_space = disk_total // (2**30) except: used_space = -1 free_space = "N/A" disk_total = "N/A" service_status = {} for service in services.services_list: running_status = "False" if services.services_list[service].get("systemd_service", ""): if (await async_run("systemctl is-active " + services.services_list[service] ["systemd_service"]))[0] == 0: running_status = "True" elif services.services_list[service].get("docker_service", ""): client = docker.from_env() try: container = client.containers.get( services.services_list[service]["docker_service"]) container.reload() status = container.status """restarting, running, paused, exited""" if status == "running": running_status = "True" except (docker.errors.NotFound, docker.errors.APIError): running_status = "False" service_status[service] = { "id": service, "name": services.services_list[service]["name"], "running": running_status } template = "index.html" context = { "request": request, "mercure_version": mercure_defs.VERSION, "page": "homepage", "used_space": used_space, "free_space": free_space, "total_space": total_space, "service_status": service_status, } context.update(get_user_information(request)) return templates.TemplateResponse(template, context)