From 31f6d74a758e220e129bb9a588d51233fda27415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Stucke?= Date: Mon, 10 Jun 2024 16:58:54 +0200 Subject: [PATCH 01/46] added Hasura admin secret to config --- src/config.py | 4 ++++ src/config/fact-core-config.toml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/config.py b/src/config.py index 599bf6dda..4401de7dc 100644 --- a/src/config.py +++ b/src/config.py @@ -108,6 +108,10 @@ class Authentication(BaseModel): radare2_url: str + class Hasura(BaseModel): + model_config = ConfigDict(extra='forbid') + admin_secret: str + class Backend(Common): model_config = ConfigDict(extra='forbid') diff --git a/src/config/fact-core-config.toml b/src/config/fact-core-config.toml index 3f45d96fc..e3f72df37 100644 --- a/src/config/fact-core-config.toml +++ b/src/config/fact-core-config.toml @@ -169,3 +169,7 @@ radare2-url = "http://localhost:8000" enabled = false user-database = "sqlite:////media/data/fact_auth_data/fact_users.db" password-salt = "5up3r5tr0n6_p455w0rd_5417" + + +[frontend.hasura] +admin-secret = "4dM1n_S3cR3T_changemeplz" From 21b6c6eff597365a3d7fa26d4318054ac6c5c6ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Stucke?= Date: Mon, 10 Jun 2024 17:03:54 +0200 Subject: [PATCH 02/46] added hasura init script to installation --- src/graphql/hasura/init_hasura.py | 156 ++++++++++++++++++++++++++++++ src/install/frontend.py | 26 ++++- 2 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 src/graphql/hasura/init_hasura.py diff --git a/src/graphql/hasura/init_hasura.py b/src/graphql/hasura/init_hasura.py new file mode 100644 index 000000000..e4ee60514 --- /dev/null +++ b/src/graphql/hasura/init_hasura.py @@ -0,0 +1,156 @@ +from __future__ import annotations + +import logging +import sys +from pathlib import Path + +import docker +import requests +from requests import Response + +try: + import config +except ImportError: + SRC_DIR = Path(__file__).parent.parent.parent + sys.path.append(str(SRC_DIR)) + import config + +HTML_OK = 200 +HTML_BAD_REQUEST = 400 +HASURA_PORT = 8080 +DB_NAME = 'fact_db' +URL = f'http://localhost:{HASURA_PORT}/v1/metadata' +HEADERS = { + 'Content-Type': 'application/json', + 'X-Hasura-Role': 'admin', + 'X-Hasura-Admin-Secret': config.frontend.Hasura.admin_secret, +} +client = docker.from_env() +config.load() + +TRACKED_TABLES = ('analysis', 'file_object', 'firmware', 'fw_files', 'included_files', 'virtual_file_path') +RELATIONSHIPS = { + 'pg_create_object_relationship': [ + # table, name, constraint + ('analysis', 'file_object', 'uid'), + ('firmware', 'file_object', 'uid'), + ('fw_files', 'fileObjectByRootUid', 'root_uid'), + ('fw_files', 'fileObjectByFileUid', 'file_uid'), + ('fw_files', 'file_object', 'root_uid'), + ('included_files', 'fileObjectByParentUid', 'parent_uid'), + ('included_files', 'file_object', 'child_uid'), + ('file_object', 'firmware', {'column': 'uid', 'table': {'name': 'firmware', 'schema': 'public'}}), + ], + 'pg_create_array_relationship': [ + ('file_object', 'analyses', {'column': 'uid', 'table': {'name': 'analysis', 'schema': 'public'}}), + ('file_object', 'fwFilesByRootUid', {'column': 'root_uid', 'table': {'name': 'fw_files', 'schema': 'public'}}), + ('file_object', 'fw_files', {'column': 'file_uid', 'table': {'name': 'fw_files', 'schema': 'public'}}), + ( + 'file_object', + 'includedFilesByParentUid', + {'column': 'parent_uid', 'table': {'name': 'included_files', 'schema': 'public'}}, + ), + ( + 'file_object', + 'included_files', + {'column': 'child_uid', 'table': {'name': 'included_files', 'schema': 'public'}}, + ), + ( + 'file_object', + 'virtualFilePathsByParentUid', + {'column': 'parent_uid', 'table': {'name': 'virtual_file_path', 'schema': 'public'}}, + ), + ( + 'file_object', + 'virtual_file_paths', + {'column': 'file_uid', 'table': {'name': 'virtual_file_path', 'schema': 'public'}}, + ), + ], +} + + +def init_hasura(): + logging.info('Initializing Hasura...') + if not _db_was_already_added(): + _add_database() + _track_tables() + _add_relationships() + logging.info('Hasura initialization successful') + + +def _add_database(): + query = { + 'type': 'pg_add_source', + 'args': { + 'name': DB_NAME, + 'configuration': { + 'connection_info': { + 'database_url': {'from_env': 'FACT_DB_URL'}, + 'pool_settings': {'retries': 1, 'idle_timeout': 180, 'max_connections': 50}, + }, + }, + }, + } + response = requests.post(URL, headers=HEADERS, json=query) + if response.status_code != HTML_OK: + logging.error(f'Failed to add database: {response.text}') + sys.exit(5) + + +def _track_tables(): + for table in TRACKED_TABLES: + query = { + 'type': 'pg_track_table', + 'args': { + 'source': DB_NAME, + 'table': table, + }, + } + response = requests.post(URL, headers=HEADERS, json=query) + if response.status_code != HTML_OK: + if _was_already_added(response): + continue + logging.error(f'Failed to track table {table}: {response.text}') + sys.exit(6) + + +def _add_relationships(): + for action, relation_list in RELATIONSHIPS.items(): + for table, name, constraint in relation_list: + query = { + 'type': action, + 'args': { + 'table': table, + 'name': name, + 'source': DB_NAME, + 'using': {'foreign_key_constraint_on': constraint}, + }, + } + response = requests.post(URL, headers=HEADERS, json=query) + if response.status_code != HTML_OK: + if _was_already_added(response): + continue + logging.error(f'Failed to add constraint {name} on table {table}: {response.text}') + sys.exit(7) + + +def _was_already_added(response: Response) -> bool: + data = response.json() + return isinstance(data, dict) and 'error' in data and data.get('code') in ('already-exists', 'already-tracked') + + +def _db_was_already_added() -> bool: + query = {'type': 'pg_get_source_tables', 'args': {'source': DB_NAME}} + response = requests.post(URL, headers=HEADERS, json=query) + if response.status_code not in {HTML_OK, HTML_BAD_REQUEST}: + logging.error('No connection to Hasura API. Is it running?') + sys.exit(4) + data = response.json() + if isinstance(data, dict) and 'error' in data: + return False + return True + + +if __name__ == '__main__': + init_hasura() + sys.exit(0) diff --git a/src/install/frontend.py b/src/install/frontend.py index 4bf13e10b..f8424f35c 100644 --- a/src/install/frontend.py +++ b/src/install/frontend.py @@ -102,7 +102,8 @@ def _configure_nginx(): # copy is better on redhat to respect selinux context '(cd ../config && sudo install -m 644 $PWD/nginx.conf /etc/nginx/nginx.conf)', '(sudo mkdir /etc/nginx/error || true)', - '(cd ../web_interface/templates/ && sudo ln -s $PWD/maintenance.html /etc/nginx/error/maintenance.html) || true', # noqa: E501 + '(cd ../web_interface/templates/ ' + '&& sudo ln -s $PWD/maintenance.html /etc/nginx/error/maintenance.html) || true', ], error='configuring nginx', ) @@ -141,6 +142,27 @@ def _copy_mime_icons(): run_cmd_with_logging(f'cp -rL {ICON_THEME_INSTALL_PATH / source} {MIME_ICON_DIR / target}') +def _init_graphql(): + user = config.common.postgres.rw_user + pw = config.common.postgres.rw_pw + port = config.common.postgres.port + server = config.common.postgres.server + if server in ('localhost', '127.0.0.1', '::1'): + # if postgres is running on the host, the host is available through this special address (which represents the + # gateway address of the internal docker network) + server = 'host.docker.internal' + with OperateInDirectory(INSTALL_DIR.parent / 'graphql' / 'hasura'): + run_cmd_with_logging( + 'docker-compose up -d', + env={ + **os.environ, + 'HASURA_ADMIN_SECRET': config.frontend.Hasura.admin_secret, + 'FACT_DB_URL': f'postgresql://{user}:{pw}@{server}:{port} / fact_db', + }, + ) + run_cmd_with_logging('python3 init_hasura.py') + + def main(skip_docker, radare, nginx, distribution): if distribution != 'fedora': pkgs = read_package_list_from_file(INSTALL_DIR / 'apt-pkgs-frontend.txt') @@ -170,6 +192,8 @@ def main(skip_docker, radare, nginx, distribution): if not skip_docker: _install_docker_images(radare) + _init_graphql() + if not MIME_ICON_DIR.is_dir(): MIME_ICON_DIR.mkdir() _copy_mime_icons() From 7cb746e004b65e81386bdc351c42475d5dac46b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Stucke?= Date: Mon, 10 Jun 2024 17:04:22 +0200 Subject: [PATCH 03/46] added hasura poc compose yaml --- src/graphql/hasura/docker-compose.yaml | 46 ++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/graphql/hasura/docker-compose.yaml diff --git a/src/graphql/hasura/docker-compose.yaml b/src/graphql/hasura/docker-compose.yaml new file mode 100644 index 000000000..29c7cf38e --- /dev/null +++ b/src/graphql/hasura/docker-compose.yaml @@ -0,0 +1,46 @@ +version: "3.6" +services: + # the postgres instance is only for Hasura to store its metadata and should not be available from outside + postgres: + image: postgres:15 + restart: always + volumes: + - db_data:/var/lib/postgresql/data + environment: + POSTGRES_PASSWORD: postgrespassword + graphql-engine: + image: hasura/graphql-engine:v2.38.0 + ports: + - "8080:8080" + restart: always + extra_hosts: + - "host.docker.internal:host-gateway" + environment: + HASURA_GRAPHQL_METADATA_DATABASE_URL: postgres://postgres:postgrespassword@postgres:5432/postgres + PG_DATABASE_URL: postgres://postgres:postgrespassword@postgres:5432/postgres + HASURA_GRAPHQL_ENABLE_CONSOLE: "true" + HASURA_GRAPHQL_ENABLED_LOG_TYPES: startup, http-log, webhook-log, websocket-log, query-log + HASURA_GRAPHQL_CONSOLE_ASSETS_DIR: /srv/console-assets + HASURA_GRAPHQL_METADATA_DEFAULTS: '{"backend_configs":{"dataconnector":{"athena":{"uri":"http://data-connector-agent:8081/api/v1/athena"},"mariadb":{"uri":"http://data-connector-agent:8081/api/v1/mariadb"},"mysql8":{"uri":"http://data-connector-agent:8081/api/v1/mysql"},"oracle":{"uri":"http://data-connector-agent:8081/api/v1/oracle"},"snowflake":{"uri":"http://data-connector-agent:8081/api/v1/snowflake"}}}}' + # should be set during init + FACT_DB_URL: "${FACT_DB_URL}" + HASURA_GRAPHQL_ADMIN_SECRET: "${HASURA_ADMIN_SECRET}" + depends_on: + data-connector-agent: + condition: service_healthy + data-connector-agent: + image: hasura/graphql-data-connector:v2.38.0 + restart: always + ports: + - "8081:8081" + environment: + QUARKUS_LOG_LEVEL: ERROR + QUARKUS_OPENTELEMETRY_ENABLED: "false" + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8081/api/v1/athena/health"] + interval: 5s + timeout: 10s + retries: 5 + start_period: 5s +volumes: + db_data: From d003cbbb049400c13c3acbcd5142a35a6e7eb302 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Stucke?= Date: Mon, 10 Jun 2024 17:05:55 +0200 Subject: [PATCH 04/46] added poc route & template --- .../components/database_routes.py | 12 ++++++ .../templates/database/database_graphql.html | 39 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 src/web_interface/templates/database/database_graphql.html diff --git a/src/web_interface/components/database_routes.py b/src/web_interface/components/database_routes.py index a0d32f232..6ae019b54 100644 --- a/src/web_interface/components/database_routes.py +++ b/src/web_interface/components/database_routes.py @@ -2,6 +2,7 @@ import logging from datetime import datetime from itertools import chain +from pathlib import Path from flask import redirect, render_template, request, url_for from sqlalchemy.exc import SQLAlchemyError @@ -280,3 +281,14 @@ def start_quick_search(self): } } return redirect(url_for('browse_database', query=json.dumps(query))) + + @roles_accepted(*PRIVILEGES['advanced_search']) + @AppRoute('/graphql', GET) + def get_graphql(self): + return render_template('database/database_graphql.html') + + @roles_accepted(*PRIVILEGES['advanced_search']) + @AppRoute('/graphql/schema', GET) + def get_graphql_schema(self): + schema_path = Path(__file__).parent.parent / 'static' / 'graphql' / 'schema.graphql' + return schema_path.read_text() diff --git a/src/web_interface/templates/database/database_graphql.html b/src/web_interface/templates/database/database_graphql.html new file mode 100644 index 000000000..f35bcf375 --- /dev/null +++ b/src/web_interface/templates/database/database_graphql.html @@ -0,0 +1,39 @@ +{% extends "base.html" %} + +{% set active_page = "Database" %} + + +{% block head %} + + + +{% endblock %} + +{% block styles %} +{% endblock %} + + +{% block body %} + +
+ +
+

Advanced Search with GraphQL

+ + + +
+
+ +{% endblock %} From 19ee40090f4056752c025e1489208648ba7baa60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Stucke?= Date: Tue, 11 Jun 2024 17:41:49 +0200 Subject: [PATCH 05/46] fix hasura config --- src/config.py | 3 +++ src/graphql/hasura/docker-compose.yaml | 3 +-- src/graphql/hasura/init_hasura.py | 7 +++---- src/install/frontend.py | 7 ++++--- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/config.py b/src/config.py index 4401de7dc..3f8b67d5d 100644 --- a/src/config.py +++ b/src/config.py @@ -108,9 +108,12 @@ class Authentication(BaseModel): radare2_url: str + hasura: Frontend.Hasura + class Hasura(BaseModel): model_config = ConfigDict(extra='forbid') admin_secret: str + port: int = 33_333 class Backend(Common): diff --git a/src/graphql/hasura/docker-compose.yaml b/src/graphql/hasura/docker-compose.yaml index 29c7cf38e..5895e0ed2 100644 --- a/src/graphql/hasura/docker-compose.yaml +++ b/src/graphql/hasura/docker-compose.yaml @@ -1,4 +1,3 @@ -version: "3.6" services: # the postgres instance is only for Hasura to store its metadata and should not be available from outside postgres: @@ -11,7 +10,7 @@ services: graphql-engine: image: hasura/graphql-engine:v2.38.0 ports: - - "8080:8080" + - "${HASURA_PORT}:8080" restart: always extra_hosts: - "host.docker.internal:host-gateway" diff --git a/src/graphql/hasura/init_hasura.py b/src/graphql/hasura/init_hasura.py index e4ee60514..8d14122cb 100644 --- a/src/graphql/hasura/init_hasura.py +++ b/src/graphql/hasura/init_hasura.py @@ -15,18 +15,17 @@ sys.path.append(str(SRC_DIR)) import config +config.load() HTML_OK = 200 HTML_BAD_REQUEST = 400 -HASURA_PORT = 8080 DB_NAME = 'fact_db' -URL = f'http://localhost:{HASURA_PORT}/v1/metadata' +URL = f'http://localhost:{config.frontend.hasura.port}/v1/metadata' HEADERS = { 'Content-Type': 'application/json', 'X-Hasura-Role': 'admin', - 'X-Hasura-Admin-Secret': config.frontend.Hasura.admin_secret, + 'X-Hasura-Admin-Secret': config.frontend.hasura.admin_secret, } client = docker.from_env() -config.load() TRACKED_TABLES = ('analysis', 'file_object', 'firmware', 'fw_files', 'included_files', 'virtual_file_path') RELATIONSHIPS = { diff --git a/src/install/frontend.py b/src/install/frontend.py index f8424f35c..ca9ab5039 100644 --- a/src/install/frontend.py +++ b/src/install/frontend.py @@ -153,11 +153,12 @@ def _init_graphql(): server = 'host.docker.internal' with OperateInDirectory(INSTALL_DIR.parent / 'graphql' / 'hasura'): run_cmd_with_logging( - 'docker-compose up -d', + 'docker compose up -d', env={ **os.environ, - 'HASURA_ADMIN_SECRET': config.frontend.Hasura.admin_secret, - 'FACT_DB_URL': f'postgresql://{user}:{pw}@{server}:{port} / fact_db', + 'HASURA_ADMIN_SECRET': config.frontend.hasura.admin_secret, + 'FACT_DB_URL': f'postgresql://{user}:{pw}@{server}:{port}/fact_db', + 'HASURA_PORT': str(config.frontend.hasura.port), }, ) run_cmd_with_logging('python3 init_hasura.py') From 8d2a4c21e603e87a644b1cfd24c43215a25330f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Stucke?= Date: Tue, 11 Jun 2024 17:42:49 +0200 Subject: [PATCH 06/46] update postgres installation for hasura --- src/install/db.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/install/db.py b/src/install/db.py index 60fc749f9..81c456903 100644 --- a/src/install/db.py +++ b/src/install/db.py @@ -35,7 +35,21 @@ def install_postgres(version: int = 14): # increase the maximum number of concurrent connections (and restart for the change to take effect) config_path = f'/etc/postgresql/{version}/main/postgresql.conf' + hba_config_path = f'/etc/postgresql/{version}/main/pg_hba.conf' + run(f'sudo chmod 644 {hba_config_path}', shell=True, check=True) + if '192.168.0.0/16' not in Path(hba_config_path).read_text(): + # for whatever reason, the local address ranges 10.0.0.0/8 and 192.168.0.0/16 are (contrary to 172.8.0.0/12) not + # per default included in the list of allowed peer addresses for postgres, so we need to add it to pg_hba.conf, + # so that the DB may be accessed from docker containers which sometimes get an address from this range + for ip_range in ['192.168.0.0/16', '10.0.0.0/8 ']: + run(f'echo "host all all {ip_range} scram-sha-256" | sudo tee -a {hba_config_path}', shell=True, check=True) run(f'sudo sed -i -E "s/max_connections = [0-9]+/max_connections = 999/g" {config_path}', shell=True, check=True) + # set listen address from localhost to '*' (0.0.0.0) so that connections from docker containers are accepted + run( + f"sudo sed -i -E \"s/#? *listen_addresses = 'localhost'/listen_addresses = '\\*'/g\" {config_path}", + shell=True, + check=True, + ) run('sudo service postgresql restart', shell=True, check=True) From 81aba9ffb723c0a6f429bb859c25d2ccec8f8c6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Stucke?= Date: Tue, 11 Jun 2024 17:43:21 +0200 Subject: [PATCH 07/46] update graphql db routes --- src/web_interface/components/database_routes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/web_interface/components/database_routes.py b/src/web_interface/components/database_routes.py index 6ae019b54..be13539cf 100644 --- a/src/web_interface/components/database_routes.py +++ b/src/web_interface/components/database_routes.py @@ -283,12 +283,12 @@ def start_quick_search(self): return redirect(url_for('browse_database', query=json.dumps(query))) @roles_accepted(*PRIVILEGES['advanced_search']) - @AppRoute('/graphql', GET) + @AppRoute('/database/graphql', GET) def get_graphql(self): return render_template('database/database_graphql.html') @roles_accepted(*PRIVILEGES['advanced_search']) - @AppRoute('/graphql/schema', GET) + @AppRoute('/database/graphql/schema', GET) def get_graphql_schema(self): schema_path = Path(__file__).parent.parent / 'static' / 'graphql' / 'schema.graphql' return schema_path.read_text() From 118201ae307f83f2ceee91e65d260205662f31a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Stucke?= Date: Tue, 11 Jun 2024 17:43:58 +0200 Subject: [PATCH 08/46] update graphql template and deps --- src/web_interface/static/package-lock.json | 1799 +++++++++++++++-- src/web_interface/static/package.json | 5 + .../templates/database/database_graphql.html | 85 +- 3 files changed, 1738 insertions(+), 151 deletions(-) diff --git a/src/web_interface/static/package-lock.json b/src/web_interface/static/package-lock.json index 5a1bc5867..7b230ce26 100644 --- a/src/web_interface/static/package-lock.json +++ b/src/web_interface/static/package-lock.json @@ -1,73 +1,1083 @@ { - "name": "FACT_core-static", - "version": "0.0.0", "lockfileVersion": 3, - "requires": true, + "name": "FACT_core-static", "packages": { "": { - "name": "FACT_core-static", - "version": "0.0.0", - "license": "GPL-3.0-only", "dependencies": { "@fortawesome/fontawesome-free": "^5.15.4", + "@graphiql/plugin-explorer": "^3.0.2", + "@graphiql/toolkit": "^0.9.1", "@highlightjs/cdn-assets": "^11.8.0", "bootstrap": "^4.6.2", "bootstrap-datepicker": "^1.9.0", "bootstrap-select": "^1.13.18", "chart.js": "^2.3.0", "diff2html": "^3.4.18", + "graphiql": "^3.2.3", "jquery": "^3.5.0", "jstree": "^3.3.12", "jstree-bootstrap-theme": "^1.0.1", "moment": "^2.29.4", "popper.js": "^1.16.1", + "react": "^18.3.1", + "react-dom": "^18.3.1", "vis-network": "^9.1.6" - } + }, + "license": "GPL-3.0-only", + "name": "FACT_core-static", + "version": "0.0.0" }, - "node_modules/@egjs/hammerjs": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz", - "integrity": "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==", + "node_modules/@babel/runtime": { + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", + "version": "7.24.7" + }, + "node_modules/@codemirror/language": { + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.0.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + }, + "integrity": "sha512-rtjk5ifyMzOna1c7PBu7J1VCt0PvA5wy3o8eMVnxMKb7z8KA7JFecvD04dSn14vj/bBaAbqRsGed5OjtofEnLA==", + "license": "MIT", + "peer": true, + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.0.0.tgz", + "version": "6.0.0" + }, + "node_modules/@codemirror/state": { + "integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==", + "license": "MIT", + "peer": true, + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.1.tgz", + "version": "6.4.1" + }, + "node_modules/@codemirror/view": { + "dependencies": { + "@codemirror/state": "^6.4.0", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + }, + "integrity": "sha512-fo7CelaUDKWIyemw4b+J57cWuRkOu4SWCCPfNDkPvfWkGjM9D5racHQXr4EQeYCD6zEBIBxGCeaKkQo+ysl0gA==", + "license": "MIT", "peer": true, + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.28.0.tgz", + "version": "6.28.0" + }, + "node_modules/@egjs/hammerjs": { "dependencies": { "@types/hammerjs": "^2.0.36" }, "engines": { "node": ">=0.8.0" - } + }, + "integrity": "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==", + "peer": true, + "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz", + "version": "2.0.17" + }, + "node_modules/@emotion/is-prop-valid": { + "dependencies": { + "@emotion/memoize": "0.7.4" + }, + "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", + "license": "MIT", + "optional": true, + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", + "version": "0.8.8" + }, + "node_modules/@emotion/memoize": { + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", + "license": "MIT", + "optional": true, + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", + "version": "0.7.4" + }, + "node_modules/@floating-ui/core": { + "dependencies": { + "@floating-ui/utils": "^0.2.0" + }, + "integrity": "sha512-+2XpQV9LLZeanU4ZevzRnGFg2neDeKHgFLjP6YLW+tly0IvrhqT4u8enLGjLH3qeh85g19xY5rsAusfwTdn5lg==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.2.tgz", + "version": "1.6.2" + }, + "node_modules/@floating-ui/dom": { + "dependencies": { + "@floating-ui/core": "^1.0.0", + "@floating-ui/utils": "^0.2.0" + }, + "integrity": "sha512-Nsdud2X65Dz+1RHjAIP0t8z5e2ff/IRbei6BqFrl1urT8sDVzM1HMQ+R0XcU5ceRfyO3I6ayeqIfh+6Wb8LGTw==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.5.tgz", + "version": "1.6.5" + }, + "node_modules/@floating-ui/react-dom": { + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "integrity": "sha512-lNzj5EQmEKn5FFKc04+zasr09h/uX8RtJRNj5gUXsSQIXHVWTVh+hVAg1vOMCexkX8EgvemMvIFpQfkosnVNyA==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + }, + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.0.tgz", + "version": "2.1.0" + }, + "node_modules/@floating-ui/utils": { + "integrity": "sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.2.tgz", + "version": "0.2.2" }, "node_modules/@fortawesome/fontawesome-free": { - "version": "5.15.4", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.4.tgz", - "integrity": "sha512-eYm8vijH/hpzr/6/1CJ/V/Eb1xQFW2nnUKArb3z+yUWv7HTwj6M7SP957oMjfZjAHU6qpoNc2wQvIxBLWYa/Jg==", - "hasInstallScript": true, "engines": { "node": ">=6" - } + }, + "hasInstallScript": true, + "integrity": "sha512-eYm8vijH/hpzr/6/1CJ/V/Eb1xQFW2nnUKArb3z+yUWv7HTwj6M7SP957oMjfZjAHU6qpoNc2wQvIxBLWYa/Jg==", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.4.tgz", + "version": "5.15.4" + }, + "node_modules/@graphiql/plugin-explorer": { + "dependencies": { + "graphiql-explorer": "^0.9.0" + }, + "integrity": "sha512-bxbXzZ89qfpPJ+ObJ9Be3EJy6WTlvICQx4eRheLfETW0e3ujkBgtzhfWzp7lEFKq0IoTsng3cDr80RxfuEV7aA==", + "license": "MIT", + "peerDependencies": { + "@graphiql/react": "^0.22.2", + "graphql": "^15.5.0 || ^16.0.0", + "react": "^16.8.0 || ^17 || ^18", + "react-dom": "^16.8.0 || ^17 || ^18" + }, + "resolved": "https://registry.npmjs.org/@graphiql/plugin-explorer/-/plugin-explorer-3.0.2.tgz", + "version": "3.0.2" + }, + "node_modules/@graphiql/plugin-explorer/node_modules/graphiql-explorer": { + "integrity": "sha512-fZC/wsuatqiQDO2otchxriFO0LaWIo/ovF/CQJ1yOudmY0P7pzDiP+l9CEHUiWbizk3e99x6DQG4XG1VxA+d6A==", + "license": "MIT", + "peerDependencies": { + "graphql": "^0.6.0 || ^0.7.0 || ^0.8.0-b || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0", + "react": "^15.6.0 || ^16.0.0", + "react-dom": "^15.6.0 || ^16.0.0" + }, + "resolved": "https://registry.npmjs.org/graphiql-explorer/-/graphiql-explorer-0.9.0.tgz", + "version": "0.9.0" + }, + "node_modules/@graphiql/react": { + "dependencies": { + "@graphiql/toolkit": "^0.9.1", + "@headlessui/react": "^1.7.15", + "@radix-ui/react-dialog": "^1.0.4", + "@radix-ui/react-dropdown-menu": "^2.0.5", + "@radix-ui/react-tooltip": "^1.0.6", + "@radix-ui/react-visually-hidden": "^1.0.3", + "@types/codemirror": "^5.60.8", + "clsx": "^1.2.1", + "codemirror": "^5.65.3", + "codemirror-graphql": "^2.0.12", + "copy-to-clipboard": "^3.2.0", + "framer-motion": "^6.5.1", + "graphql-language-service": "^5.2.1", + "markdown-it": "^14.1.0", + "set-value": "^4.1.0" + }, + "integrity": "sha512-46UV7CBQdZ0iU537uOkOU6HOOs7P1o7vQpFSUezB4VRem0Y3I4TDaYQADCOo7gFlwBs5Vb9YOup8r7cmXGIr7A==", + "license": "MIT", + "peerDependencies": { + "graphql": "^15.5.0 || ^16.0.0", + "react": "^16.8.0 || ^17 || ^18", + "react-dom": "^16.8.0 || ^17 || ^18" + }, + "resolved": "https://registry.npmjs.org/@graphiql/react/-/react-0.22.2.tgz", + "version": "0.22.2" + }, + "node_modules/@graphiql/toolkit": { + "dependencies": { + "@n1ru4l/push-pull-async-iterable-iterator": "^3.1.0", + "meros": "^1.1.4" + }, + "integrity": "sha512-LVt9pdk0830so50ZnU2Znb2rclcoWznG8r8asqAENzV0U1FM1kuY0sdPpc/rBc9MmmNgnB6A+WZzDhq6dbhTHA==", + "license": "MIT", + "peerDependencies": { + "graphql": "^15.5.0 || ^16.0.0", + "graphql-ws": ">= 4.5.0" + }, + "peerDependenciesMeta": { + "graphql-ws": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/@graphiql/toolkit/-/toolkit-0.9.1.tgz", + "version": "0.9.1" + }, + "node_modules/@headlessui/react": { + "dependencies": { + "@tanstack/react-virtual": "^3.0.0-beta.60", + "client-only": "^0.0.1" + }, + "engines": { + "node": ">=10" + }, + "integrity": "sha512-Ll+8q3OlMJfJbAKM/+/Y2q6PPYbryqNTXDbryx7SXLIDamkF6iQFbriYHga0dY44PvDhvvBWCx1Xj4U5+G4hOw==", + "license": "MIT", + "peerDependencies": { + "react": "^16 || ^17 || ^18", + "react-dom": "^16 || ^17 || ^18" + }, + "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.19.tgz", + "version": "1.7.19" }, "node_modules/@highlightjs/cdn-assets": { - "version": "11.8.0", - "resolved": "https://registry.npmjs.org/@highlightjs/cdn-assets/-/cdn-assets-11.8.0.tgz", - "integrity": "sha512-gkfCH4xGBGY9xPaW+t26WpgnfpDhNhB5RtVUDLx3MHkC7ZrmKeIxXsfjzOiuOnEgRk+vydlY6XeOeglh+eVhyg==", "engines": { "node": ">=12.0.0" - } + }, + "integrity": "sha512-gkfCH4xGBGY9xPaW+t26WpgnfpDhNhB5RtVUDLx3MHkC7ZrmKeIxXsfjzOiuOnEgRk+vydlY6XeOeglh+eVhyg==", + "resolved": "https://registry.npmjs.org/@highlightjs/cdn-assets/-/cdn-assets-11.8.0.tgz", + "version": "11.8.0" + }, + "node_modules/@lezer/common": { + "integrity": "sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ==", + "license": "MIT", + "peer": true, + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.1.tgz", + "version": "1.2.1" + }, + "node_modules/@lezer/highlight": { + "dependencies": { + "@lezer/common": "^1.0.0" + }, + "integrity": "sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==", + "license": "MIT", + "peer": true, + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.0.tgz", + "version": "1.2.0" + }, + "node_modules/@lezer/lr": { + "dependencies": { + "@lezer/common": "^1.0.0" + }, + "integrity": "sha512-CHsKq8DMKBf9b3yXPDIU4DbH+ZJd/sJdYOW2llbW/HudP5u0VS6Bfq1hLYfgU7uAYGFIyGGQIsSOXGPEErZiJw==", + "license": "MIT", + "peer": true, + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.1.tgz", + "version": "1.4.1" + }, + "node_modules/@motionone/animation": { + "dependencies": { + "@motionone/easing": "^10.18.0", + "@motionone/types": "^10.17.1", + "@motionone/utils": "^10.18.0", + "tslib": "^2.3.1" + }, + "integrity": "sha512-9z2p5GFGCm0gBsZbi8rVMOAJCtw1WqBTIPw3ozk06gDvZInBPIsQcHgYogEJ4yuHJ+akuW8g1SEIOpTOvYs8hw==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/@motionone/animation/-/animation-10.18.0.tgz", + "version": "10.18.0" + }, + "node_modules/@motionone/dom": { + "dependencies": { + "@motionone/animation": "^10.12.0", + "@motionone/generators": "^10.12.0", + "@motionone/types": "^10.12.0", + "@motionone/utils": "^10.12.0", + "hey-listen": "^1.0.8", + "tslib": "^2.3.1" + }, + "integrity": "sha512-UdPTtLMAktHiqV0atOczNYyDd/d8Cf5fFsd1tua03PqTwwCe/6lwhLSQ8a7TbnQ5SN0gm44N1slBfj+ORIhrqw==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/@motionone/dom/-/dom-10.12.0.tgz", + "version": "10.12.0" + }, + "node_modules/@motionone/easing": { + "dependencies": { + "@motionone/utils": "^10.18.0", + "tslib": "^2.3.1" + }, + "integrity": "sha512-VcjByo7XpdLS4o9T8t99JtgxkdMcNWD3yHU/n6CLEz3bkmKDRZyYQ/wmSf6daum8ZXqfUAgFeCZSpJZIMxaCzg==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/@motionone/easing/-/easing-10.18.0.tgz", + "version": "10.18.0" + }, + "node_modules/@motionone/generators": { + "dependencies": { + "@motionone/types": "^10.17.1", + "@motionone/utils": "^10.18.0", + "tslib": "^2.3.1" + }, + "integrity": "sha512-+qfkC2DtkDj4tHPu+AFKVfR/C30O1vYdvsGYaR13W/1cczPrrcjdvYCj0VLFuRMN+lP1xvpNZHCRNM4fBzn1jg==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/@motionone/generators/-/generators-10.18.0.tgz", + "version": "10.18.0" + }, + "node_modules/@motionone/types": { + "integrity": "sha512-KaC4kgiODDz8hswCrS0btrVrzyU2CSQKO7Ps90ibBVSQmjkrt2teqta6/sOG59v7+dPnKMAg13jyqtMKV2yJ7A==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/@motionone/types/-/types-10.17.1.tgz", + "version": "10.17.1" + }, + "node_modules/@motionone/utils": { + "dependencies": { + "@motionone/types": "^10.17.1", + "hey-listen": "^1.0.8", + "tslib": "^2.3.1" + }, + "integrity": "sha512-3XVF7sgyTSI2KWvTf6uLlBJ5iAgRgmvp3bpuOiQJvInd4nZ19ET8lX5unn30SlmRH7hXbBbH+Gxd0m0klJ3Xtw==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/@motionone/utils/-/utils-10.18.0.tgz", + "version": "10.18.0" + }, + "node_modules/@n1ru4l/push-pull-async-iterable-iterator": { + "engines": { + "node": ">=12" + }, + "integrity": "sha512-3fkKj25kEjsfObL6IlKPAlHYPq/oYwUkkQ03zsTTiDjD7vg/RxjdiLeCydqtxHZP0JgsXL3D/X5oAkMGzuUp/Q==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/@n1ru4l/push-pull-async-iterable-iterator/-/push-pull-async-iterable-iterator-3.2.0.tgz", + "version": "3.2.0" + }, + "node_modules/@radix-ui/primitive": { + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz", + "version": "1.0.1" + }, + "node_modules/@radix-ui/react-arrow": { + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "integrity": "sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz", + "version": "1.0.3" + }, + "node_modules/@radix-ui/react-collection": { + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2" + }, + "integrity": "sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz", + "version": "1.0.3" + }, + "node_modules/@radix-ui/react-compose-refs": { + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz", + "version": "1.0.1" + }, + "node_modules/@radix-ui/react-context": { + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz", + "version": "1.0.1" + }, + "node_modules/@radix-ui/react-dialog": { + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.5", + "@radix-ui/react-focus-guards": "1.0.1", + "@radix-ui/react-focus-scope": "1.0.4", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-portal": "1.0.4", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2", + "@radix-ui/react-use-controllable-state": "1.0.1", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.5" + }, + "integrity": "sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz", + "version": "1.0.5" + }, + "node_modules/@radix-ui/react-direction": { + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "integrity": "sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.1.tgz", + "version": "1.0.1" + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-escape-keydown": "1.0.3" + }, + "integrity": "sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz", + "version": "1.0.5" + }, + "node_modules/@radix-ui/react-dropdown-menu": { + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-menu": "2.0.6", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-controllable-state": "1.0.1" + }, + "integrity": "sha512-i6TuFOoWmLWq+M/eCLGd/bQ2HfAX1RJgvrBQ6AQLmzfvsLdefxbWu8G9zczcPFfcSPehz9GcpF6K9QYreFV8hA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.0.6.tgz", + "version": "2.0.6" + }, + "node_modules/@radix-ui/react-focus-guards": { + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "integrity": "sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz", + "version": "1.0.1" + }, + "node_modules/@radix-ui/react-focus-scope": { + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "integrity": "sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz", + "version": "1.0.4" + }, + "node_modules/@radix-ui/react-id": { + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "integrity": "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz", + "version": "1.0.1" + }, + "node_modules/@radix-ui/react-menu": { + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-collection": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.5", + "@radix-ui/react-focus-guards": "1.0.1", + "@radix-ui/react-focus-scope": "1.0.4", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-popper": "1.1.3", + "@radix-ui/react-portal": "1.0.4", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-roving-focus": "1.0.4", + "@radix-ui/react-slot": "1.0.2", + "@radix-ui/react-use-callback-ref": "1.0.1", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.5" + }, + "integrity": "sha512-BVkFLS+bUC8HcImkRKPSiVumA1VPOOEC5WBMiT+QAVsPzW1FJzI9KnqgGxVDPBcql5xXrHkD3JOVoXWEXD8SYA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.0.6.tgz", + "version": "2.0.6" + }, + "node_modules/@radix-ui/react-popper": { + "dependencies": { + "@babel/runtime": "^7.13.10", + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1", + "@radix-ui/react-use-rect": "1.0.1", + "@radix-ui/react-use-size": "1.0.1", + "@radix-ui/rect": "1.0.1" + }, + "integrity": "sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.3.tgz", + "version": "1.1.3" + }, + "node_modules/@radix-ui/react-portal": { + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "integrity": "sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz", + "version": "1.0.4" + }, + "node_modules/@radix-ui/react-presence": { + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "integrity": "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz", + "version": "1.0.1" + }, + "node_modules/@radix-ui/react-primitive": { + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "1.0.2" + }, + "integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz", + "version": "1.0.3" + }, + "node_modules/@radix-ui/react-roving-focus": { + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-collection": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-controllable-state": "1.0.1" + }, + "integrity": "sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.4.tgz", + "version": "1.0.4" + }, + "node_modules/@radix-ui/react-slot": { + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1" + }, + "integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", + "version": "1.0.2" + }, + "node_modules/@radix-ui/react-tooltip": { + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.5", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-popper": "1.1.3", + "@radix-ui/react-portal": "1.0.4", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-visually-hidden": "1.0.3" + }, + "integrity": "sha512-lPh5iKNFVQ/jav/j6ZrWq3blfDJ0OH9R6FlNUHPMqdLuQ9vwDgFsRxvl8b7Asuy5c8xmoojHUxKHQSOAvMHxyw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.0.7.tgz", + "version": "1.0.7" + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz", + "version": "1.0.1" + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz", + "version": "1.0.1" + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz", + "version": "1.0.3" + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz", + "version": "1.0.1" + }, + "node_modules/@radix-ui/react-use-rect": { + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/rect": "1.0.1" + }, + "integrity": "sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz", + "version": "1.0.1" + }, + "node_modules/@radix-ui/react-use-size": { + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "integrity": "sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz", + "version": "1.0.1" + }, + "node_modules/@radix-ui/react-visually-hidden": { + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "integrity": "sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz", + "version": "1.0.3" + }, + "node_modules/@radix-ui/rect": { + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "integrity": "sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.1.tgz", + "version": "1.0.1" + }, + "node_modules/@tanstack/react-virtual": { + "dependencies": { + "@tanstack/virtual-core": "3.5.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "integrity": "sha512-jIsuhfgy8GqA67PdWqg73ZB2LFE+HD9hjWL1L6ifEIZVyZVAKpYmgUG4WsKQ005aEyImJmbuimPiEvc57IY0Aw==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.5.1.tgz", + "version": "3.5.1" + }, + "node_modules/@tanstack/virtual-core": { + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "integrity": "sha512-046+AUSiDru/V9pajE1du8WayvBKeCvJ2NmKPy/mR8/SbKKrqmSbj7LJBfXE+nSq4f5TBXvnCzu0kcYebI9WdQ==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.5.1.tgz", + "version": "3.5.1" + }, + "node_modules/@types/codemirror": { + "dependencies": { + "@types/tern": "*" + }, + "integrity": "sha512-dTOvwEQ+ouKJ/rE9LT1Ue2hmP6H1mZv5+CCnNWu2qtiOe2LQa9lCprEY20HxiDmV/Bxh+dXjywmy5aKvoGjULA==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.15.tgz", + "version": "5.60.15" + }, + "node_modules/@types/estree": { + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "version": "1.0.5" }, "node_modules/@types/hammerjs": { - "version": "2.0.41", - "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.41.tgz", "integrity": "sha512-ewXv/ceBaJprikMcxCmWU1FKyMAQ2X7a9Gtmzw8fcg2kIePI1crERDM818W+XYrxqdBBOdlf2rm137bU+BltCA==", - "peer": true + "peer": true, + "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.41.tgz", + "version": "2.0.41" + }, + "node_modules/@types/tern": { + "dependencies": { + "@types/estree": "*" + }, + "integrity": "sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz", + "version": "0.23.9" }, "node_modules/abbrev": { - "version": "1.1.1", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + "version": "1.1.1" + }, + "node_modules/argparse": { + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "version": "2.0.1" + }, + "node_modules/aria-hidden": { + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", + "version": "1.2.4" }, "node_modules/bootstrap": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.2.tgz", - "integrity": "sha512-51Bbp/Uxr9aTuy6ca/8FbFloBUJZLHwnhTcnjIeRn2suQWsWzcuJhGjKDB5eppVte/8oCdOL3VuwxvZDUggwGQ==", "funding": [ { "type": "github", @@ -78,90 +1088,150 @@ "url": "https://opencollective.com/bootstrap" } ], + "integrity": "sha512-51Bbp/Uxr9aTuy6ca/8FbFloBUJZLHwnhTcnjIeRn2suQWsWzcuJhGjKDB5eppVte/8oCdOL3VuwxvZDUggwGQ==", "peerDependencies": { "jquery": "1.9.1 - 3", "popper.js": "^1.16.1" - } + }, + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.2.tgz", + "version": "4.6.2" }, "node_modules/bootstrap-datepicker": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/bootstrap-datepicker/-/bootstrap-datepicker-1.10.0.tgz", - "integrity": "sha512-lWxtSYddAQOpbAO8UhYhHLcK6425eWoSjb5JDvZU3ePHEPF6A3eUr51WKaFy4PccU19JRxUG6wEU3KdhtKfvpg==", "dependencies": { "jquery": ">=3.4.0 <4.0.0" - } + }, + "integrity": "sha512-lWxtSYddAQOpbAO8UhYhHLcK6425eWoSjb5JDvZU3ePHEPF6A3eUr51WKaFy4PccU19JRxUG6wEU3KdhtKfvpg==", + "resolved": "https://registry.npmjs.org/bootstrap-datepicker/-/bootstrap-datepicker-1.10.0.tgz", + "version": "1.10.0" }, "node_modules/bootstrap-select": { - "version": "1.13.18", - "resolved": "https://registry.npmjs.org/bootstrap-select/-/bootstrap-select-1.13.18.tgz", "integrity": "sha512-V1IzK4rxBq5FrJtkzSH6RmFLFBsjx50byFbfAf8jYyXROWs7ZpprGjdHeoyq2HSsHyjJhMMwjsQhRoYAfxCGow==", "peerDependencies": { "bootstrap": ">=3.0.0", "jquery": "1.9.1 - 3" - } + }, + "resolved": "https://registry.npmjs.org/bootstrap-select/-/bootstrap-select-1.13.18.tgz", + "version": "1.13.18" }, "node_modules/chart.js": { - "version": "2.9.4", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.9.4.tgz", - "integrity": "sha512-B07aAzxcrikjAPyV+01j7BmOpxtQETxTSlQ26BEYJ+3iUkbNKaOJ/nDbT6JjyqYxseM0ON12COHYdU2cTIjC7A==", "dependencies": { "chartjs-color": "^2.1.0", "moment": "^2.10.2" - } + }, + "integrity": "sha512-B07aAzxcrikjAPyV+01j7BmOpxtQETxTSlQ26BEYJ+3iUkbNKaOJ/nDbT6JjyqYxseM0ON12COHYdU2cTIjC7A==", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.9.4.tgz", + "version": "2.9.4" }, "node_modules/chartjs-color": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.4.1.tgz", - "integrity": "sha512-haqOg1+Yebys/Ts/9bLo/BqUcONQOdr/hoEr2LLTRl6C5LXctUdHxsCYfvQVg5JIxITrfCNUDr4ntqmQk9+/0w==", "dependencies": { "chartjs-color-string": "^0.6.0", "color-convert": "^1.9.3" - } + }, + "integrity": "sha512-haqOg1+Yebys/Ts/9bLo/BqUcONQOdr/hoEr2LLTRl6C5LXctUdHxsCYfvQVg5JIxITrfCNUDr4ntqmQk9+/0w==", + "resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.4.1.tgz", + "version": "2.4.1" }, "node_modules/chartjs-color-string": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz", - "integrity": "sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==", "dependencies": { "color-name": "^1.0.0" - } + }, + "integrity": "sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==", + "resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz", + "version": "0.6.0" + }, + "node_modules/client-only": { + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "version": "0.0.1" + }, + "node_modules/clsx": { + "engines": { + "node": ">=6" + }, + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "version": "1.2.1" + }, + "node_modules/codemirror": { + "integrity": "sha512-br21LjYmSlVL0vFCPWPfhzUCT34FM/pAdK7rRIZwa0rrtrIdotvP4Oh4GUHsu2E3IrQMCfRkL/fN3ytMNxVQvg==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.16.tgz", + "version": "5.65.16" + }, + "node_modules/codemirror-graphql": { + "dependencies": { + "@types/codemirror": "^0.0.90", + "graphql-language-service": "5.2.1" + }, + "integrity": "sha512-5UCqhWzck1jClCmRewFb8aSiabnAqiaRfsvIPfmbf6WJvOb8oiefJeHilclPPiZBzY8v/Et6EBMtOeKnWCoyng==", + "license": "MIT", + "peerDependencies": { + "@codemirror/language": "6.0.0", + "codemirror": "^5.65.3", + "graphql": "^15.5.0 || ^16.0.0" + }, + "resolved": "https://registry.npmjs.org/codemirror-graphql/-/codemirror-graphql-2.0.12.tgz", + "version": "2.0.12" + }, + "node_modules/codemirror-graphql/node_modules/@types/codemirror": { + "dependencies": { + "@types/tern": "*" + }, + "integrity": "sha512-8Z9+tSg27NPRGubbUPUCrt5DDG/OWzLph5BvcDykwR5D7RyZh5mhHG0uS1ePKV1YFCA+/cwc4Ey2AJAEFfV3IA==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.90.tgz", + "version": "0.0.90" }, "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dependencies": { "color-name": "1.1.3" - } + }, + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "version": "1.9.3" }, "node_modules/color-convert/node_modules/color-name": { - "version": "1.1.3", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "version": "1.1.3" }, "node_modules/color-name": { - "version": "1.1.4", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "version": "1.1.4" }, "node_modules/component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "peer": true + "peer": true, + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "version": "1.3.0" + }, + "node_modules/copy-to-clipboard": { + "dependencies": { + "toggle-selection": "^1.0.6" + }, + "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", + "version": "3.3.3" + }, + "node_modules/detect-node-es": { + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "version": "1.1.0" }, "node_modules/diff": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", - "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", "engines": { "node": ">=0.3.1" - } + }, + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "version": "5.1.0" }, "node_modules/diff2html": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/diff2html/-/diff2html-3.4.35.tgz", - "integrity": "sha512-+pKs1BrA7l8DAwY33awHyznE3iuTIo58xmINmDBUwGsnou2KvBoSr6dAa6AvQAM7SH+nGtuOKNXmxumgbGp/Pw==", "dependencies": { "diff": "5.1.0", "hogan.js": "3.0.2" @@ -169,138 +1239,578 @@ "engines": { "node": ">=12" }, + "integrity": "sha512-+pKs1BrA7l8DAwY33awHyznE3iuTIo58xmINmDBUwGsnou2KvBoSr6dAa6AvQAM7SH+nGtuOKNXmxumgbGp/Pw==", "optionalDependencies": { "highlight.js": "11.6.0" - } + }, + "resolved": "https://registry.npmjs.org/diff2html/-/diff2html-3.4.35.tgz", + "version": "3.4.35" + }, + "node_modules/entities": { + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + }, + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "version": "4.5.0" + }, + "node_modules/framer-motion": { + "dependencies": { + "@motionone/dom": "10.12.0", + "framesync": "6.0.1", + "hey-listen": "^1.0.8", + "popmotion": "11.0.3", + "style-value-types": "5.0.0", + "tslib": "^2.1.0" + }, + "integrity": "sha512-o1BGqqposwi7cgDrtg0dNONhkmPsUFDaLcKXigzuTFC5x58mE8iyTazxSudFzmT6MEyJKfjjU8ItoMe3W+3fiw==", + "license": "MIT", + "optionalDependencies": { + "@emotion/is-prop-valid": "^0.8.2" + }, + "peerDependencies": { + "react": ">=16.8 || ^17.0.0 || ^18.0.0", + "react-dom": ">=16.8 || ^17.0.0 || ^18.0.0" + }, + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-6.5.1.tgz", + "version": "6.5.1" + }, + "node_modules/framesync": { + "dependencies": { + "tslib": "^2.1.0" + }, + "integrity": "sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.0.1.tgz", + "version": "6.0.1" + }, + "node_modules/get-nonce": { + "engines": { + "node": ">=6" + }, + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "version": "1.0.1" + }, + "node_modules/graphiql": { + "dependencies": { + "@graphiql/react": "^0.22.2", + "@graphiql/toolkit": "^0.9.1", + "graphql-language-service": "^5.2.1", + "markdown-it": "^14.1.0" + }, + "integrity": "sha512-b5XuFyTWkORhQkUZULPOPmUXocg+x7HFB53cYEjV7LcH4taB4ViGwmXqHILhfPtv+JcTN80Aw8HELVWSa16iiA==", + "license": "MIT", + "peerDependencies": { + "graphql": "^15.5.0 || ^16.0.0", + "react": "^16.8.0 || ^17 || ^18", + "react-dom": "^16.8.0 || ^17 || ^18" + }, + "resolved": "https://registry.npmjs.org/graphiql/-/graphiql-3.2.3.tgz", + "version": "3.2.3" + }, + "node_modules/graphql": { + "engines": { + "node": ">= 10.x" + }, + "integrity": "sha512-5gghUc24tP9HRznNpV2+FIoq3xKkj5dTQqf4v0CpdPbFVwFkWoxOM+o+2OC9ZSvjEMTjfmG9QT+gcvggTwW1zw==", + "license": "MIT", + "peer": true, + "resolved": "https://registry.npmjs.org/graphql/-/graphql-15.8.0.tgz", + "version": "15.8.0" + }, + "node_modules/graphql-language-service": { + "bin": { + "graphql": "dist/temp-bin.js" + }, + "dependencies": { + "nullthrows": "^1.0.0", + "vscode-languageserver-types": "^3.17.1" + }, + "integrity": "sha512-8ewD6otGO43vg2TiEGjoLz3CweTwfaf4ZnqfNREqZXS2JSJGXtsRBOMMknCxMfFVh4x14ql3jyDrXcyAAtbmkQ==", + "license": "MIT", + "peerDependencies": { + "graphql": "^15.5.0 || ^16.0.0" + }, + "resolved": "https://registry.npmjs.org/graphql-language-service/-/graphql-language-service-5.2.1.tgz", + "version": "5.2.1" + }, + "node_modules/hey-listen": { + "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", + "version": "1.0.8" }, "node_modules/highlight.js": { - "version": "11.6.0", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.6.0.tgz", - "integrity": "sha512-ig1eqDzJaB0pqEvlPVIpSSyMaO92bH1N2rJpLMN/nX396wTpDA4Eq0uK+7I/2XG17pFaaKE0kjV/XPeGt7Evjw==", - "optional": true, "engines": { "node": ">=12.0.0" - } + }, + "integrity": "sha512-ig1eqDzJaB0pqEvlPVIpSSyMaO92bH1N2rJpLMN/nX396wTpDA4Eq0uK+7I/2XG17pFaaKE0kjV/XPeGt7Evjw==", + "optional": true, + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.6.0.tgz", + "version": "11.6.0" }, "node_modules/hogan.js": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/hogan.js/-/hogan.js-3.0.2.tgz", - "integrity": "sha512-RqGs4wavGYJWE07t35JQccByczmNUXQT0E12ZYV1VKYu5UiAU9lsos/yBAcf840+zrUQQxgVduCR5/B8nNtibg==", + "bin": { + "hulk": "bin/hulk" + }, "dependencies": { "mkdirp": "0.3.0", "nopt": "1.0.10" }, - "bin": { - "hulk": "bin/hulk" - } + "integrity": "sha512-RqGs4wavGYJWE07t35JQccByczmNUXQT0E12ZYV1VKYu5UiAU9lsos/yBAcf840+zrUQQxgVduCR5/B8nNtibg==", + "resolved": "https://registry.npmjs.org/hogan.js/-/hogan.js-3.0.2.tgz", + "version": "3.0.2" + }, + "node_modules/invariant": { + "dependencies": { + "loose-envify": "^1.0.0" + }, + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "version": "2.2.4" + }, + "node_modules/is-plain-object": { + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + }, + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "version": "2.0.4" + }, + "node_modules/is-primitive": { + "engines": { + "node": ">=0.10.0" + }, + "integrity": "sha512-GljRxhWvlCNRfZyORiH77FwdFwGcMO620o37EOYC0ORWdq+WYNVqW0w2Juzew4M+L81l6/QS3t5gkkihyRqv9w==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-3.0.1.tgz", + "version": "3.0.1" + }, + "node_modules/isobject": { + "engines": { + "node": ">=0.10.0" + }, + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "version": "3.0.1" }, "node_modules/jquery": { - "version": "3.7.0", + "integrity": "sha512-umpJ0/k8X0MvD1ds0P9SfowREz2LenHsQaxSohMZ5OMNEU2r0tf8pdeEFTHMFxWVxKNyU9rTtK3CWzUCTKJUeQ==", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.0.tgz", - "integrity": "sha512-umpJ0/k8X0MvD1ds0P9SfowREz2LenHsQaxSohMZ5OMNEU2r0tf8pdeEFTHMFxWVxKNyU9rTtK3CWzUCTKJUeQ==" + "version": "3.7.0" + }, + "node_modules/js-tokens": { + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "version": "4.0.0" }, "node_modules/jstree": { - "version": "3.3.15", - "resolved": "https://registry.npmjs.org/jstree/-/jstree-3.3.15.tgz", - "integrity": "sha512-fNK2EBgGjaJQ3cJuINX/80vDeAufYWtM0csudgYl3eJG+eRAH/1r1IJVUOvAlJIa+uSgg+Fi8uGrt+Xbs92eKg==", "dependencies": { "jquery": "^3.5.0" - } + }, + "integrity": "sha512-fNK2EBgGjaJQ3cJuINX/80vDeAufYWtM0csudgYl3eJG+eRAH/1r1IJVUOvAlJIa+uSgg+Fi8uGrt+Xbs92eKg==", + "resolved": "https://registry.npmjs.org/jstree/-/jstree-3.3.15.tgz", + "version": "3.3.15" }, "node_modules/jstree-bootstrap-theme": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/jstree-bootstrap-theme/-/jstree-bootstrap-theme-1.0.1.tgz", - "integrity": "sha512-H1F1NOwfPnsQAzsLPRBRR0zO4pfXD5tUHfRj9psT/2+eEMMotG1mYtU3gP5Lsr67TKbsE53M8HLv93EAL+zC2A==", "dependencies": { "jquery": ">=1.9.1" - } + }, + "integrity": "sha512-H1F1NOwfPnsQAzsLPRBRR0zO4pfXD5tUHfRj9psT/2+eEMMotG1mYtU3gP5Lsr67TKbsE53M8HLv93EAL+zC2A==", + "resolved": "https://registry.npmjs.org/jstree-bootstrap-theme/-/jstree-bootstrap-theme-1.0.1.tgz", + "version": "1.0.1" }, "node_modules/keycharm": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/keycharm/-/keycharm-0.4.0.tgz", "integrity": "sha512-TyQTtsabOVv3MeOpR92sIKk/br9wxS+zGj4BG7CR8YbK4jM3tyIBaF0zhzeBUMx36/Q/iQLOKKOT+3jOQtemRQ==", - "peer": true + "peer": true, + "resolved": "https://registry.npmjs.org/keycharm/-/keycharm-0.4.0.tgz", + "version": "0.4.0" + }, + "node_modules/linkify-it": { + "dependencies": { + "uc.micro": "^2.0.0" + }, + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "version": "5.0.0" + }, + "node_modules/loose-envify": { + "bin": { + "loose-envify": "cli.js" + }, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "version": "1.4.0" + }, + "node_modules/markdown-it": { + "bin": { + "markdown-it": "bin/markdown-it.mjs" + }, + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "version": "14.1.0" + }, + "node_modules/mdurl": { + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "version": "2.0.0" + }, + "node_modules/meros": { + "engines": { + "node": ">=13" + }, + "integrity": "sha512-2BNGOimxEz5hmjUG2FwoxCt5HN7BXdaWyFqEwxPTrJzVdABtrL4TiHTcsWSFAxPQ/tOnEaQEJh3qWq71QRMY+w==", + "license": "MIT", + "peerDependencies": { + "@types/node": ">=13" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/meros/-/meros-1.3.0.tgz", + "version": "1.3.0" }, "node_modules/mkdirp": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", - "integrity": "sha512-OHsdUcVAQ6pOtg5JYWpCBo9W/GySVuwvP9hueRMW7UqshC0tbfzLv8wjySTPm3tfUZ/21CE9E1pJagOA91Pxew==", "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", "engines": { "node": "*" - } + }, + "integrity": "sha512-OHsdUcVAQ6pOtg5JYWpCBo9W/GySVuwvP9hueRMW7UqshC0tbfzLv8wjySTPm3tfUZ/21CE9E1pJagOA91Pxew==", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", + "version": "0.3.0" }, "node_modules/moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", "engines": { "node": "*" - } + }, + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "version": "2.29.4" }, "node_modules/nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", - "dependencies": { - "abbrev": "1" - }, "bin": { "nopt": "bin/nopt.js" }, + "dependencies": { + "abbrev": "1" + }, "engines": { "node": "*" - } + }, + "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "version": "1.0.10" + }, + "node_modules/nullthrows": { + "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", + "version": "1.1.1" + }, + "node_modules/popmotion": { + "dependencies": { + "framesync": "6.0.1", + "hey-listen": "^1.0.8", + "style-value-types": "5.0.0", + "tslib": "^2.1.0" + }, + "integrity": "sha512-Y55FLdj3UxkR7Vl3s7Qr4e9m0onSnP8W7d/xQLsoJM40vs6UKHFdygs6SWryasTZYqugMjm3BepCF4CWXDiHgA==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/popmotion/-/popmotion-11.0.3.tgz", + "version": "11.0.3" }, "node_modules/popper.js": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", - "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==", "deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1", "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" - } + }, + "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", + "version": "1.16.1" + }, + "node_modules/punycode.js": { + "engines": { + "node": ">=6" + }, + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "version": "2.3.1" + }, + "node_modules/react": { + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "version": "18.3.1" + }, + "node_modules/react-dom": { + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "peerDependencies": { + "react": "^18.3.1" + }, + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "version": "18.3.1" + }, + "node_modules/react-remove-scroll": { + "dependencies": { + "react-remove-scroll-bar": "^2.3.3", + "react-style-singleton": "^2.2.1", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.0", + "use-sidecar": "^1.1.2" + }, + "engines": { + "node": ">=10" + }, + "integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz", + "version": "2.5.5" + }, + "node_modules/react-remove-scroll-bar": { + "dependencies": { + "react-style-singleton": "^2.2.1", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "integrity": "sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==", + "license": "MIT", + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz", + "version": "2.3.6" + }, + "node_modules/react-style-singleton": { + "dependencies": { + "get-nonce": "^1.0.0", + "invariant": "^2.2.4", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==", + "license": "MIT", + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", + "version": "2.2.1" + }, + "node_modules/regenerator-runtime": { + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "version": "0.14.1" + }, + "node_modules/scheduler": { + "dependencies": { + "loose-envify": "^1.1.0" + }, + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "version": "0.23.2" + }, + "node_modules/set-value": { + "dependencies": { + "is-plain-object": "^2.0.4", + "is-primitive": "^3.0.1" + }, + "engines": { + "node": ">=11.0" + }, + "funding": [ + "https://github.com/sponsors/jonschlinkert", + "https://paypal.me/jonathanschlinkert", + "https://jonschlinkert.dev/sponsor" + ], + "integrity": "sha512-zTEg4HL0RwVrqcWs3ztF+x1vkxfm0lP+MQQFPiMJTKVceBwEV0A569Ou8l9IYQG8jOZdMVI1hGsc0tmeD2o/Lw==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-4.1.0.tgz", + "version": "4.1.0" + }, + "node_modules/style-mod": { + "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==", + "license": "MIT", + "peer": true, + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", + "version": "4.1.2" + }, + "node_modules/style-value-types": { + "dependencies": { + "hey-listen": "^1.0.8", + "tslib": "^2.1.0" + }, + "integrity": "sha512-08yq36Ikn4kx4YU6RD7jWEv27v4V+PUsOGa4n/as8Et3CuODMJQ00ENeAVXAeydX4Z2j1XHZF1K2sX4mGl18fA==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/style-value-types/-/style-value-types-5.0.0.tgz", + "version": "5.0.0" }, "node_modules/timsort": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", "integrity": "sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==", - "peer": true + "peer": true, + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "version": "0.3.0" + }, + "node_modules/toggle-selection": { + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "version": "1.0.6" + }, + "node_modules/tslib": { + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "license": "0BSD", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "version": "2.6.3" + }, + "node_modules/uc.micro": { + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "version": "2.1.0" + }, + "node_modules/use-callback-ref": { + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "integrity": "sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.2.tgz", + "version": "1.3.2" + }, + "node_modules/use-sidecar": { + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", + "version": "1.1.2" }, "node_modules/uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", - "peer": true, "bin": { "uuid": "dist/bin/uuid" - } + }, + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "peer": true, + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "version": "9.0.0" }, "node_modules/vis-data": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/vis-data/-/vis-data-7.1.6.tgz", - "integrity": "sha512-lG7LJdkawlKSXsdcEkxe/zRDyW29a4r7N7PMwxCPxK12/QIdqxJwcMxwjVj9ozdisRhP5TyWDHZwsgjmj0g6Dg==", - "hasInstallScript": true, - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/visjs" }, + "hasInstallScript": true, + "integrity": "sha512-lG7LJdkawlKSXsdcEkxe/zRDyW29a4r7N7PMwxCPxK12/QIdqxJwcMxwjVj9ozdisRhP5TyWDHZwsgjmj0g6Dg==", + "peer": true, "peerDependencies": { "uuid": "^3.4.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", "vis-util": "^5.0.1" - } + }, + "resolved": "https://registry.npmjs.org/vis-data/-/vis-data-7.1.6.tgz", + "version": "7.1.6" }, "node_modules/vis-network": { - "version": "9.1.6", - "resolved": "https://registry.npmjs.org/vis-network/-/vis-network-9.1.6.tgz", - "integrity": "sha512-Eiwx1JleAsUqfy4pzcsFngCVlCEdjAtRPB/OwCV7PHBm+o2jtE4IZPcPITAEGUlxvL4Fdw7/lZsfD32dL+IL6g==", - "hasInstallScript": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/visjs" }, + "hasInstallScript": true, + "integrity": "sha512-Eiwx1JleAsUqfy4pzcsFngCVlCEdjAtRPB/OwCV7PHBm+o2jtE4IZPcPITAEGUlxvL4Fdw7/lZsfD32dL+IL6g==", "peerDependencies": { "@egjs/hammerjs": "^2.0.0", "component-emitter": "^1.3.0", @@ -309,13 +1819,11 @@ "uuid": "^3.4.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", "vis-data": "^6.3.0 || ^7.0.0", "vis-util": "^5.0.1" - } + }, + "resolved": "https://registry.npmjs.org/vis-network/-/vis-network-9.1.6.tgz", + "version": "9.1.6" }, "node_modules/vis-util": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/vis-util/-/vis-util-5.0.3.tgz", - "integrity": "sha512-Wf9STUcFrDzK4/Zr7B6epW2Kvm3ORNWF+WiwEz2dpf5RdWkLUXFSbLcuB88n1W6tCdFwVN+v3V4/Xmn9PeL39g==", - "peer": true, "engines": { "node": ">=8" }, @@ -323,10 +1831,29 @@ "type": "opencollective", "url": "https://opencollective.com/visjs" }, + "integrity": "sha512-Wf9STUcFrDzK4/Zr7B6epW2Kvm3ORNWF+WiwEz2dpf5RdWkLUXFSbLcuB88n1W6tCdFwVN+v3V4/Xmn9PeL39g==", + "peer": true, "peerDependencies": { "@egjs/hammerjs": "^2.0.0", "component-emitter": "^1.3.0" - } + }, + "resolved": "https://registry.npmjs.org/vis-util/-/vis-util-5.0.3.tgz", + "version": "5.0.3" + }, + "node_modules/vscode-languageserver-types": { + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "version": "3.17.5" + }, + "node_modules/w3c-keyname": { + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "license": "MIT", + "peer": true, + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "version": "2.2.8" } - } + }, + "requires": true, + "version": "0.0.0" } diff --git a/src/web_interface/static/package.json b/src/web_interface/static/package.json index fd80b324d..b12ecc9ad 100644 --- a/src/web_interface/static/package.json +++ b/src/web_interface/static/package.json @@ -1,17 +1,22 @@ { "dependencies": { "@fortawesome/fontawesome-free": "^5.15.4", + "@graphiql/plugin-explorer": "^3.0.2", + "@graphiql/toolkit": "^0.9.1", "@highlightjs/cdn-assets": "^11.8.0", "bootstrap": "^4.6.2", "bootstrap-datepicker": "^1.9.0", "bootstrap-select": "^1.13.18", "chart.js": "^2.3.0", "diff2html": "^3.4.18", + "graphiql": "^3.2.3", "jquery": "^3.5.0", "jstree": "^3.3.12", "jstree-bootstrap-theme": "^1.0.1", "moment": "^2.29.4", "popper.js": "^1.16.1", + "react": "^18.3.1", + "react-dom": "^18.3.1", "vis-network": "^9.1.6" }, "license": "GPL-3.0-only", diff --git a/src/web_interface/templates/database/database_graphql.html b/src/web_interface/templates/database/database_graphql.html index f35bcf375..433f21359 100644 --- a/src/web_interface/templates/database/database_graphql.html +++ b/src/web_interface/templates/database/database_graphql.html @@ -4,12 +4,15 @@ {% block head %} - - - + + + + {% endblock %} {% block styles %} + + {% endblock %} @@ -17,21 +20,73 @@
-
+

Advanced Search with GraphQL

- + + + + + +
+ +
+ +
+ +
+ +
From 7e434b2193f57de141bbc77757ceefca2f990a56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Stucke?= Date: Wed, 12 Jun 2024 09:20:36 +0200 Subject: [PATCH 09/46] hasura: added restart script --- src/graphql/hasura/restart.py | 55 +++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 src/graphql/hasura/restart.py diff --git a/src/graphql/hasura/restart.py b/src/graphql/hasura/restart.py new file mode 100644 index 000000000..a0563fd36 --- /dev/null +++ b/src/graphql/hasura/restart.py @@ -0,0 +1,55 @@ +import os +import sys +from pathlib import Path +from shlex import split +from subprocess import PIPE, STDOUT, Popen + +from rich.progress import BarColumn, Progress, SpinnerColumn, TaskProgressColumn + +try: + import config + from helperFunctions.install import OperateInDirectory +except ImportError: + SRC_DIR = Path(__file__).parent.parent.parent + sys.path.append(str(SRC_DIR)) + import config + from helperFunctions.install import OperateInDirectory + +progress = Progress( + BarColumn(), + TaskProgressColumn(), + SpinnerColumn(), +) + + +def restart(): + user = config.common.postgres.rw_user + pw = config.common.postgres.rw_pw + port = config.common.postgres.port + server = config.common.postgres.server + if server in ('localhost', '127.0.0.1', '::1'): + server = 'host.docker.internal' + env = { + **os.environ, + 'HASURA_ADMIN_SECRET': config.frontend.hasura.admin_secret, + 'FACT_DB_URL': f'postgresql://{user}:{pw}@{server}:{port}/fact_db', + 'HASURA_PORT': str(config.frontend.hasura.port), + } + + with OperateInDirectory(Path(__file__).parent), progress: + progress.console.print('Restarting Hasura ⏳') + for command in progress.track(('docker compose down', 'docker compose up -d')): + progress.console.print(f'Running {command} ...') + process = Popen(split(command), stdout=PIPE, stderr=STDOUT, text=True, env=env) + while not process.poll(): + if output := process.stdout.readline(): + progress.console.print(f'\t{output.strip()}') + else: + break + process.wait() + progress.console.print('Finished restarting Hasura ✨') + + +if __name__ == '__main__': + config.load() + restart() From c0552fdb1f59cbe90ae2c39a818ff51d99b4b286 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Stucke?= Date: Wed, 12 Jun 2024 09:58:22 +0200 Subject: [PATCH 10/46] hasura: remove secret from template, remove unused endpoint --- src/web_interface/components/database_routes.py | 13 +++++-------- src/web_interface/templates/base.html | 2 ++ .../templates/database/database_graphql.html | 8 ++++---- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/web_interface/components/database_routes.py b/src/web_interface/components/database_routes.py index be13539cf..0cc185cde 100644 --- a/src/web_interface/components/database_routes.py +++ b/src/web_interface/components/database_routes.py @@ -2,11 +2,11 @@ import logging from datetime import datetime from itertools import chain -from pathlib import Path from flask import redirect, render_template, request, url_for from sqlalchemy.exc import SQLAlchemyError +import config from helperFunctions.data_conversion import make_unicode_string from helperFunctions.database import get_shared_session from helperFunctions.task_conversion import get_file_name_and_binary_from_request @@ -285,10 +285,7 @@ def start_quick_search(self): @roles_accepted(*PRIVILEGES['advanced_search']) @AppRoute('/database/graphql', GET) def get_graphql(self): - return render_template('database/database_graphql.html') - - @roles_accepted(*PRIVILEGES['advanced_search']) - @AppRoute('/database/graphql/schema', GET) - def get_graphql_schema(self): - schema_path = Path(__file__).parent.parent / 'static' / 'graphql' / 'schema.graphql' - return schema_path.read_text() + return render_template( + 'database/database_graphql.html', + secret=config.frontend.hasura.admin_secret, + ) diff --git a/src/web_interface/templates/base.html b/src/web_interface/templates/base.html index 7b898f662..f568134eb 100644 --- a/src/web_interface/templates/base.html +++ b/src/web_interface/templates/base.html @@ -138,6 +138,8 @@ class="fas fa-search"> Basic Search Advanced Search + GraphQL Search Binary Pattern Search Advanced Search with GraphQL @@ -48,7 +48,7 @@

Advanced Search with GraphQL

- @@ -61,7 +66,6 @@

Advanced Search with GraphQL

const root = ReactDOM.createRoot(document.getElementById('graphiql')); const fetcher = GraphiQL.createFetcher({ url: 'http://localhost:33333/v1/graphql', - // ToDo: fetch the secret from a secured endpoint! headers: { 'x-hasura-admin-secret': '{{ secret }}' }, }); const explorerPlugin = GraphiQLPluginExplorer.explorerPlugin(); From 779207bb4f0f2efe324464e851efb122efa7cd63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Stucke?= Date: Thu, 13 Jun 2024 15:04:04 +0200 Subject: [PATCH 13/46] graphql template: remove tabs --- .../templates/database/database_graphql.html | 73 +++++-------------- 1 file changed, 19 insertions(+), 54 deletions(-) diff --git a/src/web_interface/templates/database/database_graphql.html b/src/web_interface/templates/database/database_graphql.html index fbffc35e2..b9b47bbdd 100644 --- a/src/web_interface/templates/database/database_graphql.html +++ b/src/web_interface/templates/database/database_graphql.html @@ -21,51 +21,27 @@
-

Advanced Search with GraphQL

- - - - - - - -
- +

Database Search with GraphQL

+ +
+
+
+ +
+ +
-
- -
+
GraphiQL query editor:
+
Loading...
+ + + Documentation on writing Hasura postgres GraphQL queries +
From 3248adc439dc1fd13c140b3d27bacf9827a39873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Stucke?= Date: Thu, 13 Jun 2024 17:25:06 +0200 Subject: [PATCH 14/46] graphql template: replaced textarea with codemirror --- .../templates/database/database_graphql.html | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/web_interface/templates/database/database_graphql.html b/src/web_interface/templates/database/database_graphql.html index b9b47bbdd..4039450eb 100644 --- a/src/web_interface/templates/database/database_graphql.html +++ b/src/web_interface/templates/database/database_graphql.html @@ -8,11 +8,32 @@ + {% endblock %} {% block styles %} + + {% endblock %} @@ -33,6 +54,7 @@

Database Search with GraphQL

Search Database +
Note: You can only search for file_objects, firmware and analyses and your query must include the uid field.
GraphiQL query editor:
@@ -52,6 +74,9 @@
GraphiQL query editor:
plugins: [explorerPlugin], }), ); + CodeMirror.fromTextArea(document.getElementById('textarea'), { + lineNumbers: true, + }); From 711a5c187cef78f7c7515eef23408a9071b5dafa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Stucke?= Date: Fri, 14 Jun 2024 14:42:09 +0200 Subject: [PATCH 15/46] graphql db search: search only with $where filter instead of whole free text graphql query --- src/storage/graphql/interface.py | 89 +++++++++++++--- .../components/database_routes.py | 88 ++++++++------- .../templates/database/database_graphql.html | 100 +++++++++--------- 3 files changed, 175 insertions(+), 102 deletions(-) diff --git a/src/storage/graphql/interface.py b/src/storage/graphql/interface.py index f4286e821..c3a5d3230 100644 --- a/src/storage/graphql/interface.py +++ b/src/storage/graphql/interface.py @@ -4,7 +4,6 @@ from gql import Client, gql from gql.transport.aiohttp import AIOHTTPTransport, log -from graphql import GraphQLSyntaxError import config @@ -19,21 +18,79 @@ client = Client(transport=transport) log.setLevel(logging.WARNING) # disable noisy gql logs +FO_QUERY = """ +query file_object($where: file_object_bool_exp, $limit: Int, $offset: Int) { + file_object_aggregate(where: $where) { + aggregate { + totalCount: count + } + } + file_object(where: $where, limit: $limit, offset: $offset) { + uid + } +} +""" +FW_QUERY = """ +query firmware($where: firmware_bool_exp, $limit: Int, $offset: Int) { + firmware_aggregate(where: $where) { + aggregate { + totalCount: count + } + } + firmware(where: $where, limit: $limit, offset: $offset, order_by: {vendor: asc}) { + uid + } +} +""" +ANALYSIS_QUERY = """ +query analysis($where: analysis_bool_exp, $limit: Int, $offset: Int) { + analysis_aggregate(where: $where, distinct_on: uid) { + aggregate { + totalCount: count + } + } + analysis(where: $where, limit: $limit, offset: $offset, distinct_on: uid) { + uid + } +} +""" +TABLE_TO_QUERY = { + 'file_object': FO_QUERY, + 'firmware': FW_QUERY, + 'analysis': ANALYSIS_QUERY, +} + + +class GraphQLSearchError(Exception): + pass + + +def search_gql( + where: dict, + table: str, + offset: int | None = None, + limit: int | None = None, +) -> tuple[list[str], int]: + """ + Search the database using a GraphQL query. -def search_gql(query_str: str) -> list[str]: - query = gql(query_str) - response = client.execute(query) - result = [] - for value_list in response.values(): - for dict_ in value_list: - if uid := dict_.get('uid', dict_.get('file_uid')): - result.append(uid) - return result + :param where: $where part of the query as dict. + :param table: name of the table we are searching. Must be one of "file_object", "firmware", "analysis". + :param offset: number of items to skip. + :param limit: number of items to return. + :return: Tuple with a list of matching uids and the total number of matches. + """ + variables = {'where': where} + if offset is not None: + variables['offset'] = offset + if limit is not None: + variables['limit'] = limit + if not (query := TABLE_TO_QUERY[table]): + raise GraphQLSearchError(f'Unknown table {table}') -def validate_gql(query_str: str) -> tuple[bool, str]: - try: - gql(query_str) - return True, '' - except GraphQLSyntaxError as error: - return False, str(error) + response = client.execute(gql(query), variable_values=variables) + total = response.get(f'{table}_aggregate', {}).get('aggregate', {}).get('totalCount') + if not total: + raise GraphQLSearchError('Could not determine total result count') + return [e['uid'] for e in response.get(table, {})], total diff --git a/src/web_interface/components/database_routes.py b/src/web_interface/components/database_routes.py index c5a114686..dcd3e9bb8 100644 --- a/src/web_interface/components/database_routes.py +++ b/src/web_interface/components/database_routes.py @@ -6,8 +6,11 @@ from datetime import datetime from enum import Enum from itertools import chain +from json import JSONDecodeError from flask import flash, redirect, render_template, request, url_for +from gql.transport.exceptions import TransportQueryError +from graphql import GraphQLSyntaxError from sqlalchemy.exc import SQLAlchemyError import config @@ -17,7 +20,7 @@ from helperFunctions.uid import is_uid from helperFunctions.web_interface import apply_filters_to_query, filter_out_illegal_characters from helperFunctions.yara_binary_search import get_yara_error, is_valid_yara_rule_file -from storage.graphql.interface import search_gql, validate_gql +from storage.graphql.interface import TABLE_TO_QUERY, GraphQLSearchError, search_gql from storage.query_conversion import QueryConversionException from web_interface.components.component_base import GET, POST, AppRoute, ComponentBase from web_interface.pagination import extract_pagination_from_request, get_pagination @@ -27,12 +30,6 @@ @dataclass class SearchParameters: - query: dict | str - only_firmware: bool - inverted: bool - search_target: TargetType - query_title: str - class TargetType(str, Enum): yara = 'YARA' graphql = 'GraphQL' @@ -40,6 +37,12 @@ class TargetType(str, Enum): firmware = 'Firmware' inverted = 'Inverse Firmware' + query: dict | str + only_firmware: bool + inverted: bool + search_target: TargetType + query_title: str + class DatabaseRoutes(ComponentBase): @staticmethod @@ -59,28 +62,35 @@ def _add_date_to_query(query, date): @AppRoute('/database/browse', GET) def browse_database(self, query: str = '{}'): page, per_page = extract_pagination_from_request(request)[0:2] - search_parameters = self._get_search_parameters(query) - - if search_parameters.search_target == 'GraphQL': - matches = search_gql(search_parameters.query) + offset, limit = per_page * (page - 1), per_page + total = None + parameters = self._get_search_parameters(query) - if not matches: - flash('Error: No matches found. Did you forget to include the UID field in the query?') - return redirect(url_for(self.get_graphql.__name__)) - - # ToDo: move paging to GraphQL - search_parameters.query = {'uid': {'$in': matches}} + if parameters.search_target == SearchParameters.TargetType.graphql: + where = parameters.query.get('where', {}) + table = parameters.query.get('table') + try: + matches, total = search_gql(where, table, offset=offset, limit=limit) + except (GraphQLSearchError, GraphQLSyntaxError, TransportQueryError) as error: + if hasattr(error, 'errors') and error.errors: + error = ', '.join(err.get('message') for err in error.errors if err) + message = f'Error during GraphQL search: {error}' + logging.exception(message) + flash(message) + return redirect(url_for(self.get_graphql.__name__, last_query=json.dumps(where))) + parameters.query = {'uid': {'$in': matches}} # update query for "generic search" + offset, limit = None, None # should only be applied once with get_shared_session(self.db.frontend) as frontend_db: try: firmware_list = self._search_database( - search_parameters.query, - skip=per_page * (page - 1), - limit=per_page, - only_firmwares=search_parameters.only_firmware, - inverted=search_parameters.inverted, + parameters.query, + skip=offset, + limit=limit, + only_firmwares=parameters.only_firmware, + inverted=parameters.inverted, ) - if self._query_has_only_one_result(firmware_list, search_parameters.query): + if self._query_has_only_one_result(firmware_list, parameters.query): return redirect(url_for('show_analysis', uid=firmware_list[0][0])) except QueryConversionException as exception: error_message = exception.get_message() @@ -90,9 +100,10 @@ def browse_database(self, query: str = '{}'): logging.error(error_message + f' due to exception: {err}', exc_info=True) return render_template('error.html', message=error_message) - total = frontend_db.get_number_of_total_matches( - search_parameters.query, search_parameters.only_firmware, inverted=search_parameters.inverted - ) + if not total: + total = frontend_db.get_number_of_total_matches( + parameters.query, parameters.only_firmware, parameters.inverted + ) device_classes = frontend_db.get_device_class_list() vendors = frontend_db.get_vendor_list() @@ -107,7 +118,7 @@ def browse_database(self, query: str = '{}'): vendors=vendors, current_class=str(request.args.get('device_class')), current_vendor=str(request.args.get('vendor')), - search_parameters=search_parameters, + search_parameters=parameters, ) @roles_accepted(*PRIVILEGES['pattern_search']) @@ -335,25 +346,30 @@ def get_graphql(self): return render_template( 'database/database_graphql.html', secret=config.frontend.hasura.admin_secret, + port=config.frontend.hasura.port, + tables=TABLE_TO_QUERY, + last_query=request.args.get('last_query'), ) @roles_accepted(*PRIVILEGES['advanced_search']) @AppRoute('/database/graphql', POST) def post_graphql(self): - query_str = request.form.get('textarea') - if not query_str: - flash('Error: GraphQL query not found in form') - return redirect(url_for(self.get_graphql.__name__)) - - is_valid, error = validate_gql(query_str) - if not is_valid: - flash(f'Error: GraphQL syntax error: {error}') + where_str = request.form.get('textarea') + try: + where = json.loads(where_str) + except JSONDecodeError as error: + flash(f'Error: JSON decoding error: {error}') + return redirect(url_for(self.get_graphql.__name__, last_query=where_str)) + + table = request.form.get('tableRadio') + if not (where_str or table): + flash('Error: GraphQL query or table not found in request') return redirect(url_for(self.get_graphql.__name__)) return redirect( url_for( self.browse_database.__name__, - query=json.dumps(query_str), + query=json.dumps({'where': where, 'table': table}), graphql=True, ) ) diff --git a/src/web_interface/templates/database/database_graphql.html b/src/web_interface/templates/database/database_graphql.html index fbffc35e2..4edfbe9e8 100644 --- a/src/web_interface/templates/database/database_graphql.html +++ b/src/web_interface/templates/database/database_graphql.html @@ -23,44 +23,59 @@

Advanced Search with GraphQL

- - - -
- - -
- - -
-
From a506dbb1ca5efa0c555a46610cc1af56aa3bae0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Stucke?= Date: Mon, 17 Jun 2024 11:45:31 +0200 Subject: [PATCH 16/46] tests: fixed test config for new graphql cfg entries --- src/conftest.py | 1 + src/test/data/fact-core-config.toml | 4 ++++ src/test/data/fact-core-config.toml-missing-entrys | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/src/conftest.py b/src/conftest.py index 1a49ac526..fe23364e4 100644 --- a/src/conftest.py +++ b/src/conftest.py @@ -144,6 +144,7 @@ def frontend_config(request, common_config) -> config.Frontend: 'user_database': 'sqlite:////media/data/fact_auth_data/fact_users.db', 'password_salt': '5up3r5tr0n6_p455w0rd_5417', }, + 'hasura': {'admin_secret': 'admin_secret'}, } test_config.update(common_config.model_dump()) diff --git a/src/test/data/fact-core-config.toml b/src/test/data/fact-core-config.toml index f48535888..146c41b73 100644 --- a/src/test/data/fact-core-config.toml +++ b/src/test/data/fact-core-config.toml @@ -113,3 +113,7 @@ communication-timeout = 60 enabled = false user-database = "sqlite:////media/data/fact_auth_data/fact_users.db" password-salt = "5up3r5tr0n6_p455w0rd_5417" + + +[frontend.hasura] +admin-secret = "4dM1n_S3cR3T_changemeplz" diff --git a/src/test/data/fact-core-config.toml-missing-entrys b/src/test/data/fact-core-config.toml-missing-entrys index ff2c0ae0f..60bb59e36 100644 --- a/src/test/data/fact-core-config.toml-missing-entrys +++ b/src/test/data/fact-core-config.toml-missing-entrys @@ -113,3 +113,7 @@ communication-timeout = 60 enabled = false user-database = "sqlite:////media/data/fact_auth_data/fact_users.db" password-salt = "5up3r5tr0n6_p455w0rd_5417" + + +[frontend.hasura] +admin-secret = "4dM1n_S3cR3T_changemeplz" From e162f64d4f5e70672b245af568d502d29c978e9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Stucke?= Date: Mon, 17 Jun 2024 12:10:59 +0200 Subject: [PATCH 17/46] graphql db template: improve help modal --- .../templates/database/database_graphql.html | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/web_interface/templates/database/database_graphql.html b/src/web_interface/templates/database/database_graphql.html index 4edfbe9e8..df7476dc0 100644 --- a/src/web_interface/templates/database/database_graphql.html +++ b/src/web_interface/templates/database/database_graphql.html @@ -33,7 +33,7 @@

