From af1420dbde110ce51e16b2e0f3f66875a2b37af1 Mon Sep 17 00:00:00 2001 From: Merelden Date: Tue, 14 Oct 2025 12:17:13 +0500 Subject: [PATCH] Sphinx generate docs --- SRV/docs/_build/html/.buildinfo | 4 + .../_build/html/.doctrees/api/config.doctree | Bin 0 -> 2882 bytes .../html/.doctrees/api/config/cors.doctree | Bin 0 -> 13465 bytes .../.doctrees/api/config/init_config.doctree | Bin 0 -> 2475 bytes .../html/.doctrees/api/config/ports.doctree | Bin 0 -> 15427 bytes .../_build/html/.doctrees/api/core.doctree | Bin 0 -> 2764 bytes .../.doctrees/api/core/cli_executor.doctree | Bin 0 -> 11915 bytes .../html/.doctrees/api/endpoints.doctree | Bin 0 -> 2993 bytes .../html/.doctrees/api/endpoints/auth.doctree | Bin 0 -> 41389 bytes .../html/.doctrees/api/endpoints/ip.doctree | Bin 0 -> 36193 bytes .../.doctrees/api/endpoints/network.doctree | Bin 0 -> 47126 bytes .../.doctrees/api/endpoints/settings.doctree | Bin 0 -> 46199 bytes .../.doctrees/api/endpoints/subnet.doctree | Bin 0 -> 87966 bytes .../html/.doctrees/api/endpoints/ufw.doctree | Bin 0 -> 42390 bytes .../.doctrees/api/endpoints/websocket.doctree | Bin 0 -> 32808 bytes .../_build/html/.doctrees/api/models.doctree | Bin 0 -> 2914 bytes .../html/.doctrees/api/models/auth.doctree | Bin 0 -> 55390 bytes .../html/.doctrees/api/models/network.doctree | Bin 0 -> 179683 bytes .../html/.doctrees/api/models/request.doctree | Bin 0 -> 30002 bytes .../html/.doctrees/api/models/subnet.doctree | Bin 0 -> 57427 bytes .../html/.doctrees/api/models/ufw.doctree | Bin 0 -> 87791 bytes .../html/.doctrees/api/models/wifi.doctree | Bin 0 -> 48843 bytes .../html/.doctrees/api/services.doctree | Bin 0 -> 3433 bytes .../api/services/arp_service.doctree | Bin 0 -> 23516 bytes .../api/services/auth_service.doctree | Bin 0 -> 26209 bytes .../api/services/connect_service.doctree | Bin 0 -> 34848 bytes .../api/services/firewalld_service.doctree | Bin 0 -> 53040 bytes .../.doctrees/api/services/ip_service.doctree | Bin 0 -> 32031 bytes .../api/services/network_service.doctree | Bin 0 -> 122834 bytes .../api/services/settings_service.doctree | Bin 0 -> 19958 bytes .../api/services/subnet_service.doctree | Bin 0 -> 121732 bytes .../api/services/ufw_service.doctree | Bin 0 -> 41942 bytes .../api/services/wifi_service.doctree | Bin 0 -> 44950 bytes .../_build/html/.doctrees/api/storage.doctree | Bin 0 -> 2856 bytes .../api/storage/json_storage.doctree | Bin 0 -> 31702 bytes .../api/storage/session_storage.doctree | Bin 0 -> 38792 bytes .../_build/html/.doctrees/api/utils.doctree | Bin 0 -> 2793 bytes .../html/.doctrees/api/utils/i18n.doctree | Bin 0 -> 13471 bytes .../_build/html/.doctrees/environment.pickle | Bin 0 -> 555390 bytes SRV/docs/_build/html/.doctrees/index.doctree | Bin 0 -> 3996 bytes .../_build/html/_modules/api/v1/auth.html | 513 ++++++ SRV/docs/_build/html/_modules/api/v1/ip.html | 435 +++++ .../_build/html/_modules/api/v1/network.html | 558 ++++++ .../_build/html/_modules/api/v1/settings.html | 545 ++++++ .../_build/html/_modules/api/v1/subnet.html | 700 ++++++++ SRV/docs/_build/html/_modules/api/v1/ufw.html | 549 ++++++ .../html/_modules/api/v1/websocket.html | 488 ++++++ .../_build/html/_modules/config/cors.html | 368 ++++ .../html/_modules/config/init_config.html | 366 ++++ .../_build/html/_modules/config/ports.html | 432 +++++ .../html/_modules/core/cli_executor.html | 351 ++++ SRV/docs/_build/html/_modules/index.html | 344 ++++ .../_build/html/_modules/models/auth.html | 389 +++++ .../_build/html/_modules/models/network.html | 511 ++++++ .../_build/html/_modules/models/request.html | 356 ++++ .../_build/html/_modules/models/subnet.html | 371 ++++ SRV/docs/_build/html/_modules/models/ufw.html | 635 +++++++ .../_build/html/_modules/models/wifi.html | 361 ++++ .../html/_modules/services/arp_service.html | 453 +++++ .../html/_modules/services/auth_service.html | 487 ++++++ .../_modules/services/connect_service.html | 507 ++++++ .../_modules/services/firewalld_service.html | 522 ++++++ .../html/_modules/services/ip_service.html | 468 +++++ .../_modules/services/network_service.html | 744 ++++++++ .../_modules/services/settings_service.html | 431 +++++ .../_modules/services/subnet_service.html | 1095 ++++++++++++ .../html/_modules/services/ufw_service.html | 754 ++++++++ .../html/_modules/services/wifi_service.html | 718 ++++++++ .../html/_modules/storage/json_storage.html | 533 ++++++ .../_modules/storage/session_storage.html | 596 +++++++ SRV/docs/_build/html/_modules/utils/i18n.html | 392 +++++ .../_build/html/_sources/api/config.rst.txt | 9 + .../html/_sources/api/config/cors.rst.txt | 7 + .../_sources/api/config/init_config.rst.txt | 7 + .../html/_sources/api/config/ports.rst.txt | 7 + .../_build/html/_sources/api/core.rst.txt | 7 + .../_sources/api/core/cli_executor.rst.txt | 7 + .../html/_sources/api/endpoints.rst.txt | 13 + .../html/_sources/api/endpoints/auth.rst.txt | 7 + .../html/_sources/api/endpoints/ip.rst.txt | 7 + .../_sources/api/endpoints/network.rst.txt | 7 + .../_sources/api/endpoints/settings.rst.txt | 7 + .../_sources/api/endpoints/subnet.rst.txt | 7 + .../html/_sources/api/endpoints/ufw.rst.txt | 7 + .../_sources/api/endpoints/websocket.rst.txt | 7 + .../_build/html/_sources/api/models.rst.txt | 12 + .../html/_sources/api/models/auth.rst.txt | 7 + .../html/_sources/api/models/network.rst.txt | 7 + .../html/_sources/api/models/request.rst.txt | 7 + .../html/_sources/api/models/subnet.rst.txt | 7 + .../html/_sources/api/models/ufw.rst.txt | 7 + .../html/_sources/api/models/wifi.rst.txt | 7 + .../_build/html/_sources/api/services.rst.txt | 21 + .../_sources/api/services/arp_service.rst.txt | 7 + .../api/services/auth_service.rst.txt | 7 + .../api/services/connect_service.rst.txt | 7 + .../api/services/firewalld_service.rst.txt | 7 + .../_sources/api/services/ip_service.rst.txt | 7 + .../api/services/network_service.rst.txt | 7 + .../api/services/settings_service.rst.txt | 7 + .../api/services/subnet_service.rst.txt | 7 + .../_sources/api/services/ufw_service.rst.txt | 7 + .../api/services/wifi_service.rst.txt | 7 + .../_build/html/_sources/api/storage.rst.txt | 8 + .../_sources/api/storage/json_storage.rst.txt | 7 + .../api/storage/session_storage.rst.txt | 7 + .../_build/html/_sources/api/utils.rst.txt | 7 + .../html/_sources/api/utils/i18n.rst.txt | 7 + SRV/docs/_build/html/_sources/index.rst.txt | 19 + SRV/docs/_build/html/_static/basic.css | 906 ++++++++++ SRV/docs/_build/html/_static/debug.css | 69 + SRV/docs/_build/html/_static/doctools.js | 149 ++ .../html/_static/documentation_options.js | 13 + SRV/docs/_build/html/_static/file.png | Bin 0 -> 286 bytes SRV/docs/_build/html/_static/language_data.js | 192 +++ SRV/docs/_build/html/_static/minus.png | Bin 0 -> 90 bytes SRV/docs/_build/html/_static/plus.png | Bin 0 -> 90 bytes SRV/docs/_build/html/_static/pygments.css | 250 +++ .../html/_static/scripts/furo-extensions.js | 0 SRV/docs/_build/html/_static/scripts/furo.js | 3 + .../html/_static/scripts/furo.js.LICENSE.txt | 7 + .../_build/html/_static/scripts/furo.js.map | 1 + SRV/docs/_build/html/_static/searchtools.js | 635 +++++++ SRV/docs/_build/html/_static/skeleton.css | 296 ++++ .../_build/html/_static/sphinx_highlight.js | 154 ++ .../html/_static/styles/furo-extensions.css | 2 + .../_static/styles/furo-extensions.css.map | 1 + SRV/docs/_build/html/_static/styles/furo.css | 2 + .../_build/html/_static/styles/furo.css.map | 1 + SRV/docs/_build/html/api/config.html | 349 ++++ SRV/docs/_build/html/api/config/cors.html | 393 +++++ .../_build/html/api/config/init_config.html | 342 ++++ SRV/docs/_build/html/api/config/ports.html | 400 +++++ SRV/docs/_build/html/api/core.html | 347 ++++ .../_build/html/api/core/cli_executor.html | 388 +++++ SRV/docs/_build/html/api/endpoints.html | 353 ++++ SRV/docs/_build/html/api/endpoints/auth.html | 468 +++++ SRV/docs/_build/html/api/endpoints/ip.html | 447 +++++ .../_build/html/api/endpoints/network.html | 476 ++++++ .../_build/html/api/endpoints/settings.html | 475 ++++++ .../_build/html/api/endpoints/subnet.html | 579 +++++++ SRV/docs/_build/html/api/endpoints/ufw.html | 492 ++++++ .../_build/html/api/endpoints/websocket.html | 458 +++++ SRV/docs/_build/html/api/models.html | 352 ++++ SRV/docs/_build/html/api/models/auth.html | 524 ++++++ SRV/docs/_build/html/api/models/network.html | 841 +++++++++ SRV/docs/_build/html/api/models/request.html | 448 +++++ SRV/docs/_build/html/api/models/subnet.html | 497 ++++++ SRV/docs/_build/html/api/models/ufw.html | 655 +++++++ SRV/docs/_build/html/api/models/wifi.html | 470 +++++ SRV/docs/_build/html/api/services.html | 356 ++++ .../_build/html/api/services/arp_service.html | 426 +++++ .../html/api/services/auth_service.html | 419 +++++ .../html/api/services/connect_service.html | 450 +++++ .../html/api/services/firewalld_service.html | 480 ++++++ .../_build/html/api/services/ip_service.html | 440 +++++ .../html/api/services/network_service.html | 607 +++++++ .../html/api/services/settings_service.html | 414 +++++ .../html/api/services/subnet_service.html | 690 ++++++++ .../_build/html/api/services/ufw_service.html | 493 ++++++ .../html/api/services/wifi_service.html | 456 +++++ SRV/docs/_build/html/api/storage.html | 348 ++++ .../_build/html/api/storage/json_storage.html | 445 +++++ .../html/api/storage/session_storage.html | 472 +++++ SRV/docs/_build/html/api/utils.html | 347 ++++ SRV/docs/_build/html/api/utils/i18n.html | 380 +++++ SRV/docs/_build/html/genindex.html | 1519 +++++++++++++++++ SRV/docs/_build/html/index.html | 411 +++++ SRV/docs/_build/html/objects.inv | Bin 0 -> 3465 bytes SRV/docs/_build/html/py-modindex.html | 573 +++++++ SRV/docs/_build/html/search.html | 329 ++++ SRV/docs/_build/html/searchindex.js | 1 + SRV/docs/api/config.rst | 9 + SRV/docs/api/config/cors.rst | 7 + SRV/docs/api/config/init_config.rst | 7 + SRV/docs/api/config/ports.rst | 7 + SRV/docs/api/core.rst | 7 + SRV/docs/api/core/cli_executor.rst | 7 + SRV/docs/api/endpoints.rst | 13 + SRV/docs/api/endpoints/auth.rst | 7 + SRV/docs/api/endpoints/ip.rst | 7 + SRV/docs/api/endpoints/network.rst | 7 + SRV/docs/api/endpoints/settings.rst | 7 + SRV/docs/api/endpoints/subnet.rst | 7 + SRV/docs/api/endpoints/ufw.rst | 7 + SRV/docs/api/endpoints/websocket.rst | 7 + SRV/docs/api/models.rst | 12 + SRV/docs/api/models/auth.rst | 7 + SRV/docs/api/models/network.rst | 7 + SRV/docs/api/models/request.rst | 7 + SRV/docs/api/models/subnet.rst | 7 + SRV/docs/api/models/ufw.rst | 7 + SRV/docs/api/models/wifi.rst | 7 + SRV/docs/api/services.rst | 21 + SRV/docs/api/services/arp_service.rst | 7 + SRV/docs/api/services/auth_service.rst | 7 + SRV/docs/api/services/connect_service.rst | 7 + SRV/docs/api/services/firewalld_service.rst | 7 + SRV/docs/api/services/ip_service.rst | 7 + SRV/docs/api/services/network_service.rst | 7 + SRV/docs/api/services/settings_service.rst | 7 + SRV/docs/api/services/subnet_service.rst | 7 + SRV/docs/api/services/ufw_service.rst | 7 + SRV/docs/api/services/wifi_service.rst | 7 + SRV/docs/api/storage.rst | 8 + SRV/docs/api/storage/json_storage.rst | 7 + SRV/docs/api/storage/session_storage.rst | 7 + SRV/docs/api/utils.rst | 7 + SRV/docs/api/utils/i18n.rst | 7 + SRV/docs/conf.py | 58 + SRV/docs/index.rst | 19 + SRV/poetry.lock | 313 +++- SRV/pyproject.toml | 6 + SRV/requirements.txt | 4 + SRV/services/arp_service.py | 15 +- SRV/services/connect_service.py | 9 +- 216 files changed, 39471 insertions(+), 13 deletions(-) create mode 100644 SRV/docs/_build/html/.buildinfo create mode 100644 SRV/docs/_build/html/.doctrees/api/config.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/config/cors.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/config/init_config.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/config/ports.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/core.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/core/cli_executor.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/endpoints.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/endpoints/auth.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/endpoints/ip.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/endpoints/network.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/endpoints/settings.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/endpoints/subnet.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/endpoints/ufw.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/endpoints/websocket.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/models.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/models/auth.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/models/network.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/models/request.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/models/subnet.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/models/ufw.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/models/wifi.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/services.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/services/arp_service.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/services/auth_service.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/services/connect_service.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/services/firewalld_service.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/services/ip_service.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/services/network_service.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/services/settings_service.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/services/subnet_service.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/services/ufw_service.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/services/wifi_service.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/storage.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/storage/json_storage.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/storage/session_storage.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/utils.doctree create mode 100644 SRV/docs/_build/html/.doctrees/api/utils/i18n.doctree create mode 100644 SRV/docs/_build/html/.doctrees/environment.pickle create mode 100644 SRV/docs/_build/html/.doctrees/index.doctree create mode 100644 SRV/docs/_build/html/_modules/api/v1/auth.html create mode 100644 SRV/docs/_build/html/_modules/api/v1/ip.html create mode 100644 SRV/docs/_build/html/_modules/api/v1/network.html create mode 100644 SRV/docs/_build/html/_modules/api/v1/settings.html create mode 100644 SRV/docs/_build/html/_modules/api/v1/subnet.html create mode 100644 SRV/docs/_build/html/_modules/api/v1/ufw.html create mode 100644 SRV/docs/_build/html/_modules/api/v1/websocket.html create mode 100644 SRV/docs/_build/html/_modules/config/cors.html create mode 100644 SRV/docs/_build/html/_modules/config/init_config.html create mode 100644 SRV/docs/_build/html/_modules/config/ports.html create mode 100644 SRV/docs/_build/html/_modules/core/cli_executor.html create mode 100644 SRV/docs/_build/html/_modules/index.html create mode 100644 SRV/docs/_build/html/_modules/models/auth.html create mode 100644 SRV/docs/_build/html/_modules/models/network.html create mode 100644 SRV/docs/_build/html/_modules/models/request.html create mode 100644 SRV/docs/_build/html/_modules/models/subnet.html create mode 100644 SRV/docs/_build/html/_modules/models/ufw.html create mode 100644 SRV/docs/_build/html/_modules/models/wifi.html create mode 100644 SRV/docs/_build/html/_modules/services/arp_service.html create mode 100644 SRV/docs/_build/html/_modules/services/auth_service.html create mode 100644 SRV/docs/_build/html/_modules/services/connect_service.html create mode 100644 SRV/docs/_build/html/_modules/services/firewalld_service.html create mode 100644 SRV/docs/_build/html/_modules/services/ip_service.html create mode 100644 SRV/docs/_build/html/_modules/services/network_service.html create mode 100644 SRV/docs/_build/html/_modules/services/settings_service.html create mode 100644 SRV/docs/_build/html/_modules/services/subnet_service.html create mode 100644 SRV/docs/_build/html/_modules/services/ufw_service.html create mode 100644 SRV/docs/_build/html/_modules/services/wifi_service.html create mode 100644 SRV/docs/_build/html/_modules/storage/json_storage.html create mode 100644 SRV/docs/_build/html/_modules/storage/session_storage.html create mode 100644 SRV/docs/_build/html/_modules/utils/i18n.html create mode 100644 SRV/docs/_build/html/_sources/api/config.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/config/cors.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/config/init_config.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/config/ports.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/core.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/core/cli_executor.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/endpoints.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/endpoints/auth.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/endpoints/ip.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/endpoints/network.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/endpoints/settings.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/endpoints/subnet.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/endpoints/ufw.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/endpoints/websocket.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/models.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/models/auth.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/models/network.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/models/request.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/models/subnet.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/models/ufw.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/models/wifi.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/services.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/services/arp_service.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/services/auth_service.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/services/connect_service.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/services/firewalld_service.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/services/ip_service.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/services/network_service.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/services/settings_service.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/services/subnet_service.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/services/ufw_service.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/services/wifi_service.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/storage.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/storage/json_storage.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/storage/session_storage.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/utils.rst.txt create mode 100644 SRV/docs/_build/html/_sources/api/utils/i18n.rst.txt create mode 100644 SRV/docs/_build/html/_sources/index.rst.txt create mode 100644 SRV/docs/_build/html/_static/basic.css create mode 100644 SRV/docs/_build/html/_static/debug.css create mode 100644 SRV/docs/_build/html/_static/doctools.js create mode 100644 SRV/docs/_build/html/_static/documentation_options.js create mode 100644 SRV/docs/_build/html/_static/file.png create mode 100644 SRV/docs/_build/html/_static/language_data.js create mode 100644 SRV/docs/_build/html/_static/minus.png create mode 100644 SRV/docs/_build/html/_static/plus.png create mode 100644 SRV/docs/_build/html/_static/pygments.css create mode 100644 SRV/docs/_build/html/_static/scripts/furo-extensions.js create mode 100644 SRV/docs/_build/html/_static/scripts/furo.js create mode 100644 SRV/docs/_build/html/_static/scripts/furo.js.LICENSE.txt create mode 100644 SRV/docs/_build/html/_static/scripts/furo.js.map create mode 100644 SRV/docs/_build/html/_static/searchtools.js create mode 100644 SRV/docs/_build/html/_static/skeleton.css create mode 100644 SRV/docs/_build/html/_static/sphinx_highlight.js create mode 100644 SRV/docs/_build/html/_static/styles/furo-extensions.css create mode 100644 SRV/docs/_build/html/_static/styles/furo-extensions.css.map create mode 100644 SRV/docs/_build/html/_static/styles/furo.css create mode 100644 SRV/docs/_build/html/_static/styles/furo.css.map create mode 100644 SRV/docs/_build/html/api/config.html create mode 100644 SRV/docs/_build/html/api/config/cors.html create mode 100644 SRV/docs/_build/html/api/config/init_config.html create mode 100644 SRV/docs/_build/html/api/config/ports.html create mode 100644 SRV/docs/_build/html/api/core.html create mode 100644 SRV/docs/_build/html/api/core/cli_executor.html create mode 100644 SRV/docs/_build/html/api/endpoints.html create mode 100644 SRV/docs/_build/html/api/endpoints/auth.html create mode 100644 SRV/docs/_build/html/api/endpoints/ip.html create mode 100644 SRV/docs/_build/html/api/endpoints/network.html create mode 100644 SRV/docs/_build/html/api/endpoints/settings.html create mode 100644 SRV/docs/_build/html/api/endpoints/subnet.html create mode 100644 SRV/docs/_build/html/api/endpoints/ufw.html create mode 100644 SRV/docs/_build/html/api/endpoints/websocket.html create mode 100644 SRV/docs/_build/html/api/models.html create mode 100644 SRV/docs/_build/html/api/models/auth.html create mode 100644 SRV/docs/_build/html/api/models/network.html create mode 100644 SRV/docs/_build/html/api/models/request.html create mode 100644 SRV/docs/_build/html/api/models/subnet.html create mode 100644 SRV/docs/_build/html/api/models/ufw.html create mode 100644 SRV/docs/_build/html/api/models/wifi.html create mode 100644 SRV/docs/_build/html/api/services.html create mode 100644 SRV/docs/_build/html/api/services/arp_service.html create mode 100644 SRV/docs/_build/html/api/services/auth_service.html create mode 100644 SRV/docs/_build/html/api/services/connect_service.html create mode 100644 SRV/docs/_build/html/api/services/firewalld_service.html create mode 100644 SRV/docs/_build/html/api/services/ip_service.html create mode 100644 SRV/docs/_build/html/api/services/network_service.html create mode 100644 SRV/docs/_build/html/api/services/settings_service.html create mode 100644 SRV/docs/_build/html/api/services/subnet_service.html create mode 100644 SRV/docs/_build/html/api/services/ufw_service.html create mode 100644 SRV/docs/_build/html/api/services/wifi_service.html create mode 100644 SRV/docs/_build/html/api/storage.html create mode 100644 SRV/docs/_build/html/api/storage/json_storage.html create mode 100644 SRV/docs/_build/html/api/storage/session_storage.html create mode 100644 SRV/docs/_build/html/api/utils.html create mode 100644 SRV/docs/_build/html/api/utils/i18n.html create mode 100644 SRV/docs/_build/html/genindex.html create mode 100644 SRV/docs/_build/html/index.html create mode 100644 SRV/docs/_build/html/objects.inv create mode 100644 SRV/docs/_build/html/py-modindex.html create mode 100644 SRV/docs/_build/html/search.html create mode 100644 SRV/docs/_build/html/searchindex.js create mode 100644 SRV/docs/api/config.rst create mode 100644 SRV/docs/api/config/cors.rst create mode 100644 SRV/docs/api/config/init_config.rst create mode 100644 SRV/docs/api/config/ports.rst create mode 100644 SRV/docs/api/core.rst create mode 100644 SRV/docs/api/core/cli_executor.rst create mode 100644 SRV/docs/api/endpoints.rst create mode 100644 SRV/docs/api/endpoints/auth.rst create mode 100644 SRV/docs/api/endpoints/ip.rst create mode 100644 SRV/docs/api/endpoints/network.rst create mode 100644 SRV/docs/api/endpoints/settings.rst create mode 100644 SRV/docs/api/endpoints/subnet.rst create mode 100644 SRV/docs/api/endpoints/ufw.rst create mode 100644 SRV/docs/api/endpoints/websocket.rst create mode 100644 SRV/docs/api/models.rst create mode 100644 SRV/docs/api/models/auth.rst create mode 100644 SRV/docs/api/models/network.rst create mode 100644 SRV/docs/api/models/request.rst create mode 100644 SRV/docs/api/models/subnet.rst create mode 100644 SRV/docs/api/models/ufw.rst create mode 100644 SRV/docs/api/models/wifi.rst create mode 100644 SRV/docs/api/services.rst create mode 100644 SRV/docs/api/services/arp_service.rst create mode 100644 SRV/docs/api/services/auth_service.rst create mode 100644 SRV/docs/api/services/connect_service.rst create mode 100644 SRV/docs/api/services/firewalld_service.rst create mode 100644 SRV/docs/api/services/ip_service.rst create mode 100644 SRV/docs/api/services/network_service.rst create mode 100644 SRV/docs/api/services/settings_service.rst create mode 100644 SRV/docs/api/services/subnet_service.rst create mode 100644 SRV/docs/api/services/ufw_service.rst create mode 100644 SRV/docs/api/services/wifi_service.rst create mode 100644 SRV/docs/api/storage.rst create mode 100644 SRV/docs/api/storage/json_storage.rst create mode 100644 SRV/docs/api/storage/session_storage.rst create mode 100644 SRV/docs/api/utils.rst create mode 100644 SRV/docs/api/utils/i18n.rst create mode 100644 SRV/docs/conf.py create mode 100644 SRV/docs/index.rst diff --git a/SRV/docs/_build/html/.buildinfo b/SRV/docs/_build/html/.buildinfo new file mode 100644 index 0000000..e5b0f18 --- /dev/null +++ b/SRV/docs/_build/html/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file records the configuration used when building these files. When it is not found, a full rebuild will be done. +config: 0de2fdeef2a39cc42113efadfd5a855d +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/SRV/docs/_build/html/.doctrees/api/config.doctree b/SRV/docs/_build/html/.doctrees/api/config.doctree new file mode 100644 index 0000000000000000000000000000000000000000..f1718f847bbc6e5b38ab68c232720f649f38a3bb GIT binary patch literal 2882 zcmaJ@TW=gS6mBki%Vsy1UR1RSst}Yw$u1xy5WFDZEg}yE^#Lgwd1t(96OTREo@Vn< zsR&v%$Py1V65>}N@lqkdZ%F2H)^6Z*x85&!zWUbE{tjStO zeh-@s%dMbuTkiJx@8|!V|9$b(`K$9kFMhmudj7}7^Yd31zxZ$lM^?X~)8PDnG@to0 zux3UOhV^XIb)q_Mw&I#qxQA|4a7q1PJ|b?7SgZLcw@5RjwZapF6kID7#25(i-H;?L zzuHNjT`oo>KGt*`baL0SWD>SK>$5wo%kHujw&Ok>GL_O{N;MS;mBW;9In1>B4&^b! ziINomzJB;Vym$AqbI_9FZ%sykEvoEP@r(~MY%rbwlgtC*Yzzv!-5o+Ym zT$AwxrNdXRGEocKGEYY+ZQ>8ExwQgSMoDoB-8I1TGL4+Q!#-d;>;X!;iuMf9`h{}1 zjjC$E|$`Vo5qtYqLlLvoGt{>+*up_Fuus)NNoN#SJ zt>tn8)^F8gV62t26!>$^M?~akU_A$#Dz{l~{UPfgy3T}3s)^d#sVtq_eBpXw+5ijD zbgXphU%0*jfHQzH4MWSo;N4O}1uZ1e3AZeoaUA}t0~P5H-MXd$yOE%eDayI*))*1> zvf;Mq6sSjbHa9b+hHE5rluzKUe?qiG6``RiNg?T=WibsYh4{Ec%_1sel|ak8PHxBh zAMYEE@14cXh-$5L#E47;a0wV7x7d2OQC5D=N~5si-R_m0D<;^_u3s9x3}AM2XK~}o zNw*mi0PqY$eFPHnt|y3`27kGbI`UN@qI8!{3hkuZaCur=ceIJb^u446@n4tu>yEt*X}I; zR1Y|{|q1|@70Aw1~%&ZfLOcoGw<~Gf#v}77FK4AhsfOl1> zfctfm60q$19F#UF)b(OrE+e60QeGBrH)aI!pz$TyQ01b8IDMRFa1ov?#RX8!0NfUk za5)ABs-dtQri95ySJZeKuqzZPnHb&%#~zTu&60q=r-*=2YT@H%46Wmc;u04TF5ED5 zi>SA%*AB2M#^pdeb1PIn4sQ(CE=LKt3nX%xaB#y$mgosC|M0%pLQp`K#ghx;99Lu* zl)Xa?^SWS z7jtwxxQE435-2&-E5n?ZlzIpry-i0-)a@@+Z(I@9+MNZ9otf9-(Ds6Tj8<*Kli=o6 zTh$J3d<={j&my1#*Ga5|Kw*&s?u*tJmHcH${e*t>dleho? literal 0 HcmV?d00001 diff --git a/SRV/docs/_build/html/.doctrees/api/config/cors.doctree b/SRV/docs/_build/html/.doctrees/api/config/cors.doctree new file mode 100644 index 0000000000000000000000000000000000000000..cd76102bfcf7d6c4a5a6d3c3e81d9008625d55ab GIT binary patch literal 13465 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 L6iCavUe5mmZCK%4 literal 0 HcmV?d00001 diff --git a/SRV/docs/_build/html/.doctrees/api/config/ports.doctree b/SRV/docs/_build/html/.doctrees/api/config/ports.doctree new file mode 100644 index 0000000000000000000000000000000000000000..f3660429d38f04a1dcf163849d163603bed7623e GIT binary patch literal 15427 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 NgSJi-n+USj{vS^IHc|ip literal 0 HcmV?d00001 diff --git a/SRV/docs/_build/html/.doctrees/api/core.doctree b/SRV/docs/_build/html/.doctrees/api/core.doctree new file mode 100644 index 0000000000000000000000000000000000000000..7d26919c469c291804a151e9758f84a598849a51 GIT binary patch literal 2764 zcmZ8j%WfMt6m@LtZOMQ_m-l(jCI4>y`(J;j{PUw!CxsKzj+IWC4Za%%8B;EN z3g7$_-i2qrXXv%ng-KZOJJ3kDkf~uRya{~AcPvYs(5lhrE#BsxH{lsJTTVF1*0%iq z3vF14``F(9gDs<@TKYaz4%<0}c^CMR?~x`RzUSL@7*oG5g<@e^%&6~E=Zu&Y4&lde z_e5$Dn(ri%S_>h*TZ(4YS2r#4yTyzqSBA}_PT@O_F5*_;BmR&N_#?i@&-~95t}`~t zm|-$yYLZc*Cb`jnB5gZaXvOgF_g9w_1ZpQV7n1}Tj*Uh9=P!4Q8S?BVI?HvT(zQxd zMe>I>=Rz&VG)?Ocbev9{Ve3_^bom!Zk3UA`zQpedeoygxhTlWqT{%YHD4h{ywo`w) zl1~yT2wO6ATbuA9P=6p)B8!yGg)Ei%uhe%lx=dN_czAga1*Cb|S(JMr^(@@_HWw+1 zfUjYFx)F3$k?D|rLmJ^*wVRKdUHKkzgD9Oc{{oWi5*6@17qeg^8dh4TKL?k_yVcYngLGEkMHKhLv-p zm16*oZ(mYbpivDi+q!Uh;lh-UroOjeiWwSvi$zL721m0Ti-FPFAyo8v8T`FT@PjG@ z0>4*K=-11_NUVmaJ&Cl4y9lKf8+Q}@$sA^sc5H3)qaQ}{qw!=p*Cq=e{m24)IRW0| z46!iysFE4NjEZ}Z*O^reopW%Mq9)zP!fjjfaZC?){lS&sThBozEm zQne`Pf}z|g*1I64zi$}epAxVbuz{O638ucCXf;QUD2GT_Xy9!>qL>HdPULq5ur^GK zEGG>fQPol)07avyj0O)&Ny&!T`a4l>S&?e8%p#hO*fQ6ak@b@$L=s2d3M(qS9+Fg8 zI+HPxI?B$9C*a%xlCfY$NjexE)@o!mx%_AG3r;LEB{7+b)>8C)fYF0KRZGmY<488Z zMlFCsDKg>0Z%1GIQOL5SvGY-1;MXqa0llE*4zk2k`Do9dUnPGYXddRe;hKg}Ps^ zt0j>-q1C0}&l64&4;o*Q4OK2nh|^a^4j1tSQBM1M2HN%!202OugcTIM4;o*JuFs|NXeC673R96 z(nIj*Z8p=gX@9BuU`yC&_cko{R$j|PCmZ%LT8)eM|LL}^dI#4#21bJS3{ZjVB+*i$ zu*d=TMdw8=|Age#AV9{^%DaSS;TJj(>U7dDG)F3 HVmAICl+sSe literal 0 HcmV?d00001 diff --git a/SRV/docs/_build/html/.doctrees/api/core/cli_executor.doctree b/SRV/docs/_build/html/.doctrees/api/core/cli_executor.doctree new file mode 100644 index 0000000000000000000000000000000000000000..78a1b564b735cbe4443b57b1a864e9438f5f28a3 GIT binary patch literal 11915 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 literal 0 HcmV?d00001 diff --git a/SRV/docs/_build/html/.doctrees/api/endpoints.doctree b/SRV/docs/_build/html/.doctrees/api/endpoints.doctree new file mode 100644 index 0000000000000000000000000000000000000000..61c2e615540d8416d1b33f8766c0d12e8ee9686d GIT binary patch literal 2993 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 literal 0 HcmV?d00001 diff --git a/SRV/docs/_build/html/.doctrees/api/endpoints/ip.doctree b/SRV/docs/_build/html/.doctrees/api/endpoints/ip.doctree new file mode 100644 index 0000000000000000000000000000000000000000..3114f7f167e1ca81c269b8732f67179933e4fbe5 GIT binary patch literal 36193 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 literal 0 HcmV?d00001 diff --git a/SRV/docs/_build/html/.doctrees/api/endpoints/network.doctree b/SRV/docs/_build/html/.doctrees/api/endpoints/network.doctree new file mode 100644 index 0000000000000000000000000000000000000000..447259c86a0885a91cd01a5bef1b98bdf375583a GIT binary patch literal 47126 zcmeHQ4Uingb=FCDr@PbX^aH|1NO&y_tZdyKU>n)EgOKs9G)nnapv$1TW=Z$qvkMLJgk-qm9k#KZKvTwRTGU_zh!Z4%wAzuG`%!6 zq!bE{6uR_ML7ftCTOD z_-~mNC9ewaWbhoqWgTggy)s;z&>*{u)yKQi8uypeaUjWCxz=(?Dgn{i9Xsoas7NIJ zkE6^-A#(ws9OF);*^zPS;mW6Vn~c`(W?E%VOAT-(GGzBoO%_KudAp}lRZX?JORW>1 z+fjC#(<4hsdn9$nT^D-2urfVDeH4ACG?6Kfhm=sFa_hHrFo^oO5}@ajm2hpQ)@82< zX|D@c(|sSjL-F8c?Pbu@LA0OIv6mBBCU*8)6(Q5^((WdF4*K|L2ViPA!@Z2*Ft=}x z(|4Ec_!oTsACmFkL;Q2*dx(X2z8`Gn+dj=Pk?21|yL5GT9-~#=Ug2Kmlu}+RR|WiA z8O&aocuT2Hs8-#6Q;r)%ZmNClz!-tI^g>;G*zU#^G^K8@Aq{FW=S(60!e^X!HbJeo zC}guS5&?#~-A7=Q$7sB$2V`e=b5@!MLbqRQ-FDA-typCEfw8`vRnF%yI$CC3`=0jj zU3CnQbjx{xG-0c@j%xrIypDt3@%ECo&gkq|w1|uj-ha_ixtCoh!qyuPG@fZ3nYnZ3&P?O+ z#xpaAGc)@d&&}*<9BCZIlVgn&jpMl4c&>4>@gy$qqMwat8^<&F|ID3@lPJ0ue-G1( zc>UQ-<7vEl0ymE}p2E#zsOx0o=#Xo_=8>YOfyOezfVqrOgRf|_9n=;|qRkR|r13Q- z4U)rN0v%{R3S5CHi3|ARE3q4wB zeb9oZA<5&Qb%aFE#f~z^nxA+QegkprYslKP8e*Ku`_&SmU$y0&Yag~hjL|PwaU60C zp@p0YPgc!t%w$8(OPqI*nN%&UT!6E_Ibn&RXqq7(p6R2u&Y66Sx^}~zSl-~?upf)# zEQU^&hC4P9VdLXS2CmVk^aC7F9F#H(XhPKrzMRf<}eVZ`D!Jn~mx#&9eU+@}nMjR$IH(q1M zJ@&QeQmGbYwQp#T1V%R3LxiVgyw59Z@1I{MIz~h^E9S21YUZkv4+1q)t3@4CX2HW& z=3JJJKjTh>JWfO|PK)5eE63b)nqhKpOkjQ>Sj-)!!nb`Hf7Fvx)L3G7l$s`2#&#;Q z*4T!Bn8SBE!;`qF)Jl1))--GF7;*W)l}~zS3(}Ql(t3b&#Z&d@X`^wwl!A6g(;OA^ zMyFlI%_!^DzXbi6x8bMmJ!dAc2Cg@r`K_|$G{xwO;Mj4G(ev%J?*|+-MuPcyW<|~G z<9c4|+;Kl{t#JUXgq=)7#m#WLgCdQ0!ki9nY^64?HAY$e#trz#Ma8~UG+-l{lva*w zco6%3u|`*sf;WBiA0^IWT_b7Zzr|Z)h^>jy+~?#}cHwao#E$B6Dq2g`eL+ehMs=Mg z1XQ6Rk3&|J%sg1yus$-s*5>~g4SIlMO&Orijjy=AGenvPJ$2-1t*;FSHI}2w$Zac1iorx zM$jJbaP_HUhtLE?+5|&pk2w{s8K))itOXKfDT#zAg)AW>k>2D*0<*~jW|P46J}K8p zfN42UeOgK)#-t=g{b70KZH+&3kNRU$u4e`H??_3cMZJ?Ikb>I0D6hRO*g1_^(sn-C z#U!@l7J==5l(L-wce$A-V|_J|h%?sTcD>+P%@WwdSGd82P?j@prt4)Dv=cNHK>k3O zj)zkS#=9`u@GOG%9qliJ6Z-EqCC;_)YY%HbFqSL<4$m9Q@Q)pz!-NBi2VzMi$b28aED7q&GU~ z7#S3%kn+>0SF^urO!GMv1Jzwx8{`#JWTus1zQ5W!J zF0?@y^)62%UY0|1WZMbZet^j~J`YJ_-lasq(+DcXH6nhrq*CVxN|7GDjnOi)_{TNW zc6BSC*o^c=#ljZULlQWhm^GsO+Un7Hl)g$pReY_(VrJu zK73zF1D~j(%YfZO47)TcN~JjBX;hTtdb)9TCcN#2_ z<=KmFp0y3HJaU$<7(W09_ELn2*yB^1fRN?ZvF?lhzaj;W9ew4==zsCb+Mk@~WOP$QSzOn+7PD5*2^#j@&qTO# zID>h&RZVp`Zv~S<;#)50{ehc4;+>r`#HG2kMJU=7yN%8Qua;Ah#cny3*15EdltfIh z%xOXZJ{CG;3j3R9b6WRZ1d8+ngq}t8Jf>)0|Ij+rxyB zLfd%LPfgqS=o6`J)1J0X3)=P#DPU4<`zmW-B5iv@PDQ3|oJwnL`)etQ7;ST!(5|+< zDyKQ0+V&C?Mhb1?O+O`V>lb~TD4px(FSv^5{`76#e@Ow9YTs{JgA-|APnUH3mT4cS z(pvk@kdlbeKBox*?c>>@n4vYCp32IpwxxgVrl;NpUQ(?;;jK@pfb!RkE0L8tXk3NA z;)(*_>otB)4lwsTKZv(H&$H9>&LcA5{GqS4TW14yV+ZI5)(LPj=@Wya5y(e*DNf2R z5)EyRQg+!Z5>Bb=5xF{_)23YA&mqUuxwKeN%*SoY zT;$yr(=Z#5gqiY4Jw!=2gk7BsUtq6E<49)vP~UXYcW)l*p_os?)|Ix_N@dmfka*Nr;Eu4uI`aond@{QRKgvr~t)2KHK&O5qIxyx@d z-44zeM7Kf6cnYO>kBE1}gp8-qk3P@)Q6P78n>2S6&ES2Kur%_mv87VAJc*;vU*(5>LeMdlAl4X-zr_fEe1WU-WNyd-h zWEe}k@#jUl>Y=NKcQu2ngT_nj@5}7(E9~#zu)nXezkf@A$%|^r`UKes*#u&oZ*rG% z?d-`8tfJ#gukn~Z!(*CVwZ-%@my7m}P{A(EjkG&ZE1MQ==%+jt&Pw5X#Y3l6U>D|D zp^TJRG?aC6&a!E}#qy(KC4p^^r@U2?draERt~c7^*c8Q_F6C1ko09YD?P5jUIFIwo z@AmJ6pyN|k+RpGEAvz+(7)Bw?mRhd6zD!#J2-I$it!GQfIT)hNi$Ic z-0hk7c;GgjXyTm6&~)Di{3eg(sLhO7?LO`Efu89$7e|^1%{6YMYFM;HNLls=x#fFk zgExIf3N09s?VR;-7Z?d946g~UEbPlB%?Ga{{%hpt4xML zhbMv1TZ`kb+_A4oO#yi6RwBD`9acMbVxjT}pU?3cg7ach=_%YG)NV7Xf@nK^ed`3imeIA-3 zzvrPus>p9Qc4-h9M1Id&3NM+i@1kuN1L8m?L~Y$s4?QqT<=DDM*`A7v8=t}C^U20h zI?)B&DEPh#tZ1aA^E3M~X{Ez-j?fJDIJ?AoIx~m7sB62$eRX>)+9mO?1j9zAg?2T} zmm>oV&vuxDFXu0fA8ULDi$E!|{R~!sI>+76MiM%s2zX%vPjJy{(W4ABOzE8?ZCD|; zf5kh+2uRaPQd)BAq3yvtAEbOyT#!~2#xRxO;YJ^-mO*5DJC7g-9iZ9UUJ+bp+ak<* zRhmY-5fZ1}T~LwBP`*W&)&A32Z>c+7nS+{reYhzNG%Vub;L;6TZeX|s9RSgaQ?Sk1 zlpvJiXvPl}RLa?uAjn`dmq8#zH}38R=qu=X9IV;+GP*0z80?v`(LpKB_RP$^*h_X2 zTMIV0vuN54lz~B{Gpp|AJrjR%hFD}rjSTGj`wpDp*T3O|Yo!4Ac*ZE%=g(O$5;p&?@t4osF4&G+shD3qmfjKyn>9E4S13rjA*`CBuasVoO1i@ik97k>yn+&k;48sZ(G&GXXttbVh zy>K|d*2Gx_1@+Q*UvR<3cK0HDB@Of;eWjK04>Rz>zcxkYN6KlrFr#QHZP8$z| zhj`%jr@(|qn}0@EEZWSj7ASB#NdCKX+;W zaNgaO7>0MSBlX-fPw0+&;@yJBd4DhOa?x?#|72aQbM>@%g4qEWagSswyP;TIp z;=I4{xVOjWF3ua&I38R*rC9ITQe5P*Ucbr4da5QI>s=XEZP#IGe&8G4O)VIb#e5?f z`lE{ZUM@vX8vfgiq6uTZ*UPEMV!oV8>;C$2DT$Z}ozsNbi}_aMTnM2N$)Qf6a~JK_?I+;)-tgD;- z6H0M3<41EL`zOd?AD2NO`**_2{`p}?7H*U#0{J0i;#MTGR>5NdVL@Sz$bQ1SJ;ByY zP~zoyUk3k+u*viloV_rW!n%JX9dTYI*~y5b31{|4?-ufNh?1n@Q)y%tQfFFlevCue zoK%eH)xpHpk$J;;4l&l|s}XJ}vXY))c%u+$X!8!CI4u?CwKJ!nE0p?qAx>9>r7~B~ zj<8&~W~(}X(0e@%2%?drl-X+xfiy;rN^#uM7&$`w`2@6_B%5OzsnquplyVkf2&K3c zNW&r74l=`0DevhyOnG~g3>ec{%XF{qlZ6DrqnLBlsskUs1Z)hTd@G;ewmHajCjbKp z?ggOiceiuWKr42)6~Hyaa81+Qs1%n*8f7LK`pPUX8SHZ85&E96v?R-GyVLLX^V-({ z6Q0+;mabS{n_VqXUi)I_s5F1~pZg8D776a~SLI^sX*bjT_1GG-lz+#5B_SZoF_a+fL)%Oba<>b&B`9AKlAuczX!^1yJ1r`;k@{n64v>KD9IbX zDSxm9i?U34B#XgJ`Jq3cPRaXZaCgmL^)i zg`x>F>d(ok$TI4jO6xBFf|Nu|62xgj0IoHWkUM6xbvExcIUfs_F8Ohkw4$?lzmYaU znl9l~T4(b*mL+w8B{|R8%H~Bv6P?XlBjq~@G%d4v=SWG!C~`dRFdM?0{6cx<$+RI5|OLqOh=FAkwU%0HG>^@2%y{`t$$X8sz^zs6>9jXyRy9YwxrtfsJqzY zcqH=CiP?Qp#uFeP^wts#SFHz<&r5lV5tTELBv}{qHEI3QbwNqT!pEc}e2Hr2&(_9a?tf6537qZakzdw%#%x zTIux!mJd-G+M(FIlm~YslHl|ziZ&f5i==rUeQA$xnLmc?B5DU$Sg1rBBE3~Zo-p57 zO#8ZWIIXT2_fiSI7{?c1MA|+Z$lGtHPp|Xc_!Nf9Wc}#MjV0`*+kwgBCLRL6g^opy z-$c#UPK>Zs5QCWwe*9lAY&exuMfsN*FTxS3w7w!x^ORq4>$6~+N>3*)jniSB()^HE z6r7_|VuO3{7U)2PuclEwcMXZ7eYElaODG?i{>(;bV^A5ZP{!F&!+@g9nhFMLO{IQ; zlBrY=CFfY}c@G*tHx<1b}`` zHO79;EW)94n@Z`^`X{8^=L_e0{r+({iyhQ$tRrIOOpYV7r@J z)Vkd}MdtzN^Aonj z^<5Lc?|Y|vPC-nxWbuwzG5rXg!gY+?&$9%arT4are%KkfkJ`Y!rAsh`IWi55VTrns zFf`qt1{TquTnS9-43jkO5|#QiM=}7>^>dw{Xp72toHqSuO@0Yj6HhEc z1L0|a>K9Bb?F@v6V;AnvI*4rpsLvaZu4$S_ivBRWWT1T|I2}sxKXFPR*z$9e3jup8 z629#>;q)|35QoGy;C~egx0)aaZ3~s+txjWG2&?~2V8?>ik4pV6L8%~zREl@bG#pZw z_N|}1WROdmY53_9X-T$Y!%a-KzhmP%NR98FhM=juIjp%jQ1NmCDs+O#rQ`e~7@7^h5dqsWJ z?lD!%(n}L&-Ck8F=WCW;G>68k_%x?lD&WhT?Bi3W7pS$n%QdS~v+ASTz^L6jp_bID zVwJ0?sTVPA7001YR`6q4wOqEqDt2*9AuutF6_T6<#C=OU@j{>TvsNwQ*K(_z&y6Yh zo%9r}bb;sUblqM(j%rF}OBEg-u$P-^-YV55b5rBx>SX=hh&p_VRIOWj@{dJ?L_xC=eO9^m!*Jh-LT1s{Y8 z+bgLJCVbwOUe&B)S#s4pVlLzJIG?s?KM6}=Q86mZ^8cg7U(PF=%(vu0Tpb7XiJx6Pr-w563xL*?oOdej21R!<2jQrVqqbx2pOaYh3MVOX;!iy+o(DP!nG z1xu@M>9N;CCns}W=kywoGb^Ma2kn*kUX-OwK<-FoRyFRZe;0J=|of0TB2vK@xtpXAQ;Lg%5;Lpv#L1<4oub0N51CCu- zO@_j(jfGy*CrNjUWrcUfPG*vzGLvI!0X=vc9B8U)fySCV-rAW{Cl$SzQwjyLyNv5q zR5MB3REy)>rX94GsHI)?(b|~Z!>tLrFEmjv6?F6sD`zQH92>;+W)+x%W?3+|r~`Z5 z$eP=$19$10W9&9+8~1*6lSwVG`%CF2zcy~B!R{vfG3{Qujm>tfFne&&)G`<>*mG@} zb_tSf-JBWEu4`R9-l3h17qtw=tV`I^Brzjkcj+d+epYAS&;B83;bRdu&dK&Kw#f^F zCHqhsR>^G9wi)zIP~&~{hd%IZ&c@~$wdybs zYr=IoPcX8>O6JQovVj;z^JSz@neJhnTzZ9b|Fh7a{q?Qr@fD>C+u;NCvnq@=z-Q!ByBf)}-bOQ<=< zUTW0;edTieC=0V->Pw$vC8(K!Vw$NKsUvFiusfLW&spBcXy5 z^1be!>7Loy-krVMlY~Li%^PXKUh3{K^ zR=>6Mu6hl!tW$vRqn_qaa&Wq7sF_5WO zw^sWDxv-ShV!vM$Zp>d&v`c1vtTt}=gNEl-?eUt2N>L>i+XWW|_Lt_1hU?-XuKP;H z6u(+ls8!_6xRKvoH7Dqun!nUDCh4cTHDs-`mRlEDi>>wk^`lmK${d|Ct7framPV%x zyEIy;CJf%7t3d zd`Bj(b^mqzm(B4Xto|{tZq%$Rtj)5zW7Y;J)f(Y|2n^g|QTkoj86fb7^F`CB=8R%7 zS1>#yyD5{kOBr5{cmX%n{flEo8;K^b>qeZ?daDF(N zMb@htCAVl0yxnb?`^SxOvpC|`DwT588`-7&nAx7W&aA-1ZX`{vz#f{V{PdW+;&$oY#{gFF$al{tDGTJP)`C@+D{~xqaw|lq!opU~Mdwtoz@! zE|02c^y%hAp-jD2-CtChM(=?K-gE_xzmIu{I(x-4s*|Qy_t&*{eRY31^_3MfZ%^3y z`VoHsO`SXSgw7nUWqKRWV2rH&x6sFF^w$x;Wj)>!6@MM-t%X5;rcuC&Cb5AzhW#}m z*vb{NYIxos9gIOL!>#YwMC@9X2Pfyz1$w7STm!l@Xr^0XwIE7s@^@dGL6!X01wU(No5Ye zj6y;g=>*RBLA-dN2|KYD{R^j13Oh$S$%XC`xA4(NB0s{Z*?4O*moHCE8TG>i&yOMt z(e(xX9);&aHH=qB_Soj$JO<7{dT^ODN_i`So^}H_YKuWbdqa*l9X@7MI#9Gryk-~b z);)eduF#p*{nt@1+dQzOUfUVvniQqy!vjU9I}92?Vq6UWTK5O32P}`%=(Qdl7WB7l z%GM98et+3StypCJ1Ijg+^U8Trvgg*F8z}#K9)cfXPkHycI@)h%H?aZ6PHR2aF0_3M z!JfH)K1|A&OP=~Q5)3}+>tcZHIaxI-mfBXm#uGD#8pmgjG>$fo%{-84JcEl24cT2V&bYjVBsU&3v5jOR#?)O5Bf|rwQix1Moi6fNlxq^vXfBkYkO%X`IeL-`_y% zIMFydGRS~TjcW-;vXLzNSe60M#hp~rM^h>3K6-n7prk%1X&O(^?icujvhGTaxo)C> z`QtgSeo(s}NS2!VbSh2pHX3NEADViEX(~2kQo22A*`J8$mr`;4Dm_4qKcG~5BdN$L z9?Q$whkwF$`r(}>KAiDIJXo;v9$@!5?or_g_s{s5nFIJg@CHxG5OxY}_E_UN<~rej z@f#uYFkJHK#gR=2`iY`$0Qx!2^pgg4l!|LL4eH1iggQPBh$7F}Y1rWI zwhi8}YuBys*_Ssf@XfYMeZ$%w&R(s>wyDu zG2$zj+uwHOm7916l8wS|smL|3Q+O|x`_oj)je>7ED*P`@xlTLq-PzlETWkFu<{-88 z%T(HmLU9GO^$Vu0G*F~eT*qmkNS2XM)TgXcogy8Y9Omd#j!{Z6@U_-A4b<5voqeKX ztPL83sP@5rc#kAeeuCv@pLN#F%rg^xE-Kg^mu%hWU%|g1+}b!O z_I4EV`Z%GgCe{+jV?l2hjLB{b9{4p?NR}65TL#Q#=HAR0h<|Xw^v`f6Iy#vf_V~`Q z7~PP?&UQeebtjp%Olu;7t>9A|6tMI5Fs)zcEep(bN}&6}S0%65A83){YikiC zV=YFKSI)RrLCGAPb2k#fCZ7#Ul+3|86}|8;H!*P_VFTf9*|Gce!V}rCK)6)Uplq^2 zdEW|Z#Tz}ewixdyD*L29V`U7k60FwuCA(_iFCUZT0j8eQ}fZ zB!nv#zBg!U3-48Q@w8revV}V>`8V~#wIy%1;u7-5O3`1^OYgvp{$lj9QHq>r(xok9 zBZNf$J3w$-v;VukddbQY4f~ft6KFwsJO8a0{#sMR2It*z$@>=Tx~F8?TL^bnVHgz_ zo*RQrEm-Tv&40c&7v~}fiqxVlT`h_bGok{cpc@5a?R*dlga?&AeNMu4(yc|~a6$#q zhq99#)~cdKACN_&PWpgtdf~|)LI`gQ@*DKRlR++oE6UoX1o63>Yjz*e2%S{k2lYiz z?B8K;&}Eb@xs$k^St<6BlfV9ibtHy_&bP;B@dFFD-|~^hEXkNETs#4Ib-mgVeHGf z8X3trmr|Pe7qqh#>3Fouc{dU{*W$0p(BHWpPnsv5QGm<2ky233&im-`%vAv ziBgFvCwz1*<5}T%13dm8BDG+%Zb6culM-K+QA+#nu7X#?jB%ZL5abs^T{E8w!(AZ# z)nG{&AL(^A65HR`VmnUDih$JoeP6;n-xDQ_hohWM!cic(gP0=1fU%h3F!7DVY#&7s z29Xzvy@;S8F_oag5nz0{@my$@`OH$9=x1G+$a$F(@$i-rvdBd5L`gmDs%0P*X_(?fp3Ha@%T7?K$L4Z zbLo~VN4DU9SB}1Q>!q7U1~d3CWQ92$138hfu!t^3qQE0Pi>O5@>H5A>)G|D@L?4L*AdbDq&F`#+=L^_BgfMc_fU z$Dt+KuOMYyN&R2c`V{FKMEXSQN4uJ>T^j<#NOy^49p9L$jv@pg5WW<3T+iwl8{Nh< zBVwRRDz2Hpo9nw@;KnfD^9EI`)_(Gqo~ zRQid!2#Vb8X8K9vA}AHtY8up$EeLhzRKVI#rQ9g^R-(d(I|E-2!^r}JJo^S*$ubg(`jpWgi(IE1$Lmw_fOYs;8-#SDiL!iJd5f5}b1{8U zzX-KBZ=|cgr>jhObqTJHIG3`&FJphdiT(X%_V?xN?~U{~I_x=&EoXUrk){*2i!858 zjx1BsZsW>#D7l}7q2rgeZ9pG7{&ASrAMb7Gm>VD-I3AR$4F>h23J@FBhMADFb zaJ@vkb(OGHqniHWw~9+*arhW~ z-bNcR(U(N|x(g!&YmYEVnZgK>)ki%FBg{>f(m*G=Fp%>sCE{TOMS*U+FhY-)zeI`V zHe~(xQq^00N{f2aXV*U>eFt=H4;a4r;0|9-r5*9sAT=ME>K7S0`7bj7$sc6Elh!(nqD?Fxh)f7sgO zcKl(}=0?%v-ExSZi;Brs&3kI5i#3|}V;R4RUA}lId*n8@7~rzDw>ZZ znYV^TOyNI-6iJt2ry=H)f)*FvOFhqM_1t1w`gf726Gr)`df~~8vJl?3E&oC5_RH@9KG;lZzP1b^+x;j!jl0dgiCJ}UH`(qothm{^d*U-kL!z& z-UrR;BAJJnl{#`io$CAx`U-1x-q!s*trwo`emX7rH}%4GC6BtF_G@3n2YCAzC6Hdy zmaZFz#PyQKA?e>uif<*3`g$4PIt9ilD7suA=upp^^2qkGXqk2xq&Qys zWaYfmynY+PCDvp5G-INmdQgE||<0caK+7axzHuzqAIwX(0+H3J!gYX(0`qC>7UT8aR>0 zC7kprMIcJ?WpYTNPbmUX!JxDIK-5vFg$JUZqAM1NVpnG<5Vc8+wfST+{Sg}%#b>cA zX}b+U-Kofk9ELGR2WU7C;5#8$LqE5XBImFwx=mc@C2ezZ>JSy#MMen>eTTKA_JN@v zYqMw(7~;ZPgFhA+dNIty&-ZF-zJiE3iVFQIEOq**kgT!uOA-^03N4QmmxqKR&!kKJ zU7KR);z7y9kny13+vNgl2ecoi$>jeUB3z&Qq?*N8)DvukY3l75&)vGp;TN~X<$PdCfJ;{EZ=A?0?cA>_(v>!cW;^db{` zmdqE6crs{(a0%MzRN$2Jg~#+o=mNgo9bKKg zT;YApN*%$sQ=MD-3Tt)Vmf+r_7oJRTcUtm~>4ob`J`V)P!mvsqx`Uw@J|XW#s!k^j zml~hOZtMIw+~YVQmo^j^=ina4NuW3QJ*HEr`nj}os+HE*>Qw4f+w%N8l{y6-DAY;Y^86dpcMzhDcM2?<#&1*EQjnU$mc(2ChS@SUY*U7k3P-M_;s|Knhby5K1vmf7aT5ux+#H5h zTAm)Q_C?QyeG$33NQX1?@GPcNU${d1VG>hFv`G+OyB6s?XcE`XK_vsU&WS7uq$^Ts zAPOYnxi>NmqyZA8;(AL1B+@nkDJj@;PrE7AY$i39Lp=jGe3D!@ouooXVzZ&l%prD0 zyE2A3g;9|l{zRu;&*o6<8&WAd3OCxr=DN<{X1AJl-5Wh%YW6(lD4~02Ds4ugOZ$S~ zo*KH8irXL!bjflOy84tNu%mcSIf&7x6oH*!_}_hC=K-jN2X+q96$|XJt1}eXxdP{A zcO21aI^SAN+-)f5ld6y`AL+95t%oz!a2Zg9SLSPjD5S%$dJ@yIX1S5fXSD4{zkm7T zVcOmueXVLehdO@qL7BPlXNYn{k`#rtzWV%)eZ%fzyL@zvAJOBDWnDS$zuw+`D*S2+EA|jnEi0~{Es{|3wlm0GnZB6i6 z7Q7PxOg@khJAb8i;GJCbRY=qc#4gbbPllKf-WFn`df~|s6T&6LmPOQ;5XX`)NTpq) z&pJKW)~kW6c%PL%l(n7Nry~?{g>;F+GkHF=skcEga1EvyOF2P)ThKTbZd1;=bM?ZL zfh&Z!1?~a8@MPc$;S#vB3S~X2FG6|%&*@r?N0^m5f^Vlfe^OsztrteeT_}rk z=Ao>OY5d3G`=*k!QRyF*?gZQ*61y&yC`xQaSP&(~a)OapJyp0S7L&;1w z7%x$}KZl{|tYZ*)XF8^GNh&i5M(;vO#6wgYaM=NY{KVO#3O|v2zGNiJi5v zV>5^G6uW%IBl#!rVi4wU9ObSq8;vUBOT?>HOKv!1l@FvoZ%w7oXgi=iR&L^TY_~7X z&0!#A&T0$xdXA+^tr)4a5=9vWJnm*%NrN&<#dVnmWn=+D8SiuTDFs8V;8S)4`jmp9 z4m$UsXNxB4Wa+`jQB=-QKTKDQp=MXJW2j|8ves4pDy$9Iyl2a1bbX#(nsn=?bt(2w zN5{*48ridDB>c&+F2_&VyVyZhFaf%4)^%HNftz2XTZ`oPyX9InZ`Q51_!lV0A?7O8 z@+5jcw|-we>-Txs624g9vA`b;-Z2Zft-)bf3;m&d4fC5zSpL9ejrl8rCq?X7Umx?A zg+=w(6w3LUXJhC4iE6pznWaMA@4wwBVpsfd4|u=7T=Ob5uRdlCjrjwUX34A?UbzZM z0|mpw{`tmK1vks9<+6vOVq^S$b${U~C<9)E9P-_b7Y4;`ty;w0O0S&HjT`yh^s8I< z`%vcUblpF10%EZ1y{Y_o*k9?Id9PHP%I%vdSEuTS{UHn+>`E?Qo|-~J7XmNhPt2mp zp5*r0o|VfN4cA3wke^5pK3GvT*^vZAbB_siF7gL0qln-FzwPtanEPyK5!>(AT=}%G zV2;-&A#Z4}Q7r)rc%y0>Q+P4}bL35`5|z&`L1#I$lrI$? z;6hOrR8&Nr>i)`L6%90A!2Ke#RQJ!Xnt7Z(fI}TBKn}98nWp>m<& zu9;I)GL+=K+#cJtIb>Jm3bt#E7fn<|lyYg%RM6~JpfWC+I6rd%s1dK_Ca}$$TSt3; z9bF+4*R0aH46qGJE5PK-tH$2&cE>`04YfBQr!)y57VRn9tKYnOqd!zJD><|h6RkAQ z6t;q%j2kFBH4Xm_mgiO6ZKI=j?2c5XJ*!+ADOV@aqSn%ztN?g3ht*9mj|X!s!}HB= zzH%${IaHm<$HdbUDgN?6JQ}Y-klk%oM{LuZV0pkHj9T7Q5t$8m#yDD0!L#Z+miZfi z$*Ek}IKv9$*x3^GBmQcfJK-7oa+W=574Zl7T~aI)^jEl32G0Jtr-oMTLQ&_ldw!ea zgIT894SCBzd0=?n-k@csLZseZt00RZl+Lp~=%2fRBS23%ZaZFo7X>%v@3R=G7O6p-b?-9iSA4vpBH&r zGNzCf9z)thc+*cVwZNV)rHB05sZ)3NDg5R71N7LNuCT=Hha;|)L1)2!x0YL1S@`tl zg$ez-)o1myxW>hJ(aNCDx{7_~s}mpnKHJ5n_VvRw7+tywgM#(`2YR%|NqkD8wCFCu z?C-F4Ia_gxwexoRiw>A{=m1KGw&HbYt6qn8k#K0235TZJIy4>EA-2LHHlyaRRKd#B zw=!?@(^RN|@F(Pxg7>(c)F=Esw0eMhYP?(w@y}kpAoehPdmtilAjp*^IxixhICrlO zXR$UeOZ6o% z2`Qd7As{-d^qtqy(Ka^0G*h%)F9YB1W~etspOY~%_$v>h2x^361-k8B6D6| z3~6Wtqg0f2&JrLI!u|8YUP;xtzdU@*9Kv$DlrPo_A&2U^P5 A#Q*>R literal 0 HcmV?d00001 diff --git a/SRV/docs/_build/html/.doctrees/api/endpoints/subnet.doctree b/SRV/docs/_build/html/.doctrees/api/endpoints/subnet.doctree new file mode 100644 index 0000000000000000000000000000000000000000..8b7b3fffe0df5d763fd1d9f1b254146e272a3aa0 GIT binary patch literal 87966 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 literal 0 HcmV?d00001 diff --git a/SRV/docs/_build/html/.doctrees/api/endpoints/ufw.doctree b/SRV/docs/_build/html/.doctrees/api/endpoints/ufw.doctree new file mode 100644 index 0000000000000000000000000000000000000000..04cbc4592a56c571078bc6d827df20b460ecabf2 GIT binary patch literal 42390 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 literal 0 HcmV?d00001 diff --git a/SRV/docs/_build/html/.doctrees/api/models/auth.doctree b/SRV/docs/_build/html/.doctrees/api/models/auth.doctree new file mode 100644 index 0000000000000000000000000000000000000000..25009fef219fdefa379eee9318f208bea6a3605a GIT binary patch literal 55390 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 literal 0 HcmV?d00001 diff --git a/SRV/docs/_build/html/.doctrees/api/models/request.doctree b/SRV/docs/_build/html/.doctrees/api/models/request.doctree new file mode 100644 index 0000000000000000000000000000000000000000..adfd588c609849420bd196c1258bcf1058f7d702 GIT binary patch literal 30002 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 S!D$>0V`cOm0%p0^C;l%Y2%u&F literal 0 HcmV?d00001 diff --git a/SRV/docs/_build/html/.doctrees/api/models/ufw.doctree b/SRV/docs/_build/html/.doctrees/api/models/ufw.doctree new file mode 100644 index 0000000000000000000000000000000000000000..922be77f4c0446e9f61329568e35993670c638ef GIT binary patch literal 87791 zcmdsg378z!b*{D9R*vPomGgG7PQcri&i;x@} zvlxu*iv41@H^9dB_g;b%FEWc+gdqvf#3tL!7dwfGoy5*_{A^+;bTbo=HQF*e4#i|^-9fKn)x}w!kRZ;FO*B_`+Q%% zKfmyn<`7=?)eH5aXS@mqr%(^7YfRqOq_6PNf*ZU8RGszio+hunzmh{$^=AHMQw_my%}{BF@i zqoRL|<;M3_y-E6}5iG3d_R_0nep!B9esTVU{QUg-;Nq?M@|3rA%By4&%n&1Zrcg3) zIlak{5!@f)Up8IH3gUn1dYlp zXJvAwQn{WB$!u`t?h7Qu>^|?{ZRP63Xy$@b>X6OR%qG^HO&47>oY7y^D!Fmb_$Gt* z;0_c0;oz7sG@}(kAA`p`z#92Wh&))bhszshNzURyFn3=wXG#LG@ogw`Gni*kCCjig z>ztdB_2Ev}b*rnc+gj@?^SV3>D-)q$;J{RIlvf`ts#Lw2SKaS53C}m9>sB&$86lQ%R-=PaFP6G2-xn;_9brTg#CoPl^S*e} z3eLMBw9bdLR;~l?*3nUeK=YRf`3vE`x*%MaSaglR~FHkNuRo;0gvB_+`r# zubQiutLEo(H%pe_PB->Qd}D*TsaX^CtLg}HK&_4SLNy3#*%Ra$DT z^hI82e8-S-@F+g~BCJ(x526o)t(%~E8wfW+41t!RIYg+ ziQ4B|*&3`8Mh6OQWPicCZ5)QU87!B#*<5KnZ*ilY2^xU4tmmqGy?QfPmE~rpW>`F! zT6|HMXyzXZ`f-P@+YFZ3R*|WNA8Bm4$}?!VBS)qWmK;2Uhd_c|9wU3(wE9@k&+E6H z2&q}~{lTKiMzP3b0N4y>>*aBxYQ5I<=ff4sKXh*s{slc1-w+jd<=69$3jD4mX)rX) zc)3(Jy(DQc$?F*)e(kO1DtS}3M=s030~yVXLW?m?F}equY|pUy;C8Rnm>RhPmpx1{ zhtT&IigLO?_{R{+%q0<4dpKB9@k$dU1qVQBv)bjM`nbTuZz~qiwR1(|TYo*lf7ajUrTpd7(Oa7 zpj@yJ59$XikX>O#gT;8_9jFuw;|1y*^ucYpY6-UkptTST#%IEf6Q6|A6kyg~LUApj zxIXBec|!Y%e~_g$!MtI^6t}R|KOWyne=IoGU}RWM{Efz!{x4G#I2#v`EE#;=bW%T)av(#dG!uqqK2cqMeU!Inx)B=P-U_EC08|+~=E*{-K+ZC^J=zTr=3!2-I`mBzie<`B zE5V;$szzm_gO^@?^|d#RW~M(ieR%qbnLDN*pSf@P$(g%mf($NqOdp6kk2ZzPc;otIYJ5z=tO?Gk1dxkAo@CPCqk!1b1J-#~&Enb;TR6xcr9pP-gCo zgaA0tWTu}5j|qdr#A)0=4gq-o;Gj^?7`;M^TCH7dv{rf-)-%og_jJVkd+6)+{z3X@ z!;blo@H=q}Ju)X4)GROv&pBj^@mK#-&|lK^KS^t}MBLwzL)UW}V%i};}QEviWiH|2`O@@<=hwyUTY(ZK}&dkW7;Yxa(o$OWPa z{$HUG^Q>V=ct|H(X_Ut6jg}bA+zVyL$DO9^KRIg+=2lt;YyLk{5mj~DcFmpV;$7a^ zE_m>giW+5VvndSmeNT!3Vkd?e_}?J_j9t2@{{kNJ@lyU5`TuK4lRppdtO%>WfIvLt z_t6Vmvi*KM^EaZ=D)JBrd45mRUqml6cnOb$#z+bmVE8qH$-SpR+)8A`vRNlhFt{5|v%JO!Ze7uE1 ztOqfQSR!7^-z)Hi82{DON6SlP`AmLCkf^?zSFdAe);vT6yN0oS9oIX;=}Q-Ng(f_i z+@yAj@Yq&GNX5F)gNub8 z2lj75sQ+MM6}VSn*?|fiN1?`;|BBINf*57jaKiV00T5v=E43Z3CEr%t-R-O*M%xMC zh*b)DX{OUFlj(M!Hr*3b*=ctBV+0_q>~`Q`I%Q{xFV}US1~9GbxYSBj_ZhsCs!n18 zO?3?+)s3o?08*(s{|%_#e}pdQ(&bUQ{1`kq@h< zMVqP~#X}P-sJ~X2cA%jC5`_{fs2@AwI~5eKrM-gsnX`%*1toy52{gqt>*=M*`eZ6f zGu?lQ0EAW4^LUs}MTJC}E2-n>n@UQ`uT)d(@K&lRi4D}$S4GRfy>uR#1ki<=+6>gV znmU8-n3`gDU8|{hK7ka%r{uAFC;B3iY*4Fub zVK0bg$^Bg@gt2B@7ihu_JbJ*#fyaxa>D}y96$CfUuDwtkqihqJV}7rqs5Q?m=xROF z)IF&1i?R|!`H$p3YV{V`HcWE+x^4ae0>jbJfsz3OMIJ0Mo@f-_(*^}sz$6-lFDOdb zQ`nD1KOf3JtzJaWO#7d3;+TJ-MF=r9?SCAFz?VNzz~$t#V2S*xN%7n6YW}>X8caru zYTAn<=A-=&Gn7JI$jCP$_U3 zKNnElPJcT}bZB<_Sr~-X#cl0h00#ScuLyV$8q&NS&7a`+${ZitvAE_;N>l8w9L0d|Qihv%|OO+1);BBg}~+ zwC?rc3`z3;#Qca+>LnrE@P@6~-E*zk-LHsQN1F3f(}~vnMpfa?gyB z=!W&ya{!GEYW=DaniBJhcNmX?8;n`c!RH5K$doUmS*vtoJ4zVe6YkK$EH_^Q8 z$1sEL_()8$`uCa@SyQRfhcPNQ<^`_oh^AtC^&&1}G{JQlZXqZiF{&3$w`Awa*=eqv z+L$eHTDETLhz_FD2s+U8;0=rpwjBsdq?lARN^$DBJE)jcG-!Ttg1W?{qESi zDcg5iF)QVxX4GUuXi`yqF|vK9J-$*MN1A8X5NnAICls`rN3gk_rd!R_z{ym1B>g^B zcDAPz?Y3~oUyl?QY$}uz$EhAtNJfo7VuCymF!4zgNdLSOzB5k6YiU1(|AK$g)V2tJrHLv~q2>|1XF6!Tnhn+dg+`Hn_h8DlM=lm@I42%I?KyoXC zR4?JB^r9s?K=K~KteaT61Ry({ZeW6GLmo^25pdu^s+Z}G1*zCwnn(}V0NnwQcg^erFPndw^BPvgrJ>%(XDop0J_jlV?d2-r*XPt+KJtDt(|OD z8TRW$T80dBI9m`OMXxCAZ&lc{yENF(LKOYP>#AiW`Oao-c{GK45I_H2pcWB7*Y&r=&+kNu&dhFJ#LuZNZfK9`#~uBY z@pHPDwpGeB2zNIg$9n#~i=VFndZy;=`K6 zc^sDUjK$9xA0y-Ez0|Z22jWXa_}tB|rlv*i>6b_%i-VC45+=)d!v{2@enfJ}ylVPi8 zXcf(~(@*2?2>u?ig6HW9Yde=0NJ-x`p6|9{>I-#Py0wX`4qA8}UUYy#y@3VLa_Q@y78jMprt z3$GdO-%kL-0f4*lurp7D`5b&rLr986bP^B)?JWmfmSq6oCA^hRibM##`5wZptMH@% zAUmH;ogu#BHf~je2u}ip2v5=-4^PrvnjtjT2Hl1yPr2dp>)I|RUhf`zt}+NQ#-oaU&qvps2Iw0RMRRHN~oG% z=Y;Q6O}v)&s_7hO6)~zw0H@F-BbVkmz3E&tX>R)~2|!pg{R~|%wPv#AS41XJi>a8H zLrw%6}`mZg!Phq z2m?Bw?zN1*0|YZ=v=hB_K$Y#8USf#1>ZQg+a=r8cg=GhN>3t}aP%nML3E!!gcrESq z(r-Jfh|x;|IO!!uF3od#WAfbg3C!W5=`m?;`|l+HVZBtt!&%Tv#Br{dz7BwvUZPi0 zFTIGDQZGq#pqCniSvUTY1kimiz4VuK$Mh1rJ9>I)s3K+^YSCU3USm(J`a$HG zY>1eD7*WMzRaCI2H-T`oYPLAX#&&4C5q=fhE{5`cMx_YM9W`w2h!ZiV zrPz$3NhII`XB9D)f&;e#(ISZ+sX)|FqYZC#S71y$nDIqE%+xzC&Byw9Yq#SSr`v(rbVG*V>2x_>Za%#+dXTYFWO?SXWp&YvD;|M9%oY$up^pc zp``Ybvli3XngsLTjAA+<%XUn#=*4Q8Z8~vbM!%YSi$SNmoGnN|r%rW?(e-pu|7GZ& z)c!@3Y2aSQgG6W}EMuq?>1%ufpGaRrY6bWjKM~3heq!tTMmx#7JpDlgpPxWnn)aDG zgd}gosWS*yKZl*2?xgs1bg)`(`b8f-M@H0UW+C5Y1NNue zg{`6E(p_lhXt^)5_|*0>lV`hpQIsFrJebF+HYui-sO2mn-UMJ%G#{VDPjJbY8 zdA-n9uBTNiZ2e)P644O5QRT+Qs^0!Wcmpu~4V)umgJ}O8CxuO+4HnZ*u{mrSPiGbc z!#ns^?_0{H;=zpCXStTCdGrI@Q)h;*yW+acIcJ@D_UUY&(X*(kb^Zwc2^+wAYO%bY zS`wS_kdID^x|X4lXrPsI!gqSCyq5MMxw5m0n1Pl6ZcxggVDxCBW&d^mPIt{QBe1pu z3OcPp8e)N!V|Zch=VlDYc9KgbhGOqz3=3Hk7jN4u{77+JZ5VWA#9M@NKz$|U(s-gp z^EceojYj`7tfh%W^D!rUr)ct8+KcAzIje{fO#xhsrm>EObYrx18XsvAe#2dVS^r5+yZKEy@kvkbEB@{W{_>>|ysK+ZU z0@ughNpTauw6ML=O|@uYyOOmlk+8kl3EwGfyq5ODHtDP)M%V=Kw8D14UFXpdwmM@< zBHPOwpHkSChX%wg61RXpkOIelXnA|kO}J=z`xt9lB6<5QCw!;8@mku;+mp^JV&qK# z*YY-?A_|;<(bjf8zB1ynFS@HvEp}8(O0)U`<4t?2{goT_=%@e(9%B4Eq-@Q7he4XZ zcfxlvK4}yFkF$yx#)~GHr)#L0mXHNowAf{s&9U0-&Ko!^7{@-ODO{a2^r<0{&hh0+ z9aNKnv=+TWVO+M3PV!$r(E4TAO~Yu^hft^`^&@&hZ`29jDQ3Ku_C4h)XB9DGCV*@G zqUSf_Jt+N+Cg9EP+6{(wB!rP`98;>C8yUM2SvpRZltQ^KK9buaoD0RbQa06ZP1)Ps zl#Le6x3ji}G+?x722S`+(d4zX7tQ;fRm6y<0Io%I5HWTU80U11v3>SZr_Z_TPAiz* z`1cPp=Cmi<@3>KqwtXCUi1X35?~k4Eot#hFgl{{mh~d0wLZt0mK=UGo{r-`=f`sh7 z8B+t+q&cr9rJ~{ZhqQ;QpSBl;1xuVZK3Wt+4^rXlxb50kx`9V;5Sg^^XWYP@m}5<7 zp~Ozk)kiu7oHZ;8kED~3IGpLMjgG*a%;C(!EIUcjK5_$x4>l?^2Fcc@aO&QzjWV6k zOPi1TBLsDw&zl_Lt07^nu8R9xd$Y>uS(KM&f3wPU6zr0R9p-;ZfqvSoQrF+I*!CrK zkIoV*VbHtip+q)L4ckIMqDV&I#V$zOqD6`Gng5AR|wg|Tf zSS=wVLo^M3TmIMEQBEfh$?c4p-ith~ke_qMc*z#wGXAnfxW~m%#yI${m(#A09O7a) zmBSqUP)`=wBLR_~XOHCQmgfOmJLYYieh}$1bcP;MX?V^JJ6;WoL)pHH(qn7;o%ZBD zB&w1L8#cqEQSC9!bn8>E0eu~xw6oHM#eB212=sVk+DN<~5%Yc4`P#n3Ns`I63UpZ* z68f9D3{=i2ihd}6YMwmdAK!dXR(-^YPl%2&laT4y)^&RvD6{3v}?p1Ra7U9z*;O`lYuF>}N$ z94Q(xv&oy}b_2)=(k^&focym++ZfYzQU42g*qIe(*zkp>;f}(Ma{p3N|1vuykljP3 zGcLqyX`&@cK=iPne58qvJu#Z7A~M$`0NKNJBaQSs_fpg2fEv%gTT6E=|Bl_I$zJ0s zr`x5br-`wATTQqwLKCVuK>yBqGe<<<0!nwH3YSY&s6d5up^7aCWvG5@ZFm-F8J}+9 z)`nYD+2EGmXhYSousUpw3?xy5+Z4hbtHDc9G>IDA<*Xt`4RYYoYA_V~-Q$LzRtL5X zZnV*QNy$@pqaQss6y&iv939}e%L(6U=#nq8&Ww@ZLQn-2HpuqI%4v*69f#?5og?+~+@83~0iOCyW=5iro z6e0&6ZSo>B$JV-QNvIX+bXhCx+j-_#8!gEjmP|`((enR|_!QQXe}jjqwPZx^;d=5s zP}0zoMi-Kr@_c+DHKjxhn(})>mQs%bBmmhBZ7L<&aArGD5-~$yqK%iGE-hXU!K6`5hq7@i6KB;_=@LCUoix7h$a)+ z3cJ%YTQ0KJ{K+5~Qy_Wmb6xG+27=rnT~Sh4@M7vAx!q{UE-B zLP<2$Ke&NMkHk6fWc~bGs)E4ljo%z%wcHn5uYz0N@&#-Kmaw-x*EC;tBq%?UcFH*)tW|z#wv4|YUFh}5IN`_F_ui5z`~(nLzrpT<=F6PU~wyJ`FyHo|xUGyBhAtBfN!{O=Ch z3WM!EfKvdUz-}At96;I&`blh?G4np`qCu$uhpi1XX5N?C1g{5+`*Zb1EmLYtjd@jX zV$+DUs9{#345j<=?x^c+2 z&Tgz+THRY49ppB1`qPR*;EBl~@G&!l4NnfU9lf6fhaaE0V>om2?z3*moOk9{Y`dAc z^y;gxy(xo}^v*i}?2)t1J#XZ!ku$fR{rb!)nVY<^%=KP%zgIo^^vH7^`xp|Ezv7qH zTEEi~$;DZYPg0=`JLoLOXK)Kzv^&eO@Tw6(wsb0Gjo-WD!43)ky*oZ}x*^0f9Z^&y z_Xvv8sgQrnC>pY%A<-A7LK>wwAG9WnI<{4u3TbffDb77hYA@Qwt>fsRP%t=9^b@oz z4|K{?nVD8S#KczR88P|~*=LI`k{~5>^aCx4|IiWLY)R|~-CpKxX(x$qNZ%^>-6>1! zR?a!5a~0$h(@%<&aH^v6Y)LHW;lZZE{Y9m;V@|SVP|D#Y`G(vnU&2p z)uMMu{H(Jf38-ePbF<}U!iDbZ{w}I9+!yEl>N~k_(LN&cyH1Q_`ovdIs3oX$q4Zeh zUTCoPPn__b6ydeBZ^Cz+RXCdfT}-sn8kp)dhgDthLl^Z&0X6MVJBYS3eDM7<05%5S zwq23)9Y4oMa=t@~3tW~9MN=b(*!HfKQ@qzUup1S&tjbJcOR9R_<0f=CQ_Ghd#fi+A zm&svEJ+|Lou6hs~-j(p-6q(z4IkS6@?)iIe*{xO+&{u`MBl_hne1JU!)?Ch?c2%vx z+xaVO-eQ-p<@G|n=po28iLrfY+%v;mOH7QVY?~@YOU=>{WcP?CWXHL9N{7+)8)M zOu{U@ViYD;}3(hKH%mxP@?G1)@<+#laKdmFkYU#q8 zNlKpgxzUfFG8E*oIUJq5`*A0Hr(=+`3BT#AA`OS_wdaq!;iu*>)zUSG<86cKNou9w z-$<=tomUt2FUG^vo{O#MxX1EM05m)nuI;3!@@IG{Jr#)#%+{rZSvQ#(5`a{jx`Bxo zr2HLl;705RbjOSsyX(S;an+XEfNhdHpR_e%Cqx80WZ~F&iNMZr%*HWF@ta$)nCtWO zUMylxTR+fV&775-y^=ef7`2}Z(a5h4-qxF_ho)25u&p-dL9eq&kSIfKm9yPW4Y(Xd zli0YEoK?hVKn^_G#zpN7v(;TmS~b}5-Y{*Zb9uAut{*qR)?(!^qt=Dh;=e)cQme(* zZo^gMwW!HZjk>Ey^>`gVkm^yQ1@-t-(FY<2a!J$+v-O1@LH+Q#O8>pJ^ZC1Kp_{u&E|`52L;U-L?`-izbZT1$Jd6M6jp{R zCns5U?yo64JNEAnp=c7__kgpC7~RK#N9#V;L~XV{>xQ3J?Xg@FOq=Cri< z_fc!YYVKA%Os(cZoq+4OuLGi?<3vx83hqU`l?qNG1O-YUJJ;Tm{KX!x=2)c+zxO-f29y7v`T_En=7I~PsooX$fs6?3#td?&Wa$Y74* zWc(SLNDlozk6xwG_2;jWo!>8GB3tfB6ey*hNFlByD2Y;%I-14lRv|6}TCRYnpovW5 zizG|F2WfoAMhkOP7N5q6TT89o{g)whX;rj;9!SIW@+G)4^m4d^2n8LijmFu`;Ugh{ z9f5%TMD&M9zto;!xM2Dr9EkNeQfIzI2V+e?F>^ON7|Y6+c{Y5;)y&=28CTr_(K_HN zYC}-{ zq>fOWgMHWv6S=yV#2S>AFSx_8Jp5d1n@=mO zqeepRDIMKn|5F5p56ZMZFn2$92lUz#`9v?KkWX-&(SBpH^a&?gF^f$eMIo2glkNvC zRQO_oZ|#K&(XH))WmXCHVXj3a*lVO(OmhV-Ol_LKgTmZ2e;;>-X>JiEX`5AoeF$sA zI7=Y``D=h0JEpRY^FC&rpA=d0PvX2vWY;4Hd*(f`*f@L=M@@=7;^_2Bdhlgjk$w;R z%&hvObhag>bmIY1@{zKQw~xV^!_ptY=SaPuxtHKfKPBhcU)4HwXZ*3CO0@fLdeVmJ zuTmjy`hTRTY7ZR3C57piOn~XhoOz?vYuePG0%37DxzVP6BMK!k^=G+(N1J*MJejFKK~)gE zD%=m9Kfc)Dvea1CGp$jsRG4;d-gPVeD+o-;y!&4wZ9@)53j4m>iC2t$zX^qs+V>k0 zd~4hH=;rpozOQQW%?p*NVYf!8#kQ|O>r&hHJ5iY1_CvTcY`Y~x(yp(Gz}B$oQZiuE z*8*BXoBm78rqlV+u*NeV;Dha_r=P_SGR{xW+yxW|BI$S~8<`|Zh)ek1K#IJW-;%LzE_W8D++vhlp+F9+_M|7=dSVQ}VGoWt6 ztG$MY@4qj4LQGGJw&~BKP!gN|O*impo6dnJv+2*N3WB33F(FHTQrj*iwPAuUwa)vI zYHR0Sfu@uHLjn`>3W5P8sth&j#A6W;Xgm6!vxZxe7)grWrr_tmCaHJu@9bN{ap~v1 zi&InMXFXmM$e)^$Ytvx58ihK;)Gflc9}~@>9{4e*Cstok-i=1H#k+Y5+)3@-Tm<5B z@8(L}8Qx8T9wL37GZMGV@PuS`NWb%ZAbFNa(sT3wE~1tJsAC|^i; zOn2hpQz6F)MWiyAU7%XpceYZk!QbO_;Yw=hP# z8}CA)B<{wqyMaf$8yvV>S3|tncKpApDhS4V?4a`XBHT{v+=QhTr={iyJ=I>Bc=&7N-f5A zKiZet*nSsj~Zm)>O$9TYzmiE+Hw&BoA?_W`;0(gIO6+|Jl>yxM1-BCG@^Yv@H)fMtdfme9oC? zo;iBX`RAXXZdaX!$8sr4Mrg5@$$wDe`K~(iQ63p4?e;Rs^}O*?gL^%x3484*LWg$I zS=$i>MZSulAnl^FhEdQRJQKU<7^OJf<83XukzyAegIjYrx2#=su9sWh48Ey^h}CW! zJB>xf_3AAU*AJ>G!Y2`aB&Yl^v;#tum~?EJg;XlzaOCz*dM6#Z7)4fW%zK~P5ixCX zq>W(C<_&Boj@xIgsZ#7_5u(qzq_JZEmt8}A7;7_mPeSGWI7O%hv2wn)-VOmSPa#PvEN zt}SW9qqS0c!M4mi6wQ(2Ir@`CN1_Wy>!%w}ZZU!8cxLR=h zVPmRdqZ#sma7i^=e+!qUk1ZL9+TZe5R10>g7O=ZCEyypHSfrk3I8Ah?+AgnFDVJ(q zW{59$D3pxp1e}-=u}6a{W6vu9D?_SWlL~JRS3b0PIOCOaV?}RbG&5E%7l$*|MiGOf z%QZta&@6r-)(MeAVA!bxiE9VB=UUA)q@wc=0IH8{uqr^2WSXJivhd%rMp-e zJT{3D>SEDO$37HFG6nHYH}Ghug9EpP)|7P9>Da3(=+WjCh5&KH{$A;n2a64NS$Y}P zGfm_VDm0`0i;v_#YHc(tn|KJ;K{wI=bppfD(32o?F9ZWqzG9;Fg+}O8Z4i1m|Jg`{ z9*9LqFPXS%mb-|Nnz@a9Mhxo9&i2JPPLH6F%Th?L(AtYHuy2j* zi=~!Q2;mt-ysDvI`(y7&Ay+8X5a0YW^TP?`^wQ> zclLc{|1)St#FUw?zr`sYLy69u;(R|lC!N*B=gU^Eg@Z#?-`n&T;h;!%FK5A|u0mg# zz$YTCIEc@r0_9PS&@9*)m*ptZm>I1c9A#F1bkFT-r^~5ap)^7tTvML#ihG#&(Jq$@ zMIl2090%M?vt}*74o*!B-rs0o?4LwXxw;upco?;w5~|fUrbGT_I!<$5dR5`dM z!UH2O?nHb9s%-=;mo_r^{o$8xoWuUJR)uc?fM6ju=7eA3H3MP!*}fRwfl9G3UZ^(@ zu@7#`RZGo7{spMmXH312?ufJgMTGHjF&v;NPk$|;+pE2!45P+9dg5ziEWQ&GMq(Ur zF)@ReT1~e+nQ1xDl@b_SGyNduK^{Xy`p8T`Q>ZxW1o3Q^!2K9YT%wZ(j^I7kWZ@Me z-p{c)zo*&sD!vmZ5m>9JXBnziaaW3FNSE3cFVjcTWO!BlB8aO&;?~Jj`=pXzyR?pQ zOpm8yIpUwNI%DelJn6x!YaelcKkeIYJxcsIBb&07Aqtt5ALJYOi z&IN20aucx4RAqz7p3GcCw2+V8^ob7(D1gA-m_+*>KCW<%8b>Kx?smUI?Pwj`(<~tGFCq@@{}Voh zBfyf?`=CvCfR=?dIxO1jzNXP1U7)6t)@Ix2%R{?5j7p9Hn;eO0FP(zd-W1Af5%zP7HOJ_z3 z5S*D~MdOpqozKEqIOc&iZO)vbzSe^yc)l$fES)cZ3HGj50j&;B-1(m&K(+-pyDt1)-o2}iA)Er{56CeR0KHuErF*i7D^>WKA#i_%;9q&adY_Y6L(js$RK$&s z_@%v%jUce8zidHtyOJlUvOU@`wxw-hyLD3AY5xw!_h+l-bnH-$plA|@@)BniF_wn| zk9H^*P`-oVPwaG8kkFaDc@d1m08L)xs(ZaU4s$WGE1XBWv>E1$rr3T!v_CoKY>Cqk za1m^w7?v3{t2k>ib#X_|8z73+NpfEvq1?+7jGp@r&4!+e+$*fI;Sq`AB%^n zotQZF#=VzM0(6x3BGj97TOPt^(ru9_!l+nC338#!b-R=jHcAkw}zwMx0x+6KT_OBeV(jzU;Sw2Ruv~Fx#pl z$lIM+W0Fd#636KD6h>n?NYW3q@&02W$Wjh%=&UTDu)XQ5v^`<4jg!J#(5Va?Ow~pG z-vOyp+d#K6;+F7Y&@jppS^_NX;U)N3+CwRWu!qkJsZKDAzYK{2&tl=~Bb0rCY5v1} z=RMl@KH;3dS_?lLpmfjt<;1dN40L}_^h3~Kn<$@fpesp+fi4FUH_+8;+e+#5-#|Oc zybYCdCm>6A%n4w3T{r=8_?TMQ7_rOrFlj=x>>932e7bs3wO~rMfZe5O!7QY!(+Mlr z*K_qoEiO-ewe7I*k8STHd4f9aV8tOsq}@(=wOVekH%e>4Uo#o!?**IU$NMoZ!@Ve! z#9(~P4LsUq;J|IUGlkW38SYXQ1UJpP+bBhW8|GB0=AT#iMf(S?RCC=n|8oR}qoGsH z2b8Qym(X~k72%OKD7cc#qfyYwi|ud{D;v%{I?JQl6e=5?xom=IE_1Dc7ixZ54Qf8mcA9*LaC|8T;0I*+`T_LD42 z*E%vH}TN2S1H6<5{w^Dhi-&*P}cM~sK>MmxjOC)u_;)L&%I$leAsmnU6h#9g7;F?yQ zof~$myUw&S*Y?~nhsaGari8))F8*~Qa_(>KMHtZ*bz!3Ee+3%NNXjQY{v+sST|YO-8h8JlDAC!_OfJHpy7=hpw?JoBwOoWj zuc+=_7#K(^4m93{$FUK|9xcN7Vn{441)6#h##029$4DX@EAv?s!go`fzoQMy<5v4vyw-Jjj zF6(&F1?_W(!*A}!@{7aPN{iV5rH#*D354Z9)D|4m_af8E6Uuu#2(YQxZe7L0#;S$( z>nfC~#>$Fk@jnlr@WY6(NP@3^5mbMCu$ZgWv*UShd>^fQp8gK+$6D+y7U*ywLbEB80 z95oV359+myqV89H8@M9qq+j`hU$jtT9?H9vF!e^UP{Sz2xvZ^iRJMs&sA0&)kC|+^ zd%jqxVU+rukVr%*(k|2(64Dl)_7b5abA`tGsQ8gyMq%yGBr%h;cP-6Wp@#Pw7@Ka= z5dbw%j2}3TI=Sthpg32R4YEB7uIQ$PXFv2nFWCX?*C=G8A}!9b#C9gp?Z*!up*ZgBR)@u?!C=I(+AI=KyM!gmlZz}E(w%l*zi=S!hq`NS1#8GkNsGzmOLOsU?n=@sSY4HCiBuac zE83DI5xDAXhSSq`QOsQHrD=ADvog~TSP{o|F1cCRY*Q^d2e|2MNCK+ay4`HKX#sU_ z_b;Rx!+o)Xg=L>}VjR;a9!4RUGh2?L7nVKkgzuyXuciHn@oUa1oK0w1SVnc4I=Bmd z=%W7X05!Eo5@&0;Tk>}RZH>un>mq%VAK)|Tn@Ev?Z!#pB8|jV#k+FgdCItIQ+7}vt2MzEG3fy zfr&H@Bre0w?Qb5pQTm%}>5loE?5+!cGY+9Jtwl@Zk<7F_ga`801mSEi|X=B*2u)bdVQ0`kUM@|dnV?8vLJCB_KUkJ))XQ1@D42(Qgkb4J zW2%x>A_>B>2RIwZtCn&_0=S$#VZu(_*N-YSuQ5^19+)D5ItF=d<(ii@OEM`LuV?oc zY6UK`%d-=O8f`!$_dD4cERR8>@mY4VfP@O1ToD{gcc`R>bv<LW_1uAMzOXl6#2@5$L9tAtzoa&mLr1%{fnHrh-&nIWrn6eig7h4hf$`&-G#4dYS^2Iw*EpGhCF7l&&342%VP4^wtQ9yvc^jjGh^ z`Mz*c(FM%mHH~x7SE#|@HSZ&j=rgDbTWDT0t5aT0?)30XayVK$@;CS|V4;ft2Xxs% z$^X~U<$1b%jV{~gDxsxvU(&g@cT<)XG&*(B|Auct#9H7g~ zba{m?FJTR@|JQW+!V+8_rOVJ#T(;2VZ|L%Uy4<`Bmn>brL6>jQW%L+aE}_fC*x$h4 zMwfr1%g^a@>q=bq)8%Elyh4}CDtbnjH8`!;KY=cE3bRkAH2ZW!q)$gk`g9xwJFLW~ zJ#g9f+HAirwy77}_QI#VO4!;_pB9+16*NAroni@FETM}{DzTX-pCYV2g<;vq#;4(E zBUoy7XC`1vvE3^7IUscW32ANl9hYSD38@?K1mI6>A=7z#dBHuC=f3rPgd literal 0 HcmV?d00001 diff --git a/SRV/docs/_build/html/.doctrees/api/models/wifi.doctree b/SRV/docs/_build/html/.doctrees/api/models/wifi.doctree new file mode 100644 index 0000000000000000000000000000000000000000..54520397c8ef730cbf7e2bb5e0b4923ae4df1e4f GIT binary patch literal 48843 zcmeHw3y@q_d8S6vj5ITPShi)ywnW-sTP-u{!4Hx!k-Zp$gHb$o42uCpZr|y?GqE!X_cu)zduJKbiy$n8Jcf7J#=}^faKb4k2%43Jc7V!|@S7^-76c8ql&Vgvg+x4$ z*PLbfw6)xB#4ig@>1fkkq&)3#OW-WgPaSWEcd<9^UE*!>_JpsU_3F#+?6TW*t7W$~ zyX;hIvyEomhZ0(|OZA$If8KoiTW29?Yu0I0W@UY6slJ6~3)R?rXk&XJ+}Ub)mD-7d zQ!Xpbk5_7C_k^JwFT?*=;Qu^T30i4ZL`Cx9=7v*gcEa<(u3dG1 zG-s+gJXfY3FcZCr`Jm&py(_%~asB4Kz0jgpke1h$%qYS{t5REnwZkj!KxJED%9ZwoC!C>TT5Na%6l_#Y5Y=bRbaiK-Ap*&Kc;x( zubj!XT9xu#t`#(M_vGebzua}=pPAf3^vg`HYXd)H zC)H%bn+_k;dMh4vSB}@4(8|GA1ZbIaxqZTZ`>wx!CKu;wHJlRoMR{`g9iAy#{!DmI znwnv&a9q%n&40IkQ|%qjc$aK!(ixqE_l09eJC4~3$bs)fnRmc#4cixmbSBTSoaTq8 znAhDLujAdk>XMh6kjfM&oIJ5yos;att&OJJa+}B84$1j8lzoc=l44dMkxfM6M`LA< za0RWtwv?+cnysn3`RqM3CUmk*S{*n;Xr@u!8xOactxqc_nHcNoq27ARhtp^S2T)bP zA>2+3g@whtYih@PzxM%B#mQI|-h+TdDe9yUH7)1PRIAmZcBRMCevFfLD@iLw-AW95 zOZgG2l;;X55tX3Nz-?A5Ky^c{fBo+PRD=y;w2hQuDq`$PX{niTm(Up0UaU9VrW4eg zMn3N;)eYF@EG#tLW0f>EsSLYA5R6Uyh2ADM9WI=9Pc*6yVgdD0!kI%7+YZ!g)s?MI^0&R=w%j#Wz3S@yqS2m#4?K&WMWVsy!wVEh3PJ~>(w%Uu-br{odHd6# zaVky2lu|c~Cy? z2*ar^UymqH#qmE4DzZ}I33va3~_??7(B_GDX{tS`e?3(J2e7j4Ly8ZUm zVvDb&Rg0oEJ)`v(j$IcS|2ji`@8Kzbxl3k(O=_ooWqz2a+*{@i;Y6h-29HJnm*x;b zsoMu0Wm!?*XVGa_gW@r#+IEwm7M|B?HyZV3P;4zb&EW1f`s=OENou1XBdVgZDMK1Q z)jLK^#X}W^UNN$bi{G?8)LR6~{?lxAJgXo1r%(uHFJb=DrnVzM3gZ8U=V=&Av~LCRDFE>Ifl8wN3ob)8|8Ia;SI>eo+b|AxaqsbO zm&0vQd9}+$@3GTOZ)5|uUHUC3l*Q>T;)&axP7&|xbo+SXcBfOsW2c*nXAz|OdN?1l z8SwcUp7+qsM_h(R+1kjhB`1=}k7dcY%mHSKy-4X1C%;!&QswPWFDZStSyiF15{}c!%x<`Hc5~_YFBO311yE}0a29q_%Hlx`E_L%aetQza}YYg>&%GRq#|6&z| zvOxXUdE)k#uZZ{c*zfSf?RHnhM}_*0o7ezpSaGm^#t2aVJ!zA^YP*0J-L5u8yf4&W z#uK+2Jgeka^TavHvqAl9c?KD+y1q~^m6JjVJJe@wIPc)AY1gA7-WTdUp12+Avr2x9 zC(cPe0Mx&i;e0~fI?Yd|JQlq zb`(&=`{LKrJaM}ZDB=d}r{@#x2$33I{sGV1u;^g0PxF*R_VB+XZPHh5f60q(SDPZ< z7xur!6So^YtK|R86Xzti8#Ud-pWM72!F+vTUn(bs7v5%LjB1ow+4M9CC2G@F!$ejrRIAhWc5yUOk}xl_-=2>aXL8+grXO z-WRhD@x<+RSHwq!`XfAV!-_8$)W2QYq_5iU=0&%wO%d-4_3!41+YO#o@}J>}bCPF+ z`j7AoGFo+gp>{2uG;3ZAQBx`FUriZSH;&C4NE#^nK7mHS%0nyhmBBEdXQ+>QiU zCI3mDI45}$3C6{Wd);!>TkHrv%V!uYkTo0z3oh)jE1(MwHd97jCHWH9=mEAyJ)q#- zD3k>X9^r}Gp+FJu3k8qz#O*d$#A7JvS|4ghgw%ZQXL;Vk0>UWEg+489(pPQ2&5LeV znw$qXACb|#xm4OkruZWqkRr| z#b%Y0i64WeQ$en!VyV8o>~tQa4GW*fVpO$LNpH`=iW9kG5j(;P$13je5;k&n!kzlD z=+sJHa$|>FDBpAcqPfKV{GwdT-ZJ+~Vl_P0ywe_rN5cs`Veb^T3~Lp)!Qv;(5|6MsX~o~G8}iBwdAG?VRU0n zK77J-=nH$;MYyZjlCdXzLG}PbLuBsug3%sQJBg_U1<2#=@qd~A!FD{9x|<$_$NFtN zM(fySRzJFWX7$O{Q>)Leg}Jr+SI?|{VD*{R(>bJ_S$ogg{c8`bKEL{S?vBcVo75KX z-Qi$`;*RI9DDAA!J%Fc&=B8*LS1y06*huVD594$Gk^OVI)sL+{zWUVa zZ{Y89$bG7}DGE7Zvd^uaTDw07vJZhQEcc$(XV&f$FAuGLUhW~t*JVDi&z@5LK_7UsXNH|ty;zHo+qKfU_=+5@>;Zn*IPh(3u5o`L|6 z@1Ii3#f|!5m71vEe-<@^1T#5E^LP&Y&Paanzt87D;_=nbiloOy&BSlyipZL9MFM=R zQnax61c;o03aM5?Bd1rNAYQA_f=Isny5;@G`WBH8 zjqjX&HU8|Ml5_SJ8jlLWP77C|c6%E1`nBCWwDLU4oe~y*cI{zOFeHKCWX_}&*B;nE zB`1$^d72U-|M_Lo;OY~GlYuWvlWrrQMM7k=F8Iik*YBUAQK_X;9|oPzlTg~+kg+$h zsVNl=an=N)mD%LNU0v->4}vYf#K82Wfq?0^Dfb@#Y5HgHp-n*7FX9n>feqnQj0px3 zNXSS4@7rzwIJ^V}FG0cm1aL1wfyUs$pg=Y5mm1ScarLFR+TEudurbB)@P7;x57lTx z#ly{rhyPu~!{kUX-M#+`@ra@*m6B1EAK5&gC`GaN^BH(O(^bmo?HBZ5XpkeCyqm)V zVl!?ok7)*d23GpqmRxjx70ySxl*w378+*-ToT4$C9xi$FNlEkGn{`6CJ9VszCMXW@ z`Dm1_&j#Y1TjeNGb4*$=?|styFNuM>p2Wh@D*kj-HgwPOaX7ki@}!x!N3|1k=`>f7 z@4ub-<&n77v8a98`ei7DeqP@R_gC@6?UR<0cw+V@I=mqDE#i8fzBylFoK7+N)@7O+ zOU#ncI9<$tTygF@D+bARYe#wF6vJpQO@%DWw7*J{7E>TUm-~n$Ir=$ zy+{3{^h=NC;8%&-ouD3eoi`M!w()_EU!z<_?B;V#d=L4E)DPS?PHC(?}dCp7umN8Budkpe3S4kf&qnrO2masumO>F;oU_FZjd7 zR82qwX;N!>`SGb&O!Wh>_pgFzew&`Irl(`k({VhV^iPPtE8_1v#owP2fA0~0@1?&~ zv2~4oSH~MPvyU>a;KE-_FnJ*BKtqj-(-F|5nhJu(gu9-IHIw2{cnC@t{RZo&A%5iX zV^P`AKii?utZP>^#3YtF*H6t*8;fV`hbr9}i9SFyd%F33j0gVPsV?bg_&Owx6b(59lmYRpU||V}Cidx=_%&qJF;R<)nD}~ozKRkkK>+N0qGY^^;zZk~%mS)$=(JaId2O5%3h zOhv=1dHNY)b6@)GFTsV|FTh2^th%|0mz~p%X;u5!@GU&?(X6__6XztiTQwaG8$5%I zW?koKDD9dp8pd$o@1nY-qv1aRE=G!m4Eo7n_(8C>1VaM~bu4@YIdv@5!Xg&_i$b)a z$A_B02(j=nh$ds<$LUGLLha_Y7y%bAfoKvJ_t2BTMe+3F;o`Ld=CA{zwpdxpK6A!>$S;^A zdtzkx>Z}*Kr09uznwYQqd(zOF6el&tkhi|5`s-=8WMT}qKLo1{3$|BBWy9*(2HRwf z(~x`qjdUT{Tb<_N8->+hUnvFp`l7y*a5qQQ7yyFbfI?Xy_zs@99fBosI|OglRW;Ob*$uPlT?T}-KjS8&r6|j#HI4w=ZeqgZq0MBMvJ(LzTw$t~jkroeOM#b~(O&#Skq<=ldsb8=_r9Uc5WJQKpW>*0A?KstW5)6>TRH zH)j@UCmQZm!_}MdcA{{bDY5o+6H#JM`hJRXa=nN-9S%GA|4O+p_DLMHr`_Sp`luu3 zldeJRcIj&?iH}9H0m(EXjBcoFy%<(yUcElW1Yl2zQ z1^Mr^KNOV>%V)d0C~KCET|>-C-x3KuK3|n|pcLIo+6@2m74#Lp*VN=OL@)zrYjcB)3~NJtzHZJcEp8UFSJzshuQln<2?u zuM{JJ|3<2dXs2;e|0*Po)VZ-Eo$S4qILbc;vE*24!9y|d^;E5dwhc(L!m73 z?mV8j9T_EYJ2GxcuFt)cr=JlRyQZTBmthC?S#|SjUUp75{o~z@JaK!x%PRR#@WeUE z?N&|4yCTmZqgmHE-bw8w@w->Ni?P7pKy^vSyKe$3Mv8a65Kl(DcY~)T;%U6oG4DQP z(=ktriJ13y3dpPj_{2rZX7!EnohG1BG^w*pW4jMRG#T?Ap(hdZ#M6rx^Je7k{j3p> z9oG@55#n7}Z%5KgMLX^WY5!AD>#*axUyaJ1{c)YFSy!AJeC6$Pkz+oTEOG7+ zc;a?Ml*H|bm>So8ji;Xx5WC~tC@XKX>gHR#?3`};$GLyziQD5`R>>!KaBakXHM!4O zH67pI6dS%D;Gn?P@l>tZDEPm?9maqclBjuhwYNGGG*4?}yFC?{c0$G9Is zCLQCnh=_5YP<}IXjMD^0h;eU(Xfnp#Oiv=liKiDY#ufBAL|LO8JFe3g5ez+l+11O@ z6jPB--*KSFbt+ptu3LbDhmCf3MrF_bxK7t^9pYU(5_^DncQ*=UiFXh1#O;_aiD!y; zkMQ&}0^|C|yR5qT6<&5uH~r(?6FhNyyvr*27kT2G1%?^TgB0F|W4)=afort5s~^(1`$N z+`bE^MBod3;m2`e@!@x+CAdLkzK!x8*{?p!#~Vp*I()F~wn{}zau$!eE63~2@?7rV zD*_x{JeS)iYPs+F>t}Ltu2#b-xkjEGe%GtPiu%NkP?=VB64YDVN0@+kz(cY5cREFuV_dn z#%jJb64cf&$$S6F`*7mq7hTDd&~{u&f1b!lLE@F>JCc5>MM)>e!W`)%40ryKU6}tG zY31N3twJ@5Cp9(iDdD!!(d_E|`PA=NJ9ee9GNsK{&m7c+({yCAQxgkuI^G|Vf1iQI zo1lMwI($UoOp>O`vNy6j|MeEJo>3U)h5;&vT%oh=nR+&6e$ z8P(M_lDUKlsq3dc+1%1AQvWYsB~E>*SjIKCR8~V#-6+E6_2+RA*Gd0Y{MFYZ*dvL5 zd-emq634~!_aFF0C@gu7Jvpts^}vus#>2QS{tu$uaHEL{aVnZ%EJqGko`c>t$?<-D z+8Le+wsFNl6ib21mm<067$Kiry}4fZ8O%a9v*VR$V*}i0;6GrP&wmY3#XJ$b*Lsb? zkEXaLyL);6XF)9MB?dnXTInch#=WHR6K4v&0YwM7#K8Y0$mI)jK1NSuQ~%>6YnD3; zUWl$CyofBCUeammgWO-OBE5smfPNfG&CsJ9pC+y>xN7y6aFqE|xR?N^+2hz(99u6= zCRazP%QN8h!R?8A3x+5eBln$396caPI*7eGFG$}zsj6pT`=}Qv?ribDU?hd_>^dQR zD$!-i<*i);ItKnR92b8Y=lq{peMX-B{y;xwNlG3u`z;yjG+!}uS(ydLt`6w#9aTM8KPJYX%81)7}e-*>I=m#*5msI(xYml zN{@78@BKoLd{a;d{TijD_T$KATUD+O`Za8}oPn1*=+`JUqiTpzc_G;@dM;fEx6t{P z6}Q!)lVooWuT6PDx~~^t`3eb&$!V7e_cYLO$Nq{<9KGqLzKf)WL8_sp zQe)?MhII~pt@r1mvS(*&VDCE1Nq~6z=*sMKPimCicVPF2QRQrN7CqL;{T>QsnHu_^ zJaPNLP7+V_l%lhPH>XBy-{ct>J?lizq~B?WnMZ8YSusXzqRV28+LWFQBiKi6|H@ax zKJ4K1#EjaecO|=|y#bF?+4_yz23DbcY0Z?Iox>-DjKGz{lXOR>CYu|$gGD#T58ng3FU7?FA>lLG_J>b$h`FX zO%P2k5jaFo;&X|38s?jW3^b!2(0q-EhCMgi$2gscjr)4X(A3uW#9{#*%yG2Uwd_{= zm~Dz&zn%7%B&Up1rBm@Vxero>(_~^COb9M&c=gpa09R5m<%c4zZb6;21!k@nf2 zh-xuF*bGo8OW1rrPuw0hC2@P$j5oi>Htg_BGKS8sI1sbq7Mb?}eWj?rar2XW9qgdb zDayWA^>aL%VO3=^TUTY-BjoS%wcwPMgm-g(z>JV_%l2EuE8XA**lquHURHYx_zM(5 z^D?~VIz1X4Gk4p6hbL}Vgd*PeTgLZz;;iK9-S))OgxL|y5Eu0igX*Xq6*jcUe#)M6 zZT*yFMA6-py~wM(DcW_=O}SIm-`Y({PT2ncvEBYM=)p9&|2^zJ*CYKwcGu6O2qgow z*??)ZqITC4ceI~DcGsr`j@`^Q52EH=j*wXa4zTd0N=|NunIu%Z>$O8*VqX#&Zg>4H z;3~VG^YkRTp5keQu4gZS(z;eP(H|q&4s`DYJBOnh{A5%E@ia^g;srag`SRXDeI?C- z<5-M?t6S9FygIock{#~~M4S8(x;@yN#9h*xr2l%agoQV9W(_A=^>!2IM|iIc&&O5Y zwN_Ppp)WR?^`#~j+;r~6#n|IPJ-{8#hj1OG&f}Kx+9rNu;f_+fiBl+oqM|h)Zqq4r zzj$Xp+^X(#X?4O~=w`QrO0|W9a>WhTZU2&V=VKf(-5NH-3CD+}^?s;I;O0@dTX)(s?l4fz1q+hPVeE1zR9^iA-^; z?lPqmk5>Y(C_WpbGT^5Q&`DAvVO!H})SE%E>K=2U&W+)e=TxKD@o<-Wq5>@jPS9@Y zOyl8r*I2VK`$M#?12QF5tDBwwWlofUB$NWDyV=Y*5(VDZ3@9KbTC zPPixeW6^Ck>&>F))XG&ChoeuBsHFh+r%TNnRK^9BPPjMqONxa5b7%wM4oNqbL6_c> z{2^61JcsH4-)nhLQ5ALSgxj%NWU1{exzIRWCBM^bYpk9oV!j0G#6>cxLmm9^%C81c6fW=~N6Bhv!!JQ&SI6GNC zPHME=cDY_Wu}lti4%W2PTW--TDZHc<6pvL}73s1&i{(noS*TJ)=*$=t+@hNbn%y>3 zriIIn@1R7g~m$Y)oX=%a|tbK zH{}!pAZHO5Qo}p}%&`^e2QGirwb18|=3*&Do>F4+S7`ERybihSQMXyBxWS^}0YI4b zg5@fhO$N>aT2VRhI)}D~d*PGI#i(&chusv#ay;BJbxv!g7C0x0US-Lv;(z$>=4zc> ze_LzWK|{N{jaJ=)qRtcV^0ip4mmHaGFal&CY3;38o{^YGk6YRy>&DOx`h z!^xJHL$DCPBbXh;DMb@fmei4y~l{zY1QMnz?nIrzkqI z<0IFjqO*0#dyBXsPFy4B)7>^cU4Y}$x&A&K{_oS7#y%ZqV8Y$x<(^Q5BfnD)ihNhM) z+3K?%>$9x~;TW4e%dMbmTYmif`{)0A@$K^;pa1mY(To3vs0qbupVDP?`K$fMp$ej3 z5V*8~?fYTU#>h5(zYcBcuL&+`n7SG9+r(PUXU^hE@d}$fGYHpjrGgj(A-;Q(gBk7r@^QhzemL-e071)@)#&*~(w#g3tdlRN|I?1V~BBOGW6D}u(R{usS%w(Y? z#ebjN|9pZ#&4d(uVyJ$|Q)*7N!5xn7U3D|RX9~vU@|0v*o%|k`8C|ZWtI8yM9d}^2 zk*Gi7_b2?`!0$NT2Px6Kyv4DRBr;@F$)WOc16 zJ3{jC7=u*hg>o|cbtG>iBPz9;u0QH;M9E(ByX!gVhCTH+*Ec6b7fBsw_)nPnSG&8; zvd;ElXFF9=;te_)uXi-&T+?$RMAp&yLq~^qBj~!o+ny_Z);WIEIc})6TrNyUok7-1>(gPbfg zT38nDZJ-uNQ5G&LXCc%qJoSCXGvFCtquO9A7)Y1TfcGriyW+RYvNlSJ3+QfxA67Vw zp!$#ahc0zgHvZ&{y?VQ=}DttQe45_6QGxEGq5icNY}St6=L)H-Ee zT=ci$$EnkitCE(Pb{k2>P=3%rB>01tfah9COHp*b|2Yv3@7OYMUpZSi8>VbL^}_{~ zR1>Q-s$-ZDaGWK1fyIti$|6+2U>W?zB>0^wgam%ips-ypofcS)@D532ySNML@5M*F z2>$vUW~8!oZS#pAMVA~w&QJb+VmQ7JyPFBsT4_{?%me@l7~pQH1#?!JU$fFHtptB` zY3Gs&_Jbc+MppqeM-RIjmrnZqcmV*n`w7pbG%YU%_vw~NadkY`zR zFgmK$l7`s)EAb0X&?3shq%vB|@ZLM1A-q$y1iu}`VgqdC0w@$b=Qez_{}(?lXpx{q zz-1B*8BTViWJVBstP6jWSzDMplL^G;w79TL$y22lD5o1yvkU;5iKRK7$Kp8#ke~ed zoxdTUV?9qVnU9i{`8OK#s9X~9dPemrr*>ZY02@r0%>|qdEt#Q&vdGp0e*{40Nn4ri z1rk$4ggo{8=0aMsOc-AQGgrVx92QIFuQ>p10unCgz(7q3*CR_9H@l?9^N3xcNL7h(Qai(>LeWyt zi0uU;V3b<;xRFBZETI^wM1sjEl5QFGK|N;zt72Xe+Ec$t<-<7f`9Vb`s9hitQxv*9 zaA%2LU=)kf-T{IFvMh#O&^bC~q;h|~BdumYh+%EsF8YyAF+M`R6@}F|`%c0nI!Za<8FPi}SCYsdjb4@0 zbxEa%OaqAdOo4{Q0e_)`DBw_zTK`zTlZS%JdG9|8slt-Q8O*Aefu;u2&#^?(FWFCxCS Pvq;7@Z^d!L%}##-!x>-L literal 0 HcmV?d00001 diff --git a/SRV/docs/_build/html/.doctrees/api/services/arp_service.doctree b/SRV/docs/_build/html/.doctrees/api/services/arp_service.doctree new file mode 100644 index 0000000000000000000000000000000000000000..18da1f53240e62fb882315ab4606f1b2b314f302 GIT binary patch literal 23516 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 literal 0 HcmV?d00001 diff --git a/SRV/docs/_build/html/.doctrees/api/services/connect_service.doctree b/SRV/docs/_build/html/.doctrees/api/services/connect_service.doctree new file mode 100644 index 0000000000000000000000000000000000000000..683552cb19d36b21d147116bab8dc31c9f8641f7 GIT binary patch literal 34848 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 literal 0 HcmV?d00001 diff --git a/SRV/docs/_build/html/.doctrees/api/services/ip_service.doctree b/SRV/docs/_build/html/.doctrees/api/services/ip_service.doctree new file mode 100644 index 0000000000000000000000000000000000000000..1f02980903692c66d7cf428faff4b76c39abb868 GIT binary patch literal 32031 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 H*Tnw<9EdnK literal 0 HcmV?d00001 diff --git a/SRV/docs/_build/html/.doctrees/api/services/network_service.doctree b/SRV/docs/_build/html/.doctrees/api/services/network_service.doctree new file mode 100644 index 0000000000000000000000000000000000000000..f1d5b3cdaa0bad9b775c4acfb2923135da649be3 GIT binary patch literal 122834 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 Bp@jed literal 0 HcmV?d00001 diff --git a/SRV/docs/_build/html/.doctrees/api/services/settings_service.doctree b/SRV/docs/_build/html/.doctrees/api/services/settings_service.doctree new file mode 100644 index 0000000000000000000000000000000000000000..231678fdf1650188fe0676cd68639cdfeeb22f49 GIT binary patch literal 19958 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 literal 0 HcmV?d00001 diff --git a/SRV/docs/_build/html/.doctrees/api/services/subnet_service.doctree b/SRV/docs/_build/html/.doctrees/api/services/subnet_service.doctree new file mode 100644 index 0000000000000000000000000000000000000000..5dcb82b6b559f135b3f008179d48f17f66ba03d3 GIT binary patch literal 121732 zcmeHw3%p!ac`t!HPhJFw@(41G4CoSEZ1LZZM8 zga9EMqcVaoK*UFFtA`y>tL?ScdjH>g?6ub3Yp>aR z<~*WSf1K=@+3Q>1`o3>{Ypw6Kw$FOsr;nI%1pUunG+ZmRn&nDkuv!}~HroDN`eL+L zZMN@euehasQ#6fwy$rOj_~I+iiKvmRt?@S%__|< z&AGmv#gDU^v(wlg3=7!={>N4{C&NfG4_E;WWHru76 z{du_n$I?;$Y=PQ3e`cjzEw|{W*n7zR|#!c%D_w zkMgIv!>uv(VJKhNTrX~-cUu0OW`2ZzYL^z3PAttYom4uiw9LPBb*VO5Ts>N>7c0ZX z>gv&axw?9+Uh_bT#_Ew82H04={>rOY1JTCn{8)K)gAcc{y1|B@6Q2j`4GihzOO9*} z`HLH4rE+!aV19U5@M2cEI$YeUQI8Y0^b!oIlmXqAA2JvpTy(?*Ee#>=UmSlZ1!htZs&C|BuV)Lw0Rrk zLnC62VW;;wBP096lj`g3uD))F_EqD36%DL(g#3A1M=NW1_x{|mdXa>u*d{!$McWNx zR=+dptd_DDp3Gz0*3g8<6s(S9YMbK3*Dj+cUowNyYXO%CC;?JCQVEx4`Nzv{LlbyV z5;6V-Mcdu+w9WeSA?m9@9@ewJkPu-~!9Evtik5CK-9hBrrN~#h6MBpfVICVo82@7} zQ%0h0{Y_zgzYFWRgf&NbE_ED_?T4(`9>cK3@&bA{i}gwwDp@1@io~;;4@OFS08j^Z zq6tjb39L1#1V(q#I=L_f7wD}e>C7(`&7O*)km2#mF=g=Q*APEOAv_lkxO8Jg^^iKCfjk+2T@ax&$h=^zSRKYh z&27bEuklE%u}gikl^W)c9oXoL@jSpCuF=+@?H@U|4XZQ0Xl@(BvM1Clc2@W%Umqzp z+y061%SGFtj~5$b#X@;gxzOI_&x5Ani+0<8ag18PMh$Oqt-g-t>I**uTJIUe+&RW8v&@1fL`=Z7;ROW1OyN6fNa#*Yy0GTs`=6>6iS`SzWJ z^QX~;P|s4o_{WNU5|+WiE#=}31#Dc~{$lx<%U27fT0P3q*jZEtydjTW_L|HZDS~S@@&RoWYHq1NN4%mGi1&KJ zj_t6;?(#>$6h0;f5*+2(%o%}=Gt&rs&SDO& z;&aI01o8Az0&}AGyZFa72?8ncUWqr^lowjTdnR^w8q&l*bbKMAUVwK>%e*T6gY5)bokx#M9=+G$ zF_4Izoey@N>g*lAW&D;*=kd-{<9jmWw{#xq92(!=*_-Jc=sZ1s8~bn&FWrvM2Qr;Q zokud`JJ94-ymYAZ1U+>g#qT>;X7KNmfby>KJMeiAecac18h{Djq0Xa#m>J)RPt@dT z{PslW;P~!f?{%19qlCOA$W~fr+q3I?IYsuvLP`|BF}HC%+ElKV=|shs47D*+Mh5Ix zjF)oI2cw!cxV8aWbl%T6cL?l#8p7}-iNoWaz4-9-`0nwYYX(+jF3T4#t<^TS#@3J| z9qK$IxV;y$@D!7V1D$=D&NC2*eUL2x!E1XnmtA;KCVTl+YgeFE2JrTF_LEGpZuWH! z@&ZzogRP$?IVF5M5h<8TfZi=5j%iVtT+GN`G&QZug_$wL(N7Z zUtO_=W(Exl(oI9(2T1IF>Y06k2SzWNWnv_t2x!TG;!6SZ`R(tazQ*6vd3ta_sCpQ8 zpr3Vy_?d3pko(K-FDi+DPb!JuLvJthcF;d3Uvd-_&2Btm(L2H)Q1YZnk=Pd=;m;q! z>RN2Z%i}!S+=PYDuTQ;rF)A8b)B7}ymoJRdfso?XX9*E)tv458yMB_~nh9?GG2@oI z)u*VBfXCG86Ref@NBAdLNXDjm3R>9mCBl@MX<&O{_-Naruh5SgsYov}yK0U$3OKIA zjH>jazc{2#4C>ag@{|Lc%6ZPx#_K6-Lh1s&y|NY6-RvMOJDkR!Ri0M5G2!DYg z>>isbFx1K>Fr_I~pvL_5N2aK6DKt~opVACJNhOYA!zdU%8!USvHKVB&XLP(!`Lns& zZ~HS=u_N8b2u(w|Jd~j02P3WcvJ>AiZO?WYt!S~jo%QA2g@2?XyY?)6=sbv}{3*7@ zpiK@o!cUIhJ$@&?V7pkpEg9cK+nvrwuzK$w-+^6*PYXRZ#k<)~B)0{Yks7uksx3Ai z@51l5V5O%`4t7S^ARHRsg_iHiY}`aO{vu&Yh{Y!$E>m-r}m_12|A?ppd-wc_@jqE-b}2r*rRowZ(||J zGUwa8fE!X!=Lk)#Nz~R52)AsAnMVDxnRaIS)j;dpjs9YZzF0TpAQl?Si3aYjzJ@Ym zwFb(K*^&S=xq?aD7E9d34;si1_l4aV18JpS>pDD|G+JW-^S{E_?UoEvYfvk$HRAj7 z<^!y^fM8>ztOLS94!Rt@!F1-)nSVMr$(d%eb}D-SReiFW5cOj!v_C^%PW5 zTI*j#OCZ^i$gZDVk;zIQM%ZI)@x*4}{4CXm5n6F>idKVqk7k@jFd%7xEUl9}RcX3g zFTe0#ZNbQZoh`#E2haM7%&H6M-^`7fbqKQ&8t%gZpamvE+|prwsWxE$0Kw1?)}~-) zOMh7UlwtLkjxYF;y}u3G`gcxa+<-wOiXKMVS`dyc&YN4XeL`ewvO3D>a%-pzaLo#+N`bs$UN%7=Uu zgpO0@M{+d6BDfT8JN%|dwbI?8<{;I*L8V_RS5WC!hBJr3cldy#z91+k^7ndKpb~LT zxp)+Ah1m3gjx63;Lf2Au|n>60fNSM&7=yPOe~jhPBLh@?JPJY+9`s&CEuRK`ymRR3 z%Xspw!C$_)Xy4KH&L!x}yA3V7g2nYAoFO1OSn=1SZMYM84ZxW6&zc9jb=>v?TNFIO zaB|%m@%u{m8_ZTPvB9Lo+=wt$kpph+6|3<1@o{nz!aW#xh$kAXnkHXQJWYtv{49T% zZCb#huZdB>qGkB`&*?k}T9Tu42WQ4Dbbw*Jf%~8Qr|{i_1u$Wfw^i831G4wpT#@(N zJ&@OXjv8^lqBgLd7TMEHxc=D})<+s^2FhdDyJNGv#*`*&?1J>Ong+~S43Jz9#3wbT zpQIX-Fg=Pfkz3>iw6~ic4c@IvH4uqy%2sMrN6zH4T%I`MHz;Z%NKTOt;8$!^?pETU zC}_d_4XjoCsz7}S1HMMVf8rE0xL>-G?|{|}Ts}r25BbV*sp8vQRctSpS7|hub z2+~iAhCX6q?1PN3xCU6xVXlX#oqP88;Y-9EkYixn=aVCAO3Yl@xnzU-LQy!Aio&Mc z(D8T~+9!~k+*FJocCktCM^WyKRFo42S0OLVbjm1~MmDJxr*|6JBrcI`rZ8e?Qmu5I zyU*7^5uzixEXxUNHp=o4yxH6`5e|ivL3D2eVf;DR=CROP?>hR(;=uLK)!HhaOM`H_ zYE3>al3%Z)yqRqXuv;L0l%JR24H5m=2wqxTt;z4?djyI4YZRMJgs`@EVbYf_W3~lh z*%4t_y(P@RwsWC!Z}e>0IVY`UhdToMu) zYJT19oL_ER&O3s({_WFk%aJ4xTXN<~QgQXHOgZKcx-I7@G?B`jJu7OxFBowOYtDPn z2-YR-#A(d~@E&#px1Tr#@UCa&N8G^eCr$ypCu`0Z-5n-4gie?>=NZO;&>CR3=I{}l z`ZR2zAst25oV#scwQe~DYW#-H*P#*tHg3uB*{Q~t5DIEOMDe z`Izz5KAQ4){R07;rnFDR#xr|hqjv^1;x+`0gk5b2=WrVW7Jq3(Foo7C-91?m-kfSw z;xs5mMOK81&_2Ow5b5=)h9dVosoN=jU7KoT;Gc#q7m0)JKuxuH!A1udv`@}?oO#v1)E#ZQ|o--miRZr2sJcCB!E3(Q<|=x-~M4c zy`SHHe;0}?LA@gt4NXBM1Ho;KhG_(qT5-y!5me$A32F+nf+pfh{ki)TW(BzyNN-l~ za@av4H>z=ZJ~m^Wf;j zmF(1)f40!R_W9^{KcMK&Hxuv&iW{tDR4$uurA*Yl-KVW@ebyxJRAUEbbA^ zt%y;C4GmX;k%z{2B8&im8smF-7y%^+g|nY1&7b8tBE?Rr>mDXcm zmnejLQVmQT0L8$_ey|7a6W9;V?s+&k()HF@G4Erk#wZSGVvJ--c!-TLeg)Pn30f;o znfNtcvm|Kr^b_HgYG-u64G^c~5 zEWP;F;3ze*WfpS*Tz5r9@8=K!hh=|rB<39@6}_LK5zbj%k3w_iIDy-bLL7MfERY8S z)J*h#5@7GQrq*R1h#2>f-xbh1zWkvG9Eg8Gsq1AGu`uk3<~D?3H8M@L1O4>nB3)M6vbmQ-lM^6tHeHtP62FK8nPq*vRHCOsef=ZXQF_e(Q;-V*?%y_L7K|CqZd%v?we?~1*>C(HG znw;n*6_HOmdnh>vb%Z~=TxEr@wURJPA`7P+ndKKX()~_6s8H#qJnAmww&W|VqB&|* zY27idRMkUfiNy&b~+#WVu9JqHM&wanqQn|d9phv8YR$&#f(f%{` z=Y85}XY*t&!hqXo-wxI>gl^}_SnL@ZoodGh+e~QdABnR;5*ur8g<`jags2om7y5C9 zV!g|>+#7E@0pQ8uvQnzjU$=&!n~EaqT}#^=R?)e8b=aE$E8>^YHK42^9IX!Pqp3;> z4r)fnR)_UG;N>wUZKfy2xC-Gahe*7lVzASAVuN@P^AeVUeSkQDV-#97=+ROJc9W$H ztg!S37IzqPVZNs@F|Cv*)rJZRfTAQ648HN@VFP3DRCTAM@#A9P(%NH6W7tY1A^@kC z!y2!m2Is74arQbNgH3%q7=R2uGAvW6Q%da9JuRIBYX;u3{_=GwFG^ua%p3D0{@p<= zrC_E5#L9tpqW;(NVRoZPWBHoQb^N@&GBY?hc>Tal)fbu$1pZhLcQ-2GF*EvLll+ZIKrkholE}vyG0b`VpV5kW zRBQPF3nN6BAgeg|VRF4e%|4#lm@8C@h-j)+hI4{{D0?hY7ln%sdf_%G1SkWjgr{ll zTf(xD{zzGm__G)Bh^XJql=`DAK9T%%d#ENLs)7T|KD_s8Qu?fX_sY!p9SoUAQ1*~3 z8~n5n8igtbQqe(j@B%7RH29Z-ltn->^ZcR>Mn@jvm?DCiZX}U{(EUXvCrjq>buig- zU}_Tz-CKzER&8P_8fX@3`&MUK*f&+oGiT{He?te$M2PH7GB86m)KRVvEE5jL>VEcMX zu;q~gxlQFtg8FIFZyHoidsQOmC@a~eb!0kAgM*G#@GgJpd3=W}5D5d01lHb6zSK&2 zXz&XBD>Q*}Pc04+K+fyP_i8b^5yBDg!*s{lZVlS{JEz5i7$#S=dRioqR(Ntex=pI-3Ef@L!ApxCeMG~30cJfR?;O)sy#T=iQs}PP@bQ>6Y ze->SC5o8s(mJCRv?g@DELoo<`MCHq<+f$t5(n4;fG(xspLX?u2gRkkl7e_T*ltYG{ z@m<*Pv6RqfaPniPu_pry(fE!a#P&%vc^bzws<=gWGvQ*Vko71q-%T2PHFJIk1dU%4 zW9)T@(FzETy+jNF95XWM@}2v@5q>j}h@!m(XXS%9E#rKRWAv%3Pvr&pPtD;$WbA@H zFxXMJEl$#mJya%*wPPXC0ZX-FY2|J`J-_|^tDoQg0p1%v zv(?Df)$Dm*?}4^fJ7$+i+c%~fny?y(p;4smg=lZL8W;l7Td=G!<@MHBvG3|sV-)8r zF-9`ht!HBlE$!MGteteUR-7{Nt30(5CmoHR7jSwSIlHe2a&~DgM$+zsHmzrkvsR_L z1TEDUiayO$^f6_C(l^IA&fU^&PwtLA4)k5#QdsXyg|*35I#b@tuuj8OYQ@KzhO0y& z;%bWXt0oUhb-4Q!G1PKNm1jh=QTG_?&j3*#L;YELVlmY0sh2=xeq!x67WuW6T0SrZ zcTaGQoW0LXcJ|hL>EqzN-rNW2P@|`A6b3_0u)&MVg(lVWTba2T*SHpOTMjn@i0i=R zJ?-j1zCZ^nPvhp7YM%V5kUc39ro_9C;$I5d`nOKcQQRCU51dues*$tygq_2`7mPf` zIs7>^N)kr;f*ZK~94>%&J%|6o4cvYX7r=WuhcBF;bYDABVWh5u;5=XxiL0=m!}*|1 z6{&B-^>g?)8EehC6Molr4p%eC`vx(_at_bloK6n}AuZ~RP8virD8IUIjzp7U=W9s1 zt{rD_CUHiRvlw%9L)CRsea*N|8^9&*0oW&q03IbzsGitc-N3bt-BieHlq4$ylC0E7 zGU~?KNs_KQ-#rW$54vg@ zX_>Msj47~8ofyA!i8o6rY$Il=XQ1vT%q;aZC@%`b<0)d6GWR~xCbcYnM4CZLs%31` zKQETfBnGM9V}^*wSj5riggMF?y9X5ME zu;^<7|F8wB*7+oYF6cgNW*1cPdx!wR1=|@4fm)>pk->3HH+8csE`ua~&OjI#$tWd4 zMeJ44q>i5QeibXf-2*GV=cth=Kd(r6xLKusl4@weA|Zyxs?ekT;ck^WazWB7KZC%dpBfdQjydY8#2=@L;D0`bH$_#7@LiW2duQk z8L4O^%w<9Z$nZ7DXp=_Vs1>JZ8gU~&khrBVmuZ5kmIrsA!dxa7d}$jo8+A9Ay$wX+ z=CbSPiJ8mT(=?jP8u=|n896z5<}$sPKFnp;n_Dy;s%|b@nHlE6+eqXW=g>>bjplWB zdl|b5Fs4?VMjW|>FVK6{4lPaRjBy*z#)7ufwRtiI#mx@d^?$G77DiS+AsulU)%0BM zhz%}Pyvy!@P$}$ecc4)cJKG1`!0qQX0le#(`IBzo_A|2p-jki}33rFu)_p4M7W)_v zLhFIu&c+9A>ejIB4EqjNa()K`tC?|^1~Z?{c(KC}QmE)SN4zE{b?{lOfZuV`&yM@I zI6hrdcYo*xZWlEHysN1F+zs3=Y67?-(EUt=PyjUO^uVfcv~Xts@I60 zIY7)*0(M*CDeeK-IU|5a$rH70t#ZRpKP@NLwiP9l-L`e1dl)V+hL7s}jIar~%J@ln&H z7z7gb)K-!7k9r`f_bF<`je6RUx*GM~-AAaI*luOA`lbTcQo``Vv)>NEs$((xNCCbD zv45d#?4E9#cqSFSgvm$Hi!6mtp?w0A&n|vmN>})DU^0j#H@JrpMoM}+skkAGOoAI^ zo%}lEM(9wXEpM7lQ)|U}7{6W6Y?>P94{**KHqC3V%$FNQ+_U<|t%V}HX%iCtKIC$; zOElGWGIYxYOGQ`LT~J;++y-zfL_|C0DvUXzGs)55v!dsZQqj|tZZg096CY!@$h<2i zWw>O}oN&O3Hor_BWuJ5lC=~DO-)LbK6m#i44QJ&(W^l- zyGR-|SI`qPXtJli4VsD2mjz;+=*O2)Jtx5a)dBYGsTb@gpp5DnxO4i3T7C0+T+vZ2 z))UoHeMMZjq6y2ixh5~9y4D1fEmxbx`lfuLC`^H7`Lvsd@?ssjLUaL}`in5D$KoDaMmvWs%zgbrAXulebt`wh6gm0f&mNPJZudBU=b1p*LPcCi?O zZYs%iWf${+6_I0odc~Jrtf6W8vWtvKtn4CVoVD!Ybng5hAH=MYWwz2X8HW;#KzChRBlExY+wy^MIwgP9AjykZq{ z!1pps3oh`bp990|l=rK&_nm?i^4Mw$sP5|4?BLVPbd7!848_+jL9I}6Q!J{f$s4OW z`_>F}9zdZa`ffX15~4gBH@6~E0SQ1bTKRPrWFs6yVTcITfkd2?$!QCd=K#icZU&!?H|HQBz8%eHQ=zjhz# zr8ZL*&>u8)DQ=%V((K2DYzIVrN!xw$8*x+I*tU(C?8Y}XOU3%e%xRg8P#qgrNH1&W zF+f8}NTvuFELyOj*jSIyKBO%+HtJ&=MGLAZscS4$w_Gh2GyR@FQkXpOnQBu?y?2mQ zBwrp`-AVugd|p%o{V;VV(i))oM5Tvu82|jG7!ffav4nyH_whyd6J+8hA}x#Ceqs{37rm;?NsX z>w_Suw26E1_!@y94rH!cYuBC2yK_>`-XyOoDgSyZDL1t$E1=3H&*DV+IOI>-fp{`z z)~t;AgU%EQ-n5GyV~tr9=C^F{R6BF2mET%4-Szui!#Wg$QrAGD?~?F;2yaTZY-P}n~#>d|<8Fh=ZC}Ar0 z1k~!gtd*&9KXMqYe(`IcsVN^k%yE`-eAwfu1oDlk=9npvv+N7ESNv*g* z#tXl-rmiN?&n1`!=7yT4!LzJYe6P_BHEnBXLoMt##-{phw5fiegUn{^=E!8DE~u8< zP!xK@xBS;VtbY4P@%FyXBiO;d3+H4MSLe|%bgM=Xv>4V`w0T5oF1DSA^qnvAZg-FG z4DYnF*sJG<%9V1nTx_ha*J@21!LikSudo)tHn0 z1m<+sM-e*Q_`&zA%&tNE*QXg;(U3OU^dDqUA@=AY5*~^v2fr#6x#eU` z*-A}~TB@mG?uQpba5l54;q|f~-qM*CL182_RIay^h`iFB|1}u0Et)GPr0D;$ zNv2o(vWFDST;e>!+k-m<@UFof$GCypgF6Ip72J^`r08Yt4s~Di1eK*H7qeb*O@;wb zS^5mHiJW8XaT|Ql@j6O|EmBy~j~Hv?cO3MtEp9_i9q$DaDNBal%hSyEXoO8Te|25o z?C2wZ)y0{&FwW?+!=Ar74!=qlP-m2Dyjbenqw zDG3xR7WiH_@C4*Z8z7jDB=%x~pKxRCBuQ6|`nVgoU8BYdWQg8ha08!qdfStyzv0H( zNpCIo(GL9YdI#QBnaf0ByI!7xMB!u2{U_hp#0J*6pmk6H6xJ5moqqDbWe-FJE)+@lIX(* zjZiYZZMLt11U^o#m+@OzSfchBj_s)WiSuFDBXZPTA;MCwzEHDuBoz}(fu+>g0w;{c zn_#Slw@f>Uq--Asm?-LRO+|fk3XsFH#i*ZV3Q#M~vouqHI89T);&Ak@>L=+QOJT4{ zg1`r%<<=siL597M=isv<=)I{3YKrp$4D!8?V65O8wYnwk!vqE^Ky4;-Ta8 z#B%M~Q!m--+~_Xu+A%Q+=N}04Wmb?A{rFt_?*`a^FTkEX^@9Bb-F6ero-`Spq--b)BfIKO_rfj)Pj8Q-auVO1A2T$%NDG?Y8*@XM^f z$T`{=H`wdYC`o4h@4127jfetxSEFst4ctD<1n{2RU~hMKI0=T_xXk)dM*q;}!|np( z1MaR?$OU#D1FLyhCoQx7eQr|Phw>hdo@QOPJ6|7m1Gh_^0Nz#V9&rP=OPv6&q|Qv5 zXM6XkW6OL=DX1^HQAjU`6P;QA3}aLx@#K_ABcASeZ5~>yQl5&;`qiLAtl?i_6*2t( z7|VNqhJTq^PZ;pb`v0IO`Y1E&=^2I{mRT>COz420h^+-IZe8v9y=B(VIL_Q1rOvFM zPXOXF>lcQgn^KrAvwj|6MIsIK=@p+@|6(*vpIOhC#4_s{UQEvQp^he2^Hm4P(0x%C3Ah6{Apg^qM>>QIOFO*o! zuS$~MluD9>1@8hr0V#?84R}o8LHSkZx0`j+9R6ER2PJveq>?;gB^8oK>GT_z;9-~uO>A`QjMSkN~I@#^(X=iDCL`{`Lr>GAHnzR zTLd6L10m~&=>{Z^Lkm#;4=F3u`>-13@dMLZ2hfVt9eb1NY#@jMJ)))yRxFlyH2xmE z$o*9m-w-IO3=-7OT$wrd^p%;j&pwMB<>R*wb{!|eIIY>oBc>dBMen%iFw#SgDzXMoPt~)&ZtV{@HE(xUN7b0E* zh$vUYzyO;pS?}Fi%!hwsJgMc}a=oI&u9ZscOoc~o(6@6@uz29Q=C}Vja`vZPA|^^6 z7}d>OlHa`X!Ata;H7J?~@B89k9l`#0Q#%WRzB|=~F!eCyQ|@9D!mWoz?x)bG)arIV zjf`}v67Bk?Ou?A4yJ>3rmbz-Hx>I`Z<9)E1yx0$=W;3r=Ut(ixfnCwWgiTMAdGwmnd%o1Qa|&gzzVq&XQ>+EF%vSI(AmBoUV0Ojpj4 z?keO7N98eA{yLg$M43f=@h++I!_LH_zjsQdJF)2bpsl}k8a!1oSw(XMb@)tI)ryVp z^=zUe%VSb2(4(5MzYNBgB8%wfXq3c#GJmPlf?zjf3*cQ1*(bPx+YQ+QxH4p?$Raw; z-Jxz*o1iQrGN49Q7Q8Gls6}oAl9)nEBeRrOt#H7Z-kyaiM$0P}w~I`%DnPqvQ!CXx!ii zKE;9=C5b(UVz(P>7fHH`vdp2dOXU>gP#mqTq+Jpy6pj%;%6Bl6`tzeOj|oqp_+!wU zyHXy*lWznMgt8}&31hFNPRQ8}UGY7d=>4Ql7&dWsz~@#4I?cn7iYR;*Nl4{oVR*o? z_F!f$%FPA=ww--}b=;adF%j_9Qu&lyV6|yRTR(n=ndVIcm~=p39nPD$4Ym$sERd<% zlGPAzAYKXM4miM49q1pM{d?ReZm~laH%OUPAf+K@Z@|670~y8Yul2xc?^$ZZ^Bpvz zck@K}8+g7$c}(79W{9{Y6GfHpu^aTp_t=g2UD6!Bmuk$yW+lc<*S0-}_6dyimrdwE z<)(Imp(;uEKdGo93>Shbl>G4mqe}dKid2O-TWYO1E#sFt?QE%W;4e7`bOW+~jhWCu zyRL8&gP|pPnJ3s#g=aF$GTE4#&|F5y`5@m<$b71D5rkt%eW3`t=ta&mK}d_5Vn)aA z0klsbX6smT!o&_2hPsIctLSn{D!Q10Mxp%67+ul`8nxooOe1K-0}`|pMhs0vm11=F zDFUM94nvv{%tqbgxqlZ#;qlyS>50X2v!`AnqCIBNNE6R3V^N&w$H#MD9bkV=fIWNa z1^Wqz=RSRXYp7amt}oWNlncc~SEs!^&UR#g>NTeOTmwqhyhi8O;i9xA`05C;ut#x^ zp;3}3?pxi!?FJGK+?;2+!&BM{D2lrhbl|t9$~I|i8_Zg!?TNJy=l2fL; z8X}YBV>YnbXyZ(l*7-3NVYQzG!m(ks3af}YXaw#0GY84ATEc+af|kD+PxMiS)zUML z7c8t+YA_JFjIWd0dU^}1Jr$VfYhmiJ+A|11Tv+W{A?T(QrVFcG4OkJ=yC!fZZ}rLM zy%tT=ht)DBv9MakIBQt#G_Jr94`N=zz}m}+6Fji?W%S4mh(=&-t@98CFtV5?L=f@P z9dX7E7DyD_SVc8ADDY>8Dha}4i|C%BNKp?MvJT3tz;U{p@_v=_zFLq%9u-Xa)OTQ- zadCQiu5quMk?Yzcjzk>WM__Qo)hZN+7I3$-m)}@5eizvEI1A<4gI{muFHu-4StlK_ zYgv?f!F4EriSRWZr+bK3-+>L10_DUtRDeQ~fl#sAScup`M1imiWCpV6g2K-pr|$s? zQNLum#5WM8&Or{Gs!{JDhzu9hc^SVO*Iw~BHWtE$TUkV0@BykHLs4z}A%z$(*-M2r zg`(D;k+Hd~6LieF!Waj}ccK}Yx>+zB*~7) zC91#SqAG;%zc~I|qnxyen43~*6JdxI+Jr*J-@&wrd;Jy`f2|eQLGcG3&El_Vt|Hf5 zy2bz6Z_rW;-yU0X;HJaf4Z(WI78cqG$w=_MxN41p!g38}+S|Bd&A=HbQ}h7aJ<)=U z1w$=Iw3eu=L9uSK@bWt#B?vO7#g#66!wT>y_085QzT#pL#+%|owDBD%!*wP)xesq5 zG#ra0T{r=6uU!1tYJW(shFa8lbOUEBh!jA6767XNXbpEtmZ8;2-tER0H4zAky#ZT| z>1vEDkiCVEgyP?^V?ZlxK|u!Grb(B#8Dzj<&R#*+pyGbZfUMemY;4EC*L_^cNeD1d z91VV%C6*NWf%Y$K1*P4q$nF6)6wFNtM0;5jKi`4q8v`KlDh&F=cwsw6D7$7Z8wAUP z?36w5MCWNjPyq&B`{g!fFRqhg7u;dPz=HkhE)kLy5$yjc`FvG^5w69P^0RMn;2d=R z5Kt$NkN1hp2NO z7WVxs(*V&_#fL(w!n`_cp)s@MhGxDt-84`Shd{aYhqyC~`iJEU*NClHqrnQJ<<=_Z zeu$hx_i?Qf%|0j{;V&*$TcgE#zF8d3RmzQK`<}M*y1o3Q>k>v-BhK3UG7*zo0;ZsU zwGsVlDkhps2L&>JkrCd_dJ*L=76EGY-#NRCDEF%-MY~!^{cT0+N$OLP_q%jLivADl%INZ6jOfI2UDmv*iTY3g<9E|a?`Xf!WIq<93QBmX~s>l zYXRYuGO61W%7zNS%|&~c?UVMYri!9gHg;{Bwk?Z=RllQHHH~;FHodeDHqo^>r=(^R zwX(5kWGXjz#xm{~r=)8p4cJ3XQE!UJYx>|ZUAS~kY93Q7&g1wkp+A>)c!-TJw*A@p zIxhGt;9;y@+*IC*I1PVxqqT`1{TVCTG}#Z4{y))sDuEg|PrbBt;XBQEWTK~D+Mm*? zm%9zzEkzEMc3ys2(6Z+os@LR)hjZ0hv*^0P$#C+kOsc^|(J+Iu2Qw_jlgLXKfh(Wt zUIT*wVsoO@)uUGOBqF0 zcB7D97JH2%bLqY!V^$*F$ElM>MBVS&3N)%shW9Q~OqS%=^W}c1k8M+Qfr?xmcTeBk zizdn@zdFvvvOSDt`uwurt@lay2<-Dq0FM%AChbo()A&9&^z_orJMib@eAE~#7RsA& zT-xQ&8{4*Kv^LzT6x&`pqS!Bwd)AG%lPF!4&Ntn_?Mf$>;t`Sjz8ic3l25lt{@gtR zC-tKu8QN6NInlY74S$_sL9iBW75$6rJ( zb}e$fLHAY4$WSd}lgiAB2y07xG>_uB?81w*vJ2xog2esF0j`V)Uv&$wHtmSV+@pS2 z82Z4ZFA3biBqO?gn1$@3v?Z9aAHi9y#3jWQg_wfc3K52k??9+d5T8WlhACPG@1epc zk6=QG4+3!m22D+8iecaAfnnbNrbfJSf=0A%LFN7-UO54_O<6j@lv4}+I(r8P$Y>=S zbUw=Bnh^D5ke?#SbgOtKyP=-1HY)jMxmIngKnA^x1AdYRgi$;@4^>ALITiF4GfEGe zVi9MY<%nyt$3u6i(eT4m)D@9Eg1S_r;UCbxd!)0!|JRhHa?|l^i@%yj|B;H*B9usQ zn(9vcoN+p2AqlBV;g8c=aruZ}_BDT;CRKmOrAm)N^*4#zDx08{sg|h~*9d7aBt1fEIYpR+rqIQviB)M~PrcMR;b9V@N=wgC9_J4iE5&B9*FcXmk_CEbo%9jw z@h}Zi-!eUs z9>zF%;g8SA;CTNm8Jw{)lb9EbT75@&d^R9ViU^NCMx!J}HJ)|@w_BG4@UDj7KXU`O zoAd;5lB1^?lkKrZi`~HOk|%(7mAsSP!0nPJfGf%C%MriYjY4`^ zoD4_&X+SbztO%!08WDBBYjeb_HNg7>P37rx#J{y~?nS*!6XS@#im|M_BYxC90{i?D zz%BlikR$$`Zs_TyyFW*Klqhyb{2n*jPNH;GI`_MQ+m%i%#UqaRKX!vpK=SDp$!FXn za8f@ilA*Q#>u%r)$TN{^e^P<`8#mTYl5`cwAG?946v(h6{+Dj(2?!pQ#|d%7M`>?& z#4kD7xxML+_NTakJ87@&o$QYIRc_$@VSb()xD)f}EDby2*SewiBsKjx;**NY^=`~v zM0f9m$c!u;@%9bS6gc8fh&4jQ`{EU{5nucs?A|8K7k?87&wcSb@f7jJ8@34Pjz1}O zJeo&d?lEC(dk<|l6W)bzMdxAWX$a~xg&e;s_~e9WFlgM^ceZxa$Pj$fy2kw70wK3`fno)B4mWn^w{kd-x%yMEVP_69B;Rjyaz=~L zcv0nK>Zbqlf_Dj>kTZV@yx{+@4=ErY=1*g#z)-G}>!22;geJEWrS#^##Cfa?sV`IM zk+*m@+DB}%p;o5SqgI^KX_OxE=O>ImZUrm6;6hbWs~;t3WnxIJxJF2WA?XoP%PG9z znnG9mA$Om`3ocIpy?Vhf2HUt7{EhU)yx{C<8ol6Jt+F1<9VGOEYn}Ar1;55TuxUWE z4OZKb6*~}x2r{o_^$2)910-q{D~&<^)}R1`nuu2x8?8!H_+Zslvqk|op&E?u)J*;Z z=S+6Hv)>l9oi2B_M#5FDTZAr{k?WKEs0i8$h?2sU{dP1; z;>zCP25z^{2;f~!sqb+Ew;RO-aOF@OK(PWL!a^>i2D6Y^`(by-+PW|mF6{?7FS?Rz zzZ+{ixdia8*3U1yf!i6K6!ZV-2JXZ>%FM9!^KabH6L2$XGw;v($>|bWA?-m5N$1Kh z+=H}}kOTMbn{&hwLCnpJ*Gg4_C#6N^o#NVHc@G0bUJst2^WMGq61$0{LwK>Xi;{B) zpVNoHnIo2&ur$2f*^Bpu@Id@y8?CXiTD_TTjOOdjx3`c9(P;0Y@$J*c7viQSReBuB zDA?rMXU5&TqE?D@X?}Uo*C0%grHL>sY zK(rfOiDkm%Zzt!}oBZ|dLTvh-LN;RZzY4N2VJ3eMG~_1#I-a5?e;wHvlfU+gX7ZO3 z3te!HP*X7ebxpz>LMc0{K229rP{nI@!-a9XF*h@7rKNFE2Bjp}F6WKrLyYeZGQOsw zD>bX$k$6^-$nwML;j*Cx4{s3Yhus40&U;nabdfZP@}jGdRuZt061_M}JNwuLBvh%J zDq!zr?4`=#=3OIHBOU2_k)%FU?%o+LlPwc9BN-_O$3HxgQ!=ru2bu8hq(kBy|I@}S#4-T1ZkYGeNzfzvwgOv3Hu3^H_ghC~Ab^QyH^D-B$z4%HLS*IHB-19^C znBW$e!UXBv$EZgXP07ND3DK{JpW6x;D7z!p*U42$QomHBLlL!~A zu&K(*=yQ(ZjXfE3#!BSl(iFNG<^WY&XOe}qL%y%~-i>tSU?0;ZEAW9-^cJR8L2vS4 zd=TyJ9*ijYO;hVvCY|n`>7x0@$sU^AnhsBRN6&BnuqM6FZ@(YcHLKbAxjv+Yd@7H| zN{eBV9i9r#1vH$J&J+#5nu>FQnI1|vYaGDsV0fKhI|2R86n=E6mRcXL9>V`0(eLEIM?Jk1S7X?8 zxw;cwoLSTtO1}OL6p7D+(a$Qxb5o;}(feOGMSF0b?Vl!cPX?*COwU-PQkt&rl^89~ z^e#r^ahZ?y_Kks|>R;C<^CZFghNDKFNi}j)L6bM^WH$0N3YuDRdZkf+MCG{&`Z5(X zwVIWnm8qbq6;}*tFeG(C3R(oW8Q~3AU70U8ij6e`Z`@iavebSkXSrcxd*4G)tovEc zJuV^AwIhX0*_AUt$c7T{=ZK!9s05!C%dbeqa#Ls09R-&%f_fI6X$bpe6F*iQ=_VMe zqDnp$Rm_P%R|5P2qe_~IK&?0}(})>yK#7^Vr8yaB2XR9npzfB!RVytwy}D}e0#UfD zb~imSS1o&*MptcXY#5g^O>)Ar)=3|(+7F0zxme#)E))kfpjyImP<1**zU;);r=PDO zCv}hUotkMMbIvrkv-cxG+v&268RHD+E*}YoZ|l*0GBAB~duexG#%x4Q%d@UG6x zZ@Piook0S)^8KbrWB!4=L(QL)(AB~E?$w|1ea3^(5@Pod@wjs~Hax2^Jr8kAgL6 z`SCe7Iqi~n9UHZtziZErA9Vw_OP&DURr22H25y%;0bEI5U-|KOyHQ9li<6Nbzl$*| zk%)5Yq!Cf~ySDuJj|2;Y_b5GDT<=plx^5ehdhMr!!`^%4y62+b$|AHHBCsDd8onN?t+m%i%#UuIg^OigJ0SQPx-6DCi zdjwAEM@2FeNO+nXcmncFWPW^7fqb1CYbQy%3gl&O;3)+%oF9Lk8+rnQN9A!s^5dhl zx97*#+ym&3_BXkKJ89oFLUgwqcz>9Gzzy7qd32VBV*)?vhTfCZ^cNwTR9v2NW9}ll zdncp?%YDSU0h;9|ij1fVlOY z_rsBc;3x#O?P2k4;gt9Bca86YBM>oeU2^3q)`_CAf{4XQfvq_5?>%tD`#Ck@;bd06Z9p5E5zKUR+lDdWvXRr#Wg}23|TCc zpk`~x-O|L8HVg!~-1y3f-me$KJ{EbmiS-m=wvz|Um4P0CtD^`aa zMw{F)`?6RqY+JE{Z3nZZyMi8%j~kTSIL5!AG}G6>T78J7j>(qpE!}5`hIFp)!fg6V z(AIy)R29>YQdyQAP{kLHvAr}0=EtSJ?I%TyZv_LgMUS|na}+VWza%i>@R&;{QW!Xg zRr!S1n!q<`_mCwJEg@N&%T)&_rD=ijr6SsookWYN-~K~1N)j3IOE+-4xlaJ^YMGz^ zGMDVf#tnunq^J(hHd5 zCjh}np0PdVASu~j?H;q;w9SDh%G`gW6TUNZKj{$GJG*d7j%Q@rITF4aojQz!2g0YtT^c62wNuyj=A9&c~4a z^axU-D614{t(28|8#2zH?mRhuJHABrsi-eYDNOstm3Q?0_}vJ|-wzA#L8L*!?oOG^ z88cK@itHr!qU8O3PI=d5PqU^^_l^ZDd#}JdY)1cIo%gdWvqK=TkK12)B>-kIe-zBQ z1?dt8kuCv?FVffEly5XIykf0n7`Q^sc2Kr3*vVK)DQI`$Bd#H({EBBNc^Mz6Xn?qg z)LuehAj)i$)kx2f=4W=fEUeK;cV>yrl=e&*lQ^NTCjvYEZ|2~XFn+8DVe}rRM%SH_t9e3;aZc;K#tH}xtdnj$!ngFJaYi5_d=*`8LSiQrHGHNmh#2-BQ>F z<$g=rhRjC7xHflFt6HGjjBWO{mYj?!gss80{c76{ubHaP3-Ro z`&**FbV$35S#M5M_BDk_wsgL~h#SmS30o2HSPnx7&Xw{PX*Fx-ORvB$+3VW`6{LjO ziStTp)3EzS2UavU}Qm@9WR5wVGqCW_w*}(K>(LNU>V1=bN=UAkCvJxJENSI))GP z>$O@Fs1`Ssx3>KmtK0s3jzSLoZpI4(;$y2`!Ka1IS|K--FKnh?fyyl4T;JCAkJ$t; zEO4)@W|)rdoZpeJ7~ite3}fh1%#S8a4p%B>oMq5@p}yZYVcPId%%i zV9=jvKvr7z#e#Z~RcEUdw-iCnBmIF=zS7KzkF)$G#jRyfv6*kS8tU6w!^NT22;eQc zAz!b8ig=@5%#Y%md0<(gNJAq0%2m)T2a{uM7;m-xd9CKARp+j1l<~aG{4rOo*K74$ zDPJ9~6zkvsp;~CR{l%Q}8kAWKO4~m<^h*eY_g2gVe-VePaFDH+nLmUY`$y9l8X!~& zB&uLcZGT}UUma=XM~a~MFg_nythW7Q>%~H?KCG;(Wk%T9g<5qJh6#$4howN2X8Vis zt!6E!zVeSOL);b)w?@ZchZZ6U!m=;Qkj6SLFDKS5X5X+0LEKkgjWvp`;aYC%C<)Zj z#jRttMlq+iWKvRS=C+g@WiGOdbHn9EeyBnN5m?TVQw7s*0S42+q3W6kQ-__{GTwyjyJRR?SJ5zMHi^d>_9Z|0iCQLv{8 zcFe{1t6qNk*`Vj5`ldn%Jbk0Uze0k?QZyGg3jhO^Fx?L z!_89rlDYoLkjc?pFgb$(d-OrY0GvqUAwm*B-x25Z*&%nGBQ^Zi-n2Ao3eJ89GUAo?mT z^GBx0MInOZxaSBq?n_D=yzd@~G5;<7X<^;=-a&spKMQ}pLVx~wHvasC{xs&{&sO^L z%mDs;iT-R}fInmO=go`o=TiFfjid4BIr{VCWANvv^yfRr;m`B*X9RvfuR?!rI39m) zq(5|5l1Fzbc~nBtqY{fA#fN$nKkAVwi@9@J{z84KFy<{RlriCm`(P;lhBTslk4uUE z4Sx?h2UBZws8$JPAA9w<(3ip6^9+ddWUs8zF$R1rZuMpxs%mK%miPsG4Cw1Ztwq`x z3wohesqk5V6?@J_;`@6bvOC*XLidd2>(F8l;s&N0b(n?VEoy9QKvl`FBLZmJ)Lwj5 z6^b046o)gIM_Ak$%>uhe?C)xy{pTd8n!C}xV`ls86TN5A$hkS2C3Xf!`7>#%VaM{u zE3dp9qn5ii@3Fa9bV;=o+n3Cy8S_;_1fcOf&ftHFPo_E19Qk=~fG*seVQMl%KFkbl zVX)9=@|hv5zne1z`n*g_5vYY72$3kDPG2w-1^=-Z3VuH+1&@>zoO8{ESFT&T?#;AP zFsjWliJ6{;MNN9J8)%fVHfPvckg07c)~T0Ft)SHT;?`zns94Ci8b$neY9rIAWf~bEa(d%JTtGuqo- z@6PJO!H#5OTf&VM@Pt$xgTbUQkV+yO3(4riBq25t1ondCgsPBATu>ndl1lzaQU;3r zUib8L_w4NK&fe)xaYa?Sosa%_{od>MUjMqM`#{g{eCzWI_&+vSDCg^*Q>=}a$_1#OGW*2?v2-tyT3q{!P&u~4;2{%yX!fGw?AdCw`A!t{2J z-D@ws%^$|g9?$WLmiCGbeE6E1GP|tm9tXuyGNf8^gSC(9Ghg24i|TtTIaKEP_A1tw z4XLviv)+KH9c)R_DOvuG`go4@=RB|KjMqIhhKOJ66lx%pEzK8mwHhAcx~G(z5?9L# z^@_R~&*gVltqDp~XG^`@B)#(OL3^z|V6U?m+w0l2TkZ0cwROs>TE&7@+B%hUN?R+{ zvI{2EwoaBy7Jhc^ym2cCt!>R!oUJv{*4ozk#NKRhGg_^oJsY+!s*kgkwTkVO_KoHW zg+K&7PN`t+)0igsX}<>Tu`}Sq~S5V5Z zl4r=3N@Xt>QFeCCt=nXA+1=Luz2$0QEVJ!(9&~yvvx&EH)0J0l$*5GdN-l3{X)<^Z zQSv^sg{_K~rZs`}@bW~*8IpHFPl2p%7iubGl{19H7Vh?Qy4sL@e*<}LfmAh?WI1(u zoeMLvK3u7~?ntU@Z*yH`QI};E-1Cf z=y{d>W7IX#UrLji@!!$u{`TD(b+Q#i0#^WjZ|Tgl@3$WyP90X9vJb(;2;%!V@lneAm99&2^+$@vPbbm1 zjA#@DE~9E%)Asw#w6RwO<`2DPbdO{0&BG1(Y^A)+=1O_HT-Es9&KUN=f_u5@q~-Z+ zLspo%TC5d@BS2cG;MT(yMDKCm(nzx_PXBN$5>sOzn4BT@GEh&txpH^9*?t3yqV87P*T;tgEfyPsf6VnGX_&MG<+BnfT)i~35 zx^b-W%=E$Od-%&E8N7!v$_#(FlrK*a=W?D^$Xw%8t-ZNoadT)+XS2vqgWLgX1rPAz zJg07EgLco4L?nk7P&_i>lpG30vPB2Mr1D29_+Ar9ltrhS(o)|1J~X@WX~^Vw<8~s%1lq!4imDx_&t6!jJ zzofuu`hIww6WsMaLcRuX;6tY$6fTII+ex^mBhwGT4IhL{BzMhAIG(w^Rxgw@z;`mE zqocQ9E^<7D98Z(4I|^TS5WZSuBey04Fe0L|zmO|=PCjtZCxx?pmX|GPdM4tcsc!Pj zPc}|YKg4}En#4&W7mUh|;351YQVG|38cjP2NB;o#n@>`cPB)IxA0qAE=|lLvXPV&w zVgYzzw2Id}{m^KC;9K2GHBPdP?b#?hV<$Rk-!6mLKZ^YIarf;SvBJl? zmp45cHjV$D#=J;9>Ty3q9N<9I5PM5%lGHHJ{VWxv*$4z7D>)dPrNI#p4Zy!DXk~+@ z4omSZ4>kLx17FZe|5Bv%WOMQarC!TPLNqkQ-qB?j(Flk(FVR5okx~$r^?{H_K~K_H zvpHm-T))S2nOftcC!C*MR`d6tJpYK4`gJ~%rp(c|(rCG^Wc^T==Tj>gkoSCvbyIjD0#^yFDX%0vf8=2YQ1AlvcFa@(ih7@wBze_$~ z*C<3+6*kN%9n0JelnOxH=m#;DyY+6sP!^{B5H%33JJFciHfKhz%oH3Tmcj)*8kXCq zQ6|P?+7qR)%NLnO-!#j^_809h8OWOitpNicd5nC*bl6I9yJkC}d|< zBM1xYn4!YV$jOzkJl*lF353IiA_Wofe~Uo;X`)J?yqd%8;rXN;3+|L8Hiz7dL$uh! zQq%m5yNR-_bzg@c4jThL5|d=vO1+f#>P-`03&&*jxKdS*wW+$&T=hGRsw<(|#m_vs z*Cy6rf23)e3sX-)yWC;q4a;8<$Mqk`*8w~-#FkJpt%bKH*RM~_YEZGjpcPuY7(g~K zd_s?d>52iZgJLoqZ)z1Q?@T!PMCcgS5h(Fm_a=~q=<{8A3m4uR%IH?D1$QL2z%>xQ*)#HAQVgcArr82~#7Kt7myz)S?H45x}ZO|JCtKLRpj zq}5svsSTL^n%77LeEYYMZ|*t-xe?YG@DscGz17LuSbq?1G(HEghURYws4?eno)#lY z!f=eqn&~64U`!7$S9+P?PVsc<;!gWE^{QYB<5$!!F@>=h`C}m!O)<93q*0RcnH8cM zd{LSP^FdqCTACIZ;;l{8J2x0UX}JWCT6+!RY=BW}oLwL|t7DYx>Yc)rkM)!N9?LX7 z3yAVKV7Zgij8BcB$Hgdyp+TIX!zN<4OqMITtsW@Oy*&-*BC660=Dk%ks2P3JRAp?2 z?Lu%clOj}OKai%ex|%FSOW(&^nnq11m*7ttH6gB&nxq(IYYL{UQ(~R=U!fN10i@|Q za}0~vxmS#&qw8scr5J7nl(MCWs`+XR_YS({E0Xm4Qh7uyuf)2HQdwRO%Eu$LlyX19 z?d8EsdA}Mdf+aMyFwx3VSw55Ls3fYdW_ccBDgQ9JitBiEL%4$+!*`23yek}tXUDNR zCl&lK+A;jV%kCC~xINDA3u*&9-4=-00u-VumKBd>uvRZ~MP}0`j!ul1%SB;eY=uq? znoN;4Ci+-)K0nZDDGswICF2yo9p+`fIX^f>ARQiqyX)a9rQc^)%!t8(;ohj1m;g_h zL+Lhn#2Ge+pBEB0jK!;Btae~j4At)o(@9gUJyBMAB)RoTILc;Z?a*di zo?J_k%@DP;w;9(aR?)&{1Whm)w-~-qu&fz==u)-i4-#vlxrd1ypu_B*T(NGM?ZUGCdZkjXdfD0( zR;1riFVpu{6zjT=>&;DRPLh+mUxC*;>^_aZIyR{SGWT&Z`XVm+svgBG3B$%1=OJDy zwtORzExJiefktuy(s!M&gO+vxFEKqpBV$=%pHnpQ;+D~V93-g-Y=R^ogzUP6B!5B@ zJL~=@eoRO*Is)LhUdzEHb7Q!&rGt3z=x0FxFtT zM$`=8#6T}+tvsdJFL$iiG-f4ubKVCyVE=yyRKuOO#W)Ym0>&A}CP2SvZM;4^@{F(oe@c+@Gqbl}rcPPm7p@L;p%5MP@{1RRqK_)pO7YdHVS zC?_hl0J_z338}UAdnD@sy47UeCuD6vw+~2&wV!;=SmuUm-O9iQ9prk6?-&l!noFt8 zq^U2zkEAP(I=MG)&i3o)%zT$2p!x5|H)fT6X{q8vzKog9XizFq=@W{xBn+N~o@aQA~scMo0NNmqNrt9`gS?C$4( z@8o}flmGn)|9cnzdpG?h*E@S~@#Tb!(ue;}sf+b$Z|(#z)=TdeGQKVnlIdl3yx6KW zKD;W*1?h#FU#On(aG-Ef6-+fx#)yxEdD&fajuGq33zoE*C~?Oe}u;}o^T4C!O=;FlAt(F3*4NA3)u+G*bZQt%+RHiRxU`0whWg;EaNn6aY>vE6y!|6NN(z_Yt$I zOp(|G;d~6z>k`7bnPhg>y&XR$gwyPzIf%0%DPmA-%%nacfMkIINQQB?#jP=WYWh9` za`5rpVGMeXWrm&7=IBrg^n@$VW>$y_LF|qdSpqiaL9_u@TLImjKsLKH$R;yvtjFFA z1ZJ+5sokB1)pGE;hcXHD5|H)z(TanqAYV_og5314pR<*%^dBqkbef10t@*uU2!agU(FL1 zvQ44@0DijwL*CLU7!nrWEgG^TCbkI+?RfCs8V*_A#0cq3V&16kv2dx-I;jt)t|tQ` zzYH`Mb4qv53m_8m@4Q7sE=pu?;!tf3vUZ4wT$)@<5+WjMX^)6pmRLm#L?md!Y#<`n zB-cMrh{#oOGKxn;Zb@uF;wWF#(itLRCsxzlFnM+mkyu{EBO)J3Y(xjVGQC;{-OS9O zvnhgztl|eUkr; zTh*dyY=T7o2ac~xNaQ&Z+FAEI_%R`o7%$B+k=G>At*vd5Duqb$<-ji|gCg&a14Z!h zSQHk4lTE}$W?YU6Mqcb#qa`$QKExg1`1bAK1j%CVVqJdMgzE+MBhDo@fSl}~|;;6KZn}U)wzPvq^pd+pMBbxCHhOs+ z49S-2Q{z?@8kB7QBR} zBNCVwv-;ygGYg-sAwKh@!{!Hp%k{J&hmD7d!}o_=O1&K99%Sth{P2@&NdiAaE$zXN z#}lh)0e%EcFu;$#2y|aq@`=KE9dQfW*PPs3nF;i=m&|65kgS ztI&go&PT$$iu6kZVi^;Ua{MHb>JAuV`lm#cLnxz}LFY>Z<+z)Rx3zDlPl|zK?b* zrcIgo=ERLt;Zlzom7)CK`#M%#31`f6FaajNH=wx_BqOI*dB}((SO>wwwCF+)#cOe1 z1AT8JkCzZ1Q<?WqCtskITH3H>Zo1G+ZwekLN)f0G>z5Oghu?o%Uha8 zO(>V(PZ~8Lu9BLhzzH-3Q+6-0PL2~uN7OA&@aLdX-~`{GD~=QJt9il+-d=EOxp52| zu}U=6-a~I{3lpel=NC#}@Xg}q002GQ)>L6Y5P>Ns8Qz-Q|9O~OmB=yF2k(4U8+d%3P z&3LQQ5bw2$qxw}U8Aj=kls-QK*0(mm$x+m&!n|znoJLW4<7OdIWpPv+j@ZW5!T=hv6ZV4$vwU z)BsPZJuvkTQ>kq?P#WR#S}wt1GboA5CD_PElglr-ToOByJibcHrPMmHJ{~qn%OE?gnWKH! zbOpE}!lo0W!3TJXLCcQ=-}@M_0STyzI_7jjke1oP_{OHN~~4RWSk9`nF<- zL9bRpvO7uZLU}3Hdm;N~F9=)LeA1DsIz7Nu0f|X=O=5k)!uwb*p4-?=2aQa%h=I5i zeNExn4z#q(@az$i<5~A{{Fvccq|@;TZEfqW9>QEw1L0V%r?T4yX3K9J<~y5 z0ZyjnQo5d49}oDXm64s<%+WsJdl?jpfbZvY#REQmHBSNGyQC+mc{#71F5+u@;)z*y zw;|tU=vH##(JV1G=G(;^(iH3snptJE7k#Xhe$8^>Po$(x4NeaCR)u-zc$42~BWEGr zQ*CiKQnO;>y`f0Fr->jY(CZ_43Lz_vEM%m^>2Gks_`0@_l9cCidQx1pWB zelMZ|F|nN@uqn11KvTPn?QS9oo^`YMF=M-E7vsU5zAu(Zu?E3OeS;TSL8Z5i?q1(` zjCRk(Vm7Qb3f3J)nyQS(aC^>c!LmePCSmA+YP==Hu9Yqz_>!lxVC}Z^?D#D zdW#vsAugjGo=;q#wT#y4lu1Nu`*GPW zTKhm~hGwCW2+3xxJf(3DrC|e)y8<3i-1Q*mf$6m(N-E!yV z6~tXyE~T1@_3^k%_J7&=%pC3GuH&Fk#9b%oipO32YG!elNk)WGM7aGSeL~Jm9#Gwr zFXn1B^VmJH-|=6BL_Zf2&9Ay5dZ3wi!}c~d0Ju+LcOYUTzLCJUEc|gMfb?)W$)$Ka zd>bEk_n}pbMXT&Lp~Edo@{O#wR<2j`0N>uk*5C-_Qmx2OI?qvmUSP z<%<4xcn+DyD&V$G3q=;P!F;`1#SwtnpsXEixqQNpW7)x$1qTAwd>lMe&euJsSR0+F zmP?*hDq#BQMml6g9ihy6@iBd+?)f|H!5yq`(kcZ<)ZnI&!(l64ZmQz%K&VhIdmz=C zaQ69Z;a0FkkU-}>??!_D;I>{Z;%F51gOY())c z@Zsya?7oR|b;>`)266t7Q{m@BBV!E(t`kr2G2IkB$?kPLJImDtjX`}u2GSq?V0qQz z$Ho+`Jr>xxi1ph!95EBz_OR8~J_lUn!Iyg4Q?SPClPGU+Z?0ORBdSrZm7BtoK8Pc4 zQIm*1rv#p5tx~>Rpu-ngU)`G+dFM#Y!S#CcW!9=z%hjx%D;0`X6%rs)^PbOE3eIb# z^zc%AwjuH+QiS^p^Z_;~%2hPT-0RI3k<4rrwE?J5r8dJk*nY`a2`3kg`8k3!HfVTSR>#wFORD$mAIpq>$Lu)4r z5cxngw>P}qzL2e^{szq{O+pci&XnW%*R9&b1}j!2i(X=(m*#~g(i1vEGfQ2AU1WP+ zr8c&8D<0QIEBigWTpBG`C()yZC?zKVDYKq61@U+g$1*%0dBdA813w3=6ZuH-^h6bZ zsVp9yH%BVF+p3N_mN&ucfI--5ds9VJ*5~EM(TfV6?QdVkHb5t*vSH^88<4G4NJ9>@ zRki&kFSjpiJCk-1|DnH2ie*y$<@{u(Y;ZKwA)MHxPHM^)%lTZu+6{Rdhr;I}KGEO6 zWzIsR-d(Stia}84IUe{g%)nu2PdV?DCZGd>UATY@MXf#_N#;zE?iR~A(HWO;lLVE) z=eh;-;QgS$wk!{I4CRq(ch;K13G`WPPeyhZQaZ#|Q^_RpnpK<-HtjH5Y?b!-JL=Fy2T)7yL|&cCY~Q7@6zc^E}h=w(k8zyZ367l z-Zn1nedE$cU@m{3SFr=n^)u=bPbXd zSTCNC?ULz)JoP6c9jpU7!PI!U81@sMbWP-8n7Yp>u}{{@GhJYm4>wfxd>ECXXj#D< zL<6v_`Es4CA6##~Tr3KGg>PMYb@2RQ=*NBjPFRUbt_phtb*rIchzdgkYdjtZA9LLv Za7y`Ny+D0wlFVU$fqXzT~dDcmHC*7Ui_7P)CWU-C4vQKv;JZw&Wl;03xnOMdc9A`DRJ9jgq-JSK$ ztaQTIk!@sKcz8HoVnQIs1*8H5u&{*XHv~dqLST=g0!gZXG6Z50oC*mL5)$(L-P1EY zuin?`c`|tnN|KI&j{|AQNd(WqrvH!faCA-*g&9XCDwM)9w^j6W0ie7b_ z_cgcP*1V;e_eSd4KF4mw8VWqxbOI;I;ZZbr^eku*ne^u4(AbZ5r#nkwSo*h?u#c&Ay6=ul+s@}?CS#uoR#Pv{BtMIE;rA96MF{2gt z)b&|t0jG2 zq8lfv@e&Nk$bn|3Lwdf-W#lGX1(ccuGpnUS>K)$2u#WdG`&sy z`L^`O#_&$JsWptVjcwuRcN&{PV`Gv_hTwFEXbn4Nbq>?G;zXPABQG)HpJj@ zZ5;RfU%vK%`MbI3bLMLiZhHfJnwl($Brv~^HgAELNc1f*?BqTNa-u(6g?-(m>T6%J zFPrzJN3fC+@NAuaF8re)q6gVb*WIasgW8bX3e z0sF1X6g2KM?jmv>3dv>M4b#QZA7SXnaUYYkM<5)z7h?HA6_%?AOOE0y>L?q#KS{-I zBf}2!2Wac+^|A@8E0Os1i>cUroI>b>KIQ%{i1yc|a}yIN+2VB~5tZytW_HIBvN6YnUT_BMj#YceMJP?l!$sv*(?rH-;CTnqI`zT5KNjMqrWn{ND7& zQw<3`IlRLy|4!2HpMWaQ!GnsL(k~^bC&Zu~Q>@hk%a$o9k1VqZ7X2m4&FsS+Igb+zW zPcM2et^f^Mz#GOD9D%0y;!Mw3HZIsJwq5y)U1=lVT z&0VKyIfTUncQz4L(Ov%?qVYAxCQe(>`fAc)$yF)ZRaXuWMHoN<-w1JYuCCP#Szlj# ziH5*yIyWs0&h&0RoZU>w^TxQh(tkx#=Rb%Ev+;ajt~X{tqIk4yVxrf|QvcQ=f<&R_ zxHs(6G1QrN#=42xKSWc^`g8jXi@A+k%m^QdqxTS)Q?0x3k4pRl39(GP$;`Vy16c2) z8_5}f47)yGE1N}=w1ZyQr`4-b8_!rN8FX-3mS8bzV(m{5#GVju7t|q^HfAJ2kHxV> z$XoD^vB~-*{R1Zxt=>wPOa`r6aT!QKt@U8*=)!^4XIjS=Zp*cfv>so0cW&Xf)?=-Q zT2JEMf!0%a#;@_<_SVtXV+(gI9Kxf6xi^{59=P`klWsq?a2Guy2v05Cws8Bx9j&KZ z59M0Na;-}+vPi4yq0ec!RJ`PbKxgbemV)ZnM83|YJ;x|lE7v{cPN#C10-<(C{ zePPZiYSo}WG!_U3Ans~C z*W31u_3JFx`+ zMsJ^LJLX2?b-`8*7cy}D~YQ!CErY?<-z~i@jYfI)D-(pq7|Q$ zvO6ZP>&uZ=G?V=m>&tou{|K9O{Q!Qn9w1Kxj>YZVk0IBk^@j`hz+J)Yo#Rf(!rhqA z$!EFv4SY7I$(&z7d%2hUK1<9~4&>@EeCZ=VlKb4$FZ0+RTDTMAeROiv_eiX-ON8+@B&Z^EAmvHu zZMZQ(RC6}!ZL9*1YD^HV?@Fj}55xLF;#NL7y}Wj@c*X`AF*4RWXjnXWUA%5Ws(D>e z%@5A}IeG+CyPHu>P3!0!O|1@cT1V#S3;L4Q6v{mqQm#+fA-O)>2f0Qu@CUjk7qvATI>}5f>)e zv%KS&NEN_V^hAzm#xA$!wq2OJ6+25X{DtM z!CNE2TmGYPFL96_2{Xojg!DRTg^FHA>Ta904vi4C{dLfL`u3{zQZ?{zCARXgzbb&Y z-Tsp9mbHhMXd`M1ER96Q6iGa@ZkwcbsZUURYzW<{LRj0l%%2$u_;JW^{4x0injtAd)UatVyZ}&SS`l>NH)AT2o)T-LBju=YW&kx#0Q=v&mhgs67*-)(7P8|-a${6FiDY#fFctT z&OcYzFZY;+S+$f=4QlqV+xZ3Jd zZ1*TBrtEu5#@@OC^Fhol%S<_!O>L(cGTzB)Axkrw6hT<>{1|CUil@j}KSl7svt+Ca zu(qoB6NxmeI__lUz9BK@0n4R!ZwIlh23>i$@*+v9UKsJ*)wK2zyceYnAitI66C-IR zASOusGe5j(%vk%;G)mX#s>+3}Ce}u2i~2K^#6SrL@`@_57IMf>z?Lv&A{;~R!Zu_s zu#H`|uPgMnUofFV*;I?5q&dVbe^u~5Xuv#W<6ghLDiwxW&Vw)#sX#yu1Ya3MMLEwC zaE5tsA5Vi4DKwuT7OsRRz>+Afzo4HiZ&?0awYTxlE09c6wZ-LHaeEtmO0gg5HLLxH z;`au;;paX%!At9^wzwzWBS_Srqq{ERZI(l4T*ZQ9e>p@NWpCe7U{U0}l8!BJy_a>7 z^e;Z-HIzvSc=poB%}c#B62Yq7>d57?rb;ct1(KdtMaCphLhmB?LM3V`l}Nod^3|ZN zcY?_rTONrme8|CTk4qvy3Wl1R$B-VpejklGSrYk~8hH9#>H}}m;@^N1h*#J}$)?LM`s6Cg{IOHXf?=rr|Vq$4r^ln=48tRMn9b!2PUaiOA?1*{S zTfA`fqQ2RX^eY!3THVfA(mq<UUn#>eMGBCilj%Y4K>b6 zinO&7cc_7<$2ghfjzz2Ys)2V;?cSqRALX|6_^29XB|X|ks}HMz_s7Z4tAQ&qPf?s$ zwEA^5^bS~=v?_f>t4ZBSk5<30Mq5Skwo`(LR@1HfagSDiBg2Kv4p>7(69Ma&FfA=c zz`9~s1gvAYiUq6@PgKOLFU}Z-6t;>v6S3GczQ@!-*m`d3!z}MQBV=tGulC$N^s!F@ z4YKziWiLYtRKM(d2pH{pzkR}$kX3DW{XNS5ewO`xg#A6n{ys{7flxf446x}rpgb4f7#8;8i$}ZOWKbK5n<`zJRXTqb}ju9`0NA>rB!?ysQ z#Z}Csl8v=+s7}e448m;yAzBdB3jvd-&b8eJxT*E0@)uoVBOv8_uB$G-bQ^XHj?m{{ z_@?X*>{JtqP>csE6SPeYg@AHAT_4}??15fw+eBhGb2ifH0N%2SMSx72P z-JxUr_lN*$XJ~Y z;eKTo>4(Vpz^!R{2+Lpba-lMG8H&J1l|5Kj#0Sy#;1|F9&fM~I(7S3aCCe=jO`LT$ z4IsrWKke2wRyjN%BuFV<%dCEAyhAd5QAs9w;iyARIaPsOPYU$84oE3E^yw5l?hgHU z>b#o0LK>o(@aH#Tz>DF}{}^z%KmSu)#r%0`au)7g6|+HsR!vh!6790c#5kOc1eVt@)o|G)e(bmecKOpUm|{fmGG zG3zc(FOt74NCtnK1L?uv-mpse+Z*YM`P=NOAAdW*re}Zqyhg309k(PL33#^8z2w;; z5v=YzIOpN|t6A)6SDPX$>G$MU6YP_m63IuyMx zxmFkhV#Yb$_2wh)%(vUlz{(k4J$|nAUNx!GF};`fE@c3uo-t~DSPeW~s(j#WrRuO6 zc)C>iz%~*tt zujd#`+Gl(XjHqTlBV8wa;K?~CcfiV| zRp}$+E2%r_8DICR(N-2S7_W30hZrMtN&zKKE=W2|LpgjIvK&csGF@boE}1CPqP ztRdnP6qJOqeg)y?%S20fZn6+SzP$5}fYMo2Ui=f`vL1(Ka*L8&dE<|a`y;9@C~+u4 z3F|BZ!;uSf8~Mkw0(E{!P~={S#;e+(p*klhWQdGLxCHjr6Q^=)#Zf*NXrdcj{MO;Z zz-qa*Sj~*y3`x2}Nz#ntX|6=0PS)n$tOlO$i|c)AMvz(ZoOxTUi+z5VJA zC0}8&*5(GW%_^P8FAA(!4MzWt7EwB-=6&kHsi@h}uK0sW_{v@JPKWSmWfvK{;v9H% zSNxQ$nN}!M_6wn6J*mW|mmPGDYWwMj5?e1t@6gaNS9(#3MulnIx{dLHx3w`pRRd4g3Lm&ctYlrNWLTe32Fe;76OKgc=1KQf zr^fn_+&q4E4`f zZuWw044pgqK{D(baXD353cOla|6lsSN9tNkK6+|QZ)KHkjtNk>v*~4(RkA!O^UOL( z0JyU+>I#M8c~=a2)YHjZ_mf}5YTdQ3rE~{iCCrm-HAS_pWG$t8(KI{(-6_eO8L<{dm5hbdXL&cP&I!VZQq8n6nCUQ2>680EM z0QhG^Ug9#033NeTi3Jgye}O0DrkDQaixAvh@*G-scHW`}XP4n@4lG(@}XE zWrFuOr%&f@xcPKCj0IQ7UzornoE?fESJ$Zcl2*O}cW=Dq#?f0+kKbPtfh`>8*xiuE zpX-Xo{;~VVxQNmL(NEwK)-HMMe%?xwAxec;-aOj!l8Y;kD*Z| znr~(eEo#SqyT?J`+_(#$J@5g+_h%1$kT{P!#C!IOai3lR|JjY01W03juB);7md0lz z6<+xv8>?Eu{L{##R-8iF{sdVyznX^Mis-t~L0|q!vu)fNqAT4VMU2S!{VBtZWSd4>CdBlSdr2qFM<7FMKtKsF{%zmWzvqa;vy&- z0Beov9Sir;A#+DqaS+N$rE+Bl@C$WB5;Fmn86#6qa9HUMDp_`ntUmj7Y$C^sjve7J zA4eAtGq^$hJt`FPG%Gc9l*)ckkrRBrMlc8y6a!%;)=+xy01{<^3)MAQxQE(M#|w9{ zyjdCqbrr9Y_7F{0RH=_uXj|~;W4Jd~O{xJ3oH2C)UQpH#<@``jE!FUyltMBg9;wel zOR=RZfr%PHI$!?`v`;mH?Q4%5M4Iu5XDOpo#H?8t5KXwB_yWN(xPwl!YI!^x{l zgz%UuED@qQx`e+w8N!Zk4+n^&NXXJ1x{-oZyBz%2NW0V0Xh12cD9B}I(WS6JT@qwe zCI&a{@~u!|-ltR;_1CV=pzR5!YwUB^<;b34^I6uB$YSdh1%Os~!!=)$1yAGKp-=KRTavrTTb{R!pRAu3%M*ie?-JV$rR}^uzO? zQ4b(}8^8yiBu^|`@>^=)@@f(tXzd>)+)oja?nsiJo%+vetd%5byEF1jHSqK$`cCP+ za!sd^(_-zkc0lj8BsoouwUQ*#HdlIr>Q*)I{+M~L8n_bkr1J3O(a?-vu7=)$_9Sgf z`vlbvI4Ti6JxBG;YRpwc&vsyZ122xyNw@LGJx7%vp_6n3GB-LxJQEuo?}9DpXQP8J zl(U2M_k-emuj4jc`B|zQ%cduL2_i|ApQ4i(hm@l#W=>2d_xeUFbzdYJy* zv!#U3;wpZaJ}n7|S&T2FOUY3j;2b!+@}1p4NsPq@GGHUm_4-p#uAOfgE7@^qbKTk_g-7TcMagt`u{1 zMDR$^cIhJmd3?-?m%hC{vlH5JvV`e8oBT#9uRlyc8Deh>()zzbs(vG&Oseyrp3Cty zG>SUr>G?U|Qv*+*Lw(?FU9RWVz|$QUA9zTfaTOI){F_+pv}|p^-fGgnS(Mo6R|yuS z$4)1!2awJpA9#{HvDoQ!HE?NWS_)r2Ns{!~>D6kil_Y5^wU?=Zr%P?8^u9(7{KU|^ zElFn8SSv{)x$)_-ldA^aA2V-P16N|6RGxV3^d2?z4!D`LE&at#NexPmoj#(*Tt)O) z?4+81Vy{Z{5bJ1^4f_>l2(vd@=-sr0v(RO*o}rm0W&Mp4ldb5r=jNByG?B2+vW1Jy z%`XBYmCqjwR?`Wxw85(6OJxSDA)bj~^)MK@NWtp!ARZ4^e~znouqxr18mxxTrC?Qv z8HDx-NgU6*YFGK~Jlee#8}8!3SK7;dg4KFsXF`R+>SuybHHb@l2M~IZ|J$)}OCjjP zzFJ|t|LwO|iK7&P3i2QX;W1p`1fzdIk20{Rb2Ik9lKF4qA>M2-pSIpjD=d=o~!GQ)4Zjb_l8{C)ymBs%e+zX zj$Xpg286;G@YWU^^*XBO6?|Abz18AQ*+fClo!%a@>ulYwx~Nxx zv*xbT$_+iNO^l??hFfd6&7H>Do!-ctUe)Uw7McKQgwE@7T&+^WkFmOKyFgW+HRqe& zz!VO;i=xi%lNg%wTp!rt+euLn~>bhRD>u#Z} z@6|!h<=&{Fm4lx{-UfZ%1QlJ)Z8+kkA#bRp&ot%$Z|y#`4rS^%iJ>x0b^VaS*LHMIXc(d+TWo4hUs{L}iSr>8&Yi)wu>f4F<(a_`O`O zHocSTdeN?z3MIV;$w402pom!#0$~h$Yqf@J7s6ZKauedVrqrm^3V}$1 zuvQbZ*V(=oZot}B#G zN1G|rKzuA$3RQvHt;S#+6q?j1w-Z3u_^%aa%`yfDWq%P}p%bV(y#wA5v=Uf8R@e3g zKX(jx8%WR?2IpnQ(_E%kP zzF?ShMj8J@eh14oiT-M*qCwGaYS7{s2z3(s&3_ikc2V<5ySZp+zz2-y`UWa95~B5< zMh#u81$M6Kg8tkL9D?-NMYB2!8Sw4GMluvmV5oqeJYs@35JFt(`plSRd$EGk-HQBebnjytvJ zI8=)c3bE)Q5sN-NVIQDdv@^r92rP1YEpm$+-Wpk$8F|+fO{fI%2PEfr$YzQ6I3MLZ z{5@C&2!Cb9E(iL-UOhQ>GkAL>0&zt2${KBqz{m7ccbhOK<8fI26L|c)QTh89A*GXk_cAX!_U=lSaN3X>cN#%QUrkm3oJcSx+_5C?lpLcUJ zdQodQI-YHDa*myI3{1;`OFnnpdVBW3AItrP9{k^spd;cCzG!y99C+}Et+??l literal 0 HcmV?d00001 diff --git a/SRV/docs/_build/html/.doctrees/api/storage.doctree b/SRV/docs/_build/html/.doctrees/api/storage.doctree new file mode 100644 index 0000000000000000000000000000000000000000..6612d61268d0497be57e0b59d0a2dde5fbd39054 GIT binary patch literal 2856 zcmZuzU2hyU6wTLuWwV=)wy0_oR3RvVl3hSNAjAWL$B2C>sH#+cQ58|=Ksv!&i|gjeRny3)D>` zM0Kug(>1FUM{Y}SN&P4v6SqyQ)qI>=94#DWlP3mgyH+fSF)-r0AxT<(wUa!%8jMMN zrs*X3#DUOobZzOV?yP0c7~rAPnnt-&RLMI{6ktJz{L02nfkt*sw^KO0UUAIgj!oFv4 z*sg}T78vzVh$tv3%p#RWsNw~rK0z3%EM1y><$9r~fCbTPqIBwCxxN8mvj{o~t;rzZ z-O@q@EiBPFw=7zy8v9iXDw&Vmj;5K?HWKt11)Ljhn-NhD8*ZP@I8e02=4Rp4aE*kH z^C`CLpA#*CB0MxDDJ&h7ET-W|AwMpGSwv;55_oyn$?fFi>l4HAeYiRqQLUAZ7?Ftp zBS8b4Ew1d-Dm zO$u-)7;iwO?uMqZ(n$nKgErVEw2zT%#!60bM!><71shn?^+|43bY*fI99-)qd74Ev zAAxFN;Gm+GM1+95g{4qKDE*ZwGc-?BG)n`U_UJ5AhDOUP3yQ=xdd3Ye`MMh=+>o&d z2dT1bEp!6S?cgv5$|yvKpuNg1T68k^UiiW$hU!9?REB5?>fJ>jLY>MX8r*&m8)ze^ zV4>hCxBkbyk6k~bSp+}xVh?nKA+xE#W*tk$ zkWdoZb=w_*kZDvavpzs%vcQl_w`VS-C9{a}DHHgCyjwyA-tU-{KxMz?khB3%H;QpN zjD(6wc}cjVm=WZI!k1zL$_0d2eU@j~B0Nlr)?f7i+!L^HIROW%rf?9tgvrO(+;|$e zD-O z6xdtASbno7?6Gm3V6Yb%GJNjYlS{+y;s0aSHLkZ@t73I8=BRks9uZ4RkmSOzOmo>% z`XPDLHXSQbm%nhmdCgdRcUB^H7P}UU4p!o$wQ3t41Glcns%CK4qhZ8&2Z0s1PGThl zfW;YbU$lOx?61S&|2$ncFa1L(0Ixx43GX+9qL;hY&11#ft<6Q MRjnZ0;Q4s)FTEIq<^TWy literal 0 HcmV?d00001 diff --git a/SRV/docs/_build/html/.doctrees/api/storage/json_storage.doctree b/SRV/docs/_build/html/.doctrees/api/storage/json_storage.doctree new file mode 100644 index 0000000000000000000000000000000000000000..b06a7168383eb63d47373b7821c32f19b62e1045 GIT binary patch literal 31702 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 pLolUO){tmby-Zg#Y{ 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 literal 0 HcmV?d00001 diff --git a/SRV/docs/_build/html/.doctrees/api/utils/i18n.doctree b/SRV/docs/_build/html/.doctrees/api/utils/i18n.doctree new file mode 100644 index 0000000000000000000000000000000000000000..78bae8cc80d15ed78955580a8d3cd24e533eca1a GIT binary patch literal 13471 zcmeHOTWlOx8FqYG-|g6mDQc2xsY+|pc+I6qRj7Iq0#SvU-Uv~ljAv)hdS*R4Go4Ff zR|0bB&GaZy+d%?>sJOiF=B9DuHi>!Vja`W+9{XH@K!}IJ1K)qn%+A@hcjKhFv?@_- z@0`nj{`23?fByOQMBmTi`?|zGK2~y#D75UL;J76g#C(7ns>})FH{zL-@sHwZKIrSm z12^&w7V|EQFiguX`OJx*jm<9JA21`dT&FSK>@#y_|FiKlI{QK^w3*VyhvaJ3m+p6B z8ihf;&Y#)SLLUHQsiY1Mcr8PS((N}yg$?zsVg?e%-hXjbD!B`?&lB9 znQoQMRhiH15_9INy5-DyzFUC^g1JT4VfgdZlSk&jXfUUH)|}Yw9PPH?2iV1d!`)Gl zj|ZMD2nsx-C_?FnmH1G9v%a8eF z)h$If`&wR+F`tl)vr3zpn-60>GQVKXW}BNg4?u)a#~>uGFZ#M?D(vuL?RWK4wT;@v z+D83U{nb1^Y}VeZtx-emLT#=768>)F=|7%o=P>LfS}xT#>nCYqzV^r3di_Lg9bKEX z^Y}TJ$DfP!v-xl8LHNkyj~1joOx={F67$0LAC|h6+t=F)oTT+2OyIGdnt-BtZ{-jf z1oY)B=H0TY9vH-~R0l%(<0V$M9E&_kvn`lR>V~RY?QI#PSs|+`Bai;2yQ{1A8zE>C z`a4n+we>tHq;|f3ngoAnXg0sF$U-gjbtkZOS~HlQSvZuhzW^1!g^3%a;&vKM^mZ^0 z4ZkeS0?J*hpQ*3n_gTe6>KukqyD*f`KNm57<^2<{!5Y>;e+~Ss!=CUdzhJwDZnK4h zdBb&=EY@hCxv(w+!us|h|U`5kXvom-X^I~IFXVG z77C;>O{(a$aH6-sosjh=^z|ARJ0&$HJxefgv3?rrS;xQ)QcANMu95Oc{^ZOXLOkTS z#QeoN$8Jf|Ycv-WE@59-U>))}4H2!^-c@vr+0&__R0&@e8`%_6hMd+xT__U*t6fg0 zfd#2uQW^pV+1zEa)|A=v#KTNsNQ4MFSb9U|s}|4iyt28c@nt7Nv-t3XoyUh~%>5?f z$l)G%^bz6BLz$y0?wQ2)EP*3k#4XN!0LVOLkz&2`)x?zSHXrxjLz-Cb5% z-9s{$GW(AHUwJnq;@&{zQM?%!iL8eF5V@a&OgKB1`QGh(F%348TMyVc3oa*i9$bEm zpz=I|$`p@o+yULe&lRqpSGhKSlA1OjNRRMz^Er8NDN|opr*_V^vJDS5QUZn2M_}Uj z*TTfU4r#~^*qYdqs-!|1Z^>a4V((lFqhB7n>R_rY*-oszlqIDscl(htO)<>Ii!<5%yC1F&Ed~hwy?J6h~ zn%H1hx+sZ6s24Ho81CgA2Zu@4M1!A0fQuUO zge+2n@4lH;qg^~xA#h=cVl9IUtGT6N8)rtF(`q3&CeSNqsqxvOPXXII6M zoJs59k9wQy!2Hg_%K+FJ63Xk$b{b(Yo+3yDGDXKf`RLP-2R)a-aQ`9X`To4tQw1Pl2$4m9od5pb)&7*!H zkKHXv8EP0LbShPW?{)CMfvJLBltV7pU#-7L?4Cwp=mm7WC)9LqCM{(me8>_8RYyd@ zMKJ#>(2zwpFnR@Y=WXl3=g%HqzXZ#&7W_zjkX*{_NgZIY&yN_v};rR&bN4;rvxuN2-S1 z5bN4isDZ{yHMCcASw4-IiB0>J)I=YNkog2%(5`ed_>OWj z5WoX?NTE4ZYFkffh6UsTyl@8mg3oL%Lg%L~CoiD^%KPYnU>>iUZM_KLNT|v| zJ|ajEB7}QCd*|Q^YfC!gmZV zSM5WArxWOA08;|yA7gYUt$av}++O*2W38JY|HHdrga`SckWs6EcDG76mD1LZOjHxN z<#rO)0EtSlL;GB>xRfbo`EiYVoH%dq% zO18t>G%xnK&WlM?@)=Cg2~<-WXtFv8bi`v$2~Mgl zAsH>2VOp@wl|z!!JgMO;QZjS+)2Uq2EY8^u;^cc0tQ0U}g<}>wZUCni^YQefraOk| z`dek!PLoX=-lNG)1qx6LqQTy+Qe4>szIaZE(1`h1GoDNM;!~}hOO0eFLgfRvsZQuE zfYD&mkRBPfTO`O>-0bqX?F3x;1}%}}17$!3gcC@%hP2Q%G#GRk#1$K3MDt6~r|6KL zv)#)_($c>ucI2>j+BNi-48BJJ03olwOArU0ZYxj5R5Sv7(gT|fzAP1ZEF1<0iJ zk*!sW6yc$u!cc9Y(#T0s5J9zMJK#^tJF_9wVg#yWE38N?nc+(Ak*_50!Rx0tQ6xga zh7eH~QGO99mpTerO#m1SAnXtoz_MBZR(rQmOGn= zj+6QBC|JQT31L_H8=0bj#Z|&&a;H#2_UzNR%!nIA0BFoyQ97ZBf^a5Bf>v8RFo~6w zGa$(O;iV#0R?gC+IEK_`$DNg^%~js-xar5Sw0V{gnk$H%BS~laJb@93a|Mk-mZjLyCVZ`)B1uW?gP_(UwPfB4 z*#o26oVqlnCMojPyqE7)po1EKu!~4kB=WW|bVJ>a4|nmQbPOxuF{1T$@i8Ov3D>JN zP`o2)i$wGCfn?jjBu^0TSAdkuz6eUCnCFh@b_DoqnyB%d8--pJ#`EUbJRe+S4)gU8 z2Lm2TI`F`uUiI)W?7MCVRuSQsQRbhEk(3E+GeB6*5(W$eX@nwOtfgntc|PJZ!U}3OJH{Z+Za!q{cH^;+Pq1YRQVfyz2I-(a-dAG9Xc5be9oKz_5)9B~ zdKE2$P=~>2OT?e$KxP^cEiR=YI6b1UJbVAFIH~qGcWTV{U0*YGr(`prjdR2*4lzD1 zC9hEF6I5b;prxmUMCH%qS7VZH#zQ)Le{)C6WWJmB0N83^LPWMmlt=9Z2MUQ(me385 zI^W|n!}UvAiBYnJWe7Dr&vS6MG7e-,o*frEBTk3v_=ns_%-0p#jY)zca#3Big6 z3rD2y=r$1?7cIirN&8uo7qF=0YDnlYYByW<+<xooF-2RXtcccqiB3pEbkK3l7cAp*bjcE1~H+1=nAM zM@`X4!2m{TfF?sdp}5B|Z}#(Fybtmm^UFpHd1}eX-<^_&^Xg=>OUy4=EG&yP5D@0f zuxewm!B8*4i%OvxA0FTbV3Sp?;hb=DiPzHPRce**4py8{U)D@((X{ac`|Y(|GX0UD zifl@IE`nDF5Y!&=EFZN5flJ>XIAEB#Mi@}M^fyRZh!C@vA`kJNzEdILVloV@!g^f8 za>}rQBrZ%+pa`O3%P^}73^Uf!r8CkO8{rgj$7*2a@dXFb zkK^nC0PS+gT_la`jjXeg&p#m2>82B@&kB$`$FI-zMUz#~4Y8k^Yu zsA0imNq!KCY@wKvj+1g$TjV&H7L2o6bnS-yilKX38XKbrn)tVC901z}M6~ f+-MdyH*7hE9hJz&7s;1^k49`x!WbnWQL*qpZgyjE literal 0 HcmV?d00001 diff --git a/SRV/docs/_build/html/.doctrees/environment.pickle b/SRV/docs/_build/html/.doctrees/environment.pickle new file mode 100644 index 0000000000000000000000000000000000000000..592e9fc10d7b32fd15d25271c85763030441607e GIT binary patch literal 555390 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 vE8N_~tf3}`L2nBh{aYqD0IV_0r=ULvJB z{!fS)|4-(dZpuVa%n5(B(|rXE4-02IWI#R#fV+TEYZ4U-!?DEccff#>iQ8FD6Tc`; z(%j729XC7=Nz^1lU=nE0L^XCs8I)QsbJn>r zrsR@@SfVDs4S`9LMZCuAJSL~Jy~wx#=>MoWq2_@%#Ci-qg z1B^%u2~CvmlM0z6Ozu;nx^Wl4q3BPgcKHAVc9tT~aY$?+<()mjM>%PRA(}QPM`@q1 zEbBpPybMG#j09YUNYr6oNXH)PS?f|j*$RZ}I0C7flChPPJqK|q-g%8~3u zp9<_^-a!{(%z*g@nW@oIS@ebR@(2Qns>iA2BV>mVDkAKp6rj2nBJhUv8O}S|biUzo zwgN!YUbEXq_#NISdS5#{5s)Rw8Pv4aXd+AB?5W1#BhBJeWFiqTrycPb_U;9EY+-;C z_c1fSpC^Bdu2z{p}(fFN0`k6vh~`;c-qa zKK~#zqsr7eM^DP$;lBd;Qc~_5_sgTvZ zA@M9zA+gNQDzQYB;^?hNrIp{19j1ok z9yzoh9BL!=&ORdQl-9p!xY84kk@&^R4ASLz~Z5 zIt=DMXEn7K{(Sl0;DwJ@UNC#4wR&m!KcLk=t+eu1LTmT8%Wt6Frz-7WqofurW?F0d zi{<}@rXP))N|QY--J1LlEtr-7(CNI|gw@i{N>R1ApGd~FW?%pC1bS~wA~j+qYI7y2mYmk#{d8T literal 0 HcmV?d00001 diff --git a/SRV/docs/_build/html/.doctrees/index.doctree b/SRV/docs/_build/html/.doctrees/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..82be9c3627af14c0bf692208fe7cb7f0c776bf8d GIT binary patch literal 3996 zcmcgvTW=f36_zYfBz2)Ku?xeo45F7*X(F*fi=qeu6h&JEg%Ad?jr1v440nf|wRU&5 zGqW=FP#{j>)IujkifrH8e~=n+>o#%v1NyT1NBz$1g%q2(d8z?goHJ);&Ybgo-yFWz z`qLk`m)t+w3#6Z!Fw#ef47kp1hYT@KO#X5H@`L<0`GM^!c3;a(`8>Bv@bE<#1&Sy6 zySZ4hZOwfX%B1lZEwLur@8$=XY?;tRd~V8KfAj6(FYeyy-?{xx|L4_F%&n(2Sh9MT zPwDXIcs|U_z}8ZRFib9XY}aez7AtnG3U_E%qcGw5FdH$u&WusvC^NVwU27!_G_GLV ze#Eqf5#L)0i_34FAWLV15%W(JACpgJ+lEbODi=L*Lu`l_#frFTe>4y>=7X3k9tAuZ z#4Jn(sgn1Q9zB@IgyRoxJwTv(5GDbi9x09B_m8h+BV4?T+pvjZshH2#q{xdeioagG zSA5Yg{)Ml9D?TYc!{5inCl~Ku*naW2Uwn%3KQDe;d|o^$o?X0;nGcGm7k}tqJSd)E z24iiivzl^Gn0eX5hVh2n@lu<%WM zzKzdI_#7bd;(Ov%aZ|j4jIGv*N9NW`n9g9gT^sDx;xD-Lr|$Zcw`axE;z@neFQ1%+ z^^>n37UAHH(%psEb_MPDGEQZd1atK)sbRNuD#B!X#DbvWUE4_CC_c}X()TO#A#T`~ zgApJM7O2nx_h4u@SsD&BSEr#5(+Bww(1#@k!J~4#MPr@>sSFb{=iC+s!cXqb%U#Fe;-EO!$I}i3r7sh`H^=Y#Q*?i2V2p7{^ja zdD|=gjI#WlN>qGy@%!AaP9izV&+VEB1F#6+wyLcgLnq1N5m*-F$4hqI35=FWbOzsb zDAg<#Zde~BG~wq?g`X}8_Vyy!>~>Xy!~0-i%2m!5@%Pr{LIbb7sSgRtYgkgj)?Nks z{u_L39?ld-r5`nJ=JvZ@ipHTry^jMj95CHK=@CNixdwvhDeqB60Q_Dc-Ctr6k*q=Ej7{1dq-x zh3;OVv66`aGIP834vR8?sd@i%#06z ze5gTld-L+7$CZ-G6D$cLfFCfx-F%bV?K1OoyR_Ucx!qrwS=d56v%PZDSpfCYo0lgR zR@xo90Dz|j@)O}gx!sCbGRdGoNOyqo73hO~L2;;5;6Ypf8yrJ2IJ9e(lp!5qP@_St z*&fS`^kyb|B?Q*Cf-FwG29L<9Ge8`nQ( zztTa(H{NNe!xFFCUJzC^8}pR#AP&?QF0xSsocC;UF|pM8xDcUla8V= zv|E6BZ$JT?;qkKFC9(lF$pk2j!Zj_s!Ava@p2Gn36OdR2`u3;lkD4>UCT)@*QA>`cd=(EJIsVBmTi0}b; zS0hQd-_$V!%YL1K(i(|+!HvtI7fGL$50~Bd1==tlg)hm5EV~q9^+}fE2cqOeCQp|jC~TNo2|Bgsl8QfqpW12TBc0?_vi5eUu=&ba2o>%`;eAtMhx z0*S7Rdbet}fz@bS3fj3{;mIkrxVBSD3AhU+qBRJ?4dWT6Cg?q>z1>AnK$d~l0yTK9 zNy_csC2QIA!i?R<&0vl66#X*nZ8DbM55z~}L-9yF%xsse<}5{+)W3anE*_m{qEFvH z#2fceyph?h`O3Myw)_zFr(m#uX(58ver3?}meLPbL8ar7j2bXF*Q*Q0#=CoI$8LS9 zTeNp+e+aEIIhb>8aj$9yJqP5&M_&hw;BoO~6d}#H1745z&no-J$b$Iy@v?rH-v%Gh z+Jjm6X`ulDIu-qXPoHV9r?xf^-K>#4Y#P}ccj + + + + + + + api.v1.auth - GONETS SRV 0.0.3 documentation + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark, in light mode + + + + + + + + + + + + + + + Auto light/dark, in dark mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Skip to content + + + +

+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for api.v1.auth

+from fastapi import APIRouter, Depends, HTTPException, Request
+from fastapi.security import OAuth2PasswordBearer
+from services.auth_service import AuthService
+from models.auth import UserCreate, UserLogin, UserResponse, Token
+from storage.json_storage import JsonStorage
+from utils.i18n import get_translations, _
+from babel.support import Translations
+import logging
+import os
+
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger(__name__)
+
+router = APIRouter()
+auth_service = AuthService()
+oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/login")
+
+
+[docs] +@router.post("/register", response_model=UserResponse) +async def register(user: UserCreate, translations: Translations = Depends(get_translations())): + """Регистрирует нового пользователя. + + Создаёт нового пользователя на основе предоставленных данных и возвращает + информацию о зарегистрированном пользователе. + + Args: + user (UserCreate): Объект с данными нового пользователя (username, password, is_admin). + translations: Объект переводов для текущего языка. + + Returns: + UserResponse: Объект с данными зарегистрированного пользователя (id, username, is_admin). + + Raises: + HTTPException: Если пользователь уже существует (status_code=400) или произошла + внутренняя ошибка (status_code=500). + """ + try: + db_user = auth_service.register(user) + logger.info(f"Пользователь {user.username} успешно зарегистрирован") + return db_user + except HTTPException as e: + raise e + except Exception as e: + logger.error(f"Error in register: {type(e).__name__} - {str(e)}") + raise HTTPException(status_code=500, detail=translations.gettext("Не удалось зарегистрировать пользователя"))
+ + +
+[docs] +@router.post("/login", response_model=Token) +async def login(user: UserLogin, request: Request, translations: Translations = Depends(get_translations())): + """Авторизует пользователя и создаёт сессию. + + Проверяет учетные данные пользователя и возвращает токен и session_id для + последующей авторизации. + + Args: + user (UserLogin): Объект с данными для входа (username, password). + request (Request): Объект запроса для получения client_id (IP-адрес клиента). + translations: Объект переводов для текущего языка. + + Returns: + Token: Объект с access_token и session_id для авторизации. + + Raises: + HTTPException: Если учетные данные неверны (status_code=401) или произошла + внутренняя ошибка (status_code=500). + """ + try: + client_id = request.client.host + logger.debug(f"Попытка входа в систему клиента {client_id}") + result = auth_service.login(user, client_id) + logger.info(f"Успешная авторизация для клиента {client_id}, Идентификатор сессии: {result['session_id']}") + return result + except HTTPException as e: + raise e + except Exception as e: + logger.error(f"Ошибка при авторизации: {type(e).__name__} - {str(e)}") + raise HTTPException(status_code=500, detail=translations.gettext("Не удалось авторизовать пользователя"))
+ + +
+[docs] +@router.get("/me") +async def get_current_user(request: Request, translations: Translations = Depends(get_translations())): + """Возвращает данные текущего пользователя и токен. + + Проверяет активную сессию по client_id и возвращает информацию о пользователе + и токен для дальнейшего использования. + + Args: + request (Request): Объект запроса для получения client_id (IP-адрес клиента). + translations: Объект переводов для текущего языка. + + Returns: + dict: Словарь с ключами "user" (UserResponse), "access_token" (str) и + "token_type" (str, всегда "bearer"). + + Raises: + HTTPException: Если сессия не найдена или истекла (status_code=401) или + произошла внутренняя ошибка (status_code=500). + """ + try: + client_id = request.client.host + logger.debug(f"Запрос /me для клиента {client_id}") + result = auth_service.get_user_by_session(client_id) + return { + "user": result["user"], + "access_token": result["access_token"], + "token_type": translations.gettext("bearer"), + } + except HTTPException as e: + raise e + except Exception as e: + logger.error(f"Ошибка при get_current_user: {type(e).__name__} - {str(e)}") + raise HTTPException(status_code=500, detail=translations.gettext("Не удалось получить данные пользователя"))
+ + +
+[docs] +@router.get("/logout") +async def logout(request: Request, translations: Translations = Depends(get_translations())): + """Выход из системы, удаление сессии. + + Удаляет активную сессию пользователя по client_id. + + Args: + request (Request): Объект запроса для получения client_id (IP-адрес клиента). + translations: Объект переводов для текущего языка. + + Returns: + dict: Пустой словарь или сообщение об успешном выходе. + + Raises: + HTTPException: Если сессия не найдена (status_code=401) или произошла + внутренняя ошибка (status_code=500). + """ + try: + client_id = request.client.host + logger.debug(f"Запрос на выход из системы для клиента {client_id}") + return auth_service.logout(client_id) + except HTTPException as e: + raise e + except Exception as e: + logger.error(f"Не удалось выйти из системы: {type(e).__name__} - {str(e)}") + raise HTTPException(status_code=500, detail=translations.gettext("Не удалось выйти из системы"))
+ + +
+[docs] +@router.get("/check_json") +async def check_users_file(translations: Translations = Depends(get_translations())): + """Проверяет, существует ли файл users.json и содержит ли он пользователей. + + Возвращает информацию о состоянии файла и наличии пользователей. + + Returns: + dict: Словарь с ключами: + - "exists" (bool): Существует ли файл. + - "has_users" (bool): Содержит ли файл пользователей. + - "message" (str): Сообщение с описанием результата. + + Raises: + HTTPException: Если произошла ошибка при проверке файла (status_code=500). + """ + try: + file_path = "/etc/gonets/users.json" + storage = JsonStorage(file_path=file_path) + + if not os.path.exists(file_path): + logger.info(f"Файл {file_path} не существует") + return { + "exists": False, + "has_users": False, + "message": translations.gettext("Файл users.json не существует"), + } + + data = storage._read_data() + has_users = len(data.get("users", [])) > 0 + if has_users: + logger.info(f"Файл {file_path} существует и содержит пользователей") + return { + "exists": True, + "has_users": True, + "message": translations.gettext("Файл users.json существует и содержит пользователей"), + } + else: + logger.info(f"Файл {file_path} существует, но не содержит пользователей") + return { + "exists": True, + "has_users": False, + "message": translations.gettext("Файл users.json существует, но не содержит пользователей"), + } + except Exception as e: + logger.error(f"Ошибка при проверке файла: {type(e).__name__} - {str(e)}") + raise HTTPException(status_code=500, detail=translations.gettext("Ошибка при проверке файла"))
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/SRV/docs/_build/html/_modules/api/v1/ip.html b/SRV/docs/_build/html/_modules/api/v1/ip.html new file mode 100644 index 0000000..4ca4a98 --- /dev/null +++ b/SRV/docs/_build/html/_modules/api/v1/ip.html @@ -0,0 +1,435 @@ + + + + + + + + api.v1.ip - GONETS SRV 0.0.3 documentation + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark, in light mode + + + + + + + + + + + + + + + Auto light/dark, in dark mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Skip to content + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for api.v1.ip

+from typing import Dict, Any
+from fastapi import APIRouter, Body, HTTPException, Depends
+from services.ip_service import IPService
+from utils.i18n import get_translations
+from babel.support import Translations
+
+router = APIRouter()
+ip_service = IPService()
+
+
+[docs] +@router.post("/check_ip") +async def check_ip_config(data: Dict[str, Any] = Body(...), translations: Translations = Depends(get_translations())): + """Проверяет доступность IP-адреса перед его применением. + + Выполняет ARP-сканирование для проверки, занят ли указанный IP-адрес, + и возвращает результат проверки. + + Args: + data (Dict[str, Any]): Словарь с данными конфигурации, включая + "ipv4_updates" с полем "ipv4.address-data" для нового IP-адреса. + translations: Объект переводов для текущего языка. + + Returns: + Dict[str, Any]: Словарь с ключами: + - "available" (bool): Доступен ли IP-адрес. + - "message" (str): Сообщение о результате проверки. + + Raises: + HTTPException: Если произошла внутренняя ошибка (status_code=500). + """ + try: + result = await ip_service.check_ip_config(data, translations) + return result + except Exception as e: + raise HTTPException( + status_code=500, + detail=translations.gettext("Ошибка проверки IP конфигурации: {error}").format(error=str(e)) + )
+ + +
+[docs] +@router.post("/apply_ip") +async def apply_ip_config(data: Dict[str, Any] = Body(...), translations: Translations = Depends(get_translations())): + """Применяет проверенные IP-настройки к указанному сетевому профилю. + + Обновляет настройки соединения через NetworkManager, отключает и + переподключает устройство к сети. + + Args: + data (Dict[str, Any]): Словарь с данными конфигурации, включая + "connection_id" и "ipv4_updates" с обновлениями IP-настроек. + translations: Объект переводов для текущего языка. + + Returns: + dict: Словарь с ключами: + - "success" (bool): Успешность операции. + - "message" (str): Сообщение об успешном применении настроек. + + Raises: + HTTPException: Если подключение не найдено (status_code=404) или + произошла внутренняя ошибка (status_code=500). + """ + try: + await ip_service.apply_ip_config(data, translations) + return { + "success": True, + "message": translations.gettext("Настройки сетевого профиля для {connection_id} применены").format( + connection_id=data["connection_id"] + ), + } + except ValueError as e: + raise HTTPException(status_code=404, detail=translations.gettext(str(e))) + except Exception as e: + raise HTTPException( + status_code=500, + detail=translations.gettext("Ошибка проверки IP конфигурации: {error}").format(error=str(e)) + )
+ + +
+[docs] +@router.post("/save_ip") +async def save_ip_config(data: Dict[str, Any] = Body(...), translations: Translations = Depends(get_translations())): + """Применяет проверенные IP-настройки для отката сетевого профиля. + + Используется для сохранения и отката настроек сетевого профиля к предыдущему + состоянию через NetworkManager. + + Args: + data (Dict[str, Any]): Словарь с данными конфигурации, включая + "connection_id" и "ipv4_updates" с обновлениями IP-настроек. + translations: Объект переводов для текущего языка. + + Returns: + dict: Словарь с ключами: + - "success" (bool): Успешность операции. + - "message" (str): Сообщение об успешном применении настроек. + + Raises: + HTTPException: Если подключение не найдено (status_code=404) или + произошла внутренняя ошибка (status_code=500). + """ + try: + await ip_service.apply_ip_config(data, translations) + return { + "success": True, + "message": translations.gettext("Настройки сетевого профиля для {connection_id} применены").format( + connection_id=data["connection_id"] + ), + } + except ValueError as e: + raise HTTPException(status_code=404, detail=translations.gettext(str(e))) + except Exception as e: + raise HTTPException( + status_code=500, + detail=translations.gettext("Ошибка проверки IP конфигурации: {error}").format(error=str(e)) + )
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/SRV/docs/_build/html/_modules/api/v1/network.html b/SRV/docs/_build/html/_modules/api/v1/network.html new file mode 100644 index 0000000..a213c29 --- /dev/null +++ b/SRV/docs/_build/html/_modules/api/v1/network.html @@ -0,0 +1,558 @@ + + + + + + + + api.v1.network - GONETS SRV 0.0.3 documentation + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark, in light mode + + + + + + + + + + + + + + + Auto light/dark, in dark mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Skip to content + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for api.v1.network

+from fastapi import APIRouter, Body, HTTPException, Depends
+import logging
+from typing import Dict, Tuple, List, Union
+from services.network_service import NetworkService
+from models.network import (
+    NetworkInterfacesContainer,
+    Activation,
+    Deactivation,
+    NetworkInterface,
+    WiFiDevice,
+)
+from utils.i18n import get_translations, _
+from babel.support import Translations
+
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger(__name__)
+
+router = APIRouter()
+network_service = NetworkService()
+
+
+[docs] +class ContainerPositionManager: + """Класс для управления позициями и состоянием контейнеров. + + Хранит и обновляет координаты (x, y) и состояние (isOpen) для контейнеров + активации и деактивации. + """ + + def __init__(self): + """Инициализирует менеджер позиций контейнеров. + + Создаёт начальные позиции и состояния для контейнеров "activation" и + "deactivation" с координатами (0, 0) и закрытым состоянием (False). + """ + self.positions: Dict[str, Dict[str, Union[int, bool]]] = { + "activation": { + "x": 0, + "y": 0, + "isOpen": False + }, + "deactivation": { + "x": 0, + "y": 0, + "isOpen": False + }, + } + +
+[docs] + def get_position_and_state(self, container_name: str, translations: Translations = Depends(get_translations())) -> Tuple[int, int, bool]: + """Получает текущие координаты и состояние указанного контейнера. + + Args: + container_name (str): Имя контейнера ("activation" или "deactivation"). + translations: Объект переводов для текущего языка. + + Returns: + Tuple[int, int, bool]: Кортеж с координатами (x, y) и состоянием (isOpen). + + Raises: + ValueError: Если указан неверный идентификатор контейнера. + """ + container = self.positions.get(container_name) + if not container: + raise ValueError(translations.gettext("Неверное имя контейнера: {container_name}").format(container_name=container_name)) + return container["x"], container["y"], container["isOpen"]
+ + +
+[docs] + def update_position(self, container_id: int, x: int, y: int, isOpen: bool) -> None: + """Обновляет позицию и состояние контейнера по его идентификатору. + + Args: + container_id (int): Идентификатор контейнера (1 для "activation", 2 для "deactivation"). + x (int): Новая координата X. + y (int): Новая координата Y. + isOpen (bool): Новое состояние контейнера (открыт/закрыт). + + Raises: + ValueError: Если container_id не соответствует ни одному контейнеру. + """ + container_name = "activation" if container_id == 1 else "deactivation" + self.positions[container_name] = {"x": x, "y": y, "isOpen": isOpen}
+
+ + +container_manager = ContainerPositionManager() + +
+[docs] +async def get_network_data(translations: Translations = Depends(get_translations())) -> Dict[str, Dict]: + """Возвращает текущие сетевые данные в виде словаря. + + Получает данные о сетевых интерфейсах, разделяет их на активные и неактивные, + и возвращает их в формате Activation и Deactivation с учётом позиций и состояний + контейнеров. + + Args: + translations: Объект переводов для текущего языка. + + Returns: + Dict[str, Dict]: Словарь с ключами "activation" и "deactivation", содержащими + данные о соответствующих контейнерах. + + Raises: + HTTPException: Если произошла ошибка при получении данных (status_code=500). + """ + logger.debug("Calling get_network_data()") + try: + network_data = await network_service.get_network_interfaces_container() + + activation_interfaces, deactivation_interfaces = _split_interfaces(network_data.interfaces) + + activation_x, activation_y, activation_is_open = (container_manager.get_position_and_state("activation")) + deactivation_x, deactivation_y, deactivation_is_open = (container_manager.get_position_and_state("deactivation")) + + activation = Activation( + id=1, + interfaces=activation_interfaces, + container_x=activation_x, + container_y=activation_y, + isOpen=activation_is_open, + ) + deactivation = Deactivation( + id=2, + interfaces=deactivation_interfaces, + container_x=deactivation_x, + container_y=deactivation_y, + isOpen=deactivation_is_open, + ) + + return { + "activation": activation.model_dump(), + "deactivation": deactivation.model_dump(), + } + except Exception as e: + logger.error(f"Error in get_network_data: {e}") + raise HTTPException(status_code=500, detail=translations.gettext("Не удалось получить сетевые данные"))
+ + +def _split_interfaces( + interfaces: List[Union[NetworkInterface, WiFiDevice]], +) -> Tuple[List[Union[NetworkInterface, WiFiDevice]], List[Union[NetworkInterface, WiFiDevice]]]: + """Разделяет интерфейсы на активные и неактивные. + + Args: + interfaces (List[Union[NetworkInterface, WiFiDevice]]): Список сетевых интерфейсов. + + Returns: + Tuple[List[Union[NetworkInterface, WiFiDevice]], List[Union[NetworkInterface, WiFiDevice]]]: + Кортеж из списков активных и неактивных интерфейсов. + """ + activation_interfaces = [] + deactivation_interfaces = [] + for interface in interfaces: + if interface.device_state == "100": + activation_interfaces.append(interface) + else: + deactivation_interfaces.append(interface) + return activation_interfaces, deactivation_interfaces + +
+[docs] +@router.post("/update_container_position") +async def update_container_position(data: Dict[str, Union[int, bool]] = Body(...), translations: Translations = Depends(get_translations())): + """Обновляет позицию и состояние контейнера. + + Принимает данные о новом положении и состоянии контейнера и обновляет их + в менеджере позиций. + + Args: + data (Dict[str, Union[int, bool]]): Словарь с ключами: + - "container_id" (int): Идентификатор контейнера (1 или 2). + - "container_x" (int): Новая координата X. + - "container_y" (int): Новая координата Y. + - "isOpen" (bool): Новое состояние контейнера (открыт/закрыт). + translations: Объект переводов для текущего языка. + + Returns: + dict: Словарь с ключами: + - "success" (bool): Успешность операции. + - "message" (str): Сообщение об успешном обновлении. + + Raises: + HTTPException: Если отсутствуют обязательные поля (status_code=400) или + типы данных неверны. + """ + try: + required_keys = [ + "container_id", "container_x", "container_y", "isOpen" + ] + if not all(key in data for key in required_keys): + raise ValueError( + translations.gettext("Отсутствуют обязательные поля: container_id, container_x, container_y, isOpen") + ) + + container_id = data["container_id"] + container_x = data["container_x"] + container_y = data["container_y"] + is_open = data["isOpen"] + + if (not isinstance(container_id, int) + or not isinstance(container_x, int) + or not isinstance(container_y, int) + or not isinstance(is_open, bool)): + raise ValueError(translations.gettext("Неверный тип данных в запросе")) + + container_manager.update_position(container_id, container_x, container_y, is_open) + return { + "success": True, + "message": translations.gettext("Позиция и состояние контейнера обновлены") + } + except Exception as e: + logger.error(f"Error updating container position: {e}") + raise HTTPException(status_code=400, detail=translations.gettext("Не удалось обновить положение контейнера: {error}").format(error=str(e)))
+ + +
+[docs] +@router.get("/get_hostname") +async def get_hostname(translations: Translations = Depends(get_translations())): + """Возвращает имя хоста системы. + + Выполняет запрос к сервису для получения имени хоста. + + Args: + translations: Объект переводов для текущего языка. + + Returns: + dict: Словарь с ключом "hostname" и значением имени хоста. + + Raises: + HTTPException: Если произошла ошибка при получении имени хоста (status_code=500). + """ + try: + hostname_info = await network_service.get_hostname() + return hostname_info + except Exception as e: + logger.error(f"Error in get_hostname: {e}") + raise HTTPException(status_code=500, detail=translations.gettext("Не удалось получить имя хоста"))
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/SRV/docs/_build/html/_modules/api/v1/settings.html b/SRV/docs/_build/html/_modules/api/v1/settings.html new file mode 100644 index 0000000..a3a6f4e --- /dev/null +++ b/SRV/docs/_build/html/_modules/api/v1/settings.html @@ -0,0 +1,545 @@ + + + + + + + + api.v1.settings - GONETS SRV 0.0.3 documentation + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark, in light mode + + + + + + + + + + + + + + + Auto light/dark, in dark mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Skip to content + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for api.v1.settings

+import os
+import logging
+import subprocess
+from typing import Optional
+from fastapi import APIRouter, HTTPException, Depends, Response, Request, Body
+from starlette.responses import FileResponse
+from services.settings_service import SettingsService
+from utils.i18n import get_translations
+from babel.support import Translations
+from models.subnet import SubnetScanRequest  # Предполагается, что модель определена
+
+logging.basicConfig(level=logging.DEBUG)
+logger = logging.getLogger(__name__)
+
+router = APIRouter()
+settings_service = SettingsService()
+
+
+[docs] +@router.post("/clear_subnet") +def clear_subnet(subnet: Optional[str] = None, translations: Translations = Depends(get_translations())): + """Очищает данные узлов подсети, сохранённые в devices.json. + + Удаляет все записи об узлах подсети, оставляя файл пустым. + + Args: + subnet (Optional[str]): Подсеть в формате CIDR (например, "10.1.1.0/24"). + Если None, очищает все подсети. + translations: Объект переводов для текущего языка. + + Returns: + dict: Словарь с сообщением об успешной очистке. + + Raises: + HTTPException: Если произошла ошибка очистки (status_code=500). + """ + try: + success = settings_service.clear_subnet(subnet) + if success: + if subnet: + return {"message": translations.gettext("Данные узлов подсети {subnet} успешно очищены").format(subnet=subnet)} + else: + return {"message": translations.gettext("Данные всех подсетей успешно очищены")} + else: + raise HTTPException(status_code=500, detail=translations.gettext("Не удалось очистить данные узлов подсети")) + except Exception as e: + logger.error(f"Error clearing subnet data: {str(e)}") + raise HTTPException(status_code=500, detail=translations.gettext("Ошибка очистки: {error}").format(error=str(e)))
+ + +
+[docs] +@router.post("/clear_wifi") +def clear_wifi(translations: Translations = Depends(get_translations())): + """Очищает данные точек доступа Wi-Fi, сохранённые в wifi.json. + + Удаляет все записи о точках доступа, оставляя файл пустым. + + Args: + translations: Объект переводов для текущего языка. + + Returns: + dict: Словарь с сообщением об успешной очистке. + + Raises: + HTTPException: Если произошла ошибка очистки (status_code=500). + """ + try: + success = settings_service.clear_wifi() + if success: + return {"message": translations.gettext("Данные точек доступа Wi-Fi успешно очищены")} + else: + raise HTTPException(status_code=500, detail=translations.gettext("Не удалось очистить данные точек доступа Wi-Fi")) + except Exception as e: + logger.error(f"Error clearing wifi data: {str(e)}") + raise HTTPException(status_code=500, detail=translations.gettext("Ошибка очистки: {error}").format(error=str(e)))
+ + +
+[docs] +@router.post("/clear_all_data") +def clear_all_data(translations: Translations = Depends(get_translations())): + """Очищает данные узлов подсети и точек доступа Wi-Fi в обоих файлах. + + Удаляет все записи из devices.json и wifi.json. + + Args: + translations: Объект переводов для текущего языка. + + Returns: + dict: Словарь с сообщением об успешной очистке. + + Raises: + HTTPException: Если произошла ошибка очистки (status_code=500). + """ + try: + success = settings_service.clear_all_data() + if success: + return {"message": translations.gettext("Все сетевые данные успешно очищены")} + else: + raise HTTPException(status_code=500, detail=translations.gettext("Не удалось очистить все сетевые данные")) + except Exception as e: + logger.error(f"Error clearing all data: {str(e)}") + raise HTTPException(status_code=500, detail=translations.gettext("Ошибка очистки: {error}").format(error=str(e)))
+ + +WIFI_PATH = "storage/wifi/wifi.json" + +
+[docs] +@router.post("/export_devices", response_class=FileResponse) +async def export_devices(request: SubnetScanRequest = Body(...), translations: Translations = Depends(get_translations())): + """ + Экспортирует файл с данными устройств в формате JSON для указанной подсети. + + Args: + request (SubnetScanRequest): Объект с полем subnet в формате CIDR. + translations: Объект переводов для текущего языка. + + Returns: + FileResponse: Файл <subnet>.json для скачивания. + + Raises: + HTTPException: Если файл не найден (status_code=404). + """ + subnet = request.subnet + file_path = f"storage/subnets/{subnet.replace('/', '_')}.json" + if not os.path.exists(file_path): + logger.warning(f"Export file not found: {file_path}") + raise HTTPException(status_code=404, detail=translations.gettext("Файл для подсети {subnet} не найден").format(subnet=subnet)) + logger.info(f"Exporting file: {file_path}") + return FileResponse( + path=file_path, + filename=f"{subnet.replace('/', '_')}.json", + media_type="application/json" + )
+ + +
+[docs] +@router.get("/export_wifi", response_class=FileResponse) +async def export_wifi(translations: Translations = Depends(get_translations())): + """ + Экспортирует файл с данными устройств в формате JSON. + + Args: + translations: Объект переводов для текущего языка. + + Returns: + FileResponse: Файл wifi.json для скачивания. + + Raises: + HTTPException: Если файл не найден (status_code=404). + """ + if not os.path.exists(WIFI_PATH): + logger.warning(f"Export file not found: {WIFI_PATH}") + raise HTTPException(status_code=404, detail=translations.gettext("Файл не найден")) + logger.info(f"Exporting file: {WIFI_PATH}") + return FileResponse( + path=WIFI_PATH, + filename="wifi.json", + media_type="application/json" + )
+ + +
+[docs] +@router.post("/set_language") +def set_language(locale: str, response: Response, translations: Translations = Depends(get_translations())): + """ + Устанавливает куку locale для смены языка пользователя. + + Args: + locale (str): Используемый язык. + response (Response): Запрос. + translations: Объект переводов текущего языка. + + Returns: + dict: Сообщение об успешной установке языка. + """ + lc_messages_path = os.path.join(os.path.dirname(__file__), '..', '..', 'locales', locale, 'LC_MESSAGES') + if not os.path.isdir(lc_messages_path): + raise HTTPException( + status_code=400, + detail=translations.gettext("Язык '{locale}' не поддерживается.").format(locale=locale) + ) + has_mo_or_po = any(f.endswith('.mo') or f.endswith('.po') for f in os.listdir(lc_messages_path)) + if not has_mo_or_po: + raise HTTPException( + status_code=400, + detail=translations.gettext("Язык '{locale}' не поддерживается (нет файлов переводов).").format(locale=locale) + ) + response.set_cookie(key="locale", value=locale, path="/", httponly=True) + return {"message": translations.gettext("Язык успешно установлен: {locale}").format(locale=locale)}
+ + +
+[docs] +@router.get("/get_system_language") +async def get_system_language(request: Request, translations: Translations = Depends(get_translations())): + """Выводит текущий язык, установленный в системе на основе locale. + + Выполняет команду 'locale' в системе и извлекает параметры LANG + + Args: + request (Request): Объект запроса для доступа к параметрам. + translations: Объект переводов текущего языка. + + Returns: + dict: Словарь с текущим языком системы. + + Raises: + HTTPException: Если не удалось извлечь системный язык (status_code=500). + """ + try: + result = subprocess.run(['locale'], capture_output=True, text=True, check=True) + output = result.stdout + lang = next((line.split('=')[1].strip() for line in output.splitlines() if line.startswith('LANG=')), None) + if lang: + return {"language": lang} + else: + logger.warning(f"No locale found: {output}") + return {"language": 'unknown'} + except subprocess.CalledProcessError as e: + logger.error(f"Error executing locale command: {e}") + raise HTTPException(status_code=500, detail=translations.gettext("Не удалось определить язык в системе")) + except Exception as e: + logger.error(f"Unexpected error getting system language: {str(e)}") + raise HTTPException(status_code=500, detail=translations.gettext("Ошибка при определении языка в системе {error}").format(error=str(e)))
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/SRV/docs/_build/html/_modules/api/v1/subnet.html b/SRV/docs/_build/html/_modules/api/v1/subnet.html new file mode 100644 index 0000000..22cf03d --- /dev/null +++ b/SRV/docs/_build/html/_modules/api/v1/subnet.html @@ -0,0 +1,700 @@ + + + + + + + + api.v1.subnet - GONETS SRV 0.0.3 documentation + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark, in light mode + + + + + + + + + + + + + + + Auto light/dark, in dark mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Skip to content + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for api.v1.subnet

+from fastapi import APIRouter, HTTPException, Body, Depends
+from models.subnet import SubnetScanResult, SubnetScanRequest
+from models.wifi import WifiScanResult
+from models.request import NoteRequest, WifiConnectRequest, WifiDisconnectRequest
+from services.subnet_service import SubnetService, get_subnet_service
+from services.wifi_service import WifiService
+from services.connect_service import WifiConnectionService
+from utils.i18n import get_translations, _
+from babel.support import Translations
+import logging
+
+router = APIRouter()
+
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger(__name__)
+
+
+[docs] +@router.post("/devices", response_model=SubnetScanResult) +def scan_subnet(request: SubnetScanRequest = Body(...), subnet_service: SubnetService = Depends(get_subnet_service), translations: Translations = Depends(get_translations())): + """Сканирует локальную подсеть и возвращает данные об устройствах. + + Выполняет сканирование указанной подсети для обнаружения активных устройств. + + Args: + request (SubnetScanRequest): Объект с полем subnet. + subnet_service: Сервис сканирования подсети. + translations: Объект переводов для текущего языка. + + Returns: + SubnetScanResult: Объект с результатами сканирования (устройства и их данные). + + Raises: + HTTPException: Если произошла ошибка сканирования (status_code=500). + """ + try: + # Если subnet пустая строка, определяем автоматически + subnet = request.subnet.strip() if request.subnet else None + logger.info(f"Requested subnet scan for: {subnet or 'auto-detected'}") + result = subnet_service.scan_subnet(subnet) + if not result.devices and result.error: + logger.warning(f"No devices found in subnet {subnet or 'auto-detected'}") + return result + except Exception as e: + logger.error(f"Error in scan_subnet: {str(e)}") + raise HTTPException(status_code=500, detail=translations.gettext("Ошибка сканирования подсети: {error}").format(error=str(e)))
+ + +
+[docs] +@router.get("/wifi", response_model=WifiScanResult) +def scan_wifi(timeout: int = 30, translations: Translations = Depends(get_translations())): + """Сканирует Wi-Fi точки доступа и возвращает данные о них. + + Выполняет сканирование доступных Wi-Fi сетей с указанным таймаутом. + + Args: + timeout (int, optional): Таймаут сканирования в секундах. По умолчанию 30. + translations: Объект переводов для текущего языка. + + Returns: + WifiScanResult: Объект с результатами сканирования (список Wi-Fi сетей). + + Raises: + HTTPException: Если произошла ошибка сканирования Wi-Fi (status_code=500). + """ + try: + wifi_service = WifiService() + result = wifi_service.scan_wifi(timeout=timeout) + return result + except Exception as e: + raise HTTPException(status_code=500, detail=translations.gettext("Ошибка сканирования Wi-Fi: {error}").format(error=str(e)))
+ + +
+[docs] +@router.post("/devices/{mac}/note") +def add_note(mac: str, request: NoteRequest, subnet_service: SubnetService = Depends(get_subnet_service), translations: Translations = Depends(get_translations())): + """Добавляет заметку для устройства по MAC-адресу. + + Привязывает заметку к устройству, идентифицируемому по MAC-адресу. + + Args: + mac (str): MAC-адрес устройства. + request (NoteRequest): Объект с полем "note" для текста заметки. + subnet_service: Сервис сканирования подсети. + translations: Объект переводов для текущего языка. + + Returns: + dict: Словарь с сообщением об успешном добавлении заметки. + + Raises: + HTTPException: Если устройство не найдено (status_code=404), заметка уже + существует (status_code=400), или произошла внутренняя ошибка (status_code=500). + """ + try: + success, result = subnet_service.add_note(mac, request.note, translations) + if not success: + if result[0] == "not_found": + raise HTTPException(status_code=404, detail=translations.gettext("Устройство с MAC {mac} не найдено").format(mac=mac)) + elif result[0] == "note_exists": + raise HTTPException(status_code=400, detail=translations.gettext("Заметка для устройства с MAC {mac} уже существует").format(mac=mac)) + else: + raise HTTPException(status_code=500, detail=translations.gettext("Внутренняя ошибка сервера: {error}").format(error=result[1]["error"])) + return {"message": translations.gettext("Заметка '{note}' добавлена для устройства {mac}").format(note=request.note, mac=mac)} + except HTTPException as e: + raise e + except Exception as e: + raise HTTPException(status_code=500, detail=translations.gettext("Ошибка добавления заметки: {error}").format(error=str(e)))
+ + +
+[docs] +@router.put("/devices/{mac}/note") +def update_note(mac: str, request: NoteRequest, subnet_service: SubnetService = Depends(get_subnet_service), translations: Translations = Depends(get_translations())): + """Обновляет заметку для устройства по MAC-адресу. + + Изменяет существующую заметку, привязанную к устройству. + + Args: + mac (str): MAC-адрес устройства. + request (NoteRequest): Объект с полем "note" для нового текста заметки. + subnet_service: Сервис сканирования подсети. + translations: Объект переводов для текущего языка. + + Returns: + dict: Словарь с сообщением об успешном обновлении заметки. + + Raises: + HTTPException: Если устройство не найдено (status_code=404), заметка не + существует (status_code=400), или произошла внутренняя ошибка (status_code=500). + """ + try: + success, result = subnet_service.update_note(mac, request.note, translations) + if not success: + if result[0] == "not_found": + raise HTTPException(status_code=404, detail=translations.gettext("Устройство с MAC {mac} не найдено").format(mac=mac)) + elif result[0] == "note_not_exists": + raise HTTPException(status_code=400, detail=translations.gettext("Заметка для устройства с MAC {mac} не существует").format(mac=mac)) + else: + raise HTTPException(status_code=500, detail=translations.gettext("Внутренняя ошибка сервера: {error}").format(error=result[1]["error"])) + return {"message": translations.gettext("Заметка '{note}' обновлена для устройства {mac}").format(note=request.note, mac=mac)} + except HTTPException as e: + raise e + except Exception as e: + raise HTTPException(status_code=500, detail=translations.gettext("Ошибка обновления заметки: {error}").format(error=str(e)))
+ + +
+[docs] +@router.delete("/devices/{mac}/note") +def delete_note(mac: str, subnet_service: SubnetService = Depends(get_subnet_service), translations: Translations = Depends(get_translations())): + """Удаляет заметку для устройства по MAC-адресу. + + Удаляет привязанную заметку для указанного устройства. + + Args: + mac (str): MAC-адрес устройства. + subnet_service: Сервис сканирования подсети. + translations: Объект переводов для текущего языка. + + Returns: + dict: Словарь с сообщением об успешном удалении заметки. + + Raises: + HTTPException: Если устройство не найдено (status_code=404), заметка не + существует (status_code=400), или произошла внутренняя ошибка (status_code=500). + """ + try: + success, result = subnet_service.delete_note(mac, translations) + if not success: + if result[0] == "not_found": + raise HTTPException(status_code=404, detail=translations.gettext("Устройство с MAC {mac} не найдено").format(mac=mac)) + elif result[0] == "note_not_exists": + raise HTTPException(status_code=400, detail=translations.gettext("Заметка для устройства с MAC {mac} не существует").format(mac=mac)) + else: + raise HTTPException(status_code=500, detail=translations.gettext("Внутренняя ошибка сервера: {error}").format(error=result[1]["error"])) + return {"message": translations.gettext("Заметка удалена для устройства {mac}").format(mac=mac)} + except HTTPException as e: + raise e + except Exception as e: + raise HTTPException(status_code=500, detail=translations.gettext("Ошибка удаления заметки: {error}").format(error=str(e)))
+ + +
+[docs] +@router.post("/wifi/{ssid}/note") +def add_wifi_note(ssid: str, request: NoteRequest, translations: Translations = Depends(get_translations())): + """Добавляет заметку для сети Wi-Fi по SSID. + + Привязывает заметку к Wi-Fi сети, идентифицируемой по SSID. + + Args: + ssid (str): Идентификатор Wi-Fi сети. + request (NoteRequest): Объект с полем "note" для текста заметки. + translations: Объект переводов для текущего языка. + + Returns: + dict: Словарь с сообщением об успешном добавлении заметки. + + Raises: + HTTPException: Если сеть не найдена (status_code=404), заметка уже + существует (status_code=400), или произошла внутренняя ошибка (status_code=500). + """ + try: + wifi_service = WifiService() + success, result = wifi_service.add_note(ssid, request.note) + if not success: + if result[0] == "not_found": + raise HTTPException(status_code=404, detail=translations.gettext("Сеть с SSID {ssid} не найдена").format(ssid=ssid)) + elif result[0] == "note_exists": + raise HTTPException(status_code=400, detail=translations.gettext("Заметка для сети с SSID {ssid} уже существует").format(ssid=ssid)) + else: + raise HTTPException(status_code=500, detail=translations.gettext("Внутренняя ошибка сервера: {error}").format(error=result[1]["error"])) + return {"message": translations.gettext("Заметка '{note}' добавлена для сети с SSID {ssid}").format(note=request.note, ssid=ssid)} + except HTTPException as e: + raise e + except Exception as e: + raise HTTPException(status_code=500, detail=translations.gettext("Ошибка добавления заметки: {error}").format(error=str(e)))
+ + +
+[docs] +@router.put("/wifi/{ssid}/note") +def update_wifi_note(ssid: str, request: NoteRequest, translations: Translations = Depends(get_translations())): + """Обновляет заметку для сети Wi-Fi по SSID. + + Изменяет существующую заметку, привязанную к Wi-Fi сети. + + Args: + ssid (str): Идентификатор Wi-Fi сети. + request (NoteRequest): Объект с полем "note" для нового текста заметки. + translations: Объект переводов для текущего языка. + + Returns: + dict: Словарь с сообщением об успешном обновлении заметки. + + Raises: + HTTPException: Если сеть не найдена (status_code=404), заметка не + существует (status_code=400), или произошла внутренняя ошибка (status_code=500). + """ + try: + wifi_service = WifiService() + success, result = wifi_service.update_note(ssid, request.note) + if not success: + if result[0] == "not_found": + raise HTTPException(status_code=404, detail=translations.gettext("Сеть с SSID {ssid} не найдена").format(ssid=ssid)) + elif result[0] == "note_not_exists": + raise HTTPException(status_code=400, detail=translations.gettext("Заметка для сети с SSID {ssid} не существует").format(ssid=ssid)) + else: + raise HTTPException(status_code=500, detail=translations.gettext("Внутренняя ошибка сервера: {error}").format(error=result[1]["error"])) + return {"message": translations.gettext("Заметка '{note}' обновлена для сети с SSID {ssid}").format(note=request.note, ssid=ssid)} + except HTTPException as e: + raise e + except Exception as e: + raise HTTPException(status_code=500, detail=translations.gettext("Ошибка обновления заметки: {error}").format(error=str(e)))
+ + +
+[docs] +@router.delete("/wifi/{ssid}/note") +def delete_wifi_note(ssid: str, translations: Translations = Depends(get_translations())): + """Удаляет заметку для сети Wi-Fi по SSID. + + Удаляет привязанную заметку для указанной Wi-Fi сети. + + Args: + ssid (str): Идентификатор Wi-Fi сети. + translations: Объект переводов для текущего языка. + + Returns: + dict: Словарь с сообщением об успешном удалении заметки. + + Raises: + HTTPException: Если сеть не найдена (status_code=404), заметка не + существует (status_code=400), или произошла внутренняя ошибка (status_code=500). + """ + try: + wifi_service = WifiService() + success, result = wifi_service.delete_note(ssid) + if not success: + if result[0] == "not_found": + raise HTTPException(status_code=404, detail=translations.gettext("Сеть с SSID {ssid} не найдена").format(ssid=ssid)) + elif result[0] == "note_not_exists": + raise HTTPException(status_code=400, detail=translations.gettext("Заметка для сети с SSID {ssid} не существует").format(ssid=ssid)) + else: + raise HTTPException(status_code=500, detail=translations.gettext("Внутренняя ошибка сервера: {error}").format(error=result[1]["error"])) + return {"message": translations.gettext("Заметка удалена для сети с SSID {ssid}").format(ssid=ssid)} + except HTTPException as e: + raise e + except Exception as e: + raise HTTPException(status_code=500, detail=translations.gettext("Ошибка удаления заметки: {error}").format(error=str(e)))
+ + +
+[docs] +@router.post("/wifi/connect") +def connect_to_wifi(request: WifiConnectRequest, translations: Translations = Depends(get_translations())): + """Подключается к Wi-Fi сети по SSID и паролю. + + Выполняет подключение к указанной Wi-Fi сети с использованием предоставленных + учетных данных. + + Args: + request (WifiConnectRequest): Объект с полями "ssid" и "password". + translations: Объект переводов для текущего языка. + + Returns: + dict: Словарь с сообщением об успешном подключении. + + Raises: + HTTPException: Если подключение не удалось (status_code=400). + """ + try: + wifi_service = WifiConnectionService() + success, message = wifi_service.connect(request.ssid, request.password) + if not success: + raise HTTPException(status_code=400, detail=translations.gettext(message)) + return {"message": translations.gettext("Успешно подключено к {ssid}").format(ssid=request.ssid)} + except HTTPException as e: + raise e + except Exception as e: + raise HTTPException(status_code=500, detail=translations.gettext("Ошибка подключения к Wi-Fi: {error}").format(error=str(e)))
+ + +
+[docs] +@router.post("/wifi/disconnect") +def disconnect_from_wifi(request: WifiDisconnectRequest, translations: Translations = Depends(get_translations())): + """Отключается от Wi-Fi сети по указанному SSID. + + Выполняет отключение от указанной Wi-Fi сети. + + Args: + request (WifiDisconnectRequest): Объект с полем "ssid". + translations: Объект переводов для текущего языка. + + Returns: + dict: Словарь с сообщением об успешном отключении. + + Raises: + HTTPException: Если отключение не удалось (status_code=400). + """ + try: + wifi_service = WifiConnectionService() + success, message = wifi_service.disconnect(request.ssid) + if not success: + raise HTTPException(status_code=400, detail=translations.gettext(message)) + return {"message": translations.gettext(message or "Успешно отключено от {ssid}").format(ssid=request.ssid)} + except HTTPException as e: + raise e + except Exception as e: + raise HTTPException(status_code=500, detail=translations.gettext("Ошибка отключения от Wi-Fi: {error}").format(error=str(e)))
+ + +
+[docs] +@router.get("/wifi/status") +def get_wifi_status(translations: Translations = Depends(get_translations())): + """Проверяет статус текущего подключения к Wi-Fi. + + Возвращает информацию о текущем состоянии подключения и SSID, если подключение активно. + + Args: + translations: Объект переводов для текущего языка. + + Returns: + dict: Словарь с ключами: + - "status" (str): Состояние ("connected" или другое). + - "ssid" (str, optional): SSID текущей сети, если подключено. + + Raises: + HTTPException: Если проверка статуса не удалась (status_code=400). + """ + try: + wifi_service = WifiConnectionService() + success, message = wifi_service.get_status() + if not success: + raise HTTPException(status_code=400, detail=translations.gettext(message)) + return {"status": translations.gettext("connected"), "ssid": message} + except HTTPException as e: + raise e + except Exception as e: + raise HTTPException(status_code=500, detail=translations.gettext("Ошибка проверки статуса Wi-Fi: {error}").format(error=str(e)))
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/SRV/docs/_build/html/_modules/api/v1/ufw.html b/SRV/docs/_build/html/_modules/api/v1/ufw.html new file mode 100644 index 0000000..71382f2 --- /dev/null +++ b/SRV/docs/_build/html/_modules/api/v1/ufw.html @@ -0,0 +1,549 @@ + + + + + + + + api.v1.ufw - GONETS SRV 0.0.3 documentation + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark, in light mode + + + + + + + + + + + + + + + Auto light/dark, in dark mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Skip to content + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for api.v1.ufw

+from fastapi import APIRouter, HTTPException
+from typing import List
+from pydantic import BaseModel, Field
+
+from models.ufw import UFWRulesResponse, UFWStatus, UFWRule
+from services.ufw_service import UFWService
+
+router = APIRouter()
+
+
+
+[docs] +class RuleNumbers(BaseModel): + numbers: List[int] = Field( + ..., description="Список номеров правил для удаления")
+ + + +
+[docs] +@router.get("/rules", response_model=UFWRulesResponse) +async def get_ufw_rules(): + """Возвращает все правила UFW. + + Осуществляет обращение к сервису `UFWService` для чтения текущего списка + правил брандмауэра и возвращает результат в сериализованном виде для клиента. + + Returns: + UFWRulesResponse: Структура с коллекцией правил и сопутствующей + информацией о состоянии. + + Raises: + HTTPException: В случае ошибок взаимодействия с UFW (status_code=500). + """ + ufw_service = UFWService() + try: + return ufw_service.get_ufw_rules() + except Exception as e: + raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
+ + + +
+[docs] +@router.get("/status", response_model=UFWStatus) +async def get_ufw_status(): + """Возвращает текущий статус UFW. + + Returns: + UFWStatus: Объект со сведениями о включенности UFW и деталями статуса. + + Raises: + HTTPException: Если не удалось получить статус (status_code=500). + """ + ufw_service = UFWService() + try: + return ufw_service.get_ufw_status() + except Exception as e: + raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
+ + + +
+[docs] +@router.post("/enable") +async def enable_ufw(): + """Включает UFW. + + Returns: + dict: Словарь с ключами `status` и `message` при успешном выполнении. + + Raises: + HTTPException: Если включение завершилось неуспешно (status_code=500). + """ + ufw_service = UFWService() + try: + success = ufw_service.enable_ufw() + if success: + return {"status": "success", "message": "UFW успешно включен"} + else: + raise HTTPException(status_code=500, + detail="Не удалось включить UFW") + except Exception as e: + raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
+ + + +
+[docs] +@router.post("/disable") +async def disable_ufw(): + """Выключает UFW. + + Returns: + dict: Словарь с ключами `status` и `message` при успешном выполнении. + + Raises: + HTTPException: Если выключение завершилось неуспешно (status_code=500). + """ + ufw_service = UFWService() + try: + success = ufw_service.disable_ufw() + if success: + return {"status": "success", "message": "UFW успешно выключен"} + else: + raise HTTPException(status_code=500, + detail="Не удалось выключить UFW") + except Exception as e: + raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
+ + + +
+[docs] +@router.post("/rules/in") +async def add_ufw_in_rules(rules: List[UFWRule]): + """Добавляет входящие (in) правила UFW. + + Args: + rules (List[UFWRule]): Список правил, определяющих направления, + порты/диапазоны/протоколы и действия (allow/deny/reject). + + Returns: + dict: Итог выполнения с количеством успешно добавленных правил и деталями + по каждому правилу. + + Raises: + HTTPException: Если UFW отключен (status_code=400) или при внутренних + ошибках (status_code=500). + """ + ufw_service = UFWService() + try: + # Проверяем статус UFW + status = ufw_service.get_ufw_status() + if not status.enabled: + raise HTTPException( + status_code=400, + detail="UFW отключен. Включите UFW перед добавлением правил.", + ) + + result = ufw_service.add_rules(rules) + if result["success"]: + successful_rules = len( + [r for r in result["results"] if r["success"]]) + total_rules = len(result["results"]) + return { + "status": "success", + "message": + f"Добавлено {successful_rules} из {total_rules} правил", + "details": result, + } + else: + raise HTTPException( + status_code=500, + detail=result.get("error", "Не удалось добавить правила"), + ) + except Exception as e: + raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
+ + + +
+[docs] +@router.post("/rules/out") +async def add_ufw_out_rules(rules: List[UFWRule]): + """Добавляет исходящие (out) правила UFW. + + Args: + rules (List[UFWRule]): Список правил исходящего трафика для добавления. + + Returns: + dict: Объект статуса с сообщением об успешном добавлении. + + Raises: + HTTPException: Если UFW отключен (status_code=400) или при внутренних + ошибках (status_code=500). + """ + ufw_service = UFWService() + try: + # Проверяем статус UFW + status = ufw_service.get_ufw_status() + if not status.enabled: + raise HTTPException( + status_code=400, + detail="UFW отключен. Включите UFW перед добавлением правил.", + ) + + success = ufw_service.add_ufw_out_rules(rules) + if success: + return { + "status": "success", + "message": "Исходящие правила UFW успешно добавлены", + } + else: + raise HTTPException( + status_code=500, + detail="Не удалось добавить исходящие правила UFW") + except Exception as e: + raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
+ + + +
+[docs] +@router.delete("/rules") +async def delete_ufw_rules(rule_numbers: RuleNumbers): + """Удаляет правила UFW по их номерам. + + Args: + rule_numbers (RuleNumbers): Номера правил, подлежащих удалению (как в + выводе `ufw status numbered`). + + Returns: + dict: Информация об общем количестве удаляемых правил, числе успешных и + возможной ошибке. + + Raises: + HTTPException: При ошибках удаления (status_code=500). + """ + ufw_service = UFWService() + try: + result = ufw_service.delete_rules(rule_numbers.numbers) + if result["success"]: + return { + "status": "success", + "message": + f"Удалено {result['successful']} из {result['total']} правил", + "details": result, + } + else: + raise HTTPException(status_code=500, detail=result["error"]) + except Exception as e: + raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/SRV/docs/_build/html/_modules/api/v1/websocket.html b/SRV/docs/_build/html/_modules/api/v1/websocket.html new file mode 100644 index 0000000..de6a1be --- /dev/null +++ b/SRV/docs/_build/html/_modules/api/v1/websocket.html @@ -0,0 +1,488 @@ + + + + + + + + api.v1.websocket - GONETS SRV 0.0.3 documentation + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark, in light mode + + + + + + + + + + + + + + + Auto light/dark, in dark mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Skip to content + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for api.v1.websocket

+import asyncio
+import logging
+from typing import Set
+
+from fastapi import APIRouter, WebSocket, WebSocketDisconnect, Depends
+from starlette.websockets import WebSocketState
+from utils.i18n import get_translations, _
+from babel.support import Translations
+
+from .network import update_container_position, get_network_data
+
+router = APIRouter()
+
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger(__name__)
+
+
+[docs] +class WebSocketManager: + """Класс для управления подключениями к WebSocket-серверу. + + Обрабатывает подключение, отключение и трансляцию сообщений для клиентов, + подключённых через WebSocket. + """ + + def __init__(self): + """Инициализирует менеджер подключений к WebSocket-серверу. + + Создаёт пустое множество для хранения активных соединений. + """ + self.active_connections: Set[WebSocket] = set() + +
+[docs] + async def connect(self, websocket: WebSocket): + """Подключает клиента к WebSocket-серверу. + + Принимает подключение и добавляет WebSocket в список активных соединений. + + Args: + websocket (WebSocket): Объект WebSocket для подключения. + + Raises: + Exception: Если произошла ошибка при принятии подключения. + """ + await websocket.accept() + self.active_connections.add(websocket)
+ + +
+[docs] + def disconnect(self, websocket: WebSocket): + """Отключает клиента от WebSocket-серверу. + + Удаляет WebSocket из списка активных соединений. + + Args: + websocket (WebSocket): Объект WebSocket для отключения. + """ + self.active_connections.remove(websocket)
+ + +
+[docs] + async def broadcast(self, message: dict): + """Транслирует сообщения всем подключённым клиентам. + + Отправляет JSON-данные всем активным соединениям, обрабатывая + отключения клиентов. + + Args: + message (dict): Сообщение для трансляции в формате JSON. + + Raises: + WebSocketDisconnect: Если клиент неожиданно отключился. + Exception: Если произошла другая ошибка при отправке. + """ + for connection in list(self.active_connections): + try: + if connection.client_state == WebSocketState.CONNECTED: + await connection.send_json(message) + except WebSocketDisconnect: + self.disconnect(connection) + except Exception as e: + logger.error(f"Broadcast error: {e}") + self.disconnect(connection)
+
+ + +manager = WebSocketManager() + +
+[docs] +@router.websocket("/api/v1") +async def websocket_endpoint(websocket: WebSocket, translations: Translations = Depends(get_translations())): + """Обработчик WebSocket-соединения. + + Управляет соединением клиента, принимает данные для обновления позиций + контейнеров и транслирует сетевые данные. + + Args: + websocket (WebSocket): Объект WebSocket для обработки соединения. + translations: Объект переводов для текущего языка. + + Raises: + WebSocketDisconnect: Если клиент отключился. + Exception: Если произошла ошибка при обработке данных. + """ + await manager.connect(websocket) + try: + while True: + data = await websocket.receive_json() + logger.info(f"Received data: {data}") + + if all(key in data for key in ["container_id", "container_x", "container_y"]): + await update_container_position(data) + + network_data = await get_network_data() + logger.info(f"Current network data: {network_data}") + + await manager.broadcast(network_data) + await asyncio.sleep(2) + except WebSocketDisconnect: + logger.info("Client disconnected") + except Exception as e: + logger.error(f"Error in websocket_endpoint: {e}") + finally: + manager.disconnect(websocket) + if websocket.client_state != WebSocketState.DISCONNECTED: + await websocket.close()
+ + +
+[docs] +@router.on_event("startup") +async def startup_event(): + """Запуск таска для трансляции сетевых данных при старте приложения. + + Создаёт асинхронную задачу для периодической трансляции данных. + + Raises: + Exception: Если произошла ошибка при запуске таска. + """ + asyncio.create_task(broadcast_data())
+ + +
+[docs] +async def broadcast_data(translations: Translations = Depends(get_translations())): + """Таск для периодической трансляции сетевых данных. + + Выполняет сканирование сети и отправляет данные всем подключённым клиентам + с интервалом в 2 секунды. + + Args: + translations: Объект переводов для текущего языка. + + Raises: + Exception: Если произошла ошибка при получении данных или трансляции. + """ + while True: + try: + network_data = await get_network_data() + if "activation" not in network_data: + logger.error(translations.gettext("Ошибка: Ключ 'activation' отсутствует в полученных данных сети")) + await asyncio.sleep(2) + continue + + await manager.broadcast(network_data) + except Exception as e: + logger.error(f"Error in broadcast_data: {e}") + await asyncio.sleep(2)
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/SRV/docs/_build/html/_modules/config/cors.html b/SRV/docs/_build/html/_modules/config/cors.html new file mode 100644 index 0000000..2089b55 --- /dev/null +++ b/SRV/docs/_build/html/_modules/config/cors.html @@ -0,0 +1,368 @@ + + + + + + + + config.cors - GONETS SRV 0.0.3 documentation + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark, in light mode + + + + + + + + + + + + + + + Auto light/dark, in dark mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Skip to content + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for config.cors

+"""
+Конфигурация CORS для FastAPI-приложения GONETS.
+
+Модуль инкапсулирует добавление middleware `CORSMiddleware` со списком
+разрешённых источников (origins), характерных для локальной разработки и
+типовых портов фронтенда (например, Vite/React на 5173/5174, dev‑серверы).
+
+Использование:
+    cors_config = CORSMiddlewareConfig(app)
+    cors_config.add_cors_middleware()
+
+Поведение:
+ - Разрешены все методы (`allow_methods=["*"]`) и заголовки (`allow_headers=["*"]`).
+ - Разрешены креденшлы (`allow_credentials=True`) для работы с куками (локаль и др.).
+ - При необходимости список `origins` можно расширить или параметризовать
+   на основе конфигурационного файла окружения.
+"""
+from fastapi import FastAPI
+from starlette.middleware.cors import CORSMiddleware
+
+
+
+[docs] +class CORSMiddlewareConfig: + """ + Класс для настройки CORS политики в FastAPI + """ + + def __init__(self, app: FastAPI) -> None: + """Инициализируем приложение FastAPI и список допустимых CORS-оригинов""" + self.app = app + self.origins = [ + "http://localhost.tiangolo.com", + "https://localhost.tiangolo.com", + "http://localhost", + "http://localhost:8080", + "http://localhost:5174", + "http://localhost:5173", + ] + +
+[docs] + def add_cors_middleware(self) -> None: + """Добавляем CORS в приложение FastAPI""" + self.app.add_middleware( + CORSMiddleware, + allow_origins=self.origins, + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], + )
+
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/SRV/docs/_build/html/_modules/config/init_config.html b/SRV/docs/_build/html/_modules/config/init_config.html new file mode 100644 index 0000000..707f1dd --- /dev/null +++ b/SRV/docs/_build/html/_modules/config/init_config.html @@ -0,0 +1,366 @@ + + + + + + + + config.init_config - GONETS SRV 0.0.3 documentation + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark, in light mode + + + + + + + + + + + + + + + Auto light/dark, in dark mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Skip to content + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for config.init_config

+import configparser
+import os
+import secrets
+import logging
+
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger(__name__)
+
+
+
+[docs] +def init_config(): + """ + Инициализация конфигурации приложения (JWT SECRET_KEY). + + Назначение: + - Создаёт файл `/etc/gonets/config.ini` с секцией `[JWT]` и случайным + `secret_key`, если он отсутствует. Устанавливает права `0o600`. + + Замечания: + - Функция предназначена для однократного запуска (в инсталляторе/скрипте + подготовки окружения) и безопасно пропускает работу, если файл уже существует. + """ + config_path = "/etc/gonets/config.ini" + config_dir = os.path.dirname(config_path) + + if os.path.exists(config_path): + logger.info( + f"Конфигурационный файл {config_path} уже существует, пропускаем инициализацию" + ) + return + + try: + secret_key = secrets.token_urlsafe(32) + os.makedirs(config_dir, exist_ok=True) + config = configparser.ConfigParser() + config["JWT"] = {"secret_key": secret_key} + with open(config_path, "w") as config_file: + config.write(config_file) + os.chmod(config_path, 0o600) + logger.info(f"Создан конфигурационный файл {config_path}") + except Exception as e: + logger.error( + f"Ошибка при создании конфигурационного файла: {type(e).__name__} - {str(e)}" + ) + raise
+ + +# Нативный запуск для тестов +if __name__ == "__main__": + init_config() +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/SRV/docs/_build/html/_modules/config/ports.html b/SRV/docs/_build/html/_modules/config/ports.html new file mode 100644 index 0000000..0bb3985 --- /dev/null +++ b/SRV/docs/_build/html/_modules/config/ports.html @@ -0,0 +1,432 @@ + + + + + + + + config.ports - GONETS SRV 0.0.3 documentation + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark, in light mode + + + + + + + + + + + + + + + Auto light/dark, in dark mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Skip to content + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for config.ports

+from typing import Dict, List
+
+
+
+[docs] +class PortConfig: + """Конфигурация известных портов для сетевого анализа. + + Назначение: + Предоставляет сопоставление распространённых портов с человекочитаемыми + названиями сервисов для отображения в UI и логах. Поддерживает расширение + пользовательским словарём. + """ + + _DEFAULT_PORTS: Dict[int, str] = { + 20: "FTP (Data)", + 21: "FTP (Control)", + 22: "SSH", + 23: "Telnet", + 25: "SMTP", + 53: "DNS", + 67: "DHCP (Server)", + 68: "DHCP (Client)", + 69: "TFTP", + 80: "HTTP", + 110: "POP3", + 123: "NTP (Network Time Protocol)", + 137: "NetBIOS Name Service", + 138: "NetBIOS Datagram Service", + 139: "NetBIOS Session Service", + 143: "IMAP", + 161: "SNMP", + 162: "SNMP Trap", + 179: "BGP (Border Gateway Protocol)", + 443: "HTTPS", + 445: "SMB (Microsoft-DS)", + 465: "SMTPS", + 514: "Syslog (UDP)", + 515: "LPD (Printer)", + 587: "SMTP Submission", + 631: "IPP (Printing)", + 993: "IMAPS", + 995: "POP3S", + # --- Базы данных --- + 1433: "Microsoft SQL Server", + 1521: "Oracle DB", + 3306: "MySQL/MariaDB", + 5432: "PostgreSQL", + 6379: "Redis", + 27017: "MongoDB", + # --- VPN и удалённый доступ --- + 1723: "PPTP VPN", + 1701: "L2TP", + 500: "IPSec", + 4500: "IPSec NAT-T", + 1194: "OpenVPN", + 3389: "RDP (Remote Desktop Protocol)", + 5900: "VNC", + # --- Прокси / Веб --- + 1080: "SOCKS Proxy", + 8000: "HTTP-Alt", + 8080: "HTTP Proxy", + 8443: "HTTPS-Alt", + # --- DevOps / Monitoring / Tools --- + 9000: "SonarQube / PHP-FPM", + 9100: "Prometheus Node Exporter", + 9200: "Elasticsearch HTTP", + 9300: "Elasticsearch Cluster", + 11211: "Memcached", + # --- Прочее --- + 6667: "IRC", + 4000: "ICQ / Custom apps", + 25565: "Minecraft Server", + 53: "DNS (UDP & TCP)", + } + + def __init__(self, custom_ports: Dict[int, str] = None): + """ + Инициализирует конфигурацию портов. + + Args: + custom_ports: Пользовательский словарь портов для добавления или переопределения. + """ + self._ports = self._DEFAULT_PORTS.copy() + if custom_ports: + self._ports.update(custom_ports) + +
+[docs] + def get_port_name(self, port: int) -> str: + """ + Возвращает название сервиса для указанного порта. + + Args: + port: Номер порта. + + Returns: + Название сервиса или строка вида 'Port-<port>'. + """ + return self._ports.get(port, f"Port-{port}")
+ + +
+[docs] + def get_ports(self) -> List[int]: + """ + Возвращает список всех номеров портов. + + Returns: + Список номеров портов. + """ + return list(self._ports.keys())
+
+ + + +port_config = PortConfig() +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/SRV/docs/_build/html/_modules/core/cli_executor.html b/SRV/docs/_build/html/_modules/core/cli_executor.html new file mode 100644 index 0000000..9d805c7 --- /dev/null +++ b/SRV/docs/_build/html/_modules/core/cli_executor.html @@ -0,0 +1,351 @@ + + + + + + + + core.cli_executor - GONETS SRV 0.0.3 documentation + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark, in light mode + + + + + + + + + + + + + + + Auto light/dark, in dark mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Skip to content + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for core.cli_executor

+import subprocess
+from typing import Tuple
+
+
+
+[docs] +class CLIExecutor: + """Утилита для безопасного выполнения CLI‑команд. + + Назначение: + Инкапсулирует вызов `subprocess.run` и приводит результат к удобному + кортежу (код возврата, stdout) без генерации исключений при ошибках. + """ + +
+[docs] + @staticmethod + def run(command: list) -> Tuple[int, str]: + """ + Выполняет команду в системе и возвращает код и вывод. + + Args: + command (list): Список аргументов команды для выполнения. + + Returns: + tuple: Код возврата и вывод команды. + """ + result = subprocess.run( + command, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + check=False, # Не вызываем исключение при ошибке + ) + return result.returncode, result.stdout.decode("utf-8")
+
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/SRV/docs/_build/html/_modules/index.html b/SRV/docs/_build/html/_modules/index.html new file mode 100644 index 0000000..a68c954 --- /dev/null +++ b/SRV/docs/_build/html/_modules/index.html @@ -0,0 +1,344 @@ + + + + + + + + Overview: module code - GONETS SRV 0.0.3 documentation + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark, in light mode + + + + + + + + + + + + + + + Auto light/dark, in dark mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Skip to content + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ + +
+ + + + + \ No newline at end of file diff --git a/SRV/docs/_build/html/_modules/models/auth.html b/SRV/docs/_build/html/_modules/models/auth.html new file mode 100644 index 0000000..0005fc8 --- /dev/null +++ b/SRV/docs/_build/html/_modules/models/auth.html @@ -0,0 +1,389 @@ + + + + + + + + models.auth - GONETS SRV 0.0.3 documentation + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark, in light mode + + + + + + + + + + + + + + + Auto light/dark, in dark mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Skip to content + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for models.auth

+from pydantic import BaseModel
+from typing import Optional
+
+
+
+[docs] +class UserCreate(BaseModel): + """ + Модель созданного пользователя + + Атрибуты: + username (str): Имя пользователя + password (str): Пароль пользователя + is_admin (bool): Административный доступ + """ + + username: str + password: str + is_admin: bool = False
+ + + +
+[docs] +class UserLogin(BaseModel): + """ + Модель авторизации пользователя + + Атрибуты: + username (str): Имя пользователя + password (str): Пароль пользователя + """ + + username: str + password: str
+ + + +
+[docs] +class UserResponse(BaseModel): + """ + Модель проверки наличия пользователя + + Атрибуты: + id (int): Идентификатор + username (str): Имя пользователя + is_admin (bool): Административный доступ + """ + + id: int + username: str + is_admin: bool
+ + + +
+[docs] +class Token(BaseModel): + """ + Токен пользователя + + Атрибуты: + access_token (str): Токен пользователя (захэширован) + token_type (str): Тип токена + session_id (Optional[str]): Идентификатор сессии + user (Optional[UserResponse]): Пользователь на основе проверки наличия пользователя + """ + + access_token: str + token_type: str + session_id: Optional[str] = None + user: Optional[UserResponse] = None
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/SRV/docs/_build/html/_modules/models/network.html b/SRV/docs/_build/html/_modules/models/network.html new file mode 100644 index 0000000..7ad1813 --- /dev/null +++ b/SRV/docs/_build/html/_modules/models/network.html @@ -0,0 +1,511 @@ + + + + + + + + models.network - GONETS SRV 0.0.3 documentation + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark, in light mode + + + + + + + + + + + + + + + Auto light/dark, in dark mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Skip to content + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for models.network

+from typing import List, Optional, Union
+from pydantic import BaseModel
+
+
+
+[docs] +class IPv4Route(BaseModel): + """ + Модель маршрута IPv4 + + Атрибуты: + destination (str): IP-адрес назначения + prefix (int): Префикс подсети в формате CIDR + next_hop (str): Адрес следующего узла (маршрутизатор) + metric (int): Метрика маршрута + """ + + destination: str + prefix: int + next_hop: str + metric: int
+ + + +
+[docs] +class IPv6Route(BaseModel): + """ + Модель маршрута IPv6 + + Атрибуты: + destination (str): IP-адрес назначения + prefix (int): Префикс подсети (CIDR) + next_hop (str): Адрес следующего узла (маршрутизатор) + metric (int): Метрика маршрута + """ + + destination: str + prefix: int + next_hop: str + metric: int
+ + + +
+[docs] +class IndividualIPConfig(BaseModel): + """ + Модель индивидуальной конфигурации IP-адреса + + Атрибуты: + address (str): IP-адрес интерфейса + routes (List[Union[IPv4Route, IPv6Route]]): Список маршрутов (IPv4/IPv6) + method (Optional[str]): Метод назначения IP + gateway (str): Основной шлюз по умолчанию + dns (List[str]): Список DNS-серверов + """ + + address: str + routes: List[Union[IPv4Route, IPv6Route]] + method: Optional[str] + gateway: str + dns: List[str]
+ + + +
+[docs] +class IPConfig(BaseModel): + """ + Модель конфигурации IP (IPv4/IPv6) + + Атрибуты: + configurations (List[IndividualIPConfig]): Список индивидуальных IP-конфигураций + """ + + configurations: List[IndividualIPConfig]
+ + + +
+[docs] +class NetworkInterface(BaseModel): + """ + Модель базового сетевого интерфейса + + Атрибуты: + name (str): Название интерфейса + type (str): Тип интерфейса + mac_address (str): MAC-адрес устройства + device_state (str): Состояние устройства + active_connection (str): Название активного соединения + device_mtu (str): MTU устройства + ipv4 (IPConfig): Конфигурация IPv4 + ipv6 (IPConfig): Конфигурация IPv6 + container_x (int): Положение по X на холсте + container_y (int): Положение по Y на холсте + """ + + name: str + type: str + mac_address: str + device_state: str + active_connection: str + device_mtu: str + ipv4: IPConfig + ipv6: IPConfig + container_x: int = 0 + container_y: int = 0
+ + + +
+[docs] +class WiFiDevice(NetworkInterface): + """ + Модель Wi-Fi устройства (наследуется от NetworkInterface) + + Атрибуты: + ssid (Optional[str]): Название Wi-Fi сети + bssid (Optional[str]): MAC-адрес точки доступа + bitrate (Optional[int]): Скорость соединения + strength (Optional[int]): Уровень сигнала + frequency (Optional[float]): Частота + mode (Union[str, int]): Режим работы + """ + + ssid: Optional[str] = None + bssid: Optional[str] = None + bitrate: Optional[int] = None + strength: Optional[int] = None + frequency: Optional[float] = None + mode: Union[str, int] = None
+ + + +
+[docs] +class NetworkInterfacesContainer(BaseModel): + """ + Контейнер для группы сетевых интерфейсов с координатами + + Атрибуты: + interfaces (List[Union[NetworkInterface, WiFiDevice]]): Список интерфейсов + container_x (int): Положение по оси X + container_y (int): Положение по оси Y + """ + + interfaces: List[Union[NetworkInterface, WiFiDevice]] + container_x: int + container_y: int
+ + + +
+[docs] +class Activation(BaseModel): + """ + Модель активированных интерфейсов + + Атрибуты: + id (int): Идентификатор ноды активированных интерфейсов + interfaces (List[Union[NetworkInterface, WiFiDevice]]): Активированные интерфейсы + container_x (int): Положение по X на холсте + container_y (int): Положение по Y на холсте + isOpen (bool): Флаг скрытия ноды на холсте + """ + + id: int + interfaces: List[Union[NetworkInterface, WiFiDevice]] + container_x: int + container_y: int + isOpen: bool = True
+ + + +
+[docs] +class Deactivation(BaseModel): + """ + Модель отключенных интерфейсов + + Атрибуты: + id (int): Идентификатор ноды отключенных интерфейсов + interfaces (List[Union[NetworkInterface, WiFiDevice]]): Отключенные интерфейсы + container_x (int): Положение по X на холсте + container_y (int): Положение по Y на холсте + isOpen (bool): Флаг скрытия ноды на холсте + """ + + id: int + interfaces: List[Union[NetworkInterface, WiFiDevice]] + container_x: int + container_y: int + isOpen: bool = True
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/SRV/docs/_build/html/_modules/models/request.html b/SRV/docs/_build/html/_modules/models/request.html new file mode 100644 index 0000000..f72b1e3 --- /dev/null +++ b/SRV/docs/_build/html/_modules/models/request.html @@ -0,0 +1,356 @@ + + + + + + + + models.request - GONETS SRV 0.0.3 documentation + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark, in light mode + + + + + + + + + + + + + + + Auto light/dark, in dark mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Skip to content + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for models.request

+from pydantic import BaseModel
+
+
+
+[docs] +class NoteRequest(BaseModel): + """ + Модель добавления заметки к просканированному устройству + + Атрибуты: + note (str): Заметка устройства + """ + + note: str
+ + +
+[docs] +class WifiConnectRequest(BaseModel): + """ + Модель для подключения к точке доступа Wi-Fi + + Атрибуты: + ssid (str): Название точки доступа Wi-Fi + password (str): Пароль точки доступа Wi-Fi + """ + ssid: str + password: str
+ + +
+[docs] +class WifiDisconnectRequest(BaseModel): + """ + Модель для отключения от точки доступа Wi-Fi + + Атрибуты: + ssid (str): Название точки доступа Wi-Fi + """ + ssid: str
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/SRV/docs/_build/html/_modules/models/subnet.html b/SRV/docs/_build/html/_modules/models/subnet.html new file mode 100644 index 0000000..e334dba --- /dev/null +++ b/SRV/docs/_build/html/_modules/models/subnet.html @@ -0,0 +1,371 @@ + + + + + + + + models.subnet - GONETS SRV 0.0.3 documentation + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark, in light mode + + + + + + + + + + + + + + + Auto light/dark, in dark mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Skip to content + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for models.subnet

+from pydantic import BaseModel
+from typing import Optional, List
+
+
+[docs] +class Device(BaseModel): + """ + Сканируемое устройство в подсети + + Атрибуты: + ip (str): IP-адрес устройства + mac (Optional[str]): MAC-адрес устройства + hostname (Optional[str]): Имя хоста + vendor (Optional[str]): Название производителя устройства (По MAC-адресу) + open_ports (List[str]): Открытые порты устройства + status (str): Статус устройства (онлайн/оффлайн) + last_seen (Optional[str]): Последняя активность устройства + note (Optional[str]): Заметка устройства + """ + + ip: str + mac: Optional[str] = None + hostname: Optional[str] = None + vendor: Optional[str] = None + open_ports: List[str] = [] + status: str + last_seen: Optional[str] = None + note: Optional[str] = None
+ + +
+[docs] +class SubnetScanResult(BaseModel): + """ + Результат сканирования устройств в подсети + + Атрибуты: + devices (List[Device]): Список просканированных устройств + error (Optional[str]): Сообщение об ошибке, если сканирование не удалось + """ + + devices: List[Device] = [] + error: Optional[str] = None
+ + +
+[docs] +class SubnetScanRequest(BaseModel): + """ + Параметры для сканирования подсети + + Атрибуты: + subnet (Optional[str]): Подсеть для сканирования (например 192.168.1.0/24), опционально + """ + subnet: Optional[str] = None
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/SRV/docs/_build/html/_modules/models/ufw.html b/SRV/docs/_build/html/_modules/models/ufw.html new file mode 100644 index 0000000..6028b30 --- /dev/null +++ b/SRV/docs/_build/html/_modules/models/ufw.html @@ -0,0 +1,635 @@ + + + + + + + + models.ufw - GONETS SRV 0.0.3 documentation + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark, in light mode + + + + + + + + + + + + + + + Auto light/dark, in dark mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Skip to content + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for models.ufw

+from typing import List, Optional, Annotated, Union
+from enum import Enum
+from pydantic import BaseModel, Field, validator
+import ipaddress
+
+
+
+[docs] +class Protocol(str, Enum): + """ + Enum для сетевых протоколов UFW. + """ + + TCP = "tcp" + UDP = "udp" + BOTH = "both" + + @property + def protocols(self) -> List[str]: + """Возвращает список протоколов для использования в команде UFW.""" + return ["tcp", "udp"] if self == Protocol.BOTH else [self.value]
+ + + +
+[docs] +class Action(str, Enum): + """ + Enum для действий UFW. + + Attributes: + ALLOW: Разрешить трафик + DENY: Запретить трафик без уведомления + REJECT: Запретить трафик с уведомлением отправителю + """ + + ALLOW = "allow" + DENY = "deny" + REJECT = "reject"
+ + + +
+[docs] +class Direction(str, Enum): + """ + Enum для направления трафика UFW. + + Attributes: + IN: Входящий трафик + OUT: Исходящий трафик + """ + + IN = "in" + OUT = "out"
+ + + +
+[docs] +class UFWRule(BaseModel): + """Модель правила UFW""" + + action: Action + direction: Direction + from_ip: str + to_ip: str + port: Union[int, str] + protocol: Protocol + comment: Optional[str] = None + +
+[docs] + @validator("port") + def validate_port(cls, v): + """ + Проверяет корректность порта или диапазона портов. + + Args: + v: Порт (число) или диапазон портов (строка вида "3000:3999") + + Returns: + Union[int, str]: Валидированное значение порта + + Raises: + ValueError: Если порт или диапазон портов некорректен + """ + if isinstance(v, int): + if not (1 <= v <= 65535): + raise ValueError( + f"Порт должен быть в диапазоне 1-65535, получено: {v}") + return v + elif isinstance(v, str): + try: + start, end = map(int, v.split(":")) + if not (1 <= start <= end <= 65535): + raise ValueError + return v + except (ValueError, TypeError): + raise ValueError( + f"Некорректный диапазон портов: {v}. Формат: start:end, где 1 <= start <= end <= 65535" + ) + else: + raise ValueError( + f"Порт должен быть числом или строкой с диапазоном, получено: {type(v)}" + )
+ + +
+[docs] + @validator("from_ip", "to_ip") + def validate_ip(cls, v): + """Проверяет корректность IP адреса""" + try: + # Пробуем создать объект IP-сети, что автоматически проверит корректность адреса + ipaddress.ip_network(v, strict=False) + return v + except ValueError: + raise ValueError(f"Некорректный IP адрес или подсеть: {v}")
+ + +
+[docs] + @validator("to_ip") + def validate_ip_version_compatibility(cls, v, values): + """Проверяет совместимость версий IP адресов""" + if "from_ip" not in values: + return v + + # Определяем версии IP + from_ip = ipaddress.ip_network(values["from_ip"], strict=False) + to_ip = ipaddress.ip_network(v, strict=False) + + if isinstance(from_ip, ipaddress.IPv6Network) != isinstance( + to_ip, ipaddress.IPv6Network): + raise ValueError( + "Несовместимые версии IP: нельзя смешивать IPv4 и IPv6 в одном правиле" + ) + + return v
+ + +
+[docs] + @validator("action", pre=True) + def validate_action(cls, v): + """Приводит значение action к нижнему регистру перед валидацией""" + if isinstance(v, str): + return v.lower() + return v
+ + +
+[docs] + @validator("protocol", pre=True) + def validate_protocol(cls, v): + """Приводит значение protocol к нижнему регистру перед валидацией""" + if isinstance(v, str): + return v.lower() + return v
+ + +
+[docs] + def to_ufw_command(self) -> str: + """Преобразует правило в команду UFW.""" + cmd_parts = ["ufw"] + + # Добавляем действие + cmd_parts.append(self.action.value) + + # Добавляем направление + cmd_parts.append(self.direction.value) + + # Добавляем from с IP + cmd_parts.extend(["from", self.from_ip]) + + # Добавляем to с IP + cmd_parts.extend(["to", self.to_ip]) + + # Добавляем порт и протокол + port_str = str( + self.port) # Работает как с числами, так и с диапазонами + cmd_parts.extend(["port", port_str]) + + # Добавляем протокол + proto = self.protocol.value + if proto == "both": + cmd_parts.append("proto tcp/udp") + else: + cmd_parts.extend(["proto", proto]) + + # Добавляем комментарий если есть + if self.comment: + cmd_parts.extend(["comment", f"'{self.comment}'"]) + + return " ".join(cmd_parts)
+ + +
+[docs] + @classmethod + def from_ufw_output(cls, line: str) -> "UFWRule": + """ + Создает экземпляр UFWRule из строки вывода команды 'ufw status numbered'. + + Args: + line: Строка вывода UFW (например, "[1] 80/tcp ALLOW IN 192.168.1.0/24 # Web Server") + + Returns: + UFWRule: Новый экземпляр правила + """ + import re + + # Регулярное выражение для разбора строки правила UFW + pattern = r"""^\s* + \[\s*(?P<number>\d+)\s*\]\s* + (?P<port>\d+(?::\d+)?)/(?P<protocol>tcp(?:/udp)?|udp)\s+ + (?P<action>ALLOW|DENY|REJECT)\s+(?P<direction>IN|OUT)\s+ + (?P<from_ip>[\w\d\./]+|\bAnywhere\b) + (?:\s+\(v6\))? + (?:\s+\((?:out|in)\))? + \s* + (?:\#\s*(?P<comment>.*?))? + \s*$ + """ + + match = re.match(pattern, line, re.VERBOSE | re.IGNORECASE) + if not match: + raise ValueError( + f"Невозможно разобрать строку правила UFW: {line}") + + groups = match.groupdict() + + # Преобразуем протокол в enum + proto_str = groups["protocol"].lower() + protocol = (Protocol.BOTH if proto_str == "tcp/udp" else + Protocol.UDP if proto_str == "udp" else Protocol.TCP) + + # Преобразуем действие в enum (игнорируем регистр) + action_str = groups["action"].lower() + action = (Action.ALLOW if action_str == "allow" else + Action.DENY if action_str == "deny" else Action.REJECT) + + # Преобразуем направление в enum + direction_str = groups["direction"].lower() + direction = Direction.IN if direction_str == "in" else Direction.OUT + + # Обрабатываем IP адреса + from_ip = groups["from_ip"].strip() + if from_ip.lower() == "anywhere": + from_ip = "0.0.0.0/0" + + # Для исходящих правил меняем местами from и to + to_ip = "0.0.0.0/0" # По умолчанию anywhere + + # Обрабатываем порт + port = groups["port"] + if ":" in port: + # Оставляем как строку для диапазона + port = port.strip() + else: + # Преобразуем в число для одиночного порта + port = int(port) + + # Обрабатываем комментарий + comment = groups.get("comment", + "").strip() if groups.get("comment") else None + + return cls( + action=action, + direction=direction, + from_ip=from_ip, + to_ip=to_ip, + port=port, + protocol=protocol, + comment=comment, + )
+
+ + + +
+[docs] +class UFWRulesResponse(BaseModel): + """ + Модель для представления списка правил UFW. + + Attributes: + enabled: Включен ли UFW + rules: Список правил UFW + total_rules: Общее количество правил + """ + + enabled: bool = Field(..., description="Включен ли UFW") + rules: List[UFWRule] = Field(default_factory=list, + description="Список правил UFW") + + @property + def total_rules(self) -> int: + """Возвращает общее количество правил.""" + return len(self.rules)
+ + + +
+[docs] +class UFWStatus(BaseModel): + """ + Модель для представления статуса UFW. + + Attributes: + enabled: Включен ли UFW + message: Сообщение о статусе + last_checked: Время последней проверки статуса + """ + + enabled: bool = Field(..., description="Включен ли UFW") + message: str = Field(..., description="Сообщение о статусе")
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/SRV/docs/_build/html/_modules/models/wifi.html b/SRV/docs/_build/html/_modules/models/wifi.html new file mode 100644 index 0000000..d5122c1 --- /dev/null +++ b/SRV/docs/_build/html/_modules/models/wifi.html @@ -0,0 +1,361 @@ + + + + + + + + models.wifi - GONETS SRV 0.0.3 documentation + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark, in light mode + + + + + + + + + + + + + + + Auto light/dark, in dark mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Skip to content + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for models.wifi

+from pydantic import BaseModel
+from typing import Optional, List
+
+
+
+[docs] +class WifiAccessPoint(BaseModel): + """ + Модель точки доступа Wi-Fi + + Атрибуты: + ssid (Optional[str]): Название точки доступа. Может быть скрытым + bssid (str): Уникальный идентификатор точки доступа (по типу MAC-адреса) + channel (Optional[int]): Канал, на котором работает точка доступа + signal (Optional[int]): Уровень сигнала (dBm) + encryption (Optional[str]): Тип шифрования (WPA2, WPA3, открытая и т.д.) + frequency (Optional[str]): Частота вещания (2.4GHz/5GHz) + vendor (Optional[str]): Производитель точки доступа (определяется по MAC-адресу) + hidden (bool): Флаг скрытия названия точки доступа (ssid) + note (Optional[str]): Заметка для точки доступа + """ + + ssid: Optional[str] = None + bssid: str + channel: Optional[int] = None + signal: Optional[int] = None + encryption: Optional[str] = None + frequency: Optional[str] = None + vendor: Optional[str] = None + hidden: bool = False + note: Optional[str] = None
+ + + +
+[docs] +class WifiScanResult(BaseModel): + """ + Результат сканирования Wi-Fi + + Атрибуты: + access_points (List[WifiAccessPoint]): Список найденных Wi-Fi точек доступа + """ + + access_points: List[WifiAccessPoint]
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/SRV/docs/_build/html/_modules/services/arp_service.html b/SRV/docs/_build/html/_modules/services/arp_service.html new file mode 100644 index 0000000..55816eb --- /dev/null +++ b/SRV/docs/_build/html/_modules/services/arp_service.html @@ -0,0 +1,453 @@ + + + + + + + + services.arp_service - GONETS SRV 0.0.3 documentation + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark, in light mode + + + + + + + + + + + + + + + Auto light/dark, in dark mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Skip to content + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for services.arp_service

+"""
+Сервис низкоуровневого ARP-сканирования локальной сети.
+
+Назначение:
+
+- Обнаружение активных узлов в пределах сетей хоста путём широковещательных ARP‑запросов от имени выбранного интерфейса.
+- Помощь сервисам более высокого уровня (например, IPService) при проверке занятости адресов перед применением конфигураций.
+
+Особенности реализации:
+
+- Определяет подключённые сети интерфейса через `netifaces` и строит CIDR для сканирования.
+- Формирует ARP‑пакеты с помощью `scapy` (Ether/ARP) и отправляет их с таймаутом.
+- Добавляет локальный адрес интерфейса в результирующий список клиентов.
+
+Требования/ограничения:
+
+- Для отправки ARP‑кадров могут потребоваться повышенные привилегии или capability на сырой сокет.
+"""
+from fastapi import Depends
+from scapy.layers.l2 import ARP, Ether
+from scapy.sendrecv import srp
+import netifaces
+from netaddr import IPNetwork
+from utils.i18n import get_translations, _
+from babel.support import Translations
+
+
+[docs] +class NetworkScanner: + """Инициализация сетевого сканера. + + Предоставляет методы для сканирования сети с использованием ARP-запросов и + вывода информации о клиентах. + """ + + def __init__(self, interface=None, translations: Translations = Depends(get_translations())): + """Инициализация параметров сетевого сканера. + + Args: + interface (str, optional): Название сетевого интерфейса. По умолчанию + используется интерфейс по умолчанию из netifaces.gateways(). + translations: Объект переводов для текущего языка. + + Инициализирует интерфейс, информацию об IP-адресах и локальный MAC-адрес. + """ + self.interface = ( + interface or netifaces.gateways()["default"][netifaces.AF_INET][1]) + self.ip_info = netifaces.ifaddresses(self.interface).get( + netifaces.AF_INET, []) + self.networks = [ + IPNetwork(f"{ip['addr']}/{ip['netmask']}") for ip in self.ip_info + ] + self.local_mac = netifaces.ifaddresses( + self.interface)[netifaces.AF_LINK][0]["addr"] + self.translations = translations + + def _create_arp_packet(self, target_ip): + """Создаёт ARP-пакет для отправки на указанный IP-адрес. + + Args: + target_ip (str): Целевой IP-адрес или подсеть для сканирования. + + Returns: + scapy.packet.Packet: Сформированный ARP-пакет с Ethernet-заголовком. + """ + arp = ARP(pdst=target_ip) + ether = Ether(dst="ff:ff:ff:ff:ff:ff") + return ether / arp + +
+[docs] + def scan_network(self, attempts=3, timeout=2): + """Сканирует сеть для обнаружения активных клиентов. + + Args: + attempts (int, optional): Количество попыток отправки ARP-запросов. + По умолчанию 3. + timeout (int, optional): Таймаут ожидания ответа в секундах. + По умолчанию 2. + + Returns: + List[dict]: Список словарей с ключами "ip" и "mac", представляющих + обнаруженных клиентов, включая локальные IP-адреса. + + Note: + Учитывает локальные IP-адреса интерфейса и удаляет дубликаты. + """ + all_clients = [] + + for network in self.networks: + target_ip = f"{network.network}/{network.prefixlen}" + packet = self._create_arp_packet(target_ip) + clients = [] + + for _ in range(attempts): + result = srp(packet, + timeout=timeout, + iface=self.interface, + verbose=0)[0] + for _, received in result: + client = {"ip": received.psrc, "mac": received.hwsrc} + if client not in clients: + clients.append(client) + + all_clients.extend(clients) + + for ip_info in self.ip_info: + local_client = {"ip": ip_info["addr"], "mac": self.local_mac} + if local_client not in all_clients: + all_clients.append(local_client) + + unique_clients = {tuple(client.items()) for client in all_clients} + return [dict(client) for client in unique_clients]
+ + +
+[docs] + def print_clients(self, clients): + """Выводит список адресов клиентов в формате таблицы. + + Args: + clients (List[dict]): Список словарей с ключами "ip" и "mac", + представляющих клиентов. + + Выводит IP-адреса и MAC-адреса в отсортированном по IP порядке. + """ + print(self.translations.gettext("Доступные устройства в сети:")) + print(self.translations.gettext("IP") + " " * 18 + self.translations.gettext("MAC")) + for client in sorted(clients, key=lambda x: x["ip"]): + print(f"{client['ip']:<16} {client['mac']}")
+
+ + +# Оставил нативный запуск сервиса для тестов +if __name__ == "__main__": + scanner = NetworkScanner() + clients = scanner.scan_network() + scanner.print_clients(clients) +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/SRV/docs/_build/html/_modules/services/auth_service.html b/SRV/docs/_build/html/_modules/services/auth_service.html new file mode 100644 index 0000000..84cb13b --- /dev/null +++ b/SRV/docs/_build/html/_modules/services/auth_service.html @@ -0,0 +1,487 @@ + + + + + + + + services.auth_service - GONETS SRV 0.0.3 documentation + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark, in light mode + + + + + + + + + + + + + + + Auto light/dark, in dark mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Skip to content + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for services.auth_service

+from jose import JWTError, jwt
+from datetime import datetime, timedelta
+from fastapi import HTTPException, Depends, Request
+from fastapi.security import OAuth2PasswordBearer
+from models.auth import UserCreate, UserLogin, UserResponse, Token
+from storage.json_storage import JsonStorage
+from storage.session_storage import SessionStorage
+from typing import Optional
+import configparser
+import os
+import logging
+from utils.i18n import get_translations, _
+from babel.support import Translations
+
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger(__name__)
+
+config = configparser.ConfigParser()
+config_path = "/etc/gonets/config.ini"
+if not os.path.exists(config_path):
+    raise RuntimeError(f"Configuration file {config_path} not found")
+config.read(config_path)
+SECRET_KEY = config["JWT"]["secret_key"]
+
+ALGORITHM = "HS256"
+ACCESS_TOKEN_EXPIRE_MINUTES = 30
+
+oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/login")
+
+
+[docs] +class AuthService: + """Сервис для регистрации, авторизации и управления сессиями""" + + def __init__(self, translations: Translations = Depends(get_translations())): + self.storage = JsonStorage() + self.session_storage = SessionStorage() + self.translations = translations + +
+[docs] + def create_access_token(self, data: dict) -> str: + """Создаёт токен""" + to_encode = data.copy() + expire = datetime.utcnow() + timedelta( + minutes=ACCESS_TOKEN_EXPIRE_MINUTES) + to_encode.update({"exp": expire}) + encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) + return encoded_jwt
+ + +
+[docs] + def verify_token(self, token: str) -> Optional[dict]: + """Проверяет токен""" + try: + payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) + username: str = payload.get("sub") + if username is None: + return None + return {"username": username} + except JWTError: + return None
+ + +
+[docs] + def register(self, user: UserCreate) -> UserResponse: + """Регистрирует нового пользователя""" + try: + db_user = self.storage.create_user(user) + return db_user + except ValueError as e: + logger.error( + f"Error registering user {user.username}: {str(e)}" + ) + raise HTTPException(status_code=400, detail=self.translations.gettext(str(e))) + except Exception as e: + logger.error( + f"Error registering user {user.username}: {type(e).__name__} - {str(e)}" + ) + raise HTTPException(status_code=500, detail=self.translations.gettext("Ошибка регистрации"))
+ + +
+[docs] + def login(self, user: UserLogin, client_id: str) -> dict: + """Авторизует пользователя, создаёт сессию и возвращает токен""" + try: + db_user = self.storage.get_user_by_username(user.username) + if not db_user: + logger.error(f"User {user.username} not found") + raise HTTPException(status_code=401, detail=self.translations.gettext("Неверный логин или пароль")) + if not self.storage.verify_password(user.password, db_user["password_hash"]): + logger.error( + f"Invalid password for user {user.username}") + raise HTTPException(status_code=401, detail=self.translations.gettext("Неверный логин или пароль")) + access_token = self.create_access_token( + data={"sub": user.username}) + session_id = self.session_storage.create_session( + user.username, access_token, client_id) + logger.info(f"User {user.username} successfully logged in") + return { + "access_token": access_token, + "token_type": "bearer", + "session_id": session_id, + "user": UserResponse(id=db_user["id"], username=db_user["username"], is_admin=db_user["is_admin"]), + } + except Exception as e: + logger.error( + f"Error logging in user {user.username}: {type(e).__name__} - {str(e)}" + ) + raise HTTPException(status_code=500, detail=self.translations.gettext("Ошибка авторизации"))
+ + +
+[docs] + def get_current_user( + self, token: str = Depends(oauth2_scheme)) -> UserResponse: + """Получает текущего пользователя по токену""" + credentials = self.verify_token(token) + if not credentials: + logger.error("Invalid or expired token") + raise HTTPException(status_code=401, detail=self.translations.gettext("Неверный токен")) + username = credentials["username"] + db_user = self.storage.get_user_by_username(username) + if not db_user: + logger.error(f"User {username} not found") + raise HTTPException(status_code=401, detail=self.translations.gettext("Пользователь не найден")) + return UserResponse(id=db_user["id"], username=db_user["username"], is_admin=db_user["is_admin"])
+ + +
+[docs] + def get_user_by_session(self, client_id: str) -> dict: + """Получает пользователя и токен по client_id""" + session = self.session_storage.get_latest_session(client_id) + if not session: + logger.error( + f"Session for client_id {client_id} not found or expired") + raise HTTPException(status_code=401, detail=self.translations.gettext("Сессия недействительна")) + username = session["username"] + token = session["token"] + db_user = self.storage.get_user_by_username(username) + if not db_user: + logger.error(f"User {username} not found") + raise HTTPException(status_code=401, detail=self.translations.gettext("Пользователь не найден")) + return { + "user": + UserResponse(id=db_user["id"], username=db_user["username"], is_admin=db_user["is_admin"]), + "access_token": token + }
+ + +
+[docs] + def logout(self, client_id: str): + """Выполняет выход из системы, удаляя последнюю сессию для client_id""" + try: + session = self.session_storage.get_latest_session(client_id) + if session: + self.session_storage.delete_session(session["session_id"]) + logger.info( + f"User successfully logged out for client_id {client_id}" + ) + return {"message": self.translations.gettext("Успешный выход из системы")} + except Exception as e: + logger.error( + f"Error logging out: {type(e).__name__} - {str(e)}") + raise HTTPException(status_code=500, detail=self.translations.gettext("Ошибка при выходе из системы"))
+
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/SRV/docs/_build/html/_modules/services/connect_service.html b/SRV/docs/_build/html/_modules/services/connect_service.html new file mode 100644 index 0000000..ac82525 --- /dev/null +++ b/SRV/docs/_build/html/_modules/services/connect_service.html @@ -0,0 +1,507 @@ + + + + + + + + services.connect_service - GONETS SRV 0.0.3 documentation + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark, in light mode + + + + + + + + + + + + + + + Auto light/dark, in dark mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Skip to content + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for services.connect_service

+"""
+Сервис управления подключениями Wi‑Fi через `nmcli`.
+
+Назначение:
+
+- Предоставляет операции подключения/отключения к точкам доступа и проверки текущего статуса, ориентируясь на системный NetworkManager.
+
+Поведение:
+
+- Автоматически определяет активный Wi‑Fi‑интерфейс.
+- Валидирует наличие SSID в листинге перед подключением и интерпретирует частые ошибки `nmcli` (неверный пароль, отсутствующая сеть и т.п.).
+
+Ограничения:
+
+- Требуются права для управления сетевыми соединениями в системе.
+- В окружениях без NetworkManager команды `nmcli` будут недоступны.
+"""
+import subprocess
+import logging
+from typing import Tuple, Optional
+from utils.i18n import get_translations, _
+from babel.support import Translations
+from fastapi import Depends
+
+logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
+logger = logging.getLogger(__name__)
+
+
+[docs] +class WifiConnectionService: + """Сервис для управления подключением к Wi-Fi сетям с использованием nmcli. + + Предоставляет методы для подключения, отключения и проверки статуса Wi-Fi соединений. + """ + + def __init__(self, translations: Translations = Depends(get_translations())): + """Инициализирует сервис Wi-Fi. + + Автоматически определяет доступный Wi-Fi-интерфейс и сохраняет его имя. + """ + self.interface = self._get_wifi_interface() + self.translations = translations + + def _get_wifi_interface(self) -> str: + """Автоматически определяет Wi-Fi-интерфейс. + + Returns: + str: Название Wi-Fi-интерфейса (например, "wlan0") или пустая строка, + если интерфейс не найден или Wi-Fi отключён. + + Raises: + Exception: Если произошла ошибка при выполнении команд nmcli. + """ + try: + result = subprocess.run(["nmcli", "radio", "wifi"], capture_output=True, text=True) + if "enabled" in result.stdout.lower(): + interfaces = subprocess.run(["nmcli", "device", "status"], capture_output=True, text=True).stdout + for line in interfaces.splitlines(): + if "wifi" in line.lower() and "connected" in line.lower(): + iface = line.split()[0] + logger.info(f"Using Wi-Fi interface: {iface}") + return iface + logger.warning("Wi-Fi interface not found or not connected") + return "wlan0" + logger.error("Wi-Fi radio is disabled") + return "" + except Exception as e: + logger.error(f"Error detecting Wi-Fi interface: {str(e)}") + return "" + +
+[docs] + def connect(self, ssid: str, password: str) -> Tuple[bool, Optional[str]]: + """Подключается к Wi-Fi сети по SSID и паролю. + + Args: + ssid (str): Идентификатор сети Wi-Fi. + password (str): Пароль для подключения. + + Returns: + Tuple[bool, Optional[str]]: Кортеж (успех, сообщение об ошибке). + - Успех: (True, None). + - Ошибка: (False, сообщение об ошибке). + + Raises: + subprocess.CalledProcessError: Если команда nmcli завершилась с ошибкой. + Exception: Если произошла неизвестная ошибка при выполнении. + """ + try: + if not self.interface: + return False, self.translations.gettext("Wi-Fi-интерфейс не доступен") + + result = subprocess.run(["nmcli", "-f", "SSID", "device", "wifi", "list"], capture_output=True, text=True) + if ssid not in result.stdout: + return False, self.translations.gettext("Сеть с SSID {ssid} не найдена в списке доступных сетей").format(ssid=ssid) + + cmd = ["nmcli", "device", "wifi", "connect", ssid, "password", password] + process = subprocess.run(cmd, capture_output=True, text=True, check=False) + if process.returncode != 0: + error_msg = process.stderr.lower() + if "secrets were required" in error_msg or "authentication failed" in error_msg or "802-11-wireless-security.psk: недействительное свойство" in process.stderr: + return False, self.translations.gettext("Ошибка подключения к {ssid}: Неверный пароль").format(ssid=ssid) + elif "no network with ssid" in error_msg: + return False, self.translations.gettext("Ошибка подключения к {ssid}: Сеть с SSID {ssid} недоступна или неверно указана").format(ssid=ssid) + else: + return False, self.translations.gettext("Ошибка подключения к {ssid}: {error}").format(ssid=ssid, error=process.stderr) + logger.info(f"Successfully connected to network {ssid}") + return True, None + except subprocess.CalledProcessError as e: + logger.error(f"Error connecting to {ssid}: {e.stderr}") + error_msg = e.stderr.lower() + if "secrets were required" in error_msg or "authentication failed" in error_msg or "802-11-wireless-security.psk: недействительное свойство" in e.stderr: + return False, self.translations.gettext("Ошибка подключения к {ssid}: Неверный пароль").format(ssid=ssid) + elif "no network with ssid" in error_msg: + return False, self.translations.gettext("Ошибка подключения к {ssid}: Сеть с SSID {ssid} недоступна или неверно указана").format(ssid=ssid) + return False, self.translations.gettext("Ошибка подключения к {ssid}: {error}").format(ssid=ssid, error=e.stderr) + except Exception as e: + logger.error(f"Unknown error connecting to {ssid}: {str(e)}") + return False, self.translations.gettext("Ошибка подключения к {ssid}: Неизвестная ошибка - {error}").format(ssid=ssid, error=str(e))
+ + +
+[docs] + def disconnect(self, ssid: str) -> Tuple[bool, Optional[str]]: + """Отключается от Wi-Fi сети по указанному SSID. + + Args: + ssid (str): Идентификатор сети Wi-Fi для отключения. + + Returns: + Tuple[bool, Optional[str]]: Кортеж (успех, сообщение об ошибке). + - Успех: (True, None или сообщение, если подключение не найдено). + - Ошибка: (False, сообщение об ошибке). + + Raises: + subprocess.CalledProcessError: Если команда nmcli завершилась с ошибкой. + Exception: Если произошла неизвестная ошибка при выполнении. + """ + try: + if not self.interface: + return False, self.translations.gettext("Wi-Fi-интерфейс не доступен") + result = subprocess.run(["nmcli", "connection", "show", "--active"], capture_output=True, text=True) + active_connections = result.stdout.strip().splitlines() + if not active_connections: + logger.info(f"No active connections for SSID {ssid}") + return True, self.translations.gettext("Нет активного подключения для SSID {ssid}").format(ssid=ssid) + for conn in active_connections: + conn_details = conn.split() + if len(conn_details) > 1: + conn_name = conn_details[0] + conn_info = subprocess.run(["nmcli", "connection", "show", conn_name], capture_output=True, text=True).stdout + if "802-11-wireless.ssid:" in conn_info and ssid in conn_info: + subprocess.run(["nmcli", "connection", "down", conn_name], capture_output=True, text=True, check=True) + logger.info(f"Disconnected from SSID {ssid}") + return True, None + logger.warning(f"Active connection with SSID {ssid} not found") + return True, self.translations.gettext("Активное подключение с SSID {ssid} не найдено").format(ssid=ssid) + except subprocess.CalledProcessError as e: + logger.error(f"Error disconnecting from {ssid}: {e.stderr}") + return False, self.translations.gettext("Ошибка отключения от {ssid}: {error}").format(ssid=ssid, error=e.stderr) + except Exception as e: + logger.error(f"Unknown error disconnecting from {ssid}: {str(e)}") + return False, self.translations.gettext("Ошибка отключения от {ssid}: {error}").format(ssid=ssid, error=str(e))
+ + +
+[docs] + def get_status(self) -> Tuple[bool, Optional[str]]: + """Проверяет статус текущего подключения к Wi-Fi. + + Returns: + Tuple[bool, Optional[str]]: Кортеж (успех, SSID или сообщение об ошибке). + - Успех: (True, SSID текущей сети). + - Ошибка или отсутствие подключения: (False, сообщение об ошибке). + + Raises: + Exception: Если произошла ошибка при выполнении команды nmcli. + """ + try: + if not self.interface: + return False, self.translations.gettext("Wi-Fi-интерфейс не доступен") + result = subprocess.run(["nmcli", "connection", "show", "--active"], capture_output=True, text=True) + if result.stdout.strip(): + ssid = result.stdout.splitlines()[1].split()[0] + logger.info(f"Current network: {ssid}") + return True, ssid + return False, self.translations.gettext("Не подключено к Wi-Fi") + except Exception as e: + logger.error(f"Error checking status: {str(e)}") + return False, self.translations.gettext("Ошибка проверки статуса: {error}").format(error=str(e))
+
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/SRV/docs/_build/html/_modules/services/firewalld_service.html b/SRV/docs/_build/html/_modules/services/firewalld_service.html new file mode 100644 index 0000000..905309f --- /dev/null +++ b/SRV/docs/_build/html/_modules/services/firewalld_service.html @@ -0,0 +1,522 @@ + + + + + + + + services.firewalld_service - GONETS SRV 0.0.3 documentation + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark, in light mode + + + + + + + + + + + + + + + Auto light/dark, in dark mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Skip to content + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for services.firewalld_service

+"""
+Сервис для управления firewalld через D‑Bus.
+
+Назначение:
+- Обёртка над интерфейсами `org.fedoraproject.FirewallD1` для выполнения
+  типовых операций по зонам: просмотр/изменение дефолтной зоны, добавление и
+  удаление сервисов, портов, источников, перезагрузка конфигурации.
+
+Примечания:
+- Используется `pydbus.SystemBus` для доступа к системной шине.
+- Методы генерируют дружелюбные исключения при ошибках, содержащие контекст
+  операции.
+"""
+from re import DEBUG
+from typing import Dict, List, Optional
+
+from pydbus import SystemBus
+
+DEBUG = True
+
+
+
+[docs] +class FirewalldService: + + def __init__(self): + self.bus = SystemBus() + self.proxy = self.bus.get("org.fedoraproject.FirewallD1", + "/org/fedoraproject/FirewallD1") + +
+[docs] + def get_zones(self) -> List[str]: + """Get list of all zones.""" + try: + return self.proxy.getZones() + except Exception as e: + raise Exception(f"Failed to get zones: {str(e)}")
+ + +
+[docs] + def get_default_zone(self) -> str: + """Get the default zone.""" + try: + return self.proxy.getDefaultZone() + except Exception as e: + raise Exception(f"Failed to get default zone: {str(e)}")
+ + +
+[docs] + def set_default_zone(self, zone: str) -> None: + """Set the default zone.""" + try: + self.proxy.setDefaultZone(zone) + except Exception as e: + raise Exception(f"Failed to set default zone to {zone}: {str(e)}")
+ + +
+[docs] + def add_service(self, + zone: str, + service: str, + timeout: Optional[int] = 0) -> None: + """Add a service to a zone.""" + try: + self.proxy.addService(zone, service, timeout) + except Exception as e: + raise Exception( + f"Failed to add service {service} to zone {zone}: {str(e)}")
+ + +
+[docs] + def remove_service(self, zone: str, service: str) -> None: + """Remove a service from a zone.""" + try: + self.proxy.removeService(zone, service) + except Exception as e: + raise Exception( + f"Failed to remove service {service} from zone {zone}: {str(e)}" + )
+ + +
+[docs] + def get_services(self, zone: str) -> List[str]: + """Get list of services in a zone.""" + try: + return self.proxy.getServices(zone) + except Exception as e: + raise Exception( + f"Failed to get services for zone {zone}: {str(e)}")
+ + +
+[docs] + def add_port(self, + zone: str, + port: int, + protocol: str, + timeout: Optional[int] = 0) -> None: + """Add a port to a zone.""" + try: + if DEBUG: + print(f"Adding port {port}/{protocol} to zone {zone}") + print(f"Timeout: {timeout}") + # Get the zone interface + zone_proxy = self.bus.get("org.fedoraproject.FirewallD1", + "/org/fedoraproject/FirewallD1") + zone_iface = zone_proxy["org.fedoraproject.FirewallD1.zone"] + # Convert port to string + port_str = str(port) + # Convert timeout to integer + timeout_int = int(timeout) if timeout is not None else 0 + # Call addPort with the correct signature (sssi) + zone_iface.addPort(zone, port_str, protocol, timeout_int) + except Exception as e: + raise Exception( + f"Failed to add port {port}/{protocol} to zone {zone}: {str(e)}" + )
+ + +
+[docs] + def remove_port(self, zone: str, port: int, protocol: str) -> None: + """Remove a port from a zone.""" + try: + port_str = f"{port}/{protocol}" + self.proxy.removePort(zone, port_str) + except Exception as e: + raise Exception( + f"Failed to remove port {port}/{protocol} from zone {zone}: {str(e)}" + )
+ + +
+[docs] + def get_ports(self, zone: str) -> List[str]: + """Get list of ports in a zone.""" + try: + ports = self.proxy.getPorts(zone) + # Convert port list items to strings in format "port/protocol" + return [f"{port[0]}/{port[1]}" for port in ports] + except Exception as e: + raise Exception(f"Failed to get ports for zone {zone}: {str(e)}")
+ + +
+[docs] + def add_source(self, zone: str, source: str) -> None: + """Add a source to a zone.""" + try: + self.proxy.addSource(zone, source) + except Exception as e: + raise Exception( + f"Failed to add source {source} to zone {zone}: {str(e)}")
+ + +
+[docs] + def remove_source(self, zone: str, source: str) -> None: + """Remove a source from a zone.""" + try: + self.proxy.removeSource(zone, source) + except Exception as e: + raise Exception( + f"Failed to remove source {source} from zone {zone}: {str(e)}")
+ + +
+[docs] + def get_sources(self, zone: str) -> List[str]: + """Get list of sources in a zone.""" + try: + return self.proxy.getSources(zone) + except Exception as e: + raise Exception(f"Failed to get sources for zone {zone}: {str(e)}")
+ + +
+[docs] + def reload(self) -> None: + """Reload firewall rules.""" + try: + self.proxy.reload() + except Exception as e: + raise Exception(f"Failed to reload firewall rules: {str(e)}")
+ + +
+[docs] + def get_zone_settings(self, zone: str) -> Dict: + """Get all settings for a zone.""" + try: + return { + "services": self.get_services(zone), + "ports": self.get_ports(zone), + "sources": self.get_sources(zone), + } + except Exception as e: + raise Exception( + f"Failed to get zone settings for {zone}: {str(e)}")
+
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/SRV/docs/_build/html/_modules/services/ip_service.html b/SRV/docs/_build/html/_modules/services/ip_service.html new file mode 100644 index 0000000..45ae784 --- /dev/null +++ b/SRV/docs/_build/html/_modules/services/ip_service.html @@ -0,0 +1,468 @@ + + + + + + + + services.ip_service - GONETS SRV 0.0.3 documentation + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark, in light mode + + + + + + + + + + + + + + + Auto light/dark, in dark mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Skip to content + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for services.ip_service

+"""
+Сервис проверки и применения IP‑настроек через NetworkManager (D‑Bus).
+
+Назначение:
+- Перед применением конфигурации проверяет занятость IP адреса, сканируя сегмент
+  сети средствами ARP (через `NetworkScanner`).
+- Применяет изменения профиля подключения через `sdbus_async.networkmanager` и
+  выполняет переподключение интерфейса.
+
+Требования/ограничения:
+- Доступ к системной шине D‑Bus и права на изменения сетевых профилей.
+- Структура входных данных должна соответствовать схеме `ipv4_updates` для
+  корректного обновления вложенных полей у профиля.
+"""
+from typing import Dict, Any
+from sdbus_async.networkmanager import (
+    NetworkManager,
+    NetworkManagerSettings,
+    NetworkConnectionSettings,
+    NetworkDeviceGeneric,
+)
+import sdbus
+from sdbus_async.networkmanager.settings import ConnectionProfile
+from services.arp_service import NetworkScanner
+from utils.i18n import get_translations
+from babel.support import Translations
+from fastapi import Depends
+
+
+[docs] +class IPService: + """Сервис проверки доступности IP-адреса и применения настроек сети. + + Предоставляет асинхронные методы для проверки занятости IP-адреса с использованием + ARP-сканирования и применения настроек через NetworkManager. + """ + + def __init__(self): + """Инициализирует сервис IP. + + Создаёт объект ARP-сканера и устанавливает системную шину D-Bus как + по умолчанию. + """ + self.arp_scanner = NetworkScanner() + self.bus = sdbus.sd_bus_open_system() + sdbus.set_default_bus(self.bus) + + def _reset_bus(self): + """Перезапускает соединение с системной шиной D-Bus. + + Закрывает текущее соединение и создаёт новое, устанавливая его как + по умолчанию. + """ + new_bus = sdbus.sd_bus_open_system() + sdbus.set_default_bus(new_bus) + if hasattr(self, "bus") and self.bus is not None: + self.bus = None + self.bus = new_bus + +
+[docs] + async def check_ip_config(self, args: Dict[str, Any], translations: Translations = Depends(get_translations())) -> Dict[str, Any]: + """Проверяет доступность IP-адреса перед его применением. + + Выполняет ARP-сканирование сети для проверки, занят ли указанный IP-адрес. + Перезапускает шину D-Bus перед выполнением. + + Args: + args (Dict[str, Any]): Словарь с данными, включая "ipv4_updates" + с полем "ipv4.address-data" для нового IP-адреса. + translations (Translations): Объект переводов. + + Returns: + Dict[str, Any]: Словарь с ключами: + - "available" (bool): Доступен ли IP-адрес. + - "message" (str): Сообщение о результате проверки. + + Raises: + Exception: Если произошла ошибка при сканировании ARP. + """ + self._reset_bus() + clients = self.arp_scanner.scan_network() + new_ip = args["ipv4_updates"]["ipv4.address-data"][0]["address"] + if any(client["ip"] == new_ip for client in clients): + return { + "available": False, + "message": translations.gettext("IP-адрес {ip} уже занят.").format(ip=new_ip) + } + return { + "available": True, + "message": translations.gettext("Настройки сетевого профиля доступны для использования.") + }
+ + +
+[docs] + async def apply_ip_config(self, args: Dict[str, Any], translations: Translations = Depends(get_translations())) -> None: + """Применяет проверенные настройки IP к указанному соединению. + + Перезапускает шину D-Bus, обновляет настройки соединения через NetworkManager, + отключает и переподключает устройство к сети. + + Args: + args (Dict[str, Any]): Словарь с данными, включая: + - "connection_id" (str): Идентификатор соединения. + - "ipv4_updates" (Dict): Обновления настроек IPv4. + translations (Translations): Объект переводов. + + Raises: + ValueError: Если соединение с указанным ID не найдено. + RuntimeError: Если устройство для интерфейса не найдено. + + Note: + Требуются права для изменения настроек через NetworkManager. + """ + self._reset_bus() + nm = NetworkManager() + settings = NetworkManagerSettings() + connection_paths = await settings.get_connections_by_id( + args["connection_id"]) + + if not connection_paths: + raise ValueError(translations.gettext("Подключение {connection_id} не найдено").format(connection_id=args["connection_id"])) + + connection = NetworkConnectionSettings(connection_paths[0]) + profile = await connection.get_profile() + settings_dict = profile.to_settings_dict() + + for key_path, value in args["ipv4_updates"].items(): + section, *props = key_path.split(".") + current = settings_dict[section] + for prop in props[:-1]: + current = current.setdefault(prop, {}) + current[props[-1]] = value + + new_profile = ConnectionProfile.from_settings_dict(settings_dict) + await connection.update(new_profile.to_dbus()) + print(f"Updated settings for {args['connection_id']}") + + interface_name = settings_dict["connection"]["interface-name"] + devices = await nm.get_devices() + for device_path in devices: + device = NetworkDeviceGeneric(device_path) + if await device.interface == interface_name: + await device.disconnect() + print(f"Disconnected from {interface_name}") + await nm.activate_connection(connection=connection_paths[0], device=device_path) + print(f"Reconnected to {args['connection_id']}") + return + + raise RuntimeError(translations.gettext("Устройство для интерфейса {interface} не найдено").format(interface=interface_name))
+
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/SRV/docs/_build/html/_modules/services/network_service.html b/SRV/docs/_build/html/_modules/services/network_service.html new file mode 100644 index 0000000..40d3045 --- /dev/null +++ b/SRV/docs/_build/html/_modules/services/network_service.html @@ -0,0 +1,744 @@ + + + + + + + + services.network_service - GONETS SRV 0.0.3 documentation + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark, in light mode + + + + + + + + + + + + + + + Auto light/dark, in dark mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Skip to content + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for services.network_service

+"""
+Сервис чтения сетевого состояния через NetworkManager (D‑Bus).
+
+Назначение:
+- Предоставляет асинхронные методы для сборки унифицированных моделей
+  сетевых интерфейсов (Ethernet/Wi‑Fi), их IP‑конфигураций, а также получения
+  имени хоста. Используется API‑слоем для визуализации и управления.
+
+Особенности:
+- Фильтрация loopback и явно неактивных интерфейсов.
+- Адаптация данных D‑Bus в pydantic‑модели (`NetworkInterface`, `WiFiDevice`,
+  `NetworkInterfacesContainer`).
+"""
+from typing import List, Dict, Tuple, Union, Optional
+from sdbus_async.networkmanager import (
+    NetworkManager,
+    NetworkDeviceGeneric,
+    IPv4Config,
+    IPv6Config,
+    NetworkManagerSettings,
+    NetworkDeviceWireless,
+    AccessPoint,
+    DeviceType,
+)
+from sdbus import sd_bus_open_system
+from models.network import (
+    NetworkInterface,
+    IPv4Route,
+    IPv6Route,
+    IPConfig,
+    IndividualIPConfig,
+    NetworkInterfacesContainer,
+    WiFiDevice,
+)
+import subprocess
+from utils.i18n import get_translations, _
+from babel.support import Translations
+from fastapi import Depends
+
+
+[docs] +class NetworkService: + """Сервис для взаимодействия с NetworkManager через D-Bus. + + Предоставляет асинхронные методы для получения информации о сетевых интерфейсах, + их конфигурациях IP, Wi-Fi и состояниях. + """ + + def __init__(self, translations: Translations = Depends(get_translations())): + """Инициализирует сервис NetworkManager. + + Устанавливает системный D-Bus и создаёт объекты для взаимодействия с + NetworkManager и его настройками. + """ + self.system_bus = sd_bus_open_system() + self.nm = NetworkManager(self.system_bus) + self.nm_settings = NetworkManagerSettings(self.system_bus) + self.translations = translations + +
+[docs] + async def get_ip_method(self, device: NetworkDeviceGeneric, ip_type: str) -> str: + """Получает метод IP (например, "auto", "manual") для указанного устройства. + + Args: + device (NetworkDeviceGeneric): Объект устройства NetworkManager. + ip_type (str): Тип IP (например, "ipv4" или "ipv6"). + + Returns: + str: Метод IP или "unknown" в случае ошибки. + + Raises: + Exception: Если произошла ошибка при получении настроек соединения. + """ + try: + config = await device.get_applied_connection() + settings = config[0].get(ip_type, {}) + return str(settings.get("method", ("s", "unknown"))[1]) + except Exception: + return "unknown"
+ + +
+[docs] + async def get_connection_name(self, device: NetworkDeviceGeneric) -> str: + """Извлекает имя соединения (GENERAL.CONNECTION) для устройства. + + Args: + device (NetworkDeviceGeneric): Объект устройства NetworkManager. + + Returns: + str: Имя соединения или "unknown" в случае ошибки. + + Raises: + Exception: Если произошла ошибка при получении настроек соединения. + """ + try: + applied_connection = await device.get_applied_connection() + connection_settings = applied_connection[0] + connection_info = connection_settings.get("connection", {}) + return str(connection_info.get("id", ("s", "unknown"))[1]) + except Exception as e: + return "unknown"
+ + +
+[docs] + async def get_ip_config(self, device: NetworkDeviceGeneric, ip_version: str) -> List[IndividualIPConfig]: + """Получает конфигурацию IP для устройства. + + Args: + device (NetworkDeviceGeneric): Объект устройства NetworkManager. + ip_version (str): Версия IP ("4" или "6"). + + Returns: + List[IndividualIPConfig]: Список конфигураций IP, включая адреса и маршруты. + + Raises: + Exception: Если произошла ошибка при получении данных IP. + """ + configs = [] + device_ip = await getattr(device, f"ip{ip_version}_config") + if device_ip != "/": + ip_config = globals()[f"IPv{ip_version}Config"](device_ip, self.system_bus) + address_data = await ip_config.address_data + for address in address_data: + config = IndividualIPConfig( + address=str(address["address"][1]), + routes=[ + globals()[f"IPv{ip_version}Route"]( + destination=route.get("dest", ("s", "0.0.0.0" if ip_version == "4" else "::"))[1], + prefix=route.get("prefix", ("u", 0))[1], + next_hop=route.get("next-hop", ("s", "0.0.0.0" if ip_version == "4" else "::"))[1], + metric=route.get("metric", ("u", 0))[1], + ) for route in await ip_config.route_data + ], + method=await self.get_ip_method(device, f"ipv{ip_version}"), + gateway=str(await ip_config.gateway), + dns=[ + str(ns["address"][1]) if ip_version == "4" else str(ns) + for ns in await getattr( + ip_config, + "nameserver_data" if ip_version == "4" else "nameservers", + ) + ], + ) + configs.append(config) + return configs
+ + +
+[docs] + async def get_wifi_info(self, wifi_device: NetworkDeviceWireless) -> Dict[str, Optional[Union[str, int]]]: + """Получает информацию о Wi-Fi устройстве. + + Args: + wifi_device (NetworkDeviceWireless): Объект Wi-Fi устройства NetworkManager. + + Returns: + Dict[str, Optional[Union[str, int]]]: Словарь с данными Wi-Fi (SSID, BSSID, + bitrate, strength, frequency, mode), где значения могут быть None. + + Raises: + Exception: Если произошла ошибка при получении данных точки доступа. + """ + active_ap = await wifi_device.active_access_point + wifi_info = { + "ssid": None, + "bssid": None, + "bitrate": await wifi_device.bitrate, + "strength": None, + "frequency": None, + "mode": None, + } + if active_ap != "/": + ap = AccessPoint(active_ap, self.system_bus) + wifi_info.update({ + "ssid": await ap.ssid, + "bssid": await ap.hw_address, + "strength": await ap.strength, + "frequency": await ap.frequency, + "mode": await ap.mode, + }) + return wifi_info
+ + +
+[docs] + async def get_network_interfaces( + self, + initial_positions: Dict[str, Tuple[int, int]] = None + ) -> List[Union[NetworkInterface, WiFiDevice]]: + """Получает список сетевых интерфейсов. + + Args: + initial_positions (Dict[str, Tuple[int, int]], optional): Словарь начальных + координат (x, y) для интерфейсов. По умолчанию None (используются (100, 100)). + + Returns: + List[Union[NetworkInterface, WiFiDevice]]: Список объектов интерфейсов, + включая Wi-Fi устройства. + + Исключает интерфейсы типа LOOPBACK и неактивные состояния (state == "10"). + """ + devices_paths = await self.nm.get_devices() + network_interfaces = [] + initial_positions = initial_positions or {} + + for device_path in devices_paths: + generic_device = NetworkDeviceGeneric(device_path, self.system_bus) + interface_name = str(await generic_device.interface) + device_type = DeviceType(await generic_device.device_type).name + + if device_type == "LOOPBACK": + continue + + device_state = str(await generic_device.state) + if device_state == "10": + continue + + common_data = { + "name": interface_name, + "type": device_type, + "device_state": device_state, + "active_connection": await self.get_connection_name(generic_device), + "mac_address": str(await generic_device.hw_address), + "device_mtu": str(await generic_device.mtu), + "ipv4": IPConfig(configurations=await self.get_ip_config(generic_device, "4")), + "ipv6": IPConfig(configurations=await self.get_ip_config(generic_device, "6")), + "x": initial_positions.get(interface_name, (100, 100))[0], + "y": initial_positions.get(interface_name, (100, 100))[1], + } + + if device_type == "WIFI": + wifi_device = NetworkDeviceWireless(device_path, self.system_bus) + wifi_info = await self.get_wifi_info(wifi_device) + network_interfaces.append(WiFiDevice(**common_data, **wifi_info)) + else: + network_interfaces.append(NetworkInterface(**common_data)) + + return network_interfaces
+ + +
+[docs] + async def get_hostname(self) -> Dict[str, str]: + """Возвращает имя хоста из команды hostname в формате JSON. + + Returns: + Dict[str, str]: Словарь с ключом "hostname" и значением имени хоста. + + Raises: + RuntimeError: Если произошла ошибка при выполнении команды hostname + или обработке вывода. + """ + try: + result = subprocess.run(["hostname"], + capture_output=True, + text=True, + check=True) + hostname = result.stdout.strip() + return {"hostname": hostname} + except subprocess.CalledProcessError as e: + raise RuntimeError(self.translations.gettext("Не удалось выполнить команду hostname: {error}").format(error=str(e))) + except Exception as e: + raise RuntimeError(self.translations.gettext("Ошибка обработки вывода hostname: {error}").format(error=str(e)))
+ + +
+[docs] + async def get_interfaces_by_state( + self, + state: str, + initial_positions: Dict[str, Tuple[int, int]] = None + ) -> List[NetworkInterface]: + """Получает список интерфейсов по указанному состоянию. + + Args: + state (str): Состояние интерфейса (например, "100" для активных). + initial_positions (Dict[str, Tuple[int, int]], optional): Словарь начальных + координат (x, y) для интерфейсов. По умолчанию None. + + Returns: + List[NetworkInterface]: Список интерфейсов с указанным состоянием. + """ + all_interfaces = await self.get_network_interfaces(initial_positions) + return [ + interface for interface in all_interfaces + if interface.device_state == state + ]
+ + +
+[docs] + async def get_interfaces_container( + self, + state: Optional[str] = None, + initial_positions: Dict[str, Tuple[int, int]] = None, + container_x: int = 0, + container_y: int = 0, + ) -> NetworkInterfacesContainer: + """Получает объект, содержащий список интерфейсов и координаты контейнера. + + Args: + state (Optional[str], optional): Состояние интерфейсов (например, "100"). + По умолчанию None (все интерфейсы). + initial_positions (Dict[str, Tuple[int, int]], optional): Словарь начальных + координат (x, y) для интерфейсов. По умолчанию None. + container_x (int, optional): Координата X контейнера. По умолчанию 0. + container_y (int, optional): Координата Y контейнера. По умолчанию 0. + + Returns: + NetworkInterfacesContainer: Объект с интерфейсами и координатами контейнера. + """ + if state: + interfaces = await self.get_interfaces_by_state(state, initial_positions) + else: + interfaces = await self.get_network_interfaces(initial_positions) + return NetworkInterfacesContainer(interfaces=interfaces, + container_x=container_x, + container_y=container_y)
+ + +
+[docs] + async def get_network_interfaces_container( + self, + initial_positions: Optional[Dict[str, Tuple[int, int]]] = None, + ) -> NetworkInterfacesContainer: + """Получает контейнер с сетевыми интерфейсами и координатами контейнера. + + Args: + initial_positions (Optional[Dict[str, Tuple[int, int]]], optional): + Словарь начальных координат (x, y) для интерфейсов. По умолчанию None. + + Returns: + NetworkInterfacesContainer: Объект с интерфейсами и координатами (0, 0). + """ + interfaces = await self.get_network_interfaces(initial_positions) + return NetworkInterfacesContainer(interfaces=interfaces, + container_x=0, + container_y=0)
+ + +
+[docs] + async def get_activated_interfaces( + self, + initial_positions: Dict[str, Tuple[int, int]] = None + ) -> List[NetworkInterface]: + """Получает список активных сетевых интерфейсов. + + Args: + initial_positions (Dict[str, Tuple[int, int]], optional): Словарь начальных + координат (x, y) для интерфейсов. По умолчанию None. + + Returns: + List[NetworkInterface]: Список активных интерфейсов (state == "100"). + """ + return await self.get_interfaces_by_state("100", initial_positions)
+ + +
+[docs] + async def get_deactivated_interfaces( + self, + initial_positions: Dict[str, Tuple[int, int]] = None + ) -> List[NetworkInterface]: + """Получает список неактивных сетевых интерфейсов. + + Args: + initial_positions (Dict[str, Tuple[int, int]], optional): Словарь начальных + координат (x, y) для интерфейсов. По умолчанию None. + + Returns: + List[NetworkInterface]: Список интерфейсов с state != "100". + """ + all_interfaces = await self.get_network_interfaces(initial_positions) + return [ + interface for interface in all_interfaces + if interface.device_state != "100" + ]
+ + +
+[docs] + async def get_activated_interfaces_container( + self, + initial_positions: Dict[str, Tuple[int, int]] = None, + container_x: int = 0, + container_y: int = 0, + ) -> NetworkInterfacesContainer: + """Получает объект, содержащий список активных интерфейсов и координаты контейнера. + + Args: + initial_positions (Dict[str, Tuple[int, int]], optional): Словарь начальных + координат (x, y) для интерфейсов. По умолчанию None. + container_x (int, optional): Координата X контейнера. По умолчанию 0. + container_y (int, optional): Координата Y контейнера. По умолчанию 0. + + Returns: + NetworkInterfacesContainer: Объект с активными интерфейсами и координатами. + """ + return await self.get_interfaces_container("100", initial_positions, container_x, container_y)
+ + +
+[docs] + async def get_deactivated_interfaces_container( + self, + initial_positions: Dict[str, Tuple[int, int]] = None, + container_x: int = 0, + container_y: int = 0, + ) -> NetworkInterfacesContainer: + """Получает объект, содержащий список неактивных интерфейсов и координаты контейнера. + + Args: + initial_positions (Dict[str, Tuple[int, int]], optional): Словарь начальных + координат (x, y) для интерфейсов. По умолчанию None. + container_x (int, optional): Координата X контейнера. По умолчанию 0. + container_y (int, optional): Координата Y контейнера. По умолчанию 0. + + Returns: + NetworkInterfacesContainer: Объект с неактивными интерфейсами и координатами. + """ + interfaces = await self.get_deactivated_interfaces(initial_positions) + return NetworkInterfacesContainer(interfaces=interfaces, container_x=container_x, container_y=container_y)
+
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/SRV/docs/_build/html/_modules/services/settings_service.html b/SRV/docs/_build/html/_modules/services/settings_service.html new file mode 100644 index 0000000..5caff6b --- /dev/null +++ b/SRV/docs/_build/html/_modules/services/settings_service.html @@ -0,0 +1,431 @@ + + + + + + + + services.settings_service - GONETS SRV 0.0.3 documentation + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark, in light mode + + + + + + + + + + + + + + + Auto light/dark, in dark mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Skip to content + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for services.settings_service

+import json
+import logging
+from typing import Optional
+
+from utils.i18n import get_translations
+from babel.support import Translations
+from fastapi import Depends
+from pathlib import Path
+
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger(__name__)
+
+
+[docs] +class SettingsService: + """Сервис для управления настройками и очистки данных сети.""" + + def __init__(self, translations: Translations = Depends(get_translations())): + self.subnet_base_path = "storage/subnets" + self.wifi_file = "storage/wifi/wifi.json" + self.translations = translations + +
+[docs] + def clear_subnet(self, subnet: Optional[str] = None) -> bool: + """Очищает данные узлов подсети в указанном файле или во всех файлах. + + Args: + subnet (Optional[str]): Подсеть в формате CIDR (например, "10.1.1.0/24"). + Если None, очищает все подсети. + + Returns: + bool: True, если очистка успешна. + + Raises: + Exception: Если произошла ошибка при записи в файл. + """ + try: + if subnet: + file_path = Path(self.subnet_base_path) / f"{subnet.replace('/', '_')}.json" + if file_path.exists(): + with open(file_path, "w") as f: + json.dump([], f) + logger.info(f"Subnet nodes data cleared for {subnet}") + else: + logger.info(f"Subnet file not found for {subnet}, clearing considered successful") + return True + else: + for file_path in Path(self.subnet_base_path).glob("*.json"): + with open(file_path, "w") as f: + json.dump([], f) + logger.info(f"Cleared subnet data in {file_path}") + return True + except FileNotFoundError: + logger.info("No subnet files found, clearing considered successful") + return True + except Exception as e: + logger.error(f"Error clearing subnet nodes: {str(e)}") + raise Exception(self.translations.gettext("Ошибка очистки узлов подсети: {error}").format(error=str(e)))
+ + +
+[docs] + def clear_wifi(self) -> bool: + """Очищает данные точек доступа Wi-Fi в файле wifi.json. + + Returns: + bool: True, если очистка успешна. + + Raises: + Exception: Если произошла ошибка при записи в файл. + """ + try: + with open(self.wifi_file, "w") as f: + json.dump([], f) + logger.info("Wi-Fi access points data successfully cleared") + return True + except FileNotFoundError: + logger.info("Wi-Fi file not found, clearing considered successful") + return True + except Exception as e: + logger.error(f"Error clearing Wi-Fi access points: {str(e)}") + raise Exception(self.translations.gettext("Ошибка очистки точек доступа Wi-Fi: {error}").format(error=str(e)))
+ + +
+[docs] + def clear_all_data(self) -> bool: + """Очищает данные узлов всех подсетей и точек доступа Wi-Fi. + + Returns: + bool: True, если очистка успешна. + + Raises: + Exception: Если произошла ошибка при записи в один из файлов. + """ + try: + try: + for file_path in Path(self.subnet_base_path).glob("*.json"): + with open(file_path, "w") as f: + json.dump([], f) + logger.info(f"Cleared subnet data in {file_path}") + except FileNotFoundError: + logger.info("No subnet files found, clearing considered successful") + try: + with open(self.wifi_file, "w") as f: + json.dump([], f) + except FileNotFoundError: + logger.info("Wi-Fi file not found, clearing considered successful") + logger.info("All network data successfully cleared") + return True + except Exception as e: + logger.error(f"Error clearing all network data: {str(e)}") + raise Exception(self.translations.gettext("Ошибка очистки всех сетевых данных: {error}").format(error=str(e)))
+
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/SRV/docs/_build/html/_modules/services/subnet_service.html b/SRV/docs/_build/html/_modules/services/subnet_service.html new file mode 100644 index 0000000..c37b763 --- /dev/null +++ b/SRV/docs/_build/html/_modules/services/subnet_service.html @@ -0,0 +1,1095 @@ + + + + + + + + services.subnet_service - GONETS SRV 0.0.3 documentation + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark, in light mode + + + + + + + + + + + + + + + Auto light/dark, in dark mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Skip to content + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for services.subnet_service

+from scapy.config import conf
+from mac_vendor_lookup import MacLookup
+from typing import List, Optional, Tuple, Dict
+import socket
+import logging
+import netifaces
+import ipaddress
+from concurrent.futures import ThreadPoolExecutor
+import time
+from functools import lru_cache
+import json
+from pathlib import Path
+from datetime import datetime, timedelta
+import subprocess
+
+from scapy.layers.l2 import ARP, Ether
+from scapy.sendrecv import srp
+from utils.i18n import get_translations
+from babel.support import Translations
+from fastapi import Depends
+
+from models.subnet import SubnetScanResult, Device
+
+logging.basicConfig(
+    level=logging.INFO,
+    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
+)
+logger = logging.getLogger(__name__)
+
+
+[docs] +def get_subnet_service(translations: Translations = Depends(get_translations())): + """Фабричная функция для создания экземпляра SubnetService. + + Использует зависимости FastAPI для получения объекта переводов и возвращает + экземпляр SubnetService с текущей локализацией. + + Args: + translations (Translations): Объект переводов для текущего языка. + + Returns: + SubnetService: Экземпляр сервиса для работы с подсетями. + """ + return SubnetService(translations)
+ + +
+[docs] +class NetworkScanner: + """Класс для сканирования подсети с использованием ARP-запросов. + + Назначение: + Обнаруживает активные устройства в локальной сети, отправляя широковещательные + ARP‑запросы по указанной подсети и собирая ответы (IP, MAC). + + Детали реализации: + - Использует `scapy` (`Ether` + `ARP`) для генерации пакетов и `srp` для + отправки/получения на канальном уровне от имени заданного интерфейса. + - Таймаут ожидания ответа короткий (2 секунды), ориентирован на интерактивные + сценарии UI. + + Ограничения и замечания: + - Требуются права для отправки низкоуровневых пакетов (capabilities/root). + - В сетях с фильтрацией широковещания количество обнаруженных устройств + может быть ограничено. + """ + def __init__(self, interface: str, translations: Translations): + """Инициализирует сканер сети. + + Устанавливает интерфейс для сканирования и объект переводов. + + Args: + interface (str): Имя сетевого интерфейса (например, "wlp0s20f3"). + translations (Translations): Объект переводов для текущего языка. + """ + self.interface = interface + conf.iface = interface + self.translations = translations + +
+[docs] + def scan(self, subnet: str) -> List[Tuple[str, str]]: + """Сканирует подсеть с использованием ARP-запросов. + + Отправляет ARP-пакеты для обнаружения активных устройств и возвращает + список кортежей (IP, MAC). + + Args: + subnet (str): Подсеть в формате CIDR (например, "10.1.1.0/24"). + + Returns: + List[Tuple[str, str]]: Список кортежей (IP-адрес, MAC-адрес) обнаруженных устройств. + + Raises: + Exception: Если произошла ошибка сканирования. + """ + try: + arp = ARP(pdst=subnet) + except Exception as e: + logger.error(f"Error checking status: {str(e)}") + try: + arp = ARP(pdst=subnet) + ether = Ether(dst="ff:ff:ff:ff:ff:ff") + packet = ether / arp + result = srp(packet, timeout=2, verbose=False, iface=self.interface)[0] + return [(received.psrc, received.hwsrc) for sent, received in result] + except Exception as e: + logger.error(f"Error scanning subnet {subnet}: {str(e)}") + return []
+
+ + +
+[docs] +class PortScanner: + """Класс для сканирования открытых портов на устройствах. + + Назначение: + Плейсхолдер для будущего функционала активного сканирования TCP/UDP‑портов + у обнаруженных узлов. В текущей версии возвращает пустой список. + + План развития: + - Добавить неблокирующее сканирование наиболее распространённых портов + (22, 80, 443 и т.д.) с ограничением по времени. + - Настраиваемые профили глубины и скорости сканирования. + """ + def __init__(self, interface: str, translations: Translations): + """Инициализирует сканер портов. + + Устанавливает интерфейс для сканирования и объект переводов. + + Args: + interface (str): Имя сетевого интерфейса. + translations (Translations): Объект переводов для текущего языка. + """ + self.interface = interface + self.translations = translations + +
+[docs] + def scan(self, ip: str) -> List[str]: + """Сканирует открытые порты на указанном IP-адресе. + + Пока возвращает пустой список, так как функциональность не реализована. + + Args: + ip (str): IP-адрес устройства для сканирования. + + Returns: + List[str]: Список открытых портов (пока пустой). + """ + return []
+
+ + +
+[docs] +class DeviceInfoCollector: + """Класс для сбора информации об устройствах, включая вендоров и хостнеймы. + + Источники данных: + - MacLookup: сопоставление префикса MAC (OUI) с названием производителя. + - DNS: `gethostbyaddr` с малым таймаутом для обратного разрешения имени. + - NetBIOS (nbtscan): опрос сегмента сети для извлечения NetBIOS‑имён. + + Политика кэширования: + - Результаты `get_vendor` кэшируются с помощью `functools.lru_cache`. + - Результаты `nbtscan` аккумулируются в словарях `nbtscan_cache`/`nbtscan_results` + на время жизни экземпляра. + """ + def __init__(self, translations: Translations): + """Инициализирует коллектор информации об устройствах. + + Настраивает кэширование и объект переводов. + + Args: + translations (Translations): Объект переводов для текущего языка. + """ + self.mac_lookup = MacLookup() + self.nbtscan_cache = {} + self.nbtscan_results = {} + self.translations = translations + +
+[docs] + @lru_cache(maxsize=1000) + def get_vendor(self, mac: str) -> str: + """Получает вендора устройства по MAC-адресу. + + Использует кэширование для повышения производительности. + + Args: + mac (str): MAC-адрес устройства. + + Returns: + str: Название вендора или "Неизвестный" в случае ошибки. + """ + try: + return self.mac_lookup.lookup(mac) + except Exception as e: + logger.warning(f"Failed to determine vendor for MAC {mac}: {str(e)}") + return self.translations.gettext("Неизвестный")
+ + +
+[docs] + def scan_nbtscan(self, subnet: str) -> None: + """Сканирует подсеть с помощью nbtscan для получения хостнеймов. + + Сохраняет результаты в кэш и логирует процесс. + + Args: + subnet (str): Подсеть в формате CIDR. + """ + if subnet not in self.nbtscan_results: + try: + logger.debug(f"Starting nbtscan for subnet {subnet}") + result = subprocess.run(["sudo", "nbtscan", "-r", str(subnet)], + capture_output=True, + text=True, + check=False, + timeout=10) + logger.debug(f"Full nbtscan output: {result.stdout}") + if result.returncode == 0: + for line in result.stdout.splitlines(): + logger.debug(f"Processing line: {line}") + if not line.startswith(("IP address", "-", "Doing NBT")): + parts = [p for p in line.split() if p] + if len(parts) >= 2: + ip_addr, hostname = parts[0], parts[1] + if hostname != "<unknown>": + self.nbtscan_cache[ip_addr] = hostname + logger.info(f"NetBIOS: Found hostname {hostname} for IP {ip_addr}") + self.nbtscan_results[subnet] = True + except subprocess.TimeoutExpired: + logger.error(f"NetBIOS: Timeout for subnet {subnet}") + except Exception as e: + logger.error(f"NetBIOS: Error for subnet {subnet}: {str(e)}")
+ + +
+[docs] + def get_hostname(self, ip: str) -> Optional[str]: + """Получает хостнейм для указанного IP-адреса. + + Пробует сначала DNS, затем NetBIOS. + + Args: + ip (str): IP-адрес устройства. + + Returns: + Optional[str]: Хостнейм или None, если не удалось определить. + """ + try: + socket.setdefaulttimeout(2) + return socket.gethostbyaddr(ip)[0] + except (socket.herror, socket.gaierror, socket.timeout): + logger.debug(f"DNS: Failed to resolve name for IP {ip}") + + subnet = ipaddress.ip_network(f"{ip}/24", strict=False) + if subnet not in self.nbtscan_results: + self.scan_nbtscan(str(subnet)) + + if ip in self.nbtscan_cache: + return self.nbtscan_cache[ip] + + logger.debug(f"Failed to determine hostname for IP {ip} via DNS or NetBIOS") + return None
+
+ + +
+[docs] +class DeviceStorage: + """Класс для хранения и управления данными об устройствах. + + Формат файла подсети: + JSON‑структура вида + { + "devices": [Device, ...] + } + + Политика очистки: + - Метод `_clean_old_devices` удаляет оффлайн‑устройства, не наблюдавшиеся + более 7 дней, чтобы поддерживать актуальность списка. + """ + def __init__(self, base_path: str = "storage/subnets", translations: Translations = None): + """Инициализирует хранилище устройств. + + Создаёт директорию, если её нет, и задаёт параметры очистки. + + Args: + base_path (str): Путь к директории хранения данных. + translations (Translations, optional): Объект переводов. + """ + self.base_path = Path(base_path) + self.base_path.mkdir(parents=True, exist_ok=True) + self.offline_timeout = timedelta(days=7) + self.translations = translations or Translations.load("locales", ["ru"], "messages") # Fallback на русский + +
+[docs] + def get_subnet_file(self, subnet: str) -> Path: + """Возвращает путь к файлу для конкретной подсети. + + Заменяет символы '/' на '_' для корректного имени файла. + + Args: + subnet (str): Подсеть в формате CIDR. + + Returns: + Path: Путь к файлу. + """ + sanitized_subnet = subnet.replace("/", "_") + return self.base_path / f"{sanitized_subnet}.json"
+ + +
+[docs] + def load_devices(self, subnet: str) -> Dict[str, Device]: + """Загружает устройства для указанной подсети. + + Читает данные из JSON-файла и фильтрует устаревшие устройства. + + Args: + subnet (str): Подсеть в формате CIDR. + + Returns: + Dict[str, Device]: Словарь устройств с MAC как ключом. + """ + file_path = self.get_subnet_file(subnet) + try: + if file_path.exists(): + with open(file_path, "r", encoding="utf-8") as f: + data = json.load(f) + if not isinstance(data, dict) or "devices" not in data: + logger.warning(f"Invalid JSON structure in {file_path}, expected {'devices': [...]}, got {data}") + return {} + devices = {} + for device_data in data.get("devices", []): + device = Device(**device_data) + if device.mac: + devices[device.mac] = device + return self._clean_old_devices(devices) + return {} + except json.JSONDecodeError as e: + logger.error(f"JSON decode error in {file_path}: {str(e)}") + return {} + except Exception as e: + logger.error(f"Error loading devices for subnet {subnet}: {str(e)}") + return {}
+ + + def _clean_old_devices(self, devices: Dict[str, Device]) -> Dict[str, Device]: + """Очищает список устройств от устаревших оффлайн-устройств. + + Удаляет устройства, которые оффлайн более 7 дней. + + Args: + devices (Dict[str, Device]): Словарь устройств. + + Returns: + Dict[str, Device]: Отфильтрованный словарь устройств. + """ + now = datetime.now() + filtered = {} + for mac, device in devices.items(): + if device.status == "offline" and device.last_seen: + try: + last_seen = datetime.fromisoformat(device.last_seen) + if now - last_seen <= self.offline_timeout: + filtered[mac] = device + else: + logger.info(f"Removed device {mac} (offline > 7 days)") + except ValueError: + logger.warning(f"Invalid last_seen format for device {mac}") + else: + filtered[mac] = device + return filtered + +
+[docs] + def save_devices(self, subnet: str, devices: List[Device]) -> None: + """Сохраняет устройства для указанной подсети. + + Записывает данные в JSON-файл. + + Args: + subnet (str): Подсеть в формате CIDR. + devices (List[Device]): Список устройств для сохранения. + + Raises: + Exception: Если произошла ошибка при сохранении. + """ + file_path = self.get_subnet_file(subnet) + try: + data = SubnetScanResult(devices=devices).model_dump() + with open(file_path, "w", encoding="utf-8") as f: + json.dump(data, f, indent=2, ensure_ascii=False) + logger.info(f"Saved {len(devices)} devices to {file_path}") + except Exception as e: + logger.error(f"Error saving devices for subnet {subnet}: {str(e)}") + if self.translations: + raise Exception(self.translations.gettext("Ошибка при сохранении устройств: {error}").format(error=str(e))) + else: + raise Exception(f"Ошибка при сохранении устройств: {str(e)}")
+
+ + +
+[docs] +class SubnetService: + """Сервис для сканирования подсетей и управления устройствами. + + Роль сервиса: + Объединяет сетевое сканирование (ARP), сбор дополнительной информации + (вендор, хостнейм), слияние с кэшем предыдущих результатов и персистентное + хранение данных по подсетям. Предоставляет вспомогательные операции для + локального узла и работы с заметками устройств. + + Поток выполнения скана (`scan_subnet`): + 1) Определение подсети (вручную или авто по интерфейсу). + 2) Загрузка ранее сохранённых устройств подсети. + 3) Добавление локального устройства (если принадлежит подсети). + 4) ARP‑сканирование и параллельная обработка узлов (обогащение данных). + 5) Маркировка оффлайн‑узлов из кэша, не встреченных в текущем скане. + 6) Сортировка по IP и сохранение объединённого списка в JSON. + """ + def __init__(self, translations: Translations): + """Инициализирует сервис подсети. + + Настраивает интерфейс, сканеры и хранилище с текущей локализацией. + + Args: + translations (Translations): Объект переводов для текущего языка. + """ + self.interface = self._get_default_interface() + self.network_scanner = NetworkScanner(self.interface, translations) + self.port_scanner = PortScanner(self.interface, translations) + self.device_info = DeviceInfoCollector(translations) + self.storage = DeviceStorage(translations=translations) + self.translations = translations + logger.info(f"Initialized SubnetService with interface: {self.interface}") + + def _get_default_interface(self) -> str: + """Определяет интерфейс по умолчанию. + + Использует информацию о шлюзе или первый доступный интерфейс. + + Returns: + str: Имя интерфейса (например, "wlp0s20f3" или "eth0"). + + Raises: + Exception: Если не удалось определить интерфейс. + """ + try: + gateways = netifaces.gateways() + default_gateway = gateways.get("default", {}).get(netifaces.AF_INET) + if default_gateway: + iface = default_gateway[1] + logger.info(f"Default interface from gateway: {iface}") + return iface + interfaces = netifaces.interfaces() + for iface in interfaces: + if iface != "lo": + addrs = netifaces.ifaddresses(iface) + if netifaces.AF_INET in addrs: + logger.info(f"Selected interface: {iface}") + return iface + logger.warning("No suitable interface found, defaulting to eth0") + return "eth0" + except Exception as e: + logger.warning(f"Failed to determine interface: {str(e)}, defaulting to eth0") + return "eth0" + + def _get_default_subnet(self) -> str: + """Автоматически определяет подсеть локального устройства. + + Использует информацию о текущем интерфейсе для определения подсети. + + Returns: + str: Подсеть в формате CIDR (например, "10.1.1.0/24"). + + Raises: + Exception: Если не удалось определить подсеть. + """ + try: + interfaces = netifaces.interfaces() + for iface in interfaces: + if iface == "lo" or iface != self.interface: + continue + addrs = netifaces.ifaddresses(iface) + if netifaces.AF_INET in addrs: + ip_info = addrs[netifaces.AF_INET][0] + ip = ip_info["addr"] + netmask = ip_info["netmask"] + # Преобразуем маску из строки (например, "255.255.255.0") в CIDR + mask = ipaddress.ip_network(f"0.0.0.0/{netmask}", strict=False).prefixlen + network = ipaddress.ip_network(f"{ip}/{mask}", strict=False) + logger.info(f"Auto-detected subnet: {network} (IP: {ip}, Mask: {netmask} -> /{mask})") + return str(network) + logger.warning("No suitable subnet found, defaulting to 0.0.0.0/0") + return "0.0.0.0/0" + except Exception as e: + logger.error(f"Error determining default subnet: {str(e)}") + return "0.0.0.0/0" + +
+[docs] + def scan_subnet(self, subnet: Optional[str] = None) -> SubnetScanResult: + """Сканирует указанную подсеть и возвращает результаты. + + Выполняет ARP-сканирование, собирает информацию о устройствах и сохраняет данные. + + Args: + subnet (Optional[str]): Подсеть в формате CIDR (например, "10.1.1.0/24"). + Если None или пустая строка, определяется автоматически. + + Returns: + SubnetScanResult: Объект с результатами сканирования, содержащий список + устройств или сообщение об ошибке. + + Raises: + Exception: Если произошла ошибка сканирования. + """ + start_time = time.time() + devices = [] + now = datetime.now().astimezone() + formatted_now = now.strftime("%d-%m-%Y, %H:%M") + try: + # Определяем подсеть, если не указана или пустая + if not subnet or subnet.strip() == "": + subnet = self._get_default_subnet() + if subnet == "0.0.0.0/0": + logger.warning("Default subnet is invalid, scan aborted") + return SubnetScanResult(devices=devices, error=self.translations.gettext("Недопустимая подсеть по умолчанию")) + + saved_devices = self.storage.load_devices(subnet) + + local_device = self._get_local_device(subnet) + if local_device: + local_device.status = "online" + local_device.last_seen = formatted_now + devices.append(local_device) + if local_device.mac: + saved_devices[local_device.mac] = local_device + logger.info(f"Detected local device: {local_device.ip}, MAC: {local_device.mac}") + + logger.info(f"Scanning subnet: {subnet} with interface: {self.interface}") + discovered_devices = self.network_scanner.scan(subnet) + logger.info(f"Discovered devices: {len(discovered_devices)}") + self.device_info.scan_nbtscan(subnet) + + if not discovered_devices and not local_device: + logger.warning(f"No devices found in subnet {subnet}") + return SubnetScanResult(devices=[], error=self.translations.gettext("В данной подсети не обнаружено устройств")) + + def process_device(ip_mac: Tuple[str, str]) -> Optional[Device]: + ip, mac = ip_mac + if local_device and ip == local_device.ip: + return None + + existing_device = saved_devices.get(mac) + if existing_device: + return Device( + ip=ip, + mac=mac, + hostname=self.device_info.get_hostname(ip) or existing_device.hostname, + vendor=self.device_info.get_vendor(mac), + open_ports=existing_device.open_ports, + status="online", + last_seen=formatted_now, + note=existing_device.note, + ) + vendor = self.device_info.get_vendor(mac) + hostname = self.device_info.get_hostname(ip) + return Device( + ip=ip, + mac=mac, + hostname=hostname, + vendor=vendor, + open_ports=[], + status="online", + last_seen=formatted_now, + note=None, + ) + + with ThreadPoolExecutor(max_workers=10) as executor: + device_results = executor.map(process_device, discovered_devices) + devices.extend([d for d in device_results if d is not None]) + + active_macs = {d.mac for d in devices if d.mac} + for mac, device in saved_devices.items(): + if mac not in active_macs: + device.status = "offline" + devices.append(device) + + devices.sort(key=lambda x: int(ipaddress.IPv4Address(x.ip))) + + for device in devices: + if device.mac: + saved_devices[device.mac] = device + self.storage.save_devices(subnet, list(saved_devices.values())) + + logger.info( + f"Subnet scan {subnet} completed in {time.time() - start_time:.2f} seconds, " + f"found {len(devices)} devices (active: {len(active_macs)})" + ) + return SubnetScanResult(devices=devices) + + except Exception as e: + logger.error(f"Error scanning subnet {subnet}: {str(e)}") + return SubnetScanResult(devices=devices, error=self.translations.gettext("Ошибка сканирования: {error}").format(error=str(e)))
+ + + def _get_local_device(self, subnet: str) -> Optional[Device]: + """Определяет локальное устройство в указанной подсети. + + Использует информацию об интерфейсе для обнаружения. + + Args: + subnet (str): Подсеть в формате CIDR. + + Returns: + Optional[Device]: Объект устройства или None, если не найдено. + """ + now = datetime.now().astimezone() + formatted_now = now.strftime("%d-%m-%Y, %H:%M") + try: + subnet_network = ipaddress.ip_network(subnet, strict=False) + interfaces = netifaces.interfaces() + for iface in interfaces: + if iface == "lo": + continue + addrs = netifaces.ifaddresses(iface) + if netifaces.AF_INET in addrs and netifaces.AF_LINK in addrs: + ip = addrs[netifaces.AF_INET][0]["addr"] + if ipaddress.ip_address(ip) in subnet_network: + mac = addrs[netifaces.AF_LINK][0]["addr"] + vendor = self.device_info.get_vendor(mac) + hostname = self.device_info.get_hostname(ip) + logger.info(f"Local device detected: IP={ip}, MAC={mac}, Interface={iface}") + return Device( + ip=ip, + mac=mac, + hostname=hostname, + vendor=vendor, + open_ports=[], + status="online", + last_seen=formatted_now, + note=None, + ) + logger.warning(f"Local device not found in subnet {subnet}") + return None + except Exception as e: + logger.warning(f"Failed to get local device: {str(e)}") + return None + +
+[docs] + def add_note(self, mac: str, note: str, translations: Translations) -> Tuple[bool, Optional[Tuple[str, Dict]]]: + """Добавляет заметку для устройства по MAC-адресу. + + Привязывает заметку к устройству, если оно существует. + + Args: + mac (str): MAC-адрес устройства. + note (str): Текст заметки. + translations (Translations): Объект переводов для текущего языка. + + Returns: + Tuple[bool, Optional[Tuple[str, Dict]]]: Кортеж (успех, результат), + где результат содержит код ошибки/успеха и данные. + """ + try: + saved_devices = {} + for subnet in self.storage.base_path.glob("*.json"): + devices = self.storage.load_devices(subnet.stem.replace("_", "/")) + saved_devices.update(devices) + if mac not in saved_devices: + logger.warning(f"Device with MAC {mac} not found") + return False, ("not_found", {"mac": mac}) + if saved_devices[mac].note is not None: + logger.error(f"Note for device with MAC {mac} already exists") + return False, ("note_exists", {"mac": mac}) + for subnet_file in self.storage.base_path.glob("*.json"): + subnet = subnet_file.stem.replace("_", "/") + devices = self.storage.load_devices(subnet) + if mac in devices: + devices[mac].note = note + self.storage.save_devices(subnet, list(devices.values())) + break + logger.info(f"Added note '{note}' for device with MAC {mac}") + return True, ("success", {"mac": mac, "note": note}) + except Exception as e: + logger.error(f"Error adding note for MAC {mac}: {str(e)}") + return False, ("internal_error", {"error": str(e)})
+ + +
+[docs] + def update_note(self, mac: str, note: str, translations: Translations) -> Tuple[bool, Optional[Tuple[str, Dict]]]: + """Обновляет заметку для устройства по MAC-адресу. + + Изменяет существующую заметку, если она есть. + + Args: + mac (str): MAC-адрес устройства. + note (str): Новый текст заметки. + translations (Translations): Объект переводов для текущего языка. + + Returns: + Tuple[bool, Optional[Tuple[str, Dict]]]: Кортеж (успех, результат), + где результат содержит код ошибки/успеха и данные. + """ + try: + saved_devices = {} + for subnet in self.storage.base_path.glob("*.json"): + devices = self.storage.load_devices(subnet.stem.replace("_", "/")) + saved_devices.update(devices) + if mac not in saved_devices: + logger.warning(f"Device with MAC {mac} not found") + return False, ("not_found", {"mac": mac}) + if saved_devices[mac].note is None: + logger.error(f"Note for device with MAC {mac} does not exist") + return False, ("note_not_exists", {"mac": mac}) + for subnet_file in self.storage.base_path.glob("*.json"): + subnet = subnet_file.stem.replace("_", "/") + devices = self.storage.load_devices(subnet) + if mac in devices: + devices[mac].note = note + self.storage.save_devices(subnet, list(devices.values())) + break + logger.info(f"Updated note '{note}' for device with MAC {mac}") + return True, ("success", {"mac": mac, "note": note}) + except Exception as e: + logger.error(f"Error updating note for MAC {mac}: {str(e)}") + return False, ("internal_error", {"error": str(e)})
+ + +
+[docs] + def delete_note(self, mac: str, translations: Translations) -> Tuple[bool, Optional[Tuple[str, Dict]]]: + """Удаляет заметку для устройства по MAC-адресу. + + Удаляет привязанную заметку, если она существует. + + Args: + mac (str): MAC-адрес устройства. + translations (Translations): Объект переводов для текущего языка. + + Returns: + Tuple[bool, Optional[Tuple[str, Dict]]]: Кортеж (успех, результат), + где результат содержит код ошибки/успеха и данные. + """ + try: + saved_devices = {} + for subnet in self.storage.base_path.glob("*.json"): + devices = self.storage.load_devices(subnet.stem.replace("_", "/")) + saved_devices.update(devices) + if mac not in saved_devices: + logger.warning(f"Device with MAC {mac} not found") + return False, ("not_found", {"mac": mac}) + if saved_devices[mac].note is None: + logger.error(f"Note for device with MAC {mac} does not exist") + return False, ("note_not_exists", {"mac": mac}) + for subnet_file in self.storage.base_path.glob("*.json"): + subnet = subnet_file.stem.replace("_", "/") + devices = self.storage.load_devices(subnet) + if mac in devices: + devices[mac].note = None + self.storage.save_devices(subnet, list(devices.values())) + break + logger.info(f"Deleted note for device with MAC {mac}") + return True, ("success", {"mac": mac}) + except Exception as e: + logger.error(f"Error deleting note for MAC {mac}: {str(e)}") + return False, ("internal_error", {"error": str(e)})
+
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/SRV/docs/_build/html/_modules/services/ufw_service.html b/SRV/docs/_build/html/_modules/services/ufw_service.html new file mode 100644 index 0000000..3afe7f0 --- /dev/null +++ b/SRV/docs/_build/html/_modules/services/ufw_service.html @@ -0,0 +1,754 @@ + + + + + + + + services.ufw_service - GONETS SRV 0.0.3 documentation + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark, in light mode + + + + + + + + + + + + + + + Auto light/dark, in dark mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Skip to content + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for services.ufw_service

+import ipaddress
+import subprocess
+from typing import List, Union
+
+from models.ufw import Direction, Protocol, UFWRule, UFWRulesResponse, UFWStatus
+
+DEBUG = True
+
+
+
+[docs] +class UFWService: + """Сервис взаимодействия с UFW (Uncomplicated Firewall). + + Назначение: + - Чтение статуса и списка правил брандмауэра. + - Добавление, удаление и массовая обработка правил с поддержкой диапазонов портов, + комментариев и выбором протоколов. + + Детали реализации: + - Команды выполняются через `sudo ufw ...`; вывод парсится в модели pydantic. + - Для операций удаления соблюдается порядок с конца, чтобы не нарушать нумерацию. + """ + + def _run_command(self, command: str) -> str: + """ + Выполняет команду в системе и возвращает ее вывод. + + Args: + command (str): Команда для выполнения в системе. + + Returns: + str: Вывод команды. + """ + try: + if DEBUG: + print(f"Running command: {command}") + + # Используем shell=True для корректной обработки аргументов с пробелами + result = subprocess.run( + f"sudo {command}", + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + ) + + if DEBUG: + print(f"Command output: {result.stdout}") + print(f"Command error: {result.stderr}") + + if result.returncode != 0: + raise subprocess.CalledProcessError(result.returncode, command, + result.stdout, + result.stderr) + + return result.stdout + + except subprocess.CalledProcessError as e: + if DEBUG: + print(f"Command failed with return code {e.returncode}") + print(f"Error output: {e.stderr}") + raise Exception(f"Command failed: {e.stderr}") + + def _get_port_range_args(self, port: Union[int, str]) -> List[str]: + """ + Преобразует порт или диапазон портов в аргументы команды UFW. + + Args: + port: Порт (число) или диапазон портов (строка вида "3000:3999") + + Returns: + List[str]: Список аргументов для UFW + """ + if isinstance(port, int): + return [str(port)] + elif isinstance(port, str) and ":" in port: + # Диапазон портов уже в нужном формате + return [port] + else: + raise ValueError(f"Некорректный формат порта: {port}") + +
+[docs] + def get_ufw_rules(self) -> UFWRulesResponse: + """ + Получает список правил UFW. + + Returns: + UFWRulesResponse: Объект, содержащий статус UFW и список правил + """ + # Check UFW status + status_result = self._run_command("ufw status numbered") + lines = status_result.splitlines() + + if not lines: + return UFWRulesResponse(enabled=False, rules=[]) + + first_line = lines[0] + is_enabled = "Status: active" in first_line + + if not is_enabled: + return UFWRulesResponse(enabled=False, rules=[]) + + rules = [] + # Skip header lines + for line in lines[4:]: # Skip Status and header lines + if not line.strip() or not line.startswith("["): + continue + + try: + rule = UFWRule.from_ufw_output(line) + rules.append(rule) + except Exception as e: + if DEBUG: + print(f"Ошибка парсинга правила: {line}, ошибка: {str(e)}") + continue + + return UFWRulesResponse(enabled=is_enabled, rules=rules)
+ + +
+[docs] + def get_ufw_status(self) -> UFWStatus: + """ + Проверяет статус UFW. + + Returns: + UFWStatus: Объект с информацией о статусе UFW + """ + try: + status_result = self._run_command("ufw status") + first_line = (status_result.splitlines()[0] + if status_result.splitlines() else "") + is_enabled = "Status: active" in first_line + + message = "UFW активен" if is_enabled else "UFW неактивен" + + if DEBUG: + print(f"Результат статуса: {status_result}") + print(f"Включен: {is_enabled}") + + return UFWStatus(enabled=is_enabled, message=message) + except Exception as e: + if DEBUG: + print(f"Ошибка при проверке статуса UFW: {e}") + return UFWStatus( + enabled=False, + message=f"Ошибка при проверке статуса: {str(e)}", + )
+ + +
+[docs] + def enable_ufw(self) -> bool: + """ + Включает UFW. + + Returns: + bool: True если успешно включен, False в противном случае + """ + try: + self._run_command("ufw enable") + return True + except Exception as e: + if DEBUG: + print(f"Ошибка при включении UFW: {e}") + return False
+ + +
+[docs] + def disable_ufw(self) -> bool: + """ + Выключает UFW. + + Returns: + bool: True если успешно выключен, False в противном случае + """ + try: + self._run_command("ufw disable") + return True + except Exception as e: + if DEBUG: + print(f"Ошибка при выключении UFW: {e}") + return False
+ + +
+[docs] + def add_rules_base(self, + rules: List[UFWRule], + direction: str = "") -> bool: + """ + Базовый метод для добавления правил UFW. + + Args: + rules: Список правил для добавления + direction: Направление трафика ("in" или "out") + + Returns: + bool: True если все правила были успешно добавлены, False в противном случае + """ + try: + for rule in rules: + # Устанавливаем направление правила + rule.direction = Direction.IN if direction == "in" else Direction.OUT + + # Формируем базовую команду + command = f"ufw {rule.action.value}" + + # Добавляем направление + command += f" {rule.direction.value}" + + # Добавляем from IP если он указан + if rule.from_ip and rule.from_ip != "any": + command += f" from {rule.from_ip}" + else: + command += " from any" + + # Добавляем to IP если он указан + if rule.to_ip and rule.to_ip != "any": + command += f" to {rule.to_ip}" + else: + command += " to any" + + # Добавляем порт если он указан + if rule.port: + command += f" port {rule.port}" + + # Добавляем протокол + if rule.protocol != Protocol.BOTH: + command += f" proto {rule.protocol.value}" + + # Добавляем комментарий если он есть + if rule.comment: + command += f" comment '{rule.comment}'" + + self._run_command(command) + + return True + except Exception as e: + if DEBUG: + print(f"Error adding UFW rules: {str(e)}") + return False
+ + +
+[docs] + def add_ufw_in_rules(self, rules: List[UFWRule]) -> bool: + """ + Добавляет входящие (in) правила UFW. + + Args: + rules (List[UFWRule]): Список правил для добавления + + Returns: + bool: True если все правила были успешно добавлены, False в противном случае + """ + return self.add_rules_base(rules, "in")
+ + +
+[docs] + def add_ufw_out_rules(self, rules: List[UFWRule]) -> bool: + """ + Добавляет исходящие (out) правила UFW. + + Args: + rules (List[UFWRule]): Список правил для добавления + + Returns: + bool: True если все правила были успешно добавлены, False в противном случае + """ + return self.add_rules_base(rules, "out")
+ + +
+[docs] + def add_rules(self, rules: List[UFWRule]) -> dict: + """ + Добавляет правила UFW. + + Args: + rules: Список правил для добавления + + Returns: + dict: Результат добавления правил + """ + results = [] + + for rule in rules: + try: + # Определяем версию IP + from_ip = ipaddress.ip_network(rule.from_ip, strict=False) + is_ipv6 = isinstance(from_ip, ipaddress.IPv6Network) + + # Получаем список протоколов + protocols = rule.protocol.protocols + print(f"Обработка правила для протоколов: {protocols}") + + for proto in protocols: + # Формируем базовую команду + command = f"ufw {rule.action.value}" + + # Добавляем IP адреса + command += f" from {rule.from_ip}" + if rule.to_ip: + command += f" to {rule.to_ip}" + + # Добавляем порт и протокол + port_args = self._get_port_range_args(rule.port) + command += f" port {' '.join(port_args)} proto {proto}" + + # Добавляем флаг IPv6 если нужно + if is_ipv6: + command += " v6" + + # Добавляем комментарий если есть + if rule.comment: + command += f" comment '{rule.comment}'" + + print(f"Подготовлена команда: {command}") + + # Выполняем команду + try: + output = self._run_command(command) + results.append({"success": True, "message": output}) + except Exception as e: + error_msg = f"Ошибка выполнения команды: {str(e)}" + print(error_msg) + results.append({ + "success": False, + "message": error_msg + }) + + except Exception as e: + error_msg = f"Ошибка при добавлении правила: {str(e)}" + print(error_msg) + results.append({"success": False, "message": error_msg}) + + # Выводим результаты + print("\nРезультаты добавления правил:") + for result in results: + status = "Успешно" if result["success"] else "Ошибка" + print(f" - {status}: {result['message']}") + + return {"success": True, "results": results}
+ + +
+[docs] + def delete_rules(self, rule_numbers: List[int]) -> dict: + """ + Удаляет правила UFW по их номерам. + + Args: + rule_numbers (List[int]): Список номеров правил для удаления + + Returns: + dict: Результат операции удаления + """ + try: + # Проверяем существование правил + status_result = self._run_command("ufw status numbered") + lines = status_result.splitlines() + + # Пропускаем заголовок (первые 4 строки) + rules = [] + for line in lines[4:]: # Skip header lines + if not line.strip() or not line.startswith("["): + continue + + try: + # Извлекаем номер правила из строки [N] + rule_num = int(line[1:line.index("]")]) + rules.append(rule_num) + except (ValueError, IndexError): + continue + + if DEBUG: + print(f"Найдены правила: {rules}") + + results = [] + # Сортируем номера в обратном порядке, чтобы удаление происходило с конца + # Это предотвратит смещение номеров при удалении + for rule_number in sorted(rule_numbers, reverse=True): + try: + # Проверяем существует ли правило с таким номером + if rule_number not in rules: + results.append({ + "number": + rule_number, + "success": + False, + "error": + f"Правило с номером {rule_number} не найдено", + }) + continue + + # Удаляем правило + self._run_command(f"ufw --force delete {rule_number}") + + results.append({ + "number": + rule_number, + "success": + True, + "message": + f"Правило {rule_number} успешно удалено", + }) + + except Exception as e: + if DEBUG: + print( + f"Ошибка при удалении правила {rule_number}: {e}") + results.append({ + "number": rule_number, + "success": False, + "error": str(e) + }) + + # Подсчитываем статистику + total = len(rule_numbers) + successful = len([r for r in results if r["success"]]) + + return { + "success": True, + "total": total, + "successful": successful, + "results": results, + } + + except Exception as e: + if DEBUG: + print(f"Ошибка при удалении правил: {e}") + return {"success": False, "error": str(e), "results": []}
+
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/SRV/docs/_build/html/_modules/services/wifi_service.html b/SRV/docs/_build/html/_modules/services/wifi_service.html new file mode 100644 index 0000000..d5798fb --- /dev/null +++ b/SRV/docs/_build/html/_modules/services/wifi_service.html @@ -0,0 +1,718 @@ + + + + + + + + services.wifi_service - GONETS SRV 0.0.3 documentation + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark, in light mode + + + + + + + + + + + + + + + Auto light/dark, in dark mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Skip to content + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for services.wifi_service

+import time
+import logging
+import subprocess
+import re
+from functools import lru_cache
+from typing import List, Optional, Dict, Tuple
+from mac_vendor_lookup import MacLookup
+import pywifi
+from models.wifi import WifiAccessPoint, WifiScanResult
+from pathlib import Path
+import json
+from utils.i18n import get_translations
+from babel.support import Translations
+from fastapi import Depends
+
+# Настройка логирования
+logging.basicConfig(
+    level=logging.DEBUG,
+    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
+)
+logger = logging.getLogger(__name__)
+
+
+[docs] +class WifiStorage: + """Класс для хранения данных о Wi‑Fi сетях и их заметках по SSID. + + Формат: + { + "networks": [{"ssid": str, "note": Optional[str]}] + } + + Поведение: + - Создаёт директорию хранения при инициализации, ведёт логирование путей. + - Валидация структуры при чтении, безопасная запись с Unicode и отступами. + """ + def __init__(self, storage_path: str = "storage/wifi/wifi.json", translations: Translations = Depends(get_translations())): + self.storage_file = Path(storage_path) + self.storage_file.parent.mkdir(parents=True, exist_ok=True) + self.translations = translations + logger.info(f"Wi-Fi storage initialized at: {self.storage_file.absolute()}") + +
+[docs] + def load_notes(self) -> Dict[str, Optional[str]]: + """Загружает заметки по SSID из файла хранения.""" + try: + if self.storage_file.exists(): + with open(self.storage_file, "r", encoding="utf-8") as f: + data = json.load(f) + if not isinstance(data, dict) or "networks" not in data: + logger.warning(f"Invalid JSON structure in {self.storage_file}, expected {'networks': [...]}, got {data}") + return {} + return {item["ssid"]: item.get("note") for item in data.get("networks", [])} + return {} + except json.JSONDecodeError as e: + logger.error(f"JSON decode error in {self.storage_file}: {str(e)}") + return {} + except Exception as e: + logger.error(f"Error loading notes from {self.storage_file}: {str(e)}") + return {}
+ + +
+[docs] + def save_notes(self, notes: Dict[str, Optional[str]]) -> None: + """Сохраняет заметки по SSID в файл.""" + try: + data = {"networks": [{"ssid": ssid, "note": note} for ssid, note in notes.items()]} + with open(self.storage_file, "w", encoding="utf-8") as f: + json.dump(data, f, indent=2, ensure_ascii=False) + logger.info(f"Saved {len(notes)} notes to {self.storage_file.absolute()}") + except PermissionError as e: + logger.error(f"Permission denied writing to {self.storage_file}: {str(e)}") + raise + except Exception as e: + logger.error(f"Error saving notes to {self.storage_file}: {str(e)}") + raise Exception(self.translations.gettext("Ошибка при сохранении заметок: {error}").format(error=str(e)))
+
+ + +
+[docs] +class WifiService: + """Сервис для сканирования текущих Wi‑Fi точек доступа и заметок. + + Источники данных: + - `pywifi` для базового сканирования и получения списка AP. + - `iw dev <iface> scan` для уточнения частоты, сигнала и безопасности. + - `MacLookup` для определения вендора по BSSID. + + Политики: + - Выбор лучшего AP по SSID по уровню сигнала. + - Кэширование определения вендора. + - Сохранение SSID даже без заметок для будущих операций. + """ + def __init__(self, translations: Translations = Depends(get_translations())): + self.mac_lookup = MacLookup() + self.wifi = pywifi.PyWiFi() + self.interface = self._get_wifi_interface() + if not hasattr(translations, 'gettext'): + logger.warning("Translations object does not support gettext, using fallback") + self.translations = Translations.load("utils/locales/en/LC_MESSAGES/services.mo") + else: + self.translations = translations + self.storage = WifiStorage() + + def _get_wifi_interface(self) -> Optional[object]: + """Автоматически определяет активный Wi-Fi-интерфейс.""" + try: + interfaces = self.wifi.interfaces() + if not interfaces: + logger.error(self.translations.gettext("Не найдено сетевых интерфейсов")) + return None + + logger.debug(f"Available interfaces: {[iface.name() for iface in interfaces]}") + + for iface in interfaces: + iface_name = iface.name() + if "p2p" in iface_name.lower(): + logger.debug(f"Skipped interface {iface_name} (P2P)") + continue + try: + iface.scan() + time.sleep(1) + results = iface.scan_results() + if results: + logger.info(f"Selected Wi-Fi interface: {iface_name}") + return iface + logger.debug(f"Interface {iface_name} returned no scan results") + except Exception as e: + logger.debug(f"Interface {iface_name} inactive: {str(e)}") + + logger.error(self.translations.gettext("Не найден активный Wi-Fi-интерфейс")) + return None + except Exception as e: + logger.error(f"Error determining Wi-Fi interface: {str(e)}") + return None + +
+[docs] + @lru_cache(maxsize=1000) + def get_vendor(self, mac: str) -> str: + """Определяет вендора по MAC-адресу с использованием кэширования.""" + try: + return self.mac_lookup.lookup(mac) + except Exception as e: + logger.warning(f"Failed to determine vendor for MAC {mac}: {str(e)}") + return self.translations.gettext("Неизвестный")
+ + + def _get_encryption_from_iw(self, ap_info: Dict) -> str: + """Определяет тип шифрования из данных, полученных через iw.""" + security = ap_info.get("security", "") + logger.debug(f"Encryption for BSSID {ap_info.get('bssid')}: {security}") + if "WPA3" in security: + return self.translations.gettext("WPA3") + elif "WPA2" in security or "RSN" in security: + return self.translations.gettext("WPA2") + elif "WPA" in security: + return self.translations.gettext("WPA") + elif "WEP" in security: + return self.translations.gettext("WEP") + return self.translations.gettext("Открытая") + + def _get_channel(self, frequency: Optional[int]) -> Optional[int]: + """Преобразует частоту в номер канала.""" + if frequency is None: + return None + try: + if 2412 <= frequency <= 2472: + return (frequency - 2412) // 5 + 1 + elif 5180 <= frequency <= 5825: + return (frequency - 5180) // 5 + 36 + logger.debug(f"Frequency {frequency} outside 2.4/5 GHz range") + return None + except Exception as e: + logger.debug(f"Error determining channel for frequency {frequency}: {str(e)}") + return None + + def _get_frequency(self, frequency: Optional[int]) -> Optional[str]: + """Определяет частотный диапазон на основе частоты.""" + if frequency is None: + return None + try: + if 2412 <= frequency <= 2472: + return self.translations.gettext("2.4 ГГц") + elif 5180 <= frequency <= 5825: + return self.translations.gettext("5 ГГц") + logger.debug(f"Frequency {frequency} outside 2.4/5 GHz range") + return None + except Exception as e: + logger.debug(f"Error determining band for frequency {frequency}: {str(e)}") + return None + + def _get_iw_scan_data(self) -> List[Dict]: + """Получает данные сканирования Wi-Fi через команду iw.""" + try: + if not self.interface: + logger.error(self.translations.gettext("Интерфейс не определён")) + return [] + cmd = ["sudo", "iw", "dev", self.interface.name(), "scan"] + logger.debug(f"Executing command: {' '.join(cmd)}") + result = subprocess.run(cmd, capture_output=True, text=True, timeout=10) + if result.returncode != 0: + logger.error(f"iw scan error: {result.stderr}") + return [] + output = result.stdout + logger.debug(f"iw scan output (first 500 chars): {output[:500]}...") + + aps = [] + current_ap = None + bssid_pattern = re.compile(r"BSS ([0-9a-f:]{17})") + ssid_pattern = re.compile(r"SSID: ?(.*)") + freq_pattern = re.compile(r"freq: (\d+)") + signal_pattern = re.compile(r"signal: ([-\d.]+) dBm") + rsn_pattern = re.compile(r"RSN:") + wpa_pattern = re.compile(r"WPA:") + security_type_pattern = re.compile(r"\* (WPA3|WPA2|WPA|WEP)") + + for line in output.splitlines(): + line = line.strip() + if bssid_match := bssid_pattern.match(line): + if current_ap: + aps.append(current_ap) + current_ap = {"bssid": bssid_match.group(1).lower(), "security": ""} + elif current_ap: + if ssid_match := ssid_pattern.match(line): + current_ap["ssid"] = ssid_match.group(1).strip() if ssid_match.group(1).strip() else "" + elif freq_match := freq_pattern.match(line): + current_ap["frequency"] = int(freq_match.group(1)) + elif signal_match := signal_pattern.match(line): + current_ap["signal"] = float(signal_match.group(1)) + elif rsn_pattern.match(line): + current_ap["security"] += "RSN;" + elif wpa_pattern.match(line): + current_ap["security"] += "WPA;" + elif security_type_match := security_type_pattern.search(line): + current_ap["security"] += security_type_match.group(1) + ";" + + if current_ap: + aps.append(current_ap) + + logger.debug(f"Found {len(aps)} access points via iw: {[ap['bssid'] for ap in aps]}") + return aps + except subprocess.CalledProcessError as e: + logger.error(f"Error executing iw scan: {e.stderr}") + return [] + except subprocess.TimeoutExpired: + logger.error(self.translations.gettext("Таймаут при выполнении iw scan")) + return [] + except Exception as e: + logger.error(f"Error parsing iw scan: {str(e)}") + return [] + +
+[docs] + def scan_wifi(self, timeout: int = 30) -> WifiScanResult: + """Сканирует текущие Wi-Fi точки доступа.""" + start_time = time.time() + aps = [] + + if not self.interface: + logger.error(self.translations.gettext("Wi-Fi-интерфейс не доступен")) + return WifiScanResult(access_points=aps) + + try: + logger.info(f"Starting Wi-Fi scan on interface {self.interface.name()}") + max_attempts = 3 + for attempt in range(max_attempts): + self.interface.scan() + time.sleep(min(timeout, 5)) + py_results = self.interface.scan_results() + if py_results: + logger.info(f"Found {len(py_results)} access points via pywifi after attempt {attempt + 1}") + break + else: + logger.warning(f"Scan attempt {attempt + 1} failed, trying again (FAIL-BUSY?)") + else: + logger.error("All scan attempts failed") + return WifiScanResult(access_points=aps) + + iw_results = self._get_iw_scan_data() + iw_by_bssid = {ap["bssid"]: ap for ap in iw_results if ap.get("bssid")} + + best_aps_by_ssid: Dict[str, WifiAccessPoint] = {} + saved_notes = self.storage.load_notes() + + for ap in py_results: + bssid = ap.bssid.lower() if ap.bssid else None + if not bssid: + logger.debug("Skipped access point without BSSID") + continue + ssid = ap.ssid if ap.ssid else f"Hidden_{bssid}" + hidden = not ap.ssid + signal = ap.signal if ap.signal else None + iw_data = iw_by_bssid.get(bssid, {}) + + frequency = iw_data.get("frequency") + channel = self._get_channel(frequency) + freq_band = self._get_frequency(frequency) + encryption = self._get_encryption_from_iw(iw_data) + vendor = self.get_vendor(bssid) + + current_ap = WifiAccessPoint( + ssid=ssid, + bssid=bssid, + channel=channel, + signal=signal if signal else iw_data.get("signal"), + encryption=encryption, + frequency=freq_band, + vendor=vendor, + hidden=hidden, + note=saved_notes.get(ssid) if ap.ssid else None, + ) + logger.debug(f"Processed AP: {current_ap.model_dump()}") + + if ssid not in best_aps_by_ssid or ( + current_ap.signal and best_aps_by_ssid[ssid].signal + and current_ap.signal > best_aps_by_ssid[ssid].signal): + current_ap.note = saved_notes.get(ssid) if ap.ssid else None + best_aps_by_ssid[ssid] = current_ap + + aps = list(best_aps_by_ssid.values()) + # Сохраняем хотя бы SSID, если заметок нет + notes_to_save = {ap.ssid: ap.note for ap in aps if ap.ssid} + if not notes_to_save and aps: + notes_to_save = {ap.ssid: None for ap in aps if ap.ssid} # Сохраняем SSID без заметок + if notes_to_save: + self.storage.save_notes(notes_to_save) + else: + logger.warning("No notes or SSIDs to save, skipping file write") + + logger.info(f"Wi-Fi scan completed, found {len(aps)} unique networks") + return WifiScanResult(access_points=aps) + + except Exception as e: + logger.error(f"Error scanning Wi-Fi: {str(e)}") + return WifiScanResult(access_points=aps)
+ + +
+[docs] + def add_note(self, ssid: str, note: str) -> Tuple[bool, Optional[str]]: + """Добавляет заметку к сети Wi-Fi по SSID.""" + try: + saved_notes = self.storage.load_notes() + if ssid not in saved_notes or saved_notes[ssid] is None: + logger.info(f"Added new note for network with SSID {ssid}") + elif saved_notes[ssid] is not None: + logger.error(f"Note for network with SSID {ssid} already exists") + return False, self.translations.gettext("Заметка для сети с SSID {ssid} уже существует").format(ssid=ssid) + saved_notes[ssid] = note + self.storage.save_notes(saved_notes) + logger.info(f"Added note '{note}' for network with SSID {ssid}") + return True, None + except Exception as e: + logger.error(f"Error adding note for SSID {ssid}: {str(e)}") + return False, self.translations.gettext("Внутренняя ошибка сервера: {error}").format(error=str(e))
+ + +
+[docs] + def update_note(self, ssid: str, note: str) -> Tuple[bool, Optional[str]]: + """Обновляет заметку сети Wi-Fi по SSID.""" + try: + saved_notes = self.storage.load_notes() + if ssid not in saved_notes: + logger.warning(f"Network with SSID {ssid} not found") + return False, self.translations.gettext("Сеть с SSID {ssid} не найдена").format(ssid=ssid) + if saved_notes[ssid] is None: + logger.error(f"Note for network with SSID {ssid} does not exist") + return False, self.translations.gettext("Заметка для сети с SSID {ssid} не существует").format(ssid=ssid) + saved_notes[ssid] = note + self.storage.save_notes(saved_notes) + logger.info(f"Updated note '{note}' for network with SSID {ssid}") + return True, None + except Exception as e: + logger.error(f"Error updating note for SSID {ssid}: {str(e)}") + return False, self.translations.gettext("Внутренняя ошибка сервера: {error}").format(error=str(e))
+ + +
+[docs] + def delete_note(self, ssid: str) -> Tuple[bool, Optional[str]]: + """Удаляет заметку сети Wi-Fi по SSID.""" + try: + saved_notes = self.storage.load_notes() + if ssid not in saved_notes: + logger.warning(f"Network with SSID {ssid} not found") + return False, self.translations.gettext("Сеть с SSID {ssid} не найдена").format(ssid=ssid) + if saved_notes[ssid] is None: + logger.error(f"Note for network with SSID {ssid} does not exist") + return False, self.translations.gettext("Заметка для сети с SSID {ssid} не существует").format(ssid=ssid) + saved_notes[ssid] = None + self.storage.save_notes(saved_notes) + logger.info(f"Deleted note for network with SSID {ssid}") + return True, None + except Exception as e: + logger.error(f"Error deleting note for SSID {ssid}: {str(e)}") + return False, self.translations.gettext("Внутренняя ошибка сервера: {error}").format(error=str(e))
+
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/SRV/docs/_build/html/_modules/storage/json_storage.html b/SRV/docs/_build/html/_modules/storage/json_storage.html new file mode 100644 index 0000000..7ebd5f4 --- /dev/null +++ b/SRV/docs/_build/html/_modules/storage/json_storage.html @@ -0,0 +1,533 @@ + + + + + + + + storage.json_storage - GONETS SRV 0.0.3 documentation + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark, in light mode + + + + + + + + + + + + + + + Auto light/dark, in dark mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Skip to content + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for storage.json_storage

+import json
+import os
+import logging
+from passlib.context import CryptContext
+from typing import Optional
+from models.auth import UserCreate, UserResponse
+
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger(__name__)
+
+
+[docs] +class JsonStorage: + """Хранилище пользователей в JSON-файле. + + Назначение: + Обеспечивает простое файловое хранилище для учетных записей пользователей + без полноценной СУБД. Подходит для встраиваемых окружений и сценариев, + где важна минимальная зависимость от внешних сервисов. + + Формат данных: + JSON‑структура вида: + { + "users": [ + {"id": int, "username": str, "password_hash": str, "is_admin": bool}, + ... + ], + "next_id": int + } + + Безопасность: + Пароли никогда не сохраняются в открытом виде. Для хеширования используется + `passlib.context.CryptContext` со схемой `bcrypt`. Файл создаётся с правами + доступа `0o600` (только владелец). + + Ограничения и замечания: + - Отсутствует конкурентный доступ/блокировки файла — при использовании в + многопроцессной среде может потребоваться внешняя синхронизация. + - Имя файла и каталог по умолчанию `/etc/gonets/users.json` предполагают + наличие прав на запись и предварительную подготовку окружения. + """ + + def __init__(self, file_path: str = "/etc/gonets/users.json"): + """Инициализирует хранилище пользователей. + + Args: + file_path (str, optional): Путь к JSON-файлу для хранения данных. + По умолчанию "/etc/gonets/users.json". + + Создаёт объект CryptContext для хеширования паролей и проверяет/создаёт + файл, если он отсутствует. + """ + self.file_path = file_path + self.pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") + self._ensure_file_exists() + + def _ensure_file_exists(self): + """Создаёт JSON-файл, если он не существует. + + Создаёт родительские директории и файл с начальной структурой данных, + устанавливая права доступа 600. + + Raises: + Exception: Если произошла ошибка при создании файла или директории. + """ + try: + logger.info(f"Проверка существования файла {self.file_path}") + if not os.path.exists(self.file_path): + logger.info(f"Создание директории {os.path.dirname(self.file_path)}") + os.makedirs(os.path.dirname(self.file_path), exist_ok=True) + logger.info(f"Создание файла {self.file_path}") + with open(self.file_path, "w") as f: + json.dump({"users": [], "next_id": 1}, f) + os.chmod(self.file_path, 0o600) + logger.info(f"Создан файл {self.file_path}") + else: + logger.info(f"Файл {self.file_path} уже существует") + except Exception as e: + logger.error( + f"Ошибка при создании файла {self.file_path}: {type(e).__name__} - {str(e)}" + ) + raise + + def _read_data(self) -> dict: + """Читает данные из JSON-файла. + + Returns: + dict: Словарь с данными пользователей и следующим ID. + + Raises: + FileNotFoundError: Если файл отсутствует (возвращает пустую структуру). + Exception: Если произошла ошибка при чтении файла. + """ + try: + with open(self.file_path, "r") as f: + return json.load(f) + except FileNotFoundError: + logger.info( + f"Файл {self.file_path} не существует, возвращаем пустую структуру" + ) + return {"users": [], "next_id": 1} + except Exception as e: + logger.error( + f"Ошибка при чтении файла {self.file_path}: {type(e).__name__} - {str(e)}" + ) + raise + + def _write_data(self, data: dict): + """Записывает данные в JSON-файл. + + Args: + data (dict): Словарь с данными для записи (пользователи и next_id). + + Raises: + Exception: Если произошла ошибка при записи в файл. + """ + try: + with open(self.file_path, "w") as f: + json.dump(data, f, indent=2) + except Exception as e: + logger.error( + f"Ошибка при записи файла {self.file_path}: {type(e).__name__} - {str(e)}" + ) + raise + +
+[docs] + def create_user(self, user: UserCreate) -> UserResponse: + """Создаёт нового пользователя с хешированием пароля. + + Args: + user (UserCreate): Объект с данными нового пользователя (username, password, is_admin). + + Returns: + UserResponse: Объект с данными созданного пользователя (id, username, is_admin). + + Raises: + ValueError: Если пользователь с таким именем уже существует. + Exception: Если произошла ошибка при записи данных. + """ + try: + data = self._read_data() + users = data["users"] + next_id = data["next_id"] + if any(u["username"] == user.username for u in users): + logger.error(f"Пользователь {user.username} уже существует") + raise ValueError("Пользователь уже существует") + password_hash = self.pwd_context.hash(user.password) + new_user = { + "id": next_id, + "username": user.username, + "password_hash": password_hash, + "is_admin": user.is_admin, + } + users.append(new_user) + data["next_id"] = next_id + 1 + self._write_data(data) + logger.info( + f"Пользователь {user.username} успешно зарегистрирован") + return UserResponse( + id=new_user["id"], + username=new_user["username"], + is_admin=new_user["is_admin"], + ) + except Exception as e: + logger.error( + f"Ошибка при создании пользователя {user.username}: {type(e).__name__} - {str(e)}" + ) + raise
+ + +
+[docs] + def get_user_by_username(self, username: str) -> Optional[dict]: + """Получает данные пользователя по имени. + + Args: + username (str): Имя пользователя для поиска. + + Returns: + Optional[dict]: Словарь с данными пользователя или None, если пользователь не найден. + + Raises: + Exception: Если произошла ошибка при чтении файла. + """ + try: + if not os.path.exists(self.file_path): + logger.info( + f"Файл {self.file_path} не существует, пользователь {username} не найден" + ) + return None + data = self._read_data() + for user in data["users"]: + if user["username"] == username: + return user + return None + except Exception as e: + logger.error( + f"Ошибка при получении пользователя {username}: {type(e).__name__} - {str(e)}" + ) + raise
+ + +
+[docs] + def verify_password(self, plain_password: str, hashed_password: str) -> bool: + """Проверяет соответствие введённого пароля хэшированному. + + Args: + plain_password (str): Введённый пользователем пароль. + hashed_password (str): Хэшированный пароль из хранилища. + + Returns: + bool: True, если пароль верен, False в противном случае. + """ + return self.pwd_context.verify(plain_password, hashed_password)
+
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/SRV/docs/_build/html/_modules/storage/session_storage.html b/SRV/docs/_build/html/_modules/storage/session_storage.html new file mode 100644 index 0000000..dfdcc72 --- /dev/null +++ b/SRV/docs/_build/html/_modules/storage/session_storage.html @@ -0,0 +1,596 @@ + + + + + + + + storage.session_storage - GONETS SRV 0.0.3 documentation + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark, in light mode + + + + + + + + + + + + + + + Auto light/dark, in dark mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Skip to content + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for storage.session_storage

+import json
+import os
+import uuid
+import logging
+from typing import Optional, Dict
+from datetime import datetime, timedelta
+
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger(__name__)
+
+
+[docs] +class SessionStorage: + """Хранилище сессий пользователей в JSON-файле. + + Назначение: + Лёгкое файловое хранилище для сессионных токенов, сопоставленных с + `client_id` (например, IP клиента). Предназначено для сред без БД и + persistent‑кэшей. Обеспечивает базовые операции и авто‑очистку протухших + сессий. + + Формат данных: + { + "sessions": { + "<session_id>": { + "username": str, + "token": str, + "client_id": str, + "created_at": ISO8601 str, + "expiry": ISO8601 str + }, + ... + } + } + + Политика TTL: + Каждая сессия действует 30 минут. При создании/чтении/удалении вызывается + `clean_expired_sessions()` для удаления протухших записей. + + Ограничения: + - Нет файловых блокировок: в многопроцессных конфигурациях возможны гонки. + - Размещение по умолчанию `/etc/gonets/sessions.json` требует прав на запись. + """ + + def __init__(self, file_path: str = "/etc/gonets/sessions.json"): + """Инициализирует хранилище сессий. + + Args: + file_path (str, optional): Путь к JSON-файлу для хранения сессий. + По умолчанию "/etc/gonets/sessions.json". + + Создаёт файл и директории, если они отсутствуют, с правами доступа 600. + """ + self.file_path = file_path + self._ensure_file_exists() + + def _ensure_file_exists(self): + """Создаёт JSON-файл, если он не существует. + + Создаёт родительские директории и файл с начальной структурой данных, + устанавливая права доступа 600. + + Raises: + Exception: Если произошла ошибка при создании файла или директории. + """ + try: + logger.info(f"Проверка существования файла {self.file_path}") + if not os.path.exists(self.file_path): + logger.info(f"Создание директории {os.path.dirname(self.file_path)}") + os.makedirs(os.path.dirname(self.file_path), exist_ok=True) + logger.info(f"Создание файла {self.file_path}") + with open(self.file_path, "w") as f: + json.dump({"sessions": {}}, f) + os.chmod(self.file_path, 0o600) + logger.info(f"Создан файл {self.file_path}") + else: + logger.info(f"Файл {self.file_path} уже существует") + except Exception as e: + logger.error( + f"Ошибка при создании файла {self.file_path}: {type(e).__name__} - {str(e)}" + ) + raise + + def _read_data(self) -> Dict: + """Читает данные из JSON-файла. + + Returns: + Dict: Словарь с данными сессий. + + Raises: + Exception: Если произошла ошибка при чтении файла. + """ + try: + with open(self.file_path, "r") as f: + return json.load(f) + except Exception as e: + logger.error( + f"Ошибка при чтении файла {self.file_path}: {type(e).__name__} - {str(e)}" + ) + raise + + def _write_data(self, data: Dict): + """Записывает данные в JSON-файл. + + Args: + data (Dict): Словарь с данными сессий для записи. + + Raises: + Exception: Если произошла ошибка при записи в файл. + """ + try: + with open(self.file_path, "w") as f: + json.dump(data, f, indent=2) + except Exception as e: + logger.error( + f"Ошибка при записи файла {self.file_path}: {type(e).__name__} - {str(e)}" + ) + raise + +
+[docs] + def create_session(self, username: str, token: str, client_id: str) -> str: + """Создаёт новую сессию и возвращает уникальный session_id. + + Args: + username (str): Имя пользователя, для которого создаётся сессия. + token (str): Токен аутентификации. + client_id (str): Идентификатор клиента. + + Returns: + str: Уникальный идентификатор сессии (session_id). + + Raises: + Exception: Если произошла ошибка при создании или записи сессии. + + Note: + Удаляет истекшие сессии перед созданием новой и устанавливает срок + действия 30 минут. + """ + try: + self.clean_expired_sessions() + data = self._read_data() + session_id = str(uuid.uuid4()) + created_at = datetime.utcnow().isoformat() + expiry = (datetime.utcnow() + timedelta(minutes=30)).isoformat() + data["sessions"][session_id] = { + "username": username, + "token": token, + "client_id": client_id, + "created_at": created_at, + "expiry": expiry, + } + self._write_data(data) + logger.info( + f"Сессия создана для пользователя {username} с session_id {session_id}" + ) + return session_id + except Exception as e: + logger.error( + f"Ошибка при создании сессии для {username}: {type(e).__name__} - {str(e)}" + ) + raise
+ + +
+[docs] + def get_latest_session(self, client_id: str) -> Optional[Dict]: + """Получает последнюю активную сессию для указанного client_id. + + Args: + client_id (str): Идентификатор клиента. + + Returns: + Optional[Dict]: Словарь с данными последней активной сессии, включая + "session_id", или None, если сессия не найдена. + + Raises: + Exception: Если произошла ошибка при чтении или обработке данных. + + Note: + Очищает истекшие сессии перед поиском и возвращает сессию с самым + поздним временем создания. + """ + try: + self.clean_expired_sessions() + data = self._read_data() + latest_session = None + latest_time = None + for session_id, session in data["sessions"].items(): + if session["client_id"] == client_id: + expiry = datetime.fromisoformat(session["expiry"]) + if datetime.utcnow() > expiry: + logger.info(f"Сессия {session_id} истекла") + del data["sessions"][session_id] + continue + created_at = datetime.fromisoformat(session["created_at"]) + if latest_time is None or created_at > latest_time: + latest_time = created_at + latest_session = session + latest_session["session_id"] = session_id + if latest_session: + logger.info(f"Найдена сессия для client_id {client_id}") + return latest_session + logger.info(f"Сессия для client_id {client_id} не найдена") + self._write_data(data) + return None + except Exception as e: + logger.error( + f"Ошибка при получении сессии для client_id {client_id}: {type(e).__name__} - {str(e)}" + ) + raise
+ + +
+[docs] + def delete_session(self, session_id: str): + """Удаляет сессию по указанному session_id. + + Args: + session_id (str): Уникальный идентификатор сессии. + + Raises: + Exception: Если произошла ошибка при чтении или записи данных. + + Note: + Очищает истекшие сессии перед удалением. + """ + try: + self.clean_expired_sessions() + data = self._read_data() + if session_id in data["sessions"]: + del data["sessions"][session_id] + self._write_data(data) + logger.info(f"Сессия с session_id {session_id} удалена") + except Exception as e: + logger.error( + f"Ошибка при удалении сессии {session_id}: {type(e).__name__} - {str(e)}" + ) + raise
+ + +
+[docs] + def clean_expired_sessions(self): + """Удаляет все истекшие сессии из хранилища. + + Raises: + Exception: Если произошла ошибка при чтении или записи данных. + + Note: + Проверяет срок действия каждой сессии и удаляет те, что истекли, + записывая обновлённые данные. + """ + try: + data = self._read_data() + expired_sessions = [] + current_time = datetime.utcnow() + + for session_id, session in list(data["sessions"].items()): + expiry = datetime.fromisoformat(session["expiry"]) + if current_time > expiry: + expired_sessions.append(session_id) + + for session_id in expired_sessions: + del data["sessions"][session_id] + logger.info( + f"Удалена истекшая сессия с session_id {session_id}") + + if expired_sessions: + self._write_data(data) + logger.info(f"Удалено {len(expired_sessions)} истекших сессий") + else: + logger.debug("Истекшие сессии не найдены") + + except Exception as e: + logger.error( + f"Ошибка при очистке истекших сессий: {type(e).__name__} - {str(e)}" + ) + raise
+
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/SRV/docs/_build/html/_modules/utils/i18n.html b/SRV/docs/_build/html/_modules/utils/i18n.html new file mode 100644 index 0000000..71583d5 --- /dev/null +++ b/SRV/docs/_build/html/_modules/utils/i18n.html @@ -0,0 +1,392 @@ + + + + + + + + utils.i18n - GONETS SRV 0.0.3 documentation + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark, in light mode + + + + + + + + + + + + + + + Auto light/dark, in dark mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Skip to content + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for utils.i18n

+"""
+Утилиты локализации и зависимости переводов для FastAPI.
+
+Содержимое модуля:
+ - `get_translations()`: фабрика зависимости FastAPI, считывающая локаль из
+   query‑параметра `locale`, cookie `locale` или заголовка `Accept-Language`
+   (по приоритету), с дефолтом `ru`. Возвращает функцию‑зависимость, которая
+   подгружает `babel.support.Translations` из каталога `SRV/locales`.
+ - `_()`: удобная обёртка для получения перевода строки с использованием
+   текущей локали (или дефолтного `ru`, если контекст запроса не передан).
+
+Особенности:
+ - При отсутствии словаря перевода для выбранной локали выполняется fallback на
+   русскую локаль, что гарантирует наличие строк интерфейса.
+ - Локаль определяется только по первому языку из `Accept-Language` (без
+   региональных суффиксов) для упрощения.
+ - Модуль ведёт отладочное логирование выбора и загрузки локали.
+"""
+from fastapi import Request, Depends
+from babel.support import Translations
+import logging
+
+logger = logging.getLogger(__name__)
+
+
+[docs] +def get_translations() -> callable: + """ + Создаёт зависимость для загрузки переводов на основе заголовка Accept-Language + или параметра запроса 'locale'. По умолчанию используется русский язык. + """ + def translations_dependency(request: Request) -> Translations: + # Проверяем параметр запроса 'locale', если он есть + locale = request.query_params.get("locale") + if not locale: + # Если параметр отсутствует, проверяем куку 'locale' + locale = request.cookies.get("locale") + if not locale: + # Если куки нет, используем заголовок Accept-Language + locale = request.headers.get("Accept-Language", "ru").split(",")[0].split("-")[0] + + logger.debug(f"Determined locale: {locale}") + try: + translations = Translations.load("locales", [locale], "messages") + if not translations._catalog: + logger.warning(f"Translations for locale {locale} not found, falling back to ru") + translations = Translations.load("locales", ["ru"], "messages") + logger.debug(f"Loaded translations for locale {locale}, catalog size: {len(translations._catalog)}") + return translations + except Exception as e: + logger.error(f"Failed to load translations for locale {locale}: {str(e)}") + # Fallback на русский + logger.debug("Falling back to ru translations") + return Translations.load("locales", ["ru"], "messages") + + return translations_dependency
+ + +def _(msgid: str, request: Request = None) -> str: + """ + Переводит строку msgid для текущей локали. + + Args: + msgid (str): Строка для перевода. + request (Request, optional): Объект запроса FastAPI для получения локали. + + Returns: + str: Переведенная строка. + """ + if request is None: + # Язык, который будет отображаться по умолчанию + translations = Translations.load("locales", ["ru"], "messages") + logger.debug(f"Using default ru translations for msgid: {msgid}") + else: + translations = get_translations()(request) + logger.debug(f"Using translations for locale {request.cookies.get('locale', 'ru')} for msgid: {msgid}") + return translations.gettext(msgid) +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/SRV/docs/_build/html/_sources/api/config.rst.txt b/SRV/docs/_build/html/_sources/api/config.rst.txt new file mode 100644 index 0000000..3dfa707 --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/config.rst.txt @@ -0,0 +1,9 @@ +Конфигурация +============ + +.. toctree:: + :maxdepth: 1 + + config/cors + config/init_config + config/ports diff --git a/SRV/docs/_build/html/_sources/api/config/cors.rst.txt b/SRV/docs/_build/html/_sources/api/config/cors.rst.txt new file mode 100644 index 0000000..6dfd244 --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/config/cors.rst.txt @@ -0,0 +1,7 @@ +config.cors +=========== + +.. automodule:: config.cors + :members: + :undoc-members: + :show-inheritance: diff --git a/SRV/docs/_build/html/_sources/api/config/init_config.rst.txt b/SRV/docs/_build/html/_sources/api/config/init_config.rst.txt new file mode 100644 index 0000000..3386987 --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/config/init_config.rst.txt @@ -0,0 +1,7 @@ +config.init_config +=================== + +.. automodule:: config.init_config + :members: + :undoc-members: + :show-inheritance: diff --git a/SRV/docs/_build/html/_sources/api/config/ports.rst.txt b/SRV/docs/_build/html/_sources/api/config/ports.rst.txt new file mode 100644 index 0000000..b893ee3 --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/config/ports.rst.txt @@ -0,0 +1,7 @@ +config.ports +============ + +.. automodule:: config.ports + :members: + :undoc-members: + :show-inheritance: diff --git a/SRV/docs/_build/html/_sources/api/core.rst.txt b/SRV/docs/_build/html/_sources/api/core.rst.txt new file mode 100644 index 0000000..f859c03 --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/core.rst.txt @@ -0,0 +1,7 @@ +Core +==== + +.. toctree:: + :maxdepth: 1 + + core/cli_executor diff --git a/SRV/docs/_build/html/_sources/api/core/cli_executor.rst.txt b/SRV/docs/_build/html/_sources/api/core/cli_executor.rst.txt new file mode 100644 index 0000000..a305ccb --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/core/cli_executor.rst.txt @@ -0,0 +1,7 @@ +core.cli_executor +================== + +.. automodule:: core.cli_executor + :members: + :undoc-members: + :show-inheritance: diff --git a/SRV/docs/_build/html/_sources/api/endpoints.rst.txt b/SRV/docs/_build/html/_sources/api/endpoints.rst.txt new file mode 100644 index 0000000..ca6734e --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/endpoints.rst.txt @@ -0,0 +1,13 @@ +API эндпоинты +============= + +.. toctree:: + :maxdepth: 1 + + endpoints/auth + endpoints/ip + endpoints/network + endpoints/settings + endpoints/subnet + endpoints/ufw + endpoints/websocket diff --git a/SRV/docs/_build/html/_sources/api/endpoints/auth.rst.txt b/SRV/docs/_build/html/_sources/api/endpoints/auth.rst.txt new file mode 100644 index 0000000..df442bf --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/endpoints/auth.rst.txt @@ -0,0 +1,7 @@ +api.v1.auth +=========== + +.. automodule:: api.v1.auth + :members: + :undoc-members: + :show-inheritance: diff --git a/SRV/docs/_build/html/_sources/api/endpoints/ip.rst.txt b/SRV/docs/_build/html/_sources/api/endpoints/ip.rst.txt new file mode 100644 index 0000000..a3199b5 --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/endpoints/ip.rst.txt @@ -0,0 +1,7 @@ +api.v1.ip +========= + +.. automodule:: api.v1.ip + :members: + :undoc-members: + :show-inheritance: diff --git a/SRV/docs/_build/html/_sources/api/endpoints/network.rst.txt b/SRV/docs/_build/html/_sources/api/endpoints/network.rst.txt new file mode 100644 index 0000000..86ff9c4 --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/endpoints/network.rst.txt @@ -0,0 +1,7 @@ +api.v1.network +============== + +.. automodule:: api.v1.network + :members: + :undoc-members: + :show-inheritance: diff --git a/SRV/docs/_build/html/_sources/api/endpoints/settings.rst.txt b/SRV/docs/_build/html/_sources/api/endpoints/settings.rst.txt new file mode 100644 index 0000000..7958dd6 --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/endpoints/settings.rst.txt @@ -0,0 +1,7 @@ +api.v1.settings +================ + +.. automodule:: api.v1.settings + :members: + :undoc-members: + :show-inheritance: diff --git a/SRV/docs/_build/html/_sources/api/endpoints/subnet.rst.txt b/SRV/docs/_build/html/_sources/api/endpoints/subnet.rst.txt new file mode 100644 index 0000000..05ddfac --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/endpoints/subnet.rst.txt @@ -0,0 +1,7 @@ +api.v1.subnet +============= + +.. automodule:: api.v1.subnet + :members: + :undoc-members: + :show-inheritance: diff --git a/SRV/docs/_build/html/_sources/api/endpoints/ufw.rst.txt b/SRV/docs/_build/html/_sources/api/endpoints/ufw.rst.txt new file mode 100644 index 0000000..cd5fc6e --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/endpoints/ufw.rst.txt @@ -0,0 +1,7 @@ +api.v1.ufw +========== + +.. automodule:: api.v1.ufw + :members: + :undoc-members: + :show-inheritance: diff --git a/SRV/docs/_build/html/_sources/api/endpoints/websocket.rst.txt b/SRV/docs/_build/html/_sources/api/endpoints/websocket.rst.txt new file mode 100644 index 0000000..0b8116c --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/endpoints/websocket.rst.txt @@ -0,0 +1,7 @@ +api.v1.websocket +================ + +.. automodule:: api.v1.websocket + :members: + :undoc-members: + :show-inheritance: diff --git a/SRV/docs/_build/html/_sources/api/models.rst.txt b/SRV/docs/_build/html/_sources/api/models.rst.txt new file mode 100644 index 0000000..49197ba --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/models.rst.txt @@ -0,0 +1,12 @@ +Модели +====== + +.. toctree:: + :maxdepth: 1 + + models/auth + models/network + models/request + models/subnet + models/ufw + models/wifi diff --git a/SRV/docs/_build/html/_sources/api/models/auth.rst.txt b/SRV/docs/_build/html/_sources/api/models/auth.rst.txt new file mode 100644 index 0000000..2c266d2 --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/models/auth.rst.txt @@ -0,0 +1,7 @@ +models.auth +=========== + +.. automodule:: models.auth + :members: + :undoc-members: + :show-inheritance: diff --git a/SRV/docs/_build/html/_sources/api/models/network.rst.txt b/SRV/docs/_build/html/_sources/api/models/network.rst.txt new file mode 100644 index 0000000..2e88043 --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/models/network.rst.txt @@ -0,0 +1,7 @@ +models.network +============== + +.. automodule:: models.network + :members: + :undoc-members: + :show-inheritance: diff --git a/SRV/docs/_build/html/_sources/api/models/request.rst.txt b/SRV/docs/_build/html/_sources/api/models/request.rst.txt new file mode 100644 index 0000000..33c43ba --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/models/request.rst.txt @@ -0,0 +1,7 @@ +models.request +============== + +.. automodule:: models.request + :members: + :undoc-members: + :show-inheritance: diff --git a/SRV/docs/_build/html/_sources/api/models/subnet.rst.txt b/SRV/docs/_build/html/_sources/api/models/subnet.rst.txt new file mode 100644 index 0000000..de0c59c --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/models/subnet.rst.txt @@ -0,0 +1,7 @@ +models.subnet +============= + +.. automodule:: models.subnet + :members: + :undoc-members: + :show-inheritance: diff --git a/SRV/docs/_build/html/_sources/api/models/ufw.rst.txt b/SRV/docs/_build/html/_sources/api/models/ufw.rst.txt new file mode 100644 index 0000000..881593a --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/models/ufw.rst.txt @@ -0,0 +1,7 @@ +models.ufw +========== + +.. automodule:: models.ufw + :members: + :undoc-members: + :show-inheritance: diff --git a/SRV/docs/_build/html/_sources/api/models/wifi.rst.txt b/SRV/docs/_build/html/_sources/api/models/wifi.rst.txt new file mode 100644 index 0000000..399ba8a --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/models/wifi.rst.txt @@ -0,0 +1,7 @@ +models.wifi +=========== + +.. automodule:: models.wifi + :members: + :undoc-members: + :show-inheritance: diff --git a/SRV/docs/_build/html/_sources/api/services.rst.txt b/SRV/docs/_build/html/_sources/api/services.rst.txt new file mode 100644 index 0000000..ebca7d0 --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/services.rst.txt @@ -0,0 +1,21 @@ +Сервисы +======= + +.. automodule:: services + :members: + :undoc-members: + :show-inheritance: + +.. toctree:: + :maxdepth: 1 + + services/arp_service + services/auth_service + services/connect_service + services/firewalld_service + services/ip_service + services/network_service + services/settings_service + services/subnet_service + services/ufw_service + services/wifi_service diff --git a/SRV/docs/_build/html/_sources/api/services/arp_service.rst.txt b/SRV/docs/_build/html/_sources/api/services/arp_service.rst.txt new file mode 100644 index 0000000..c99d729 --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/services/arp_service.rst.txt @@ -0,0 +1,7 @@ +ARP Service +=========== + +.. automodule:: services.arp_service + :members: + :undoc-members: + :show-inheritance: diff --git a/SRV/docs/_build/html/_sources/api/services/auth_service.rst.txt b/SRV/docs/_build/html/_sources/api/services/auth_service.rst.txt new file mode 100644 index 0000000..0cbe8ee --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/services/auth_service.rst.txt @@ -0,0 +1,7 @@ +Auth Service +============ + +.. automodule:: services.auth_service + :members: + :undoc-members: + :show-inheritance: diff --git a/SRV/docs/_build/html/_sources/api/services/connect_service.rst.txt b/SRV/docs/_build/html/_sources/api/services/connect_service.rst.txt new file mode 100644 index 0000000..237c613 --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/services/connect_service.rst.txt @@ -0,0 +1,7 @@ +Connect Service +=============== + +.. automodule:: services.connect_service + :members: + :undoc-members: + :show-inheritance: diff --git a/SRV/docs/_build/html/_sources/api/services/firewalld_service.rst.txt b/SRV/docs/_build/html/_sources/api/services/firewalld_service.rst.txt new file mode 100644 index 0000000..9b6332f --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/services/firewalld_service.rst.txt @@ -0,0 +1,7 @@ +Firewalld Service +================= + +.. automodule:: services.firewalld_service + :members: + :undoc-members: + :show-inheritance: diff --git a/SRV/docs/_build/html/_sources/api/services/ip_service.rst.txt b/SRV/docs/_build/html/_sources/api/services/ip_service.rst.txt new file mode 100644 index 0000000..b976a29 --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/services/ip_service.rst.txt @@ -0,0 +1,7 @@ +IP Service +========== + +.. automodule:: services.ip_service + :members: + :undoc-members: + :show-inheritance: diff --git a/SRV/docs/_build/html/_sources/api/services/network_service.rst.txt b/SRV/docs/_build/html/_sources/api/services/network_service.rst.txt new file mode 100644 index 0000000..2794801 --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/services/network_service.rst.txt @@ -0,0 +1,7 @@ +Network Service +=============== + +.. automodule:: services.network_service + :members: + :undoc-members: + :show-inheritance: diff --git a/SRV/docs/_build/html/_sources/api/services/settings_service.rst.txt b/SRV/docs/_build/html/_sources/api/services/settings_service.rst.txt new file mode 100644 index 0000000..c459fca --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/services/settings_service.rst.txt @@ -0,0 +1,7 @@ +Settings Service +================ + +.. automodule:: services.settings_service + :members: + :undoc-members: + :show-inheritance: diff --git a/SRV/docs/_build/html/_sources/api/services/subnet_service.rst.txt b/SRV/docs/_build/html/_sources/api/services/subnet_service.rst.txt new file mode 100644 index 0000000..d059f8f --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/services/subnet_service.rst.txt @@ -0,0 +1,7 @@ +Subnet Service +============== + +.. automodule:: services.subnet_service + :members: + :undoc-members: + :show-inheritance: diff --git a/SRV/docs/_build/html/_sources/api/services/ufw_service.rst.txt b/SRV/docs/_build/html/_sources/api/services/ufw_service.rst.txt new file mode 100644 index 0000000..14ddc77 --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/services/ufw_service.rst.txt @@ -0,0 +1,7 @@ +UFW Service +=========== + +.. automodule:: services.ufw_service + :members: + :undoc-members: + :show-inheritance: diff --git a/SRV/docs/_build/html/_sources/api/services/wifi_service.rst.txt b/SRV/docs/_build/html/_sources/api/services/wifi_service.rst.txt new file mode 100644 index 0000000..787b7c5 --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/services/wifi_service.rst.txt @@ -0,0 +1,7 @@ +Wifi Service +============ + +.. automodule:: services.wifi_service + :members: + :undoc-members: + :show-inheritance: diff --git a/SRV/docs/_build/html/_sources/api/storage.rst.txt b/SRV/docs/_build/html/_sources/api/storage.rst.txt new file mode 100644 index 0000000..0a04d57 --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/storage.rst.txt @@ -0,0 +1,8 @@ +Хранилища +========= + +.. toctree:: + :maxdepth: 1 + + storage/json_storage + storage/session_storage diff --git a/SRV/docs/_build/html/_sources/api/storage/json_storage.rst.txt b/SRV/docs/_build/html/_sources/api/storage/json_storage.rst.txt new file mode 100644 index 0000000..0bb5039 --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/storage/json_storage.rst.txt @@ -0,0 +1,7 @@ +storage.json_storage +===================== + +.. automodule:: storage.json_storage + :members: + :undoc-members: + :show-inheritance: diff --git a/SRV/docs/_build/html/_sources/api/storage/session_storage.rst.txt b/SRV/docs/_build/html/_sources/api/storage/session_storage.rst.txt new file mode 100644 index 0000000..9ae2a9a --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/storage/session_storage.rst.txt @@ -0,0 +1,7 @@ +storage.session_storage +======================= + +.. automodule:: storage.session_storage + :members: + :undoc-members: + :show-inheritance: diff --git a/SRV/docs/_build/html/_sources/api/utils.rst.txt b/SRV/docs/_build/html/_sources/api/utils.rst.txt new file mode 100644 index 0000000..c50b082 --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/utils.rst.txt @@ -0,0 +1,7 @@ +Утилиты +======= + +.. toctree:: + :maxdepth: 1 + + utils/i18n diff --git a/SRV/docs/_build/html/_sources/api/utils/i18n.rst.txt b/SRV/docs/_build/html/_sources/api/utils/i18n.rst.txt new file mode 100644 index 0000000..74cd021 --- /dev/null +++ b/SRV/docs/_build/html/_sources/api/utils/i18n.rst.txt @@ -0,0 +1,7 @@ +utils.i18n +========== + +.. automodule:: utils.i18n + :members: + :undoc-members: + :show-inheritance: diff --git a/SRV/docs/_build/html/_sources/index.rst.txt b/SRV/docs/_build/html/_sources/index.rst.txt new file mode 100644 index 0000000..003a09a --- /dev/null +++ b/SRV/docs/_build/html/_sources/index.rst.txt @@ -0,0 +1,19 @@ +GONETS SRV Documentation +========================= + +Добро пожаловать в документацию серверной части GONETS (SRV). + +Содержание +--------- + +.. toctree:: + :maxdepth: 2 + :caption: Разделы + + api/services + api/models + api/endpoints + api/config + api/core + api/storage + api/utils diff --git a/SRV/docs/_build/html/_static/basic.css b/SRV/docs/_build/html/_static/basic.css new file mode 100644 index 0000000..4738b2e --- /dev/null +++ b/SRV/docs/_build/html/_static/basic.css @@ -0,0 +1,906 @@ +/* + * Sphinx stylesheet -- basic theme. + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin-top: 10px; +} + +ul.search li { + padding: 5px 0; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 360px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a:visited { + color: #551A8B; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +nav.contents, +aside.topic, +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +nav.contents, +aside.topic, +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +nav.contents > :last-child, +aside.topic > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +nav.contents::after, +aside.topic::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +aside.footnote > span, +div.citation > span { + float: left; +} +aside.footnote > span:last-of-type, +div.citation > span:last-of-type { + padding-right: 0.5em; +} +aside.footnote > p { + margin-left: 2em; +} +div.citation > p { + margin-left: 4em; +} +aside.footnote > p:last-of-type, +div.citation > p:last-of-type { + margin-bottom: 0em; +} +aside.footnote > p:last-of-type:after, +div.citation > p:last-of-type:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +.sig dd { + margin-top: 0px; + margin-bottom: 0px; +} + +.sig dl { + margin-top: 0px; + margin-bottom: 0px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/SRV/docs/_build/html/_static/debug.css b/SRV/docs/_build/html/_static/debug.css new file mode 100644 index 0000000..74d4aec --- /dev/null +++ b/SRV/docs/_build/html/_static/debug.css @@ -0,0 +1,69 @@ +/* + This CSS file should be overridden by the theme authors. It's + meant for debugging and developing the skeleton that this theme provides. +*/ +body { + font-family: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, + "Apple Color Emoji", "Segoe UI Emoji"; + background: lavender; +} +.sb-announcement { + background: rgb(131, 131, 131); +} +.sb-announcement__inner { + background: black; + color: white; +} +.sb-header { + background: lightskyblue; +} +.sb-header__inner { + background: royalblue; + color: white; +} +.sb-header-secondary { + background: lightcyan; +} +.sb-header-secondary__inner { + background: cornflowerblue; + color: white; +} +.sb-sidebar-primary { + background: lightgreen; +} +.sb-main { + background: blanchedalmond; +} +.sb-main__inner { + background: antiquewhite; +} +.sb-header-article { + background: lightsteelblue; +} +.sb-article-container { + background: snow; +} +.sb-article-main { + background: white; +} +.sb-footer-article { + background: lightpink; +} +.sb-sidebar-secondary { + background: lightgoldenrodyellow; +} +.sb-footer-content { + background: plum; +} +.sb-footer-content__inner { + background: palevioletred; +} +.sb-footer { + background: pink; +} +.sb-footer__inner { + background: salmon; +} +.sb-article { + background: white; +} diff --git a/SRV/docs/_build/html/_static/doctools.js b/SRV/docs/_build/html/_static/doctools.js new file mode 100644 index 0000000..0398ebb --- /dev/null +++ b/SRV/docs/_build/html/_static/doctools.js @@ -0,0 +1,149 @@ +/* + * Base JavaScript utilities for all Sphinx HTML documentation. + */ +"use strict"; + +const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ + "TEXTAREA", + "INPUT", + "SELECT", + "BUTTON", +]); + +const _ready = (callback) => { + if (document.readyState !== "loading") { + callback(); + } else { + document.addEventListener("DOMContentLoaded", callback); + } +}; + +/** + * Small JavaScript module for the documentation. + */ +const Documentation = { + init: () => { + Documentation.initDomainIndexTable(); + Documentation.initOnKeyListeners(); + }, + + /** + * i18n support + */ + TRANSLATIONS: {}, + PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), + LOCALE: "unknown", + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext: (string) => { + const translated = Documentation.TRANSLATIONS[string]; + switch (typeof translated) { + case "undefined": + return string; // no translation + case "string": + return translated; // translation exists + default: + return translated[0]; // (singular, plural) translation tuple exists + } + }, + + ngettext: (singular, plural, n) => { + const translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated !== "undefined") + return translated[Documentation.PLURAL_EXPR(n)]; + return n === 1 ? singular : plural; + }, + + addTranslations: (catalog) => { + Object.assign(Documentation.TRANSLATIONS, catalog.messages); + Documentation.PLURAL_EXPR = new Function( + "n", + `return (${catalog.plural_expr})` + ); + Documentation.LOCALE = catalog.locale; + }, + + /** + * helper function to focus on search bar + */ + focusSearchBar: () => { + document.querySelectorAll("input[name=q]")[0]?.focus(); + }, + + /** + * Initialise the domain index toggle buttons + */ + initDomainIndexTable: () => { + const toggler = (el) => { + const idNumber = el.id.substr(7); + const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); + if (el.src.substr(-9) === "minus.png") { + el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; + toggledRows.forEach((el) => (el.style.display = "none")); + } else { + el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; + toggledRows.forEach((el) => (el.style.display = "")); + } + }; + + const togglerElements = document.querySelectorAll("img.toggler"); + togglerElements.forEach((el) => + el.addEventListener("click", (event) => toggler(event.currentTarget)) + ); + togglerElements.forEach((el) => (el.style.display = "")); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); + }, + + initOnKeyListeners: () => { + // only install a listener if it is really needed + if ( + !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && + !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + ) + return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.altKey || event.ctrlKey || event.metaKey) return; + + if (!event.shiftKey) { + switch (event.key) { + case "ArrowLeft": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const prevLink = document.querySelector('link[rel="prev"]'); + if (prevLink && prevLink.href) { + window.location.href = prevLink.href; + event.preventDefault(); + } + break; + case "ArrowRight": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const nextLink = document.querySelector('link[rel="next"]'); + if (nextLink && nextLink.href) { + window.location.href = nextLink.href; + event.preventDefault(); + } + break; + } + } + + // some keyboard layouts may need Shift to get / + switch (event.key) { + case "/": + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; + Documentation.focusSearchBar(); + event.preventDefault(); + } + }); + }, +}; + +// quick alias for translations +const _ = Documentation.gettext; + +_ready(Documentation.init); diff --git a/SRV/docs/_build/html/_static/documentation_options.js b/SRV/docs/_build/html/_static/documentation_options.js new file mode 100644 index 0000000..74029b8 --- /dev/null +++ b/SRV/docs/_build/html/_static/documentation_options.js @@ -0,0 +1,13 @@ +const DOCUMENTATION_OPTIONS = { + VERSION: '0.0.3', + LANGUAGE: 'en', + COLLAPSE_INDEX: false, + BUILDER: 'html', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false, + SHOW_SEARCH_SUMMARY: true, + ENABLE_SEARCH_SHORTCUTS: true, +}; \ No newline at end of file diff --git a/SRV/docs/_build/html/_static/file.png b/SRV/docs/_build/html/_static/file.png new file mode 100644 index 0000000000000000000000000000000000000000..a858a410e4faa62ce324d814e4b816fff83a6fb3 GIT binary patch literal 286 zcmV+(0pb3MP)s`hMrGg#P~ix$^RISR_I47Y|r1 z_CyJOe}D1){SET-^Amu_i71Lt6eYfZjRyw@I6OQAIXXHDfiX^GbOlHe=Ae4>0m)d(f|Me07*qoM6N<$f}vM^LjV8( literal 0 HcmV?d00001 diff --git a/SRV/docs/_build/html/_static/language_data.js b/SRV/docs/_build/html/_static/language_data.js new file mode 100644 index 0000000..c7fe6c6 --- /dev/null +++ b/SRV/docs/_build/html/_static/language_data.js @@ -0,0 +1,192 @@ +/* + * This script contains the language-specific data used by searchtools.js, + * namely the list of stopwords, stemmer, scorer and splitter. + */ + +var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"]; + + +/* Non-minified version is copied as a separate JS file, if available */ + +/** + * Porter Stemmer + */ +var Stemmer = function() { + + var step2list = { + ational: 'ate', + tional: 'tion', + enci: 'ence', + anci: 'ance', + izer: 'ize', + bli: 'ble', + alli: 'al', + entli: 'ent', + eli: 'e', + ousli: 'ous', + ization: 'ize', + ation: 'ate', + ator: 'ate', + alism: 'al', + iveness: 'ive', + fulness: 'ful', + ousness: 'ous', + aliti: 'al', + iviti: 'ive', + biliti: 'ble', + logi: 'log' + }; + + var step3list = { + icate: 'ic', + ative: '', + alize: 'al', + iciti: 'ic', + ical: 'ic', + ful: '', + ness: '' + }; + + var c = "[^aeiou]"; // consonant + var v = "[aeiouy]"; // vowel + var C = c + "[^aeiouy]*"; // consonant sequence + var V = v + "[aeiou]*"; // vowel sequence + + var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 + var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 + var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 + var s_v = "^(" + C + ")?" + v; // vowel in stem + + this.stemWord = function (w) { + var stem; + var suffix; + var firstch; + var origword = w; + + if (w.length < 3) + return w; + + var re; + var re2; + var re3; + var re4; + + firstch = w.substr(0,1); + if (firstch == "y") + w = firstch.toUpperCase() + w.substr(1); + + // Step 1a + re = /^(.+?)(ss|i)es$/; + re2 = /^(.+?)([^s])s$/; + + if (re.test(w)) + w = w.replace(re,"$1$2"); + else if (re2.test(w)) + w = w.replace(re2,"$1$2"); + + // Step 1b + re = /^(.+?)eed$/; + re2 = /^(.+?)(ed|ing)$/; + if (re.test(w)) { + var fp = re.exec(w); + re = new RegExp(mgr0); + if (re.test(fp[1])) { + re = /.$/; + w = w.replace(re,""); + } + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = new RegExp(s_v); + if (re2.test(stem)) { + w = stem; + re2 = /(at|bl|iz)$/; + re3 = new RegExp("([^aeiouylsz])\\1$"); + re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re2.test(w)) + w = w + "e"; + else if (re3.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + else if (re4.test(w)) + w = w + "e"; + } + } + + // Step 1c + re = /^(.+?)y$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(s_v); + if (re.test(stem)) + w = stem + "i"; + } + + // Step 2 + re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step2list[suffix]; + } + + // Step 3 + re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step3list[suffix]; + } + + // Step 4 + re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + re2 = /^(.+?)(s|t)(ion)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + if (re.test(stem)) + w = stem; + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = new RegExp(mgr1); + if (re2.test(stem)) + w = stem; + } + + // Step 5 + re = /^(.+?)e$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + re2 = new RegExp(meq1); + re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) + w = stem; + } + re = /ll$/; + re2 = new RegExp(mgr1); + if (re.test(w) && re2.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + + // and turn initial Y back to y + if (firstch == "y") + w = firstch.toLowerCase() + w.substr(1); + return w; + } +} + diff --git a/SRV/docs/_build/html/_static/minus.png b/SRV/docs/_build/html/_static/minus.png new file mode 100644 index 0000000000000000000000000000000000000000..d96755fdaf8bb2214971e0db9c1fd3077d7c419d GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^+#t*WBp7;*Yy1LIik>cxAr*|t7R?Mi>2?kWtu=nj kDsEF_5m^0CR;1wuP-*O&G^0G}KYk!hp00i_>zopr08q^qX#fBK literal 0 HcmV?d00001 diff --git a/SRV/docs/_build/html/_static/plus.png b/SRV/docs/_build/html/_static/plus.png new file mode 100644 index 0000000000000000000000000000000000000000..7107cec93a979b9a5f64843235a16651d563ce2d GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^+#t*WBp7;*Yy1LIik>cxAr*|t7R?Mi>2?kWtu>-2 m3q%Vub%g%s<8sJhVPMczOq}xhg9DJoz~JfX=d#Wzp$Pyb1r*Kz literal 0 HcmV?d00001 diff --git a/SRV/docs/_build/html/_static/pygments.css b/SRV/docs/_build/html/_static/pygments.css new file mode 100644 index 0000000..9d1083b --- /dev/null +++ b/SRV/docs/_build/html/_static/pygments.css @@ -0,0 +1,250 @@ +.highlight pre { line-height: 125%; } +.highlight td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +.highlight span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +.highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight .hll { background-color: #fdf2e2 } +.highlight { background: #f2f2f2; color: #1E1E1E } +.highlight .c { color: #515151 } /* Comment */ +.highlight .err { color: #D71835 } /* Error */ +.highlight .k { color: #8045E5 } /* Keyword */ +.highlight .l { color: #7F4707 } /* Literal */ +.highlight .n { color: #1E1E1E } /* Name */ +.highlight .o { color: #163 } /* Operator */ +.highlight .p { color: #1E1E1E } /* Punctuation */ +.highlight .ch { color: #515151 } /* Comment.Hashbang */ +.highlight .cm { color: #515151 } /* Comment.Multiline */ +.highlight .cp { color: #515151 } /* Comment.Preproc */ +.highlight .cpf { color: #515151 } /* Comment.PreprocFile */ +.highlight .c1 { color: #515151 } /* Comment.Single */ +.highlight .cs { color: #515151 } /* Comment.Special */ +.highlight .gd { color: #00749C } /* Generic.Deleted */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .gh { color: #00749C } /* Generic.Heading */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #00749C } /* Generic.Subheading */ +.highlight .kc { color: #8045E5 } /* Keyword.Constant */ +.highlight .kd { color: #8045E5 } /* Keyword.Declaration */ +.highlight .kn { color: #8045E5 } /* Keyword.Namespace */ +.highlight .kp { color: #8045E5 } /* Keyword.Pseudo */ +.highlight .kr { color: #8045E5 } /* Keyword.Reserved */ +.highlight .kt { color: #7F4707 } /* Keyword.Type */ +.highlight .ld { color: #7F4707 } /* Literal.Date */ +.highlight .m { color: #7F4707 } /* Literal.Number */ +.highlight .s { color: #163 } /* Literal.String */ +.highlight .na { color: #7F4707 } /* Name.Attribute */ +.highlight .nb { color: #7F4707 } /* Name.Builtin */ +.highlight .nc { color: #00749C } /* Name.Class */ +.highlight .no { color: #00749C } /* Name.Constant */ +.highlight .nd { color: #7F4707 } /* Name.Decorator */ +.highlight .ni { color: #163 } /* Name.Entity */ +.highlight .ne { color: #8045E5 } /* Name.Exception */ +.highlight .nf { color: #00749C } /* Name.Function */ +.highlight .nl { color: #7F4707 } /* Name.Label */ +.highlight .nn { color: #1E1E1E } /* Name.Namespace */ +.highlight .nx { color: #1E1E1E } /* Name.Other */ +.highlight .py { color: #00749C } /* Name.Property */ +.highlight .nt { color: #00749C } /* Name.Tag */ +.highlight .nv { color: #D71835 } /* Name.Variable */ +.highlight .ow { color: #8045E5 } /* Operator.Word */ +.highlight .pm { color: #1E1E1E } /* Punctuation.Marker */ +.highlight .w { color: #1E1E1E } /* Text.Whitespace */ +.highlight .mb { color: #7F4707 } /* Literal.Number.Bin */ +.highlight .mf { color: #7F4707 } /* Literal.Number.Float */ +.highlight .mh { color: #7F4707 } /* Literal.Number.Hex */ +.highlight .mi { color: #7F4707 } /* Literal.Number.Integer */ +.highlight .mo { color: #7F4707 } /* Literal.Number.Oct */ +.highlight .sa { color: #163 } /* Literal.String.Affix */ +.highlight .sb { color: #163 } /* Literal.String.Backtick */ +.highlight .sc { color: #163 } /* Literal.String.Char */ +.highlight .dl { color: #163 } /* Literal.String.Delimiter */ +.highlight .sd { color: #163 } /* Literal.String.Doc */ +.highlight .s2 { color: #163 } /* Literal.String.Double */ +.highlight .se { color: #163 } /* Literal.String.Escape */ +.highlight .sh { color: #163 } /* Literal.String.Heredoc */ +.highlight .si { color: #163 } /* Literal.String.Interpol */ +.highlight .sx { color: #163 } /* Literal.String.Other */ +.highlight .sr { color: #D71835 } /* Literal.String.Regex */ +.highlight .s1 { color: #163 } /* Literal.String.Single */ +.highlight .ss { color: #00749C } /* Literal.String.Symbol */ +.highlight .bp { color: #7F4707 } /* Name.Builtin.Pseudo */ +.highlight .fm { color: #00749C } /* Name.Function.Magic */ +.highlight .vc { color: #D71835 } /* Name.Variable.Class */ +.highlight .vg { color: #D71835 } /* Name.Variable.Global */ +.highlight .vi { color: #D71835 } /* Name.Variable.Instance */ +.highlight .vm { color: #7F4707 } /* Name.Variable.Magic */ +.highlight .il { color: #7F4707 } /* Literal.Number.Integer.Long */ +@media not print { +body[data-theme="dark"] .highlight pre { line-height: 125%; } +body[data-theme="dark"] .highlight td.linenos .normal { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; } +body[data-theme="dark"] .highlight span.linenos { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; } +body[data-theme="dark"] .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +body[data-theme="dark"] .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +body[data-theme="dark"] .highlight .hll { background-color: #404040 } +body[data-theme="dark"] .highlight { background: #202020; color: #D0D0D0 } +body[data-theme="dark"] .highlight .c { color: #ABABAB; font-style: italic } /* Comment */ +body[data-theme="dark"] .highlight .err { color: #A61717; background-color: #E3D2D2 } /* Error */ +body[data-theme="dark"] .highlight .esc { color: #D0D0D0 } /* Escape */ +body[data-theme="dark"] .highlight .g { color: #D0D0D0 } /* Generic */ +body[data-theme="dark"] .highlight .k { color: #6EBF26; font-weight: bold } /* Keyword */ +body[data-theme="dark"] .highlight .l { color: #D0D0D0 } /* Literal */ +body[data-theme="dark"] .highlight .n { color: #D0D0D0 } /* Name */ +body[data-theme="dark"] .highlight .o { color: #D0D0D0 } /* Operator */ +body[data-theme="dark"] .highlight .x { color: #D0D0D0 } /* Other */ +body[data-theme="dark"] .highlight .p { color: #D0D0D0 } /* Punctuation */ +body[data-theme="dark"] .highlight .ch { color: #ABABAB; font-style: italic } /* Comment.Hashbang */ +body[data-theme="dark"] .highlight .cm { color: #ABABAB; font-style: italic } /* Comment.Multiline */ +body[data-theme="dark"] .highlight .cp { color: #FF3A3A; font-weight: bold } /* Comment.Preproc */ +body[data-theme="dark"] .highlight .cpf { color: #ABABAB; font-style: italic } /* Comment.PreprocFile */ +body[data-theme="dark"] .highlight .c1 { color: #ABABAB; font-style: italic } /* Comment.Single */ +body[data-theme="dark"] .highlight .cs { color: #E50808; font-weight: bold; background-color: #520000 } /* Comment.Special */ +body[data-theme="dark"] .highlight .gd { color: #FF3A3A } /* Generic.Deleted */ +body[data-theme="dark"] .highlight .ge { color: #D0D0D0; font-style: italic } /* Generic.Emph */ +body[data-theme="dark"] .highlight .ges { color: #D0D0D0; font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +body[data-theme="dark"] .highlight .gr { color: #FF3A3A } /* Generic.Error */ +body[data-theme="dark"] .highlight .gh { color: #FFF; font-weight: bold } /* Generic.Heading */ +body[data-theme="dark"] .highlight .gi { color: #589819 } /* Generic.Inserted */ +body[data-theme="dark"] .highlight .go { color: #CCC } /* Generic.Output */ +body[data-theme="dark"] .highlight .gp { color: #AAA } /* Generic.Prompt */ +body[data-theme="dark"] .highlight .gs { color: #D0D0D0; font-weight: bold } /* Generic.Strong */ +body[data-theme="dark"] .highlight .gu { color: #FFF; text-decoration: underline } /* Generic.Subheading */ +body[data-theme="dark"] .highlight .gt { color: #FF3A3A } /* Generic.Traceback */ +body[data-theme="dark"] .highlight .kc { color: #6EBF26; font-weight: bold } /* Keyword.Constant */ +body[data-theme="dark"] .highlight .kd { color: #6EBF26; font-weight: bold } /* Keyword.Declaration */ +body[data-theme="dark"] .highlight .kn { color: #6EBF26; font-weight: bold } /* Keyword.Namespace */ +body[data-theme="dark"] .highlight .kp { color: #6EBF26 } /* Keyword.Pseudo */ +body[data-theme="dark"] .highlight .kr { color: #6EBF26; font-weight: bold } /* Keyword.Reserved */ +body[data-theme="dark"] .highlight .kt { color: #6EBF26; font-weight: bold } /* Keyword.Type */ +body[data-theme="dark"] .highlight .ld { color: #D0D0D0 } /* Literal.Date */ +body[data-theme="dark"] .highlight .m { color: #51B2FD } /* Literal.Number */ +body[data-theme="dark"] .highlight .s { color: #ED9D13 } /* Literal.String */ +body[data-theme="dark"] .highlight .na { color: #BBB } /* Name.Attribute */ +body[data-theme="dark"] .highlight .nb { color: #2FBCCD } /* Name.Builtin */ +body[data-theme="dark"] .highlight .nc { color: #71ADFF; text-decoration: underline } /* Name.Class */ +body[data-theme="dark"] .highlight .no { color: #40FFFF } /* Name.Constant */ +body[data-theme="dark"] .highlight .nd { color: #FFA500 } /* Name.Decorator */ +body[data-theme="dark"] .highlight .ni { color: #D0D0D0 } /* Name.Entity */ +body[data-theme="dark"] .highlight .ne { color: #BBB } /* Name.Exception */ +body[data-theme="dark"] .highlight .nf { color: #71ADFF } /* Name.Function */ +body[data-theme="dark"] .highlight .nl { color: #D0D0D0 } /* Name.Label */ +body[data-theme="dark"] .highlight .nn { color: #71ADFF; text-decoration: underline } /* Name.Namespace */ +body[data-theme="dark"] .highlight .nx { color: #D0D0D0 } /* Name.Other */ +body[data-theme="dark"] .highlight .py { color: #D0D0D0 } /* Name.Property */ +body[data-theme="dark"] .highlight .nt { color: #6EBF26; font-weight: bold } /* Name.Tag */ +body[data-theme="dark"] .highlight .nv { color: #40FFFF } /* Name.Variable */ +body[data-theme="dark"] .highlight .ow { color: #6EBF26; font-weight: bold } /* Operator.Word */ +body[data-theme="dark"] .highlight .pm { color: #D0D0D0 } /* Punctuation.Marker */ +body[data-theme="dark"] .highlight .w { color: #666 } /* Text.Whitespace */ +body[data-theme="dark"] .highlight .mb { color: #51B2FD } /* Literal.Number.Bin */ +body[data-theme="dark"] .highlight .mf { color: #51B2FD } /* Literal.Number.Float */ +body[data-theme="dark"] .highlight .mh { color: #51B2FD } /* Literal.Number.Hex */ +body[data-theme="dark"] .highlight .mi { color: #51B2FD } /* Literal.Number.Integer */ +body[data-theme="dark"] .highlight .mo { color: #51B2FD } /* Literal.Number.Oct */ +body[data-theme="dark"] .highlight .sa { color: #ED9D13 } /* Literal.String.Affix */ +body[data-theme="dark"] .highlight .sb { color: #ED9D13 } /* Literal.String.Backtick */ +body[data-theme="dark"] .highlight .sc { color: #ED9D13 } /* Literal.String.Char */ +body[data-theme="dark"] .highlight .dl { color: #ED9D13 } /* Literal.String.Delimiter */ +body[data-theme="dark"] .highlight .sd { color: #ED9D13 } /* Literal.String.Doc */ +body[data-theme="dark"] .highlight .s2 { color: #ED9D13 } /* Literal.String.Double */ +body[data-theme="dark"] .highlight .se { color: #ED9D13 } /* Literal.String.Escape */ +body[data-theme="dark"] .highlight .sh { color: #ED9D13 } /* Literal.String.Heredoc */ +body[data-theme="dark"] .highlight .si { color: #ED9D13 } /* Literal.String.Interpol */ +body[data-theme="dark"] .highlight .sx { color: #FFA500 } /* Literal.String.Other */ +body[data-theme="dark"] .highlight .sr { color: #ED9D13 } /* Literal.String.Regex */ +body[data-theme="dark"] .highlight .s1 { color: #ED9D13 } /* Literal.String.Single */ +body[data-theme="dark"] .highlight .ss { color: #ED9D13 } /* Literal.String.Symbol */ +body[data-theme="dark"] .highlight .bp { color: #2FBCCD } /* Name.Builtin.Pseudo */ +body[data-theme="dark"] .highlight .fm { color: #71ADFF } /* Name.Function.Magic */ +body[data-theme="dark"] .highlight .vc { color: #40FFFF } /* Name.Variable.Class */ +body[data-theme="dark"] .highlight .vg { color: #40FFFF } /* Name.Variable.Global */ +body[data-theme="dark"] .highlight .vi { color: #40FFFF } /* Name.Variable.Instance */ +body[data-theme="dark"] .highlight .vm { color: #40FFFF } /* Name.Variable.Magic */ +body[data-theme="dark"] .highlight .il { color: #51B2FD } /* Literal.Number.Integer.Long */ +@media (prefers-color-scheme: dark) { +body:not([data-theme="light"]) .highlight pre { line-height: 125%; } +body:not([data-theme="light"]) .highlight td.linenos .normal { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; } +body:not([data-theme="light"]) .highlight span.linenos { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; } +body:not([data-theme="light"]) .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +body:not([data-theme="light"]) .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +body:not([data-theme="light"]) .highlight .hll { background-color: #404040 } +body:not([data-theme="light"]) .highlight { background: #202020; color: #D0D0D0 } +body:not([data-theme="light"]) .highlight .c { color: #ABABAB; font-style: italic } /* Comment */ +body:not([data-theme="light"]) .highlight .err { color: #A61717; background-color: #E3D2D2 } /* Error */ +body:not([data-theme="light"]) .highlight .esc { color: #D0D0D0 } /* Escape */ +body:not([data-theme="light"]) .highlight .g { color: #D0D0D0 } /* Generic */ +body:not([data-theme="light"]) .highlight .k { color: #6EBF26; font-weight: bold } /* Keyword */ +body:not([data-theme="light"]) .highlight .l { color: #D0D0D0 } /* Literal */ +body:not([data-theme="light"]) .highlight .n { color: #D0D0D0 } /* Name */ +body:not([data-theme="light"]) .highlight .o { color: #D0D0D0 } /* Operator */ +body:not([data-theme="light"]) .highlight .x { color: #D0D0D0 } /* Other */ +body:not([data-theme="light"]) .highlight .p { color: #D0D0D0 } /* Punctuation */ +body:not([data-theme="light"]) .highlight .ch { color: #ABABAB; font-style: italic } /* Comment.Hashbang */ +body:not([data-theme="light"]) .highlight .cm { color: #ABABAB; font-style: italic } /* Comment.Multiline */ +body:not([data-theme="light"]) .highlight .cp { color: #FF3A3A; font-weight: bold } /* Comment.Preproc */ +body:not([data-theme="light"]) .highlight .cpf { color: #ABABAB; font-style: italic } /* Comment.PreprocFile */ +body:not([data-theme="light"]) .highlight .c1 { color: #ABABAB; font-style: italic } /* Comment.Single */ +body:not([data-theme="light"]) .highlight .cs { color: #E50808; font-weight: bold; background-color: #520000 } /* Comment.Special */ +body:not([data-theme="light"]) .highlight .gd { color: #FF3A3A } /* Generic.Deleted */ +body:not([data-theme="light"]) .highlight .ge { color: #D0D0D0; font-style: italic } /* Generic.Emph */ +body:not([data-theme="light"]) .highlight .ges { color: #D0D0D0; font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +body:not([data-theme="light"]) .highlight .gr { color: #FF3A3A } /* Generic.Error */ +body:not([data-theme="light"]) .highlight .gh { color: #FFF; font-weight: bold } /* Generic.Heading */ +body:not([data-theme="light"]) .highlight .gi { color: #589819 } /* Generic.Inserted */ +body:not([data-theme="light"]) .highlight .go { color: #CCC } /* Generic.Output */ +body:not([data-theme="light"]) .highlight .gp { color: #AAA } /* Generic.Prompt */ +body:not([data-theme="light"]) .highlight .gs { color: #D0D0D0; font-weight: bold } /* Generic.Strong */ +body:not([data-theme="light"]) .highlight .gu { color: #FFF; text-decoration: underline } /* Generic.Subheading */ +body:not([data-theme="light"]) .highlight .gt { color: #FF3A3A } /* Generic.Traceback */ +body:not([data-theme="light"]) .highlight .kc { color: #6EBF26; font-weight: bold } /* Keyword.Constant */ +body:not([data-theme="light"]) .highlight .kd { color: #6EBF26; font-weight: bold } /* Keyword.Declaration */ +body:not([data-theme="light"]) .highlight .kn { color: #6EBF26; font-weight: bold } /* Keyword.Namespace */ +body:not([data-theme="light"]) .highlight .kp { color: #6EBF26 } /* Keyword.Pseudo */ +body:not([data-theme="light"]) .highlight .kr { color: #6EBF26; font-weight: bold } /* Keyword.Reserved */ +body:not([data-theme="light"]) .highlight .kt { color: #6EBF26; font-weight: bold } /* Keyword.Type */ +body:not([data-theme="light"]) .highlight .ld { color: #D0D0D0 } /* Literal.Date */ +body:not([data-theme="light"]) .highlight .m { color: #51B2FD } /* Literal.Number */ +body:not([data-theme="light"]) .highlight .s { color: #ED9D13 } /* Literal.String */ +body:not([data-theme="light"]) .highlight .na { color: #BBB } /* Name.Attribute */ +body:not([data-theme="light"]) .highlight .nb { color: #2FBCCD } /* Name.Builtin */ +body:not([data-theme="light"]) .highlight .nc { color: #71ADFF; text-decoration: underline } /* Name.Class */ +body:not([data-theme="light"]) .highlight .no { color: #40FFFF } /* Name.Constant */ +body:not([data-theme="light"]) .highlight .nd { color: #FFA500 } /* Name.Decorator */ +body:not([data-theme="light"]) .highlight .ni { color: #D0D0D0 } /* Name.Entity */ +body:not([data-theme="light"]) .highlight .ne { color: #BBB } /* Name.Exception */ +body:not([data-theme="light"]) .highlight .nf { color: #71ADFF } /* Name.Function */ +body:not([data-theme="light"]) .highlight .nl { color: #D0D0D0 } /* Name.Label */ +body:not([data-theme="light"]) .highlight .nn { color: #71ADFF; text-decoration: underline } /* Name.Namespace */ +body:not([data-theme="light"]) .highlight .nx { color: #D0D0D0 } /* Name.Other */ +body:not([data-theme="light"]) .highlight .py { color: #D0D0D0 } /* Name.Property */ +body:not([data-theme="light"]) .highlight .nt { color: #6EBF26; font-weight: bold } /* Name.Tag */ +body:not([data-theme="light"]) .highlight .nv { color: #40FFFF } /* Name.Variable */ +body:not([data-theme="light"]) .highlight .ow { color: #6EBF26; font-weight: bold } /* Operator.Word */ +body:not([data-theme="light"]) .highlight .pm { color: #D0D0D0 } /* Punctuation.Marker */ +body:not([data-theme="light"]) .highlight .w { color: #666 } /* Text.Whitespace */ +body:not([data-theme="light"]) .highlight .mb { color: #51B2FD } /* Literal.Number.Bin */ +body:not([data-theme="light"]) .highlight .mf { color: #51B2FD } /* Literal.Number.Float */ +body:not([data-theme="light"]) .highlight .mh { color: #51B2FD } /* Literal.Number.Hex */ +body:not([data-theme="light"]) .highlight .mi { color: #51B2FD } /* Literal.Number.Integer */ +body:not([data-theme="light"]) .highlight .mo { color: #51B2FD } /* Literal.Number.Oct */ +body:not([data-theme="light"]) .highlight .sa { color: #ED9D13 } /* Literal.String.Affix */ +body:not([data-theme="light"]) .highlight .sb { color: #ED9D13 } /* Literal.String.Backtick */ +body:not([data-theme="light"]) .highlight .sc { color: #ED9D13 } /* Literal.String.Char */ +body:not([data-theme="light"]) .highlight .dl { color: #ED9D13 } /* Literal.String.Delimiter */ +body:not([data-theme="light"]) .highlight .sd { color: #ED9D13 } /* Literal.String.Doc */ +body:not([data-theme="light"]) .highlight .s2 { color: #ED9D13 } /* Literal.String.Double */ +body:not([data-theme="light"]) .highlight .se { color: #ED9D13 } /* Literal.String.Escape */ +body:not([data-theme="light"]) .highlight .sh { color: #ED9D13 } /* Literal.String.Heredoc */ +body:not([data-theme="light"]) .highlight .si { color: #ED9D13 } /* Literal.String.Interpol */ +body:not([data-theme="light"]) .highlight .sx { color: #FFA500 } /* Literal.String.Other */ +body:not([data-theme="light"]) .highlight .sr { color: #ED9D13 } /* Literal.String.Regex */ +body:not([data-theme="light"]) .highlight .s1 { color: #ED9D13 } /* Literal.String.Single */ +body:not([data-theme="light"]) .highlight .ss { color: #ED9D13 } /* Literal.String.Symbol */ +body:not([data-theme="light"]) .highlight .bp { color: #2FBCCD } /* Name.Builtin.Pseudo */ +body:not([data-theme="light"]) .highlight .fm { color: #71ADFF } /* Name.Function.Magic */ +body:not([data-theme="light"]) .highlight .vc { color: #40FFFF } /* Name.Variable.Class */ +body:not([data-theme="light"]) .highlight .vg { color: #40FFFF } /* Name.Variable.Global */ +body:not([data-theme="light"]) .highlight .vi { color: #40FFFF } /* Name.Variable.Instance */ +body:not([data-theme="light"]) .highlight .vm { color: #40FFFF } /* Name.Variable.Magic */ +body:not([data-theme="light"]) .highlight .il { color: #51B2FD } /* Literal.Number.Integer.Long */ +} +} \ No newline at end of file diff --git a/SRV/docs/_build/html/_static/scripts/furo-extensions.js b/SRV/docs/_build/html/_static/scripts/furo-extensions.js new file mode 100644 index 0000000..e69de29 diff --git a/SRV/docs/_build/html/_static/scripts/furo.js b/SRV/docs/_build/html/_static/scripts/furo.js new file mode 100644 index 0000000..87e1767 --- /dev/null +++ b/SRV/docs/_build/html/_static/scripts/furo.js @@ -0,0 +1,3 @@ +/*! For license information please see furo.js.LICENSE.txt */ +(()=>{var t={856:function(t,e,n){var o,r;r=void 0!==n.g?n.g:"undefined"!=typeof window?window:this,o=function(){return function(t){"use strict";var e={navClass:"active",contentClass:"active",nested:!1,nestedClass:"active",offset:0,reflow:!1,events:!0},n=function(t,e,n){if(n.settings.events){var o=new CustomEvent(t,{bubbles:!0,cancelable:!0,detail:n});e.dispatchEvent(o)}},o=function(t){var e=0;if(t.offsetParent)for(;t;)e+=t.offsetTop,t=t.offsetParent;return e>=0?e:0},r=function(t){t&&t.sort(function(t,e){return o(t.content)=Math.max(document.body.scrollHeight,document.documentElement.scrollHeight,document.body.offsetHeight,document.documentElement.offsetHeight,document.body.clientHeight,document.documentElement.clientHeight)},l=function(t,e){var n=t[t.length-1];if(function(t,e){return!(!s()||!c(t.content,e,!0))}(n,e))return n;for(var o=t.length-1;o>=0;o--)if(c(t[o].content,e))return t[o]},a=function(t,e){if(e.nested&&t.parentNode){var n=t.parentNode.closest("li");n&&(n.classList.remove(e.nestedClass),a(n,e))}},i=function(t,e){if(t){var o=t.nav.closest("li");o&&(o.classList.remove(e.navClass),t.content.classList.remove(e.contentClass),a(o,e),n("gumshoeDeactivate",o,{link:t.nav,content:t.content,settings:e}))}},u=function(t,e){if(e.nested){var n=t.parentNode.closest("li");n&&(n.classList.add(e.nestedClass),u(n,e))}};return function(o,c){var s,a,d,f,m,v={setup:function(){s=document.querySelectorAll(o),a=[],Array.prototype.forEach.call(s,function(t){var e=document.getElementById(decodeURIComponent(t.hash.substr(1)));e&&a.push({nav:t,content:e})}),r(a)},detect:function(){var t=l(a,m);t?d&&t.content===d.content||(i(d,m),function(t,e){if(t){var o=t.nav.closest("li");o&&(o.classList.add(e.navClass),t.content.classList.add(e.contentClass),u(o,e),n("gumshoeActivate",o,{link:t.nav,content:t.content,settings:e}))}}(t,m),d=t):d&&(i(d,m),d=null)}},h=function(e){f&&t.cancelAnimationFrame(f),f=t.requestAnimationFrame(v.detect)},g=function(e){f&&t.cancelAnimationFrame(f),f=t.requestAnimationFrame(function(){r(a),v.detect()})};return v.destroy=function(){d&&i(d,m),t.removeEventListener("scroll",h,!1),m.reflow&&t.removeEventListener("resize",g,!1),a=null,s=null,d=null,f=null,m=null},m=function(){var t={};return Array.prototype.forEach.call(arguments,function(e){for(var n in e){if(!e.hasOwnProperty(n))return;t[n]=e[n]}}),t}(e,c||{}),v.setup(),v.detect(),t.addEventListener("scroll",h,!1),m.reflow&&t.addEventListener("resize",g,!1),v}}(r)}.apply(e,[]),void 0===o||(t.exports=o)}},e={};function n(o){var r=e[o];if(void 0!==r)return r.exports;var c=e[o]={exports:{}};return t[o].call(c.exports,c,c.exports,n),c.exports}n.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return n.d(e,{a:e}),e},n.d=(t,e)=>{for(var o in e)n.o(e,o)&&!n.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:e[o]})},n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(t){if("object"==typeof window)return window}}(),n.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),(()=>{"use strict";var t=n(856),e=n.n(t),o=null,r=null,c=document.documentElement.scrollTop;function s(){const t=localStorage.getItem("theme")||"auto";var e;"light"!==(e=window.matchMedia("(prefers-color-scheme: dark)").matches?"auto"===t?"light":"light"==t?"dark":"auto":"auto"===t?"dark":"dark"==t?"light":"auto")&&"dark"!==e&&"auto"!==e&&(console.error(`Got invalid theme mode: ${e}. Resetting to auto.`),e="auto"),document.body.dataset.theme=e,localStorage.setItem("theme",e),console.log(`Changed to ${e} mode.`)}function l(){!function(){const t=document.getElementsByClassName("theme-toggle");Array.from(t).forEach(t=>{t.addEventListener("click",s)})}(),function(){let t=0,e=!1;window.addEventListener("scroll",function(n){t=window.scrollY,e||(window.requestAnimationFrame(function(){var n;(function(t){t>0?r.classList.add("scrolled"):r.classList.remove("scrolled")})(n=t),function(t){t<64?document.documentElement.classList.remove("show-back-to-top"):tc&&document.documentElement.classList.remove("show-back-to-top"),c=t}(n),function(t){null!==o&&(0==t?o.scrollTo(0,0):Math.ceil(t)>=Math.floor(document.documentElement.scrollHeight-window.innerHeight)?o.scrollTo(0,o.scrollHeight):document.querySelector(".scroll-current"))}(n),e=!1}),e=!0)}),window.scroll()}(),null!==o&&new(e())(".toc-tree a",{reflow:!0,recursive:!0,navClass:"scroll-current",offset:()=>{let t=parseFloat(getComputedStyle(document.documentElement).fontSize);const e=r.getBoundingClientRect();return e.top+e.height+2.5*t+1}})}document.addEventListener("DOMContentLoaded",function(){document.body.parentNode.classList.remove("no-js"),r=document.querySelector("header"),o=document.querySelector(".toc-scroll"),l()})})()})(); +//# sourceMappingURL=furo.js.map \ No newline at end of file diff --git a/SRV/docs/_build/html/_static/scripts/furo.js.LICENSE.txt b/SRV/docs/_build/html/_static/scripts/furo.js.LICENSE.txt new file mode 100644 index 0000000..1632189 --- /dev/null +++ b/SRV/docs/_build/html/_static/scripts/furo.js.LICENSE.txt @@ -0,0 +1,7 @@ +/*! + * gumshoejs v5.1.2 (patched by @pradyunsg) + * A simple, framework-agnostic scrollspy script. + * (c) 2019 Chris Ferdinandi + * MIT License + * http://github.com/cferdinandi/gumshoe + */ diff --git a/SRV/docs/_build/html/_static/scripts/furo.js.map b/SRV/docs/_build/html/_static/scripts/furo.js.map new file mode 100644 index 0000000..3b316f3 --- /dev/null +++ b/SRV/docs/_build/html/_static/scripts/furo.js.map @@ -0,0 +1 @@ +{"version":3,"file":"scripts/furo.js","mappings":"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","sources":["webpack:///./src/furo/assets/scripts/gumshoe-patched.js","webpack:///webpack/bootstrap","webpack:///webpack/runtime/compat get default export","webpack:///webpack/runtime/define property getters","webpack:///webpack/runtime/global","webpack:///webpack/runtime/hasOwnProperty shorthand","webpack:///./src/furo/assets/scripts/furo.js"],"sourcesContent":["/*!\n * gumshoejs v5.1.2 (patched by @pradyunsg)\n * A simple, framework-agnostic scrollspy script.\n * (c) 2019 Chris Ferdinandi\n * MIT License\n * http://github.com/cferdinandi/gumshoe\n */\n\n(function (root, factory) {\n if (typeof define === \"function\" && define.amd) {\n define([], function () {\n return factory(root);\n });\n } else if (typeof exports === \"object\") {\n module.exports = factory(root);\n } else {\n root.Gumshoe = factory(root);\n }\n})(\n typeof global !== \"undefined\"\n ? global\n : typeof window !== \"undefined\"\n ? window\n : this,\n function (window) {\n \"use strict\";\n\n //\n // Defaults\n //\n\n var defaults = {\n // Active classes\n navClass: \"active\",\n contentClass: \"active\",\n\n // Nested navigation\n nested: false,\n nestedClass: \"active\",\n\n // Offset & reflow\n offset: 0,\n reflow: false,\n\n // Event support\n events: true,\n };\n\n //\n // Methods\n //\n\n /**\n * Merge two or more objects together.\n * @param {Object} objects The objects to merge together\n * @returns {Object} Merged values of defaults and options\n */\n var extend = function () {\n var merged = {};\n Array.prototype.forEach.call(arguments, function (obj) {\n for (var key in obj) {\n if (!obj.hasOwnProperty(key)) return;\n merged[key] = obj[key];\n }\n });\n return merged;\n };\n\n /**\n * Emit a custom event\n * @param {String} type The event type\n * @param {Node} elem The element to attach the event to\n * @param {Object} detail Any details to pass along with the event\n */\n var emitEvent = function (type, elem, detail) {\n // Make sure events are enabled\n if (!detail.settings.events) return;\n\n // Create a new event\n var event = new CustomEvent(type, {\n bubbles: true,\n cancelable: true,\n detail: detail,\n });\n\n // Dispatch the event\n elem.dispatchEvent(event);\n };\n\n /**\n * Get an element's distance from the top of the Document.\n * @param {Node} elem The element\n * @return {Number} Distance from the top in pixels\n */\n var getOffsetTop = function (elem) {\n var location = 0;\n if (elem.offsetParent) {\n while (elem) {\n location += elem.offsetTop;\n elem = elem.offsetParent;\n }\n }\n return location >= 0 ? location : 0;\n };\n\n /**\n * Sort content from first to last in the DOM\n * @param {Array} contents The content areas\n */\n var sortContents = function (contents) {\n if (contents) {\n contents.sort(function (item1, item2) {\n var offset1 = getOffsetTop(item1.content);\n var offset2 = getOffsetTop(item2.content);\n if (offset1 < offset2) return -1;\n return 1;\n });\n }\n };\n\n /**\n * Get the offset to use for calculating position\n * @param {Object} settings The settings for this instantiation\n * @return {Float} The number of pixels to offset the calculations\n */\n var getOffset = function (settings) {\n // if the offset is a function run it\n if (typeof settings.offset === \"function\") {\n return parseFloat(settings.offset());\n }\n\n // Otherwise, return it as-is\n return parseFloat(settings.offset);\n };\n\n /**\n * Get the document element's height\n * @private\n * @returns {Number}\n */\n var getDocumentHeight = function () {\n return Math.max(\n document.body.scrollHeight,\n document.documentElement.scrollHeight,\n document.body.offsetHeight,\n document.documentElement.offsetHeight,\n document.body.clientHeight,\n document.documentElement.clientHeight,\n );\n };\n\n /**\n * Determine if an element is in view\n * @param {Node} elem The element\n * @param {Object} settings The settings for this instantiation\n * @param {Boolean} bottom If true, check if element is above bottom of viewport instead\n * @return {Boolean} Returns true if element is in the viewport\n */\n var isInView = function (elem, settings, bottom) {\n var bounds = elem.getBoundingClientRect();\n var offset = getOffset(settings);\n if (bottom) {\n return (\n parseInt(bounds.bottom, 10) <\n (window.innerHeight || document.documentElement.clientHeight)\n );\n }\n return parseInt(bounds.top, 10) <= offset;\n };\n\n /**\n * Check if at the bottom of the viewport\n * @return {Boolean} If true, page is at the bottom of the viewport\n */\n var isAtBottom = function () {\n if (\n Math.ceil(window.innerHeight + window.pageYOffset) >=\n getDocumentHeight()\n )\n return true;\n return false;\n };\n\n /**\n * Check if the last item should be used (even if not at the top of the page)\n * @param {Object} item The last item\n * @param {Object} settings The settings for this instantiation\n * @return {Boolean} If true, use the last item\n */\n var useLastItem = function (item, settings) {\n if (isAtBottom() && isInView(item.content, settings, true)) return true;\n return false;\n };\n\n /**\n * Get the active content\n * @param {Array} contents The content areas\n * @param {Object} settings The settings for this instantiation\n * @return {Object} The content area and matching navigation link\n */\n var getActive = function (contents, settings) {\n var last = contents[contents.length - 1];\n if (useLastItem(last, settings)) return last;\n for (var i = contents.length - 1; i >= 0; i--) {\n if (isInView(contents[i].content, settings)) return contents[i];\n }\n };\n\n /**\n * Deactivate parent navs in a nested navigation\n * @param {Node} nav The starting navigation element\n * @param {Object} settings The settings for this instantiation\n */\n var deactivateNested = function (nav, settings) {\n // If nesting isn't activated, bail\n if (!settings.nested || !nav.parentNode) return;\n\n // Get the parent navigation\n var li = nav.parentNode.closest(\"li\");\n if (!li) return;\n\n // Remove the active class\n li.classList.remove(settings.nestedClass);\n\n // Apply recursively to any parent navigation elements\n deactivateNested(li, settings);\n };\n\n /**\n * Deactivate a nav and content area\n * @param {Object} items The nav item and content to deactivate\n * @param {Object} settings The settings for this instantiation\n */\n var deactivate = function (items, settings) {\n // Make sure there are items to deactivate\n if (!items) return;\n\n // Get the parent list item\n var li = items.nav.closest(\"li\");\n if (!li) return;\n\n // Remove the active class from the nav and content\n li.classList.remove(settings.navClass);\n items.content.classList.remove(settings.contentClass);\n\n // Deactivate any parent navs in a nested navigation\n deactivateNested(li, settings);\n\n // Emit a custom event\n emitEvent(\"gumshoeDeactivate\", li, {\n link: items.nav,\n content: items.content,\n settings: settings,\n });\n };\n\n /**\n * Activate parent navs in a nested navigation\n * @param {Node} nav The starting navigation element\n * @param {Object} settings The settings for this instantiation\n */\n var activateNested = function (nav, settings) {\n // If nesting isn't activated, bail\n if (!settings.nested) return;\n\n // Get the parent navigation\n var li = nav.parentNode.closest(\"li\");\n if (!li) return;\n\n // Add the active class\n li.classList.add(settings.nestedClass);\n\n // Apply recursively to any parent navigation elements\n activateNested(li, settings);\n };\n\n /**\n * Activate a nav and content area\n * @param {Object} items The nav item and content to activate\n * @param {Object} settings The settings for this instantiation\n */\n var activate = function (items, settings) {\n // Make sure there are items to activate\n if (!items) return;\n\n // Get the parent list item\n var li = items.nav.closest(\"li\");\n if (!li) return;\n\n // Add the active class to the nav and content\n li.classList.add(settings.navClass);\n items.content.classList.add(settings.contentClass);\n\n // Activate any parent navs in a nested navigation\n activateNested(li, settings);\n\n // Emit a custom event\n emitEvent(\"gumshoeActivate\", li, {\n link: items.nav,\n content: items.content,\n settings: settings,\n });\n };\n\n /**\n * Create the Constructor object\n * @param {String} selector The selector to use for navigation items\n * @param {Object} options User options and settings\n */\n var Constructor = function (selector, options) {\n //\n // Variables\n //\n\n var publicAPIs = {};\n var navItems, contents, current, timeout, settings;\n\n //\n // Methods\n //\n\n /**\n * Set variables from DOM elements\n */\n publicAPIs.setup = function () {\n // Get all nav items\n navItems = document.querySelectorAll(selector);\n\n // Create contents array\n contents = [];\n\n // Loop through each item, get it's matching content, and push to the array\n Array.prototype.forEach.call(navItems, function (item) {\n // Get the content for the nav item\n var content = document.getElementById(\n decodeURIComponent(item.hash.substr(1)),\n );\n if (!content) return;\n\n // Push to the contents array\n contents.push({\n nav: item,\n content: content,\n });\n });\n\n // Sort contents by the order they appear in the DOM\n sortContents(contents);\n };\n\n /**\n * Detect which content is currently active\n */\n publicAPIs.detect = function () {\n // Get the active content\n var active = getActive(contents, settings);\n\n // if there's no active content, deactivate and bail\n if (!active) {\n if (current) {\n deactivate(current, settings);\n current = null;\n }\n return;\n }\n\n // If the active content is the one currently active, do nothing\n if (current && active.content === current.content) return;\n\n // Deactivate the current content and activate the new content\n deactivate(current, settings);\n activate(active, settings);\n\n // Update the currently active content\n current = active;\n };\n\n /**\n * Detect the active content on scroll\n * Debounced for performance\n */\n var scrollHandler = function (event) {\n // If there's a timer, cancel it\n if (timeout) {\n window.cancelAnimationFrame(timeout);\n }\n\n // Setup debounce callback\n timeout = window.requestAnimationFrame(publicAPIs.detect);\n };\n\n /**\n * Update content sorting on resize\n * Debounced for performance\n */\n var resizeHandler = function (event) {\n // If there's a timer, cancel it\n if (timeout) {\n window.cancelAnimationFrame(timeout);\n }\n\n // Setup debounce callback\n timeout = window.requestAnimationFrame(function () {\n sortContents(contents);\n publicAPIs.detect();\n });\n };\n\n /**\n * Destroy the current instantiation\n */\n publicAPIs.destroy = function () {\n // Undo DOM changes\n if (current) {\n deactivate(current, settings);\n }\n\n // Remove event listeners\n window.removeEventListener(\"scroll\", scrollHandler, false);\n if (settings.reflow) {\n window.removeEventListener(\"resize\", resizeHandler, false);\n }\n\n // Reset variables\n contents = null;\n navItems = null;\n current = null;\n timeout = null;\n settings = null;\n };\n\n /**\n * Initialize the current instantiation\n */\n var init = function () {\n // Merge user options into defaults\n settings = extend(defaults, options || {});\n\n // Setup variables based on the current DOM\n publicAPIs.setup();\n\n // Find the currently active content\n publicAPIs.detect();\n\n // Setup event listeners\n window.addEventListener(\"scroll\", scrollHandler, false);\n if (settings.reflow) {\n window.addEventListener(\"resize\", resizeHandler, false);\n }\n };\n\n //\n // Initialize and return the public APIs\n //\n\n init();\n return publicAPIs;\n };\n\n //\n // Return the Constructor\n //\n\n return Constructor;\n },\n);\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.g = (function() {\n\tif (typeof globalThis === 'object') return globalThis;\n\ttry {\n\t\treturn this || new Function('return this')();\n\t} catch (e) {\n\t\tif (typeof window === 'object') return window;\n\t}\n})();","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","import Gumshoe from \"./gumshoe-patched.js\";\n\n////////////////////////////////////////////////////////////////////////////////\n// Scroll Handling\n////////////////////////////////////////////////////////////////////////////////\nvar tocScroll = null;\nvar header = null;\nvar lastScrollTop = document.documentElement.scrollTop;\nconst GO_TO_TOP_OFFSET = 64;\n\nfunction scrollHandlerForHeader(positionY) {\n if (positionY > 0) {\n header.classList.add(\"scrolled\");\n } else {\n header.classList.remove(\"scrolled\");\n }\n}\n\nfunction scrollHandlerForBackToTop(positionY) {\n if (positionY < GO_TO_TOP_OFFSET) {\n document.documentElement.classList.remove(\"show-back-to-top\");\n } else {\n if (positionY < lastScrollTop) {\n document.documentElement.classList.add(\"show-back-to-top\");\n } else if (positionY > lastScrollTop) {\n document.documentElement.classList.remove(\"show-back-to-top\");\n }\n }\n lastScrollTop = positionY;\n}\n\nfunction scrollHandlerForTOC(positionY) {\n if (tocScroll === null) {\n return;\n }\n\n // top of page.\n if (positionY == 0) {\n tocScroll.scrollTo(0, 0);\n } else if (\n // bottom of page.\n Math.ceil(positionY) >=\n Math.floor(document.documentElement.scrollHeight - window.innerHeight)\n ) {\n tocScroll.scrollTo(0, tocScroll.scrollHeight);\n } else {\n // somewhere in the middle.\n const current = document.querySelector(\".scroll-current\");\n if (current == null) {\n return;\n }\n\n // https://github.com/pypa/pip/issues/9159 This breaks scroll behaviours.\n // // scroll the currently \"active\" heading in toc, into view.\n // const rect = current.getBoundingClientRect();\n // if (0 > rect.top) {\n // current.scrollIntoView(true); // the argument is \"alignTop\"\n // } else if (rect.bottom > window.innerHeight) {\n // current.scrollIntoView(false);\n // }\n }\n}\n\nfunction scrollHandler(positionY) {\n scrollHandlerForHeader(positionY);\n scrollHandlerForBackToTop(positionY);\n scrollHandlerForTOC(positionY);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Theme Toggle\n////////////////////////////////////////////////////////////////////////////////\nfunction setTheme(mode) {\n if (mode !== \"light\" && mode !== \"dark\" && mode !== \"auto\") {\n console.error(`Got invalid theme mode: ${mode}. Resetting to auto.`);\n mode = \"auto\";\n }\n\n document.body.dataset.theme = mode;\n localStorage.setItem(\"theme\", mode);\n console.log(`Changed to ${mode} mode.`);\n}\n\nfunction cycleThemeOnce() {\n const currentTheme = localStorage.getItem(\"theme\") || \"auto\";\n const prefersDark = window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n\n if (prefersDark) {\n // Auto (dark) -> Light -> Dark\n if (currentTheme === \"auto\") {\n setTheme(\"light\");\n } else if (currentTheme == \"light\") {\n setTheme(\"dark\");\n } else {\n setTheme(\"auto\");\n }\n } else {\n // Auto (light) -> Dark -> Light\n if (currentTheme === \"auto\") {\n setTheme(\"dark\");\n } else if (currentTheme == \"dark\") {\n setTheme(\"light\");\n } else {\n setTheme(\"auto\");\n }\n }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Setup\n////////////////////////////////////////////////////////////////////////////////\nfunction setupScrollHandler() {\n // Taken from https://developer.mozilla.org/en-US/docs/Web/API/Document/scroll_event\n let last_known_scroll_position = 0;\n let ticking = false;\n\n window.addEventListener(\"scroll\", function (e) {\n last_known_scroll_position = window.scrollY;\n\n if (!ticking) {\n window.requestAnimationFrame(function () {\n scrollHandler(last_known_scroll_position);\n ticking = false;\n });\n\n ticking = true;\n }\n });\n window.scroll();\n}\n\nfunction setupScrollSpy() {\n if (tocScroll === null) {\n return;\n }\n\n // Scrollspy -- highlight table on contents, based on scroll\n new Gumshoe(\".toc-tree a\", {\n reflow: true,\n recursive: true,\n navClass: \"scroll-current\",\n offset: () => {\n let rem = parseFloat(getComputedStyle(document.documentElement).fontSize);\n const headerRect = header.getBoundingClientRect();\n return headerRect.top + headerRect.height + 2.5 * rem + 1;\n },\n });\n}\n\nfunction setupTheme() {\n // Attach event handlers for toggling themes\n const buttons = document.getElementsByClassName(\"theme-toggle\");\n Array.from(buttons).forEach((btn) => {\n btn.addEventListener(\"click\", cycleThemeOnce);\n });\n}\n\nfunction setup() {\n setupTheme();\n setupScrollHandler();\n setupScrollSpy();\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Main entrypoint\n////////////////////////////////////////////////////////////////////////////////\nfunction main() {\n document.body.parentNode.classList.remove(\"no-js\");\n\n header = document.querySelector(\"header\");\n tocScroll = document.querySelector(\".toc-scroll\");\n\n setup();\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", main);\n"],"names":["root","g","window","this","defaults","navClass","contentClass","nested","nestedClass","offset","reflow","events","emitEvent","type","elem","detail","settings","event","CustomEvent","bubbles","cancelable","dispatchEvent","getOffsetTop","location","offsetParent","offsetTop","sortContents","contents","sort","item1","item2","content","isInView","bottom","bounds","getBoundingClientRect","parseFloat","getOffset","parseInt","innerHeight","document","documentElement","clientHeight","top","isAtBottom","Math","ceil","pageYOffset","max","body","scrollHeight","offsetHeight","getActive","last","length","item","useLastItem","i","deactivateNested","nav","parentNode","li","closest","classList","remove","deactivate","items","link","activateNested","add","selector","options","navItems","current","timeout","publicAPIs","querySelectorAll","Array","prototype","forEach","call","getElementById","decodeURIComponent","hash","substr","push","active","activate","scrollHandler","cancelAnimationFrame","requestAnimationFrame","detect","resizeHandler","destroy","removeEventListener","merged","arguments","obj","key","hasOwnProperty","extend","setup","addEventListener","factory","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","undefined","exports","module","__webpack_modules__","n","getter","__esModule","d","a","definition","o","Object","defineProperty","enumerable","get","globalThis","Function","e","prop","tocScroll","header","lastScrollTop","scrollTop","cycleThemeOnce","currentTheme","localStorage","getItem","mode","matchMedia","matches","console","error","dataset","theme","setItem","log","buttons","getElementsByClassName","from","btn","setupTheme","last_known_scroll_position","ticking","scrollY","positionY","scrollHandlerForHeader","scrollHandlerForBackToTop","scrollTo","floor","querySelector","scrollHandlerForTOC","scroll","setupScrollHandler","recursive","rem","getComputedStyle","fontSize","headerRect","height"],"sourceRoot":""} \ No newline at end of file diff --git a/SRV/docs/_build/html/_static/searchtools.js b/SRV/docs/_build/html/_static/searchtools.js new file mode 100644 index 0000000..91f4be5 --- /dev/null +++ b/SRV/docs/_build/html/_static/searchtools.js @@ -0,0 +1,635 @@ +/* + * Sphinx JavaScript utilities for the full-text search. + */ +"use strict"; + +/** + * Simple result scoring code. + */ +if (typeof Scorer === "undefined") { + var Scorer = { + // Implement the following function to further tweak the score for each result + // The function takes a result array [docname, title, anchor, descr, score, filename] + // and returns the new score. + /* + score: result => { + const [docname, title, anchor, descr, score, filename, kind] = result + return score + }, + */ + + // query matches the full name of an object + objNameMatch: 11, + // or matches in the last dotted part of the object name + objPartialMatch: 6, + // Additive scores depending on the priority of the object + objPrio: { + 0: 15, // used to be importantResults + 1: 5, // used to be objectResults + 2: -5, // used to be unimportantResults + }, + // Used when the priority is not in the mapping. + objPrioDefault: 0, + + // query found in title + title: 15, + partialTitle: 7, + // query found in terms + term: 5, + partialTerm: 2, + }; +} + +// Global search result kind enum, used by themes to style search results. +class SearchResultKind { + static get index() { return "index"; } + static get object() { return "object"; } + static get text() { return "text"; } + static get title() { return "title"; } +} + +const _removeChildren = (element) => { + while (element && element.lastChild) element.removeChild(element.lastChild); +}; + +/** + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping + */ +const _escapeRegExp = (string) => + string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string + +const _displayItem = (item, searchTerms, highlightTerms) => { + const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; + const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; + const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; + const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; + const contentRoot = document.documentElement.dataset.content_root; + + const [docName, title, anchor, descr, score, _filename, kind] = item; + + let listItem = document.createElement("li"); + // Add a class representing the item's type: + // can be used by a theme's CSS selector for styling + // See SearchResultKind for the class names. + listItem.classList.add(`kind-${kind}`); + let requestUrl; + let linkUrl; + if (docBuilder === "dirhtml") { + // dirhtml builder + let dirname = docName + "/"; + if (dirname.match(/\/index\/$/)) + dirname = dirname.substring(0, dirname.length - 6); + else if (dirname === "index/") dirname = ""; + requestUrl = contentRoot + dirname; + linkUrl = requestUrl; + } else { + // normal html builders + requestUrl = contentRoot + docName + docFileSuffix; + linkUrl = docName + docLinkSuffix; + } + let linkEl = listItem.appendChild(document.createElement("a")); + linkEl.href = linkUrl + anchor; + linkEl.dataset.score = score; + linkEl.innerHTML = title; + if (descr) { + listItem.appendChild(document.createElement("span")).innerHTML = + " (" + descr + ")"; + // highlight search terms in the description + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + } + else if (showSearchSummary) + fetch(requestUrl) + .then((responseData) => responseData.text()) + .then((data) => { + if (data) + listItem.appendChild( + Search.makeSearchSummary(data, searchTerms, anchor) + ); + // highlight search terms in the summary + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + }); + Search.output.appendChild(listItem); +}; +const _finishSearch = (resultCount) => { + Search.stopPulse(); + Search.title.innerText = _("Search Results"); + if (!resultCount) + Search.status.innerText = Documentation.gettext( + "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." + ); + else + Search.status.innerText = Documentation.ngettext( + "Search finished, found one page matching the search query.", + "Search finished, found ${resultCount} pages matching the search query.", + resultCount, + ).replace('${resultCount}', resultCount); +}; +const _displayNextItem = ( + results, + resultCount, + searchTerms, + highlightTerms, +) => { + // results left, load the summary and display it + // this is intended to be dynamic (don't sub resultsCount) + if (results.length) { + _displayItem(results.pop(), searchTerms, highlightTerms); + setTimeout( + () => _displayNextItem(results, resultCount, searchTerms, highlightTerms), + 5 + ); + } + // search finished, update title and status message + else _finishSearch(resultCount); +}; +// Helper function used by query() to order search results. +// Each input is an array of [docname, title, anchor, descr, score, filename, kind]. +// Order the results by score (in opposite order of appearance, since the +// `_displayNextItem` function uses pop() to retrieve items) and then alphabetically. +const _orderResultsByScoreThenName = (a, b) => { + const leftScore = a[4]; + const rightScore = b[4]; + if (leftScore === rightScore) { + // same score: sort alphabetically + const leftTitle = a[1].toLowerCase(); + const rightTitle = b[1].toLowerCase(); + if (leftTitle === rightTitle) return 0; + return leftTitle > rightTitle ? -1 : 1; // inverted is intentional + } + return leftScore > rightScore ? 1 : -1; +}; + +/** + * Default splitQuery function. Can be overridden in ``sphinx.search`` with a + * custom function per language. + * + * The regular expression works by splitting the string on consecutive characters + * that are not Unicode letters, numbers, underscores, or emoji characters. + * This is the same as ``\W+`` in Python, preserving the surrogate pair area. + */ +if (typeof splitQuery === "undefined") { + var splitQuery = (query) => query + .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) + .filter(term => term) // remove remaining empty strings +} + +/** + * Search Module + */ +const Search = { + _index: null, + _queued_query: null, + _pulse_status: -1, + + htmlToText: (htmlString, anchor) => { + const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html'); + for (const removalQuery of [".headerlink", "script", "style"]) { + htmlElement.querySelectorAll(removalQuery).forEach((el) => { el.remove() }); + } + if (anchor) { + const anchorContent = htmlElement.querySelector(`[role="main"] ${anchor}`); + if (anchorContent) return anchorContent.textContent; + + console.warn( + `Anchored content block not found. Sphinx search tries to obtain it via DOM query '[role=main] ${anchor}'. Check your theme or template.` + ); + } + + // if anchor not specified or not found, fall back to main content + const docContent = htmlElement.querySelector('[role="main"]'); + if (docContent) return docContent.textContent; + + console.warn( + "Content block not found. Sphinx search tries to obtain it via DOM query '[role=main]'. Check your theme or template." + ); + return ""; + }, + + init: () => { + const query = new URLSearchParams(window.location.search).get("q"); + document + .querySelectorAll('input[name="q"]') + .forEach((el) => (el.value = query)); + if (query) Search.performSearch(query); + }, + + loadIndex: (url) => + (document.body.appendChild(document.createElement("script")).src = url), + + setIndex: (index) => { + Search._index = index; + if (Search._queued_query !== null) { + const query = Search._queued_query; + Search._queued_query = null; + Search.query(query); + } + }, + + hasIndex: () => Search._index !== null, + + deferQuery: (query) => (Search._queued_query = query), + + stopPulse: () => (Search._pulse_status = -1), + + startPulse: () => { + if (Search._pulse_status >= 0) return; + + const pulse = () => { + Search._pulse_status = (Search._pulse_status + 1) % 4; + Search.dots.innerText = ".".repeat(Search._pulse_status); + if (Search._pulse_status >= 0) window.setTimeout(pulse, 500); + }; + pulse(); + }, + + /** + * perform a search for something (or wait until index is loaded) + */ + performSearch: (query) => { + // create the required interface elements + const searchText = document.createElement("h2"); + searchText.textContent = _("Searching"); + const searchSummary = document.createElement("p"); + searchSummary.classList.add("search-summary"); + searchSummary.innerText = ""; + const searchList = document.createElement("ul"); + searchList.setAttribute("role", "list"); + searchList.classList.add("search"); + + const out = document.getElementById("search-results"); + Search.title = out.appendChild(searchText); + Search.dots = Search.title.appendChild(document.createElement("span")); + Search.status = out.appendChild(searchSummary); + Search.output = out.appendChild(searchList); + + const searchProgress = document.getElementById("search-progress"); + // Some themes don't use the search progress node + if (searchProgress) { + searchProgress.innerText = _("Preparing search..."); + } + Search.startPulse(); + + // index already loaded, the browser was quick! + if (Search.hasIndex()) Search.query(query); + else Search.deferQuery(query); + }, + + _parseQuery: (query) => { + // stem the search terms and add them to the correct list + const stemmer = new Stemmer(); + const searchTerms = new Set(); + const excludedTerms = new Set(); + const highlightTerms = new Set(); + const objectTerms = new Set(splitQuery(query.toLowerCase().trim())); + splitQuery(query.trim()).forEach((queryTerm) => { + const queryTermLower = queryTerm.toLowerCase(); + + // maybe skip this "word" + // stopwords array is from language_data.js + if ( + stopwords.indexOf(queryTermLower) !== -1 || + queryTerm.match(/^\d+$/) + ) + return; + + // stem the word + let word = stemmer.stemWord(queryTermLower); + // select the correct list + if (word[0] === "-") excludedTerms.add(word.substr(1)); + else { + searchTerms.add(word); + highlightTerms.add(queryTermLower); + } + }); + + if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js + localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" ")) + } + + // console.debug("SEARCH: searching for:"); + // console.info("required: ", [...searchTerms]); + // console.info("excluded: ", [...excludedTerms]); + + return [query, searchTerms, excludedTerms, highlightTerms, objectTerms]; + }, + + /** + * execute search (requires search index to be loaded) + */ + _performSearch: (query, searchTerms, excludedTerms, highlightTerms, objectTerms) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + const allTitles = Search._index.alltitles; + const indexEntries = Search._index.indexentries; + + // Collect multiple result groups to be sorted separately and then ordered. + // Each is an array of [docname, title, anchor, descr, score, filename, kind]. + const normalResults = []; + const nonMainIndexResults = []; + + _removeChildren(document.getElementById("search-progress")); + + const queryLower = query.toLowerCase().trim(); + for (const [title, foundTitles] of Object.entries(allTitles)) { + if (title.toLowerCase().trim().includes(queryLower) && (queryLower.length >= title.length/2)) { + for (const [file, id] of foundTitles) { + const score = Math.round(Scorer.title * queryLower.length / title.length); + const boost = titles[file] === title ? 1 : 0; // add a boost for document titles + normalResults.push([ + docNames[file], + titles[file] !== title ? `${titles[file]} > ${title}` : title, + id !== null ? "#" + id : "", + null, + score + boost, + filenames[file], + SearchResultKind.title, + ]); + } + } + } + + // search for explicit entries in index directives + for (const [entry, foundEntries] of Object.entries(indexEntries)) { + if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) { + for (const [file, id, isMain] of foundEntries) { + const score = Math.round(100 * queryLower.length / entry.length); + const result = [ + docNames[file], + titles[file], + id ? "#" + id : "", + null, + score, + filenames[file], + SearchResultKind.index, + ]; + if (isMain) { + normalResults.push(result); + } else { + nonMainIndexResults.push(result); + } + } + } + } + + // lookup as object + objectTerms.forEach((term) => + normalResults.push(...Search.performObjectSearch(term, objectTerms)) + ); + + // lookup as search terms in fulltext + normalResults.push(...Search.performTermsSearch(searchTerms, excludedTerms)); + + // let the scorer override scores with a custom scoring function + if (Scorer.score) { + normalResults.forEach((item) => (item[4] = Scorer.score(item))); + nonMainIndexResults.forEach((item) => (item[4] = Scorer.score(item))); + } + + // Sort each group of results by score and then alphabetically by name. + normalResults.sort(_orderResultsByScoreThenName); + nonMainIndexResults.sort(_orderResultsByScoreThenName); + + // Combine the result groups in (reverse) order. + // Non-main index entries are typically arbitrary cross-references, + // so display them after other results. + let results = [...nonMainIndexResults, ...normalResults]; + + // remove duplicate search results + // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept + let seen = new Set(); + results = results.reverse().reduce((acc, result) => { + let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(','); + if (!seen.has(resultStr)) { + acc.push(result); + seen.add(resultStr); + } + return acc; + }, []); + + return results.reverse(); + }, + + query: (query) => { + const [searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms] = Search._parseQuery(query); + const results = Search._performSearch(searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms); + + // for debugging + //Search.lastresults = results.slice(); // a copy + // console.info("search results:", Search.lastresults); + + // print the results + _displayNextItem(results, results.length, searchTerms, highlightTerms); + }, + + /** + * search for object names + */ + performObjectSearch: (object, objectTerms) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const objects = Search._index.objects; + const objNames = Search._index.objnames; + const titles = Search._index.titles; + + const results = []; + + const objectSearchCallback = (prefix, match) => { + const name = match[4] + const fullname = (prefix ? prefix + "." : "") + name; + const fullnameLower = fullname.toLowerCase(); + if (fullnameLower.indexOf(object) < 0) return; + + let score = 0; + const parts = fullnameLower.split("."); + + // check for different match types: exact matches of full name or + // "last name" (i.e. last dotted part) + if (fullnameLower === object || parts.slice(-1)[0] === object) + score += Scorer.objNameMatch; + else if (parts.slice(-1)[0].indexOf(object) > -1) + score += Scorer.objPartialMatch; // matches in last name + + const objName = objNames[match[1]][2]; + const title = titles[match[0]]; + + // If more than one term searched for, we require other words to be + // found in the name/title/description + const otherTerms = new Set(objectTerms); + otherTerms.delete(object); + if (otherTerms.size > 0) { + const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase(); + if ( + [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0) + ) + return; + } + + let anchor = match[3]; + if (anchor === "") anchor = fullname; + else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; + + const descr = objName + _(", in ") + title; + + // add custom score for some objects according to scorer + if (Scorer.objPrio.hasOwnProperty(match[2])) + score += Scorer.objPrio[match[2]]; + else score += Scorer.objPrioDefault; + + results.push([ + docNames[match[0]], + fullname, + "#" + anchor, + descr, + score, + filenames[match[0]], + SearchResultKind.object, + ]); + }; + Object.keys(objects).forEach((prefix) => + objects[prefix].forEach((array) => + objectSearchCallback(prefix, array) + ) + ); + return results; + }, + + /** + * search for full-text terms in the index + */ + performTermsSearch: (searchTerms, excludedTerms) => { + // prepare search + const terms = Search._index.terms; + const titleTerms = Search._index.titleterms; + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + + const scoreMap = new Map(); + const fileMap = new Map(); + + // perform the search on the required terms + searchTerms.forEach((word) => { + const files = []; + // find documents, if any, containing the query word in their text/title term indices + // use Object.hasOwnProperty to avoid mismatching against prototype properties + const arr = [ + { files: terms.hasOwnProperty(word) ? terms[word] : undefined, score: Scorer.term }, + { files: titleTerms.hasOwnProperty(word) ? titleTerms[word] : undefined, score: Scorer.title }, + ]; + // add support for partial matches + if (word.length > 2) { + const escapedWord = _escapeRegExp(word); + if (!terms.hasOwnProperty(word)) { + Object.keys(terms).forEach((term) => { + if (term.match(escapedWord)) + arr.push({ files: terms[term], score: Scorer.partialTerm }); + }); + } + if (!titleTerms.hasOwnProperty(word)) { + Object.keys(titleTerms).forEach((term) => { + if (term.match(escapedWord)) + arr.push({ files: titleTerms[term], score: Scorer.partialTitle }); + }); + } + } + + // no match but word was a required one + if (arr.every((record) => record.files === undefined)) return; + + // found search word in contents + arr.forEach((record) => { + if (record.files === undefined) return; + + let recordFiles = record.files; + if (recordFiles.length === undefined) recordFiles = [recordFiles]; + files.push(...recordFiles); + + // set score for the word in each file + recordFiles.forEach((file) => { + if (!scoreMap.has(file)) scoreMap.set(file, new Map()); + const fileScores = scoreMap.get(file); + fileScores.set(word, record.score); + }); + }); + + // create the mapping + files.forEach((file) => { + if (!fileMap.has(file)) fileMap.set(file, [word]); + else if (fileMap.get(file).indexOf(word) === -1) fileMap.get(file).push(word); + }); + }); + + // now check if the files don't contain excluded terms + const results = []; + for (const [file, wordList] of fileMap) { + // check if all requirements are matched + + // as search terms with length < 3 are discarded + const filteredTermCount = [...searchTerms].filter( + (term) => term.length > 2 + ).length; + if ( + wordList.length !== searchTerms.size && + wordList.length !== filteredTermCount + ) + continue; + + // ensure that none of the excluded terms is in the search result + if ( + [...excludedTerms].some( + (term) => + terms[term] === file || + titleTerms[term] === file || + (terms[term] || []).includes(file) || + (titleTerms[term] || []).includes(file) + ) + ) + break; + + // select one (max) score for the file. + const score = Math.max(...wordList.map((w) => scoreMap.get(file).get(w))); + // add result to the result list + results.push([ + docNames[file], + titles[file], + "", + null, + score, + filenames[file], + SearchResultKind.text, + ]); + } + return results; + }, + + /** + * helper function to return a node containing the + * search summary for a given text. keywords is a list + * of stemmed words. + */ + makeSearchSummary: (htmlText, keywords, anchor) => { + const text = Search.htmlToText(htmlText, anchor); + if (text === "") return null; + + const textLower = text.toLowerCase(); + const actualStartPosition = [...keywords] + .map((k) => textLower.indexOf(k.toLowerCase())) + .filter((i) => i > -1) + .slice(-1)[0]; + const startWithContext = Math.max(actualStartPosition - 120, 0); + + const top = startWithContext === 0 ? "" : "..."; + const tail = startWithContext + 240 < text.length ? "..." : ""; + + let summary = document.createElement("p"); + summary.classList.add("context"); + summary.textContent = top + text.substr(startWithContext, 240).trim() + tail; + + return summary; + }, +}; + +_ready(Search.init); diff --git a/SRV/docs/_build/html/_static/skeleton.css b/SRV/docs/_build/html/_static/skeleton.css new file mode 100644 index 0000000..467c878 --- /dev/null +++ b/SRV/docs/_build/html/_static/skeleton.css @@ -0,0 +1,296 @@ +/* Some sane resets. */ +html { + height: 100%; +} + +body { + margin: 0; + min-height: 100%; +} + +/* All the flexbox magic! */ +body, +.sb-announcement, +.sb-content, +.sb-main, +.sb-container, +.sb-container__inner, +.sb-article-container, +.sb-footer-content, +.sb-header, +.sb-header-secondary, +.sb-footer { + display: flex; +} + +/* These order things vertically */ +body, +.sb-main, +.sb-article-container { + flex-direction: column; +} + +/* Put elements in the center */ +.sb-header, +.sb-header-secondary, +.sb-container, +.sb-content, +.sb-footer, +.sb-footer-content { + justify-content: center; +} +/* Put elements at the ends */ +.sb-article-container { + justify-content: space-between; +} + +/* These elements grow. */ +.sb-main, +.sb-content, +.sb-container, +article { + flex-grow: 1; +} + +/* Because padding making this wider is not fun */ +article { + box-sizing: border-box; +} + +/* The announcements element should never be wider than the page. */ +.sb-announcement { + max-width: 100%; +} + +.sb-sidebar-primary, +.sb-sidebar-secondary { + flex-shrink: 0; + width: 17rem; +} + +.sb-announcement__inner { + justify-content: center; + + box-sizing: border-box; + height: 3rem; + + overflow-x: auto; + white-space: nowrap; +} + +/* Sidebars, with checkbox-based toggle */ +.sb-sidebar-primary, +.sb-sidebar-secondary { + position: fixed; + height: 100%; + top: 0; +} + +.sb-sidebar-primary { + left: -17rem; + transition: left 250ms ease-in-out; +} +.sb-sidebar-secondary { + right: -17rem; + transition: right 250ms ease-in-out; +} + +.sb-sidebar-toggle { + display: none; +} +.sb-sidebar-overlay { + position: fixed; + top: 0; + width: 0; + height: 0; + + transition: width 0ms ease 250ms, height 0ms ease 250ms, opacity 250ms ease; + + opacity: 0; + background-color: rgba(0, 0, 0, 0.54); +} + +#sb-sidebar-toggle--primary:checked + ~ .sb-sidebar-overlay[for="sb-sidebar-toggle--primary"], +#sb-sidebar-toggle--secondary:checked + ~ .sb-sidebar-overlay[for="sb-sidebar-toggle--secondary"] { + width: 100%; + height: 100%; + opacity: 1; + transition: width 0ms ease, height 0ms ease, opacity 250ms ease; +} + +#sb-sidebar-toggle--primary:checked ~ .sb-container .sb-sidebar-primary { + left: 0; +} +#sb-sidebar-toggle--secondary:checked ~ .sb-container .sb-sidebar-secondary { + right: 0; +} + +/* Full-width mode */ +.drop-secondary-sidebar-for-full-width-content + .hide-when-secondary-sidebar-shown { + display: none !important; +} +.drop-secondary-sidebar-for-full-width-content .sb-sidebar-secondary { + display: none !important; +} + +/* Mobile views */ +.sb-page-width { + width: 100%; +} + +.sb-article-container, +.sb-footer-content__inner, +.drop-secondary-sidebar-for-full-width-content .sb-article, +.drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 100vw; +} + +.sb-article, +.match-content-width { + padding: 0 1rem; + box-sizing: border-box; +} + +@media (min-width: 32rem) { + .sb-article, + .match-content-width { + padding: 0 2rem; + } +} + +/* Tablet views */ +@media (min-width: 42rem) { + .sb-article-container { + width: auto; + } + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 42rem; + } + .sb-article, + .match-content-width { + width: 42rem; + } +} +@media (min-width: 46rem) { + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 46rem; + } + .sb-article, + .match-content-width { + width: 46rem; + } +} +@media (min-width: 50rem) { + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 50rem; + } + .sb-article, + .match-content-width { + width: 50rem; + } +} + +/* Tablet views */ +@media (min-width: 59rem) { + .sb-sidebar-secondary { + position: static; + } + .hide-when-secondary-sidebar-shown { + display: none !important; + } + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 59rem; + } + .sb-article, + .match-content-width { + width: 42rem; + } +} +@media (min-width: 63rem) { + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 63rem; + } + .sb-article, + .match-content-width { + width: 46rem; + } +} +@media (min-width: 67rem) { + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 67rem; + } + .sb-article, + .match-content-width { + width: 50rem; + } +} + +/* Desktop views */ +@media (min-width: 76rem) { + .sb-sidebar-primary { + position: static; + } + .hide-when-primary-sidebar-shown { + display: none !important; + } + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 59rem; + } + .sb-article, + .match-content-width { + width: 42rem; + } +} + +/* Full desktop views */ +@media (min-width: 80rem) { + .sb-article, + .match-content-width { + width: 46rem; + } + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 63rem; + } +} + +@media (min-width: 84rem) { + .sb-article, + .match-content-width { + width: 50rem; + } + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 67rem; + } +} + +@media (min-width: 88rem) { + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 67rem; + } + .sb-page-width { + width: 88rem; + } +} diff --git a/SRV/docs/_build/html/_static/sphinx_highlight.js b/SRV/docs/_build/html/_static/sphinx_highlight.js new file mode 100644 index 0000000..8a96c69 --- /dev/null +++ b/SRV/docs/_build/html/_static/sphinx_highlight.js @@ -0,0 +1,154 @@ +/* Highlighting utilities for Sphinx HTML documentation. */ +"use strict"; + +const SPHINX_HIGHLIGHT_ENABLED = true + +/** + * highlight a given string on a node by wrapping it in + * span elements with the given class name. + */ +const _highlight = (node, addItems, text, className) => { + if (node.nodeType === Node.TEXT_NODE) { + const val = node.nodeValue; + const parent = node.parentNode; + const pos = val.toLowerCase().indexOf(text); + if ( + pos >= 0 && + !parent.classList.contains(className) && + !parent.classList.contains("nohighlight") + ) { + let span; + + const closestNode = parent.closest("body, svg, foreignObject"); + const isInSVG = closestNode && closestNode.matches("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.classList.add(className); + } + + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + const rest = document.createTextNode(val.substr(pos + text.length)); + parent.insertBefore( + span, + parent.insertBefore( + rest, + node.nextSibling + ) + ); + node.nodeValue = val.substr(0, pos); + /* There may be more occurrences of search term in this node. So call this + * function recursively on the remaining fragment. + */ + _highlight(rest, addItems, text, className); + + if (isInSVG) { + const rect = document.createElementNS( + "http://www.w3.org/2000/svg", + "rect" + ); + const bbox = parent.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute("class", className); + addItems.push({ parent: parent, target: rect }); + } + } + } else if (node.matches && !node.matches("button, select, textarea")) { + node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); + } +}; +const _highlightText = (thisNode, text, className) => { + let addItems = []; + _highlight(thisNode, addItems, text, className); + addItems.forEach((obj) => + obj.parent.insertAdjacentElement("beforebegin", obj.target) + ); +}; + +/** + * Small JavaScript module for the documentation. + */ +const SphinxHighlight = { + + /** + * highlight the search words provided in localstorage in the text + */ + highlightSearchWords: () => { + if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight + + // get and clear terms from localstorage + const url = new URL(window.location); + const highlight = + localStorage.getItem("sphinx_highlight_terms") + || url.searchParams.get("highlight") + || ""; + localStorage.removeItem("sphinx_highlight_terms") + url.searchParams.delete("highlight"); + window.history.replaceState({}, "", url); + + // get individual terms from highlight string + const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); + if (terms.length === 0) return; // nothing to do + + // There should never be more than one element matching "div.body" + const divBody = document.querySelectorAll("div.body"); + const body = divBody.length ? divBody[0] : document.querySelector("body"); + window.setTimeout(() => { + terms.forEach((term) => _highlightText(body, term, "highlighted")); + }, 10); + + const searchBox = document.getElementById("searchbox"); + if (searchBox === null) return; + searchBox.appendChild( + document + .createRange() + .createContextualFragment( + '" + ) + ); + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords: () => { + document + .querySelectorAll("#searchbox .highlight-link") + .forEach((el) => el.remove()); + document + .querySelectorAll("span.highlighted") + .forEach((el) => el.classList.remove("highlighted")); + localStorage.removeItem("sphinx_highlight_terms") + }, + + initEscapeListener: () => { + // only install a listener if it is really needed + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; + if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { + SphinxHighlight.hideSearchWords(); + event.preventDefault(); + } + }); + }, +}; + +_ready(() => { + /* Do not call highlightSearchWords() when we are on the search page. + * It will highlight words from the *previous* search query. + */ + if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords(); + SphinxHighlight.initEscapeListener(); +}); diff --git a/SRV/docs/_build/html/_static/styles/furo-extensions.css b/SRV/docs/_build/html/_static/styles/furo-extensions.css new file mode 100644 index 0000000..2d74267 --- /dev/null +++ b/SRV/docs/_build/html/_static/styles/furo-extensions.css @@ -0,0 +1,2 @@ +#furo-sidebar-ad-placement{padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)}#furo-sidebar-ad-placement .ethical-sidebar{background:var(--color-background-secondary);border:none;box-shadow:none}#furo-sidebar-ad-placement .ethical-sidebar:hover{background:var(--color-background-hover)}#furo-sidebar-ad-placement .ethical-sidebar a{color:var(--color-foreground-primary)}#furo-sidebar-ad-placement .ethical-callout a{color:var(--color-foreground-secondary)!important}#furo-readthedocs-versions{background:transparent;display:block;position:static;width:100%}#furo-readthedocs-versions .rst-versions{background:#1a1c1e}#furo-readthedocs-versions .rst-current-version{background:var(--color-sidebar-item-background);cursor:unset}#furo-readthedocs-versions .rst-current-version:hover{background:var(--color-sidebar-item-background)}#furo-readthedocs-versions .rst-current-version .fa-book{color:var(--color-foreground-primary)}#furo-readthedocs-versions>.rst-other-versions{padding:0}#furo-readthedocs-versions>.rst-other-versions small{opacity:1}#furo-readthedocs-versions .injected .rst-versions{position:unset}#furo-readthedocs-versions:focus-within,#furo-readthedocs-versions:hover{box-shadow:0 0 0 1px var(--color-sidebar-background-border)}#furo-readthedocs-versions:focus-within .rst-current-version,#furo-readthedocs-versions:hover .rst-current-version{background:#1a1c1e;font-size:inherit;height:auto;line-height:inherit;padding:12px;text-align:right}#furo-readthedocs-versions:focus-within .rst-current-version .fa-book,#furo-readthedocs-versions:hover .rst-current-version .fa-book{color:#fff;float:left}#furo-readthedocs-versions:focus-within .fa-caret-down,#furo-readthedocs-versions:hover .fa-caret-down{display:none}#furo-readthedocs-versions:focus-within .injected,#furo-readthedocs-versions:focus-within .rst-current-version,#furo-readthedocs-versions:focus-within .rst-other-versions,#furo-readthedocs-versions:hover .injected,#furo-readthedocs-versions:hover .rst-current-version,#furo-readthedocs-versions:hover .rst-other-versions{display:block}#furo-readthedocs-versions:focus-within>.rst-current-version,#furo-readthedocs-versions:hover>.rst-current-version{display:none}.highlight:hover button.copybtn{color:var(--color-code-foreground)}.highlight button.copybtn{align-items:center;background-color:var(--color-code-background);border:none;color:var(--color-background-item);cursor:pointer;height:1.25em;right:.5rem;top:.625rem;transition:color .3s,opacity .3s;width:1.25em}.highlight button.copybtn:hover{background-color:var(--color-code-background);color:var(--color-brand-content)}.highlight button.copybtn:after{background-color:transparent;color:var(--color-code-foreground);display:none}.highlight button.copybtn.success{color:#22863a;transition:color 0s}.highlight button.copybtn.success:after{display:block}.highlight button.copybtn svg{padding:0}body{--sd-color-primary:var(--color-brand-primary);--sd-color-primary-highlight:var(--color-brand-content);--sd-color-primary-text:var(--color-background-primary);--sd-color-shadow:rgba(0,0,0,.05);--sd-color-card-border:var(--color-card-border);--sd-color-card-border-hover:var(--color-brand-content);--sd-color-card-background:var(--color-card-background);--sd-color-card-text:var(--color-foreground-primary);--sd-color-card-header:var(--color-card-marginals-background);--sd-color-card-footer:var(--color-card-marginals-background);--sd-color-tabs-label-active:var(--color-brand-content);--sd-color-tabs-label-hover:var(--color-foreground-muted);--sd-color-tabs-label-inactive:var(--color-foreground-muted);--sd-color-tabs-underline-active:var(--color-brand-content);--sd-color-tabs-underline-hover:var(--color-foreground-border);--sd-color-tabs-underline-inactive:var(--color-background-border);--sd-color-tabs-overline:var(--color-background-border);--sd-color-tabs-underline:var(--color-background-border)}.sd-tab-content{box-shadow:0 -2px var(--sd-color-tabs-overline),0 1px var(--sd-color-tabs-underline)}.sd-card{box-shadow:0 .1rem .25rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)}.sd-shadow-sm{box-shadow:0 .1rem .25rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)!important}.sd-shadow-md{box-shadow:0 .3rem .75rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)!important}.sd-shadow-lg{box-shadow:0 .6rem 1.5rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)!important}.sd-card-hover:hover{transform:none}.sd-cards-carousel{gap:.25rem;padding:.25rem}body{--tabs--label-text:var(--color-foreground-muted);--tabs--label-text--hover:var(--color-foreground-muted);--tabs--label-text--active:var(--color-brand-content);--tabs--label-text--active--hover:var(--color-brand-content);--tabs--label-background:transparent;--tabs--label-background--hover:transparent;--tabs--label-background--active:transparent;--tabs--label-background--active--hover:transparent;--tabs--padding-x:0.25em;--tabs--margin-x:1em;--tabs--border:var(--color-background-border);--tabs--label-border:transparent;--tabs--label-border--hover:var(--color-foreground-muted);--tabs--label-border--active:var(--color-brand-content);--tabs--label-border--active--hover:var(--color-brand-content)}[role=main] .container{max-width:none;padding-left:0;padding-right:0}.shadow.docutils{border:none;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .0625rem rgba(0,0,0,.1)!important}.sphinx-bs .card{background-color:var(--color-background-secondary);color:var(--color-foreground)} +/*# sourceMappingURL=furo-extensions.css.map*/ \ No newline at end of file diff --git a/SRV/docs/_build/html/_static/styles/furo-extensions.css.map b/SRV/docs/_build/html/_static/styles/furo-extensions.css.map new file mode 100644 index 0000000..68fb7fd --- /dev/null +++ b/SRV/docs/_build/html/_static/styles/furo-extensions.css.map @@ -0,0 +1 @@ +{"version":3,"file":"styles/furo-extensions.css","mappings":"AAGA,2BACE,oFACA,4CAKE,6CAHA,YACA,eAEA,CACA,kDACE,yCAEF,8CACE,sCAEJ,8CACE,kDAEJ,2BAGE,uBACA,cAHA,gBACA,UAEA,CAGA,yCACE,mBAEF,gDAEE,gDADA,YACA,CACA,sDACE,gDACF,yDACE,sCAEJ,+CACE,UACA,qDACE,UAGF,mDACE,eAEJ,yEAEE,4DAEA,mHASE,mBAPA,kBAEA,YADA,oBAGA,aADA,gBAIA,CAEA,qIAEE,WADA,UACA,CAEJ,uGACE,aAEF,iUAGE,cAEF,mHACE,aC1EJ,gCACE,mCAEF,0BAEE,mBAUA,8CACA,YAFA,mCAKA,eAZA,cAIA,YADA,YAYA,iCAdA,YAcA,CAEA,gCAEE,8CADA,gCACA,CAEF,gCAGE,6BADA,mCADA,YAEA,CAEF,kCAEE,cADA,mBACA,CACA,wCACE,cAEJ,8BACE,UCzCN,KAEE,6CAA8C,CAC9C,uDAAwD,CACxD,uDAAwD,CAGxD,iCAAsC,CAGtC,+CAAgD,CAChD,uDAAwD,CACxD,uDAAwD,CACxD,oDAAqD,CACrD,6DAA8D,CAC9D,6DAA8D,CAG9D,uDAAwD,CACxD,yDAA0D,CAC1D,4DAA6D,CAC7D,2DAA4D,CAC5D,8DAA+D,CAC/D,iEAAkE,CAClE,uDAAwD,CACxD,wDAAyD,CAG3D,gBACE,qFAGF,SACE,6EAEF,cACE,uFAEF,cACE,uFAEF,cACE,uFAGF,qBACE,eAEF,mBACE,WACA,eChDF,KACE,gDAAiD,CACjD,uDAAwD,CACxD,qDAAsD,CACtD,4DAA6D,CAC7D,oCAAqC,CACrC,2CAA4C,CAC5C,4CAA6C,CAC7C,mDAAoD,CACpD,wBAAyB,CACzB,oBAAqB,CACrB,6CAA8C,CAC9C,gCAAiC,CACjC,yDAA0D,CAC1D,uDAAwD,CACxD,8DAA+D,CCbjE,uBACE,eACA,eACA,gBAGF,iBACE,YACA,+EAGF,iBACE,mDACA","sources":["webpack:///./src/furo/assets/styles/extensions/_readthedocs.sass","webpack:///./src/furo/assets/styles/extensions/_copybutton.sass","webpack:///./src/furo/assets/styles/extensions/_sphinx-design.sass","webpack:///./src/furo/assets/styles/extensions/_sphinx-inline-tabs.sass","webpack:///./src/furo/assets/styles/extensions/_sphinx-panels.sass"],"sourcesContent":["// This file contains the styles used for tweaking how ReadTheDoc's embedded\n// contents would show up inside the theme.\n\n#furo-sidebar-ad-placement\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n .ethical-sidebar\n // Remove the border and box-shadow.\n border: none\n box-shadow: none\n // Manage the background colors.\n background: var(--color-background-secondary)\n &:hover\n background: var(--color-background-hover)\n // Ensure the text is legible.\n a\n color: var(--color-foreground-primary)\n\n .ethical-callout a\n color: var(--color-foreground-secondary) !important\n\n#furo-readthedocs-versions\n position: static\n width: 100%\n background: transparent\n display: block\n\n // Make the background color fit with the theme's aesthetic.\n .rst-versions\n background: rgb(26, 28, 30)\n\n .rst-current-version\n cursor: unset\n background: var(--color-sidebar-item-background)\n &:hover\n background: var(--color-sidebar-item-background)\n .fa-book\n color: var(--color-foreground-primary)\n\n > .rst-other-versions\n padding: 0\n small\n opacity: 1\n\n .injected\n .rst-versions\n position: unset\n\n &:hover,\n &:focus-within\n box-shadow: 0 0 0 1px var(--color-sidebar-background-border)\n\n .rst-current-version\n // Undo the tweaks done in RTD's CSS\n font-size: inherit\n line-height: inherit\n height: auto\n text-align: right\n padding: 12px\n\n // Match the rest of the body\n background: #1a1c1e\n\n .fa-book\n float: left\n color: white\n\n .fa-caret-down\n display: none\n\n .rst-current-version,\n .rst-other-versions,\n .injected\n display: block\n\n > .rst-current-version\n display: none\n",".highlight\n &:hover button.copybtn\n color: var(--color-code-foreground)\n\n button.copybtn\n // Align things correctly\n align-items: center\n\n height: 1.25em\n width: 1.25em\n\n top: 0.625rem // $code-spacing-vertical\n right: 0.5rem\n\n // Make it look better\n color: var(--color-background-item)\n background-color: var(--color-code-background)\n border: none\n\n // Change to cursor to make it obvious that you can click on it\n cursor: pointer\n\n // Transition smoothly, for aesthetics\n transition: color 300ms, opacity 300ms\n\n &:hover\n color: var(--color-brand-content)\n background-color: var(--color-code-background)\n\n &::after\n display: none\n color: var(--color-code-foreground)\n background-color: transparent\n\n &.success\n transition: color 0ms\n color: #22863a\n &::after\n display: block\n\n svg\n padding: 0\n","body\n // Colors\n --sd-color-primary: var(--color-brand-primary)\n --sd-color-primary-highlight: var(--color-brand-content)\n --sd-color-primary-text: var(--color-background-primary)\n\n // Shadows\n --sd-color-shadow: rgba(0, 0, 0, 0.05)\n\n // Cards\n --sd-color-card-border: var(--color-card-border)\n --sd-color-card-border-hover: var(--color-brand-content)\n --sd-color-card-background: var(--color-card-background)\n --sd-color-card-text: var(--color-foreground-primary)\n --sd-color-card-header: var(--color-card-marginals-background)\n --sd-color-card-footer: var(--color-card-marginals-background)\n\n // Tabs\n --sd-color-tabs-label-active: var(--color-brand-content)\n --sd-color-tabs-label-hover: var(--color-foreground-muted)\n --sd-color-tabs-label-inactive: var(--color-foreground-muted)\n --sd-color-tabs-underline-active: var(--color-brand-content)\n --sd-color-tabs-underline-hover: var(--color-foreground-border)\n --sd-color-tabs-underline-inactive: var(--color-background-border)\n --sd-color-tabs-overline: var(--color-background-border)\n --sd-color-tabs-underline: var(--color-background-border)\n\n// Tabs\n.sd-tab-content\n box-shadow: 0 -2px var(--sd-color-tabs-overline), 0 1px var(--sd-color-tabs-underline)\n\n// Shadows\n.sd-card // Have a shadow by default\n box-shadow: 0 0.1rem 0.25rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1)\n\n.sd-shadow-sm\n box-shadow: 0 0.1rem 0.25rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n.sd-shadow-md\n box-shadow: 0 0.3rem 0.75rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n.sd-shadow-lg\n box-shadow: 0 0.6rem 1.5rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n// Cards\n.sd-card-hover:hover // Don't change scale on hover\n transform: none\n\n.sd-cards-carousel // Have a bit of gap in the carousel by default\n gap: 0.25rem\n padding: 0.25rem\n","// This file contains styles to tweak sphinx-inline-tabs to work well with Furo.\n\nbody\n --tabs--label-text: var(--color-foreground-muted)\n --tabs--label-text--hover: var(--color-foreground-muted)\n --tabs--label-text--active: var(--color-brand-content)\n --tabs--label-text--active--hover: var(--color-brand-content)\n --tabs--label-background: transparent\n --tabs--label-background--hover: transparent\n --tabs--label-background--active: transparent\n --tabs--label-background--active--hover: transparent\n --tabs--padding-x: 0.25em\n --tabs--margin-x: 1em\n --tabs--border: var(--color-background-border)\n --tabs--label-border: transparent\n --tabs--label-border--hover: var(--color-foreground-muted)\n --tabs--label-border--active: var(--color-brand-content)\n --tabs--label-border--active--hover: var(--color-brand-content)\n","// This file contains styles to tweak sphinx-panels to work well with Furo.\n\n// sphinx-panels includes Bootstrap 4, which uses .container which can conflict\n// with docutils' `.. container::` directive.\n[role=\"main\"] .container\n max-width: initial\n padding-left: initial\n padding-right: initial\n\n// Make the panels look nicer!\n.shadow.docutils\n border: none\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n// Make panel colors respond to dark mode\n.sphinx-bs .card\n background-color: var(--color-background-secondary)\n color: var(--color-foreground)\n"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/SRV/docs/_build/html/_static/styles/furo.css b/SRV/docs/_build/html/_static/styles/furo.css new file mode 100644 index 0000000..592d5bf --- /dev/null +++ b/SRV/docs/_build/html/_static/styles/furo.css @@ -0,0 +1,2 @@ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}[hidden],template{display:none}@media print{.content-icon-container,.headerlink,.mobile-header,.related-pages{display:none!important}.highlight{border:.1pt solid var(--color-foreground-border)}a,blockquote,dl,ol,p,pre,table,ul{page-break-inside:avoid}caption,figure,h1,h2,h3,h4,h5,h6,img{page-break-after:avoid;page-break-inside:avoid}dl,ol,ul{page-break-before:avoid}}.visually-hidden{height:1px!important;margin:-1px!important;overflow:hidden!important;padding:0!important;position:absolute!important;width:1px!important;clip:rect(0,0,0,0)!important;background:var(--color-background-primary);border:0!important;color:var(--color-foreground-primary);white-space:nowrap!important}:-moz-focusring{outline:auto}body{--font-stack:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;--font-stack--monospace:"SFMono-Regular",Menlo,Consolas,Monaco,Liberation Mono,Lucida Console,monospace;--font-stack--headings:var(--font-stack);--font-size--normal:100%;--font-size--small:87.5%;--font-size--small--2:81.25%;--font-size--small--3:75%;--font-size--small--4:62.5%;--sidebar-caption-font-size:var(--font-size--small--2);--sidebar-item-font-size:var(--font-size--small);--sidebar-search-input-font-size:var(--font-size--small);--toc-font-size:var(--font-size--small--3);--toc-font-size--mobile:var(--font-size--normal);--toc-title-font-size:var(--font-size--small--4);--admonition-font-size:0.8125rem;--admonition-title-font-size:0.8125rem;--code-font-size:var(--font-size--small--2);--api-font-size:var(--font-size--small);--header-height:calc(var(--sidebar-item-line-height) + var(--sidebar-item-spacing-vertical)*4);--header-padding:0.5rem;--sidebar-tree-space-above:1.5rem;--sidebar-caption-space-above:1rem;--sidebar-item-line-height:1rem;--sidebar-item-spacing-vertical:0.5rem;--sidebar-item-spacing-horizontal:1rem;--sidebar-item-height:calc(var(--sidebar-item-line-height) + var(--sidebar-item-spacing-vertical)*2);--sidebar-expander-width:var(--sidebar-item-height);--sidebar-search-space-above:0.5rem;--sidebar-search-input-spacing-vertical:0.5rem;--sidebar-search-input-spacing-horizontal:0.5rem;--sidebar-search-input-height:1rem;--sidebar-search-icon-size:var(--sidebar-search-input-height);--toc-title-padding:0.25rem 0;--toc-spacing-vertical:1.5rem;--toc-spacing-horizontal:1.5rem;--toc-item-spacing-vertical:0.4rem;--toc-item-spacing-horizontal:1rem;--icon-search:url('data:image/svg+xml;charset=utf-8,');--icon-pencil:url('data:image/svg+xml;charset=utf-8,');--icon-abstract:url('data:image/svg+xml;charset=utf-8,');--icon-info:url('data:image/svg+xml;charset=utf-8,');--icon-flame:url('data:image/svg+xml;charset=utf-8,');--icon-question:url('data:image/svg+xml;charset=utf-8,');--icon-warning:url('data:image/svg+xml;charset=utf-8,');--icon-failure:url('data:image/svg+xml;charset=utf-8,');--icon-spark:url('data:image/svg+xml;charset=utf-8,');--color-admonition-title--caution:#ff9100;--color-admonition-title-background--caution:rgba(255,145,0,.2);--color-admonition-title--warning:#ff9100;--color-admonition-title-background--warning:rgba(255,145,0,.2);--color-admonition-title--danger:#ff5252;--color-admonition-title-background--danger:rgba(255,82,82,.2);--color-admonition-title--attention:#ff5252;--color-admonition-title-background--attention:rgba(255,82,82,.2);--color-admonition-title--error:#ff5252;--color-admonition-title-background--error:rgba(255,82,82,.2);--color-admonition-title--hint:#00c852;--color-admonition-title-background--hint:rgba(0,200,82,.2);--color-admonition-title--tip:#00c852;--color-admonition-title-background--tip:rgba(0,200,82,.2);--color-admonition-title--important:#00bfa5;--color-admonition-title-background--important:rgba(0,191,165,.2);--color-admonition-title--note:#00b0ff;--color-admonition-title-background--note:rgba(0,176,255,.2);--color-admonition-title--seealso:#448aff;--color-admonition-title-background--seealso:rgba(68,138,255,.2);--color-admonition-title--admonition-todo:grey;--color-admonition-title-background--admonition-todo:hsla(0,0%,50%,.2);--color-admonition-title:#651fff;--color-admonition-title-background:rgba(101,31,255,.2);--icon-admonition-default:var(--icon-abstract);--color-topic-title:#14b8a6;--color-topic-title-background:rgba(20,184,166,.2);--icon-topic-default:var(--icon-pencil);--color-problematic:#b30000;--color-foreground-primary:#000;--color-foreground-secondary:#5a5c63;--color-foreground-muted:#6b6f76;--color-foreground-border:#878787;--color-background-primary:#fff;--color-background-secondary:#f8f9fb;--color-background-hover:#efeff4;--color-background-hover--transparent:#efeff400;--color-background-border:#eeebee;--color-background-item:#ccc;--color-announcement-background:#000000dd;--color-announcement-text:#eeebee;--color-brand-primary:#0a4bff;--color-brand-content:#2757dd;--color-brand-visited:#872ee0;--color-api-background:var(--color-background-hover--transparent);--color-api-background-hover:var(--color-background-hover);--color-api-overall:var(--color-foreground-secondary);--color-api-name:var(--color-problematic);--color-api-pre-name:var(--color-problematic);--color-api-paren:var(--color-foreground-secondary);--color-api-keyword:var(--color-foreground-primary);--color-api-added:#21632c;--color-api-added-border:#38a84d;--color-api-changed:#046172;--color-api-changed-border:#06a1bc;--color-api-deprecated:#605706;--color-api-deprecated-border:#f0d90f;--color-api-removed:#b30000;--color-api-removed-border:#ff5c5c;--color-highlight-on-target:#ffc;--color-inline-code-background:var(--color-background-secondary);--color-highlighted-background:#def;--color-highlighted-text:var(--color-foreground-primary);--color-guilabel-background:#ddeeff80;--color-guilabel-border:#bedaf580;--color-guilabel-text:var(--color-foreground-primary);--color-admonition-background:transparent;--color-table-header-background:var(--color-background-secondary);--color-table-border:var(--color-background-border);--color-card-border:var(--color-background-secondary);--color-card-background:transparent;--color-card-marginals-background:var(--color-background-secondary);--color-header-background:var(--color-background-primary);--color-header-border:var(--color-background-border);--color-header-text:var(--color-foreground-primary);--color-sidebar-background:var(--color-background-secondary);--color-sidebar-background-border:var(--color-background-border);--color-sidebar-brand-text:var(--color-foreground-primary);--color-sidebar-caption-text:var(--color-foreground-muted);--color-sidebar-link-text:var(--color-foreground-secondary);--color-sidebar-link-text--top-level:var(--color-brand-primary);--color-sidebar-item-background:var(--color-sidebar-background);--color-sidebar-item-background--current:var( --color-sidebar-item-background );--color-sidebar-item-background--hover:linear-gradient(90deg,var(--color-background-hover--transparent) 0%,var(--color-background-hover) var(--sidebar-item-spacing-horizontal),var(--color-background-hover) 100%);--color-sidebar-item-expander-background:transparent;--color-sidebar-item-expander-background--hover:var( --color-background-hover );--color-sidebar-search-text:var(--color-foreground-primary);--color-sidebar-search-background:var(--color-background-secondary);--color-sidebar-search-background--focus:var(--color-background-primary);--color-sidebar-search-border:var(--color-background-border);--color-sidebar-search-icon:var(--color-foreground-muted);--color-toc-background:var(--color-background-primary);--color-toc-title-text:var(--color-foreground-muted);--color-toc-item-text:var(--color-foreground-secondary);--color-toc-item-text--hover:var(--color-foreground-primary);--color-toc-item-text--active:var(--color-brand-primary);--color-content-foreground:var(--color-foreground-primary);--color-content-background:transparent;--color-link:var(--color-brand-content);--color-link-underline:var(--color-background-border);--color-link--hover:var(--color-brand-content);--color-link-underline--hover:var(--color-foreground-border);--color-link--visited:var(--color-brand-visited);--color-link-underline--visited:var(--color-background-border);--color-link--visited--hover:var(--color-brand-visited);--color-link-underline--visited--hover:var(--color-foreground-border)}.only-light{display:block!important}html body .only-dark{display:none!important}@media not print{body[data-theme=dark]{--color-problematic:#ee5151;--color-foreground-primary:#cfd0d0;--color-foreground-secondary:#9ca0a5;--color-foreground-muted:#81868d;--color-foreground-border:#666;--color-background-primary:#131416;--color-background-secondary:#1a1c1e;--color-background-hover:#1e2124;--color-background-hover--transparent:#1e212400;--color-background-border:#303335;--color-background-item:#444;--color-announcement-background:#000000dd;--color-announcement-text:#eeebee;--color-brand-primary:#3d94ff;--color-brand-content:#5ca5ff;--color-brand-visited:#b27aeb;--color-highlighted-background:#083563;--color-guilabel-background:#08356380;--color-guilabel-border:#13395f80;--color-api-keyword:var(--color-foreground-secondary);--color-highlight-on-target:#330;--color-api-added:#3db854;--color-api-added-border:#267334;--color-api-changed:#09b0ce;--color-api-changed-border:#056d80;--color-api-deprecated:#b1a10b;--color-api-deprecated-border:#6e6407;--color-api-removed:#ff7575;--color-api-removed-border:#b03b3b;--color-admonition-background:#18181a;--color-card-border:var(--color-background-secondary);--color-card-background:#18181a;--color-card-marginals-background:var(--color-background-hover)}html body[data-theme=dark] .only-light{display:none!important}body[data-theme=dark] .only-dark{display:block!important}@media(prefers-color-scheme:dark){body:not([data-theme=light]){--color-problematic:#ee5151;--color-foreground-primary:#cfd0d0;--color-foreground-secondary:#9ca0a5;--color-foreground-muted:#81868d;--color-foreground-border:#666;--color-background-primary:#131416;--color-background-secondary:#1a1c1e;--color-background-hover:#1e2124;--color-background-hover--transparent:#1e212400;--color-background-border:#303335;--color-background-item:#444;--color-announcement-background:#000000dd;--color-announcement-text:#eeebee;--color-brand-primary:#3d94ff;--color-brand-content:#5ca5ff;--color-brand-visited:#b27aeb;--color-highlighted-background:#083563;--color-guilabel-background:#08356380;--color-guilabel-border:#13395f80;--color-api-keyword:var(--color-foreground-secondary);--color-highlight-on-target:#330;--color-api-added:#3db854;--color-api-added-border:#267334;--color-api-changed:#09b0ce;--color-api-changed-border:#056d80;--color-api-deprecated:#b1a10b;--color-api-deprecated-border:#6e6407;--color-api-removed:#ff7575;--color-api-removed-border:#b03b3b;--color-admonition-background:#18181a;--color-card-border:var(--color-background-secondary);--color-card-background:#18181a;--color-card-marginals-background:var(--color-background-hover)}html body:not([data-theme=light]) .only-light{display:none!important}body:not([data-theme=light]) .only-dark{display:block!important}}}body[data-theme=auto] .theme-toggle svg.theme-icon-when-auto-light{display:block}@media(prefers-color-scheme:dark){body[data-theme=auto] .theme-toggle svg.theme-icon-when-auto-dark{display:block}body[data-theme=auto] .theme-toggle svg.theme-icon-when-auto-light{display:none}}body[data-theme=dark] .theme-toggle svg.theme-icon-when-dark,body[data-theme=light] .theme-toggle svg.theme-icon-when-light{display:block}body{font-family:var(--font-stack)}code,kbd,pre,samp{font-family:var(--font-stack--monospace)}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}article{line-height:1.5}h1,h2,h3,h4,h5,h6{border-radius:.5rem;font-family:var(--font-stack--headings);font-weight:700;line-height:1.25;margin:.5rem -.5rem;padding-left:.5rem;padding-right:.5rem}h1+p,h2+p,h3+p,h4+p,h5+p,h6+p{margin-top:0}h1{font-size:2.5em;margin-bottom:1rem}h1,h2{margin-top:1.75rem}h2{font-size:2em}h3{font-size:1.5em}h4{font-size:1.25em}h5{font-size:1.125em}h6{font-size:1em}small{font-size:80%;opacity:75%}p{margin-bottom:.75rem;margin-top:.5rem}hr.docutils{background-color:var(--color-background-border);border:0;height:1px;margin:2rem 0;padding:0}.centered{text-align:center}a{color:var(--color-link);text-decoration:underline;text-decoration-color:var(--color-link-underline)}a:visited{color:var(--color-link--visited);text-decoration-color:var(--color-link-underline--visited)}a:visited:hover{color:var(--color-link--visited--hover);text-decoration-color:var(--color-link-underline--visited--hover)}a:hover{color:var(--color-link--hover);text-decoration-color:var(--color-link-underline--hover)}a.muted-link{color:inherit}a.muted-link:hover{color:var(--color-link--hover);text-decoration-color:var(--color-link-underline--hover)}a.muted-link:hover:visited{color:var(--color-link--visited--hover);text-decoration-color:var(--color-link-underline--visited--hover)}html{overflow-x:hidden;overflow-y:scroll;scroll-behavior:smooth}.sidebar-scroll,.toc-scroll,article[role=main] *{scrollbar-color:var(--color-foreground-border) transparent;scrollbar-width:thin}body,html{height:100%}.skip-to-content,body,html{background:var(--color-background-primary);color:var(--color-foreground-primary)}.skip-to-content{border-radius:1rem;left:.25rem;padding:1rem;position:fixed;top:.25rem;transform:translateY(-200%);transition:transform .3s ease-in-out;z-index:40}.skip-to-content:focus-within{transform:translateY(0)}article{background:var(--color-content-background);color:var(--color-content-foreground);overflow-wrap:break-word}.page{display:flex;min-height:100%}.mobile-header{background-color:var(--color-header-background);border-bottom:1px solid var(--color-header-border);color:var(--color-header-text);display:none;height:var(--header-height);width:100%;z-index:10}.mobile-header.scrolled{border-bottom:none;box-shadow:0 0 .2rem rgba(0,0,0,.1),0 .2rem .4rem rgba(0,0,0,.2)}.mobile-header .header-center a{color:var(--color-header-text);text-decoration:none}.main{display:flex;flex:1}.sidebar-drawer{background:var(--color-sidebar-background);border-right:1px solid var(--color-sidebar-background-border);box-sizing:border-box;display:flex;justify-content:flex-end;min-width:15em;width:calc(50% - 26em)}.sidebar-container,.toc-drawer{box-sizing:border-box;width:15em}.toc-drawer{background:var(--color-toc-background);padding-right:1rem}.sidebar-sticky,.toc-sticky{display:flex;flex-direction:column;height:min(100%,100vh);height:100vh;position:sticky;top:0}.sidebar-scroll,.toc-scroll{flex-grow:1;flex-shrink:1;overflow:auto;scroll-behavior:smooth}.content{display:flex;flex-direction:column;justify-content:space-between;padding:0 3em;width:46em}.icon{display:inline-block;height:1rem;width:1rem}.icon svg{height:100%;width:100%}.announcement{align-items:center;background-color:var(--color-announcement-background);color:var(--color-announcement-text);display:flex;height:var(--header-height);overflow-x:auto}.announcement+.page{min-height:calc(100% - var(--header-height))}.announcement-content{box-sizing:border-box;min-width:100%;padding:.5rem;text-align:center;white-space:nowrap}.announcement-content a{color:var(--color-announcement-text);text-decoration-color:var(--color-announcement-text)}.announcement-content a:hover{color:var(--color-announcement-text);text-decoration-color:var(--color-link--hover)}.no-js .theme-toggle-container{display:none}.theme-toggle-container{display:flex}.theme-toggle{background:transparent;border:none;cursor:pointer;display:flex;padding:0}.theme-toggle svg{color:var(--color-foreground-primary);display:none;height:1.25rem;width:1.25rem}.theme-toggle-header{align-items:center;display:flex;justify-content:center}.nav-overlay-icon,.toc-overlay-icon{cursor:pointer;display:none}.nav-overlay-icon .icon,.toc-overlay-icon .icon{color:var(--color-foreground-secondary);height:1.5rem;width:1.5rem}.nav-overlay-icon,.toc-header-icon{align-items:center;justify-content:center}.toc-content-icon{height:1.5rem;width:1.5rem}.content-icon-container{display:flex;float:right;gap:.5rem;margin-bottom:1rem;margin-left:1rem;margin-top:1.5rem}.content-icon-container .edit-this-page svg,.content-icon-container .view-this-page svg{color:inherit;height:1.25rem;width:1.25rem}.sidebar-toggle{display:none;position:absolute}.sidebar-toggle[name=__toc]{left:20px}.sidebar-toggle:checked{left:40px}.overlay{background-color:rgba(0,0,0,.54);height:0;opacity:0;position:fixed;top:0;transition:width 0s,height 0s,opacity .25s ease-out;width:0}.sidebar-overlay{z-index:20}.toc-overlay{z-index:40}.sidebar-drawer{transition:left .25s ease-in-out;z-index:30}.toc-drawer{transition:right .25s ease-in-out;z-index:50}#__navigation:checked~.sidebar-overlay{height:100%;opacity:1;width:100%}#__navigation:checked~.page .sidebar-drawer{left:0;top:0}#__toc:checked~.toc-overlay{height:100%;opacity:1;width:100%}#__toc:checked~.page .toc-drawer{right:0;top:0}.back-to-top{background:var(--color-background-primary);border-radius:1rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 1px 0 hsla(220,9%,46%,.502);display:none;font-size:.8125rem;left:0;margin-left:50%;padding:.5rem .75rem .5rem .5rem;position:fixed;text-decoration:none;top:1rem;transform:translateX(-50%);z-index:10}.back-to-top svg{height:1rem;width:1rem;fill:currentColor;display:inline-block}.back-to-top span{margin-left:.25rem}.show-back-to-top .back-to-top{align-items:center;display:flex}@media(min-width:97em){html{font-size:110%}}@media(max-width:82em){.toc-content-icon{display:flex}.toc-drawer{border-left:1px solid var(--color-background-muted);height:100vh;position:fixed;right:-15em;top:0}.toc-tree{border-left:none;font-size:var(--toc-font-size--mobile)}.sidebar-drawer{width:calc(50% - 18.5em)}}@media(max-width:67em){.content{margin-left:auto;margin-right:auto;padding:0 1em}}@media(max-width:63em){.nav-overlay-icon{display:flex}.sidebar-drawer{height:100vh;left:-15em;position:fixed;top:0;width:15em}.theme-toggle-header,.toc-header-icon{display:flex}.theme-toggle-content,.toc-content-icon{display:none}.mobile-header{align-items:center;display:flex;justify-content:space-between;position:sticky;top:0}.mobile-header .header-left,.mobile-header .header-right{display:flex;height:var(--header-height);padding:0 var(--header-padding)}.mobile-header .header-left label,.mobile-header .header-right label{height:100%;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:100%}.nav-overlay-icon .icon,.theme-toggle svg{height:1.5rem;width:1.5rem}:target{scroll-margin-top:calc(var(--header-height) + 2.5rem)}.back-to-top{top:calc(var(--header-height) + .5rem)}.page{flex-direction:column;justify-content:center}}@media(max-width:48em){.content{overflow-x:auto;width:100%}}@media(max-width:46em){article[role=main] aside.sidebar{float:none;margin:1rem 0;width:100%}}.admonition,.topic{background:var(--color-admonition-background);border-radius:.2rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .0625rem rgba(0,0,0,.1);font-size:var(--admonition-font-size);margin:1rem auto;overflow:hidden;padding:0 .5rem .5rem;page-break-inside:avoid}.admonition>:nth-child(2),.topic>:nth-child(2){margin-top:0}.admonition>:last-child,.topic>:last-child{margin-bottom:0}.admonition p.admonition-title,p.topic-title{font-size:var(--admonition-title-font-size);font-weight:500;line-height:1.3;margin:0 -.5rem .5rem;padding:.4rem .5rem .4rem 2rem;position:relative}.admonition p.admonition-title:before,p.topic-title:before{content:"";height:1rem;left:.5rem;position:absolute;width:1rem}p.admonition-title{background-color:var(--color-admonition-title-background)}p.admonition-title:before{background-color:var(--color-admonition-title);-webkit-mask-image:var(--icon-admonition-default);mask-image:var(--icon-admonition-default);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}p.topic-title{background-color:var(--color-topic-title-background)}p.topic-title:before{background-color:var(--color-topic-title);-webkit-mask-image:var(--icon-topic-default);mask-image:var(--icon-topic-default);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.admonition{border-left:.2rem solid var(--color-admonition-title)}.admonition.caution{border-left-color:var(--color-admonition-title--caution)}.admonition.caution>.admonition-title{background-color:var(--color-admonition-title-background--caution)}.admonition.caution>.admonition-title:before{background-color:var(--color-admonition-title--caution);-webkit-mask-image:var(--icon-spark);mask-image:var(--icon-spark)}.admonition.warning{border-left-color:var(--color-admonition-title--warning)}.admonition.warning>.admonition-title{background-color:var(--color-admonition-title-background--warning)}.admonition.warning>.admonition-title:before{background-color:var(--color-admonition-title--warning);-webkit-mask-image:var(--icon-warning);mask-image:var(--icon-warning)}.admonition.danger{border-left-color:var(--color-admonition-title--danger)}.admonition.danger>.admonition-title{background-color:var(--color-admonition-title-background--danger)}.admonition.danger>.admonition-title:before{background-color:var(--color-admonition-title--danger);-webkit-mask-image:var(--icon-spark);mask-image:var(--icon-spark)}.admonition.attention{border-left-color:var(--color-admonition-title--attention)}.admonition.attention>.admonition-title{background-color:var(--color-admonition-title-background--attention)}.admonition.attention>.admonition-title:before{background-color:var(--color-admonition-title--attention);-webkit-mask-image:var(--icon-warning);mask-image:var(--icon-warning)}.admonition.error{border-left-color:var(--color-admonition-title--error)}.admonition.error>.admonition-title{background-color:var(--color-admonition-title-background--error)}.admonition.error>.admonition-title:before{background-color:var(--color-admonition-title--error);-webkit-mask-image:var(--icon-failure);mask-image:var(--icon-failure)}.admonition.hint{border-left-color:var(--color-admonition-title--hint)}.admonition.hint>.admonition-title{background-color:var(--color-admonition-title-background--hint)}.admonition.hint>.admonition-title:before{background-color:var(--color-admonition-title--hint);-webkit-mask-image:var(--icon-question);mask-image:var(--icon-question)}.admonition.tip{border-left-color:var(--color-admonition-title--tip)}.admonition.tip>.admonition-title{background-color:var(--color-admonition-title-background--tip)}.admonition.tip>.admonition-title:before{background-color:var(--color-admonition-title--tip);-webkit-mask-image:var(--icon-info);mask-image:var(--icon-info)}.admonition.important{border-left-color:var(--color-admonition-title--important)}.admonition.important>.admonition-title{background-color:var(--color-admonition-title-background--important)}.admonition.important>.admonition-title:before{background-color:var(--color-admonition-title--important);-webkit-mask-image:var(--icon-flame);mask-image:var(--icon-flame)}.admonition.note{border-left-color:var(--color-admonition-title--note)}.admonition.note>.admonition-title{background-color:var(--color-admonition-title-background--note)}.admonition.note>.admonition-title:before{background-color:var(--color-admonition-title--note);-webkit-mask-image:var(--icon-pencil);mask-image:var(--icon-pencil)}.admonition.seealso{border-left-color:var(--color-admonition-title--seealso)}.admonition.seealso>.admonition-title{background-color:var(--color-admonition-title-background--seealso)}.admonition.seealso>.admonition-title:before{background-color:var(--color-admonition-title--seealso);-webkit-mask-image:var(--icon-info);mask-image:var(--icon-info)}.admonition.admonition-todo{border-left-color:var(--color-admonition-title--admonition-todo)}.admonition.admonition-todo>.admonition-title{background-color:var(--color-admonition-title-background--admonition-todo)}.admonition.admonition-todo>.admonition-title:before{background-color:var(--color-admonition-title--admonition-todo);-webkit-mask-image:var(--icon-pencil);mask-image:var(--icon-pencil)}.admonition-todo>.admonition-title{text-transform:uppercase}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) dd{margin-left:2rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) dd>:first-child{margin-top:.125rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list,dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) dd>:last-child{margin-bottom:.75rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list>dt{font-size:var(--font-size--small);text-transform:uppercase}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list dd:empty{margin-bottom:.5rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list dd>ul{margin-left:-1.2rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list dd>ul>li>p:nth-child(2){margin-top:0}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list dd>ul>li>p+p:last-child:empty{margin-bottom:0;margin-top:0}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple)>dt{color:var(--color-api-overall)}.sig:not(.sig-inline){background:var(--color-api-background);border-radius:.25rem;font-family:var(--font-stack--monospace);font-size:var(--api-font-size);font-weight:700;margin-left:-.25rem;margin-right:-.25rem;padding:.25rem .5rem .25rem 3em;text-indent:-2.5em;transition:background .1s ease-out}.sig:not(.sig-inline):hover{background:var(--color-api-background-hover)}.sig:not(.sig-inline) a.reference .viewcode-link{font-weight:400;width:4.25rem}em.property{font-style:normal}em.property:first-child{color:var(--color-api-keyword)}.sig-name{color:var(--color-api-name)}.sig-prename{color:var(--color-api-pre-name);font-weight:400}.sig-paren{color:var(--color-api-paren)}.sig-param{font-style:normal}div.deprecated,div.versionadded,div.versionchanged,div.versionremoved{border-left:.1875rem solid;border-radius:.125rem;padding-left:.75rem}div.deprecated p,div.versionadded p,div.versionchanged p,div.versionremoved p{margin-bottom:.125rem;margin-top:.125rem}div.versionadded{border-color:var(--color-api-added-border)}div.versionadded .versionmodified{color:var(--color-api-added)}div.versionchanged{border-color:var(--color-api-changed-border)}div.versionchanged .versionmodified{color:var(--color-api-changed)}div.deprecated{border-color:var(--color-api-deprecated-border)}div.deprecated .versionmodified{color:var(--color-api-deprecated)}div.versionremoved{border-color:var(--color-api-removed-border)}div.versionremoved .versionmodified{color:var(--color-api-removed)}.viewcode-back,.viewcode-link{float:right;text-align:right}.line-block{margin-bottom:.75rem;margin-top:.5rem}.line-block .line-block{margin-bottom:0;margin-top:0;padding-left:1rem}.code-block-caption,article p.caption,table>caption{font-size:var(--font-size--small);text-align:center}.toctree-wrapper.compound .caption,.toctree-wrapper.compound :not(.caption)>.caption-text{font-size:var(--font-size--small);margin-bottom:0;text-align:initial;text-transform:uppercase}.toctree-wrapper.compound>ul{margin-bottom:0;margin-top:0}.sig-inline,code.literal{background:var(--color-inline-code-background);border-radius:.2em;font-size:var(--font-size--small--2);padding:.1em .2em}pre.literal-block .sig-inline,pre.literal-block code.literal{font-size:inherit;padding:0}p .sig-inline,p code.literal{border:1px solid var(--color-background-border)}.sig-inline{font-family:var(--font-stack--monospace)}div[class*=" highlight-"],div[class^=highlight-]{display:flex;margin:1em 0}div[class*=" highlight-"] .table-wrapper,div[class^=highlight-] .table-wrapper,pre{margin:0;padding:0}pre{overflow:auto}article[role=main] .highlight pre{line-height:1.5}.highlight pre,pre.literal-block{font-size:var(--code-font-size);padding:.625rem .875rem}pre.literal-block{background-color:var(--color-code-background);border-radius:.2rem;color:var(--color-code-foreground);margin-bottom:1rem;margin-top:1rem}.highlight{border-radius:.2rem;width:100%}.highlight .gp,.highlight span.linenos{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.highlight .hll{display:block;margin-left:-.875rem;margin-right:-.875rem;padding-left:.875rem;padding-right:.875rem}.code-block-caption{background-color:var(--color-code-background);border-bottom:1px solid;border-radius:.25rem;border-bottom-left-radius:0;border-bottom-right-radius:0;border-color:var(--color-background-border);color:var(--color-code-foreground);display:flex;font-weight:300;padding:.625rem .875rem}.code-block-caption+div[class]{margin-top:0}.code-block-caption+div[class]>.highlight{border-top-left-radius:0;border-top-right-radius:0}.highlighttable{display:block;width:100%}.highlighttable tbody{display:block}.highlighttable tr{display:flex}.highlighttable td.linenos{background-color:var(--color-code-background);border-bottom-left-radius:.2rem;border-top-left-radius:.2rem;color:var(--color-code-foreground);padding:.625rem 0 .625rem .875rem}.highlighttable .linenodiv{box-shadow:-.0625rem 0 var(--color-foreground-border) inset;font-size:var(--code-font-size);padding-right:.875rem}.highlighttable td.code{display:block;flex:1;overflow:hidden;padding:0}.highlighttable td.code .highlight{border-bottom-left-radius:0;border-top-left-radius:0}.highlight span.linenos{box-shadow:-.0625rem 0 var(--color-foreground-border) inset;display:inline-block;margin-right:.875rem;padding-left:0;padding-right:.875rem}.footnote-reference{font-size:var(--font-size--small--4);vertical-align:super}dl.footnote.brackets{color:var(--color-foreground-secondary);display:grid;font-size:var(--font-size--small);grid-template-columns:max-content auto}dl.footnote.brackets dt{margin:0}dl.footnote.brackets dt>.fn-backref{margin-left:.25rem}dl.footnote.brackets dt:after{content:":"}dl.footnote.brackets dt .brackets:before{content:"["}dl.footnote.brackets dt .brackets:after{content:"]"}dl.footnote.brackets dd{margin:0;padding:0 1rem}aside.footnote{color:var(--color-foreground-secondary);font-size:var(--font-size--small)}aside.footnote>span,div.citation>span{float:left;font-weight:500;padding-right:.25rem}aside.footnote>:not(span),div.citation>p{margin-left:2rem}img{box-sizing:border-box;height:auto;max-width:100%}article .figure,article figure{border-radius:.2rem;margin:0}article .figure :last-child,article figure :last-child{margin-bottom:0}article .align-left{clear:left;float:left;margin:0 1rem 1rem}article .align-right{clear:right;float:right;margin:0 1rem 1rem}article .align-center,article .align-default{display:block;margin-left:auto;margin-right:auto;text-align:center}article table.align-default{display:table;text-align:initial}.domainindex-jumpbox,.genindex-jumpbox{border-bottom:1px solid var(--color-background-border);border-top:1px solid var(--color-background-border);padding:.25rem}.domainindex-section h2,.genindex-section h2{margin-bottom:.5rem;margin-top:.75rem}.domainindex-section ul,.genindex-section ul{margin-bottom:0;margin-top:0}ol,ul{margin-bottom:1rem;margin-top:1rem;padding-left:1.2rem}ol li>p:first-child,ul li>p:first-child{margin-bottom:.25rem;margin-top:.25rem}ol li>p:last-child,ul li>p:last-child{margin-top:.25rem}ol li>ol,ol li>ul,ul li>ol,ul li>ul{margin-bottom:.5rem;margin-top:.5rem}ol.arabic{list-style:decimal}ol.loweralpha{list-style:lower-alpha}ol.upperalpha{list-style:upper-alpha}ol.lowerroman{list-style:lower-roman}ol.upperroman{list-style:upper-roman}.simple li>ol,.simple li>ul,.toctree-wrapper li>ol,.toctree-wrapper li>ul{margin-bottom:0;margin-top:0}.field-list dt,.option-list dt,dl.footnote dt,dl.glossary dt,dl.simple dt,dl:not([class]) dt{font-weight:500;margin-top:.25rem}.field-list dt+dt,.option-list dt+dt,dl.footnote dt+dt,dl.glossary dt+dt,dl.simple dt+dt,dl:not([class]) dt+dt{margin-top:0}.field-list dt .classifier:before,.option-list dt .classifier:before,dl.footnote dt .classifier:before,dl.glossary dt .classifier:before,dl.simple dt .classifier:before,dl:not([class]) dt .classifier:before{content:":";margin-left:.2rem;margin-right:.2rem}.field-list dd ul,.field-list dd>p:first-child,.option-list dd ul,.option-list dd>p:first-child,dl.footnote dd ul,dl.footnote dd>p:first-child,dl.glossary dd ul,dl.glossary dd>p:first-child,dl.simple dd ul,dl.simple dd>p:first-child,dl:not([class]) dd ul,dl:not([class]) dd>p:first-child{margin-top:.125rem}.field-list dd ul,.option-list dd ul,dl.footnote dd ul,dl.glossary dd ul,dl.simple dd ul,dl:not([class]) dd ul{margin-bottom:.125rem}.math-wrapper{overflow-x:auto;width:100%}div.math{position:relative;text-align:center}div.math .headerlink,div.math:focus .headerlink{display:none}div.math:hover .headerlink{display:inline-block}div.math span.eqno{position:absolute;right:.5rem;top:50%;transform:translateY(-50%);z-index:1}abbr[title]{cursor:help}.problematic{color:var(--color-problematic)}kbd:not(.compound){background-color:var(--color-background-secondary);border:1px solid var(--color-foreground-border);border-radius:.2rem;box-shadow:0 .0625rem 0 rgba(0,0,0,.2),inset 0 0 0 .125rem var(--color-background-primary);color:var(--color-foreground-primary);display:inline-block;font-size:var(--font-size--small--3);margin:0 .2rem;padding:0 .2rem;vertical-align:text-bottom}blockquote{background:var(--color-background-secondary);border-left:4px solid var(--color-background-border);margin-left:0;margin-right:0;padding:.5rem 1rem}blockquote .attribution{font-weight:600;text-align:right}blockquote.highlights,blockquote.pull-quote{font-size:1.25em}blockquote.epigraph,blockquote.pull-quote{border-left-width:0;border-radius:.5rem}blockquote.highlights{background:transparent;border-left-width:0}p .reference img{vertical-align:middle}p.rubric{font-size:1.125em;font-weight:700;line-height:1.25}dd p.rubric{font-size:var(--font-size--small);font-weight:inherit;line-height:inherit;text-transform:uppercase}article .sidebar{background-color:var(--color-background-secondary);border:1px solid var(--color-background-border);border-radius:.2rem;clear:right;float:right;margin-left:1rem;margin-right:0;width:30%}article .sidebar>*{padding-left:1rem;padding-right:1rem}article .sidebar>ol,article .sidebar>ul{padding-left:2.2rem}article .sidebar .sidebar-title{border-bottom:1px solid var(--color-background-border);font-weight:500;margin:0;padding:.5rem 1rem}[role=main] .table-wrapper.container{margin-bottom:.5rem;margin-top:1rem;overflow-x:auto;padding:.2rem .2rem .75rem;width:100%}table.docutils{border-collapse:collapse;border-radius:.2rem;border-spacing:0;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .0625rem rgba(0,0,0,.1)}table.docutils th{background:var(--color-table-header-background)}table.docutils td,table.docutils th{border-bottom:1px solid var(--color-table-border);border-left:1px solid var(--color-table-border);border-right:1px solid var(--color-table-border);padding:0 .25rem}table.docutils td p,table.docutils th p{margin:.25rem}table.docutils td:first-child,table.docutils th:first-child{border-left:none}table.docutils td:last-child,table.docutils th:last-child{border-right:none}table.docutils td.text-left,table.docutils th.text-left{text-align:left}table.docutils td.text-right,table.docutils th.text-right{text-align:right}table.docutils td.text-center,table.docutils th.text-center{text-align:center}:target{scroll-margin-top:2.5rem}@media(max-width:67em){:target{scroll-margin-top:calc(2.5rem + var(--header-height))}section>span:target{scroll-margin-top:calc(2.8rem + var(--header-height))}}.headerlink{font-weight:100;-webkit-user-select:none;-moz-user-select:none;user-select:none}.code-block-caption>.headerlink,dl dt>.headerlink,figcaption p>.headerlink,h1>.headerlink,h2>.headerlink,h3>.headerlink,h4>.headerlink,h5>.headerlink,h6>.headerlink,p.caption>.headerlink,table>caption>.headerlink{margin-left:.5rem;visibility:hidden}.code-block-caption:hover>.headerlink,dl dt:hover>.headerlink,figcaption p:hover>.headerlink,h1:hover>.headerlink,h2:hover>.headerlink,h3:hover>.headerlink,h4:hover>.headerlink,h5:hover>.headerlink,h6:hover>.headerlink,p.caption:hover>.headerlink,table>caption:hover>.headerlink{visibility:visible}.code-block-caption>.toc-backref,dl dt>.toc-backref,figcaption p>.toc-backref,h1>.toc-backref,h2>.toc-backref,h3>.toc-backref,h4>.toc-backref,h5>.toc-backref,h6>.toc-backref,p.caption>.toc-backref,table>caption>.toc-backref{color:inherit;text-decoration-line:none}figure:hover>figcaption>p>.headerlink,table:hover>caption>.headerlink{visibility:visible}:target>h1:first-of-type,:target>h2:first-of-type,:target>h3:first-of-type,:target>h4:first-of-type,:target>h5:first-of-type,:target>h6:first-of-type,span:target~h1:first-of-type,span:target~h2:first-of-type,span:target~h3:first-of-type,span:target~h4:first-of-type,span:target~h5:first-of-type,span:target~h6:first-of-type{background-color:var(--color-highlight-on-target)}:target>h1:first-of-type code.literal,:target>h2:first-of-type code.literal,:target>h3:first-of-type code.literal,:target>h4:first-of-type code.literal,:target>h5:first-of-type code.literal,:target>h6:first-of-type code.literal,span:target~h1:first-of-type code.literal,span:target~h2:first-of-type code.literal,span:target~h3:first-of-type code.literal,span:target~h4:first-of-type code.literal,span:target~h5:first-of-type code.literal,span:target~h6:first-of-type code.literal{background-color:transparent}.literal-block-wrapper:target .code-block-caption,.this-will-duplicate-information-and-it-is-still-useful-here li :target,figure:target,table:target>caption{background-color:var(--color-highlight-on-target)}dt:target{background-color:var(--color-highlight-on-target)!important}.footnote-reference:target,.footnote>dt:target+dd{background-color:var(--color-highlight-on-target)}.guilabel{background-color:var(--color-guilabel-background);border:1px solid var(--color-guilabel-border);border-radius:.5em;color:var(--color-guilabel-text);font-size:.9em;padding:0 .3em}footer{display:flex;flex-direction:column;font-size:var(--font-size--small);margin-top:2rem}.bottom-of-page{align-items:center;border-top:1px solid var(--color-background-border);color:var(--color-foreground-secondary);display:flex;justify-content:space-between;line-height:1.5;margin-top:1rem;padding-bottom:1rem;padding-top:1rem}@media(max-width:46em){.bottom-of-page{flex-direction:column-reverse;gap:.25rem;text-align:center}}.bottom-of-page .left-details{font-size:var(--font-size--small)}.bottom-of-page .right-details{display:flex;flex-direction:column;gap:.25rem;text-align:right}.bottom-of-page .icons{display:flex;font-size:1rem;gap:.25rem;justify-content:flex-end}.bottom-of-page .icons a{text-decoration:none}.bottom-of-page .icons img,.bottom-of-page .icons svg{font-size:1.125rem;height:1em;width:1em}.related-pages a{align-items:center;display:flex;text-decoration:none}.related-pages a:hover .page-info .title{color:var(--color-link);text-decoration:underline;text-decoration-color:var(--color-link-underline)}.related-pages a svg.furo-related-icon,.related-pages a svg.furo-related-icon>use{color:var(--color-foreground-border);flex-shrink:0;height:.75rem;margin:0 .5rem;width:.75rem}.related-pages a.next-page{clear:right;float:right;max-width:50%;text-align:right}.related-pages a.prev-page{clear:left;float:left;max-width:50%}.related-pages a.prev-page svg{transform:rotate(180deg)}.page-info{display:flex;flex-direction:column;overflow-wrap:anywhere}.next-page .page-info{align-items:flex-end}.page-info .context{align-items:center;color:var(--color-foreground-muted);display:flex;font-size:var(--font-size--small);padding-bottom:.1rem;text-decoration:none}ul.search{list-style:none;padding-left:0}ul.search li{border-bottom:1px solid var(--color-background-border);padding:1rem 0}[role=main] .highlighted{background-color:var(--color-highlighted-background);color:var(--color-highlighted-text)}.sidebar-brand{display:flex;flex-direction:column;flex-shrink:0;padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal);text-decoration:none}.sidebar-brand-text{color:var(--color-sidebar-brand-text);font-size:1.5rem;overflow-wrap:break-word}.sidebar-brand-text,.sidebar-logo-container{margin:var(--sidebar-item-spacing-vertical) 0}.sidebar-logo{display:block;margin:0 auto;max-width:100%}.sidebar-search-container{align-items:center;background:var(--color-sidebar-search-background);display:flex;margin-top:var(--sidebar-search-space-above);position:relative}.sidebar-search-container:focus-within,.sidebar-search-container:hover{background:var(--color-sidebar-search-background--focus)}.sidebar-search-container:before{background-color:var(--color-sidebar-search-icon);content:"";height:var(--sidebar-search-icon-size);left:var(--sidebar-item-spacing-horizontal);-webkit-mask-image:var(--icon-search);mask-image:var(--icon-search);position:absolute;width:var(--sidebar-search-icon-size)}.sidebar-search{background:transparent;border:none;border-bottom:1px solid var(--color-sidebar-search-border);border-top:1px solid var(--color-sidebar-search-border);box-sizing:border-box;color:var(--color-sidebar-search-foreground);padding:var(--sidebar-search-input-spacing-vertical) var(--sidebar-search-input-spacing-horizontal) var(--sidebar-search-input-spacing-vertical) calc(var(--sidebar-item-spacing-horizontal) + var(--sidebar-search-input-spacing-horizontal) + var(--sidebar-search-icon-size));width:100%;z-index:10}.sidebar-search:focus{outline:none}.sidebar-search::-moz-placeholder{font-size:var(--sidebar-search-input-font-size)}.sidebar-search::placeholder{font-size:var(--sidebar-search-input-font-size)}#searchbox .highlight-link{margin:0;padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal) 0;text-align:center}#searchbox .highlight-link a{color:var(--color-sidebar-search-icon);font-size:var(--font-size--small--2)}.sidebar-tree{font-size:var(--sidebar-item-font-size);margin-bottom:var(--sidebar-item-spacing-vertical);margin-top:var(--sidebar-tree-space-above)}.sidebar-tree ul{display:flex;flex-direction:column;list-style:none;margin-bottom:0;margin-top:0;padding:0}.sidebar-tree li{margin:0;position:relative}.sidebar-tree li>ul{margin-left:var(--sidebar-item-spacing-horizontal)}.sidebar-tree .icon,.sidebar-tree .reference{color:var(--color-sidebar-link-text)}.sidebar-tree .reference{box-sizing:border-box;display:inline-block;height:100%;line-height:var(--sidebar-item-line-height);overflow-wrap:anywhere;padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal);text-decoration:none;width:100%}.sidebar-tree .reference:hover{background:var(--color-sidebar-item-background--hover);color:var(--color-sidebar-link-text)}.sidebar-tree .reference.external:after{color:var(--color-sidebar-link-text);content:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23607d8b' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' viewBox='0 0 24 24'%3E%3Cpath stroke='none' d='M0 0h24v24H0z'/%3E%3Cpath d='M11 7H6a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2-2v-5M10 14 20 4M15 4h5v5'/%3E%3C/svg%3E");margin:0 .25rem;vertical-align:middle}.sidebar-tree .current-page>.reference{font-weight:700}.sidebar-tree label{align-items:center;cursor:pointer;display:flex;height:var(--sidebar-item-height);justify-content:center;position:absolute;right:0;top:0;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:var(--sidebar-expander-width)}.sidebar-tree .caption,.sidebar-tree :not(.caption)>.caption-text{color:var(--color-sidebar-caption-text);font-size:var(--sidebar-caption-font-size);font-weight:700;margin:var(--sidebar-caption-space-above) 0 0 0;padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal);text-transform:uppercase}.sidebar-tree li.has-children>.reference{padding-right:var(--sidebar-expander-width)}.sidebar-tree .toctree-l1>.reference,.sidebar-tree .toctree-l1>label .icon{color:var(--color-sidebar-link-text--top-level)}.sidebar-tree label{background:var(--color-sidebar-item-expander-background)}.sidebar-tree label:hover{background:var(--color-sidebar-item-expander-background--hover)}.sidebar-tree .current>.reference{background:var(--color-sidebar-item-background--current)}.sidebar-tree .current>.reference:hover{background:var(--color-sidebar-item-background--hover)}.toctree-checkbox{display:none;position:absolute}.toctree-checkbox~ul{display:none}.toctree-checkbox~label .icon svg{transform:rotate(90deg)}.toctree-checkbox:checked~ul{display:block}.toctree-checkbox:checked~label .icon svg{transform:rotate(-90deg)}.toc-title-container{padding:var(--toc-title-padding);padding-top:var(--toc-spacing-vertical)}.toc-title{color:var(--color-toc-title-text);font-size:var(--toc-title-font-size);padding-left:var(--toc-spacing-horizontal);text-transform:uppercase}.no-toc{display:none}.toc-tree-container{padding-bottom:var(--toc-spacing-vertical)}.toc-tree{border-left:1px solid var(--color-background-border);font-size:var(--toc-font-size);line-height:1.3;padding-left:calc(var(--toc-spacing-horizontal) - var(--toc-item-spacing-horizontal))}.toc-tree>ul>li:first-child{padding-top:0}.toc-tree>ul>li:first-child>ul{padding-left:0}.toc-tree>ul>li:first-child>a{display:none}.toc-tree ul{list-style-type:none;margin-bottom:0;margin-top:0;padding-left:var(--toc-item-spacing-horizontal)}.toc-tree li{padding-top:var(--toc-item-spacing-vertical)}.toc-tree li.scroll-current>.reference{color:var(--color-toc-item-text--active);font-weight:700}.toc-tree a.reference{color:var(--color-toc-item-text);overflow-wrap:anywhere;text-decoration:none}.toc-scroll{max-height:100vh;overflow-y:scroll}.contents:not(.this-will-duplicate-information-and-it-is-still-useful-here){background:rgba(255,0,0,.25);color:var(--color-problematic)}.contents:not(.this-will-duplicate-information-and-it-is-still-useful-here):before{content:"ERROR: Adding a table of contents in Furo-based documentation is unnecessary, and does not work well with existing styling. Add a 'this-will-duplicate-information-and-it-is-still-useful-here' class, if you want an escape hatch."}.text-align\:left>p{text-align:left}.text-align\:center>p{text-align:center}.text-align\:right>p{text-align:right} +/*# sourceMappingURL=furo.css.map*/ \ No newline at end of file diff --git a/SRV/docs/_build/html/_static/styles/furo.css.map b/SRV/docs/_build/html/_static/styles/furo.css.map new file mode 100644 index 0000000..280b3fe --- /dev/null +++ b/SRV/docs/_build/html/_static/styles/furo.css.map @@ -0,0 +1 @@ +{"version":3,"file":"styles/furo.css","mappings":"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,CAJE,gBAEJ,CAHI,iBAMA,yFAKA,aACA,eACA,cCxaJ,iBAEE,aADA,iBACA,6BAEA,kCAEA,SACA,UAIA,gCACA,CALA,SAEA,SAEA,CAJA,wEAEA,CAFA,OAKA,CAGA,mDACE,iBAGF,gCACE,CADF,UACE,aAEJ,iCAEE,CAFF,UAEE,wCAEA,WACA,WADA,UACA,CACA,4CAGA,MACA,CADA,KACA,wCACA,UAGA,CAJA,UAIA,6DAUA,0CACE,CAFF,mBAEE,wEACA,CAVA,YACA,CAMF,mBAJE,OAOA,gBAJJ,gCACE,CANE,cACA,CAHA,oBACA,CAGA,QAGJ,CAII,0BACA,CADA,UACA,wCAEJ,kBACE,0DACA,gCACE,kBACA,CADA,YACA,oEACA,2CAMF,mDAII,CALN,YACE,CANE,cAKJ,CACE,iBAII,kEACA,yCACE,kDACA,yDACE,+CACA,uBANN,CAMM,+BANN,uCACE,qDACA,4BAEE,mBADA,0CACA,CADA,qBACA,0DACE,wCACA,sGALJ,oCACA,sBACE,kBAFF,UAEE,2CACA,wFACE,cACA,kEANN,uBACE,iDACA,CADA,UACA,0DACE,wDAEE,iEACA,qEANN,sCACE,CAGE,iBAHF,gBAGE,qBACE,CAJJ,uBACA,gDACE,wDACA,6DAHF,2CACA,CADA,gBACA,eACE,CAGE,sBANN,8BACE,CAII,iBAFF,4DACA,WACE,YADF,uCACE,6EACA,2BANN,8CACE,kDACA,0CACE,8BACA,yFACE,sBACA,sFALJ,mEACA,sBACE,kEACA,6EACE,uCACA,kEALJ,qGAEE,kEACA,6EACE,uCACA,kEALJ,8CACA,uDACE,sEACA,2EACE,sCACA,iEALJ,mGACA,qCACE,oDACA,0DACE,6GACA,gDAGR,yDCvEA,sEACE,CACA,6GACE,gEACF,iGAIF,wFACE,qDAGA,mGAEE,2CAEF,4FACE,gCACF,wGACE,8DAEE,6FAIA,iJAKN,6GACE,gDAKF,yDACA,qCAGA,6BACA,kBACA,qDAKA,oCAEA,+DAGA,2CAGE,oDAIA,oEAEE,qBAGJ,wDAEE,uCAEF,kEAGA,8CAEA,uDAIF,gEAIE,6BACA,gEAIA,+C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","sources":["webpack:///./node_modules/normalize.css/normalize.css","webpack:///./src/furo/assets/styles/base/_print.sass","webpack:///./src/furo/assets/styles/base/_screen-readers.sass","webpack:///./src/furo/assets/styles/base/_theme.sass","webpack:///./src/furo/assets/styles/variables/_fonts.scss","webpack:///./src/furo/assets/styles/variables/_spacing.scss","webpack:///./src/furo/assets/styles/variables/_icons.scss","webpack:///./src/furo/assets/styles/variables/_admonitions.scss","webpack:///./src/furo/assets/styles/variables/_colors.scss","webpack:///./src/furo/assets/styles/base/_typography.sass","webpack:///./src/furo/assets/styles/_scaffold.sass","webpack:///./src/furo/assets/styles/content/_admonitions.sass","webpack:///./src/furo/assets/styles/content/_api.sass","webpack:///./src/furo/assets/styles/content/_blocks.sass","webpack:///./src/furo/assets/styles/content/_captions.sass","webpack:///./src/furo/assets/styles/content/_code.sass","webpack:///./src/furo/assets/styles/content/_footnotes.sass","webpack:///./src/furo/assets/styles/content/_images.sass","webpack:///./src/furo/assets/styles/content/_indexes.sass","webpack:///./src/furo/assets/styles/content/_lists.sass","webpack:///./src/furo/assets/styles/content/_math.sass","webpack:///./src/furo/assets/styles/content/_misc.sass","webpack:///./src/furo/assets/styles/content/_rubrics.sass","webpack:///./src/furo/assets/styles/content/_sidebar.sass","webpack:///./src/furo/assets/styles/content/_tables.sass","webpack:///./src/furo/assets/styles/content/_target.sass","webpack:///./src/furo/assets/styles/content/_gui-labels.sass","webpack:///./src/furo/assets/styles/components/_footer.sass","webpack:///./src/furo/assets/styles/components/_sidebar.sass","webpack:///./src/furo/assets/styles/components/_table_of_contents.sass","webpack:///./src/furo/assets/styles/_shame.sass"],"sourcesContent":["/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */\n\n/* Document\n ========================================================================== */\n\n/**\n * 1. Correct the line height in all browsers.\n * 2. Prevent adjustments of font size after orientation changes in iOS.\n */\n\nhtml {\n line-height: 1.15; /* 1 */\n -webkit-text-size-adjust: 100%; /* 2 */\n}\n\n/* Sections\n ========================================================================== */\n\n/**\n * Remove the margin in all browsers.\n */\n\nbody {\n margin: 0;\n}\n\n/**\n * Render the `main` element consistently in IE.\n */\n\nmain {\n display: block;\n}\n\n/**\n * Correct the font size and margin on `h1` elements within `section` and\n * `article` contexts in Chrome, Firefox, and Safari.\n */\n\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\n\n/* Grouping content\n ========================================================================== */\n\n/**\n * 1. Add the correct box sizing in Firefox.\n * 2. Show the overflow in Edge and IE.\n */\n\nhr {\n box-sizing: content-box; /* 1 */\n height: 0; /* 1 */\n overflow: visible; /* 2 */\n}\n\n/**\n * 1. Correct the inheritance and scaling of font size in all browsers.\n * 2. Correct the odd `em` font sizing in all browsers.\n */\n\npre {\n font-family: monospace, monospace; /* 1 */\n font-size: 1em; /* 2 */\n}\n\n/* Text-level semantics\n ========================================================================== */\n\n/**\n * Remove the gray background on active links in IE 10.\n */\n\na {\n background-color: transparent;\n}\n\n/**\n * 1. Remove the bottom border in Chrome 57-\n * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.\n */\n\nabbr[title] {\n border-bottom: none; /* 1 */\n text-decoration: underline; /* 2 */\n text-decoration: underline dotted; /* 2 */\n}\n\n/**\n * Add the correct font weight in Chrome, Edge, and Safari.\n */\n\nb,\nstrong {\n font-weight: bolder;\n}\n\n/**\n * 1. Correct the inheritance and scaling of font size in all browsers.\n * 2. Correct the odd `em` font sizing in all browsers.\n */\n\ncode,\nkbd,\nsamp {\n font-family: monospace, monospace; /* 1 */\n font-size: 1em; /* 2 */\n}\n\n/**\n * Add the correct font size in all browsers.\n */\n\nsmall {\n font-size: 80%;\n}\n\n/**\n * Prevent `sub` and `sup` elements from affecting the line height in\n * all browsers.\n */\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -0.25em;\n}\n\nsup {\n top: -0.5em;\n}\n\n/* Embedded content\n ========================================================================== */\n\n/**\n * Remove the border on images inside links in IE 10.\n */\n\nimg {\n border-style: none;\n}\n\n/* Forms\n ========================================================================== */\n\n/**\n * 1. Change the font styles in all browsers.\n * 2. Remove the margin in Firefox and Safari.\n */\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n font-family: inherit; /* 1 */\n font-size: 100%; /* 1 */\n line-height: 1.15; /* 1 */\n margin: 0; /* 2 */\n}\n\n/**\n * Show the overflow in IE.\n * 1. Show the overflow in Edge.\n */\n\nbutton,\ninput { /* 1 */\n overflow: visible;\n}\n\n/**\n * Remove the inheritance of text transform in Edge, Firefox, and IE.\n * 1. Remove the inheritance of text transform in Firefox.\n */\n\nbutton,\nselect { /* 1 */\n text-transform: none;\n}\n\n/**\n * Correct the inability to style clickable types in iOS and Safari.\n */\n\nbutton,\n[type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\n/**\n * Remove the inner border and padding in Firefox.\n */\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n border-style: none;\n padding: 0;\n}\n\n/**\n * Restore the focus styles unset by the previous rule.\n */\n\nbutton:-moz-focusring,\n[type=\"button\"]:-moz-focusring,\n[type=\"reset\"]:-moz-focusring,\n[type=\"submit\"]:-moz-focusring {\n outline: 1px dotted ButtonText;\n}\n\n/**\n * Correct the padding in Firefox.\n */\n\nfieldset {\n padding: 0.35em 0.75em 0.625em;\n}\n\n/**\n * 1. Correct the text wrapping in Edge and IE.\n * 2. Correct the color inheritance from `fieldset` elements in IE.\n * 3. Remove the padding so developers are not caught out when they zero out\n * `fieldset` elements in all browsers.\n */\n\nlegend {\n box-sizing: border-box; /* 1 */\n color: inherit; /* 2 */\n display: table; /* 1 */\n max-width: 100%; /* 1 */\n padding: 0; /* 3 */\n white-space: normal; /* 1 */\n}\n\n/**\n * Add the correct vertical alignment in Chrome, Firefox, and Opera.\n */\n\nprogress {\n vertical-align: baseline;\n}\n\n/**\n * Remove the default vertical scrollbar in IE 10+.\n */\n\ntextarea {\n overflow: auto;\n}\n\n/**\n * 1. Add the correct box sizing in IE 10.\n * 2. Remove the padding in IE 10.\n */\n\n[type=\"checkbox\"],\n[type=\"radio\"] {\n box-sizing: border-box; /* 1 */\n padding: 0; /* 2 */\n}\n\n/**\n * Correct the cursor style of increment and decrement buttons in Chrome.\n */\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n/**\n * 1. Correct the odd appearance in Chrome and Safari.\n * 2. Correct the outline style in Safari.\n */\n\n[type=\"search\"] {\n -webkit-appearance: textfield; /* 1 */\n outline-offset: -2px; /* 2 */\n}\n\n/**\n * Remove the inner padding in Chrome and Safari on macOS.\n */\n\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n/**\n * 1. Correct the inability to style clickable types in iOS and Safari.\n * 2. Change font properties to `inherit` in Safari.\n */\n\n::-webkit-file-upload-button {\n -webkit-appearance: button; /* 1 */\n font: inherit; /* 2 */\n}\n\n/* Interactive\n ========================================================================== */\n\n/*\n * Add the correct display in Edge, IE 10+, and Firefox.\n */\n\ndetails {\n display: block;\n}\n\n/*\n * Add the correct display in all browsers.\n */\n\nsummary {\n display: list-item;\n}\n\n/* Misc\n ========================================================================== */\n\n/**\n * Add the correct display in IE 10+.\n */\n\ntemplate {\n display: none;\n}\n\n/**\n * Add the correct display in IE 10.\n */\n\n[hidden] {\n display: none;\n}\n","// This file contains styles for managing print media.\n\n////////////////////////////////////////////////////////////////////////////////\n// Hide elements not relevant to print media.\n////////////////////////////////////////////////////////////////////////////////\n@media print\n // Hide icon container.\n .content-icon-container\n display: none !important\n\n // Hide showing header links if hovering over when printing.\n .headerlink\n display: none !important\n\n // Hide mobile header.\n .mobile-header\n display: none !important\n\n // Hide navigation links.\n .related-pages\n display: none !important\n\n////////////////////////////////////////////////////////////////////////////////\n// Tweaks related to decolorization.\n////////////////////////////////////////////////////////////////////////////////\n@media print\n // Apply a border around code which no longer have a color background.\n .highlight\n border: 0.1pt solid var(--color-foreground-border)\n\n////////////////////////////////////////////////////////////////////////////////\n// Avoid page break in some relevant cases.\n////////////////////////////////////////////////////////////////////////////////\n@media print\n ul, ol, dl, a, table, pre, blockquote, p\n page-break-inside: avoid\n\n h1, h2, h3, h4, h5, h6, img, figure, caption\n page-break-inside: avoid\n page-break-after: avoid\n\n ul, ol, dl\n page-break-before: avoid\n",".visually-hidden\n position: absolute !important\n width: 1px !important\n height: 1px !important\n padding: 0 !important\n margin: -1px !important\n overflow: hidden !important\n clip: rect(0,0,0,0) !important\n white-space: nowrap !important\n border: 0 !important\n color: var(--color-foreground-primary)\n background: var(--color-background-primary)\n\n:-moz-focusring\n outline: auto\n","// This file serves as the \"skeleton\" of the theming logic.\n//\n// This contains the bulk of the logic for handling dark mode, color scheme\n// toggling and the handling of color-scheme-specific hiding of elements.\n\n@use \"../variables\" as *\n\nbody\n @include fonts\n @include spacing\n @include icons\n @include admonitions\n @include default-admonition(#651fff, \"abstract\")\n @include default-topic(#14B8A6, \"pencil\")\n\n @include colors\n\n.only-light\n display: block !important\nhtml body .only-dark\n display: none !important\n\n// Ignore dark-mode hints if print media.\n@media not print\n // Enable dark-mode, if requested.\n body[data-theme=\"dark\"]\n @include colors-dark\n\n html & .only-light\n display: none !important\n .only-dark\n display: block !important\n\n // Enable dark mode, unless explicitly told to avoid.\n @media (prefers-color-scheme: dark)\n body:not([data-theme=\"light\"])\n @include colors-dark\n\n html & .only-light\n display: none !important\n .only-dark\n display: block !important\n\n//\n// Theme toggle presentation\n//\nbody[data-theme=\"auto\"]\n .theme-toggle svg.theme-icon-when-auto-light\n display: block\n\n @media (prefers-color-scheme: dark)\n .theme-toggle svg.theme-icon-when-auto-dark\n display: block\n .theme-toggle svg.theme-icon-when-auto-light\n display: none\n\nbody[data-theme=\"dark\"]\n .theme-toggle svg.theme-icon-when-dark\n display: block\n\nbody[data-theme=\"light\"]\n .theme-toggle svg.theme-icon-when-light\n display: block\n","// Fonts used by this theme.\n//\n// There are basically two things here -- using the system font stack and\n// defining sizes for various elements in %ages. We could have also used `em`\n// but %age is easier to reason about for me.\n\n@mixin fonts {\n // These are adapted from https://systemfontstack.com/\n --font-stack:\n -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif,\n Apple Color Emoji, Segoe UI Emoji;\n --font-stack--monospace:\n \"SFMono-Regular\", Menlo, Consolas, Monaco, Liberation Mono, Lucida Console,\n monospace;\n --font-stack--headings: var(--font-stack);\n\n --font-size--normal: 100%;\n --font-size--small: 87.5%;\n --font-size--small--2: 81.25%;\n --font-size--small--3: 75%;\n --font-size--small--4: 62.5%;\n\n // Sidebar\n --sidebar-caption-font-size: var(--font-size--small--2);\n --sidebar-item-font-size: var(--font-size--small);\n --sidebar-search-input-font-size: var(--font-size--small);\n\n // Table of Contents\n --toc-font-size: var(--font-size--small--3);\n --toc-font-size--mobile: var(--font-size--normal);\n --toc-title-font-size: var(--font-size--small--4);\n\n // Admonitions\n //\n // These aren't defined in terms of %ages, since nesting these is permitted.\n --admonition-font-size: 0.8125rem;\n --admonition-title-font-size: 0.8125rem;\n\n // Code\n --code-font-size: var(--font-size--small--2);\n\n // API\n --api-font-size: var(--font-size--small);\n}\n","// Spacing for various elements on the page\n//\n// If the user wants to tweak things in a certain way, they are permitted to.\n// They also have to deal with the consequences though!\n\n@mixin spacing {\n // Header!\n --header-height: calc(\n var(--sidebar-item-line-height) + 4 *\n #{var(--sidebar-item-spacing-vertical)}\n );\n --header-padding: 0.5rem;\n\n // Sidebar\n --sidebar-tree-space-above: 1.5rem;\n --sidebar-caption-space-above: 1rem;\n\n --sidebar-item-line-height: 1rem;\n --sidebar-item-spacing-vertical: 0.5rem;\n --sidebar-item-spacing-horizontal: 1rem;\n --sidebar-item-height: calc(\n var(--sidebar-item-line-height) + 2 *#{var(--sidebar-item-spacing-vertical)}\n );\n\n --sidebar-expander-width: var(--sidebar-item-height); // be square\n\n --sidebar-search-space-above: 0.5rem;\n --sidebar-search-input-spacing-vertical: 0.5rem;\n --sidebar-search-input-spacing-horizontal: 0.5rem;\n --sidebar-search-input-height: 1rem;\n --sidebar-search-icon-size: var(--sidebar-search-input-height);\n\n // Table of Contents\n --toc-title-padding: 0.25rem 0;\n --toc-spacing-vertical: 1.5rem;\n --toc-spacing-horizontal: 1.5rem;\n --toc-item-spacing-vertical: 0.4rem;\n --toc-item-spacing-horizontal: 1rem;\n}\n","// Expose theme icons as CSS variables.\n\n$icons: (\n // Adapted from tabler-icons\n // url: https://tablericons.com/\n \"search\":\n url('data:image/svg+xml;charset=utf-8,'),\n // Factored out from mkdocs-material on 24-Aug-2020.\n // url: https://squidfunk.github.io/mkdocs-material/reference/admonitions/\n \"pencil\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"abstract\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"info\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"flame\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"question\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"warning\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"failure\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"spark\":\n url('data:image/svg+xml;charset=utf-8,')\n);\n\n@mixin icons {\n @each $name, $glyph in $icons {\n --icon-#{$name}: #{$glyph};\n }\n}\n","@use \"sass:list\";\n// Admonitions\n\n// Structure of these is:\n// admonition-class: color \"icon-name\";\n//\n// The colors are translated into CSS variables below. The icons are\n// used directly in the main declarations to set the `mask-image` in\n// the title.\n\n// prettier-ignore\n$admonitions: (\n // Each of these has an reST directives for it.\n \"caution\": #ff9100 \"spark\",\n \"warning\": #ff9100 \"warning\",\n \"danger\": #ff5252 \"spark\",\n \"attention\": #ff5252 \"warning\",\n \"error\": #ff5252 \"failure\",\n \"hint\": #00c852 \"question\",\n \"tip\": #00c852 \"info\",\n \"important\": #00bfa5 \"flame\",\n \"note\": #00b0ff \"pencil\",\n \"seealso\": #448aff \"info\",\n \"admonition-todo\": #808080 \"pencil\"\n);\n\n@mixin default-admonition($color, $icon-name) {\n --color-admonition-title: #{$color};\n --color-admonition-title-background: #{rgba($color, 0.2)};\n\n --icon-admonition-default: var(--icon-#{$icon-name});\n}\n\n@mixin default-topic($color, $icon-name) {\n --color-topic-title: #{$color};\n --color-topic-title-background: #{rgba($color, 0.2)};\n\n --icon-topic-default: var(--icon-#{$icon-name});\n}\n\n@mixin admonitions {\n @each $name, $values in $admonitions {\n --color-admonition-title--#{$name}: #{list.nth($values, 1)};\n --color-admonition-title-background--#{$name}: #{rgba(\n list.nth($values, 1),\n 0.2\n )};\n }\n}\n","// Colors used throughout this theme.\n//\n// The aim is to give the user more control. Thus, instead of hard-coding colors\n// in various parts of the stylesheet, the approach taken is to define all\n// colors as CSS variables and reusing them in all the places.\n//\n// `colors-dark` depends on `colors` being included at a lower specificity.\n\n@mixin colors {\n --color-problematic: #b30000;\n\n // Base Colors\n --color-foreground-primary: black; // for main text and headings\n --color-foreground-secondary: #5a5c63; // for secondary text\n --color-foreground-muted: #6b6f76; // for muted text\n --color-foreground-border: #878787; // for content borders\n\n --color-background-primary: white; // for content\n --color-background-secondary: #f8f9fb; // for navigation + ToC\n --color-background-hover: #efeff4ff; // for navigation-item hover\n --color-background-hover--transparent: #efeff400;\n --color-background-border: #eeebee; // for UI borders\n --color-background-item: #ccc; // for \"background\" items (eg: copybutton)\n\n // Announcements\n --color-announcement-background: #000000dd;\n --color-announcement-text: #eeebee;\n\n // Brand colors\n --color-brand-primary: #0a4bff;\n --color-brand-content: #2757dd;\n --color-brand-visited: #872ee0;\n\n // API documentation\n --color-api-background: var(--color-background-hover--transparent);\n --color-api-background-hover: var(--color-background-hover);\n --color-api-overall: var(--color-foreground-secondary);\n --color-api-name: var(--color-problematic);\n --color-api-pre-name: var(--color-problematic);\n --color-api-paren: var(--color-foreground-secondary);\n --color-api-keyword: var(--color-foreground-primary);\n\n --color-api-added: #21632c;\n --color-api-added-border: #38a84d;\n --color-api-changed: #046172;\n --color-api-changed-border: #06a1bc;\n --color-api-deprecated: #605706;\n --color-api-deprecated-border: #f0d90f;\n --color-api-removed: #b30000;\n --color-api-removed-border: #ff5c5c;\n\n --color-highlight-on-target: #ffffcc;\n\n // Inline code background\n --color-inline-code-background: var(--color-background-secondary);\n\n // Highlighted text (search)\n --color-highlighted-background: #ddeeff;\n --color-highlighted-text: var(--color-foreground-primary);\n\n // GUI Labels\n --color-guilabel-background: #ddeeff80;\n --color-guilabel-border: #bedaf580;\n --color-guilabel-text: var(--color-foreground-primary);\n\n // Admonitions!\n --color-admonition-background: transparent;\n\n //////////////////////////////////////////////////////////////////////////////\n // Everything below this should be one of:\n // - var(...)\n // - *-gradient(...)\n // - special literal values (eg: transparent, none)\n //////////////////////////////////////////////////////////////////////////////\n\n // Tables\n --color-table-header-background: var(--color-background-secondary);\n --color-table-border: var(--color-background-border);\n\n // Cards\n --color-card-border: var(--color-background-secondary);\n --color-card-background: transparent;\n --color-card-marginals-background: var(--color-background-secondary);\n\n // Header\n --color-header-background: var(--color-background-primary);\n --color-header-border: var(--color-background-border);\n --color-header-text: var(--color-foreground-primary);\n\n // Sidebar (left)\n --color-sidebar-background: var(--color-background-secondary);\n --color-sidebar-background-border: var(--color-background-border);\n\n --color-sidebar-brand-text: var(--color-foreground-primary);\n --color-sidebar-caption-text: var(--color-foreground-muted);\n --color-sidebar-link-text: var(--color-foreground-secondary);\n --color-sidebar-link-text--top-level: var(--color-brand-primary);\n\n --color-sidebar-item-background: var(--color-sidebar-background);\n --color-sidebar-item-background--current: var(\n --color-sidebar-item-background\n );\n --color-sidebar-item-background--hover: linear-gradient(\n 90deg,\n var(--color-background-hover--transparent) 0%,\n var(--color-background-hover) var(--sidebar-item-spacing-horizontal),\n var(--color-background-hover) 100%\n );\n\n --color-sidebar-item-expander-background: transparent;\n --color-sidebar-item-expander-background--hover: var(\n --color-background-hover\n );\n\n --color-sidebar-search-text: var(--color-foreground-primary);\n --color-sidebar-search-background: var(--color-background-secondary);\n --color-sidebar-search-background--focus: var(--color-background-primary);\n --color-sidebar-search-border: var(--color-background-border);\n --color-sidebar-search-icon: var(--color-foreground-muted);\n\n // Table of Contents (right)\n --color-toc-background: var(--color-background-primary);\n --color-toc-title-text: var(--color-foreground-muted);\n --color-toc-item-text: var(--color-foreground-secondary);\n --color-toc-item-text--hover: var(--color-foreground-primary);\n --color-toc-item-text--active: var(--color-brand-primary);\n\n // Actual page contents\n --color-content-foreground: var(--color-foreground-primary);\n --color-content-background: transparent;\n\n // Links\n --color-link: var(--color-brand-content);\n --color-link-underline: var(--color-background-border);\n --color-link--hover: var(--color-brand-content);\n --color-link-underline--hover: var(--color-foreground-border);\n\n --color-link--visited: var(--color-brand-visited);\n --color-link-underline--visited: var(--color-background-border);\n --color-link--visited--hover: var(--color-brand-visited);\n --color-link-underline--visited--hover: var(--color-foreground-border);\n}\n\n@mixin colors-dark {\n --color-problematic: #ee5151;\n\n // Base Colors\n --color-foreground-primary: #cfd0d0; // for main text and headings\n --color-foreground-secondary: #9ca0a5; // for secondary text\n --color-foreground-muted: #81868d; // for muted text\n --color-foreground-border: #666666; // for content borders\n\n --color-background-primary: #131416; // for content\n --color-background-secondary: #1a1c1e; // for navigation + ToC\n --color-background-hover: #1e2124ff; // for navigation-item hover\n --color-background-hover--transparent: #1e212400;\n --color-background-border: #303335; // for UI borders\n --color-background-item: #444; // for \"background\" items (eg: copybutton)\n\n // Announcements\n --color-announcement-background: #000000dd;\n --color-announcement-text: #eeebee;\n\n // Brand colors\n --color-brand-primary: #3d94ff;\n --color-brand-content: #5ca5ff;\n --color-brand-visited: #b27aeb;\n\n // Highlighted text (search)\n --color-highlighted-background: #083563;\n\n // GUI Labels\n --color-guilabel-background: #08356380;\n --color-guilabel-border: #13395f80;\n\n // API documentation\n --color-api-keyword: var(--color-foreground-secondary);\n --color-highlight-on-target: #333300;\n\n --color-api-added: #3db854;\n --color-api-added-border: #267334;\n --color-api-changed: #09b0ce;\n --color-api-changed-border: #056d80;\n --color-api-deprecated: #b1a10b;\n --color-api-deprecated-border: #6e6407;\n --color-api-removed: #ff7575;\n --color-api-removed-border: #b03b3b;\n\n // Admonitions\n --color-admonition-background: #18181a;\n\n // Cards\n --color-card-border: var(--color-background-secondary);\n --color-card-background: #18181a;\n --color-card-marginals-background: var(--color-background-hover);\n}\n","// This file contains the styling for making the content throughout the page,\n// including fonts, paragraphs, headings and spacing among these elements.\n\nbody\n font-family: var(--font-stack)\npre,\ncode,\nkbd,\nsamp\n font-family: var(--font-stack--monospace)\n\n// Make fonts look slightly nicer.\nbody\n -webkit-font-smoothing: antialiased\n -moz-osx-font-smoothing: grayscale\n\n// Line height from Bootstrap 4.1\narticle\n line-height: 1.5\n\n//\n// Headings\n//\nh1,\nh2,\nh3,\nh4,\nh5,\nh6\n line-height: 1.25\n font-family: var(--font-stack--headings)\n font-weight: bold\n\n border-radius: 0.5rem\n margin-top: 0.5rem\n margin-bottom: 0.5rem\n margin-left: -0.5rem\n margin-right: -0.5rem\n padding-left: 0.5rem\n padding-right: 0.5rem\n\n + p\n margin-top: 0\n\nh1\n font-size: 2.5em\n margin-top: 1.75rem\n margin-bottom: 1rem\nh2\n font-size: 2em\n margin-top: 1.75rem\nh3\n font-size: 1.5em\nh4\n font-size: 1.25em\nh5\n font-size: 1.125em\nh6\n font-size: 1em\n\nsmall\n opacity: 75%\n font-size: 80%\n\n// Paragraph\np\n margin-top: 0.5rem\n margin-bottom: 0.75rem\n\n// Horizontal rules\nhr.docutils\n height: 1px\n padding: 0\n margin: 2rem 0\n background-color: var(--color-background-border)\n border: 0\n\n.centered\n text-align: center\n\n// Links\na\n text-decoration: underline\n\n color: var(--color-link)\n text-decoration-color: var(--color-link-underline)\n\n &:visited\n color: var(--color-link--visited)\n text-decoration-color: var(--color-link-underline--visited)\n &:hover\n color: var(--color-link--visited--hover)\n text-decoration-color: var(--color-link-underline--visited--hover)\n\n &:hover\n color: var(--color-link--hover)\n text-decoration-color: var(--color-link-underline--hover)\n &.muted-link\n color: inherit\n &:hover\n color: var(--color-link--hover)\n text-decoration-color: var(--color-link-underline--hover)\n &:visited\n color: var(--color-link--visited--hover)\n text-decoration-color: var(--color-link-underline--visited--hover)\n","// This file contains the styles for the overall layouting of the documentation\n// skeleton, including the responsive changes as well as sidebar toggles.\n//\n// This is implemented as a mobile-last design, which isn't ideal, but it is\n// reasonably good-enough and I got pretty tired by the time I'd finished this\n// to move the rules around to fix this. Shouldn't take more than 3-4 hours,\n// if you know what you're doing tho.\n\n// HACK: Not all browsers account for the scrollbar width in media queries.\n// This results in horizontal scrollbars in the breakpoint where we go\n// from displaying everything to hiding the ToC. We accomodate for this by\n// adding a bit of padding to the TOC drawer, disabling the horizontal\n// scrollbar and allowing the scrollbars to cover the padding.\n// https://www.456bereastreet.com/archive/201301/media_query_width_and_vertical_scrollbars/\n\n// HACK: Always having the scrollbar visible, prevents certain browsers from\n// causing the content to stutter horizontally between taller-than-viewport and\n// not-taller-than-viewport pages.\n@use \"variables\" as *\n\nhtml\n overflow-x: hidden\n overflow-y: scroll\n scroll-behavior: smooth\n\n.sidebar-scroll, .toc-scroll, article[role=main] *\n scrollbar-width: thin\n scrollbar-color: var(--color-foreground-border) transparent\n\n//\n// Overalls\n//\nhtml,\nbody\n height: 100%\n color: var(--color-foreground-primary)\n background: var(--color-background-primary)\n\n.skip-to-content\n position: fixed\n padding: 1rem\n border-radius: 1rem\n left: 0.25rem\n top: 0.25rem\n z-index: 40\n background: var(--color-background-primary)\n color: var(--color-foreground-primary)\n\n transform: translateY(-200%)\n transition: transform 300ms ease-in-out\n\n &:focus-within\n transform: translateY(0%)\n\narticle\n color: var(--color-content-foreground)\n background: var(--color-content-background)\n overflow-wrap: break-word\n\n.page\n display: flex\n // fill the viewport for pages with little content.\n min-height: 100%\n\n.mobile-header\n width: 100%\n height: var(--header-height)\n background-color: var(--color-header-background)\n color: var(--color-header-text)\n border-bottom: 1px solid var(--color-header-border)\n\n // Looks like sub-script/super-script have this, and we need this to\n // be \"on top\" of those.\n z-index: 10\n\n // We don't show the header on large screens.\n display: none\n\n // Add shadow when scrolled\n &.scrolled\n border-bottom: none\n box-shadow: 0 0 0.2rem rgba(0, 0, 0, 0.1), 0 0.2rem 0.4rem rgba(0, 0, 0, 0.2)\n\n .header-center\n a\n color: var(--color-header-text)\n text-decoration: none\n\n.main\n display: flex\n flex: 1\n\n// Sidebar (left) also covers the entire left portion of screen.\n.sidebar-drawer\n box-sizing: border-box\n\n border-right: 1px solid var(--color-sidebar-background-border)\n background: var(--color-sidebar-background)\n\n display: flex\n justify-content: flex-end\n // These next two lines took me two days to figure out.\n width: calc((100% - #{$full-width}) / 2 + #{$sidebar-width})\n min-width: $sidebar-width\n\n// Scroll-along sidebars\n.sidebar-container,\n.toc-drawer\n box-sizing: border-box\n width: $sidebar-width\n\n.toc-drawer\n background: var(--color-toc-background)\n // See HACK described on top of this document\n padding-right: 1rem\n\n.sidebar-sticky,\n.toc-sticky\n position: sticky\n top: 0\n height: min(100%, 100vh)\n height: 100vh\n\n display: flex\n flex-direction: column\n\n.sidebar-scroll,\n.toc-scroll\n flex-grow: 1\n flex-shrink: 1\n\n overflow: auto\n scroll-behavior: smooth\n\n// Central items.\n.content\n padding: 0 $content-padding\n width: $content-width\n\n display: flex\n flex-direction: column\n justify-content: space-between\n\n.icon\n display: inline-block\n height: 1rem\n width: 1rem\n svg\n width: 100%\n height: 100%\n\n//\n// Accommodate announcement banner\n//\n.announcement\n background-color: var(--color-announcement-background)\n color: var(--color-announcement-text)\n\n height: var(--header-height)\n display: flex\n align-items: center\n overflow-x: auto\n & + .page\n min-height: calc(100% - var(--header-height))\n\n.announcement-content\n box-sizing: border-box\n padding: 0.5rem\n min-width: 100%\n white-space: nowrap\n text-align: center\n\n a\n color: var(--color-announcement-text)\n text-decoration-color: var(--color-announcement-text)\n\n &:hover\n color: var(--color-announcement-text)\n text-decoration-color: var(--color-link--hover)\n\n////////////////////////////////////////////////////////////////////////////////\n// Toggles for theme\n////////////////////////////////////////////////////////////////////////////////\n.no-js .theme-toggle-container // don't show theme toggle if there's no JS\n display: none\n\n.theme-toggle-container\n display: flex\n\n.theme-toggle\n display: flex\n cursor: pointer\n border: none\n padding: 0\n background: transparent\n\n.theme-toggle svg\n height: 1.25rem\n width: 1.25rem\n color: var(--color-foreground-primary)\n display: none\n\n.theme-toggle-header\n display: flex\n align-items: center\n justify-content: center\n\n////////////////////////////////////////////////////////////////////////////////\n// Toggles for elements\n////////////////////////////////////////////////////////////////////////////////\n.toc-overlay-icon, .nav-overlay-icon\n display: none\n cursor: pointer\n\n .icon\n color: var(--color-foreground-secondary)\n height: 1.5rem\n width: 1.5rem\n\n.toc-header-icon, .nav-overlay-icon\n // for when we set display: flex\n justify-content: center\n align-items: center\n\n.toc-content-icon\n height: 1.5rem\n width: 1.5rem\n\n.content-icon-container\n float: right\n display: flex\n margin-top: 1.5rem\n margin-left: 1rem\n margin-bottom: 1rem\n gap: 0.5rem\n\n .edit-this-page, .view-this-page\n svg\n color: inherit\n height: 1.25rem\n width: 1.25rem\n\n.sidebar-toggle\n position: absolute\n display: none\n// \n.sidebar-toggle[name=\"__toc\"]\n left: 20px\n.sidebar-toggle:checked\n left: 40px\n// \n\n.overlay\n position: fixed\n top: 0\n width: 0\n height: 0\n\n transition: width 0ms, height 0ms, opacity 250ms ease-out\n\n opacity: 0\n background-color: rgba(0, 0, 0, 0.54)\n.sidebar-overlay\n z-index: 20\n.toc-overlay\n z-index: 40\n\n// Keep things on top and smooth.\n.sidebar-drawer\n z-index: 30\n transition: left 250ms ease-in-out\n.toc-drawer\n z-index: 50\n transition: right 250ms ease-in-out\n\n// Show the Sidebar\n#__navigation:checked\n & ~ .sidebar-overlay\n width: 100%\n height: 100%\n opacity: 1\n & ~ .page\n .sidebar-drawer\n top: 0\n left: 0\n // Show the toc sidebar\n#__toc:checked\n & ~ .toc-overlay\n width: 100%\n height: 100%\n opacity: 1\n & ~ .page\n .toc-drawer\n top: 0\n right: 0\n\n////////////////////////////////////////////////////////////////////////////////\n// Back to top\n////////////////////////////////////////////////////////////////////////////////\n.back-to-top\n text-decoration: none\n\n display: none\n position: fixed\n left: 0\n top: 1rem\n padding: 0.5rem\n padding-right: 0.75rem\n border-radius: 1rem\n font-size: 0.8125rem\n\n background: var(--color-background-primary)\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), #6b728080 0px 0px 1px 0px\n\n z-index: 10\n\n margin-left: 50%\n transform: translateX(-50%)\n svg\n height: 1rem\n width: 1rem\n fill: currentColor\n display: inline-block\n\n span\n margin-left: 0.25rem\n\n .show-back-to-top &\n display: flex\n align-items: center\n\n////////////////////////////////////////////////////////////////////////////////\n// Responsive layouting\n////////////////////////////////////////////////////////////////////////////////\n// Make things a bit bigger on bigger screens.\n@media (min-width: $full-width + $sidebar-width)\n html\n font-size: 110%\n\n@media (max-width: $full-width)\n // Collapse \"toc\" into the icon.\n .toc-content-icon\n display: flex\n .toc-drawer\n position: fixed\n height: 100vh\n top: 0\n right: -$sidebar-width\n border-left: 1px solid var(--color-background-muted)\n .toc-tree\n border-left: none\n font-size: var(--toc-font-size--mobile)\n\n // Accomodate for a changed content width.\n .sidebar-drawer\n width: calc((100% - #{$full-width - $sidebar-width}) / 2 + #{$sidebar-width})\n\n@media (max-width: $content-padded-width + $sidebar-width)\n // Center the page\n .content\n margin-left: auto\n margin-right: auto\n padding: 0 $content-padding--small\n\n@media (max-width: $content-padded-width--small + $sidebar-width)\n // Collapse \"navigation\".\n .nav-overlay-icon\n display: flex\n .sidebar-drawer\n position: fixed\n height: 100vh\n width: $sidebar-width\n\n top: 0\n left: -$sidebar-width\n\n // Swap which icon is visible.\n .toc-header-icon, .theme-toggle-header\n display: flex\n .toc-content-icon, .theme-toggle-content\n display: none\n\n // Show the header.\n .mobile-header\n position: sticky\n top: 0\n display: flex\n justify-content: space-between\n align-items: center\n\n .header-left,\n .header-right\n display: flex\n height: var(--header-height)\n padding: 0 var(--header-padding)\n label\n height: 100%\n width: 100%\n user-select: none\n\n .nav-overlay-icon .icon,\n .theme-toggle svg\n height: 1.5rem\n width: 1.5rem\n\n // Add a scroll margin for the content\n :target\n scroll-margin-top: calc(var(--header-height) + 2.5rem)\n\n // Show back-to-top below the header\n .back-to-top\n top: calc(var(--header-height) + 0.5rem)\n\n // Accommodate for the header.\n .page\n flex-direction: column\n justify-content: center\n\n@media (max-width: $content-width + 2* $content-padding--small)\n // Content should respect window limits.\n .content\n width: 100%\n overflow-x: auto\n\n@media (max-width: $content-width)\n article[role=main] aside.sidebar\n float: none\n width: 100%\n margin: 1rem 0\n","@use \"sass:list\"\n@use \"../variables\" as *\n\n// The design here is strongly inspired by mkdocs-material.\n.admonition, .topic\n margin: 1rem auto\n padding: 0 0.5rem 0.5rem 0.5rem\n\n background: var(--color-admonition-background)\n\n border-radius: 0.2rem\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), 0 0 0.0625rem rgba(0, 0, 0, 0.1)\n\n font-size: var(--admonition-font-size)\n\n overflow: hidden\n page-break-inside: avoid\n\n // First element should have no margin, since the title has it.\n > :nth-child(2)\n margin-top: 0\n\n // Last item should have no margin, since we'll control that w/ padding\n > :last-child\n margin-bottom: 0\n\n.admonition p.admonition-title,\np.topic-title\n position: relative\n margin: 0 -0.5rem 0.5rem\n padding-left: 2rem\n padding-right: .5rem\n padding-top: .4rem\n padding-bottom: .4rem\n\n font-weight: 500\n font-size: var(--admonition-title-font-size)\n line-height: 1.3\n\n // Our fancy icon\n &::before\n content: \"\"\n position: absolute\n left: 0.5rem\n width: 1rem\n height: 1rem\n\n// Default styles\np.admonition-title\n background-color: var(--color-admonition-title-background)\n &::before\n background-color: var(--color-admonition-title)\n mask-image: var(--icon-admonition-default)\n mask-repeat: no-repeat\n\np.topic-title\n background-color: var(--color-topic-title-background)\n &::before\n background-color: var(--color-topic-title)\n mask-image: var(--icon-topic-default)\n mask-repeat: no-repeat\n\n//\n// Variants\n//\n.admonition\n border-left: 0.2rem solid var(--color-admonition-title)\n\n @each $type, $value in $admonitions\n &.#{$type}\n border-left-color: var(--color-admonition-title--#{$type})\n > .admonition-title\n background-color: var(--color-admonition-title-background--#{$type})\n &::before\n background-color: var(--color-admonition-title--#{$type})\n mask-image: var(--icon-#{list.nth($value, 2)})\n\n.admonition-todo > .admonition-title\n text-transform: uppercase\n","// This file stylizes the API documentation (stuff generated by autodoc). It's\n// deeply nested due to how autodoc structures the HTML without enough classes\n// to select the relevant items.\n\n// API docs!\ndl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple)\n // Tweak the spacing of all the things!\n dd\n margin-left: 2rem\n > :first-child\n margin-top: 0.125rem\n > :last-child\n margin-bottom: 0.75rem\n\n // This is used for the arguments\n .field-list\n margin-bottom: 0.75rem\n\n // \"Headings\" (like \"Parameters\" and \"Return\")\n > dt\n text-transform: uppercase\n font-size: var(--font-size--small)\n\n dd:empty\n margin-bottom: 0.5rem\n dd > ul\n margin-left: -1.2rem\n > li\n > p:nth-child(2)\n margin-top: 0\n // When the last-empty-paragraph follows a paragraph, it doesn't need\n // to augument the existing spacing.\n > p + p:last-child:empty\n margin-top: 0\n margin-bottom: 0\n\n // Colorize the elements\n > dt\n color: var(--color-api-overall)\n\n.sig:not(.sig-inline)\n font-weight: bold\n\n font-size: var(--api-font-size)\n font-family: var(--font-stack--monospace)\n\n margin-left: -0.25rem\n margin-right: -0.25rem\n padding-top: 0.25rem\n padding-bottom: 0.25rem\n padding-right: 0.5rem\n\n // These are intentionally em, to properly match the font size.\n padding-left: 3em\n text-indent: -2.5em\n\n border-radius: 0.25rem\n\n background: var(--color-api-background)\n transition: background 100ms ease-out\n\n &:hover\n background: var(--color-api-background-hover)\n\n // adjust the size of the [source] link on the right.\n a.reference\n .viewcode-link\n font-weight: normal\n width: 4.25rem\n\nem.property\n font-style: normal\n &:first-child\n color: var(--color-api-keyword)\n.sig-name\n color: var(--color-api-name)\n.sig-prename\n font-weight: normal\n color: var(--color-api-pre-name)\n.sig-paren\n color: var(--color-api-paren)\n.sig-param\n font-style: normal\n\ndiv.versionadded,\ndiv.versionchanged,\ndiv.deprecated,\ndiv.versionremoved\n border-left: 0.1875rem solid\n border-radius: 0.125rem\n\n padding-left: 0.75rem\n\n p\n margin-top: 0.125rem\n margin-bottom: 0.125rem\n\ndiv.versionadded\n border-color: var(--color-api-added-border)\n .versionmodified\n color: var(--color-api-added)\n\ndiv.versionchanged\n border-color: var(--color-api-changed-border)\n .versionmodified\n color: var(--color-api-changed)\n\ndiv.deprecated\n border-color: var(--color-api-deprecated-border)\n .versionmodified\n color: var(--color-api-deprecated)\n\ndiv.versionremoved\n border-color: var(--color-api-removed-border)\n .versionmodified\n color: var(--color-api-removed)\n\n// Align the [docs] and [source] to the right.\n.viewcode-link, .viewcode-back\n float: right\n text-align: right\n",".line-block\n margin-top: 0.5rem\n margin-bottom: 0.75rem\n .line-block\n margin-top: 0rem\n margin-bottom: 0rem\n padding-left: 1rem\n","// Captions\narticle p.caption,\ntable > caption,\n.code-block-caption\n font-size: var(--font-size--small)\n text-align: center\n\n// Caption above a TOCTree\n.toctree-wrapper.compound\n .caption, :not(.caption) > .caption-text\n font-size: var(--font-size--small)\n text-transform: uppercase\n\n text-align: initial\n margin-bottom: 0\n\n > ul\n margin-top: 0\n margin-bottom: 0\n","// Inline code\ncode.literal, .sig-inline\n background: var(--color-inline-code-background)\n border-radius: 0.2em\n // Make the font smaller, and use padding to recover.\n font-size: var(--font-size--small--2)\n padding: 0.1em 0.2em\n\n pre.literal-block &\n font-size: inherit\n padding: 0\n\n p &\n border: 1px solid var(--color-background-border)\n\n.sig-inline\n font-family: var(--font-stack--monospace)\n\n// Code and Literal Blocks\n$code-spacing-vertical: 0.625rem\n$code-spacing-horizontal: 0.875rem\n\n// Wraps every literal block + line numbers.\ndiv[class*=\" highlight-\"],\ndiv[class^=\"highlight-\"]\n margin: 1em 0\n display: flex\n\n .table-wrapper\n margin: 0\n padding: 0\n\npre\n margin: 0\n padding: 0\n overflow: auto\n\n // Needed to have more specificity than pygments' \"pre\" selector. :(\n article[role=\"main\"] .highlight &\n line-height: 1.5\n\n &.literal-block,\n .highlight &\n font-size: var(--code-font-size)\n padding: $code-spacing-vertical $code-spacing-horizontal\n\n // Make it look like all the other blocks.\n &.literal-block\n margin-top: 1rem\n margin-bottom: 1rem\n\n border-radius: 0.2rem\n background-color: var(--color-code-background)\n color: var(--color-code-foreground)\n\n// All code is always contained in this.\n.highlight\n width: 100%\n border-radius: 0.2rem\n\n // Make line numbers and prompts un-selectable.\n .gp, span.linenos\n user-select: none\n pointer-events: none\n\n // Expand the line-highlighting.\n .hll\n display: block\n margin-left: -$code-spacing-horizontal\n margin-right: -$code-spacing-horizontal\n padding-left: $code-spacing-horizontal\n padding-right: $code-spacing-horizontal\n\n/* Make code block captions be nicely integrated */\n.code-block-caption\n display: flex\n padding: $code-spacing-vertical $code-spacing-horizontal\n\n border-radius: 0.25rem\n border-bottom-left-radius: 0\n border-bottom-right-radius: 0\n font-weight: 300\n border-bottom: 1px solid\n\n background-color: var(--color-code-background)\n color: var(--color-code-foreground)\n border-color: var(--color-background-border)\n\n + div[class]\n margin-top: 0\n > .highlight\n border-top-left-radius: 0\n border-top-right-radius: 0\n\n// When `html_codeblock_linenos_style` is table.\n.highlighttable\n width: 100%\n display: block\n tbody\n display: block\n\n tr\n display: flex\n\n // Line numbers\n td.linenos\n background-color: var(--color-code-background)\n color: var(--color-code-foreground)\n padding: $code-spacing-vertical $code-spacing-horizontal\n padding-right: 0\n border-top-left-radius: 0.2rem\n border-bottom-left-radius: 0.2rem\n\n .linenodiv\n padding-right: $code-spacing-horizontal\n font-size: var(--code-font-size)\n box-shadow: -0.0625rem 0 var(--color-foreground-border) inset\n\n // Actual code\n td.code\n padding: 0\n display: block\n flex: 1\n overflow: hidden\n\n .highlight\n border-top-left-radius: 0\n border-bottom-left-radius: 0\n\n// When `html_codeblock_linenos_style` is inline.\n.highlight\n span.linenos\n display: inline-block\n padding-left: 0\n padding-right: $code-spacing-horizontal\n margin-right: $code-spacing-horizontal\n box-shadow: -0.0625rem 0 var(--color-foreground-border) inset\n","// Inline Footnote Reference\n.footnote-reference\n font-size: var(--font-size--small--4)\n vertical-align: super\n\n// Definition list, listing the content of each note.\n// docutils <= 0.17\ndl.footnote.brackets\n font-size: var(--font-size--small)\n color: var(--color-foreground-secondary)\n\n display: grid\n grid-template-columns: max-content auto\n dt\n margin: 0\n > .fn-backref\n margin-left: 0.25rem\n\n &:after\n content: \":\"\n\n .brackets\n &:before\n content: \"[\"\n &:after\n content: \"]\"\n\n dd\n margin: 0\n padding: 0 1rem\n\n// docutils >= 0.18\naside.footnote\n font-size: var(--font-size--small)\n color: var(--color-foreground-secondary)\n\naside.footnote > span,\ndiv.citation > span\n float: left\n font-weight: 500\n padding-right: 0.25rem\n\naside.footnote > *:not(span),\ndiv.citation > p\n margin-left: 2rem\n","//\n// Figures\n//\nimg\n box-sizing: border-box\n max-width: 100%\n height: auto\n\narticle\n figure, .figure\n border-radius: 0.2rem\n\n margin: 0\n :last-child\n margin-bottom: 0\n\n .align-left\n float: left\n clear: left\n margin: 0 1rem 1rem\n\n .align-right\n float: right\n clear: right\n margin: 0 1rem 1rem\n\n .align-default,\n .align-center\n display: block\n text-align: center\n margin-left: auto\n margin-right: auto\n\n // WELL, table needs to be stylised like a table.\n table.align-default\n display: table\n text-align: initial\n",".genindex-jumpbox, .domainindex-jumpbox\n border-top: 1px solid var(--color-background-border)\n border-bottom: 1px solid var(--color-background-border)\n padding: 0.25rem\n\n.genindex-section, .domainindex-section\n h2\n margin-top: 0.75rem\n margin-bottom: 0.5rem\n ul\n margin-top: 0\n margin-bottom: 0\n","ul,\nol\n padding-left: 1.2rem\n\n // Space lists out like paragraphs\n margin-top: 1rem\n margin-bottom: 1rem\n // reduce margins within li.\n li\n > p:first-child\n margin-top: 0.25rem\n margin-bottom: 0.25rem\n\n > p:last-child\n margin-top: 0.25rem\n\n > ul,\n > ol\n margin-top: 0.5rem\n margin-bottom: 0.5rem\n\nol\n &.arabic\n list-style: decimal\n &.loweralpha\n list-style: lower-alpha\n &.upperalpha\n list-style: upper-alpha\n &.lowerroman\n list-style: lower-roman\n &.upperroman\n list-style: upper-roman\n\n// Don't space lists out when they're \"simple\" or in a `.. toctree::`\n.simple,\n.toctree-wrapper\n li\n > ul,\n > ol\n margin-top: 0\n margin-bottom: 0\n\n// Definition Lists\n.field-list,\n.option-list,\ndl:not([class]),\ndl.simple,\ndl.footnote,\ndl.glossary\n dt\n font-weight: 500\n margin-top: 0.25rem\n + dt\n margin-top: 0\n\n .classifier::before\n content: \":\"\n margin-left: 0.2rem\n margin-right: 0.2rem\n\n dd\n > p:first-child,\n ul\n margin-top: 0.125rem\n\n ul\n margin-bottom: 0.125rem\n",".math-wrapper\n width: 100%\n overflow-x: auto\n\ndiv.math\n position: relative\n text-align: center\n\n .headerlink,\n &:focus .headerlink\n display: none\n\n &:hover .headerlink\n display: inline-block\n\n span.eqno\n position: absolute\n right: 0.5rem\n top: 50%\n transform: translate(0, -50%)\n z-index: 1\n","// Abbreviations\nabbr[title]\n cursor: help\n\n// \"Problematic\" content, as identified by Sphinx\n.problematic\n color: var(--color-problematic)\n\n// Keyboard / Mouse \"instructions\"\nkbd:not(.compound)\n margin: 0 0.2rem\n padding: 0 0.2rem\n border-radius: 0.2rem\n border: 1px solid var(--color-foreground-border)\n color: var(--color-foreground-primary)\n vertical-align: text-bottom\n\n font-size: var(--font-size--small--3)\n display: inline-block\n\n box-shadow: 0 0.0625rem 0 rgba(0, 0, 0, 0.2), inset 0 0 0 0.125rem var(--color-background-primary)\n\n background-color: var(--color-background-secondary)\n\n// Blockquote\nblockquote\n border-left: 4px solid var(--color-background-border)\n background: var(--color-background-secondary)\n\n margin-left: 0\n margin-right: 0\n padding: 0.5rem 1rem\n\n .attribution\n font-weight: 600\n text-align: right\n\n &.pull-quote,\n &.highlights\n font-size: 1.25em\n\n &.epigraph,\n &.pull-quote\n border-left-width: 0\n border-radius: 0.5rem\n\n &.highlights\n border-left-width: 0\n background: transparent\n\n// Center align embedded-in-text images\np .reference img\n vertical-align: middle\n","p.rubric\n line-height: 1.25\n font-weight: bold\n font-size: 1.125em\n\n // For Numpy-style documentation that's got rubrics within it.\n // https://github.com/pradyunsg/furo/discussions/505\n dd &\n line-height: inherit\n font-weight: inherit\n\n font-size: var(--font-size--small)\n text-transform: uppercase\n","article .sidebar\n float: right\n clear: right\n width: 30%\n\n margin-left: 1rem\n margin-right: 0\n\n border-radius: 0.2rem\n background-color: var(--color-background-secondary)\n border: var(--color-background-border) 1px solid\n\n > *\n padding-left: 1rem\n padding-right: 1rem\n\n > ul, > ol // lists need additional padding, because bullets.\n padding-left: 2.2rem\n\n .sidebar-title\n margin: 0\n padding: 0.5rem 1rem\n border-bottom: var(--color-background-border) 1px solid\n\n font-weight: 500\n\n// TODO: subtitle\n// TODO: dedicated variables?\n","[role=main] .table-wrapper.container\n width: 100%\n overflow-x: auto\n margin-top: 1rem\n margin-bottom: 0.5rem\n padding: 0.2rem 0.2rem 0.75rem\n\ntable.docutils\n border-radius: 0.2rem\n border-spacing: 0\n border-collapse: collapse\n\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), 0 0 0.0625rem rgba(0, 0, 0, 0.1)\n\n th\n background: var(--color-table-header-background)\n\n td,\n th\n // Space things out properly\n padding: 0 0.25rem\n\n // Get the borders looking just-right.\n border-left: 1px solid var(--color-table-border)\n border-right: 1px solid var(--color-table-border)\n border-bottom: 1px solid var(--color-table-border)\n\n p\n margin: 0.25rem\n\n &:first-child\n border-left: none\n &:last-child\n border-right: none\n\n // MyST-parser tables set these classes for control of column alignment\n &.text-left\n text-align: left\n &.text-right\n text-align: right\n &.text-center\n text-align: center\n","@use \"../variables\" as *\n\n:target\n scroll-margin-top: 2.5rem\n\n@media (max-width: $full-width - $sidebar-width)\n :target\n scroll-margin-top: calc(2.5rem + var(--header-height))\n\n // When a heading is selected\n section > span:target\n scroll-margin-top: calc(2.8rem + var(--header-height))\n\n// Permalinks\n.headerlink\n font-weight: 100\n user-select: none\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\ndl dt,\np.caption,\nfigcaption p,\ntable > caption,\n.code-block-caption\n > .headerlink\n margin-left: 0.5rem\n visibility: hidden\n &:hover > .headerlink\n visibility: visible\n\n // Don't change to link-like, if someone adds the contents directive.\n > .toc-backref\n color: inherit\n text-decoration-line: none\n\n// Figure and table captions are special.\nfigure:hover > figcaption > p > .headerlink,\ntable:hover > caption > .headerlink\n visibility: visible\n\n:target >, // Regular section[id] style anchors\nspan:target ~ // Non-regular span[id] style \"extra\" anchors\n h1,\n h2,\n h3,\n h4,\n h5,\n h6\n &:nth-of-type(1)\n background-color: var(--color-highlight-on-target)\n // .headerlink\n // visibility: visible\n code.literal\n background-color: transparent\n\ntable:target > caption,\nfigure:target\n background-color: var(--color-highlight-on-target)\n\n// Inline page contents\n.this-will-duplicate-information-and-it-is-still-useful-here li :target\n background-color: var(--color-highlight-on-target)\n\n// Code block permalinks\n.literal-block-wrapper:target .code-block-caption\n background-color: var(--color-highlight-on-target)\n\n// When a definition list item is selected\n//\n// There isn't really an alternative to !important here, due to the\n// high-specificity of API documentation's selector.\ndt:target\n background-color: var(--color-highlight-on-target) !important\n\n// When a footnote reference is selected\n.footnote > dt:target + dd,\n.footnote-reference:target\n background-color: var(--color-highlight-on-target)\n",".guilabel\n background-color: var(--color-guilabel-background)\n border: 1px solid var(--color-guilabel-border)\n color: var(--color-guilabel-text)\n\n padding: 0 0.3em\n border-radius: 0.5em\n font-size: 0.9em\n","// This file contains the styles used for stylizing the footer that's shown\n// below the content.\n@use \"../variables\" as *\n\nfooter\n font-size: var(--font-size--small)\n display: flex\n flex-direction: column\n\n margin-top: 2rem\n\n// Bottom of page information\n.bottom-of-page\n display: flex\n align-items: center\n justify-content: space-between\n\n margin-top: 1rem\n padding-top: 1rem\n padding-bottom: 1rem\n\n color: var(--color-foreground-secondary)\n border-top: 1px solid var(--color-background-border)\n\n line-height: 1.5\n\n @media (max-width: $content-width)\n text-align: center\n flex-direction: column-reverse\n gap: 0.25rem\n\n .left-details\n font-size: var(--font-size--small)\n\n .right-details\n display: flex\n flex-direction: column\n gap: 0.25rem\n text-align: right\n\n .icons\n display: flex\n justify-content: flex-end\n gap: 0.25rem\n font-size: 1rem\n\n a\n text-decoration: none\n\n svg,\n img\n font-size: 1.125rem\n height: 1em\n width: 1em\n\n// Next/Prev page information\n.related-pages\n a\n display: flex\n align-items: center\n\n text-decoration: none\n &:hover .page-info .title\n text-decoration: underline\n color: var(--color-link)\n text-decoration-color: var(--color-link-underline)\n\n svg.furo-related-icon,\n svg.furo-related-icon > use\n flex-shrink: 0\n\n color: var(--color-foreground-border)\n\n width: 0.75rem\n height: 0.75rem\n margin: 0 0.5rem\n\n &.next-page\n max-width: 50%\n\n float: right\n clear: right\n text-align: right\n\n &.prev-page\n max-width: 50%\n\n float: left\n clear: left\n\n svg\n transform: rotate(180deg)\n\n.page-info\n display: flex\n flex-direction: column\n overflow-wrap: anywhere\n\n .next-page &\n align-items: flex-end\n\n .context\n display: flex\n align-items: center\n\n padding-bottom: 0.1rem\n\n color: var(--color-foreground-muted)\n font-size: var(--font-size--small)\n text-decoration: none\n","// This file contains the styles for the contents of the left sidebar, which\n// contains the navigation tree, logo, search etc.\n\n////////////////////////////////////////////////////////////////////////////////\n// Brand on top of the scrollable tree.\n////////////////////////////////////////////////////////////////////////////////\n.sidebar-brand\n display: flex\n flex-direction: column\n flex-shrink: 0\n\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n text-decoration: none\n\n.sidebar-brand-text\n color: var(--color-sidebar-brand-text)\n overflow-wrap: break-word\n margin: var(--sidebar-item-spacing-vertical) 0\n font-size: 1.5rem\n\n.sidebar-logo-container\n margin: var(--sidebar-item-spacing-vertical) 0\n\n.sidebar-logo\n margin: 0 auto\n display: block\n max-width: 100%\n\n////////////////////////////////////////////////////////////////////////////////\n// Search\n////////////////////////////////////////////////////////////////////////////////\n.sidebar-search-container\n display: flex\n align-items: center\n margin-top: var(--sidebar-search-space-above)\n\n position: relative\n\n background: var(--color-sidebar-search-background)\n &:hover,\n &:focus-within\n background: var(--color-sidebar-search-background--focus)\n\n &::before\n content: \"\"\n position: absolute\n left: var(--sidebar-item-spacing-horizontal)\n width: var(--sidebar-search-icon-size)\n height: var(--sidebar-search-icon-size)\n\n background-color: var(--color-sidebar-search-icon)\n mask-image: var(--icon-search)\n\n.sidebar-search\n box-sizing: border-box\n\n border: none\n border-top: 1px solid var(--color-sidebar-search-border)\n border-bottom: 1px solid var(--color-sidebar-search-border)\n\n padding-top: var(--sidebar-search-input-spacing-vertical)\n padding-bottom: var(--sidebar-search-input-spacing-vertical)\n padding-right: var(--sidebar-search-input-spacing-horizontal)\n padding-left: calc(var(--sidebar-item-spacing-horizontal) + var(--sidebar-search-input-spacing-horizontal) + var(--sidebar-search-icon-size))\n\n width: 100%\n\n color: var(--color-sidebar-search-foreground)\n background: transparent\n z-index: 10\n\n &:focus\n outline: none\n\n &::placeholder\n font-size: var(--sidebar-search-input-font-size)\n\n//\n// Hide Search Matches link\n//\n#searchbox .highlight-link\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal) 0\n margin: 0\n text-align: center\n\n a\n color: var(--color-sidebar-search-icon)\n font-size: var(--font-size--small--2)\n\n////////////////////////////////////////////////////////////////////////////////\n// Structure/Skeleton of the navigation tree (left)\n////////////////////////////////////////////////////////////////////////////////\n.sidebar-tree\n font-size: var(--sidebar-item-font-size)\n margin-top: var(--sidebar-tree-space-above)\n margin-bottom: var(--sidebar-item-spacing-vertical)\n\n ul\n padding: 0\n margin-top: 0\n margin-bottom: 0\n\n display: flex\n flex-direction: column\n\n list-style: none\n\n li\n position: relative\n margin: 0\n\n > ul\n margin-left: var(--sidebar-item-spacing-horizontal)\n\n .icon\n color: var(--color-sidebar-link-text)\n\n .reference\n box-sizing: border-box\n color: var(--color-sidebar-link-text)\n\n // Fill the parent.\n display: inline-block\n line-height: var(--sidebar-item-line-height)\n text-decoration: none\n\n // Don't allow long words to cause wrapping.\n overflow-wrap: anywhere\n\n height: 100%\n width: 100%\n\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n\n &:hover\n color: var(--color-sidebar-link-text)\n background: var(--color-sidebar-item-background--hover)\n\n // Add a nice little \"external-link\" arrow here.\n &.external::after\n content: url('data:image/svg+xml,')\n margin: 0 0.25rem\n vertical-align: middle\n color: var(--color-sidebar-link-text)\n\n // Make the current page reference bold.\n .current-page > .reference\n font-weight: bold\n\n label\n position: absolute\n top: 0\n right: 0\n height: var(--sidebar-item-height)\n width: var(--sidebar-expander-width)\n\n cursor: pointer\n user-select: none\n\n display: flex\n justify-content: center\n align-items: center\n\n .caption, :not(.caption) > .caption-text\n font-size: var(--sidebar-caption-font-size)\n color: var(--color-sidebar-caption-text)\n\n font-weight: bold\n text-transform: uppercase\n\n margin: var(--sidebar-caption-space-above) 0 0 0\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n\n // If it has children, add a bit more padding to wrap the content to avoid\n // overlapping with the