diff --git a/.github/ISSUE_TEMPLATE/support_request.md b/.github/ISSUE_TEMPLATE/support_request.md index 74accc8..87d75c9 100644 --- a/.github/ISSUE_TEMPLATE/support_request.md +++ b/.github/ISSUE_TEMPLATE/support_request.md @@ -1,48 +1,29 @@ --- name: Support request -about: Need some help? Got an error message? -title: "" -type: support -labels: - - "waiting-on-developer" +about: Ask for help with OliveTin +title: '' +labels: '' assignees: '' --- -**What seems to be the problem?!** +**What is the problem you are having?** -If you are getting an error message, then please copy/paste, or better, provide -a screenshot to show us exactly what is wrong. -**Can you provide a sosreport?** +**What OliveTin version are you running?** -A sosreport really helps us to help you, by providing critical information about your install. If you can generate a sosreport, please copy and paste the output here. -How to generate a sosreport: https://docs.olivetin.app/sosreport.html +**Can you provide Server Diagnostics?** -**What package/file/container did you use to install OliveTin?** +Server Diagnostics really helps us to help you, by providing critical information about your install. If you can generate Server Diagnostics, please copy and paste the output here. -eg: OliveTin-1234-x86_64.rpm -eg: Container from Dockerhub - please include the version number +How to generate Server Diagnostics: https://docs.olivetin.app/troubleshooting/server-diagnostics.html -**Your config.yaml** -``` -Please copy-paste your config.yaml here +**What is your config.yaml?** -You don't need to include anything under "actions:" unless that is where your -error probably is. -``` -**OliveTin logs** +**What are the OliveTin service logs showing?** -If possible, please copy and paste your OliveTin logs from when the error happened. -**Screenshot of WebDeveloper console logs** - -If you know how, and if you think it's relevant, a screenshot of the -WebDeveloper console from when you clicked a button is often really helpful. - -**Anything else?** - -Add any other context about the problem here. +**Screenshots (if appropriate)** diff --git a/.gitignore b/.gitignore index ad372af..2bfbf44 100644 --- a/.gitignore +++ b/.gitignore @@ -18,12 +18,12 @@ integration-tests/flakey-test-runs.jsonl service/flakey-test-runs.log service/flakey-test-runs.jsonl .vscode/ -webui/ +/webui/ server.log OliveTin integration-tests/configs/authRequireGuestsToLogin/sessions.yaml -webui -webui.dev +/webui +/webui.dev sessions.yaml docs/build/ build/ diff --git a/docs/modules/ROOT/images/action-confirmation.png b/docs/modules/ROOT/images/action-confirmation.png deleted file mode 100644 index d681c03..0000000 Binary files a/docs/modules/ROOT/images/action-confirmation.png and /dev/null differ diff --git a/docs/modules/ROOT/images/action_customization/max-rate/.gitignore b/docs/modules/ROOT/images/action_customization/max-rate/.gitignore new file mode 100644 index 0000000..431dbf1 --- /dev/null +++ b/docs/modules/ROOT/images/action_customization/max-rate/.gitignore @@ -0,0 +1,2 @@ +custom-webui/ +__pycache__/ diff --git a/docs/modules/ROOT/images/action_customization/max-rate/Makefile b/docs/modules/ROOT/images/action_customization/max-rate/Makefile new file mode 100644 index 0000000..720e66a --- /dev/null +++ b/docs/modules/ROOT/images/action_customization/max-rate/Makefile @@ -0,0 +1,2 @@ +CONFIGDIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) +include ../../screenshots.mk diff --git a/docs/modules/ROOT/images/action_customization/max-rate/config.yaml b/docs/modules/ROOT/images/action_customization/max-rate/config.yaml new file mode 100644 index 0000000..13963f1 --- /dev/null +++ b/docs/modules/ROOT/images/action_customization/max-rate/config.yaml @@ -0,0 +1,14 @@ +--- +listenAddressSingleHTTPFrontend: 0.0.0.0:11337 + +logLevel: "WARN" +checkForUpdates: false +showFooter: false + +actions: + - title: date + shell: date + icon: clock + maxRate: + - limit: 3 + duration: 5m diff --git a/docs/modules/ROOT/images/action_customization/max-rate/maxRate.png b/docs/modules/ROOT/images/action_customization/max-rate/maxRate.png new file mode 100644 index 0000000..08f763c Binary files /dev/null and b/docs/modules/ROOT/images/action_customization/max-rate/maxRate.png differ diff --git a/docs/modules/ROOT/images/action_customization/max-rate/screenshots.ini b/docs/modules/ROOT/images/action_customization/max-rate/screenshots.ini new file mode 100644 index 0000000..38883c0 --- /dev/null +++ b/docs/modules/ROOT/images/action_customization/max-rate/screenshots.ini @@ -0,0 +1,11 @@ +[DEFAULT] +base_url = http://localhost:11337/ +dir = . +width = 980 +height = 620 +post_script_sleep = 0.5 + +[max-rate] +url = . +name = maxRate +script = setup_max_rate.py diff --git a/docs/modules/ROOT/images/action_customization/max-rate/setup_max_rate.py b/docs/modules/ROOT/images/action_customization/max-rate/setup_max_rate.py new file mode 100644 index 0000000..cd3e81e --- /dev/null +++ b/docs/modules/ROOT/images/action_customization/max-rate/setup_max_rate.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +"""Show blocked rate-limit log entries for the date action.""" + +import time + +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait + +_START_ACTION_REPEATEDLY_JS = """ +const done = arguments[arguments.length - 1]; +const title = arguments[0]; +const count = arguments[1]; + +function bindingIdForTitle(actionTitle) { + const button = document.querySelector('[title="' + actionTitle + '"]'); + if (!button) { + throw new Error('Action button not found: ' + actionTitle); + } + return button.closest('.action-button').id.replace('actionButton-', ''); +} + +function uniqueTrackingId() { + if (window.isSecureContext && window.crypto?.randomUUID) { + return window.crypto.randomUUID(); + } + return 'doc-screenshot-' + Date.now() + '-' + Math.random(); +} + +async function startAndWait(actionTitle) { + const bindingId = bindingIdForTitle(actionTitle); + const trackingId = uniqueTrackingId(); + const response = await window.client.startAction({ + bindingId, + arguments: [], + uniqueTrackingId: trackingId, + }); + const executionTrackingId = response.executionTrackingId || trackingId; + + while (true) { + const result = await window.client.executionStatus({ + executionTrackingId, + }); + if (result.logEntry?.executionFinished) { + return result.logEntry; + } + await new Promise((resolve) => setTimeout(resolve, 100)); + } +} + +async function startRepeatedly(actionTitle, runs) { + for (let i = 0; i < runs; i++) { + await startAndWait(actionTitle); + } +} + +startRepeatedly(title, count).then(() => done(true)).catch((err) => done(String(err))); +""" + + +def _wait_for_dashboard(driver, timeout=15): + WebDriverWait(driver, timeout).until( + lambda d: bool(d.find_element(By.TAG_NAME, "body").get_attribute("loaded-dashboard")) + ) + WebDriverWait(driver, timeout).until( + lambda d: d.execute_script("return !!window.client") + ) + + +def _wait_for_rate_limited_logs(driver, timeout=20): + def ready(d): + try: + rows = d.find_elements(By.CSS_SELECTOR, ".logs-table tbody tr") + blocked = d.find_elements(By.CSS_SELECTOR, ".logs-table .status-blocked") + completed = d.find_elements(By.CSS_SELECTOR, ".logs-table .status-success") + except Exception: + return False + return len(rows) >= 5 and len(blocked) >= 2 and len(completed) >= 3 + + WebDriverWait(driver, timeout).until(ready) + + +def run(driver): + _wait_for_dashboard(driver) + + driver.execute_async_script(_START_ACTION_REPEATEDLY_JS, "date", 5) + + time.sleep(1) + + driver.execute_script("window.location.href = '/logs'") + _wait_for_rate_limited_logs(driver) + + time.sleep(0.2) diff --git a/docs/modules/ROOT/images/action_customization/popup-output-only/.gitignore b/docs/modules/ROOT/images/action_customization/popup-output-only/.gitignore new file mode 100644 index 0000000..431dbf1 --- /dev/null +++ b/docs/modules/ROOT/images/action_customization/popup-output-only/.gitignore @@ -0,0 +1,2 @@ +custom-webui/ +__pycache__/ diff --git a/docs/modules/ROOT/images/action_customization/popup-output-only/Makefile b/docs/modules/ROOT/images/action_customization/popup-output-only/Makefile new file mode 100644 index 0000000..720e66a --- /dev/null +++ b/docs/modules/ROOT/images/action_customization/popup-output-only/Makefile @@ -0,0 +1,2 @@ +CONFIGDIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) +include ../../screenshots.mk diff --git a/docs/modules/ROOT/images/action_customization/popup-output-only/config.yaml b/docs/modules/ROOT/images/action_customization/popup-output-only/config.yaml new file mode 100644 index 0000000..f5f96c6 --- /dev/null +++ b/docs/modules/ROOT/images/action_customization/popup-output-only/config.yaml @@ -0,0 +1,16 @@ +--- +listenAddressSingleHTTPFrontend: 0.0.0.0:11337 + +logLevel: "WARN" +checkForUpdates: false +showFooter: false + +actions: + - title: Check disk space + icon: disk + onclick: execution-dialog + shell: | + echo "Filesystem Size Used Avail Use% Mounted on" + echo "/dev/mapper/fedora_mindstorm-root 99G 82G 12G 88% /" + echo "Filesystem Size Used Avail Use% Mounted on" + echo "/dev/mapper/fedora_mindstorm-root 99G 82G 12G 88% /" diff --git a/docs/modules/ROOT/images/action_customization/popup-output-only/popupOutputOnly.png b/docs/modules/ROOT/images/action_customization/popup-output-only/popupOutputOnly.png new file mode 100644 index 0000000..5898544 Binary files /dev/null and b/docs/modules/ROOT/images/action_customization/popup-output-only/popupOutputOnly.png differ diff --git a/docs/modules/ROOT/images/action_customization/popup-output-only/screenshots.ini b/docs/modules/ROOT/images/action_customization/popup-output-only/screenshots.ini new file mode 100644 index 0000000..362cfa9 --- /dev/null +++ b/docs/modules/ROOT/images/action_customization/popup-output-only/screenshots.ini @@ -0,0 +1,11 @@ +[DEFAULT] +base_url = http://localhost:11337/ +dir = . +width = 900 +height = 620 +post_script_sleep = 0.5 + +[popup-output-only] +url = . +name = popupOutputOnly +script = setup_popup_output_only.py diff --git a/docs/modules/ROOT/images/action_customization/popup-output-only/setup_popup_output_only.py b/docs/modules/ROOT/images/action_customization/popup-output-only/setup_popup_output_only.py new file mode 100644 index 0000000..8e66353 --- /dev/null +++ b/docs/modules/ROOT/images/action_customization/popup-output-only/setup_popup_output_only.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +"""Open the execution dialog for Check disk space.""" + +import time + +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait + + +def _wait_for_body_attr(driver, attr, timeout=15): + WebDriverWait(driver, timeout).until( + lambda d: bool(d.find_element(By.TAG_NAME, "body").get_attribute(attr)) + ) + + +def _wait_for_logs_page(driver, timeout=15): + WebDriverWait(driver, timeout).until( + lambda d: "/logs/" in d.current_url and not d.current_url.rstrip("/").endswith("/logs") + ) + + +def _wait_for_execution_complete(driver, timeout=15): + def finished(d): + try: + status = d.find_element(By.CSS_SELECTOR, ".execution-dialog-status").text + except Exception: + return False + return "Still running" not in status and "Queued" not in status + + WebDriverWait(driver, timeout).until(finished) + + +def run(driver): + _wait_for_body_attr(driver, "loaded-dashboard") + + driver.find_element(By.CSS_SELECTOR, '[title="Check disk space"]').click() + + _wait_for_logs_page(driver) + _wait_for_execution_complete(driver) + + WebDriverWait(driver, 15).until( + lambda d: "fedora_mindstorm-root" in d.find_element( + By.CSS_SELECTOR, "#execution-results-popup .xterm-rows" + ).text + ) + + time.sleep(0.2) diff --git a/docs/modules/ROOT/images/advanced_configuration/diagnostics/.gitignore b/docs/modules/ROOT/images/advanced_configuration/diagnostics/.gitignore new file mode 100644 index 0000000..431dbf1 --- /dev/null +++ b/docs/modules/ROOT/images/advanced_configuration/diagnostics/.gitignore @@ -0,0 +1,2 @@ +custom-webui/ +__pycache__/ diff --git a/docs/modules/ROOT/images/advanced_configuration/diagnostics/Makefile b/docs/modules/ROOT/images/advanced_configuration/diagnostics/Makefile new file mode 100644 index 0000000..720e66a --- /dev/null +++ b/docs/modules/ROOT/images/advanced_configuration/diagnostics/Makefile @@ -0,0 +1,2 @@ +CONFIGDIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) +include ../../screenshots.mk diff --git a/docs/modules/ROOT/images/advanced_configuration/diagnostics/config.yaml b/docs/modules/ROOT/images/advanced_configuration/diagnostics/config.yaml new file mode 100644 index 0000000..9de0c0d --- /dev/null +++ b/docs/modules/ROOT/images/advanced_configuration/diagnostics/config.yaml @@ -0,0 +1,11 @@ +--- +listenAddressSingleHTTPFrontend: 0.0.0.0:11337 + +logLevel: "WARN" +checkForUpdates: false +showFooter: false + +actions: + - title: date + icon: clock + shell: date diff --git a/docs/modules/ROOT/images/advanced_configuration/diagnostics/diagnostics.png b/docs/modules/ROOT/images/advanced_configuration/diagnostics/diagnostics.png new file mode 100644 index 0000000..e2088b4 Binary files /dev/null and b/docs/modules/ROOT/images/advanced_configuration/diagnostics/diagnostics.png differ diff --git a/docs/modules/ROOT/images/advanced_configuration/diagnostics/screenshots.ini b/docs/modules/ROOT/images/advanced_configuration/diagnostics/screenshots.ini new file mode 100644 index 0000000..d023631 --- /dev/null +++ b/docs/modules/ROOT/images/advanced_configuration/diagnostics/screenshots.ini @@ -0,0 +1,11 @@ +[DEFAULT] +base_url = http://localhost:11337/ +dir = . +width = 980 +height = 640 +post_script_sleep = 0.5 + +[diagnostics] +url = /diagnostics +name = diagnostics +script = setup_diagnostics.py diff --git a/docs/modules/ROOT/images/advanced_configuration/diagnostics/setup_diagnostics.py b/docs/modules/ROOT/images/advanced_configuration/diagnostics/setup_diagnostics.py new file mode 100644 index 0000000..fb7ae37 --- /dev/null +++ b/docs/modules/ROOT/images/advanced_configuration/diagnostics/setup_diagnostics.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +"""Open the diagnostics page from advanced_configuration/diagnostics.adoc.""" + +import time + +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait + + +def _wait_for_diagnostics_page(driver, timeout=30): + WebDriverWait(driver, timeout).until( + lambda d: d.execute_script("return !!window.client") + ) + + def ready(d): + try: + ssh_heading = d.find_element( + By.XPATH, + '//*[self::h2 or self::h3][contains(normalize-space(), "SSH")]', + ) + server_diagnostics_heading = d.find_element( + By.XPATH, + '//*[self::h2 or self::h3][contains(normalize-space(), "Server Diagnostics")]', + ) + key_value = d.find_element( + By.XPATH, + '//dt[contains(normalize-space(), "Found Key")]/following-sibling::dd[1]', + ) + config_value = d.find_element( + By.XPATH, + '//dt[contains(normalize-space(), "Found Config")]/following-sibling::dd[1]', + ) + except Exception: + return False + + return all( + element.is_displayed() + for element in ( + ssh_heading, + server_diagnostics_heading, + key_value, + config_value, + ) + ) and all( + element.text.strip() not in ("", "?") + for element in (key_value, config_value) + ) + + WebDriverWait(driver, timeout).until(ready) + + +def run(driver): + _wait_for_diagnostics_page(driver) + time.sleep(0.2) diff --git a/docs/modules/ROOT/images/advanced_configuration/webui/hide-navigation/.gitignore b/docs/modules/ROOT/images/advanced_configuration/webui/hide-navigation/.gitignore new file mode 100644 index 0000000..431dbf1 --- /dev/null +++ b/docs/modules/ROOT/images/advanced_configuration/webui/hide-navigation/.gitignore @@ -0,0 +1,2 @@ +custom-webui/ +__pycache__/ diff --git a/docs/modules/ROOT/images/advanced_configuration/webui/hide-navigation/Makefile b/docs/modules/ROOT/images/advanced_configuration/webui/hide-navigation/Makefile new file mode 100644 index 0000000..b474e2a --- /dev/null +++ b/docs/modules/ROOT/images/advanced_configuration/webui/hide-navigation/Makefile @@ -0,0 +1,2 @@ +CONFIGDIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) +include ../../../screenshots.mk diff --git a/docs/modules/ROOT/images/advanced_configuration/webui/hide-navigation/config.yaml b/docs/modules/ROOT/images/advanced_configuration/webui/hide-navigation/config.yaml new file mode 100644 index 0000000..1f76cea --- /dev/null +++ b/docs/modules/ROOT/images/advanced_configuration/webui/hide-navigation/config.yaml @@ -0,0 +1,48 @@ +--- +listenAddressSingleHTTPFrontend: 0.0.0.0:11337 + +logLevel: "WARN" +checkForUpdates: false +showFooter: false +showNavigation: false + +actions: + - title: Ping the Internet + icon: ping + shell: ping -c 1 127.0.0.1 + - title: Check disk space + icon: disk + shell: df -h + - title: check dmesg logs + icon: logs + shell: dmesg | tail -n 1 + - title: date + icon: clock + shell: date + - title: Run backup script + icon: backup + shell: echo backup + - title: Ping host + icon: ping + shell: ping -c 1 127.0.0.1 + - title: Restart Docker Container + icon: restart + shell: echo restart + - title: Delete old backups + icon: ashtonished + shell: echo delete + - title: Get OliveTin Theme + icon: theme + shell: echo theme + - title: Setup easy SSH + icon: ssh + shell: echo ssh + - title: Restart httpd on server1 + icon: restart + shell: echo restart httpd + - title: Toggle GPIO light + icon: light + shell: echo toggle + - title: Run Automation Playbook + icon: robot + shell: echo ansible diff --git a/docs/modules/ROOT/images/advanced_configuration/webui/hide-navigation/hide-navigation.png b/docs/modules/ROOT/images/advanced_configuration/webui/hide-navigation/hide-navigation.png new file mode 100644 index 0000000..40158eb Binary files /dev/null and b/docs/modules/ROOT/images/advanced_configuration/webui/hide-navigation/hide-navigation.png differ diff --git a/docs/modules/ROOT/images/advanced_configuration/webui/hide-navigation/screenshots.ini b/docs/modules/ROOT/images/advanced_configuration/webui/hide-navigation/screenshots.ini new file mode 100644 index 0000000..c2e4935 --- /dev/null +++ b/docs/modules/ROOT/images/advanced_configuration/webui/hide-navigation/screenshots.ini @@ -0,0 +1,11 @@ +[DEFAULT] +base_url = http://localhost:11337/ +dir = . +width = 1180 +height = 760 +post_script_sleep = 0.5 + +[hide-navigation] +url = . +name = hide-navigation +script = setup_hide_navigation.py diff --git a/docs/modules/ROOT/images/advanced_configuration/webui/hide-navigation/setup_hide_navigation.py b/docs/modules/ROOT/images/advanced_configuration/webui/hide-navigation/setup_hide_navigation.py new file mode 100644 index 0000000..a5c4f0a --- /dev/null +++ b/docs/modules/ROOT/images/advanced_configuration/webui/hide-navigation/setup_hide_navigation.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +"""Capture the Actions view with navigation hidden.""" + +import time + +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait + + +def run(driver): + WebDriverWait(driver, 15).until( + lambda d: bool(d.find_element(By.TAG_NAME, "body").get_attribute("loaded-dashboard")) + ) + WebDriverWait(driver, 15).until( + lambda d: len(d.find_elements(By.ID, "mainnav")) == 0 + ) + WebDriverWait(driver, 15).until( + lambda d: len(d.find_elements(By.CSS_SELECTOR, ".action-button button")) >= 8 + ) + time.sleep(0.2) diff --git a/docs/modules/ROOT/images/advanced_configuration/webui/page-title/.gitignore b/docs/modules/ROOT/images/advanced_configuration/webui/page-title/.gitignore new file mode 100644 index 0000000..431dbf1 --- /dev/null +++ b/docs/modules/ROOT/images/advanced_configuration/webui/page-title/.gitignore @@ -0,0 +1,2 @@ +custom-webui/ +__pycache__/ diff --git a/docs/modules/ROOT/images/advanced_configuration/webui/page-title/Makefile b/docs/modules/ROOT/images/advanced_configuration/webui/page-title/Makefile new file mode 100644 index 0000000..b474e2a --- /dev/null +++ b/docs/modules/ROOT/images/advanced_configuration/webui/page-title/Makefile @@ -0,0 +1,2 @@ +CONFIGDIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) +include ../../../screenshots.mk diff --git a/docs/modules/ROOT/images/advanced_configuration/webui/page-title/config.yaml b/docs/modules/ROOT/images/advanced_configuration/webui/page-title/config.yaml new file mode 100644 index 0000000..e3ab997 --- /dev/null +++ b/docs/modules/ROOT/images/advanced_configuration/webui/page-title/config.yaml @@ -0,0 +1,19 @@ +--- +listenAddressSingleHTTPFrontend: 0.0.0.0:11337 + +logLevel: "WARN" +checkForUpdates: false +showFooter: false + +pageTitle: My OliveTin Instance + +actions: + - title: Ping the Internet + icon: ping + shell: ping -c 1 127.0.0.1 + - title: Check disk space + icon: disk + shell: df -h / + - title: check dmesg logs + icon: logs + shell: dmesg | tail -n 1 diff --git a/docs/modules/ROOT/images/advanced_configuration/webui/page-title/page-title.png b/docs/modules/ROOT/images/advanced_configuration/webui/page-title/page-title.png new file mode 100644 index 0000000..954b353 Binary files /dev/null and b/docs/modules/ROOT/images/advanced_configuration/webui/page-title/page-title.png differ diff --git a/docs/modules/ROOT/images/advanced_configuration/webui/page-title/screenshots.ini b/docs/modules/ROOT/images/advanced_configuration/webui/page-title/screenshots.ini new file mode 100644 index 0000000..84feadb --- /dev/null +++ b/docs/modules/ROOT/images/advanced_configuration/webui/page-title/screenshots.ini @@ -0,0 +1,11 @@ +[DEFAULT] +base_url = http://localhost:11337/ +dir = . +width = 980 +height = 220 +post_script_sleep = 0.5 + +[page-title] +url = . +name = page-title +script = setup_page_title.py diff --git a/docs/modules/ROOT/images/advanced_configuration/webui/page-title/setup_page_title.py b/docs/modules/ROOT/images/advanced_configuration/webui/page-title/setup_page_title.py new file mode 100644 index 0000000..4359421 --- /dev/null +++ b/docs/modules/ROOT/images/advanced_configuration/webui/page-title/setup_page_title.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +"""Capture the custom page title from advanced_configuration/webui.adoc.""" + +import time + +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait + + +def run(driver): + WebDriverWait(driver, 15).until( + lambda d: bool(d.find_element(By.TAG_NAME, "body").get_attribute("loaded-dashboard")) + ) + WebDriverWait(driver, 15).until( + lambda d: "My OliveTin Instance" in d.find_element(By.CSS_SELECTOR, "header h1").text + ) + WebDriverWait(driver, 15).until( + lambda d: d.find_element(By.CSS_SELECTOR, '[title="Ping the Internet"]').is_displayed() + ) + time.sleep(0.2) diff --git a/docs/modules/ROOT/images/advanced_configuration/webui/sidebar/.gitignore b/docs/modules/ROOT/images/advanced_configuration/webui/sidebar/.gitignore new file mode 100644 index 0000000..431dbf1 --- /dev/null +++ b/docs/modules/ROOT/images/advanced_configuration/webui/sidebar/.gitignore @@ -0,0 +1,2 @@ +custom-webui/ +__pycache__/ diff --git a/docs/modules/ROOT/images/advanced_configuration/webui/sidebar/Makefile b/docs/modules/ROOT/images/advanced_configuration/webui/sidebar/Makefile new file mode 100644 index 0000000..b474e2a --- /dev/null +++ b/docs/modules/ROOT/images/advanced_configuration/webui/sidebar/Makefile @@ -0,0 +1,2 @@ +CONFIGDIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) +include ../../../screenshots.mk diff --git a/docs/modules/ROOT/images/advanced_configuration/webui/sidebar/config.yaml b/docs/modules/ROOT/images/advanced_configuration/webui/sidebar/config.yaml new file mode 100644 index 0000000..f3404f6 --- /dev/null +++ b/docs/modules/ROOT/images/advanced_configuration/webui/sidebar/config.yaml @@ -0,0 +1,30 @@ +--- +listenAddressSingleHTTPFrontend: 0.0.0.0:11337 + +logLevel: "WARN" +checkForUpdates: false +showFooter: false + +sectionNavigationStyle: sidebar + +actions: + - title: Check disk space + icon: disk + shell: df -h / + - title: Setup easy SSH + icon: ssh + shell: echo ssh + - title: My Servers ping + icon: ping + shell: echo ping + - title: My Containers list + icon: disk + shell: echo list + +dashboards: + - title: My Servers + contents: + - title: My Servers ping + - title: My Containers + contents: + - title: My Containers list diff --git a/docs/modules/ROOT/images/advanced_configuration/webui/sidebar/screenshots.ini b/docs/modules/ROOT/images/advanced_configuration/webui/sidebar/screenshots.ini new file mode 100644 index 0000000..5656dd0 --- /dev/null +++ b/docs/modules/ROOT/images/advanced_configuration/webui/sidebar/screenshots.ini @@ -0,0 +1,11 @@ +[DEFAULT] +base_url = http://localhost:11337/ +dir = . +width = 980 +height = 520 +post_script_sleep = 0.5 + +[sidebar] +url = . +name = sidebar +script = setup_sidebar.py diff --git a/docs/modules/ROOT/images/advanced_configuration/webui/sidebar/setup_sidebar.py b/docs/modules/ROOT/images/advanced_configuration/webui/sidebar/setup_sidebar.py new file mode 100644 index 0000000..e2ed7c2 --- /dev/null +++ b/docs/modules/ROOT/images/advanced_configuration/webui/sidebar/setup_sidebar.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +"""Capture the default sidebar navigation style.""" + +import time + +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait + + +def _wait_for_sidebar_navigation(driver, timeout=30): + WebDriverWait(driver, timeout).until( + lambda d: bool(d.find_element(By.TAG_NAME, "body").get_attribute("loaded-dashboard")) + ) + + driver.find_element(By.ID, "sidebar-toggler-button").click() + + WebDriverWait(driver, timeout).until( + lambda d: d.find_element(By.ID, "mainnav").is_displayed() + ) + + driver.find_element(By.CSS_SELECTOR, "#mainnav .stick-toggle").click() + + def ready(d): + try: + my_servers = d.find_element( + By.XPATH, + '//*[@id="mainnav"]//a[contains(normalize-space(), "My Servers")]', + ) + my_containers = d.find_element( + By.XPATH, + '//*[@id="mainnav"]//a[contains(normalize-space(), "My Containers")]', + ) + disk_space = d.find_element(By.CSS_SELECTOR, '[title="Check disk space"]') + ssh = d.find_element(By.CSS_SELECTOR, '[title="Setup easy SSH"]') + except Exception: + return False + return all(element.is_displayed() for element in (my_servers, my_containers, disk_space, ssh)) + + WebDriverWait(driver, timeout).until(ready) + + +def run(driver): + _wait_for_sidebar_navigation(driver) + time.sleep(0.2) diff --git a/docs/modules/ROOT/images/advanced_configuration/webui/sidebar/sidebar.png b/docs/modules/ROOT/images/advanced_configuration/webui/sidebar/sidebar.png new file mode 100644 index 0000000..61fbac3 Binary files /dev/null and b/docs/modules/ROOT/images/advanced_configuration/webui/sidebar/sidebar.png differ diff --git a/docs/modules/ROOT/images/advanced_configuration/webui/topbar/.gitignore b/docs/modules/ROOT/images/advanced_configuration/webui/topbar/.gitignore new file mode 100644 index 0000000..431dbf1 --- /dev/null +++ b/docs/modules/ROOT/images/advanced_configuration/webui/topbar/.gitignore @@ -0,0 +1,2 @@ +custom-webui/ +__pycache__/ diff --git a/docs/modules/ROOT/images/advanced_configuration/webui/topbar/Makefile b/docs/modules/ROOT/images/advanced_configuration/webui/topbar/Makefile new file mode 100644 index 0000000..b474e2a --- /dev/null +++ b/docs/modules/ROOT/images/advanced_configuration/webui/topbar/Makefile @@ -0,0 +1,2 @@ +CONFIGDIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) +include ../../../screenshots.mk diff --git a/docs/modules/ROOT/images/advanced_configuration/webui/topbar/config.yaml b/docs/modules/ROOT/images/advanced_configuration/webui/topbar/config.yaml new file mode 100644 index 0000000..4f1ceb8 --- /dev/null +++ b/docs/modules/ROOT/images/advanced_configuration/webui/topbar/config.yaml @@ -0,0 +1,33 @@ +--- +listenAddressSingleHTTPFrontend: 0.0.0.0:11337 + +logLevel: "WARN" +checkForUpdates: false +showFooter: false + +sectionNavigationStyle: topbar + +actions: + - title: Ping host + icon: ping + shell: ping -c 1 127.0.0.1 + - title: Restart Docker Container + icon: restart + shell: echo restart + - title: Delete old backups + icon: ashtonished + shell: echo delete + - title: Server ping + icon: ping + shell: echo ping + - title: Container list + icon: disk + shell: echo list + +dashboards: + - title: My Servers + contents: + - title: Server ping + - title: My Containers + contents: + - title: Container list diff --git a/docs/modules/ROOT/images/advanced_configuration/webui/topbar/screenshots.ini b/docs/modules/ROOT/images/advanced_configuration/webui/topbar/screenshots.ini new file mode 100644 index 0000000..aa51d4b --- /dev/null +++ b/docs/modules/ROOT/images/advanced_configuration/webui/topbar/screenshots.ini @@ -0,0 +1,11 @@ +[DEFAULT] +base_url = http://localhost:11337/ +dir = . +width = 980 +height = 420 +post_script_sleep = 0.5 + +[topbar] +url = . +name = topbar +script = setup_topbar.py diff --git a/docs/modules/ROOT/images/advanced_configuration/webui/topbar/setup_topbar.py b/docs/modules/ROOT/images/advanced_configuration/webui/topbar/setup_topbar.py new file mode 100644 index 0000000..95a0c68 --- /dev/null +++ b/docs/modules/ROOT/images/advanced_configuration/webui/topbar/setup_topbar.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +"""Capture the topbar section navigation style.""" + +import time + +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait + + +def _wait_for_topbar_navigation(driver, timeout=30): + WebDriverWait(driver, timeout).until( + lambda d: bool(d.find_element(By.TAG_NAME, "body").get_attribute("loaded-dashboard")) + ) + + def ready(d): + try: + topbar = d.find_element(By.CSS_SELECTOR, "nav.topbar") + my_servers = d.find_element( + By.XPATH, + '//nav[contains(@class, "topbar")]//a[contains(normalize-space(), "My Servers")]', + ) + my_containers = d.find_element( + By.XPATH, + '//nav[contains(@class, "topbar")]//a[contains(normalize-space(), "My Containers")]', + ) + diagnostics = d.find_element( + By.XPATH, + '//nav[contains(@class, "topbar")]//a[contains(normalize-space(), "Diagnostics")]', + ) + logs = d.find_element( + By.XPATH, + '//nav[contains(@class, "topbar")]//a[contains(normalize-space(), "Logs")]', + ) + ping_host = d.find_element(By.CSS_SELECTOR, '[title="Ping host"]') + restart = d.find_element(By.CSS_SELECTOR, '[title="Restart Docker Container"]') + delete_backups = d.find_element(By.CSS_SELECTOR, '[title="Delete old backups"]') + except Exception: + return False + + return topbar.is_displayed() and all( + element.is_displayed() + for element in ( + my_servers, + my_containers, + diagnostics, + logs, + ping_host, + restart, + delete_backups, + ) + ) + + WebDriverWait(driver, timeout).until(ready) + + +def run(driver): + _wait_for_topbar_navigation(driver) + time.sleep(0.2) diff --git a/docs/modules/ROOT/images/advanced_configuration/webui/topbar/topbar.png b/docs/modules/ROOT/images/advanced_configuration/webui/topbar/topbar.png new file mode 100644 index 0000000..cf2edba Binary files /dev/null and b/docs/modules/ROOT/images/advanced_configuration/webui/topbar/topbar.png differ diff --git a/docs/modules/ROOT/images/advanced_configuration/webui/with-navigation/.gitignore b/docs/modules/ROOT/images/advanced_configuration/webui/with-navigation/.gitignore new file mode 100644 index 0000000..431dbf1 --- /dev/null +++ b/docs/modules/ROOT/images/advanced_configuration/webui/with-navigation/.gitignore @@ -0,0 +1,2 @@ +custom-webui/ +__pycache__/ diff --git a/docs/modules/ROOT/images/advanced_configuration/webui/with-navigation/Makefile b/docs/modules/ROOT/images/advanced_configuration/webui/with-navigation/Makefile new file mode 100644 index 0000000..b474e2a --- /dev/null +++ b/docs/modules/ROOT/images/advanced_configuration/webui/with-navigation/Makefile @@ -0,0 +1,2 @@ +CONFIGDIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) +include ../../../screenshots.mk diff --git a/docs/modules/ROOT/images/advanced_configuration/webui/with-navigation/config.yaml b/docs/modules/ROOT/images/advanced_configuration/webui/with-navigation/config.yaml new file mode 100644 index 0000000..ba7a8d0 --- /dev/null +++ b/docs/modules/ROOT/images/advanced_configuration/webui/with-navigation/config.yaml @@ -0,0 +1,47 @@ +--- +listenAddressSingleHTTPFrontend: 0.0.0.0:11337 + +logLevel: "WARN" +checkForUpdates: false +showFooter: false + +actions: + - title: Ping the Internet + icon: ping + shell: ping -c 1 127.0.0.1 + - title: Check disk space + icon: disk + shell: df -h + - title: check dmesg logs + icon: logs + shell: dmesg | tail -n 1 + - title: date + icon: clock + shell: date + - title: Run backup script + icon: backup + shell: echo backup + - title: Ping host + icon: ping + shell: ping -c 1 127.0.0.1 + - title: Restart Docker Container + icon: restart + shell: echo restart + - title: Delete old backups + icon: ashtonished + shell: echo delete + - title: Get OliveTin Theme + icon: theme + shell: echo theme + - title: Setup easy SSH + icon: ssh + shell: echo ssh + - title: Restart httpd on server1 + icon: restart + shell: echo restart httpd + - title: Toggle GPIO light + icon: light + shell: echo toggle + - title: Run Automation Playbook + icon: robot + shell: echo ansible diff --git a/docs/modules/ROOT/images/advanced_configuration/webui/with-navigation/screenshots.ini b/docs/modules/ROOT/images/advanced_configuration/webui/with-navigation/screenshots.ini new file mode 100644 index 0000000..44b0063 --- /dev/null +++ b/docs/modules/ROOT/images/advanced_configuration/webui/with-navigation/screenshots.ini @@ -0,0 +1,11 @@ +[DEFAULT] +base_url = http://localhost:11337/ +dir = . +width = 1180 +height = 760 +post_script_sleep = 0.5 + +[with-navigation] +url = . +name = with-navigation +script = setup_with_navigation.py diff --git a/docs/modules/ROOT/images/advanced_configuration/webui/with-navigation/setup_with_navigation.py b/docs/modules/ROOT/images/advanced_configuration/webui/with-navigation/setup_with_navigation.py new file mode 100644 index 0000000..2daf9a5 --- /dev/null +++ b/docs/modules/ROOT/images/advanced_configuration/webui/with-navigation/setup_with_navigation.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +"""Capture the default Actions view with sidebar navigation visible.""" + +import time + +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait + + +def run(driver): + WebDriverWait(driver, 15).until( + lambda d: bool(d.find_element(By.TAG_NAME, "body").get_attribute("loaded-dashboard")) + ) + WebDriverWait(driver, 15).until( + lambda d: len(d.find_elements(By.CSS_SELECTOR, ".action-button button")) >= 8 + ) + + driver.find_element(By.ID, "sidebar-toggler-button").click() + + WebDriverWait(driver, 15).until( + lambda d: d.find_element(By.ID, "mainnav").is_displayed() + ) + time.sleep(0.2) diff --git a/docs/modules/ROOT/images/advanced_configuration/webui/with-navigation/with-navigation.png b/docs/modules/ROOT/images/advanced_configuration/webui/with-navigation/with-navigation.png new file mode 100644 index 0000000..4e89e0c Binary files /dev/null and b/docs/modules/ROOT/images/advanced_configuration/webui/with-navigation/with-navigation.png differ diff --git a/docs/modules/ROOT/images/args-multiline-text.png b/docs/modules/ROOT/images/args-multiline-text.png deleted file mode 100644 index 02ac610..0000000 Binary files a/docs/modules/ROOT/images/args-multiline-text.png and /dev/null differ diff --git a/docs/modules/ROOT/images/args/confirmation/.gitignore b/docs/modules/ROOT/images/args/confirmation/.gitignore new file mode 100644 index 0000000..431dbf1 --- /dev/null +++ b/docs/modules/ROOT/images/args/confirmation/.gitignore @@ -0,0 +1,2 @@ +custom-webui/ +__pycache__/ diff --git a/docs/modules/ROOT/images/args/confirmation/Makefile b/docs/modules/ROOT/images/args/confirmation/Makefile new file mode 100644 index 0000000..720e66a --- /dev/null +++ b/docs/modules/ROOT/images/args/confirmation/Makefile @@ -0,0 +1,2 @@ +CONFIGDIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) +include ../../screenshots.mk diff --git a/docs/modules/ROOT/images/args/confirmation/config.yaml b/docs/modules/ROOT/images/args/confirmation/config.yaml new file mode 100644 index 0000000..85fa6a6 --- /dev/null +++ b/docs/modules/ROOT/images/args/confirmation/config.yaml @@ -0,0 +1,14 @@ +--- +listenAddressSingleHTTPFrontend: 0.0.0.0:11337 + +logLevel: "WARN" +checkForUpdates: false +showFooter: false + +actions: + - title: Delete old backups + icon: ashtonished + shell: echo "Deleted old backups" + arguments: + - type: confirmation + title: Are you sure?! diff --git a/docs/modules/ROOT/images/args/confirmation/confirmation.png b/docs/modules/ROOT/images/args/confirmation/confirmation.png new file mode 100644 index 0000000..fbdd628 Binary files /dev/null and b/docs/modules/ROOT/images/args/confirmation/confirmation.png differ diff --git a/docs/modules/ROOT/images/args/confirmation/screenshots.ini b/docs/modules/ROOT/images/args/confirmation/screenshots.ini new file mode 100644 index 0000000..7c63be3 --- /dev/null +++ b/docs/modules/ROOT/images/args/confirmation/screenshots.ini @@ -0,0 +1,11 @@ +[DEFAULT] +base_url = http://localhost:11337/ +dir = . +width = 800 +height = 420 +post_script_sleep = 0.5 + +[confirmation] +url = . +name = confirmation +script = setup_confirmation.py diff --git a/docs/modules/ROOT/images/args/confirmation/setup_confirmation.py b/docs/modules/ROOT/images/args/confirmation/setup_confirmation.py new file mode 100644 index 0000000..e7b5698 --- /dev/null +++ b/docs/modules/ROOT/images/args/confirmation/setup_confirmation.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +"""Prepare the confirmation argument screenshot.""" + +import time + +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait + + +def _wait_for_body_attr(driver, attr, timeout=15): + WebDriverWait(driver, timeout).until( + lambda d: bool(d.find_element(By.TAG_NAME, "body").get_attribute(attr)) + ) + + +def run(driver): + _wait_for_body_attr(driver, "loaded-dashboard") + + driver.find_element(By.CSS_SELECTOR, '[title="Delete old backups"]').click() + _wait_for_body_attr(driver, "loaded-argument-form") + + WebDriverWait(driver, 15).until( + lambda d: not d.find_element(By.CSS_SELECTOR, 'button[name="start"]').is_enabled() + ) + time.sleep(0.2) diff --git a/docs/modules/ROOT/images/args/input/args1.png b/docs/modules/ROOT/images/args/input/args1.png index 5400651..fb0412e 100644 Binary files a/docs/modules/ROOT/images/args/input/args1.png and b/docs/modules/ROOT/images/args/input/args1.png differ diff --git a/docs/modules/ROOT/images/args/input/args2.png b/docs/modules/ROOT/images/args/input/args2.png index e101daf..0114a07 100644 Binary files a/docs/modules/ROOT/images/args/input/args2.png and b/docs/modules/ROOT/images/args/input/args2.png differ diff --git a/docs/modules/ROOT/images/args/input/args3.png b/docs/modules/ROOT/images/args/input/args3.png index 203171a..d4ffd93 100644 Binary files a/docs/modules/ROOT/images/args/input/args3.png and b/docs/modules/ROOT/images/args/input/args3.png differ diff --git a/docs/modules/ROOT/images/args/input/screenshots.ini b/docs/modules/ROOT/images/args/input/screenshots.ini index 5006aaa..24898cf 100644 --- a/docs/modules/ROOT/images/args/input/screenshots.ini +++ b/docs/modules/ROOT/images/args/input/screenshots.ini @@ -19,3 +19,4 @@ script = setup_args2.py url = . name = args3 script = setup_args3.py +height = 560 diff --git a/docs/modules/ROOT/images/args/input/setup_args3.py b/docs/modules/ROOT/images/args/input/setup_args3.py index 9eda93e..bf358bb 100644 --- a/docs/modules/ROOT/images/args/input/setup_args3.py +++ b/docs/modules/ROOT/images/args/input/setup_args3.py @@ -65,5 +65,15 @@ def run(driver): _wait_for_logs_page(driver) _wait_for_execution_complete(driver) - + WebDriverWait(driver, 15).until( + lambda d: "Hello World" + in d.execute_script( + """ + if (!window.terminal || !window.terminal.getBufferAsString) { + return ''; + } + return window.terminal.getBufferAsString(); + """ + ) + ) time.sleep(0.2) diff --git a/docs/modules/ROOT/images/args/textarea/.gitignore b/docs/modules/ROOT/images/args/textarea/.gitignore new file mode 100644 index 0000000..431dbf1 --- /dev/null +++ b/docs/modules/ROOT/images/args/textarea/.gitignore @@ -0,0 +1,2 @@ +custom-webui/ +__pycache__/ diff --git a/docs/modules/ROOT/images/args/textarea/Makefile b/docs/modules/ROOT/images/args/textarea/Makefile new file mode 100644 index 0000000..720e66a --- /dev/null +++ b/docs/modules/ROOT/images/args/textarea/Makefile @@ -0,0 +1,2 @@ +CONFIGDIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) +include ../../screenshots.mk diff --git a/docs/modules/ROOT/images/args/textarea/config.yaml b/docs/modules/ROOT/images/args/textarea/config.yaml new file mode 100644 index 0000000..a9a28aa --- /dev/null +++ b/docs/modules/ROOT/images/args/textarea/config.yaml @@ -0,0 +1,17 @@ +--- +listenAddressSingleHTTPFrontend: 0.0.0.0:11337 + +logLevel: "WARN" +checkForUpdates: false +showFooter: false + +actions: + - title: Save text to file + shell: 'echo "{{ content }}" > file' + arguments: + - type: raw_string_multiline + name: content + title: Content + default: | + Line one of the message + Line two of the message diff --git a/docs/modules/ROOT/images/args/textarea/multiline-text.png b/docs/modules/ROOT/images/args/textarea/multiline-text.png new file mode 100644 index 0000000..ac26cff Binary files /dev/null and b/docs/modules/ROOT/images/args/textarea/multiline-text.png differ diff --git a/docs/modules/ROOT/images/args/textarea/screenshots.ini b/docs/modules/ROOT/images/args/textarea/screenshots.ini new file mode 100644 index 0000000..853dad4 --- /dev/null +++ b/docs/modules/ROOT/images/args/textarea/screenshots.ini @@ -0,0 +1,11 @@ +[DEFAULT] +base_url = http://localhost:11337/ +dir = . +width = 800 +height = 520 +post_script_sleep = 0.5 + +[multiline-text] +url = . +name = multiline-text +script = setup_textarea.py diff --git a/docs/modules/ROOT/images/args/textarea/setup_textarea.py b/docs/modules/ROOT/images/args/textarea/setup_textarea.py new file mode 100644 index 0000000..eee915a --- /dev/null +++ b/docs/modules/ROOT/images/args/textarea/setup_textarea.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +"""Prepare the multiline textarea argument screenshot.""" + +import time + +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait + + +def _wait_for_body_attr(driver, attr, timeout=15): + WebDriverWait(driver, timeout).until( + lambda d: bool(d.find_element(By.TAG_NAME, "body").get_attribute(attr)) + ) + + +def run(driver): + _wait_for_body_attr(driver, "loaded-dashboard") + + driver.find_element(By.CSS_SELECTOR, '[title="Save text to file"]').click() + + WebDriverWait(driver, 15).until( + lambda d: len( + d.find_elements(By.CSS_SELECTOR, "#argument-popup textarea#content") + ) + >= 1 + ) + time.sleep(0.2) diff --git a/docs/modules/ROOT/images/dashboards/fieldsets/.gitignore b/docs/modules/ROOT/images/dashboards/fieldsets/.gitignore new file mode 100644 index 0000000..431dbf1 --- /dev/null +++ b/docs/modules/ROOT/images/dashboards/fieldsets/.gitignore @@ -0,0 +1,2 @@ +custom-webui/ +__pycache__/ diff --git a/docs/modules/ROOT/images/dashboards/fieldsets/Makefile b/docs/modules/ROOT/images/dashboards/fieldsets/Makefile new file mode 100644 index 0000000..720e66a --- /dev/null +++ b/docs/modules/ROOT/images/dashboards/fieldsets/Makefile @@ -0,0 +1,2 @@ +CONFIGDIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) +include ../../screenshots.mk diff --git a/docs/modules/ROOT/images/dashboards/fieldsets/config.yaml b/docs/modules/ROOT/images/dashboards/fieldsets/config.yaml new file mode 100644 index 0000000..6ae2be3 --- /dev/null +++ b/docs/modules/ROOT/images/dashboards/fieldsets/config.yaml @@ -0,0 +1,25 @@ +--- +listenAddressSingleHTTPFrontend: 0.0.0.0:11337 + +logLevel: "WARN" +checkForUpdates: false +showFooter: false + +actions: + - title: Placeholder 1 + shell: echo "placeholder 1" + - title: Placeholder 2 + shell: echo "placeholder 2" + +dashboards: + - title: My First Dashboard + contents: + - title: Fieldset 1 + type: fieldset + contents: + - title: Folder 1 + contents: + - title: Placeholder 1 + - title: Folder 2 + contents: + - title: Placeholder 2 diff --git a/docs/modules/ROOT/images/dashboards/fieldsets/fieldset.png b/docs/modules/ROOT/images/dashboards/fieldsets/fieldset.png new file mode 100644 index 0000000..4aa3b68 Binary files /dev/null and b/docs/modules/ROOT/images/dashboards/fieldsets/fieldset.png differ diff --git a/docs/modules/ROOT/images/dashboards/fieldsets/screenshots.ini b/docs/modules/ROOT/images/dashboards/fieldsets/screenshots.ini new file mode 100644 index 0000000..713e135 --- /dev/null +++ b/docs/modules/ROOT/images/dashboards/fieldsets/screenshots.ini @@ -0,0 +1,11 @@ +[DEFAULT] +base_url = http://localhost:11337/ +dir = . +width = 980 +height = 400 +post_script_sleep = 0.5 + +[fieldset] +url = /dashboards/My%%20First%%20Dashboard +name = fieldset +script = setup_fieldset.py diff --git a/docs/modules/ROOT/images/dashboards/fieldsets/setup_fieldset.py b/docs/modules/ROOT/images/dashboards/fieldsets/setup_fieldset.py new file mode 100644 index 0000000..9feeec1 --- /dev/null +++ b/docs/modules/ROOT/images/dashboards/fieldsets/setup_fieldset.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +"""Open My First Dashboard from dashboards/2-fieldsets.adoc.""" + +import time + +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait + + +def _wait_for_fieldset_dashboard(driver, timeout=30): + WebDriverWait(driver, timeout).until( + lambda d: d.find_element(By.TAG_NAME, "body").get_attribute("loaded-dashboard") + == "My First Dashboard" + ) + + def ready(d): + try: + folder1 = d.find_element( + By.XPATH, + '//button[contains(@class, "directory-button")]//span[contains(@class, "title") and text()="Folder 1"]', + ) + folder2 = d.find_element( + By.XPATH, + '//button[contains(@class, "directory-button")]//span[contains(@class, "title") and text()="Folder 2"]', + ) + except Exception: + return False + return all(element.is_displayed() for element in (folder1, folder2)) + + WebDriverWait(driver, timeout).until(ready) + + +def run(driver): + _wait_for_fieldset_dashboard(driver) + time.sleep(0.2) diff --git a/docs/modules/ROOT/images/dashboards/folders/.gitignore b/docs/modules/ROOT/images/dashboards/folders/.gitignore new file mode 100644 index 0000000..431dbf1 --- /dev/null +++ b/docs/modules/ROOT/images/dashboards/folders/.gitignore @@ -0,0 +1,2 @@ +custom-webui/ +__pycache__/ diff --git a/docs/modules/ROOT/images/dashboards/folders/Makefile b/docs/modules/ROOT/images/dashboards/folders/Makefile new file mode 100644 index 0000000..720e66a --- /dev/null +++ b/docs/modules/ROOT/images/dashboards/folders/Makefile @@ -0,0 +1,2 @@ +CONFIGDIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) +include ../../screenshots.mk diff --git a/docs/modules/ROOT/images/dashboards/folders/config.yaml b/docs/modules/ROOT/images/dashboards/folders/config.yaml new file mode 100644 index 0000000..945f57d --- /dev/null +++ b/docs/modules/ROOT/images/dashboards/folders/config.yaml @@ -0,0 +1,33 @@ +--- +listenAddressSingleHTTPFrontend: 0.0.0.0:11337 + +logLevel: "WARN" +checkForUpdates: false +showFooter: false + +actions: + - title: Action 1 + shell: echo "action1" + - title: Action 2 + shell: echo "action2" + - title: Action 3 + shell: echo "action3" + - title: Action 4 + shell: echo "action4" + +dashboards: + - title: My First Dashboard + contents: + - title: Fieldset 1 + type: fieldset + contents: + - title: Folder 1 + contents: + - title: Action 1 + - title: Action 2 + - title: Subfolder 2 + contents: + - title: Action 3 + - title: Folder 2 + contents: + - title: Action 4 diff --git a/docs/modules/ROOT/images/dashboards/folders/folders.png b/docs/modules/ROOT/images/dashboards/folders/folders.png new file mode 100644 index 0000000..d15c263 Binary files /dev/null and b/docs/modules/ROOT/images/dashboards/folders/folders.png differ diff --git a/docs/modules/ROOT/images/dashboards/folders/screenshots.ini b/docs/modules/ROOT/images/dashboards/folders/screenshots.ini new file mode 100644 index 0000000..ad856b4 --- /dev/null +++ b/docs/modules/ROOT/images/dashboards/folders/screenshots.ini @@ -0,0 +1,11 @@ +[DEFAULT] +base_url = http://localhost:11337/ +dir = . +width = 980 +height = 640 +post_script_sleep = 0.5 + +[folders] +url = /dashboards/Folder%%201 +name = folders +script = setup_folders.py diff --git a/docs/modules/ROOT/images/dashboards/folders/setup_folders.py b/docs/modules/ROOT/images/dashboards/folders/setup_folders.py new file mode 100644 index 0000000..c6d4875 --- /dev/null +++ b/docs/modules/ROOT/images/dashboards/folders/setup_folders.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +"""Open Folder 1 from dashboards/3-folders.adoc.""" + +import time + +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait + + +def _wait_for_folder_dashboard(driver, timeout=30): + WebDriverWait(driver, timeout).until( + lambda d: d.find_element(By.TAG_NAME, "body").get_attribute("loaded-dashboard") + == "Folder 1" + ) + + def ready(d): + try: + action1 = d.find_element(By.CSS_SELECTOR, '[title="Action 1"]') + action2 = d.find_element(By.CSS_SELECTOR, '[title="Action 2"]') + subfolder = d.find_element( + By.XPATH, + '//button[contains(@class, "directory-button")]//span[contains(@class, "title") and text()="Subfolder 2"]', + ) + except Exception: + return False + return all( + element.is_displayed() + for element in (action1, action2, subfolder) + ) + + WebDriverWait(driver, timeout).until(ready) + + +def run(driver): + _wait_for_folder_dashboard(driver) + time.sleep(0.2) diff --git a/docs/modules/ROOT/images/dashboards/intro/preview.png b/docs/modules/ROOT/images/dashboards/intro/preview.png index 77e9158..7bb4e2f 100644 Binary files a/docs/modules/ROOT/images/dashboards/intro/preview.png and b/docs/modules/ROOT/images/dashboards/intro/preview.png differ diff --git a/docs/modules/ROOT/images/dashboards/intro/screenshots.ini b/docs/modules/ROOT/images/dashboards/intro/screenshots.ini index 048b585..254006a 100644 --- a/docs/modules/ROOT/images/dashboards/intro/screenshots.ini +++ b/docs/modules/ROOT/images/dashboards/intro/screenshots.ini @@ -6,6 +6,6 @@ height = 720 post_script_sleep = 0.5 [preview] -url = /dashboards/My%20Servers +url = /dashboards/My%%20Servers name = preview script = setup_preview.py diff --git a/docs/modules/ROOT/images/defaultUiHideNav.png b/docs/modules/ROOT/images/defaultUiHideNav.png deleted file mode 100644 index 07eaf35..0000000 Binary files a/docs/modules/ROOT/images/defaultUiHideNav.png and /dev/null differ diff --git a/docs/modules/ROOT/images/defaultUiWithNav.png b/docs/modules/ROOT/images/defaultUiWithNav.png deleted file mode 100644 index 16d1a49..0000000 Binary files a/docs/modules/ROOT/images/defaultUiWithNav.png and /dev/null differ diff --git a/docs/modules/ROOT/images/diagnostics.png b/docs/modules/ROOT/images/diagnostics.png deleted file mode 100644 index 4b216e7..0000000 Binary files a/docs/modules/ROOT/images/diagnostics.png and /dev/null differ diff --git a/docs/modules/ROOT/images/fieldset.png b/docs/modules/ROOT/images/fieldset.png deleted file mode 100644 index cd9c0a5..0000000 Binary files a/docs/modules/ROOT/images/fieldset.png and /dev/null differ diff --git a/docs/modules/ROOT/images/folders.png b/docs/modules/ROOT/images/folders.png deleted file mode 100644 index 7a36ff9..0000000 Binary files a/docs/modules/ROOT/images/folders.png and /dev/null differ diff --git a/docs/modules/ROOT/images/gitops.png b/docs/modules/ROOT/images/gitops.png deleted file mode 100644 index 4288de6..0000000 Binary files a/docs/modules/ROOT/images/gitops.png and /dev/null differ diff --git a/docs/modules/ROOT/images/hacs-custom-repo.png b/docs/modules/ROOT/images/integrations/hass/hacs-custom-repo.png similarity index 100% rename from docs/modules/ROOT/images/hacs-custom-repo.png rename to docs/modules/ROOT/images/integrations/hass/hacs-custom-repo.png diff --git a/docs/modules/ROOT/images/hacs-download.png b/docs/modules/ROOT/images/integrations/hass/hacs-download.png similarity index 100% rename from docs/modules/ROOT/images/hacs-download.png rename to docs/modules/ROOT/images/integrations/hass/hacs-download.png diff --git a/docs/modules/ROOT/images/hacs-dropdown.png b/docs/modules/ROOT/images/integrations/hass/hacs-dropdown.png similarity index 100% rename from docs/modules/ROOT/images/hacs-dropdown.png rename to docs/modules/ROOT/images/integrations/hass/hacs-dropdown.png diff --git a/docs/modules/ROOT/images/hacs-search.png b/docs/modules/ROOT/images/integrations/hass/hacs-search.png similarity index 100% rename from docs/modules/ROOT/images/hacs-search.png rename to docs/modules/ROOT/images/integrations/hass/hacs-search.png diff --git a/docs/modules/ROOT/images/hass-add-integration.png b/docs/modules/ROOT/images/integrations/hass/hass-add-integration.png similarity index 100% rename from docs/modules/ROOT/images/hass-add-integration.png rename to docs/modules/ROOT/images/integrations/hass/hass-add-integration.png diff --git a/docs/modules/ROOT/images/hass-buttons.png b/docs/modules/ROOT/images/integrations/hass/hass-buttons.png similarity index 100% rename from docs/modules/ROOT/images/hass-buttons.png rename to docs/modules/ROOT/images/integrations/hass/hass-buttons.png diff --git a/docs/modules/ROOT/images/hass-configure-integration.png b/docs/modules/ROOT/images/integrations/hass/hass-configure-integration.png similarity index 100% rename from docs/modules/ROOT/images/hass-configure-integration.png rename to docs/modules/ROOT/images/integrations/hass/hass-configure-integration.png diff --git a/docs/modules/ROOT/images/hass-devices-and-services.png b/docs/modules/ROOT/images/integrations/hass/hass-devices-and-services.png similarity index 100% rename from docs/modules/ROOT/images/hass-devices-and-services.png rename to docs/modules/ROOT/images/integrations/hass/hass-devices-and-services.png diff --git a/docs/modules/ROOT/images/hassButtonSetup.png b/docs/modules/ROOT/images/integrations/hass/hassButtonSetup.png similarity index 100% rename from docs/modules/ROOT/images/hassButtonSetup.png rename to docs/modules/ROOT/images/integrations/hass/hassButtonSetup.png diff --git a/docs/modules/ROOT/images/hassConfigYaml.png b/docs/modules/ROOT/images/integrations/hass/hassConfigYaml.png similarity index 100% rename from docs/modules/ROOT/images/hassConfigYaml.png rename to docs/modules/ROOT/images/integrations/hass/hassConfigYaml.png diff --git a/docs/modules/ROOT/images/hassFileEditor.png b/docs/modules/ROOT/images/integrations/hass/hassFileEditor.png similarity index 100% rename from docs/modules/ROOT/images/hassFileEditor.png rename to docs/modules/ROOT/images/integrations/hass/hassFileEditor.png diff --git a/docs/modules/ROOT/images/hassFileEditorConfig.png b/docs/modules/ROOT/images/integrations/hass/hassFileEditorConfig.png similarity index 100% rename from docs/modules/ROOT/images/hassFileEditorConfig.png rename to docs/modules/ROOT/images/integrations/hass/hassFileEditorConfig.png diff --git a/docs/modules/ROOT/images/maxRate.png b/docs/modules/ROOT/images/maxRate.png deleted file mode 100644 index 7721e2c..0000000 Binary files a/docs/modules/ROOT/images/maxRate.png and /dev/null differ diff --git a/docs/modules/ROOT/images/page-title.png b/docs/modules/ROOT/images/page-title.png deleted file mode 100644 index 12af3be..0000000 Binary files a/docs/modules/ROOT/images/page-title.png and /dev/null differ diff --git a/docs/modules/ROOT/images/popupOutputOnly.png b/docs/modules/ROOT/images/popupOutputOnly.png deleted file mode 100644 index 69feb03..0000000 Binary files a/docs/modules/ROOT/images/popupOutputOnly.png and /dev/null differ diff --git a/docs/modules/ROOT/images/sidebar.png b/docs/modules/ROOT/images/sidebar.png deleted file mode 100644 index bcae4ac..0000000 Binary files a/docs/modules/ROOT/images/sidebar.png and /dev/null differ diff --git a/docs/modules/ROOT/images/solutions/on-git-push/.gitignore b/docs/modules/ROOT/images/solutions/on-git-push/.gitignore new file mode 100644 index 0000000..431dbf1 --- /dev/null +++ b/docs/modules/ROOT/images/solutions/on-git-push/.gitignore @@ -0,0 +1,2 @@ +custom-webui/ +__pycache__/ diff --git a/docs/modules/ROOT/images/solutions/on-git-push/Makefile b/docs/modules/ROOT/images/solutions/on-git-push/Makefile new file mode 100644 index 0000000..720e66a --- /dev/null +++ b/docs/modules/ROOT/images/solutions/on-git-push/Makefile @@ -0,0 +1,2 @@ +CONFIGDIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) +include ../../screenshots.mk diff --git a/docs/modules/ROOT/images/solutions/on-git-push/config.yaml b/docs/modules/ROOT/images/solutions/on-git-push/config.yaml new file mode 100644 index 0000000..97ed526 --- /dev/null +++ b/docs/modules/ROOT/images/solutions/on-git-push/config.yaml @@ -0,0 +1,42 @@ +--- +listenAddressSingleHTTPFrontend: 0.0.0.0:11337 + +logLevel: "WARN" +checkForUpdates: false +showFooter: false + +actions: + - title: ServerConfiguration Ansible + id: ServerConfiguration_Ansible + icon: + shell: echo "ansible configuration" + + - title: ServerConfiguration Flux + id: ServerConfiguration_Flux + icon: + shell: echo "flux configuration" + + - title: ServerConfiguration Prom + id: ServerConfiguration_Prom + icon: + shell: echo "prometheus configuration" + + - title: Run on Git Push + id: gitops + icon: + shell: | + echo "You just pushed commit $COMMIT to git, running action..." + date + arguments: + - name: commit + type: ascii + +dashboards: + - title: GitOps + contents: + - title: ServerConfiguration + type: fieldset + contents: + - title: ServerConfiguration Ansible + - title: ServerConfiguration Flux + - title: ServerConfiguration Prom diff --git a/docs/modules/ROOT/images/solutions/on-git-push/gitops.png b/docs/modules/ROOT/images/solutions/on-git-push/gitops.png new file mode 100644 index 0000000..a487323 Binary files /dev/null and b/docs/modules/ROOT/images/solutions/on-git-push/gitops.png differ diff --git a/docs/modules/ROOT/images/solutions/on-git-push/screenshots.ini b/docs/modules/ROOT/images/solutions/on-git-push/screenshots.ini new file mode 100644 index 0000000..c44bf0a --- /dev/null +++ b/docs/modules/ROOT/images/solutions/on-git-push/screenshots.ini @@ -0,0 +1,11 @@ +[DEFAULT] +base_url = http://localhost:11337/ +dir = . +width = 980 +height = 520 +post_script_sleep = 0.5 + +[gitops] +url = /dashboards/GitOps +name = gitops +script = setup_gitops.py diff --git a/docs/modules/ROOT/images/solutions/on-git-push/setup_gitops.py b/docs/modules/ROOT/images/solutions/on-git-push/setup_gitops.py new file mode 100644 index 0000000..fc7f420 --- /dev/null +++ b/docs/modules/ROOT/images/solutions/on-git-push/setup_gitops.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +"""Open the GitOps dashboard from solutions/on-git-push/index.adoc.""" + +import time + +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait + + +def _wait_for_gitops_dashboard(driver, timeout=30): + WebDriverWait(driver, timeout).until( + lambda d: d.find_element(By.TAG_NAME, "body").get_attribute("loaded-dashboard") + == "GitOps" + ) + + def ready(d): + required_titles = [ + "ServerConfiguration Ansible", + "ServerConfiguration Flux", + "ServerConfiguration Prom", + ] + for title in required_titles: + try: + button = d.find_element(By.CSS_SELECTOR, f'[title="{title}"]') + except Exception: + return False + if not button.is_displayed(): + return False + return True + + WebDriverWait(driver, timeout).until(ready) + + +def run(driver): + _wait_for_gitops_dashboard(driver) + time.sleep(0.2) diff --git a/docs/modules/ROOT/images/topbar.png b/docs/modules/ROOT/images/topbar.png deleted file mode 100644 index 347b465..0000000 Binary files a/docs/modules/ROOT/images/topbar.png and /dev/null differ diff --git a/docs/modules/ROOT/pages/action_customization/ratelimiting.adoc b/docs/modules/ROOT/pages/action_customization/ratelimiting.adoc index a229d0d..f3380cb 100644 --- a/docs/modules/ROOT/pages/action_customization/ratelimiting.adoc +++ b/docs/modules/ROOT/pages/action_customization/ratelimiting.adoc @@ -19,12 +19,10 @@ actions: If you try to execute `date` more than 3 times in 5 minutes, you will get a log message in the UI that looks like this; -image::maxRate.png[] +image::action_customization/max-rate/maxRate.png[] Additionally, OliveTin will also output this to it's process log; ---- INFO Blocked from executing. This action has run 3 out of 3 allowed times in the last 5m. actionTitle="date" ---- - - diff --git a/docs/modules/ROOT/pages/advanced_configuration/diagnostics.adoc b/docs/modules/ROOT/pages/advanced_configuration/diagnostics.adoc index c12d09e..854ec96 100644 --- a/docs/modules/ROOT/pages/advanced_configuration/diagnostics.adoc +++ b/docs/modules/ROOT/pages/advanced_configuration/diagnostics.adoc @@ -4,11 +4,11 @@ OliveTin provides a built-in diagnostics page that can be used to help check how This is a screenshot of the diagnostics page, which can be accessed by clicking the "Diagnostics" link in the navigation bar: -image::diagnostics.png[] +image::advanced_configuration/diagnostics/diagnostics.png[] == Disabling Diagnostics -The diagnostics page is enabled by default, but you can disable it by using the OliveTin xref::security/acl.adoc#_acls_and_policies_global[security policy configuration], using the defaults, or via an ACL. Examples are shown below for each of these methods. +The diagnostics page is enabled by default, but you can disable it by using the OliveTin xref::security/acl.adoc#_acls_and_policies_global[security policy configuration], using the defaults, or via an ACL. Examples are shown below for each of these methods. === Disable Diagnostics for all users; diff --git a/docs/modules/ROOT/pages/advanced_configuration/webui.adoc b/docs/modules/ROOT/pages/advanced_configuration/webui.adoc index 52215bf..504d349 100644 --- a/docs/modules/ROOT/pages/advanced_configuration/webui.adoc +++ b/docs/modules/ROOT/pages/advanced_configuration/webui.adoc @@ -7,7 +7,7 @@ The OliveTin web UI is reasonably customizable - parts of the page that you don' You can customize the page title; -image::page-title.png[] +image::advanced_configuration/webui/page-title/page-title.png[] .`config.yaml` [source,yaml] @@ -21,7 +21,7 @@ pageTitle: My OliveTin Instance You can choose to hide the navigation elements in OliveTin, to present a simplified user interface. .The default user interface with the sidebar shown -image::defaultUiWithNav.png[] +image::advanced_configuration/webui/with-navigation/with-navigation.png[] To have OliveTin hide these buttons, add `showNavigation: false` to your config.yaml; @@ -36,7 +36,7 @@ actions: ---- .The same user interface, but with the sidebar hidden (`showNavigation: false`) -image::defaultUiHideNav.png[] +image::advanced_configuration/webui/hide-navigation/hide-navigation.png[] [#show-navigate-on-start-icons] == Navigate-on-start icons on action buttons @@ -65,13 +65,13 @@ showNavigateOnStartIcons: false `sectionNavigationStyle: sidebar` looks like this; -image::sidebar.png[] +image::advanced_configuration/webui/sidebar/sidebar.png[] === Topbar navigation style `sectionNavigationStyle: topbar` looks like this; -image::topbar.png[] +image::advanced_configuration/webui/topbar/topbar.png[] [#show-version-number] == Version number in the footer diff --git a/docs/modules/ROOT/pages/args/input_confirmation.adoc b/docs/modules/ROOT/pages/args/input_confirmation.adoc index 6eb0f93..1209f22 100644 --- a/docs/modules/ROOT/pages/args/input_confirmation.adoc +++ b/docs/modules/ROOT/pages/args/input_confirmation.adoc @@ -14,8 +14,6 @@ actions: title: Are you sure?! ---- -image::action-confirmation.png[] +image::args/confirmation/confirmation.png[] Notice in the webui the "start" button is disabled. - - diff --git a/docs/modules/ROOT/pages/args/input_textarea.adoc b/docs/modules/ROOT/pages/args/input_textarea.adoc index 2e06980..927144a 100644 --- a/docs/modules/ROOT/pages/args/input_textarea.adoc +++ b/docs/modules/ROOT/pages/args/input_textarea.adoc @@ -18,4 +18,4 @@ actions: This renders like this; -image::args-multiline-text.png[] +image::args/textarea/multiline-text.png[] diff --git a/docs/modules/ROOT/pages/dashboards/2-fieldsets.adoc b/docs/modules/ROOT/pages/dashboards/2-fieldsets.adoc index 270aa78..78b43f9 100644 --- a/docs/modules/ROOT/pages/dashboards/2-fieldsets.adoc +++ b/docs/modules/ROOT/pages/dashboards/2-fieldsets.adoc @@ -4,7 +4,7 @@ It is possible to group actions together in a "group", which is not a directory, but is called a "fieldset". This is an example of a fieldset that contains two xref:dashboards/3-folders.adoc[Folders]. -image::fieldset.png[] +image::dashboards/fieldsets/fieldset.png[] Fieldsets are defined under a xref:dashboards/intro.adoc[Dashboards] in your config.yaml. @@ -38,5 +38,3 @@ dashboards: - title: Start {{ server.Name }} - title: Shutdown {{ server.Name }} ---- - - diff --git a/docs/modules/ROOT/pages/dashboards/3-folders.adoc b/docs/modules/ROOT/pages/dashboards/3-folders.adoc index b4e964e..c6293ba 100644 --- a/docs/modules/ROOT/pages/dashboards/3-folders.adoc +++ b/docs/modules/ROOT/pages/dashboards/3-folders.adoc @@ -4,7 +4,7 @@ Folders (Directories) are a good way to group up actions in the same way that you would organize files on your computer into directories. -image::folders.png[] +image::dashboards/folders/folders.png[] You must first create a dashboard to use a directory, and then you "reference" actions that you want in that folder based on the action name. Anything without a "contents" property is treated @@ -48,5 +48,3 @@ dashboards: - title: Action 4 ---- - - diff --git a/docs/modules/ROOT/pages/integrations/homeassistant-integration.adoc b/docs/modules/ROOT/pages/integrations/homeassistant-integration.adoc index 816c924..814dc9e 100644 --- a/docs/modules/ROOT/pages/integrations/homeassistant-integration.adoc +++ b/docs/modules/ROOT/pages/integrations/homeassistant-integration.adoc @@ -2,38 +2,38 @@ Integrating OliveTin with Home Assistant allows you to control OliveTin from your Home Assistant dashboard. Using the HACS integration is the recommended way to integrate OliveTin with Home Assistant. It is easy to set up and provides a seamless experience. -If you are not familiar with HACs, it is a custom component for Home Assistant that allows you to install and manage custom integrations easily. It is similar to the Home Assistant Add-ons store but for integrations. +If you are not familiar with HACs, it is a custom component for Home Assistant that allows you to install and manage custom integrations easily. It is similar to the Home Assistant Add-ons store but for integrations. == Setup guide . link:https://hacs.xyz/docs/use/[Install HACS] -. Go to HACS in your home assistant control panel. Click the "..." dropdown in the top right corner, and select "**Custom Repositories**". +. Go to HACS in your home assistant control panel. Click the "..." dropdown in the top right corner, and select "**Custom Repositories**". + -image::hacs-dropdown.png[] +image::integrations/hass/hacs-dropdown.png[] . In the dialog that pops up, add the custom repo as follows; + -image::hacs-custom-repo.png[] +image::integrations/hass/hacs-custom-repo.png[] + .. **Repository link**: https://github.com/OliveTin/OliveTin-HomeAssistant .. **Type**: Integration . Search for "OliveTin" in the HACS store, and download it. You will probably need to restart Home Assistant for it be registered correctly. + -image::hacs-search.png[] +image::integrations/hass/hacs-search.png[] + -image::hacs-download.png[] +image::integrations/hass/hacs-download.png[] . Go to "Settings" and open "Devices & Services" + -image::hass-devices-and-services.png[] +image::integrations/hass/hass-devices-and-services.png[] . Click "Add integration" and search for "OliveTin". Note that if it does not show up, you may need to restart Home Assistant. + -image::hass-add-integration.png[] +image::integrations/hass/hass-add-integration.png[] .. Select "OliveTin" .. Host: http://your-server:1337/api .. Username: .. Password: -. Under "Configured", you should see OliveTin. Open it by clicking on the arrow. +. Under "Configured", you should see OliveTin. Open it by clicking on the arrow. + -image:hass-configure-integration.png[] +image::integrations/hass/hass-configure-integration.png[] . After configuring it, you should see buttons appear in Home Assistant; + -image:hass-buttons.png[] +image::integrations/hass/hass-buttons.png[] diff --git a/docs/modules/ROOT/pages/integrations/homeassistant.adoc b/docs/modules/ROOT/pages/integrations/homeassistant.adoc index 4a15eed..1d0dfec 100644 --- a/docs/modules/ROOT/pages/integrations/homeassistant.adoc +++ b/docs/modules/ROOT/pages/integrations/homeassistant.adoc @@ -31,11 +31,11 @@ Now that you have the URL to call to trigger your action, you can add this to yo That page assumes you will use the Home Assistant File Editor addon to edit your configuration.yaml. Install it from the Home Assistant addon store if you have not done so already; -image::hassFileEditor.png[] +image::integrations/hass/hassFileEditor.png[] The addon is started and added to the sidebar; -image::hassFileEditorConfig.png[] +image::integrations/hass/hassFileEditorConfig.png[] Here is an example of a `rest_command` that calls the action above: @@ -55,7 +55,7 @@ You save the file, and restart Home Assistant to pick up the changes. Now that you have a `rest_command` set up to call your action, you can add a button to your Home Assistant dashboard to trigger the action. This is done by adding a `button` to your dashboard configuration. -image::hassButtonSetup.png[] +image::integrations/hass/hassButtonSetup.png[] Set the "Tap Action" to "Call Service" and select the `rest_command` you created earlier. You can also set the icon and name of the button to whatever you like. diff --git a/docs/modules/ROOT/pages/solutions/on-git-push/index.adoc b/docs/modules/ROOT/pages/solutions/on-git-push/index.adoc index 361865b..455d4ac 100644 --- a/docs/modules/ROOT/pages/solutions/on-git-push/index.adoc +++ b/docs/modules/ROOT/pages/solutions/on-git-push/index.adoc @@ -1,7 +1,7 @@ [#solution-on-git-push] = GitOps (run actions on Git Push) -image::gitops.png[] +image::solutions/on-git-push/gitops.png[] A really helpful thing to do with OliveTin is to have it run actions when you push to a Git repository. This is a great way to automate things like running tests, building your project, or deploying your code - this turns OliveTin into a powerful GitOps tool, or even a Continuous Integration tool. diff --git a/docs/modules/ROOT/pages/troubleshooting/advanced.adoc b/docs/modules/ROOT/pages/troubleshooting/advanced.adoc index 149e5b7..245d3ab 100644 --- a/docs/modules/ROOT/pages/troubleshooting/advanced.adoc +++ b/docs/modules/ROOT/pages/troubleshooting/advanced.adoc @@ -9,7 +9,7 @@ All these configuration options are `false` by default, and should be deleted fr [#dump-server-diagnostics] == Dump server diagnostics -`InsecureAllowDumpSos: true` - will allow dumping xref:troubleshooting/server-diagnostics.adoc[server diagnostics] as plain text when visiting `http://server:1337/api/sosreport` +`insecureAllowDumpServerDiagnostics: true` - will allow dumping xref:troubleshooting/server-diagnostics.adoc[server diagnostics] as plain text when visiting `http://server:1337/api/ServerDiagnostics` [#dump-action-map] == Dump Action Map diff --git a/docs/modules/ROOT/pages/troubleshooting/server-diagnostics.adoc b/docs/modules/ROOT/pages/troubleshooting/server-diagnostics.adoc index 4ff61aa..a68e555 100644 --- a/docs/modules/ROOT/pages/troubleshooting/server-diagnostics.adoc +++ b/docs/modules/ROOT/pages/troubleshooting/server-diagnostics.adoc @@ -6,7 +6,7 @@ OliveTin has a useful feature to gather information about your installation when [source,yaml] .Example server diagnostics output ---- -### SOSREPORT START (copy all text to SOSREPORT END) +### SERVER DIAGNOSTICS START (copy all text to SERVER DIAGNOSTICS END) # Build: commit: nocommit version: dev @@ -23,7 +23,7 @@ lastbrowseruseragent: "" countofactions: 7 loglevel: INFO -### SOSREPORT END (copy all text from SOSREPORT START) +### SERVER DIAGNOSTICS END (copy all text from SERVER DIAGNOSTICS START) ---- The markers and field names above reflect what OliveTin prints; copy everything from the start marker through the end marker. @@ -35,9 +35,9 @@ You can then copy and paste this text into a GitHub issue, discussion, Discord c OliveTin needs to be able to start and its API needs to be functional. Once OliveTin is started, open this URL in a browser (replace the host and port with yours): -`http://myserver:1337/api/sosreport` +`http://myserver:1337/api/ServerDiagnostics` -The `/api/sosreport` path is the HTTP endpoint OliveTin uses for server diagnostics. +The `/api/ServerDiagnostics` path is the HTTP endpoint OliveTin uses for server diagnostics. == Optional: Allow insecure (but easy) dumping to the browser @@ -46,7 +46,7 @@ There is a configuration option you can set in your `config.yaml` that allows yo [source,yaml] .`config.yaml` ---- -InsecureAllowDumpSos: true +insecureAllowDumpServerDiagnostics: true ---- == Default: Diagnostics dump to logs @@ -54,7 +54,7 @@ InsecureAllowDumpSos: true You should get a simple JSON message saying something like; ---- -alert: "Your SOS Report has been logged to OliveTin logs." +alert: "Your Server Diagnostics have been logged to OliveTin logs." ---- The exact wording may match your OliveTin version. @@ -66,4 +66,3 @@ To find the diagnostics text depends on how you are running OliveTin. If you are == What if I cannot even get to the OliveTin API? OliveTin's web interface doesn't need to be working to get server diagnostics (the web interface and the API are separate). However, if OliveTin won't even start, or you cannot reach the API, then server diagnostics cannot be generated. Please specify that when you ask for help. - diff --git a/frontend/resources/scripts/gen/olivetin/api/v1/olivetin_pb.d.ts b/frontend/resources/scripts/gen/olivetin/api/v1/olivetin_pb.d.ts index 4f0085f..6450b63 100644 --- a/frontend/resources/scripts/gen/olivetin/api/v1/olivetin_pb.d.ts +++ b/frontend/resources/scripts/gen/olivetin/api/v1/olivetin_pb.d.ts @@ -1219,21 +1219,21 @@ export declare type WhoAmIResponse = Message<"olivetin.api.v1.WhoAmIResponse"> & export declare const WhoAmIResponseSchema: GenMessage; /** - * @generated from message olivetin.api.v1.SosReportRequest + * @generated from message olivetin.api.v1.ServerDiagnosticsRequest */ -export declare type SosReportRequest = Message<"olivetin.api.v1.SosReportRequest"> & { +export declare type ServerDiagnosticsRequest = Message<"olivetin.api.v1.ServerDiagnosticsRequest"> & { }; /** - * Describes the message olivetin.api.v1.SosReportRequest. - * Use `create(SosReportRequestSchema)` to create a new message. + * Describes the message olivetin.api.v1.ServerDiagnosticsRequest. + * Use `create(ServerDiagnosticsRequestSchema)` to create a new message. */ -export declare const SosReportRequestSchema: GenMessage; +export declare const ServerDiagnosticsRequestSchema: GenMessage; /** - * @generated from message olivetin.api.v1.SosReportResponse + * @generated from message olivetin.api.v1.ServerDiagnosticsResponse */ -export declare type SosReportResponse = Message<"olivetin.api.v1.SosReportResponse"> & { +export declare type ServerDiagnosticsResponse = Message<"olivetin.api.v1.ServerDiagnosticsResponse"> & { /** * @generated from field: string alert = 1; */ @@ -1241,10 +1241,10 @@ export declare type SosReportResponse = Message<"olivetin.api.v1.SosReportRespon }; /** - * Describes the message olivetin.api.v1.SosReportResponse. - * Use `create(SosReportResponseSchema)` to create a new message. + * Describes the message olivetin.api.v1.ServerDiagnosticsResponse. + * Use `create(ServerDiagnosticsResponseSchema)` to create a new message. */ -export declare const SosReportResponseSchema: GenMessage; +export declare const ServerDiagnosticsResponseSchema: GenMessage; /** * @generated from message olivetin.api.v1.DumpVarsRequest @@ -2176,12 +2176,12 @@ export declare const OliveTinApiService: GenService<{ output: typeof WhoAmIResponseSchema; }, /** - * @generated from rpc olivetin.api.v1.OliveTinApiService.SosReport + * @generated from rpc olivetin.api.v1.OliveTinApiService.ServerDiagnostics */ - sosReport: { + serverDiagnostics: { methodKind: "unary"; - input: typeof SosReportRequestSchema; - output: typeof SosReportResponseSchema; + input: typeof ServerDiagnosticsRequestSchema; + output: typeof ServerDiagnosticsResponseSchema; }, /** * @generated from rpc olivetin.api.v1.OliveTinApiService.DumpVars @@ -2280,4 +2280,3 @@ export declare const OliveTinApiService: GenService<{ output: typeof EntitySchema; }, }>; - diff --git a/frontend/resources/scripts/gen/olivetin/api/v1/olivetin_pb.js b/frontend/resources/scripts/gen/olivetin/api/v1/olivetin_pb.js index 0f5d513..825efe7 100644 --- a/frontend/resources/scripts/gen/olivetin/api/v1/olivetin_pb.js +++ b/frontend/resources/scripts/gen/olivetin/api/v1/olivetin_pb.js @@ -8,7 +8,7 @@ import { fileDesc, messageDesc, serviceDesc } from "@bufbuild/protobuf/codegenv2 * Describes the file olivetin/api/v1/olivetin.proto. */ export const file_olivetin_api_v1_olivetin = /*@__PURE__*/ - fileDesc("Ch5vbGl2ZXRpbi9hcGkvdjEvb2xpdmV0aW4ucHJvdG8SD29saXZldGluLmFwaS52MSLGBAoGQWN0aW9uEhIKCmJpbmRpbmdfaWQYASABKAkSDQoFdGl0bGUYAiABKAkSDAoEaWNvbhgDIAEoCRIQCghjYW5fZXhlYxgEIAEoCBIyCglhcmd1bWVudHMYBSADKAsyHy5vbGl2ZXRpbi5hcGkudjEuQWN0aW9uQXJndW1lbnQSFgoOcG9wdXBfb25fc3RhcnQYBiABKAkSDQoFb3JkZXIYByABKAUSDwoHdGltZW91dBgIIAEoBRIjChtkYXRldGltZV9yYXRlX2xpbWl0X2V4cGlyZXMYCSABKAkSFwoPZXhlY19vbl9zdGFydHVwGAogASgIEhQKDGV4ZWNfb25fY3JvbhgLIAMoCRIjChtleGVjX29uX2ZpbGVfY3JlYXRlZF9pbl9kaXIYDCADKAkSIwobZXhlY19vbl9maWxlX2NoYW5nZWRfaW5fZGlyGA0gAygJEh0KFWV4ZWNfb25fY2FsZW5kYXJfZmlsZRgOIAEoCRJAChBleGVjX29uX3dlYmhvb2tzGA8gAygLMiYub2xpdmV0aW4uYXBpLnYxLkFjdGlvbldlYmhvb2tFeGVjSGludBIVCg1qdXN0aWZpY2F0aW9uGBQgASgJEhwKFGhhc19ydW5uaW5nX2luc3RhbmNlGBEgASgIEhsKE2hhc19xdWV1ZWRfaW5zdGFuY2UYEiABKAgSNgoGZ3JvdXBzGBMgAygLMiYub2xpdmV0aW4uYXBpLnYxLkFjdGlvbkdyb3VwTWVtYmVyc2hpcEoECBAQESJRChVBY3Rpb25Hcm91cE1lbWJlcnNoaXASDAoEbmFtZRgBIAEoCRIWCg5tYXhfY29uY3VycmVudBgCIAEoBRISCgpxdWV1ZV9zaXplGAMgASgFIsMCChVBY3Rpb25XZWJob29rRXhlY0hpbnQSEAoIdGVtcGxhdGUYASABKAkSEgoKbWF0Y2hfcGF0aBgCIAEoCRJPCg1tYXRjaF9oZWFkZXJzGAMgAygLMjgub2xpdmV0aW4uYXBpLnYxLkFjdGlvbldlYmhvb2tFeGVjSGludC5NYXRjaEhlYWRlcnNFbnRyeRJLCgttYXRjaF9xdWVyeRgEIAMoCzI2Lm9saXZldGluLmFwaS52MS5BY3Rpb25XZWJob29rRXhlY0hpbnQuTWF0Y2hRdWVyeUVudHJ5GjMKEU1hdGNoSGVhZGVyc0VudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoCToCOAEaMQoPTWF0Y2hRdWVyeUVudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoCToCOAEiuwIKDkFjdGlvbkFyZ3VtZW50EgwKBG5hbWUYASABKAkSDQoFdGl0bGUYAiABKAkSDAoEdHlwZRgDIAEoCRIVCg1kZWZhdWx0X3ZhbHVlGAQgASgJEjYKB2Nob2ljZXMYBSADKAsyJS5vbGl2ZXRpbi5hcGkudjEuQWN0aW9uQXJndW1lbnRDaG9pY2USEwoLZGVzY3JpcHRpb24YBiABKAkSRQoLc3VnZ2VzdGlvbnMYByADKAsyMC5vbGl2ZXRpbi5hcGkudjEuQWN0aW9uQXJndW1lbnQuU3VnZ2VzdGlvbnNFbnRyeRIfChdzdWdnZXN0aW9uc19icm93c2VyX2tleRgIIAEoCRoyChBTdWdnZXN0aW9uc0VudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoCToCOAEiNAoUQWN0aW9uQXJndW1lbnRDaG9pY2USDQoFdmFsdWUYASABKAkSDQoFdGl0bGUYAiABKAki1AEKE0VudGl0eVJlbGF0ZWRBY3Rpb24SJwoGYWN0aW9uGAEgASgLMhcub2xpdmV0aW4uYXBpLnYxLkFjdGlvbhJZChNwcmVmaWxsZWRfYXJndW1lbnRzGAIgAygLMjwub2xpdmV0aW4uYXBpLnYxLkVudGl0eVJlbGF0ZWRBY3Rpb24uUHJlZmlsbGVkQXJndW1lbnRzRW50cnkaOQoXUHJlZmlsbGVkQXJndW1lbnRzRW50cnkSCwoDa2V5GAEgASgJEg0KBXZhbHVlGAIgASgJOgI4ASL/AQoGRW50aXR5Eg0KBXRpdGxlGAEgASgJEhIKCnVuaXF1ZV9rZXkYAiABKAkSDAoEdHlwZRgDIAEoCRITCgtkaXJlY3RvcmllcxgEIAMoCRIzCgZmaWVsZHMYBSADKAsyIy5vbGl2ZXRpbi5hcGkudjEuRW50aXR5LkZpZWxkc0VudHJ5Ej0KD3JlbGF0ZWRfYWN0aW9ucxgGIAMoCzIkLm9saXZldGluLmFwaS52MS5FbnRpdHlSZWxhdGVkQWN0aW9uEgwKBGljb24YByABKAkaLQoLRmllbGRzRW50cnkSCwoDa2V5GAEgASgJEg0KBXZhbHVlGAIgASgJOgI4ASJUChRHZXREYXNoYm9hcmRSZXNwb25zZRINCgV0aXRsZRgBIAEoCRItCglkYXNoYm9hcmQYBCABKAsyGi5vbGl2ZXRpbi5hcGkudjEuRGFzaGJvYXJkIl8KD0VmZmVjdGl2ZVBvbGljeRIYChBzaG93X2RpYWdub3N0aWNzGAEgASgIEhUKDXNob3dfbG9nX2xpc3QYAiABKAgSGwoTc2hvd192ZXJzaW9uX251bWJlchgDIAEoCCJNChNHZXREYXNoYm9hcmRSZXF1ZXN0Eg0KBXRpdGxlGAEgASgJEhMKC2VudGl0eV90eXBlGAIgASgJEhIKCmVudGl0eV9rZXkYAyABKAkiUQoJRGFzaGJvYXJkEg0KBXRpdGxlGAEgASgJEjUKCGNvbnRlbnRzGAIgAygLMiMub2xpdmV0aW4uYXBpLnYxLkRhc2hib2FyZENvbXBvbmVudCLbAQoSRGFzaGJvYXJkQ29tcG9uZW50Eg0KBXRpdGxlGAEgASgJEgwKBHR5cGUYAiABKAkSNQoIY29udGVudHMYAyADKAsyIy5vbGl2ZXRpbi5hcGkudjEuRGFzaGJvYXJkQ29tcG9uZW50EgwKBGljb24YBCABKAkSEQoJY3NzX2NsYXNzGAUgASgJEicKBmFjdGlvbhgGIAEoCzIXLm9saXZldGluLmFwaS52MS5BY3Rpb24SEwoLZW50aXR5X3R5cGUYByABKAkSEgoKZW50aXR5X2tleRgIIAEoCSKUAQoSU3RhcnRBY3Rpb25SZXF1ZXN0EhIKCmJpbmRpbmdfaWQYASABKAkSNwoJYXJndW1lbnRzGAIgAygLMiQub2xpdmV0aW4uYXBpLnYxLlN0YXJ0QWN0aW9uQXJndW1lbnQSGgoSdW5pcXVlX3RyYWNraW5nX2lkGAMgASgJEhUKDWp1c3RpZmljYXRpb24YBCABKAkiMgoTU3RhcnRBY3Rpb25Bcmd1bWVudBIMCgRuYW1lGAEgASgJEg0KBXZhbHVlGAIgASgJIjQKE1N0YXJ0QWN0aW9uUmVzcG9uc2USHQoVZXhlY3V0aW9uX3RyYWNraW5nX2lkGAIgASgJIn4KGVN0YXJ0QWN0aW9uQW5kV2FpdFJlcXVlc3QSEQoJYWN0aW9uX2lkGAEgASgJEjcKCWFyZ3VtZW50cxgCIAMoCzIkLm9saXZldGluLmFwaS52MS5TdGFydEFjdGlvbkFyZ3VtZW50EhUKDWp1c3RpZmljYXRpb24YAyABKAkiSgoaU3RhcnRBY3Rpb25BbmRXYWl0UmVzcG9uc2USLAoJbG9nX2VudHJ5GAEgASgLMhkub2xpdmV0aW4uYXBpLnYxLkxvZ0VudHJ5IiwKF1N0YXJ0QWN0aW9uQnlHZXRSZXF1ZXN0EhEKCWFjdGlvbl9pZBgBIAEoCSI5ChhTdGFydEFjdGlvbkJ5R2V0UmVzcG9uc2USHQoVZXhlY3V0aW9uX3RyYWNraW5nX2lkGAIgASgJIjMKHlN0YXJ0QWN0aW9uQnlHZXRBbmRXYWl0UmVxdWVzdBIRCglhY3Rpb25faWQYASABKAkiTwofU3RhcnRBY3Rpb25CeUdldEFuZFdhaXRSZXNwb25zZRIsCglsb2dfZW50cnkYASABKAsyGS5vbGl2ZXRpbi5hcGkudjEuTG9nRW50cnkiXgoOR2V0TG9nc1JlcXVlc3QSFAoMc3RhcnRfb2Zmc2V0GAEgASgDEhMKC2RhdGVfZmlsdGVyGAIgASgJEhEKCXBhZ2Vfc2l6ZRgDIAEoAxIOCgZmaWx0ZXIYBCABKAkilAQKCExvZ0VudHJ5EhgKEGRhdGV0aW1lX3N0YXJ0ZWQYASABKAkSFAoMYWN0aW9uX3RpdGxlGAIgASgJEg4KBm91dHB1dBgDIAEoCRIRCgl0aW1lZF9vdXQYBSABKAgSEQoJZXhpdF9jb2RlGAYgASgFEgwKBHVzZXIYByABKAkSEgoKdXNlcl9jbGFzcxgIIAEoCRITCgthY3Rpb25faWNvbhgJIAEoCRIMCgR0YWdzGAogAygJEh0KFWV4ZWN1dGlvbl90cmFja2luZ19pZBgLIAEoCRIZChFkYXRldGltZV9maW5pc2hlZBgMIAEoCRIZChFleGVjdXRpb25fc3RhcnRlZBgOIAEoCBIaChJleGVjdXRpb25fZmluaXNoZWQYDyABKAgSDwoHYmxvY2tlZBgQIAEoCBIWCg5kYXRldGltZV9pbmRleBgRIAEoAxIQCghjYW5fa2lsbBgSIAEoCBIjChtkYXRldGltZV9yYXRlX2xpbWl0X2V4cGlyZXMYEyABKAkSEgoKYmluZGluZ19pZBgUIAEoCRIOCgZxdWV1ZWQYFSABKAgSGAoQcXVldWVkX2Zvcl9ncm91cBgWIAEoCRIVCg1qdXN0aWZpY2F0aW9uGBcgASgJEjcKCWFyZ3VtZW50cxgYIAMoCzIkLm9saXZldGluLmFwaS52MS5TdGFydEFjdGlvbkFyZ3VtZW50IpEBCg9HZXRMb2dzUmVzcG9uc2USJwoEbG9ncxgBIAMoCzIZLm9saXZldGluLmFwaS52MS5Mb2dFbnRyeRIXCg9jb3VudF9yZW1haW5pbmcYAiABKAMSEQoJcGFnZV9zaXplGAMgASgDEhMKC3RvdGFsX2NvdW50GAQgASgDEhQKDHN0YXJ0X29mZnNldBgFIAEoAyI/ChRHZXRBY3Rpb25Mb2dzUmVxdWVzdBIRCglhY3Rpb25faWQYASABKAkSFAoMc3RhcnRfb2Zmc2V0GAIgASgDIpcBChVHZXRBY3Rpb25Mb2dzUmVzcG9uc2USJwoEbG9ncxgBIAMoCzIZLm9saXZldGluLmFwaS52MS5Mb2dFbnRyeRIXCg9jb3VudF9yZW1haW5pbmcYAiABKAMSEQoJcGFnZV9zaXplGAMgASgDEhMKC3RvdGFsX2NvdW50GAQgASgDEhQKDHN0YXJ0X29mZnNldBgFIAEoAyIaChhHZXRFeGVjdXRpb25RdWV1ZVJlcXVlc3QixgEKFEV4ZWN1dGlvblF1ZXVlQWN0aW9uEhIKCmJpbmRpbmdfaWQYASABKAkSFAoMYWN0aW9uX3RpdGxlGAIgASgJEhMKC2FjdGlvbl9pY29uGAMgASgJEhYKDm1heF9jb25jdXJyZW50GAQgASgFEhQKDGFjdGl2ZV9jb3VudBgFIAEoBRIVCg1lbnRpdHlfcHJlZml4GAYgASgJEioKB2VudHJpZXMYByADKAsyGS5vbGl2ZXRpbi5hcGkudjEuTG9nRW50cnkiwQEKE0V4ZWN1dGlvblF1ZXVlR3JvdXASDAoEbmFtZRgBIAEoCRIMCgRpY29uGAIgASgJEhYKDm1heF9jb25jdXJyZW50GAMgASgFEhQKDGFjdGl2ZV9jb3VudBgEIAEoBRI2CgdhY3Rpb25zGAUgAygLMiUub2xpdmV0aW4uYXBpLnYxLkV4ZWN1dGlvblF1ZXVlQWN0aW9uEhQKDHF1ZXVlZF9jb3VudBgGIAEoBRISCgpxdWV1ZV9zaXplGAcgASgFImcKGUdldEV4ZWN1dGlvblF1ZXVlUmVzcG9uc2USNAoGZ3JvdXBzGAEgAygLMiQub2xpdmV0aW4uYXBpLnYxLkV4ZWN1dGlvblF1ZXVlR3JvdXASFAoMdG90YWxfYWN0aXZlGAIgASgFImUKG1ZhbGlkYXRlQXJndW1lbnRUeXBlUmVxdWVzdBINCgV2YWx1ZRgBIAEoCRIMCgR0eXBlGAIgASgJEhIKCmJpbmRpbmdfaWQYAyABKAkSFQoNYXJndW1lbnRfbmFtZRgEIAEoCSJCChxWYWxpZGF0ZUFyZ3VtZW50VHlwZVJlc3BvbnNlEg0KBXZhbGlkGAEgASgIEhMKC2Rlc2NyaXB0aW9uGAIgASgJIjYKFVdhdGNoRXhlY3V0aW9uUmVxdWVzdBIdChVleGVjdXRpb25fdHJhY2tpbmdfaWQYASABKAkiJgoUV2F0Y2hFeGVjdXRpb25VcGRhdGUSDgoGdXBkYXRlGAEgASgJIkoKFkV4ZWN1dGlvblN0YXR1c1JlcXVlc3QSHQoVZXhlY3V0aW9uX3RyYWNraW5nX2lkGAEgASgJEhEKCWFjdGlvbl9pZBgCIAEoCSJhChlEYXNoYm9hcmROYXZpZ2F0aW9uVGFyZ2V0Eg0KBXRpdGxlGAEgASgJEhMKC2VudGl0eV90eXBlGAIgASgJEhIKCmVudGl0eV9rZXkYAyABKAkSDAoEcGF0aBgEIAEoCSKPAQoXRXhlY3V0aW9uU3RhdHVzUmVzcG9uc2USLAoJbG9nX2VudHJ5GAEgASgLMhkub2xpdmV0aW4uYXBpLnYxLkxvZ0VudHJ5EkYKEmJhY2tfdG9fZGFzaGJvYXJkcxgCIAMoCzIqLm9saXZldGluLmFwaS52MS5EYXNoYm9hcmROYXZpZ2F0aW9uVGFyZ2V0Ig8KDVdob0FtSVJlcXVlc3QibAoOV2hvQW1JUmVzcG9uc2USGgoSYXV0aGVudGljYXRlZF91c2VyGAEgASgJEhEKCXVzZXJncm91cBgCIAEoCRIQCghwcm92aWRlchgDIAEoCRIMCgRhY2xzGAQgAygJEgsKA3NpZBgFIAEoCSISChBTb3NSZXBvcnRSZXF1ZXN0IiIKEVNvc1JlcG9ydFJlc3BvbnNlEg0KBWFsZXJ0GAEgASgJIhEKD0R1bXBWYXJzUmVxdWVzdCKVAQoQRHVtcFZhcnNSZXNwb25zZRINCgVhbGVydBgBIAEoCRJBCghjb250ZW50cxgCIAMoCzIvLm9saXZldGluLmFwaS52MS5EdW1wVmFyc1Jlc3BvbnNlLkNvbnRlbnRzRW50cnkaLwoNQ29udGVudHNFbnRyeRILCgNrZXkYASABKAkSDQoFdmFsdWUYAiABKAk6AjgBIjsKDERlYnVnQmluZGluZxIUCgxhY3Rpb25fdGl0bGUYASABKAkSFQoNZW50aXR5X3ByZWZpeBgCIAEoCSIeChxEdW1wUHVibGljSWRBY3Rpb25NYXBSZXF1ZXN0Is4BCh1EdW1wUHVibGljSWRBY3Rpb25NYXBSZXNwb25zZRINCgVhbGVydBgBIAEoCRJOCghjb250ZW50cxgCIAMoCzI8Lm9saXZldGluLmFwaS52MS5EdW1wUHVibGljSWRBY3Rpb25NYXBSZXNwb25zZS5Db250ZW50c0VudHJ5Gk4KDUNvbnRlbnRzRW50cnkSCwoDa2V5GAEgASgJEiwKBXZhbHVlGAIgASgLMh0ub2xpdmV0aW4uYXBpLnYxLkRlYnVnQmluZGluZzoCOAEiEgoQR2V0UmVhZHl6UmVxdWVzdCIjChFHZXRSZWFkeXpSZXNwb25zZRIOCgZzdGF0dXMYASABKAkiFAoSRXZlbnRTdHJlYW1SZXF1ZXN0IpkDChNFdmVudFN0cmVhbVJlc3BvbnNlEj0KDmVudGl0eV9jaGFuZ2VkGAIgASgLMiMub2xpdmV0aW4uYXBpLnYxLkV2ZW50RW50aXR5Q2hhbmdlZEgAEj0KDmNvbmZpZ19jaGFuZ2VkGAMgASgLMiMub2xpdmV0aW4uYXBpLnYxLkV2ZW50Q29uZmlnQ2hhbmdlZEgAEkUKEmV4ZWN1dGlvbl9maW5pc2hlZBgEIAEoCzInLm9saXZldGluLmFwaS52MS5FdmVudEV4ZWN1dGlvbkZpbmlzaGVkSAASQwoRZXhlY3V0aW9uX3N0YXJ0ZWQYBSABKAsyJi5vbGl2ZXRpbi5hcGkudjEuRXZlbnRFeGVjdXRpb25TdGFydGVkSAASOQoMb3V0cHV0X2NodW5rGAYgASgLMiEub2xpdmV0aW4uYXBpLnYxLkV2ZW50T3V0cHV0Q2h1bmtIABI0CgloZWFydGJlYXQYByABKAsyHy5vbGl2ZXRpbi5hcGkudjEuRXZlbnRIZWFydGJlYXRIAEIHCgVldmVudCJBChBFdmVudE91dHB1dENodW5rEh0KFWV4ZWN1dGlvbl90cmFja2luZ19pZBgBIAEoCRIOCgZvdXRwdXQYAiABKAkiFAoSRXZlbnRFbnRpdHlDaGFuZ2VkIhQKEkV2ZW50Q29uZmlnQ2hhbmdlZCIQCg5FdmVudEhlYXJ0YmVhdCJGChZFdmVudEV4ZWN1dGlvbkZpbmlzaGVkEiwKCWxvZ19lbnRyeRgBIAEoCzIZLm9saXZldGluLmFwaS52MS5Mb2dFbnRyeSJFChVFdmVudEV4ZWN1dGlvblN0YXJ0ZWQSLAoJbG9nX2VudHJ5GAEgASgLMhkub2xpdmV0aW4uYXBpLnYxLkxvZ0VudHJ5IjIKEUtpbGxBY3Rpb25SZXF1ZXN0Eh0KFWV4ZWN1dGlvbl90cmFja2luZ19pZBgBIAEoCSJtChJLaWxsQWN0aW9uUmVzcG9uc2USHQoVZXhlY3V0aW9uX3RyYWNraW5nX2lkGAEgASgJEg4KBmtpbGxlZBgCIAEoCBIZChFhbHJlYWR5X2NvbXBsZXRlZBgDIAEoCBINCgVmb3VuZBgEIAEoCCI7ChVMb2NhbFVzZXJMb2dpblJlcXVlc3QSEAoIdXNlcm5hbWUYASABKAkSEAoIcGFzc3dvcmQYAiABKAkiKQoWTG9jYWxVc2VyTG9naW5SZXNwb25zZRIPCgdzdWNjZXNzGAEgASgIIicKE1Bhc3N3b3JkSGFzaFJlcXVlc3QSEAoIcGFzc3dvcmQYASABKAkiJAoUUGFzc3dvcmRIYXNoUmVzcG9uc2USDAoEaGFzaBgBIAEoCSIPCg1Mb2dvdXRSZXF1ZXN0IhAKDkxvZ291dFJlc3BvbnNlIhcKFUdldERpYWdub3N0aWNzUmVxdWVzdCJFChZHZXREaWFnbm9zdGljc1Jlc3BvbnNlEhMKC1NzaEZvdW5kS2V5GAEgASgJEhYKDlNzaEZvdW5kQ29uZmlnGAIgASgJIg0KC0luaXRSZXF1ZXN0IusFCgxJbml0UmVzcG9uc2USEgoKc2hvd0Zvb3RlchgBIAEoCBIWCg5zaG93TmF2aWdhdGlvbhgCIAEoCBIXCg9zaG93TmV3VmVyc2lvbnMYAyABKAgSGAoQYXZhaWxhYmxlVmVyc2lvbhgEIAEoCRIWCg5jdXJyZW50VmVyc2lvbhgFIAEoCRIRCglwYWdlVGl0bGUYBiABKAkSHgoWc2VjdGlvbk5hdmlnYXRpb25TdHlsZRgHIAEoCRIaChJkZWZhdWx0SWNvbkZvckJhY2sYCCABKAkSFgoOZW5hYmxlQ3VzdG9tSnMYCSABKAgSFAoMYXV0aExvZ2luVXJsGAogASgJEhYKDmF1dGhMb2NhbExvZ2luGAsgASgIEhEKCXN0eWxlTW9kcxgMIAMoCRI4Cg9vQXV0aDJQcm92aWRlcnMYDSADKAsyHy5vbGl2ZXRpbi5hcGkudjEuT0F1dGgyUHJvdmlkZXISOAoPYWRkaXRpb25hbExpbmtzGA4gAygLMh8ub2xpdmV0aW4uYXBpLnYxLkFkZGl0aW9uYWxMaW5rEhYKDnJvb3REYXNoYm9hcmRzGA8gAygJEhoKEmF1dGhlbnRpY2F0ZWRfdXNlchgQIAEoCRIjChthdXRoZW50aWNhdGVkX3VzZXJfcHJvdmlkZXIYESABKAkSOgoQZWZmZWN0aXZlX3BvbGljeRgSIAEoCzIgLm9saXZldGluLmFwaS52MS5FZmZlY3RpdmVQb2xpY3kSFgoOYmFubmVyX21lc3NhZ2UYEyABKAkSEgoKYmFubmVyX2NzcxgUIAEoCRIYChBzaG93X2RpYWdub3N0aWNzGBUgASgIEhUKDXNob3dfbG9nX2xpc3QYFiABKAgSFgoObG9naW5fcmVxdWlyZWQYFyABKAgSGAoQYXZhaWxhYmxlX3RoZW1lcxgYIAMoCRIkChxzaG93X25hdmlnYXRlX29uX3N0YXJ0X2ljb25zGBkgASgIIiwKDkFkZGl0aW9uYWxMaW5rEg0KBXRpdGxlGAEgASgJEgsKA3VybBgCIAEoCSI6Cg5PQXV0aDJQcm92aWRlchINCgV0aXRsZRgBIAEoCRIMCgRpY29uGAMgASgJEgsKA2tleRgEIAEoCSItChdHZXRBY3Rpb25CaW5kaW5nUmVxdWVzdBISCgpiaW5kaW5nX2lkGAEgASgJIosBChhHZXRBY3Rpb25CaW5kaW5nUmVzcG9uc2USJwoGYWN0aW9uGAEgASgLMhcub2xpdmV0aW4uYXBpLnYxLkFjdGlvbhJGChJiYWNrX3RvX2Rhc2hib2FyZHMYAiADKAsyKi5vbGl2ZXRpbi5hcGkudjEuRGFzaGJvYXJkTmF2aWdhdGlvblRhcmdldCJaChJHZXRFbnRpdGllc1JlcXVlc3QSEwoLZW50aXR5X3R5cGUYASABKAkSDgoGZmlsdGVyGAIgASgJEgwKBHBhZ2UYAyABKAUSEQoJcGFnZV9zaXplGAQgASgFIlQKE0dldEVudGl0aWVzUmVzcG9uc2USPQoSZW50aXR5X2RlZmluaXRpb25zGAEgAygLMiEub2xpdmV0aW4uYXBpLnYxLkVudGl0eURlZmluaXRpb24ixQEKEEVudGl0eURlZmluaXRpb24SDQoFdGl0bGUYASABKAkSKgoJaW5zdGFuY2VzGAIgAygLMhcub2xpdmV0aW4uYXBpLnYxLkVudGl0eRIaChJ1c2VkX29uX2Rhc2hib2FyZHMYAyADKAkSDAoEaWNvbhgEIAEoCRIzCgpwcm9wZXJ0aWVzGAUgAygLMh8ub2xpdmV0aW4uYXBpLnYxLkVudGl0eVByb3BlcnR5EhcKD3RvdGFsX2luc3RhbmNlcxgGIAEoBSItCg5FbnRpdHlQcm9wZXJ0eRIMCgRuYW1lGAEgASgJEg0KBXRpdGxlGAIgASgJIjQKEEdldEVudGl0eVJlcXVlc3QSEgoKdW5pcXVlX2tleRgBIAEoCRIMCgR0eXBlGAIgASgJIjUKFFJlc3RhcnRBY3Rpb25SZXF1ZXN0Eh0KFWV4ZWN1dGlvbl90cmFja2luZ19pZBgBIAEoCTLWEwoST2xpdmVUaW5BcGlTZXJ2aWNlEl0KDEdldERhc2hib2FyZBIkLm9saXZldGluLmFwaS52MS5HZXREYXNoYm9hcmRSZXF1ZXN0GiUub2xpdmV0aW4uYXBpLnYxLkdldERhc2hib2FyZFJlc3BvbnNlIgASWgoLU3RhcnRBY3Rpb24SIy5vbGl2ZXRpbi5hcGkudjEuU3RhcnRBY3Rpb25SZXF1ZXN0GiQub2xpdmV0aW4uYXBpLnYxLlN0YXJ0QWN0aW9uUmVzcG9uc2UiABJvChJTdGFydEFjdGlvbkFuZFdhaXQSKi5vbGl2ZXRpbi5hcGkudjEuU3RhcnRBY3Rpb25BbmRXYWl0UmVxdWVzdBorLm9saXZldGluLmFwaS52MS5TdGFydEFjdGlvbkFuZFdhaXRSZXNwb25zZSIAEmkKEFN0YXJ0QWN0aW9uQnlHZXQSKC5vbGl2ZXRpbi5hcGkudjEuU3RhcnRBY3Rpb25CeUdldFJlcXVlc3QaKS5vbGl2ZXRpbi5hcGkudjEuU3RhcnRBY3Rpb25CeUdldFJlc3BvbnNlIgASfgoXU3RhcnRBY3Rpb25CeUdldEFuZFdhaXQSLy5vbGl2ZXRpbi5hcGkudjEuU3RhcnRBY3Rpb25CeUdldEFuZFdhaXRSZXF1ZXN0GjAub2xpdmV0aW4uYXBpLnYxLlN0YXJ0QWN0aW9uQnlHZXRBbmRXYWl0UmVzcG9uc2UiABJeCg1SZXN0YXJ0QWN0aW9uEiUub2xpdmV0aW4uYXBpLnYxLlJlc3RhcnRBY3Rpb25SZXF1ZXN0GiQub2xpdmV0aW4uYXBpLnYxLlN0YXJ0QWN0aW9uUmVzcG9uc2UiABJXCgpLaWxsQWN0aW9uEiIub2xpdmV0aW4uYXBpLnYxLktpbGxBY3Rpb25SZXF1ZXN0GiMub2xpdmV0aW4uYXBpLnYxLktpbGxBY3Rpb25SZXNwb25zZSIAEmYKD0V4ZWN1dGlvblN0YXR1cxInLm9saXZldGluLmFwaS52MS5FeGVjdXRpb25TdGF0dXNSZXF1ZXN0Gigub2xpdmV0aW4uYXBpLnYxLkV4ZWN1dGlvblN0YXR1c1Jlc3BvbnNlIgASTgoHR2V0TG9ncxIfLm9saXZldGluLmFwaS52MS5HZXRMb2dzUmVxdWVzdBogLm9saXZldGluLmFwaS52MS5HZXRMb2dzUmVzcG9uc2UiABJgCg1HZXRBY3Rpb25Mb2dzEiUub2xpdmV0aW4uYXBpLnYxLkdldEFjdGlvbkxvZ3NSZXF1ZXN0GiYub2xpdmV0aW4uYXBpLnYxLkdldEFjdGlvbkxvZ3NSZXNwb25zZSIAEmwKEUdldEV4ZWN1dGlvblF1ZXVlEikub2xpdmV0aW4uYXBpLnYxLkdldEV4ZWN1dGlvblF1ZXVlUmVxdWVzdBoqLm9saXZldGluLmFwaS52MS5HZXRFeGVjdXRpb25RdWV1ZVJlc3BvbnNlIgASdQoUVmFsaWRhdGVBcmd1bWVudFR5cGUSLC5vbGl2ZXRpbi5hcGkudjEuVmFsaWRhdGVBcmd1bWVudFR5cGVSZXF1ZXN0Gi0ub2xpdmV0aW4uYXBpLnYxLlZhbGlkYXRlQXJndW1lbnRUeXBlUmVzcG9uc2UiABJLCgZXaG9BbUkSHi5vbGl2ZXRpbi5hcGkudjEuV2hvQW1JUmVxdWVzdBofLm9saXZldGluLmFwaS52MS5XaG9BbUlSZXNwb25zZSIAElQKCVNvc1JlcG9ydBIhLm9saXZldGluLmFwaS52MS5Tb3NSZXBvcnRSZXF1ZXN0GiIub2xpdmV0aW4uYXBpLnYxLlNvc1JlcG9ydFJlc3BvbnNlIgASUQoIRHVtcFZhcnMSIC5vbGl2ZXRpbi5hcGkudjEuRHVtcFZhcnNSZXF1ZXN0GiEub2xpdmV0aW4uYXBpLnYxLkR1bXBWYXJzUmVzcG9uc2UiABJ4ChVEdW1wUHVibGljSWRBY3Rpb25NYXASLS5vbGl2ZXRpbi5hcGkudjEuRHVtcFB1YmxpY0lkQWN0aW9uTWFwUmVxdWVzdBouLm9saXZldGluLmFwaS52MS5EdW1wUHVibGljSWRBY3Rpb25NYXBSZXNwb25zZSIAElQKCUdldFJlYWR5ehIhLm9saXZldGluLmFwaS52MS5HZXRSZWFkeXpSZXF1ZXN0GiIub2xpdmV0aW4uYXBpLnYxLkdldFJlYWR5elJlc3BvbnNlIgASYwoOTG9jYWxVc2VyTG9naW4SJi5vbGl2ZXRpbi5hcGkudjEuTG9jYWxVc2VyTG9naW5SZXF1ZXN0Gicub2xpdmV0aW4uYXBpLnYxLkxvY2FsVXNlckxvZ2luUmVzcG9uc2UiABJdCgxQYXNzd29yZEhhc2gSJC5vbGl2ZXRpbi5hcGkudjEuUGFzc3dvcmRIYXNoUmVxdWVzdBolLm9saXZldGluLmFwaS52MS5QYXNzd29yZEhhc2hSZXNwb25zZSIAEksKBkxvZ291dBIeLm9saXZldGluLmFwaS52MS5Mb2dvdXRSZXF1ZXN0Gh8ub2xpdmV0aW4uYXBpLnYxLkxvZ291dFJlc3BvbnNlIgASXAoLRXZlbnRTdHJlYW0SIy5vbGl2ZXRpbi5hcGkudjEuRXZlbnRTdHJlYW1SZXF1ZXN0GiQub2xpdmV0aW4uYXBpLnYxLkV2ZW50U3RyZWFtUmVzcG9uc2UiADABEmMKDkdldERpYWdub3N0aWNzEiYub2xpdmV0aW4uYXBpLnYxLkdldERpYWdub3N0aWNzUmVxdWVzdBonLm9saXZldGluLmFwaS52MS5HZXREaWFnbm9zdGljc1Jlc3BvbnNlIgASRQoESW5pdBIcLm9saXZldGluLmFwaS52MS5Jbml0UmVxdWVzdBodLm9saXZldGluLmFwaS52MS5Jbml0UmVzcG9uc2UiABJpChBHZXRBY3Rpb25CaW5kaW5nEigub2xpdmV0aW4uYXBpLnYxLkdldEFjdGlvbkJpbmRpbmdSZXF1ZXN0Gikub2xpdmV0aW4uYXBpLnYxLkdldEFjdGlvbkJpbmRpbmdSZXNwb25zZSIAEloKC0dldEVudGl0aWVzEiMub2xpdmV0aW4uYXBpLnYxLkdldEVudGl0aWVzUmVxdWVzdBokLm9saXZldGluLmFwaS52MS5HZXRFbnRpdGllc1Jlc3BvbnNlIgASSQoJR2V0RW50aXR5EiEub2xpdmV0aW4uYXBpLnYxLkdldEVudGl0eVJlcXVlc3QaFy5vbGl2ZXRpbi5hcGkudjEuRW50aXR5IgBCOFo2Z2l0aHViLmNvbS9PbGl2ZVRpbi9PbGl2ZVRpbi9nZW4vb2xpdmV0aW4vYXBpL3YxO2FwaXYxYgZwcm90bzM"); + fileDesc("Ch5vbGl2ZXRpbi9hcGkvdjEvb2xpdmV0aW4ucHJvdG8SD29saXZldGluLmFwaS52MSLGBAoGQWN0aW9uEhIKCmJpbmRpbmdfaWQYASABKAkSDQoFdGl0bGUYAiABKAkSDAoEaWNvbhgDIAEoCRIQCghjYW5fZXhlYxgEIAEoCBIyCglhcmd1bWVudHMYBSADKAsyHy5vbGl2ZXRpbi5hcGkudjEuQWN0aW9uQXJndW1lbnQSFgoOcG9wdXBfb25fc3RhcnQYBiABKAkSDQoFb3JkZXIYByABKAUSDwoHdGltZW91dBgIIAEoBRIjChtkYXRldGltZV9yYXRlX2xpbWl0X2V4cGlyZXMYCSABKAkSFwoPZXhlY19vbl9zdGFydHVwGAogASgIEhQKDGV4ZWNfb25fY3JvbhgLIAMoCRIjChtleGVjX29uX2ZpbGVfY3JlYXRlZF9pbl9kaXIYDCADKAkSIwobZXhlY19vbl9maWxlX2NoYW5nZWRfaW5fZGlyGA0gAygJEh0KFWV4ZWNfb25fY2FsZW5kYXJfZmlsZRgOIAEoCRJAChBleGVjX29uX3dlYmhvb2tzGA8gAygLMiYub2xpdmV0aW4uYXBpLnYxLkFjdGlvbldlYmhvb2tFeGVjSGludBIVCg1qdXN0aWZpY2F0aW9uGBQgASgJEhwKFGhhc19ydW5uaW5nX2luc3RhbmNlGBEgASgIEhsKE2hhc19xdWV1ZWRfaW5zdGFuY2UYEiABKAgSNgoGZ3JvdXBzGBMgAygLMiYub2xpdmV0aW4uYXBpLnYxLkFjdGlvbkdyb3VwTWVtYmVyc2hpcEoECBAQESJRChVBY3Rpb25Hcm91cE1lbWJlcnNoaXASDAoEbmFtZRgBIAEoCRIWCg5tYXhfY29uY3VycmVudBgCIAEoBRISCgpxdWV1ZV9zaXplGAMgASgFIsMCChVBY3Rpb25XZWJob29rRXhlY0hpbnQSEAoIdGVtcGxhdGUYASABKAkSEgoKbWF0Y2hfcGF0aBgCIAEoCRJPCg1tYXRjaF9oZWFkZXJzGAMgAygLMjgub2xpdmV0aW4uYXBpLnYxLkFjdGlvbldlYmhvb2tFeGVjSGludC5NYXRjaEhlYWRlcnNFbnRyeRJLCgttYXRjaF9xdWVyeRgEIAMoCzI2Lm9saXZldGluLmFwaS52MS5BY3Rpb25XZWJob29rRXhlY0hpbnQuTWF0Y2hRdWVyeUVudHJ5GjMKEU1hdGNoSGVhZGVyc0VudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoCToCOAEaMQoPTWF0Y2hRdWVyeUVudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoCToCOAEiuwIKDkFjdGlvbkFyZ3VtZW50EgwKBG5hbWUYASABKAkSDQoFdGl0bGUYAiABKAkSDAoEdHlwZRgDIAEoCRIVCg1kZWZhdWx0X3ZhbHVlGAQgASgJEjYKB2Nob2ljZXMYBSADKAsyJS5vbGl2ZXRpbi5hcGkudjEuQWN0aW9uQXJndW1lbnRDaG9pY2USEwoLZGVzY3JpcHRpb24YBiABKAkSRQoLc3VnZ2VzdGlvbnMYByADKAsyMC5vbGl2ZXRpbi5hcGkudjEuQWN0aW9uQXJndW1lbnQuU3VnZ2VzdGlvbnNFbnRyeRIfChdzdWdnZXN0aW9uc19icm93c2VyX2tleRgIIAEoCRoyChBTdWdnZXN0aW9uc0VudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoCToCOAEiNAoUQWN0aW9uQXJndW1lbnRDaG9pY2USDQoFdmFsdWUYASABKAkSDQoFdGl0bGUYAiABKAki1AEKE0VudGl0eVJlbGF0ZWRBY3Rpb24SJwoGYWN0aW9uGAEgASgLMhcub2xpdmV0aW4uYXBpLnYxLkFjdGlvbhJZChNwcmVmaWxsZWRfYXJndW1lbnRzGAIgAygLMjwub2xpdmV0aW4uYXBpLnYxLkVudGl0eVJlbGF0ZWRBY3Rpb24uUHJlZmlsbGVkQXJndW1lbnRzRW50cnkaOQoXUHJlZmlsbGVkQXJndW1lbnRzRW50cnkSCwoDa2V5GAEgASgJEg0KBXZhbHVlGAIgASgJOgI4ASL/AQoGRW50aXR5Eg0KBXRpdGxlGAEgASgJEhIKCnVuaXF1ZV9rZXkYAiABKAkSDAoEdHlwZRgDIAEoCRITCgtkaXJlY3RvcmllcxgEIAMoCRIzCgZmaWVsZHMYBSADKAsyIy5vbGl2ZXRpbi5hcGkudjEuRW50aXR5LkZpZWxkc0VudHJ5Ej0KD3JlbGF0ZWRfYWN0aW9ucxgGIAMoCzIkLm9saXZldGluLmFwaS52MS5FbnRpdHlSZWxhdGVkQWN0aW9uEgwKBGljb24YByABKAkaLQoLRmllbGRzRW50cnkSCwoDa2V5GAEgASgJEg0KBXZhbHVlGAIgASgJOgI4ASJUChRHZXREYXNoYm9hcmRSZXNwb25zZRINCgV0aXRsZRgBIAEoCRItCglkYXNoYm9hcmQYBCABKAsyGi5vbGl2ZXRpbi5hcGkudjEuRGFzaGJvYXJkIl8KD0VmZmVjdGl2ZVBvbGljeRIYChBzaG93X2RpYWdub3N0aWNzGAEgASgIEhUKDXNob3dfbG9nX2xpc3QYAiABKAgSGwoTc2hvd192ZXJzaW9uX251bWJlchgDIAEoCCJNChNHZXREYXNoYm9hcmRSZXF1ZXN0Eg0KBXRpdGxlGAEgASgJEhMKC2VudGl0eV90eXBlGAIgASgJEhIKCmVudGl0eV9rZXkYAyABKAkiUQoJRGFzaGJvYXJkEg0KBXRpdGxlGAEgASgJEjUKCGNvbnRlbnRzGAIgAygLMiMub2xpdmV0aW4uYXBpLnYxLkRhc2hib2FyZENvbXBvbmVudCLbAQoSRGFzaGJvYXJkQ29tcG9uZW50Eg0KBXRpdGxlGAEgASgJEgwKBHR5cGUYAiABKAkSNQoIY29udGVudHMYAyADKAsyIy5vbGl2ZXRpbi5hcGkudjEuRGFzaGJvYXJkQ29tcG9uZW50EgwKBGljb24YBCABKAkSEQoJY3NzX2NsYXNzGAUgASgJEicKBmFjdGlvbhgGIAEoCzIXLm9saXZldGluLmFwaS52MS5BY3Rpb24SEwoLZW50aXR5X3R5cGUYByABKAkSEgoKZW50aXR5X2tleRgIIAEoCSKUAQoSU3RhcnRBY3Rpb25SZXF1ZXN0EhIKCmJpbmRpbmdfaWQYASABKAkSNwoJYXJndW1lbnRzGAIgAygLMiQub2xpdmV0aW4uYXBpLnYxLlN0YXJ0QWN0aW9uQXJndW1lbnQSGgoSdW5pcXVlX3RyYWNraW5nX2lkGAMgASgJEhUKDWp1c3RpZmljYXRpb24YBCABKAkiMgoTU3RhcnRBY3Rpb25Bcmd1bWVudBIMCgRuYW1lGAEgASgJEg0KBXZhbHVlGAIgASgJIjQKE1N0YXJ0QWN0aW9uUmVzcG9uc2USHQoVZXhlY3V0aW9uX3RyYWNraW5nX2lkGAIgASgJIn4KGVN0YXJ0QWN0aW9uQW5kV2FpdFJlcXVlc3QSEQoJYWN0aW9uX2lkGAEgASgJEjcKCWFyZ3VtZW50cxgCIAMoCzIkLm9saXZldGluLmFwaS52MS5TdGFydEFjdGlvbkFyZ3VtZW50EhUKDWp1c3RpZmljYXRpb24YAyABKAkiSgoaU3RhcnRBY3Rpb25BbmRXYWl0UmVzcG9uc2USLAoJbG9nX2VudHJ5GAEgASgLMhkub2xpdmV0aW4uYXBpLnYxLkxvZ0VudHJ5IiwKF1N0YXJ0QWN0aW9uQnlHZXRSZXF1ZXN0EhEKCWFjdGlvbl9pZBgBIAEoCSI5ChhTdGFydEFjdGlvbkJ5R2V0UmVzcG9uc2USHQoVZXhlY3V0aW9uX3RyYWNraW5nX2lkGAIgASgJIjMKHlN0YXJ0QWN0aW9uQnlHZXRBbmRXYWl0UmVxdWVzdBIRCglhY3Rpb25faWQYASABKAkiTwofU3RhcnRBY3Rpb25CeUdldEFuZFdhaXRSZXNwb25zZRIsCglsb2dfZW50cnkYASABKAsyGS5vbGl2ZXRpbi5hcGkudjEuTG9nRW50cnkiXgoOR2V0TG9nc1JlcXVlc3QSFAoMc3RhcnRfb2Zmc2V0GAEgASgDEhMKC2RhdGVfZmlsdGVyGAIgASgJEhEKCXBhZ2Vfc2l6ZRgDIAEoAxIOCgZmaWx0ZXIYBCABKAkilAQKCExvZ0VudHJ5EhgKEGRhdGV0aW1lX3N0YXJ0ZWQYASABKAkSFAoMYWN0aW9uX3RpdGxlGAIgASgJEg4KBm91dHB1dBgDIAEoCRIRCgl0aW1lZF9vdXQYBSABKAgSEQoJZXhpdF9jb2RlGAYgASgFEgwKBHVzZXIYByABKAkSEgoKdXNlcl9jbGFzcxgIIAEoCRITCgthY3Rpb25faWNvbhgJIAEoCRIMCgR0YWdzGAogAygJEh0KFWV4ZWN1dGlvbl90cmFja2luZ19pZBgLIAEoCRIZChFkYXRldGltZV9maW5pc2hlZBgMIAEoCRIZChFleGVjdXRpb25fc3RhcnRlZBgOIAEoCBIaChJleGVjdXRpb25fZmluaXNoZWQYDyABKAgSDwoHYmxvY2tlZBgQIAEoCBIWCg5kYXRldGltZV9pbmRleBgRIAEoAxIQCghjYW5fa2lsbBgSIAEoCBIjChtkYXRldGltZV9yYXRlX2xpbWl0X2V4cGlyZXMYEyABKAkSEgoKYmluZGluZ19pZBgUIAEoCRIOCgZxdWV1ZWQYFSABKAgSGAoQcXVldWVkX2Zvcl9ncm91cBgWIAEoCRIVCg1qdXN0aWZpY2F0aW9uGBcgASgJEjcKCWFyZ3VtZW50cxgYIAMoCzIkLm9saXZldGluLmFwaS52MS5TdGFydEFjdGlvbkFyZ3VtZW50IpEBCg9HZXRMb2dzUmVzcG9uc2USJwoEbG9ncxgBIAMoCzIZLm9saXZldGluLmFwaS52MS5Mb2dFbnRyeRIXCg9jb3VudF9yZW1haW5pbmcYAiABKAMSEQoJcGFnZV9zaXplGAMgASgDEhMKC3RvdGFsX2NvdW50GAQgASgDEhQKDHN0YXJ0X29mZnNldBgFIAEoAyI/ChRHZXRBY3Rpb25Mb2dzUmVxdWVzdBIRCglhY3Rpb25faWQYASABKAkSFAoMc3RhcnRfb2Zmc2V0GAIgASgDIpcBChVHZXRBY3Rpb25Mb2dzUmVzcG9uc2USJwoEbG9ncxgBIAMoCzIZLm9saXZldGluLmFwaS52MS5Mb2dFbnRyeRIXCg9jb3VudF9yZW1haW5pbmcYAiABKAMSEQoJcGFnZV9zaXplGAMgASgDEhMKC3RvdGFsX2NvdW50GAQgASgDEhQKDHN0YXJ0X29mZnNldBgFIAEoAyIaChhHZXRFeGVjdXRpb25RdWV1ZVJlcXVlc3QixgEKFEV4ZWN1dGlvblF1ZXVlQWN0aW9uEhIKCmJpbmRpbmdfaWQYASABKAkSFAoMYWN0aW9uX3RpdGxlGAIgASgJEhMKC2FjdGlvbl9pY29uGAMgASgJEhYKDm1heF9jb25jdXJyZW50GAQgASgFEhQKDGFjdGl2ZV9jb3VudBgFIAEoBRIVCg1lbnRpdHlfcHJlZml4GAYgASgJEioKB2VudHJpZXMYByADKAsyGS5vbGl2ZXRpbi5hcGkudjEuTG9nRW50cnkiwQEKE0V4ZWN1dGlvblF1ZXVlR3JvdXASDAoEbmFtZRgBIAEoCRIMCgRpY29uGAIgASgJEhYKDm1heF9jb25jdXJyZW50GAMgASgFEhQKDGFjdGl2ZV9jb3VudBgEIAEoBRI2CgdhY3Rpb25zGAUgAygLMiUub2xpdmV0aW4uYXBpLnYxLkV4ZWN1dGlvblF1ZXVlQWN0aW9uEhQKDHF1ZXVlZF9jb3VudBgGIAEoBRISCgpxdWV1ZV9zaXplGAcgASgFImcKGUdldEV4ZWN1dGlvblF1ZXVlUmVzcG9uc2USNAoGZ3JvdXBzGAEgAygLMiQub2xpdmV0aW4uYXBpLnYxLkV4ZWN1dGlvblF1ZXVlR3JvdXASFAoMdG90YWxfYWN0aXZlGAIgASgFImUKG1ZhbGlkYXRlQXJndW1lbnRUeXBlUmVxdWVzdBINCgV2YWx1ZRgBIAEoCRIMCgR0eXBlGAIgASgJEhIKCmJpbmRpbmdfaWQYAyABKAkSFQoNYXJndW1lbnRfbmFtZRgEIAEoCSJCChxWYWxpZGF0ZUFyZ3VtZW50VHlwZVJlc3BvbnNlEg0KBXZhbGlkGAEgASgIEhMKC2Rlc2NyaXB0aW9uGAIgASgJIjYKFVdhdGNoRXhlY3V0aW9uUmVxdWVzdBIdChVleGVjdXRpb25fdHJhY2tpbmdfaWQYASABKAkiJgoUV2F0Y2hFeGVjdXRpb25VcGRhdGUSDgoGdXBkYXRlGAEgASgJIkoKFkV4ZWN1dGlvblN0YXR1c1JlcXVlc3QSHQoVZXhlY3V0aW9uX3RyYWNraW5nX2lkGAEgASgJEhEKCWFjdGlvbl9pZBgCIAEoCSJhChlEYXNoYm9hcmROYXZpZ2F0aW9uVGFyZ2V0Eg0KBXRpdGxlGAEgASgJEhMKC2VudGl0eV90eXBlGAIgASgJEhIKCmVudGl0eV9rZXkYAyABKAkSDAoEcGF0aBgEIAEoCSKPAQoXRXhlY3V0aW9uU3RhdHVzUmVzcG9uc2USLAoJbG9nX2VudHJ5GAEgASgLMhkub2xpdmV0aW4uYXBpLnYxLkxvZ0VudHJ5EkYKEmJhY2tfdG9fZGFzaGJvYXJkcxgCIAMoCzIqLm9saXZldGluLmFwaS52MS5EYXNoYm9hcmROYXZpZ2F0aW9uVGFyZ2V0Ig8KDVdob0FtSVJlcXVlc3QibAoOV2hvQW1JUmVzcG9uc2USGgoSYXV0aGVudGljYXRlZF91c2VyGAEgASgJEhEKCXVzZXJncm91cBgCIAEoCRIQCghwcm92aWRlchgDIAEoCRIMCgRhY2xzGAQgAygJEgsKA3NpZBgFIAEoCSIaChhTZXJ2ZXJEaWFnbm9zdGljc1JlcXVlc3QiKgoZU2VydmVyRGlhZ25vc3RpY3NSZXNwb25zZRINCgVhbGVydBgBIAEoCSIRCg9EdW1wVmFyc1JlcXVlc3QilQEKEER1bXBWYXJzUmVzcG9uc2USDQoFYWxlcnQYASABKAkSQQoIY29udGVudHMYAiADKAsyLy5vbGl2ZXRpbi5hcGkudjEuRHVtcFZhcnNSZXNwb25zZS5Db250ZW50c0VudHJ5Gi8KDUNvbnRlbnRzRW50cnkSCwoDa2V5GAEgASgJEg0KBXZhbHVlGAIgASgJOgI4ASI7CgxEZWJ1Z0JpbmRpbmcSFAoMYWN0aW9uX3RpdGxlGAEgASgJEhUKDWVudGl0eV9wcmVmaXgYAiABKAkiHgocRHVtcFB1YmxpY0lkQWN0aW9uTWFwUmVxdWVzdCLOAQodRHVtcFB1YmxpY0lkQWN0aW9uTWFwUmVzcG9uc2USDQoFYWxlcnQYASABKAkSTgoIY29udGVudHMYAiADKAsyPC5vbGl2ZXRpbi5hcGkudjEuRHVtcFB1YmxpY0lkQWN0aW9uTWFwUmVzcG9uc2UuQ29udGVudHNFbnRyeRpOCg1Db250ZW50c0VudHJ5EgsKA2tleRgBIAEoCRIsCgV2YWx1ZRgCIAEoCzIdLm9saXZldGluLmFwaS52MS5EZWJ1Z0JpbmRpbmc6AjgBIhIKEEdldFJlYWR5elJlcXVlc3QiIwoRR2V0UmVhZHl6UmVzcG9uc2USDgoGc3RhdHVzGAEgASgJIhQKEkV2ZW50U3RyZWFtUmVxdWVzdCKZAwoTRXZlbnRTdHJlYW1SZXNwb25zZRI9Cg5lbnRpdHlfY2hhbmdlZBgCIAEoCzIjLm9saXZldGluLmFwaS52MS5FdmVudEVudGl0eUNoYW5nZWRIABI9Cg5jb25maWdfY2hhbmdlZBgDIAEoCzIjLm9saXZldGluLmFwaS52MS5FdmVudENvbmZpZ0NoYW5nZWRIABJFChJleGVjdXRpb25fZmluaXNoZWQYBCABKAsyJy5vbGl2ZXRpbi5hcGkudjEuRXZlbnRFeGVjdXRpb25GaW5pc2hlZEgAEkMKEWV4ZWN1dGlvbl9zdGFydGVkGAUgASgLMiYub2xpdmV0aW4uYXBpLnYxLkV2ZW50RXhlY3V0aW9uU3RhcnRlZEgAEjkKDG91dHB1dF9jaHVuaxgGIAEoCzIhLm9saXZldGluLmFwaS52MS5FdmVudE91dHB1dENodW5rSAASNAoJaGVhcnRiZWF0GAcgASgLMh8ub2xpdmV0aW4uYXBpLnYxLkV2ZW50SGVhcnRiZWF0SABCBwoFZXZlbnQiQQoQRXZlbnRPdXRwdXRDaHVuaxIdChVleGVjdXRpb25fdHJhY2tpbmdfaWQYASABKAkSDgoGb3V0cHV0GAIgASgJIhQKEkV2ZW50RW50aXR5Q2hhbmdlZCIUChJFdmVudENvbmZpZ0NoYW5nZWQiEAoORXZlbnRIZWFydGJlYXQiRgoWRXZlbnRFeGVjdXRpb25GaW5pc2hlZBIsCglsb2dfZW50cnkYASABKAsyGS5vbGl2ZXRpbi5hcGkudjEuTG9nRW50cnkiRQoVRXZlbnRFeGVjdXRpb25TdGFydGVkEiwKCWxvZ19lbnRyeRgBIAEoCzIZLm9saXZldGluLmFwaS52MS5Mb2dFbnRyeSIyChFLaWxsQWN0aW9uUmVxdWVzdBIdChVleGVjdXRpb25fdHJhY2tpbmdfaWQYASABKAkibQoSS2lsbEFjdGlvblJlc3BvbnNlEh0KFWV4ZWN1dGlvbl90cmFja2luZ19pZBgBIAEoCRIOCgZraWxsZWQYAiABKAgSGQoRYWxyZWFkeV9jb21wbGV0ZWQYAyABKAgSDQoFZm91bmQYBCABKAgiOwoVTG9jYWxVc2VyTG9naW5SZXF1ZXN0EhAKCHVzZXJuYW1lGAEgASgJEhAKCHBhc3N3b3JkGAIgASgJIikKFkxvY2FsVXNlckxvZ2luUmVzcG9uc2USDwoHc3VjY2VzcxgBIAEoCCInChNQYXNzd29yZEhhc2hSZXF1ZXN0EhAKCHBhc3N3b3JkGAEgASgJIiQKFFBhc3N3b3JkSGFzaFJlc3BvbnNlEgwKBGhhc2gYASABKAkiDwoNTG9nb3V0UmVxdWVzdCIQCg5Mb2dvdXRSZXNwb25zZSIXChVHZXREaWFnbm9zdGljc1JlcXVlc3QiRQoWR2V0RGlhZ25vc3RpY3NSZXNwb25zZRITCgtTc2hGb3VuZEtleRgBIAEoCRIWCg5Tc2hGb3VuZENvbmZpZxgCIAEoCSINCgtJbml0UmVxdWVzdCLrBQoMSW5pdFJlc3BvbnNlEhIKCnNob3dGb290ZXIYASABKAgSFgoOc2hvd05hdmlnYXRpb24YAiABKAgSFwoPc2hvd05ld1ZlcnNpb25zGAMgASgIEhgKEGF2YWlsYWJsZVZlcnNpb24YBCABKAkSFgoOY3VycmVudFZlcnNpb24YBSABKAkSEQoJcGFnZVRpdGxlGAYgASgJEh4KFnNlY3Rpb25OYXZpZ2F0aW9uU3R5bGUYByABKAkSGgoSZGVmYXVsdEljb25Gb3JCYWNrGAggASgJEhYKDmVuYWJsZUN1c3RvbUpzGAkgASgIEhQKDGF1dGhMb2dpblVybBgKIAEoCRIWCg5hdXRoTG9jYWxMb2dpbhgLIAEoCBIRCglzdHlsZU1vZHMYDCADKAkSOAoPb0F1dGgyUHJvdmlkZXJzGA0gAygLMh8ub2xpdmV0aW4uYXBpLnYxLk9BdXRoMlByb3ZpZGVyEjgKD2FkZGl0aW9uYWxMaW5rcxgOIAMoCzIfLm9saXZldGluLmFwaS52MS5BZGRpdGlvbmFsTGluaxIWCg5yb290RGFzaGJvYXJkcxgPIAMoCRIaChJhdXRoZW50aWNhdGVkX3VzZXIYECABKAkSIwobYXV0aGVudGljYXRlZF91c2VyX3Byb3ZpZGVyGBEgASgJEjoKEGVmZmVjdGl2ZV9wb2xpY3kYEiABKAsyIC5vbGl2ZXRpbi5hcGkudjEuRWZmZWN0aXZlUG9saWN5EhYKDmJhbm5lcl9tZXNzYWdlGBMgASgJEhIKCmJhbm5lcl9jc3MYFCABKAkSGAoQc2hvd19kaWFnbm9zdGljcxgVIAEoCBIVCg1zaG93X2xvZ19saXN0GBYgASgIEhYKDmxvZ2luX3JlcXVpcmVkGBcgASgIEhgKEGF2YWlsYWJsZV90aGVtZXMYGCADKAkSJAocc2hvd19uYXZpZ2F0ZV9vbl9zdGFydF9pY29ucxgZIAEoCCIsCg5BZGRpdGlvbmFsTGluaxINCgV0aXRsZRgBIAEoCRILCgN1cmwYAiABKAkiOgoOT0F1dGgyUHJvdmlkZXISDQoFdGl0bGUYASABKAkSDAoEaWNvbhgDIAEoCRILCgNrZXkYBCABKAkiLQoXR2V0QWN0aW9uQmluZGluZ1JlcXVlc3QSEgoKYmluZGluZ19pZBgBIAEoCSKLAQoYR2V0QWN0aW9uQmluZGluZ1Jlc3BvbnNlEicKBmFjdGlvbhgBIAEoCzIXLm9saXZldGluLmFwaS52MS5BY3Rpb24SRgoSYmFja190b19kYXNoYm9hcmRzGAIgAygLMioub2xpdmV0aW4uYXBpLnYxLkRhc2hib2FyZE5hdmlnYXRpb25UYXJnZXQiWgoSR2V0RW50aXRpZXNSZXF1ZXN0EhMKC2VudGl0eV90eXBlGAEgASgJEg4KBmZpbHRlchgCIAEoCRIMCgRwYWdlGAMgASgFEhEKCXBhZ2Vfc2l6ZRgEIAEoBSJUChNHZXRFbnRpdGllc1Jlc3BvbnNlEj0KEmVudGl0eV9kZWZpbml0aW9ucxgBIAMoCzIhLm9saXZldGluLmFwaS52MS5FbnRpdHlEZWZpbml0aW9uIsUBChBFbnRpdHlEZWZpbml0aW9uEg0KBXRpdGxlGAEgASgJEioKCWluc3RhbmNlcxgCIAMoCzIXLm9saXZldGluLmFwaS52MS5FbnRpdHkSGgoSdXNlZF9vbl9kYXNoYm9hcmRzGAMgAygJEgwKBGljb24YBCABKAkSMwoKcHJvcGVydGllcxgFIAMoCzIfLm9saXZldGluLmFwaS52MS5FbnRpdHlQcm9wZXJ0eRIXCg90b3RhbF9pbnN0YW5jZXMYBiABKAUiLQoORW50aXR5UHJvcGVydHkSDAoEbmFtZRgBIAEoCRINCgV0aXRsZRgCIAEoCSI0ChBHZXRFbnRpdHlSZXF1ZXN0EhIKCnVuaXF1ZV9rZXkYASABKAkSDAoEdHlwZRgCIAEoCSI1ChRSZXN0YXJ0QWN0aW9uUmVxdWVzdBIdChVleGVjdXRpb25fdHJhY2tpbmdfaWQYASABKAky7hMKEk9saXZlVGluQXBpU2VydmljZRJdCgxHZXREYXNoYm9hcmQSJC5vbGl2ZXRpbi5hcGkudjEuR2V0RGFzaGJvYXJkUmVxdWVzdBolLm9saXZldGluLmFwaS52MS5HZXREYXNoYm9hcmRSZXNwb25zZSIAEloKC1N0YXJ0QWN0aW9uEiMub2xpdmV0aW4uYXBpLnYxLlN0YXJ0QWN0aW9uUmVxdWVzdBokLm9saXZldGluLmFwaS52MS5TdGFydEFjdGlvblJlc3BvbnNlIgASbwoSU3RhcnRBY3Rpb25BbmRXYWl0Eioub2xpdmV0aW4uYXBpLnYxLlN0YXJ0QWN0aW9uQW5kV2FpdFJlcXVlc3QaKy5vbGl2ZXRpbi5hcGkudjEuU3RhcnRBY3Rpb25BbmRXYWl0UmVzcG9uc2UiABJpChBTdGFydEFjdGlvbkJ5R2V0Eigub2xpdmV0aW4uYXBpLnYxLlN0YXJ0QWN0aW9uQnlHZXRSZXF1ZXN0Gikub2xpdmV0aW4uYXBpLnYxLlN0YXJ0QWN0aW9uQnlHZXRSZXNwb25zZSIAEn4KF1N0YXJ0QWN0aW9uQnlHZXRBbmRXYWl0Ei8ub2xpdmV0aW4uYXBpLnYxLlN0YXJ0QWN0aW9uQnlHZXRBbmRXYWl0UmVxdWVzdBowLm9saXZldGluLmFwaS52MS5TdGFydEFjdGlvbkJ5R2V0QW5kV2FpdFJlc3BvbnNlIgASXgoNUmVzdGFydEFjdGlvbhIlLm9saXZldGluLmFwaS52MS5SZXN0YXJ0QWN0aW9uUmVxdWVzdBokLm9saXZldGluLmFwaS52MS5TdGFydEFjdGlvblJlc3BvbnNlIgASVwoKS2lsbEFjdGlvbhIiLm9saXZldGluLmFwaS52MS5LaWxsQWN0aW9uUmVxdWVzdBojLm9saXZldGluLmFwaS52MS5LaWxsQWN0aW9uUmVzcG9uc2UiABJmCg9FeGVjdXRpb25TdGF0dXMSJy5vbGl2ZXRpbi5hcGkudjEuRXhlY3V0aW9uU3RhdHVzUmVxdWVzdBooLm9saXZldGluLmFwaS52MS5FeGVjdXRpb25TdGF0dXNSZXNwb25zZSIAEk4KB0dldExvZ3MSHy5vbGl2ZXRpbi5hcGkudjEuR2V0TG9nc1JlcXVlc3QaIC5vbGl2ZXRpbi5hcGkudjEuR2V0TG9nc1Jlc3BvbnNlIgASYAoNR2V0QWN0aW9uTG9ncxIlLm9saXZldGluLmFwaS52MS5HZXRBY3Rpb25Mb2dzUmVxdWVzdBomLm9saXZldGluLmFwaS52MS5HZXRBY3Rpb25Mb2dzUmVzcG9uc2UiABJsChFHZXRFeGVjdXRpb25RdWV1ZRIpLm9saXZldGluLmFwaS52MS5HZXRFeGVjdXRpb25RdWV1ZVJlcXVlc3QaKi5vbGl2ZXRpbi5hcGkudjEuR2V0RXhlY3V0aW9uUXVldWVSZXNwb25zZSIAEnUKFFZhbGlkYXRlQXJndW1lbnRUeXBlEiwub2xpdmV0aW4uYXBpLnYxLlZhbGlkYXRlQXJndW1lbnRUeXBlUmVxdWVzdBotLm9saXZldGluLmFwaS52MS5WYWxpZGF0ZUFyZ3VtZW50VHlwZVJlc3BvbnNlIgASSwoGV2hvQW1JEh4ub2xpdmV0aW4uYXBpLnYxLldob0FtSVJlcXVlc3QaHy5vbGl2ZXRpbi5hcGkudjEuV2hvQW1JUmVzcG9uc2UiABJsChFTZXJ2ZXJEaWFnbm9zdGljcxIpLm9saXZldGluLmFwaS52MS5TZXJ2ZXJEaWFnbm9zdGljc1JlcXVlc3QaKi5vbGl2ZXRpbi5hcGkudjEuU2VydmVyRGlhZ25vc3RpY3NSZXNwb25zZSIAElEKCER1bXBWYXJzEiAub2xpdmV0aW4uYXBpLnYxLkR1bXBWYXJzUmVxdWVzdBohLm9saXZldGluLmFwaS52MS5EdW1wVmFyc1Jlc3BvbnNlIgASeAoVRHVtcFB1YmxpY0lkQWN0aW9uTWFwEi0ub2xpdmV0aW4uYXBpLnYxLkR1bXBQdWJsaWNJZEFjdGlvbk1hcFJlcXVlc3QaLi5vbGl2ZXRpbi5hcGkudjEuRHVtcFB1YmxpY0lkQWN0aW9uTWFwUmVzcG9uc2UiABJUCglHZXRSZWFkeXoSIS5vbGl2ZXRpbi5hcGkudjEuR2V0UmVhZHl6UmVxdWVzdBoiLm9saXZldGluLmFwaS52MS5HZXRSZWFkeXpSZXNwb25zZSIAEmMKDkxvY2FsVXNlckxvZ2luEiYub2xpdmV0aW4uYXBpLnYxLkxvY2FsVXNlckxvZ2luUmVxdWVzdBonLm9saXZldGluLmFwaS52MS5Mb2NhbFVzZXJMb2dpblJlc3BvbnNlIgASXQoMUGFzc3dvcmRIYXNoEiQub2xpdmV0aW4uYXBpLnYxLlBhc3N3b3JkSGFzaFJlcXVlc3QaJS5vbGl2ZXRpbi5hcGkudjEuUGFzc3dvcmRIYXNoUmVzcG9uc2UiABJLCgZMb2dvdXQSHi5vbGl2ZXRpbi5hcGkudjEuTG9nb3V0UmVxdWVzdBofLm9saXZldGluLmFwaS52MS5Mb2dvdXRSZXNwb25zZSIAElwKC0V2ZW50U3RyZWFtEiMub2xpdmV0aW4uYXBpLnYxLkV2ZW50U3RyZWFtUmVxdWVzdBokLm9saXZldGluLmFwaS52MS5FdmVudFN0cmVhbVJlc3BvbnNlIgAwARJjCg5HZXREaWFnbm9zdGljcxImLm9saXZldGluLmFwaS52MS5HZXREaWFnbm9zdGljc1JlcXVlc3QaJy5vbGl2ZXRpbi5hcGkudjEuR2V0RGlhZ25vc3RpY3NSZXNwb25zZSIAEkUKBEluaXQSHC5vbGl2ZXRpbi5hcGkudjEuSW5pdFJlcXVlc3QaHS5vbGl2ZXRpbi5hcGkudjEuSW5pdFJlc3BvbnNlIgASaQoQR2V0QWN0aW9uQmluZGluZxIoLm9saXZldGluLmFwaS52MS5HZXRBY3Rpb25CaW5kaW5nUmVxdWVzdBopLm9saXZldGluLmFwaS52MS5HZXRBY3Rpb25CaW5kaW5nUmVzcG9uc2UiABJaCgtHZXRFbnRpdGllcxIjLm9saXZldGluLmFwaS52MS5HZXRFbnRpdGllc1JlcXVlc3QaJC5vbGl2ZXRpbi5hcGkudjEuR2V0RW50aXRpZXNSZXNwb25zZSIAEkkKCUdldEVudGl0eRIhLm9saXZldGluLmFwaS52MS5HZXRFbnRpdHlSZXF1ZXN0Ghcub2xpdmV0aW4uYXBpLnYxLkVudGl0eSIAQjhaNmdpdGh1Yi5jb20vT2xpdmVUaW4vT2xpdmVUaW4vZ2VuL29saXZldGluL2FwaS92MTthcGl2MWIGcHJvdG8z"); /** * Describes the message olivetin.api.v1.Action. @@ -284,17 +284,17 @@ export const WhoAmIResponseSchema = /*@__PURE__*/ messageDesc(file_olivetin_api_v1_olivetin, 38); /** - * Describes the message olivetin.api.v1.SosReportRequest. - * Use `create(SosReportRequestSchema)` to create a new message. + * Describes the message olivetin.api.v1.ServerDiagnosticsRequest. + * Use `create(ServerDiagnosticsRequestSchema)` to create a new message. */ -export const SosReportRequestSchema = /*@__PURE__*/ +export const ServerDiagnosticsRequestSchema = /*@__PURE__*/ messageDesc(file_olivetin_api_v1_olivetin, 39); /** - * Describes the message olivetin.api.v1.SosReportResponse. - * Use `create(SosReportResponseSchema)` to create a new message. + * Describes the message olivetin.api.v1.ServerDiagnosticsResponse. + * Use `create(ServerDiagnosticsResponseSchema)` to create a new message. */ -export const SosReportResponseSchema = /*@__PURE__*/ +export const ServerDiagnosticsResponseSchema = /*@__PURE__*/ messageDesc(file_olivetin_api_v1_olivetin, 40); /** @@ -561,4 +561,3 @@ export const RestartActionRequestSchema = /*@__PURE__*/ */ export const OliveTinApiService = /*@__PURE__*/ serviceDesc(file_olivetin_api_v1_olivetin, 0); - diff --git a/frontend/resources/vue/views/DiagnosticsView.vue b/frontend/resources/vue/views/DiagnosticsView.vue index 675ffd9..b9aea9f 100644 --- a/frontend/resources/vue/views/DiagnosticsView.vue +++ b/frontend/resources/vue/views/DiagnosticsView.vue @@ -18,18 +18,18 @@ - - {{ t('diagnostics.sos-report-description') }} + + {{ t('diagnostics.server-diagnostics-description') }} - {{ t('diagnostics.sos-report-docs') }} + {{ t('diagnostics.server-diagnostics-docs') }} - {{ t('diagnostics.generate-sos-report') }} - {{ sosReportCopied ? t('diagnostics.copied') : t('diagnostics.copy-to-clipboard') }} + {{ t('diagnostics.generate-server-diagnostics') }} + {{ serverDiagnosticsCopied ? t('diagnostics.copied') : t('diagnostics.copy-to-clipboard') }} - + @@ -53,9 +53,9 @@ const { t, locale } = useI18n() const diagnostics = ref({}) const loading = ref(false) -const sosReport = ref('') +const serverDiagnostics = ref('') const browserInfo = ref('') -const sosReportCopied = ref(false) +const serverDiagnosticsCopied = ref(false) const browserInfoCopied = ref(false) async function fetchDiagnostics() { @@ -84,21 +84,21 @@ function formatKey(key) { .trim() } -async function generateSosReport() { - const response = await window.client.sosReport() +async function generateServerDiagnostics() { + const response = await window.client.serverDiagnostics() console.log("response", response) - sosReport.value = `\`\`\`\n${response.alert}\n\`\`\`\n` + serverDiagnostics.value = `\`\`\`\n${response.alert}\n\`\`\`\n` } -async function copySosReport() { +async function copyServerDiagnostics() { try { - await navigator.clipboard.writeText(sosReport.value) - sosReportCopied.value = true + await navigator.clipboard.writeText(serverDiagnostics.value) + serverDiagnosticsCopied.value = true setTimeout(() => { - sosReportCopied.value = false + serverDiagnosticsCopied.value = false }, 2000) } catch (err) { - console.error('Failed to copy SOS report to clipboard:', err) + console.error('Failed to copy Server Diagnostics to clipboard:', err) } } diff --git a/lang/combined_output.json b/lang/combined_output.json index 962be84..1a166f8 100644 --- a/lang/combined_output.json +++ b/lang/combined_output.json @@ -10,12 +10,12 @@ "diagnostics.found-config": "Konfiguration gefunden", "diagnostics.found-key": "Schlüssel gefunden", "diagnostics.generate-browser-info": "Browser-Informationen erstellen", - "diagnostics.generate-sos-report": "SOS-Bericht erstellen", + "diagnostics.generate-server-diagnostics": "Server-Diagnostik erstellen", "diagnostics.get-support": "Unterstützung erhalten", - "diagnostics.get-support-description": "Wenn Sie Probleme mit OliveTin haben und eine Support-Anfrage stellen möchten, wäre es sehr hilfreich, einen sosreport von dieser Seite einzufügen.", - "diagnostics.sos-report": "SOS-Bericht", - "diagnostics.sos-report-description": "Dieser Abschnitt ermöglicht es Ihnen, einen detaillierten Bericht über Ihre Konfiguration und Umgebung zu erstellen. Es ist eine gute Idee, dies bei einer Support-Anfrage einzufügen.", - "diagnostics.sos-report-docs": "sosreport Dokumentation", + "diagnostics.get-support-description": "Wenn Sie Probleme mit OliveTin haben und eine Support-Anfrage stellen möchten, wäre es sehr hilfreich, Server-Diagnostik von dieser Seite einzufügen.", + "diagnostics.server-diagnostics": "Server-Diagnostik", + "diagnostics.server-diagnostics-description": "Dieser Abschnitt ermöglicht es Ihnen, einen detaillierten Bericht über Ihre Konfiguration und Umgebung zu erstellen. Es ist eine gute Idee, dies bei einer Support-Anfrage einzufügen.", + "diagnostics.server-diagnostics-docs": "Server-Diagnostik-Dokumentation", "diagnostics.ssh": "SSH", "diagnostics.unknown": "Unbekannt", "diagnostics.useragent-data-error": "Fehler beim Abrufen von userAgentData", @@ -81,12 +81,12 @@ "diagnostics.found-config": "Found Config", "diagnostics.found-key": "Found Key", "diagnostics.generate-browser-info": "Generate Browser Info", - "diagnostics.generate-sos-report": "Generate SOS Report", + "diagnostics.generate-server-diagnostics": "Generate Server Diagnostics", "diagnostics.get-support": "Get support", - "diagnostics.get-support-description": "If you are having problems with OliveTin and want to raise a support request, it would be very helpful to include a sosreport from this page.", - "diagnostics.sos-report": "SOS Report", - "diagnostics.sos-report-description": "This section allows you to generate a detailed report of your configuration and environment. It is a good idea to include this when raising a support request.", - "diagnostics.sos-report-docs": "sosreport Documentation", + "diagnostics.get-support-description": "If you are having problems with OliveTin and want to raise a support request, it would be very helpful to include Server Diagnostics from this page.", + "diagnostics.server-diagnostics": "Server Diagnostics", + "diagnostics.server-diagnostics-description": "This section allows you to generate a detailed report of your configuration and environment. It is a good idea to include this when raising a support request.", + "diagnostics.server-diagnostics-docs": "Server Diagnostics Documentation", "diagnostics.ssh": "SSH", "diagnostics.unknown": "Unknown", "diagnostics.useragent-data-error": "Error retrieving userAgentData", @@ -160,12 +160,12 @@ "diagnostics.found-config": "Configuración encontrada", "diagnostics.found-key": "Clave encontrada", "diagnostics.generate-browser-info": "Generar información del navegador", - "diagnostics.generate-sos-report": "Generar informe SOS", + "diagnostics.generate-server-diagnostics": "Generar diagnóstico del servidor", "diagnostics.get-support": "Obtener soporte", - "diagnostics.get-support-description": "Si tiene problemas con OliveTin y desea presentar una solicitud de soporte, sería muy útil incluir un sosreport de esta página.", - "diagnostics.sos-report": "Informe SOS", - "diagnostics.sos-report-description": "Esta sección le permite generar un informe detallado de su configuración y entorno. Es una buena idea incluir esto al presentar una solicitud de soporte.", - "diagnostics.sos-report-docs": "Documentación de sosreport", + "diagnostics.get-support-description": "Si tiene problemas con OliveTin y desea presentar una solicitud de soporte, sería muy útil incluir diagnósticos del servidor desde esta página.", + "diagnostics.server-diagnostics": "Diagnóstico del servidor", + "diagnostics.server-diagnostics-description": "Esta sección le permite generar un informe detallado de su configuración y entorno. Es una buena idea incluir esto al presentar una solicitud de soporte.", + "diagnostics.server-diagnostics-docs": "Documentación de diagnóstico del servidor", "diagnostics.ssh": "SSH", "diagnostics.unknown": "Desconocido", "diagnostics.useragent-data-error": "Error al recuperar userAgentData", @@ -231,12 +231,12 @@ "diagnostics.found-config": "Configurazione trovata", "diagnostics.found-key": "Chiave trovata", "diagnostics.generate-browser-info": "Genera informazioni del browser", - "diagnostics.generate-sos-report": "Genera rapporto SOS", + "diagnostics.generate-server-diagnostics": "Genera diagnostica del server", "diagnostics.get-support": "Ottenere supporto", - "diagnostics.get-support-description": "Se hai problemi con OliveTin e vuoi presentare una richiesta di supporto, sarebbe molto utile includere un sosreport da questa pagina.", - "diagnostics.sos-report": "Rapporto SOS", - "diagnostics.sos-report-description": "Questa sezione ti consente di generare un rapporto dettagliato della tua configurazione e ambiente. È una buona idea includere questo quando si presenta una richiesta di supporto.", - "diagnostics.sos-report-docs": "Documentazione sosreport", + "diagnostics.get-support-description": "Se hai problemi con OliveTin e vuoi presentare una richiesta di supporto, sarebbe molto utile includere la diagnostica del server da questa pagina.", + "diagnostics.server-diagnostics": "Diagnostica del server", + "diagnostics.server-diagnostics-description": "Questa sezione ti consente di generare un rapporto dettagliato della tua configurazione e ambiente. È una buona idea includere questo quando si presenta una richiesta di supporto.", + "diagnostics.server-diagnostics-docs": "Documentazione diagnostica del server", "diagnostics.ssh": "SSH", "diagnostics.unknown": "Sconosciuto", "diagnostics.useragent-data-error": "Errore nel recupero di userAgentData", @@ -302,12 +302,12 @@ "diagnostics.found-config": "找到配置", "diagnostics.found-key": "找到密钥", "diagnostics.generate-browser-info": "生成浏览器信息", - "diagnostics.generate-sos-report": "生成 SOS 报告", + "diagnostics.generate-server-diagnostics": "生成服务器诊断", "diagnostics.get-support": "获取支持", - "diagnostics.get-support-description": "如果您在使用 OliveTin 时遇到问题并希望提交支持请求,从本页面包含 sosreport 将非常有帮助。", - "diagnostics.sos-report": "SOS 报告", - "diagnostics.sos-report-description": "此部分允许您生成配置和环境的详细报告。在提交支持请求时包含此信息是个好主意。", - "diagnostics.sos-report-docs": "sosreport 文档", + "diagnostics.get-support-description": "如果您在使用 OliveTin 时遇到问题并希望提交支持请求,从本页面包含服务器诊断将非常有帮助。", + "diagnostics.server-diagnostics": "服务器诊断", + "diagnostics.server-diagnostics-description": "此部分允许您生成配置和环境的详细报告。在提交支持请求时包含此信息是个好主意。", + "diagnostics.server-diagnostics-docs": "服务器诊断文档", "diagnostics.ssh": "SSH", "diagnostics.unknown": "未知", "diagnostics.useragent-data-error": "检索 userAgentData 时出错", diff --git a/lang/de-DE.yaml b/lang/de-DE.yaml index a328fe0..c585fc3 100644 --- a/lang/de-DE.yaml +++ b/lang/de-DE.yaml @@ -44,15 +44,15 @@ translations: logs.queue-position: "#{position}" logs.queue-entity: Entität diagnostics.get-support: Unterstützung erhalten - diagnostics.get-support-description: Wenn Sie Probleme mit OliveTin haben und eine Support-Anfrage stellen möchten, wäre es sehr hilfreich, einen sosreport von dieser Seite einzufügen. + diagnostics.get-support-description: Wenn Sie Probleme mit OliveTin haben und eine Support-Anfrage stellen möchten, wäre es sehr hilfreich, Server-Diagnostik von dieser Seite einzufügen. diagnostics.where-to-find-help: Wo Sie Hilfe finden diagnostics.ssh: SSH diagnostics.found-key: Schlüssel gefunden diagnostics.found-config: Konfiguration gefunden - diagnostics.sos-report: SOS-Bericht - diagnostics.sos-report-description: Dieser Abschnitt ermöglicht es Ihnen, einen detaillierten Bericht über Ihre Konfiguration und Umgebung zu erstellen. Es ist eine gute Idee, dies bei einer Support-Anfrage einzufügen. - diagnostics.sos-report-docs: sosreport Dokumentation - diagnostics.generate-sos-report: SOS-Bericht erstellen + diagnostics.server-diagnostics: Server-Diagnostik + diagnostics.server-diagnostics-description: Dieser Abschnitt ermöglicht es Ihnen, einen detaillierten Bericht über Ihre Konfiguration und Umgebung zu erstellen. Es ist eine gute Idee, dies bei einer Support-Anfrage einzufügen. + diagnostics.server-diagnostics-docs: Server-Diagnostik-Dokumentation + diagnostics.generate-server-diagnostics: Server-Diagnostik erstellen diagnostics.browser-info: Browser-Informationen diagnostics.browser-info-description: Dieser Abschnitt ermöglicht es Ihnen, einen detaillierten Bericht über Ihre Browser-Informationen zu erstellen. Dies kann bei der Fehlerbehebung von browser-spezifischen Problemen hilfreich sein. diagnostics.generate-browser-info: Browser-Informationen erstellen diff --git a/lang/en.yaml b/lang/en.yaml index b9ab5cf..a334965 100644 --- a/lang/en.yaml +++ b/lang/en.yaml @@ -52,15 +52,15 @@ translations: logs.queue-entity: Entity logs.queue-action-details: Action Details diagnostics.get-support: Get support - diagnostics.get-support-description: If you are having problems with OliveTin and want to raise a support request, it would be very helpful to include a sosreport from this page. + diagnostics.get-support-description: If you are having problems with OliveTin and want to raise a support request, it would be very helpful to include Server Diagnostics from this page. diagnostics.where-to-find-help: Where to find help diagnostics.ssh: SSH diagnostics.found-key: Found Key diagnostics.found-config: Found Config - diagnostics.sos-report: SOS Report - diagnostics.sos-report-description: This section allows you to generate a detailed report of your configuration and environment. It is a good idea to include this when raising a support request. - diagnostics.sos-report-docs: sosreport Documentation - diagnostics.generate-sos-report: Generate SOS Report + diagnostics.server-diagnostics: Server Diagnostics + diagnostics.server-diagnostics-description: This section allows you to generate a detailed report of your configuration and environment. It is a good idea to include this when raising a support request. + diagnostics.server-diagnostics-docs: Server Diagnostics Documentation + diagnostics.generate-server-diagnostics: Generate Server Diagnostics diagnostics.browser-info: Browser Info diagnostics.browser-info-description: This section allows you to generate a detailed report of your browser information. This can be helpful when troubleshooting browser-specific issues. diagnostics.generate-browser-info: Generate Browser Info diff --git a/lang/es-ES.yaml b/lang/es-ES.yaml index 3c9e544..d8ef226 100644 --- a/lang/es-ES.yaml +++ b/lang/es-ES.yaml @@ -44,15 +44,15 @@ translations: logs.queue-position: "#{position}" logs.queue-entity: Entidad diagnostics.get-support: Obtener soporte - diagnostics.get-support-description: Si tiene problemas con OliveTin y desea presentar una solicitud de soporte, sería muy útil incluir un sosreport de esta página. + diagnostics.get-support-description: Si tiene problemas con OliveTin y desea presentar una solicitud de soporte, sería muy útil incluir diagnósticos del servidor desde esta página. diagnostics.where-to-find-help: Dónde encontrar ayuda diagnostics.ssh: SSH diagnostics.found-key: Clave encontrada diagnostics.found-config: Configuración encontrada - diagnostics.sos-report: Informe SOS - diagnostics.sos-report-description: Esta sección le permite generar un informe detallado de su configuración y entorno. Es una buena idea incluir esto al presentar una solicitud de soporte. - diagnostics.sos-report-docs: Documentación de sosreport - diagnostics.generate-sos-report: Generar informe SOS + diagnostics.server-diagnostics: Diagnóstico del servidor + diagnostics.server-diagnostics-description: Esta sección le permite generar un informe detallado de su configuración y entorno. Es una buena idea incluir esto al presentar una solicitud de soporte. + diagnostics.server-diagnostics-docs: Documentación de diagnóstico del servidor + diagnostics.generate-server-diagnostics: Generar diagnóstico del servidor diagnostics.browser-info: Información del navegador diagnostics.browser-info-description: Esta sección le permite generar un informe detallado de su información del navegador. Esto puede ser útil al solucionar problemas específicos del navegador. diagnostics.generate-browser-info: Generar información del navegador diff --git a/lang/it-IT.yaml b/lang/it-IT.yaml index 2175597..37e87cf 100644 --- a/lang/it-IT.yaml +++ b/lang/it-IT.yaml @@ -44,15 +44,15 @@ translations: logs.queue-position: "#{position}" logs.queue-entity: Entità diagnostics.get-support: Ottenere supporto - diagnostics.get-support-description: Se hai problemi con OliveTin e vuoi presentare una richiesta di supporto, sarebbe molto utile includere un sosreport da questa pagina. + diagnostics.get-support-description: Se hai problemi con OliveTin e vuoi presentare una richiesta di supporto, sarebbe molto utile includere la diagnostica del server da questa pagina. diagnostics.where-to-find-help: Dove trovare aiuto diagnostics.ssh: SSH diagnostics.found-key: Chiave trovata diagnostics.found-config: Configurazione trovata - diagnostics.sos-report: Rapporto SOS - diagnostics.sos-report-description: Questa sezione ti consente di generare un rapporto dettagliato della tua configurazione e ambiente. È una buona idea includere questo quando si presenta una richiesta di supporto. - diagnostics.sos-report-docs: Documentazione sosreport - diagnostics.generate-sos-report: Genera rapporto SOS + diagnostics.server-diagnostics: Diagnostica del server + diagnostics.server-diagnostics-description: Questa sezione ti consente di generare un rapporto dettagliato della tua configurazione e ambiente. È una buona idea includere questo quando si presenta una richiesta di supporto. + diagnostics.server-diagnostics-docs: Documentazione diagnostica del server + diagnostics.generate-server-diagnostics: Genera diagnostica del server diagnostics.browser-info: Informazioni del browser diagnostics.browser-info-description: Questa sezione ti consente di generare un rapporto dettagliato delle informazioni del tuo browser. Questo può essere utile durante la risoluzione dei problemi specifici del browser. diagnostics.generate-browser-info: Genera informazioni del browser diff --git a/lang/zh-Hans-CN.yaml b/lang/zh-Hans-CN.yaml index c380548..b65fa18 100644 --- a/lang/zh-Hans-CN.yaml +++ b/lang/zh-Hans-CN.yaml @@ -53,15 +53,15 @@ translations: logs.queue-position: "第 {position} 位" logs.queue-entity: 实体 diagnostics.get-support: 获取支持 - diagnostics.get-support-description: 如果您在使用 OliveTin 时遇到问题并希望提交支持请求,从本页面包含 sosreport 将非常有帮助。 + diagnostics.get-support-description: 如果您在使用 OliveTin 时遇到问题并希望提交支持请求,从本页面包含服务器诊断将非常有帮助。 diagnostics.where-to-find-help: 在哪里找到帮助 diagnostics.ssh: SSH diagnostics.found-key: 找到密钥 diagnostics.found-config: 找到配置 - diagnostics.sos-report: SOS 报告 - diagnostics.sos-report-description: 此部分允许您生成配置和环境的详细报告。在提交支持请求时包含此信息是个好主意。 - diagnostics.sos-report-docs: sosreport 文档 - diagnostics.generate-sos-report: 生成 SOS 报告 + diagnostics.server-diagnostics: 服务器诊断 + diagnostics.server-diagnostics-description: 此部分允许您生成配置和环境的详细报告。在提交支持请求时包含此信息是个好主意。 + diagnostics.server-diagnostics-docs: 服务器诊断文档 + diagnostics.generate-server-diagnostics: 生成服务器诊断 diagnostics.browser-info: 浏览器信息 diagnostics.browser-info-description: 此部分允许您生成浏览器信息的详细报告。这在排查浏览器特定问题时很有帮助。 diagnostics.generate-browser-info: 生成浏览器信息 diff --git a/proto/olivetin/api/v1/olivetin.proto b/proto/olivetin/api/v1/olivetin.proto index e6295eb..1b16b5f 100644 --- a/proto/olivetin/api/v1/olivetin.proto +++ b/proto/olivetin/api/v1/olivetin.proto @@ -282,9 +282,9 @@ message WhoAmIResponse { string sid = 5; } -message SosReportRequest {} +message ServerDiagnosticsRequest {} -message SosReportResponse { +message ServerDiagnosticsResponse { string alert = 1; } @@ -495,7 +495,7 @@ service OliveTinApiService { rpc WhoAmI(WhoAmIRequest) returns (WhoAmIResponse) {} - rpc SosReport(SosReportRequest) returns (SosReportResponse) {} + rpc ServerDiagnostics(ServerDiagnosticsRequest) returns (ServerDiagnosticsResponse) {} rpc DumpVars(DumpVarsRequest) returns (DumpVarsResponse) {} diff --git a/service/gen/olivetin/api/v1/apiv1connect/olivetin.connect.go b/service/gen/olivetin/api/v1/apiv1connect/olivetin.connect.go index 1182ed2..46a5205 100644 --- a/service/gen/olivetin/api/v1/apiv1connect/olivetin.connect.go +++ b/service/gen/olivetin/api/v1/apiv1connect/olivetin.connect.go @@ -72,9 +72,9 @@ const ( // OliveTinApiServiceWhoAmIProcedure is the fully-qualified name of the OliveTinApiService's WhoAmI // RPC. OliveTinApiServiceWhoAmIProcedure = "/olivetin.api.v1.OliveTinApiService/WhoAmI" - // OliveTinApiServiceSosReportProcedure is the fully-qualified name of the OliveTinApiService's - // SosReport RPC. - OliveTinApiServiceSosReportProcedure = "/olivetin.api.v1.OliveTinApiService/SosReport" + // OliveTinApiServiceServerDiagnosticsProcedure is the fully-qualified name of the + // OliveTinApiService's ServerDiagnostics RPC. + OliveTinApiServiceServerDiagnosticsProcedure = "/olivetin.api.v1.OliveTinApiService/ServerDiagnostics" // OliveTinApiServiceDumpVarsProcedure is the fully-qualified name of the OliveTinApiService's // DumpVars RPC. OliveTinApiServiceDumpVarsProcedure = "/olivetin.api.v1.OliveTinApiService/DumpVars" @@ -127,7 +127,7 @@ type OliveTinApiServiceClient interface { GetExecutionQueue(context.Context, *connect.Request[v1.GetExecutionQueueRequest]) (*connect.Response[v1.GetExecutionQueueResponse], error) ValidateArgumentType(context.Context, *connect.Request[v1.ValidateArgumentTypeRequest]) (*connect.Response[v1.ValidateArgumentTypeResponse], error) WhoAmI(context.Context, *connect.Request[v1.WhoAmIRequest]) (*connect.Response[v1.WhoAmIResponse], error) - SosReport(context.Context, *connect.Request[v1.SosReportRequest]) (*connect.Response[v1.SosReportResponse], error) + ServerDiagnostics(context.Context, *connect.Request[v1.ServerDiagnosticsRequest]) (*connect.Response[v1.ServerDiagnosticsResponse], error) DumpVars(context.Context, *connect.Request[v1.DumpVarsRequest]) (*connect.Response[v1.DumpVarsResponse], error) DumpPublicIdActionMap(context.Context, *connect.Request[v1.DumpPublicIdActionMapRequest]) (*connect.Response[v1.DumpPublicIdActionMapResponse], error) GetReadyz(context.Context, *connect.Request[v1.GetReadyzRequest]) (*connect.Response[v1.GetReadyzResponse], error) @@ -231,10 +231,10 @@ func NewOliveTinApiServiceClient(httpClient connect.HTTPClient, baseURL string, connect.WithSchema(oliveTinApiServiceMethods.ByName("WhoAmI")), connect.WithClientOptions(opts...), ), - sosReport: connect.NewClient[v1.SosReportRequest, v1.SosReportResponse]( + serverDiagnostics: connect.NewClient[v1.ServerDiagnosticsRequest, v1.ServerDiagnosticsResponse]( httpClient, - baseURL+OliveTinApiServiceSosReportProcedure, - connect.WithSchema(oliveTinApiServiceMethods.ByName("SosReport")), + baseURL+OliveTinApiServiceServerDiagnosticsProcedure, + connect.WithSchema(oliveTinApiServiceMethods.ByName("ServerDiagnostics")), connect.WithClientOptions(opts...), ), dumpVars: connect.NewClient[v1.DumpVarsRequest, v1.DumpVarsResponse]( @@ -327,7 +327,7 @@ type oliveTinApiServiceClient struct { getExecutionQueue *connect.Client[v1.GetExecutionQueueRequest, v1.GetExecutionQueueResponse] validateArgumentType *connect.Client[v1.ValidateArgumentTypeRequest, v1.ValidateArgumentTypeResponse] whoAmI *connect.Client[v1.WhoAmIRequest, v1.WhoAmIResponse] - sosReport *connect.Client[v1.SosReportRequest, v1.SosReportResponse] + serverDiagnostics *connect.Client[v1.ServerDiagnosticsRequest, v1.ServerDiagnosticsResponse] dumpVars *connect.Client[v1.DumpVarsRequest, v1.DumpVarsResponse] dumpPublicIdActionMap *connect.Client[v1.DumpPublicIdActionMapRequest, v1.DumpPublicIdActionMapResponse] getReadyz *connect.Client[v1.GetReadyzRequest, v1.GetReadyzResponse] @@ -407,9 +407,9 @@ func (c *oliveTinApiServiceClient) WhoAmI(ctx context.Context, req *connect.Requ return c.whoAmI.CallUnary(ctx, req) } -// SosReport calls olivetin.api.v1.OliveTinApiService.SosReport. -func (c *oliveTinApiServiceClient) SosReport(ctx context.Context, req *connect.Request[v1.SosReportRequest]) (*connect.Response[v1.SosReportResponse], error) { - return c.sosReport.CallUnary(ctx, req) +// ServerDiagnostics calls olivetin.api.v1.OliveTinApiService.ServerDiagnostics. +func (c *oliveTinApiServiceClient) ServerDiagnostics(ctx context.Context, req *connect.Request[v1.ServerDiagnosticsRequest]) (*connect.Response[v1.ServerDiagnosticsResponse], error) { + return c.serverDiagnostics.CallUnary(ctx, req) } // DumpVars calls olivetin.api.v1.OliveTinApiService.DumpVars. @@ -487,7 +487,7 @@ type OliveTinApiServiceHandler interface { GetExecutionQueue(context.Context, *connect.Request[v1.GetExecutionQueueRequest]) (*connect.Response[v1.GetExecutionQueueResponse], error) ValidateArgumentType(context.Context, *connect.Request[v1.ValidateArgumentTypeRequest]) (*connect.Response[v1.ValidateArgumentTypeResponse], error) WhoAmI(context.Context, *connect.Request[v1.WhoAmIRequest]) (*connect.Response[v1.WhoAmIResponse], error) - SosReport(context.Context, *connect.Request[v1.SosReportRequest]) (*connect.Response[v1.SosReportResponse], error) + ServerDiagnostics(context.Context, *connect.Request[v1.ServerDiagnosticsRequest]) (*connect.Response[v1.ServerDiagnosticsResponse], error) DumpVars(context.Context, *connect.Request[v1.DumpVarsRequest]) (*connect.Response[v1.DumpVarsResponse], error) DumpPublicIdActionMap(context.Context, *connect.Request[v1.DumpPublicIdActionMapRequest]) (*connect.Response[v1.DumpPublicIdActionMapResponse], error) GetReadyz(context.Context, *connect.Request[v1.GetReadyzRequest]) (*connect.Response[v1.GetReadyzResponse], error) @@ -587,10 +587,10 @@ func NewOliveTinApiServiceHandler(svc OliveTinApiServiceHandler, opts ...connect connect.WithSchema(oliveTinApiServiceMethods.ByName("WhoAmI")), connect.WithHandlerOptions(opts...), ) - oliveTinApiServiceSosReportHandler := connect.NewUnaryHandler( - OliveTinApiServiceSosReportProcedure, - svc.SosReport, - connect.WithSchema(oliveTinApiServiceMethods.ByName("SosReport")), + oliveTinApiServiceServerDiagnosticsHandler := connect.NewUnaryHandler( + OliveTinApiServiceServerDiagnosticsProcedure, + svc.ServerDiagnostics, + connect.WithSchema(oliveTinApiServiceMethods.ByName("ServerDiagnostics")), connect.WithHandlerOptions(opts...), ) oliveTinApiServiceDumpVarsHandler := connect.NewUnaryHandler( @@ -693,8 +693,8 @@ func NewOliveTinApiServiceHandler(svc OliveTinApiServiceHandler, opts ...connect oliveTinApiServiceValidateArgumentTypeHandler.ServeHTTP(w, r) case OliveTinApiServiceWhoAmIProcedure: oliveTinApiServiceWhoAmIHandler.ServeHTTP(w, r) - case OliveTinApiServiceSosReportProcedure: - oliveTinApiServiceSosReportHandler.ServeHTTP(w, r) + case OliveTinApiServiceServerDiagnosticsProcedure: + oliveTinApiServiceServerDiagnosticsHandler.ServeHTTP(w, r) case OliveTinApiServiceDumpVarsProcedure: oliveTinApiServiceDumpVarsHandler.ServeHTTP(w, r) case OliveTinApiServiceDumpPublicIdActionMapProcedure: @@ -780,8 +780,8 @@ func (UnimplementedOliveTinApiServiceHandler) WhoAmI(context.Context, *connect.R return nil, connect.NewError(connect.CodeUnimplemented, errors.New("olivetin.api.v1.OliveTinApiService.WhoAmI is not implemented")) } -func (UnimplementedOliveTinApiServiceHandler) SosReport(context.Context, *connect.Request[v1.SosReportRequest]) (*connect.Response[v1.SosReportResponse], error) { - return nil, connect.NewError(connect.CodeUnimplemented, errors.New("olivetin.api.v1.OliveTinApiService.SosReport is not implemented")) +func (UnimplementedOliveTinApiServiceHandler) ServerDiagnostics(context.Context, *connect.Request[v1.ServerDiagnosticsRequest]) (*connect.Response[v1.ServerDiagnosticsResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("olivetin.api.v1.OliveTinApiService.ServerDiagnostics is not implemented")) } func (UnimplementedOliveTinApiServiceHandler) DumpVars(context.Context, *connect.Request[v1.DumpVarsRequest]) (*connect.Response[v1.DumpVarsResponse], error) { diff --git a/service/gen/olivetin/api/v1/olivetin.pb.go b/service/gen/olivetin/api/v1/olivetin.pb.go index e6c47c6..e0a0c96 100644 --- a/service/gen/olivetin/api/v1/olivetin.pb.go +++ b/service/gen/olivetin/api/v1/olivetin.pb.go @@ -2649,26 +2649,26 @@ func (x *WhoAmIResponse) GetSid() string { return "" } -type SosReportRequest struct { +type ServerDiagnosticsRequest struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *SosReportRequest) Reset() { - *x = SosReportRequest{} +func (x *ServerDiagnosticsRequest) Reset() { + *x = ServerDiagnosticsRequest{} mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *SosReportRequest) String() string { +func (x *ServerDiagnosticsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SosReportRequest) ProtoMessage() {} +func (*ServerDiagnosticsRequest) ProtoMessage() {} -func (x *SosReportRequest) ProtoReflect() protoreflect.Message { +func (x *ServerDiagnosticsRequest) ProtoReflect() protoreflect.Message { mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[39] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2680,32 +2680,32 @@ func (x *SosReportRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SosReportRequest.ProtoReflect.Descriptor instead. -func (*SosReportRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use ServerDiagnosticsRequest.ProtoReflect.Descriptor instead. +func (*ServerDiagnosticsRequest) Descriptor() ([]byte, []int) { return file_olivetin_api_v1_olivetin_proto_rawDescGZIP(), []int{39} } -type SosReportResponse struct { +type ServerDiagnosticsResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Alert string `protobuf:"bytes,1,opt,name=alert,proto3" json:"alert,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *SosReportResponse) Reset() { - *x = SosReportResponse{} +func (x *ServerDiagnosticsResponse) Reset() { + *x = ServerDiagnosticsResponse{} mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *SosReportResponse) String() string { +func (x *ServerDiagnosticsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SosReportResponse) ProtoMessage() {} +func (*ServerDiagnosticsResponse) ProtoMessage() {} -func (x *SosReportResponse) ProtoReflect() protoreflect.Message { +func (x *ServerDiagnosticsResponse) ProtoReflect() protoreflect.Message { mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[40] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2717,12 +2717,12 @@ func (x *SosReportResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SosReportResponse.ProtoReflect.Descriptor instead. -func (*SosReportResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use ServerDiagnosticsResponse.ProtoReflect.Descriptor instead. +func (*ServerDiagnosticsResponse) Descriptor() ([]byte, []int) { return file_olivetin_api_v1_olivetin_proto_rawDescGZIP(), []int{40} } -func (x *SosReportResponse) GetAlert() string { +func (x *ServerDiagnosticsResponse) GetAlert() string { if x != nil { return x.Alert } @@ -4983,9 +4983,9 @@ const file_olivetin_api_v1_olivetin_proto_rawDesc = "" + "\tusergroup\x18\x02 \x01(\tR\tusergroup\x12\x1a\n" + "\bprovider\x18\x03 \x01(\tR\bprovider\x12\x12\n" + "\x04acls\x18\x04 \x03(\tR\x04acls\x12\x10\n" + - "\x03sid\x18\x05 \x01(\tR\x03sid\"\x12\n" + - "\x10SosReportRequest\")\n" + - "\x11SosReportResponse\x12\x14\n" + + "\x03sid\x18\x05 \x01(\tR\x03sid\"\x1a\n" + + "\x18ServerDiagnosticsRequest\"1\n" + + "\x19ServerDiagnosticsResponse\x12\x14\n" + "\x05alert\x18\x01 \x01(\tR\x05alert\"\x11\n" + "\x0fDumpVarsRequest\"\xb2\x01\n" + "\x10DumpVarsResponse\x12\x14\n" + @@ -5117,7 +5117,7 @@ const file_olivetin_api_v1_olivetin_proto_rawDesc = "" + "unique_key\x18\x01 \x01(\tR\tuniqueKey\x12\x12\n" + "\x04type\x18\x02 \x01(\tR\x04type\"J\n" + "\x14RestartActionRequest\x122\n" + - "\x15execution_tracking_id\x18\x01 \x01(\tR\x13executionTrackingId2\xd6\x13\n" + + "\x15execution_tracking_id\x18\x01 \x01(\tR\x13executionTrackingId2\xee\x13\n" + "\x12OliveTinApiService\x12]\n" + "\fGetDashboard\x12$.olivetin.api.v1.GetDashboardRequest\x1a%.olivetin.api.v1.GetDashboardResponse\"\x00\x12Z\n" + "\vStartAction\x12#.olivetin.api.v1.StartActionRequest\x1a$.olivetin.api.v1.StartActionResponse\"\x00\x12o\n" + @@ -5132,8 +5132,8 @@ const file_olivetin_api_v1_olivetin_proto_rawDesc = "" + "\rGetActionLogs\x12%.olivetin.api.v1.GetActionLogsRequest\x1a&.olivetin.api.v1.GetActionLogsResponse\"\x00\x12l\n" + "\x11GetExecutionQueue\x12).olivetin.api.v1.GetExecutionQueueRequest\x1a*.olivetin.api.v1.GetExecutionQueueResponse\"\x00\x12u\n" + "\x14ValidateArgumentType\x12,.olivetin.api.v1.ValidateArgumentTypeRequest\x1a-.olivetin.api.v1.ValidateArgumentTypeResponse\"\x00\x12K\n" + - "\x06WhoAmI\x12\x1e.olivetin.api.v1.WhoAmIRequest\x1a\x1f.olivetin.api.v1.WhoAmIResponse\"\x00\x12T\n" + - "\tSosReport\x12!.olivetin.api.v1.SosReportRequest\x1a\".olivetin.api.v1.SosReportResponse\"\x00\x12Q\n" + + "\x06WhoAmI\x12\x1e.olivetin.api.v1.WhoAmIRequest\x1a\x1f.olivetin.api.v1.WhoAmIResponse\"\x00\x12l\n" + + "\x11ServerDiagnostics\x12).olivetin.api.v1.ServerDiagnosticsRequest\x1a*.olivetin.api.v1.ServerDiagnosticsResponse\"\x00\x12Q\n" + "\bDumpVars\x12 .olivetin.api.v1.DumpVarsRequest\x1a!.olivetin.api.v1.DumpVarsResponse\"\x00\x12x\n" + "\x15DumpPublicIdActionMap\x12-.olivetin.api.v1.DumpPublicIdActionMapRequest\x1a..olivetin.api.v1.DumpPublicIdActionMapResponse\"\x00\x12T\n" + "\tGetReadyz\x12!.olivetin.api.v1.GetReadyzRequest\x1a\".olivetin.api.v1.GetReadyzResponse\"\x00\x12c\n" + @@ -5200,8 +5200,8 @@ var file_olivetin_api_v1_olivetin_proto_goTypes = []any{ (*ExecutionStatusResponse)(nil), // 36: olivetin.api.v1.ExecutionStatusResponse (*WhoAmIRequest)(nil), // 37: olivetin.api.v1.WhoAmIRequest (*WhoAmIResponse)(nil), // 38: olivetin.api.v1.WhoAmIResponse - (*SosReportRequest)(nil), // 39: olivetin.api.v1.SosReportRequest - (*SosReportResponse)(nil), // 40: olivetin.api.v1.SosReportResponse + (*ServerDiagnosticsRequest)(nil), // 39: olivetin.api.v1.ServerDiagnosticsRequest + (*ServerDiagnosticsResponse)(nil), // 40: olivetin.api.v1.ServerDiagnosticsResponse (*DumpVarsRequest)(nil), // 41: olivetin.api.v1.DumpVarsRequest (*DumpVarsResponse)(nil), // 42: olivetin.api.v1.DumpVarsResponse (*DebugBinding)(nil), // 43: olivetin.api.v1.DebugBinding @@ -5307,7 +5307,7 @@ var file_olivetin_api_v1_olivetin_proto_depIdxs = []int32{ 26, // 56: olivetin.api.v1.OliveTinApiService.GetExecutionQueue:input_type -> olivetin.api.v1.GetExecutionQueueRequest 30, // 57: olivetin.api.v1.OliveTinApiService.ValidateArgumentType:input_type -> olivetin.api.v1.ValidateArgumentTypeRequest 37, // 58: olivetin.api.v1.OliveTinApiService.WhoAmI:input_type -> olivetin.api.v1.WhoAmIRequest - 39, // 59: olivetin.api.v1.OliveTinApiService.SosReport:input_type -> olivetin.api.v1.SosReportRequest + 39, // 59: olivetin.api.v1.OliveTinApiService.ServerDiagnostics:input_type -> olivetin.api.v1.ServerDiagnosticsRequest 41, // 60: olivetin.api.v1.OliveTinApiService.DumpVars:input_type -> olivetin.api.v1.DumpVarsRequest 44, // 61: olivetin.api.v1.OliveTinApiService.DumpPublicIdActionMap:input_type -> olivetin.api.v1.DumpPublicIdActionMapRequest 46, // 62: olivetin.api.v1.OliveTinApiService.GetReadyz:input_type -> olivetin.api.v1.GetReadyzRequest @@ -5333,7 +5333,7 @@ var file_olivetin_api_v1_olivetin_proto_depIdxs = []int32{ 29, // 82: olivetin.api.v1.OliveTinApiService.GetExecutionQueue:output_type -> olivetin.api.v1.GetExecutionQueueResponse 31, // 83: olivetin.api.v1.OliveTinApiService.ValidateArgumentType:output_type -> olivetin.api.v1.ValidateArgumentTypeResponse 38, // 84: olivetin.api.v1.OliveTinApiService.WhoAmI:output_type -> olivetin.api.v1.WhoAmIResponse - 40, // 85: olivetin.api.v1.OliveTinApiService.SosReport:output_type -> olivetin.api.v1.SosReportResponse + 40, // 85: olivetin.api.v1.OliveTinApiService.ServerDiagnostics:output_type -> olivetin.api.v1.ServerDiagnosticsResponse 42, // 86: olivetin.api.v1.OliveTinApiService.DumpVars:output_type -> olivetin.api.v1.DumpVarsResponse 45, // 87: olivetin.api.v1.OliveTinApiService.DumpPublicIdActionMap:output_type -> olivetin.api.v1.DumpPublicIdActionMapResponse 47, // 88: olivetin.api.v1.OliveTinApiService.GetReadyz:output_type -> olivetin.api.v1.GetReadyzResponse diff --git a/service/internal/api/api.go b/service/internal/api/api.go index ed546f6..9f9432e 100644 --- a/service/internal/api/api.go +++ b/service/internal/api/api.go @@ -930,18 +930,18 @@ func (api *oliveTinAPI) WhoAmI(ctx ctx.Context, req *connect.Request[apiv1.WhoAm return connect.NewResponse(res), nil } -func (api *oliveTinAPI) SosReport(ctx ctx.Context, req *connect.Request[apiv1.SosReportRequest]) (*connect.Response[apiv1.SosReportResponse], error) { +func (api *oliveTinAPI) ServerDiagnostics(ctx ctx.Context, req *connect.Request[apiv1.ServerDiagnosticsRequest]) (*connect.Response[apiv1.ServerDiagnosticsResponse], error) { user := auth.UserFromApiCall(ctx, req, api.cfg) redactVersion := !user.EffectivePolicy.ShowVersionNumber - sos := installationinfo.GetSosReport(redactVersion) + diagnostics := installationinfo.GetServerDiagnostics(redactVersion) - if !api.cfg.InsecureAllowDumpSos { - log.Info(sos) - sos = "Your SOS Report has been logged to OliveTin logs.\n\nIf you are in a safe network, you can temporarily set `insecureAllowDumpSos: true` in your config.yaml, restart OliveTin, and refresh this page - it will put the output directly in the browser." + if !api.cfg.InsecureAllowDumpServerDiagnostics { + log.Info(diagnostics) + diagnostics = "Your Server Diagnostics have been logged to OliveTin logs.\n\nIf you are in a safe network, you can temporarily set `insecureAllowDumpServerDiagnostics: true` in your config.yaml, restart OliveTin, and refresh this page - it will put the output directly in the browser." } - ret := &apiv1.SosReportResponse{ - Alert: sos, + ret := &apiv1.ServerDiagnosticsResponse{ + Alert: diagnostics, } return connect.NewResponse(ret), nil diff --git a/service/internal/config/config.go b/service/internal/config/config.go index 7879d54..c27c049 100644 --- a/service/internal/config/config.go +++ b/service/internal/config/config.go @@ -160,71 +160,71 @@ type SecurityConfig struct { // Config is the global config used through the whole app. type Config struct { - UseSingleHTTPFrontend bool `koanf:"useSingleHTTPFrontend"` - ThemeName string `koanf:"themeName"` - ThemeCacheDisabled bool `koanf:"themeCacheDisabled"` - ListenAddressSingleHTTPFrontend string `koanf:"listenAddressSingleHTTPFrontend"` - ListenAddressWebUI string `koanf:"listenAddressWebUI"` - ListenAddressRestActions string `koanf:"listenAddressRestActions"` - ListenAddressPrometheus string `koanf:"listenAddressPrometheus"` - ExternalRestAddress string `koanf:"externalRestAddress"` - LogLevel string `koanf:"logLevel"` - LogDebugOptions LogDebugOptions `koanf:"logDebugOptions"` - LogHistoryPageSize int64 `koanf:"logHistoryPageSize"` - ActionGroups map[string]*ActionGroup `koanf:"actionGroups"` - Actions []*Action `koanf:"actions"` - Entities []*EntityFile `koanf:"entities"` - Dashboards []*DashboardComponent `koanf:"dashboards"` - CheckForUpdates bool `koanf:"checkForUpdates"` - PageTitle string `koanf:"pageTitle"` - ShowFooter bool `koanf:"showFooter"` - ShowNavigation bool `koanf:"showNavigation"` - ShowNewVersions bool `koanf:"showNewVersions"` - ShowNavigateOnStartIcons bool `koanf:"showNavigateOnStartIcons"` - EnableCustomJs bool `koanf:"enableCustomJs"` - AuthJwtCookieName string `koanf:"authJwtCookieName"` - AuthJwtHeader string `koanf:"authJwtHeader"` - AuthJwtAud string `koanf:"authJwtAud"` - AuthJwtDomain string `koanf:"authJwtDomain"` - AuthJwtCertsURL string `koanf:"authJwtCertsUrl"` - AuthJwtHmacSecret string `koanf:"authJwtHmacSecret"` // mutually exclusive with pub key config fields - AuthJwtClaimUsername string `koanf:"authJwtClaimUsername"` - AuthJwtClaimUserGroup string `koanf:"authJwtClaimUserGroup"` - AuthJwtPubKeyPath string `koanf:"authJwtPubKeyPath"` // will read pub key from file on disk - AuthHttpHeaderUsername string `koanf:"authHttpHeaderUsername"` - AuthHttpHeaderUserGroup string `koanf:"authHttpHeaderUserGroup"` - AuthHttpHeaderUserGroupSep string `koanf:"authHttpHeaderUserGroupSep"` - AuthLocalUsers AuthLocalUsersConfig `koanf:"authLocalUsers"` - AuthLoginUrl string `koanf:"authLoginUrl"` - AuthRequireGuestsToLogin bool `koanf:"authRequireGuestsToLogin"` - AuthOAuth2RedirectURL string `koanf:"authOAuth2RedirectUrl"` - AuthOAuth2Providers map[string]*OAuth2Provider `koanf:"authOAuth2Providers"` - DefaultPermissions PermissionsList `koanf:"defaultPermissions"` - DefaultPolicy ConfigurationPolicy `koanf:"defaultPolicy"` - AccessControlLists []*AccessControlList `koanf:"accessControlLists"` - WebUIDir string `koanf:"webUIDir"` - CronSupportForSeconds bool `koanf:"cronSupportForSeconds"` - SectionNavigationStyle string `koanf:"sectionNavigationStyle"` - DefaultOnClick string `koanf:"defaultOnClick"` - DefaultPopupOnStart string `koanf:"defaultPopupOnStart"` - InsecureAllowDumpOAuth2UserData bool `koanf:"insecureAllowDumpOAuth2UserData"` - InsecureAllowDumpVars bool `koanf:"insecureAllowDumpVars"` - InsecureAllowDumpSos bool `koanf:"insecureAllowDumpSos"` - InsecureAllowDumpActionMap bool `koanf:"insecureAllowDumpActionMap"` - InsecureAllowDumpJwtClaims bool `koanf:"insecureAllowDumpJwtClaims"` - Prometheus PrometheusConfig `koanf:"prometheus"` - Security SecurityConfig `koanf:"security"` - SaveLogs SaveLogsConfig `koanf:"saveLogs"` - ServiceLogs ServiceLogsConfig `koanf:"serviceLogs"` - DefaultIconForActions string `koanf:"defaultIconForActions"` - DefaultIconForDirectories string `koanf:"defaultIconForDirectories"` - DefaultIconForBack string `koanf:"defaultIconForBack"` - AdditionalNavigationLinks []*NavigationLink `koanf:"additionalNavigationLinks"` - ServiceHostMode string `koanf:"serviceHostMode"` - StyleMods []string `koanf:"styleMods"` - BannerMessage string `koanf:"bannerMessage"` - BannerCSS string `koanf:"bannerCss"` - Include string `koanf:"include"` + UseSingleHTTPFrontend bool `koanf:"useSingleHTTPFrontend"` + ThemeName string `koanf:"themeName"` + ThemeCacheDisabled bool `koanf:"themeCacheDisabled"` + ListenAddressSingleHTTPFrontend string `koanf:"listenAddressSingleHTTPFrontend"` + ListenAddressWebUI string `koanf:"listenAddressWebUI"` + ListenAddressRestActions string `koanf:"listenAddressRestActions"` + ListenAddressPrometheus string `koanf:"listenAddressPrometheus"` + ExternalRestAddress string `koanf:"externalRestAddress"` + LogLevel string `koanf:"logLevel"` + LogDebugOptions LogDebugOptions `koanf:"logDebugOptions"` + LogHistoryPageSize int64 `koanf:"logHistoryPageSize"` + ActionGroups map[string]*ActionGroup `koanf:"actionGroups"` + Actions []*Action `koanf:"actions"` + Entities []*EntityFile `koanf:"entities"` + Dashboards []*DashboardComponent `koanf:"dashboards"` + CheckForUpdates bool `koanf:"checkForUpdates"` + PageTitle string `koanf:"pageTitle"` + ShowFooter bool `koanf:"showFooter"` + ShowNavigation bool `koanf:"showNavigation"` + ShowNewVersions bool `koanf:"showNewVersions"` + ShowNavigateOnStartIcons bool `koanf:"showNavigateOnStartIcons"` + EnableCustomJs bool `koanf:"enableCustomJs"` + AuthJwtCookieName string `koanf:"authJwtCookieName"` + AuthJwtHeader string `koanf:"authJwtHeader"` + AuthJwtAud string `koanf:"authJwtAud"` + AuthJwtDomain string `koanf:"authJwtDomain"` + AuthJwtCertsURL string `koanf:"authJwtCertsUrl"` + AuthJwtHmacSecret string `koanf:"authJwtHmacSecret"` // mutually exclusive with pub key config fields + AuthJwtClaimUsername string `koanf:"authJwtClaimUsername"` + AuthJwtClaimUserGroup string `koanf:"authJwtClaimUserGroup"` + AuthJwtPubKeyPath string `koanf:"authJwtPubKeyPath"` // will read pub key from file on disk + AuthHttpHeaderUsername string `koanf:"authHttpHeaderUsername"` + AuthHttpHeaderUserGroup string `koanf:"authHttpHeaderUserGroup"` + AuthHttpHeaderUserGroupSep string `koanf:"authHttpHeaderUserGroupSep"` + AuthLocalUsers AuthLocalUsersConfig `koanf:"authLocalUsers"` + AuthLoginUrl string `koanf:"authLoginUrl"` + AuthRequireGuestsToLogin bool `koanf:"authRequireGuestsToLogin"` + AuthOAuth2RedirectURL string `koanf:"authOAuth2RedirectUrl"` + AuthOAuth2Providers map[string]*OAuth2Provider `koanf:"authOAuth2Providers"` + DefaultPermissions PermissionsList `koanf:"defaultPermissions"` + DefaultPolicy ConfigurationPolicy `koanf:"defaultPolicy"` + AccessControlLists []*AccessControlList `koanf:"accessControlLists"` + WebUIDir string `koanf:"webUIDir"` + CronSupportForSeconds bool `koanf:"cronSupportForSeconds"` + SectionNavigationStyle string `koanf:"sectionNavigationStyle"` + DefaultOnClick string `koanf:"defaultOnClick"` + DefaultPopupOnStart string `koanf:"defaultPopupOnStart"` + InsecureAllowDumpOAuth2UserData bool `koanf:"insecureAllowDumpOAuth2UserData"` + InsecureAllowDumpVars bool `koanf:"insecureAllowDumpVars"` + InsecureAllowDumpServerDiagnostics bool `koanf:"insecureAllowDumpServerDiagnostics"` + InsecureAllowDumpActionMap bool `koanf:"insecureAllowDumpActionMap"` + InsecureAllowDumpJwtClaims bool `koanf:"insecureAllowDumpJwtClaims"` + Prometheus PrometheusConfig `koanf:"prometheus"` + Security SecurityConfig `koanf:"security"` + SaveLogs SaveLogsConfig `koanf:"saveLogs"` + ServiceLogs ServiceLogsConfig `koanf:"serviceLogs"` + DefaultIconForActions string `koanf:"defaultIconForActions"` + DefaultIconForDirectories string `koanf:"defaultIconForDirectories"` + DefaultIconForBack string `koanf:"defaultIconForBack"` + AdditionalNavigationLinks []*NavigationLink `koanf:"additionalNavigationLinks"` + ServiceHostMode string `koanf:"serviceHostMode"` + StyleMods []string `koanf:"styleMods"` + BannerMessage string `koanf:"bannerMessage"` + BannerCSS string `koanf:"bannerCss"` + Include string `koanf:"include"` sourceFiles []string } @@ -324,7 +324,7 @@ func DefaultConfigWithBasePort(basePort int) *Config { config.DefaultOnClick = "nothing" config.DefaultPopupOnStart = "nothing" config.InsecureAllowDumpVars = false - config.InsecureAllowDumpSos = false + config.InsecureAllowDumpServerDiagnostics = false config.InsecureAllowDumpActionMap = false config.InsecureAllowDumpJwtClaims = false config.Prometheus.Enabled = false diff --git a/service/internal/installationinfo/sosreport.go b/service/internal/installationinfo/server_diagnostics.go similarity index 78% rename from service/internal/installationinfo/sosreport.go rename to service/internal/installationinfo/server_diagnostics.go index 4d6c2ce..e2edccd 100644 --- a/service/internal/installationinfo/sosreport.go +++ b/service/internal/installationinfo/server_diagnostics.go @@ -12,7 +12,7 @@ var ( Config *config.Config ) -type sosReportConfig struct { +type serverDiagnosticsConfig struct { CountOfActions int CountOfDashboards int LogLevel string @@ -25,8 +25,8 @@ type sosReportConfig struct { WebuiDirectory string } -func configToSosreport(cfg *config.Config) *sosReportConfig { - return &sosReportConfig{ +func configToServerDiagnostics(cfg *config.Config) *serverDiagnosticsConfig { + return &serverDiagnosticsConfig{ CountOfActions: len(cfg.Actions), CountOfDashboards: len(cfg.Dashboards), LogLevel: cfg.LogLevel, @@ -40,10 +40,10 @@ func configToSosreport(cfg *config.Config) *sosReportConfig { } } -func GetSosReport(redactVersion bool) string { +func GetServerDiagnostics(redactVersion bool) string { ret := "" - ret += "### SOSREPORT START (copy all text to SOSREPORT END)\n" + ret += "### SERVER DIAGNOSTICS START (copy all text to SERVER DIAGNOSTICS END)\n" buildForReport := *Build if redactVersion { @@ -59,9 +59,9 @@ func GetSosReport(redactVersion bool) string { out, _ = yaml.Marshal(&runtimeForReport) ret += fmt.Sprintf("# Runtime:\n%+v\n", string(out)) - out, _ = yaml.Marshal(configToSosreport(Config)) + out, _ = yaml.Marshal(configToServerDiagnostics(Config)) ret += fmt.Sprintf("# Config:\n%+v\n", string(out)) - ret += "### SOSREPORT END (copy all text from SOSREPORT START)\n" + ret += "### SERVER DIAGNOSTICS END (copy all text from SERVER DIAGNOSTICS START)\n" return ret }
{{ t('diagnostics.sos-report-description') }}
{{ t('diagnostics.server-diagnostics-description') }}
- {{ t('diagnostics.sos-report-docs') }} + {{ t('diagnostics.server-diagnostics-docs') }}