Advanced Search with GraphQL

{# help button to open help modal #}
@@ -63,10 +63,14 @@
@@ -91,6 +95,13 @@ plugins: [explorerPlugin], }), ); + + function copyToClipboard(elementId) { + const text = document.getElementById(elementId).innerText.trim(); + navigator.clipboard.writeText(text).then(() => {}, function(err) { + console.error('Async: Could not copy to clipboard: ', err); + }); + } From 8c8436605d133f5d44cd0b54b5e24cfe6a1685d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Stucke?= Date: Tue, 18 Jun 2024 17:39:29 +0200 Subject: [PATCH 18/46] graphql template: improved template + added docs/examples --- src/storage/graphql/interface.py | 24 ++++ .../components/database_routes.py | 6 +- .../templates/database/database_graphql.html | 128 ++++++++++++------ 3 files changed, 117 insertions(+), 41 deletions(-) diff --git a/src/storage/graphql/interface.py b/src/storage/graphql/interface.py index c3a5d3230..626473e9b 100644 --- a/src/storage/graphql/interface.py +++ b/src/storage/graphql/interface.py @@ -59,6 +59,30 @@ 'firmware': FW_QUERY, 'analysis': ANALYSIS_QUERY, } +# these queries are simplified versions of the ones above that are displayed in the web interface +TEMPLATE_QUERIES = { + 'file_object': ( + 'query file_object_query($where: file_object_bool_exp) {\n' + ' file_object(where: $where) {\n' + ' uid\n' + ' }\n' + '}' + ), + 'firmware': ( + 'query firmware_query($where: file_object_bool_exp) {\n' + ' firmware(where: $where, order_by: {vendor: asc}) {\n' + ' uid\n' + ' }\n' + '}' + ), + 'analysis': ( + 'query analysis_query($where: file_object_bool_exp) {\n' + ' analysis(where: $where, distinct_on: uid) {\n' + ' uid\n' + ' }\n' + '}' + ), +} class GraphQLSearchError(Exception): diff --git a/src/web_interface/components/database_routes.py b/src/web_interface/components/database_routes.py index dcd3e9bb8..ea065252c 100644 --- a/src/web_interface/components/database_routes.py +++ b/src/web_interface/components/database_routes.py @@ -20,7 +20,7 @@ from helperFunctions.uid import is_uid from helperFunctions.web_interface import apply_filters_to_query, filter_out_illegal_characters from helperFunctions.yara_binary_search import get_yara_error, is_valid_yara_rule_file -from storage.graphql.interface import TABLE_TO_QUERY, GraphQLSearchError, search_gql +from storage.graphql.interface import TEMPLATE_QUERIES, GraphQLSearchError, search_gql from storage.query_conversion import QueryConversionException from web_interface.components.component_base import GET, POST, AppRoute, ComponentBase from web_interface.pagination import extract_pagination_from_request, get_pagination @@ -347,7 +347,7 @@ def get_graphql(self): 'database/database_graphql.html', secret=config.frontend.hasura.admin_secret, port=config.frontend.hasura.port, - tables=TABLE_TO_QUERY, + tables=TEMPLATE_QUERIES, last_query=request.args.get('last_query'), ) @@ -361,7 +361,7 @@ def post_graphql(self): flash(f'Error: JSON decoding error: {error}') return redirect(url_for(self.get_graphql.__name__, last_query=where_str)) - table = request.form.get('tableRadio') + table = request.form.get('tableSelect') if not (where_str or table): flash('Error: GraphQL query or table not found in request') return redirect(url_for(self.get_graphql.__name__)) diff --git a/src/web_interface/templates/database/database_graphql.html b/src/web_interface/templates/database/database_graphql.html index 4a52ea7b6..902fff244 100644 --- a/src/web_interface/templates/database/database_graphql.html +++ b/src/web_interface/templates/database/database_graphql.html @@ -9,21 +9,24 @@ + + {% endblock %} {% block styles %} + {% endblock %} @@ -44,31 +58,33 @@

Database Search with GraphQL

- {% for table in tables %} -
- - - {# help button to open help modal #} - +
+
+
- {% endfor %} - -
-
-
-
{% for table, query in tables.items() %} @@ -84,9 +100,7 @@ {% endfor %} -
GraphiQL query editor:
+

Example queries:

+ +

+ On table "firmware": + {"device_class": {"_eq": "Router"}}
+ Select firmware with device class "Router" +

+

+ On table "firmware": + {vendor: {_ilike: "%link%"}}
+ Select firmware whose vendor name contains the string "link" (case-insensitive) +

+

+ On table "file_object": + {"file_name": {"_eq": "busybox"}}
+ Select files with file name "busybox" +

+

+ On table "analysis": + {"plugin": {"_eq": "file_type"}, "result": {"_contains": {"mime": "text/plain"}}}
+ Select files with MIME type "text/plain" +

+

+ On table "analysis": + {"plugin": {"_eq": "software_components"}, "summary": {"_contains": ["OpenSSL 1.0.1e"]}}
+ Select files where software OpenSSL matched in version 1.0.1e +

+

+ On table "file_object": + {"is_firmware": {"_eq": true}, "fwFilesByRootUid": {"fileObjectByFileUid": {"file_name": {"_eq": "busybox"}}}}
+ Find firmware that contains a file with the name "busybox" +

+

+ If you need additional information, try clicking on the Docs or try playing around with the Explorer in the menu on the left of GraphiQL below.
+ If you want to know what fields can be used for queries, try looking up "analysis_bool_exp", "file_object_bool_exp" or + "firmware_bool_exp" in the Docs of GraphiQL.
+ You can find documentation on $where and operators here: Documentation on writing Hasura postgres GraphQL queries +

+ +

GraphiQL query editor:

Loading...
- - - Documentation on writing Hasura postgres GraphQL queries - From b52bb780508e87ace82e2ccbe831ae71f62ea770 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Stucke?= Date: Mon, 3 Jun 2024 17:06:52 +0200 Subject: [PATCH 19/46] crypto hints: ruff fixes --- .../analysis/crypto_hints/code/crypto_hints.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/plugins/analysis/crypto_hints/code/crypto_hints.py b/src/plugins/analysis/crypto_hints/code/crypto_hints.py index c48895632..76853f222 100644 --- a/src/plugins/analysis/crypto_hints/code/crypto_hints.py +++ b/src/plugins/analysis/crypto_hints/code/crypto_hints.py @@ -1,15 +1,18 @@ -import io +from __future__ import annotations + +from typing import TYPE_CHECKING, List import pydantic -import typing -from analysis.plugin import addons, compat -from analysis.plugin import AnalysisPluginV0 +from analysis.plugin import AnalysisPluginV0, addons, compat + +if TYPE_CHECKING: + import io class AnalysisPlugin(AnalysisPluginV0, compat.AnalysisBasePluginAdapterMixin): class Schema(pydantic.BaseModel): - matches: typing.List[dict] + matches: List[dict] def __init__(self): metadata = AnalysisPluginV0.MetaData( From e5b4a557a72785b500b64ace172837efe76c782d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Stucke?= Date: Wed, 19 Jun 2024 10:42:33 +0200 Subject: [PATCH 20/46] hasura scripts: fix import path + rename attributes to make them more intuitive --- src/storage/graphql/hasura/init_hasura.py | 33 ++++++++++++++--------- src/storage/graphql/hasura/restart.py | 2 +- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/storage/graphql/hasura/init_hasura.py b/src/storage/graphql/hasura/init_hasura.py index 8d14122cb..d65cc71f0 100644 --- a/src/storage/graphql/hasura/init_hasura.py +++ b/src/storage/graphql/hasura/init_hasura.py @@ -11,7 +11,7 @@ try: import config except ImportError: - SRC_DIR = Path(__file__).parent.parent.parent + SRC_DIR = Path(__file__).parent.parent.parent.parent sys.path.append(str(SRC_DIR)) import config @@ -33,35 +33,42 @@ # table, name, constraint ('analysis', 'file_object', 'uid'), ('firmware', 'file_object', 'uid'), - ('fw_files', 'fileObjectByRootUid', 'root_uid'), - ('fw_files', 'fileObjectByFileUid', 'file_uid'), - ('fw_files', 'file_object', 'root_uid'), - ('included_files', 'fileObjectByParentUid', 'parent_uid'), - ('included_files', 'file_object', 'child_uid'), + ('fw_files', 'firmware', 'root_uid'), + ('fw_files', 'file_object', 'file_uid'), + ('included_files', 'parent', 'parent_uid'), + ('included_files', 'child', 'child_uid'), ('file_object', 'firmware', {'column': 'uid', 'table': {'name': 'firmware', 'schema': 'public'}}), ], 'pg_create_array_relationship': [ - ('file_object', 'analyses', {'column': 'uid', 'table': {'name': 'analysis', 'schema': 'public'}}), - ('file_object', 'fwFilesByRootUid', {'column': 'root_uid', 'table': {'name': 'fw_files', 'schema': 'public'}}), - ('file_object', 'fw_files', {'column': 'file_uid', 'table': {'name': 'fw_files', 'schema': 'public'}}), + ('file_object', 'analysis', {'column': 'uid', 'table': {'name': 'analysis', 'schema': 'public'}}), ( 'file_object', - 'includedFilesByParentUid', + 'firmwareFilesByFirmware', + {'column': 'root_uid', 'table': {'name': 'fw_files', 'schema': 'public'}}, + ), + ( + 'file_object', + 'firmwareFilesByFile', + {'column': 'file_uid', 'table': {'name': 'fw_files', 'schema': 'public'}}, + ), + ( + 'file_object', + 'includedFilesByParent', {'column': 'parent_uid', 'table': {'name': 'included_files', 'schema': 'public'}}, ), ( 'file_object', - 'included_files', + 'includedFilesByChild', {'column': 'child_uid', 'table': {'name': 'included_files', 'schema': 'public'}}, ), ( 'file_object', - 'virtualFilePathsByParentUid', + 'FilePathsByParent', {'column': 'parent_uid', 'table': {'name': 'virtual_file_path', 'schema': 'public'}}, ), ( 'file_object', - 'virtual_file_paths', + 'FilePathsByFile', {'column': 'file_uid', 'table': {'name': 'virtual_file_path', 'schema': 'public'}}, ), ], diff --git a/src/storage/graphql/hasura/restart.py b/src/storage/graphql/hasura/restart.py index a0563fd36..12facbb86 100644 --- a/src/storage/graphql/hasura/restart.py +++ b/src/storage/graphql/hasura/restart.py @@ -10,7 +10,7 @@ import config from helperFunctions.install import OperateInDirectory except ImportError: - SRC_DIR = Path(__file__).parent.parent.parent + SRC_DIR = Path(__file__).parent.parent.parent.parent sys.path.append(str(SRC_DIR)) import config from helperFunctions.install import OperateInDirectory From d8a715a2c883da38967479a314f3de697844940e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Stucke?= Date: Wed, 19 Jun 2024 10:59:08 +0200 Subject: [PATCH 21/46] graphql search: allow queries with unquoted keys --- src/install/requirements_frontend.txt | 1 + src/storage/graphql/interface.py | 6 ++++-- .../components/database_routes.py | 18 ++++++++++++++---- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/install/requirements_frontend.txt b/src/install/requirements_frontend.txt index 7a4ac4370..eef40ca29 100644 --- a/src/install/requirements_frontend.txt +++ b/src/install/requirements_frontend.txt @@ -9,6 +9,7 @@ flask-restx~=1.3.0 flask-sqlalchemy~=3.1.1 gql~=3.5.0 itsdangerous~=2.2.0 +json5~=0.9.25 matplotlib~=3.7.5 more-itertools~=10.2.0 prompt-toolkit~=3.0.43 diff --git a/src/storage/graphql/interface.py b/src/storage/graphql/interface.py index 626473e9b..51500ba3d 100644 --- a/src/storage/graphql/interface.py +++ b/src/storage/graphql/interface.py @@ -115,6 +115,8 @@ def search_gql( response = client.execute(gql(query), variable_values=variables) total = response.get(f'{table}_aggregate', {}).get('aggregate', {}).get('totalCount') - if not total: - raise GraphQLSearchError('Could not determine total result count') + if total == 0: + raise GraphQLSearchError('No results found.') + if total is None: + raise GraphQLSearchError('Could not determine total result count.') return [e['uid'] for e in response.get(table, {})], total diff --git a/src/web_interface/components/database_routes.py b/src/web_interface/components/database_routes.py index ea065252c..2e68cf322 100644 --- a/src/web_interface/components/database_routes.py +++ b/src/web_interface/components/database_routes.py @@ -2,12 +2,13 @@ import json import logging +import re from dataclasses import dataclass from datetime import datetime from enum import Enum from itertools import chain -from json import JSONDecodeError +import json5 from flask import flash, redirect, render_template, request, url_for from gql.transport.exceptions import TransportQueryError from graphql import GraphQLSyntaxError @@ -356,9 +357,12 @@ def get_graphql(self): def post_graphql(self): where_str = request.form.get('textarea') try: - where = json.loads(where_str) - except JSONDecodeError as error: - flash(f'Error: JSON decoding error: {error}') + where = json5.loads(where_str) + except ValueError as error: + if where_str == '': + flash('Error: Query is empty') + else: + flash(f'Error: JSON decoding error: {error}') return redirect(url_for(self.get_graphql.__name__, last_query=where_str)) table = request.form.get('tableSelect') @@ -373,3 +377,9 @@ def post_graphql(self): graphql=True, ) ) + + @staticmethod + def _fix_where_filter(where: str) -> str: + # in case someone dumps an unquoted string into the query directly from GraphiQL, + # we can try to fix it by adding some quotes + return re.sub(r'([{,])([a-zA-Z0-9_]+)(?=:)', r'\g<1>"\g<2>"', where) From b557decf3624ea9395596e9583273b43676209f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Stucke?= Date: Wed, 19 Jun 2024 11:08:44 +0200 Subject: [PATCH 22/46] graphql template: adjusted template for hasura schema changes --- .../templates/database/database_graphql.html | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/src/web_interface/templates/database/database_graphql.html b/src/web_interface/templates/database/database_graphql.html index 902fff244..3022259e4 100644 --- a/src/web_interface/templates/database/database_graphql.html +++ b/src/web_interface/templates/database/database_graphql.html @@ -59,8 +59,8 @@

Database Search with GraphQL


-
@@ -100,8 +100,10 @@
+

GraphiQL query editor:

Loading...
+ {% endblock %} {% block styles %} @@ -42,6 +43,9 @@ .graphiql-container { min-height: 666px; } + .block-button { + width: 110px; + } {% endblock %} @@ -55,6 +59,7 @@

Database Search with GraphQL


+