kopia lustrzana https://github.com/bugout-dev/moonstream
Merge branch 'fix-dashbord-worker-filters' into customized-dashboard
commit
a191f50ce0
22
README.md
22
README.md
|
@ -54,6 +54,28 @@ This monorepo contains the following components:
|
|||
[Bugout](https://bugout.dev). For more information on how that data is processed, check how the API
|
||||
inserts events from those sources into a stream.
|
||||
|
||||
### Installation and setup
|
||||
|
||||
#### Run server with Docker Compose
|
||||
|
||||
If you want to deploy Moonstream in isolation against live services, then docker compose is your choice!
|
||||
|
||||
- Run script `backend/configs/docker_generate_env.bash` which prepare for you:
|
||||
- `backend/configs/docker.moonstreamapi.env` with environment variables
|
||||
- Run script `db/configs/docker_generate_env.bash` which prepare for you:
|
||||
- `db/configs/alembic.moonstreamdb.ini` with postgresql uri
|
||||
|
||||
```bash
|
||||
./backend/configs/docker_generate_env.bash
|
||||
./db/configs/docker_generate_env.bash
|
||||
```
|
||||
|
||||
- Run local setup
|
||||
|
||||
```bash
|
||||
docker-compose up --build
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
If you would like to contribute to Moonstream, please reach out to @zomglings on the [Moonstream Discord](https://discord.gg/pYE65FuNSz).
|
||||
|
|
|
@ -0,0 +1,220 @@
|
|||
# Created by https://www.toptal.com/developers/gitignore/api/vim,visualstudiocode,linux,python
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=vim,visualstudiocode,linux,python
|
||||
|
||||
### Linux ###
|
||||
*~
|
||||
|
||||
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||
.fuse_hidden*
|
||||
|
||||
# KDE directory preferences
|
||||
.directory
|
||||
|
||||
# Linux trash folder which might appear on any partition or disk
|
||||
.Trash-*
|
||||
|
||||
# .nfs files are created when an open file is removed but is still being accessed
|
||||
.nfs*
|
||||
|
||||
### Python ###
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
pip-wheel-metadata/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
### Vim ###
|
||||
# Swap
|
||||
[._]*.s[a-v][a-z]
|
||||
!*.svg # comment out if you don't need vector files
|
||||
[._]*.sw[a-p]
|
||||
[._]s[a-rt-v][a-z]
|
||||
[._]ss[a-gi-z]
|
||||
[._]sw[a-p]
|
||||
|
||||
# Session
|
||||
Session.vim
|
||||
Sessionx.vim
|
||||
|
||||
# Temporary
|
||||
.netrwhist
|
||||
# Auto-generated tag files
|
||||
tags
|
||||
# Persistent undo
|
||||
[._]*.un~
|
||||
|
||||
### VisualStudioCode ###
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
*.code-workspace
|
||||
|
||||
### Pycharm ###
|
||||
.idea
|
||||
|
||||
### VisualStudioCode Patch ###
|
||||
# Ignore all local history of files
|
||||
.history
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/vim,visualstudiocode,linux,python
|
||||
|
||||
### VSCode ###
|
||||
.vscode
|
||||
|
||||
# Git
|
||||
.git
|
||||
.github
|
||||
|
||||
# Envionments
|
||||
.moonstream
|
||||
.moonstreamapi
|
||||
.venv
|
||||
.env
|
||||
|
||||
# Environment variables
|
||||
test.env
|
||||
dev.env
|
||||
prod.env
|
||||
|
||||
configs/test.env
|
||||
configs/dev.env
|
||||
configs/prod.env
|
||||
configs/moonstreamapi.env
|
||||
configs/docker.env
|
||||
configs/docker.dev.env
|
||||
configs/docker.test.env
|
||||
configs/docker.prod.env
|
||||
|
||||
.secrets/
|
||||
|
||||
Dockerfile
|
||||
docker-compose.yml
|
|
@ -160,9 +160,21 @@ cython_debug/
|
|||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/python,visualstudiocode
|
||||
|
||||
# Custom
|
||||
# Envionments
|
||||
.moonstream/
|
||||
.moonstreamapi/
|
||||
.venv/
|
||||
.env/
|
||||
|
||||
# Environment variables
|
||||
dev.env
|
||||
test.env
|
||||
prod.env
|
||||
.moonstream
|
||||
.venv
|
||||
.secrets
|
||||
moonstreamapi.env
|
||||
docker.env
|
||||
docker.dev.env
|
||||
docker.test.dev
|
||||
docker.prod.env
|
||||
docker.moonstreamapi.env
|
||||
|
||||
.secrets/
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
FROM python:3.8-slim-buster
|
||||
|
||||
# Update packages and
|
||||
# prepare alembic for docker compose setup
|
||||
RUN apt-get update && \
|
||||
apt-get install -y libpq-dev gcc curl && \
|
||||
rm -rf /var/lib/apt/lists/* && \
|
||||
pip3 install --no-cache-dir --upgrade pip setuptools && \
|
||||
pip3 install --no-cache-dir psycopg2-binary alembic
|
||||
|
||||
WORKDIR /usr/src/moonstreamapi
|
||||
|
||||
COPY . /usr/src/moonstreamapi
|
||||
|
||||
# Install Moonstream API package
|
||||
RUN pip3 install --no-cache-dir -e .
|
||||
|
||||
EXPOSE 7481
|
||||
|
||||
ENTRYPOINT ["./dev.sh"]
|
|
@ -1 +1,43 @@
|
|||
# moonstream backend
|
||||
|
||||
### Installation and setup
|
||||
|
||||
To set up Moonstream API for development, do the following:
|
||||
|
||||
- Clone the git repository
|
||||
- Install postgresql (https://www.postgresql.org/download/linux/ubuntu/)
|
||||
|
||||
#### Run server with Docker
|
||||
|
||||
To be able to run Moonstream API with your existing local or development services as database, you need to build your own setup. **Be aware! The files with environment variables `docker.dev.env` lives inside your docker container!**
|
||||
|
||||
- Copy `configs/sample.env` to `configs/docker.dev.env`, or use your local configs from `configs/dev.env` to `configs/docker.dev.env`
|
||||
- Edit in `docker.dev.env` file `MOONSTREAM_DB_URI` and other variables if required
|
||||
- Clean environment file from `export ` prefix and quotation marks to be able to use it with Docker
|
||||
|
||||
```bash
|
||||
sed --in-place 's|^export * ||' configs/docker.dev.env
|
||||
sed --in-place 's|"||g' configs/docker.dev.env
|
||||
```
|
||||
|
||||
Build container on your machine
|
||||
|
||||
```bash
|
||||
docker build -t moonstreamapi-dev .
|
||||
```
|
||||
|
||||
Run `moonstreamapi-dev` container, with following command we specified `--network="host"` setting which allows to Docker container use localhost interface of your machine (https://docs.docker.com/network/host/)
|
||||
|
||||
```bash
|
||||
docker run --name moonstreamapi-dev \
|
||||
--network="host" \
|
||||
--env-file="configs/docker.dev.env" \
|
||||
-p 7481:7481/tcp \
|
||||
-ti -d moonstreamapi-dev
|
||||
```
|
||||
|
||||
Attach to container to see logs
|
||||
|
||||
```bash
|
||||
docker container attach moonstreamapi-dev
|
||||
```
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Prepare Moonstream API application for docker-compose use
|
||||
|
||||
# Print help message
|
||||
function usage {
|
||||
echo "Usage: $0 [-h] -d DATABASE_NAME"
|
||||
echo
|
||||
echo "CLI to generate environment variables"
|
||||
echo
|
||||
echo "Optional arguments:"
|
||||
echo " -h Show this help message and exit"
|
||||
echo " -d Database name for postgres in docker-compose setup"
|
||||
}
|
||||
|
||||
FLAG_DATABASE_NAME="moonstream_dev"
|
||||
|
||||
while getopts 'd:' flag; do
|
||||
case "${flag}" in
|
||||
d) FLAG_DATABASE_NAME="${OPTARG}" ;;
|
||||
h) usage
|
||||
exit 1 ;;
|
||||
*) usage
|
||||
exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(realpath $(dirname $0))"
|
||||
DOCKER_MOONSTREAM_DB_URI="postgresql://postgres:postgres@db/$FLAG_DATABASE_NAME"
|
||||
DOCKER_MOONSTREAM_ENV_FILE="docker.moonstreamapi.env"
|
||||
BUGOUT_BROOD_URL="http://brood:7474"
|
||||
BUGOUT_SPIRE_URL="http://spire:7475"
|
||||
|
||||
# Generate environment variables
|
||||
|
||||
cp "$SCRIPT_DIR/sample.env" "$SCRIPT_DIR/$DOCKER_MOONSTREAM_ENV_FILE"
|
||||
|
||||
# Clean file with variables from export prefix and quotation marks
|
||||
sed --in-place 's|^export * ||' "$SCRIPT_DIR/$DOCKER_MOONSTREAM_ENV_FILE"
|
||||
sed --in-place 's|"||g' "$SCRIPT_DIR/$DOCKER_MOONSTREAM_ENV_FILE"
|
||||
|
||||
sed -i "s|^MOONSTREAM_DB_URI=.*|MOONSTREAM_DB_URI=$DOCKER_MOONSTREAM_DB_URI|" "$SCRIPT_DIR/$DOCKER_MOONSTREAM_ENV_FILE"
|
||||
sed -i "s|^BUGOUT_BROOD_URL=.*|BUGOUT_BROOD_URL=$BUGOUT_BROOD_URL|" "$SCRIPT_DIR/$DOCKER_MOONSTREAM_ENV_FILE"
|
||||
sed -i "s|^BUGOUT_SPIRE_URL=.*|BUGOUT_SPIRE_URL=$BUGOUT_SPIRE_URL|" "$SCRIPT_DIR/$DOCKER_MOONSTREAM_ENV_FILE"
|
|
@ -0,0 +1,13 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
set -e
|
||||
|
||||
HOST="$1"
|
||||
shift
|
||||
|
||||
until curl --request GET --url "http://$HOST/ping"; do
|
||||
>&2 echo "$HOST is unavailable, sleeping"
|
||||
sleep 1
|
||||
done
|
||||
|
||||
>&2 echo "$HOST is up"
|
|
@ -0,0 +1,23 @@
|
|||
# Required environment variables
|
||||
export MOONSTREAM_DB_URI="postgresql://<username>:<password>@<db_host>:<db_port>/<db_name>"
|
||||
export MOONSTREAM_CORS_ALLOWED_ORIGINS="http://localhost:3000,https://moonstream.to,https://www.moonstream.to"
|
||||
export BUGOUT_BROOD_URL="https://auth.bugout.dev"
|
||||
export BUGOUT_SPIRE_URL="https://spire.bugout.dev"
|
||||
export MOONSTREAM_APPLICATION_ID="<issued_bugout_application_id>"
|
||||
export MOONSTREAM_ADMIN_ACCESS_TOKEN="<Access_token_to_application_resources>"
|
||||
export MOONSTREAM_POOL_SIZE=0
|
||||
|
||||
# Blockchain, txpool, whalewatch data depends variables
|
||||
export MOONSTREAM_DATA_JOURNAL_ID="<bugout_journal_id_to_store_blockchain_data>"
|
||||
export HUMBUG_TXPOOL_CLIENT_ID="<Bugout_Humbug_client_id_for_txpool_transactions_in_journal>"
|
||||
export MOONSTREAM_ETHEREUM_WEB3_PROVIDER_URI="https://<connection_path_uri_to_ethereum_node>"
|
||||
export MOONSTREAM_NODE_ETHEREUM_IPC_PORT=8545
|
||||
|
||||
# Set following parameters if AWS node instance and S3 smartcontracts configured
|
||||
export MOONSTREAM_INTERNAL_HOSTED_ZONE_ID="<moonstream_internal_hosted_zone_id>"
|
||||
export MOONSTREAM_S3_SMARTCONTRACTS_BUCKET="<AWS_S3_bucket_to_store_smart_contracts>"
|
||||
export MOONSTREAM_S3_SMARTCONTRACTS_ABI_BUCKET="<AWS_S3_bucket_to_store_smart_contracts_ABI>"
|
||||
export MOONSTREAM_S3_SMARTCONTRACTS_ABI_PREFIX="<Previx_for_AWS_S3_bucket_(prod,dev,..)>"
|
||||
|
||||
# Set the following variables in the most reasonable manner for your development environment
|
||||
export HUMBUG_REPORTER_BACKEND_TOKEN="<Bugout_umbug_token_for_crash_reports>"
|
|
@ -1,9 +1,19 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
# Expects access to Python environment with the requirements for this project installed.
|
||||
# Sets up Moonstream API server
|
||||
# Expects access to Python environment with the requirements
|
||||
# for this project installed.
|
||||
set -e
|
||||
|
||||
MOONSTREAM_HOST="${MOONSTREAM_HOST:-0.0.0.0}"
|
||||
MOONSTREAM_PORT="${MOONSTREAM_PORT:-7481}"
|
||||
MOONSTREAMAPI_HOST="${MOONSTREAMAPI_HOST:-127.0.0.1}"
|
||||
MOONSTREAMAPI_PORT="${MOONSTREAMAPI_PORT:-7481}"
|
||||
MOONSTREAMAPI_APP_DIR="${MOONSTREAMAPI_APP_DIR:-$PWD}"
|
||||
MOONSTREAMAPI_ASGI_APP="${MOONSTREAMAPI_ASGI_APP:-moonstreamapi.api:app}"
|
||||
MOONSTREAMAPI_UVICORN_WORKERS="${MOONSTREAMAPI_UVICORN_WORKERS:-2}"
|
||||
|
||||
uvicorn --port "$MOONSTREAM_PORT" --host "$MOONSTREAM_HOST" moonstreamapi.api:app --reload
|
||||
uvicorn --reload \
|
||||
--port "$MOONSTREAMAPI_PORT" \
|
||||
--host "$MOONSTREAMAPI_HOST" \
|
||||
--app-dir "$MOONSTREAMAPI_APP_DIR" \
|
||||
--workers "$MOONSTREAMAPI_UVICORN_WORKERS" \
|
||||
"$MOONSTREAMAPI_ASGI_APP"
|
|
@ -16,7 +16,7 @@ from sqlalchemy.orm import with_expression
|
|||
from ..settings import BUGOUT_BROOD_URL, BUGOUT_SPIRE_URL, MOONSTREAM_APPLICATION_ID
|
||||
from ..web3_provider import yield_web3_provider
|
||||
from . import subscription_types, subscriptions
|
||||
from .migrations import checksum_address
|
||||
from .migrations import checksum_address, update_dashboard_subscription_key
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
@ -74,6 +74,8 @@ def migrations_run(args: argparse.Namespace) -> None:
|
|||
checksum_address.checksum_all_subscription_addresses(web3_session)
|
||||
logger.info("Starting update of ethereum_labels in database...")
|
||||
checksum_address.checksum_all_labels_addresses(db_session, web3_session)
|
||||
elif args.id == 20211202:
|
||||
update_dashboard_subscription_key.update_dashboard_resources_key()
|
||||
else:
|
||||
drop_keys = []
|
||||
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
"""
|
||||
Convert all addresses in user subscriptions
|
||||
and ethereum_labels column to checksum address.
|
||||
"""
|
||||
import logging
|
||||
from typing import List
|
||||
|
||||
from bugout.data import BugoutResources
|
||||
from bugout.exceptions import BugoutResponseException
|
||||
|
||||
from ...settings import BUGOUT_REQUEST_TIMEOUT_SECONDS, MOONSTREAM_ADMIN_ACCESS_TOKEN
|
||||
from ...settings import bugout_client as bc
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
BUGOUT_RESOURCE_TYPE_DASHBOARD = "dashboards"
|
||||
|
||||
|
||||
def update_dashboard_resources_key() -> None:
|
||||
"""
|
||||
Parse all existing dashboards at Brood resource
|
||||
and replace key to correct one.
|
||||
Schema can use for rename first level keys
|
||||
"""
|
||||
search_by_key = BUGOUT_RESOURCE_TYPE_DASHBOARD
|
||||
old_key = "dashboard_subscriptions"
|
||||
new_key = "subscription_settings"
|
||||
|
||||
resources: BugoutResources = bc.list_resources(
|
||||
token=MOONSTREAM_ADMIN_ACCESS_TOKEN,
|
||||
params={"type": search_by_key},
|
||||
timeout=BUGOUT_REQUEST_TIMEOUT_SECONDS,
|
||||
)
|
||||
for resource in resources.resources:
|
||||
resource_data = resource.resource_data
|
||||
try:
|
||||
if old_key not in resource_data:
|
||||
continue
|
||||
data = resource_data[old_key]
|
||||
resource_data[new_key] = data
|
||||
updated_resource = bc.update_resource(
|
||||
token=MOONSTREAM_ADMIN_ACCESS_TOKEN,
|
||||
resource_id=resource.id,
|
||||
resource_data={"update": resource_data, "drop_keys": [old_key]},
|
||||
timeout=BUGOUT_REQUEST_TIMEOUT_SECONDS,
|
||||
)
|
||||
logger.info(f"Resource id: {updated_resource.id} updated")
|
||||
except BugoutResponseException as e:
|
||||
logger.info(f"Bugout error: {e.status_code} with details: {e.detail}")
|
||||
except Exception as e:
|
||||
logger.info(f"Unexpected error: {repr(e)}")
|
||||
continue
|
|
@ -237,8 +237,8 @@ class SubdcriptionsAbiResponse(BaseModel):
|
|||
class DashboardMeta(BaseModel):
|
||||
subscription_id: UUID
|
||||
generic: Optional[List[Dict[str, str]]]
|
||||
all_methods: bool = False
|
||||
all_events: bool = False
|
||||
all_methods: bool = True
|
||||
all_events: bool = True
|
||||
methods: List[Dict[str, Any]]
|
||||
events: List[Dict[str, Any]]
|
||||
|
||||
|
@ -247,14 +247,14 @@ class DashboardResource(BaseModel):
|
|||
type: str
|
||||
user_id: str
|
||||
name: str
|
||||
dashboard_subscriptions: List[DashboardMeta]
|
||||
subscription_settings: List[DashboardMeta]
|
||||
|
||||
|
||||
class DashboardCreate(BaseModel):
|
||||
name: str
|
||||
subscriptions: List[DashboardMeta]
|
||||
subscription_settings: List[DashboardMeta]
|
||||
|
||||
|
||||
class DashboardUpdate(BaseModel):
|
||||
name: Optional[str]
|
||||
subscriptions: List[DashboardMeta] = Field(default_factory=list)
|
||||
subscription_settings: List[DashboardMeta] = Field(default_factory=list)
|
||||
|
|
|
@ -16,7 +16,7 @@ from sqlalchemy.orm import Session
|
|||
from .. import data
|
||||
from ..stream_queries import StreamQuery
|
||||
|
||||
from ..settings import ETHTXPOOL_HUMBUG_CLIENT_ID
|
||||
from ..settings import HUMBUG_TXPOOL_CLIENT_ID
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.WARN)
|
||||
|
@ -354,5 +354,5 @@ ethereum_txpool_provider = EthereumTXPoolProvider(
|
|||
description=ethereum_txpool_description,
|
||||
default_time_interval_seconds=5,
|
||||
estimated_events_per_time_interval=50,
|
||||
tags=[f"client:{ETHTXPOOL_HUMBUG_CLIENT_ID}"],
|
||||
tags=[f"client:{HUMBUG_TXPOOL_CLIENT_ID}"],
|
||||
)
|
||||
|
|
|
@ -51,7 +51,7 @@ async def add_dashboard_handler(
|
|||
|
||||
user = request.state.user
|
||||
|
||||
dashboard_subscriptions = dashboard.subscriptions
|
||||
subscription_settings = dashboard.subscription_settings
|
||||
|
||||
# Get all user subscriptions
|
||||
params = {
|
||||
|
@ -77,7 +77,7 @@ async def add_dashboard_handler(
|
|||
resource.id: resource.resource_data for resource in resources.resources
|
||||
}
|
||||
|
||||
for dashboard_subscription in dashboard_subscriptions:
|
||||
for dashboard_subscription in subscription_settings:
|
||||
if dashboard_subscription.subscription_id in available_subscriptions.keys():
|
||||
|
||||
# TODO(Andrey): Add some dedublication for get object from s3 for repeated subscription_id
|
||||
|
@ -132,7 +132,7 @@ async def add_dashboard_handler(
|
|||
type=BUGOUT_RESOURCE_TYPE_DASHBOARD,
|
||||
user_id=str(user.id),
|
||||
name=dashboard.name,
|
||||
dashboard_subscriptions=dashboard_subscriptions,
|
||||
subscription_settings=subscription_settings,
|
||||
)
|
||||
|
||||
try:
|
||||
|
@ -241,7 +241,7 @@ async def update_dashboard_handler(
|
|||
|
||||
user = request.state.user
|
||||
|
||||
dashboard_subscriptions = dashboard.subscriptions
|
||||
subscription_settings = dashboard.subscription_settings
|
||||
|
||||
params = {
|
||||
"type": BUGOUT_RESOURCE_TYPE_SUBSCRIPTION,
|
||||
|
@ -264,7 +264,7 @@ async def update_dashboard_handler(
|
|||
resource.id: resource.resource_data for resource in resources.resources
|
||||
}
|
||||
|
||||
for dashboard_subscription in dashboard_subscriptions:
|
||||
for dashboard_subscription in subscription_settings:
|
||||
|
||||
if dashboard_subscription.subscription_id in available_subscriptions:
|
||||
|
||||
|
@ -317,10 +317,10 @@ async def update_dashboard_handler(
|
|||
|
||||
dashboard_resource: Dict[str, Any] = {}
|
||||
|
||||
if dashboard_subscriptions:
|
||||
if subscription_settings:
|
||||
|
||||
dashboard_resource["dashboard_subscriptions"] = json.loads(dashboard.json())[
|
||||
"subscriptions"
|
||||
dashboard_resource["subscription_settings"] = json.loads(dashboard.json())[
|
||||
"subscription_settings"
|
||||
]
|
||||
|
||||
if dashboard.name is not None:
|
||||
|
@ -391,7 +391,7 @@ async def get_dashboard_data_links_handler(
|
|||
subscriptions_ids = [
|
||||
UUID(subscription_meta["subscription_id"])
|
||||
for subscription_meta in dashboard_resource.resource_data[
|
||||
"dashboard_subscriptions"
|
||||
"subscription_settings"
|
||||
]
|
||||
]
|
||||
|
||||
|
|
|
@ -39,14 +39,14 @@ DOCS_TARGET_PATH = "docs"
|
|||
|
||||
DEFAULT_STREAM_TIMEINTERVAL = 5 * 60
|
||||
|
||||
ETHTXPOOL_HUMBUG_CLIENT_ID = os.environ.get(
|
||||
"ETHTXPOOL_HUMBUG_CLIENT_ID", "client:ethereum-txpool-crawler-0"
|
||||
HUMBUG_TXPOOL_CLIENT_ID = os.environ.get(
|
||||
"HUMBUG_TXPOOL_CLIENT_ID", "client:ethereum-txpool-crawler-0"
|
||||
)
|
||||
|
||||
# S3 Bucket
|
||||
ETHERSCAN_SMARTCONTRACTS_BUCKET = os.environ.get("AWS_S3_SMARTCONTRACT_BUCKET")
|
||||
ETHERSCAN_SMARTCONTRACTS_BUCKET = os.environ.get("MOONSTREAM_S3_SMARTCONTRACTS_BUCKET")
|
||||
if ETHERSCAN_SMARTCONTRACTS_BUCKET is None:
|
||||
raise ValueError("AWS_S3_SMARTCONTRACT_BUCKET is not set")
|
||||
raise ValueError("MOONSTREAM_S3_SMARTCONTRACTS_BUCKET is not set")
|
||||
|
||||
MOONSTREAM_INTERNAL_HOSTED_ZONE_ID = os.environ.get(
|
||||
"MOONSTREAM_INTERNAL_HOSTED_ZONE_ID", ""
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
Moonstream library and API version.
|
||||
"""
|
||||
|
||||
MOONSTREAMAPI_VERSION = "0.1.0"
|
||||
MOONSTREAMAPI_VERSION = "0.1.1"
|
||||
|
|
|
@ -32,7 +32,7 @@ def fetch_web3_provider_ip():
|
|||
return record_value
|
||||
|
||||
|
||||
if not MOONSTREAM_ETHEREUM_WEB3_PROVIDER_URI.replace(".", "").isnumeric():
|
||||
if not MOONSTREAM_ETHEREUM_WEB3_PROVIDER_URI.startswith("http"):
|
||||
web3_provider_ip = fetch_web3_provider_ip()
|
||||
if web3_provider_ip is None:
|
||||
raise ValueError("Unable to extract web3 provider IP")
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
export MOONSTREAM_CORS_ALLOWED_ORIGINS="http://localhost:3000,https://moonstream.to,https://www.moonstream.to"
|
||||
export MOONSTREAM_APPLICATION_ID="<issued_bugout_application_id>"
|
||||
export MOONSTREAM_DATA_JOURNAL_ID="<bugout_journal_id_to_store_blockchain_data>"
|
||||
export MOONSTREAM_DB_URI="postgresql://<username>:<password>@<db_host>:<db_port>/<db_name>"
|
||||
export MOONSTREAM_POOL_SIZE=0
|
||||
export MOONSTREAM_ADMIN_ACCESS_TOKEN="<Access token to application resources>"
|
||||
export MOONSTREAM_INTERNAL_HOSTED_ZONE_ID="<moonstream_internal_hosted_zone_id>"
|
||||
export MOONSTREAM_ETHEREUM_WEB3_PROVIDER_URI="<connection_path_uri_to_ethereum_node>"
|
||||
export AWS_S3_SMARTCONTRACT_BUCKET="<AWS S3 bucket to store smart contracts>"
|
||||
export MOONSTREAM_S3_SMARTCONTRACTS_ABI_BUCKET="<AWS S3 bucket to store smart contracts ABI>"
|
||||
export MOONSTREAM_S3_SMARTCONTRACTS_ABI_PREFIX="<Previx for AWS S3 bucket (prod,dev,..)>"
|
||||
export BUGOUT_BROOD_URL="https://auth.bugout.dev"
|
||||
export BUGOUT_SPIRE_URL="https://spire.bugout.dev"
|
||||
export HUMBUG_REPORTER_BACKEND_TOKEN="<Bugout Humbug token for crash reports>"
|
||||
export ETHTXPOOL_HUMBUG_CLIENT_ID="<Bugout Humbug client id for txpool transactions in journal>"
|
|
@ -29,9 +29,9 @@ BASE_API_URL = "https://api.etherscan.io/api?module=contract&action=getsourcecod
|
|||
|
||||
ETHERSCAN_SMARTCONTRACTS_LABEL_NAME = "etherscan_smartcontract"
|
||||
|
||||
bucket = os.environ.get("AWS_S3_SMARTCONTRACT_BUCKET")
|
||||
bucket = os.environ.get("MOONSTREAM_S3_SMARTCONTRACTS_BUCKET")
|
||||
if bucket is None:
|
||||
raise ValueError("AWS_S3_SMARTCONTRACT_BUCKET must be set")
|
||||
raise ValueError("MOONSTREAM_S3_SMARTCONTRACTS_BUCKET must be set")
|
||||
|
||||
|
||||
@dataclass
|
||||
|
|
|
@ -198,45 +198,49 @@ def generate_metrics(
|
|||
return response_metric
|
||||
|
||||
try:
|
||||
start_time = time.time()
|
||||
|
||||
results["transactions_out"] = make_query(
|
||||
db_session,
|
||||
transaction_model.from_address,
|
||||
transaction_model.hash,
|
||||
func.count,
|
||||
)
|
||||
if "transactions_out" in metrics:
|
||||
start_time = time.time()
|
||||
results["transactions_out"] = make_query(
|
||||
db_session,
|
||||
transaction_model.from_address,
|
||||
transaction_model.hash,
|
||||
func.count,
|
||||
)
|
||||
|
||||
print("--- transactions_out %s seconds ---" % (time.time() - start_time))
|
||||
print("--- transactions_out %s seconds ---" % (time.time() - start_time))
|
||||
|
||||
start_time = time.time()
|
||||
results["transactions_in"] = make_query(
|
||||
db_session,
|
||||
transaction_model.to_address,
|
||||
transaction_model.hash,
|
||||
func.count,
|
||||
)
|
||||
if "transactions_in" in metrics:
|
||||
start_time = time.time()
|
||||
results["transactions_in"] = make_query(
|
||||
db_session,
|
||||
transaction_model.to_address,
|
||||
transaction_model.hash,
|
||||
func.count,
|
||||
)
|
||||
|
||||
print("--- transactions_in %s seconds ---" % (time.time() - start_time))
|
||||
print("--- transactions_in %s seconds ---" % (time.time() - start_time))
|
||||
|
||||
start_time = time.time()
|
||||
results["value_out"] = make_query(
|
||||
db_session,
|
||||
transaction_model.from_address,
|
||||
transaction_model.value,
|
||||
func.sum,
|
||||
)
|
||||
print("--- value_out %s seconds ---" % (time.time() - start_time))
|
||||
if "value_out" in metrics:
|
||||
start_time = time.time()
|
||||
results["value_out"] = make_query(
|
||||
db_session,
|
||||
transaction_model.from_address,
|
||||
transaction_model.value,
|
||||
func.sum,
|
||||
)
|
||||
print("--- value_out %s seconds ---" % (time.time() - start_time))
|
||||
|
||||
start_time = time.time()
|
||||
results["value_in"] = make_query(
|
||||
db_session,
|
||||
transaction_model.to_address,
|
||||
transaction_model.value,
|
||||
func.sum,
|
||||
)
|
||||
if "value_in" in metrics:
|
||||
start_time = time.time()
|
||||
results["value_in"] = make_query(
|
||||
db_session,
|
||||
transaction_model.to_address,
|
||||
transaction_model.value,
|
||||
func.sum,
|
||||
)
|
||||
|
||||
print("--- value_in %s seconds ---" % (time.time() - start_time))
|
||||
print("--- value_in %s seconds ---" % (time.time() - start_time))
|
||||
|
||||
except Exception as err:
|
||||
print(err)
|
||||
|
@ -341,6 +345,8 @@ def generate_data(
|
|||
func.to_timestamp(label_model.block_timestamp) < end
|
||||
)
|
||||
|
||||
# split grafics
|
||||
|
||||
label_counts_subquery = (
|
||||
label_counts.group_by(
|
||||
text("timeseries_points"), label_model.label_data["name"].astext
|
||||
|
@ -529,12 +535,9 @@ def stats_generate_handler(args: argparse.Namespace):
|
|||
with yield_db_session_ctx() as db_session:
|
||||
# read all subscriptions
|
||||
|
||||
# ethereum_blockchain
|
||||
|
||||
start_time = time.time()
|
||||
blockchain_type = AvailableBlockchainType(args.blockchain)
|
||||
|
||||
# polygon_blockchain
|
||||
dashboard_resources: BugoutResources = bc.list_resources(
|
||||
token=MOONSTREAM_ADMIN_ACCESS_TOKEN,
|
||||
params={"type": BUGOUT_RESOURCE_TYPE_DASHBOARD},
|
||||
|
@ -564,10 +567,12 @@ def stats_generate_handler(args: argparse.Namespace):
|
|||
|
||||
s3_client = boto3.client("s3")
|
||||
|
||||
subscriptions_count = 0
|
||||
|
||||
for dashboard in dashboard_resources.resources:
|
||||
|
||||
for dashboard_subscription_filters in dashboard.resource_data[
|
||||
"dashboard_subscriptions"
|
||||
"subscription_settings"
|
||||
]:
|
||||
|
||||
try:
|
||||
|
@ -577,6 +582,7 @@ def stats_generate_handler(args: argparse.Namespace):
|
|||
# Meen it's are different blockchain type
|
||||
continue
|
||||
|
||||
subscriptions_count += 1
|
||||
s3_data_object = {}
|
||||
|
||||
extention_data = []
|
||||
|
@ -587,6 +593,8 @@ def stats_generate_handler(args: argparse.Namespace):
|
|||
|
||||
generic = dashboard_subscription_filters["generic"]
|
||||
|
||||
generic_metrics_names = [item["name"] for item in generic]
|
||||
|
||||
if not subscription_by_id[subscription_id].resource_data["abi"]:
|
||||
|
||||
methods = []
|
||||
|
@ -716,7 +724,7 @@ def stats_generate_handler(args: argparse.Namespace):
|
|||
blockchain_type=blockchain_type,
|
||||
address=address,
|
||||
timescale=timescale,
|
||||
metrics=generic,
|
||||
metrics=generic_metrics_names,
|
||||
start=start_date,
|
||||
)
|
||||
|
||||
|
@ -742,7 +750,7 @@ def stats_generate_handler(args: argparse.Namespace):
|
|||
|
||||
reporter.custom_report(
|
||||
title=f"Dashboard stats generated.",
|
||||
content=f"Generate statistics for {args.blockchain}. \n Generation time: {time.time() - start_time}.",
|
||||
content=f"Generate statistics for {args.blockchain}. \n Generation time: {time.time() - start_time}. \n Total amount of dashboards: {len(dashboard_resources.resources)}. Generate stats for {subscriptions_count}.",
|
||||
tags=["dashboard", "statistics", f"blockchain:{args.blockchain}"],
|
||||
)
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ export MOONSTREAM_NODE_POLYGON_IPC_PORT="8545"
|
|||
export MOONSTREAM_CRAWL_WORKERS=4
|
||||
export MOONSTREAM_DB_URI="postgresql://<username>:<password>@<db_host>:<db_port>/<db_name>"
|
||||
export MOONSTREAM_ETHERSCAN_TOKEN="<Token for etherscan>"
|
||||
export AWS_S3_SMARTCONTRACT_BUCKET="<AWS S3 bucket for smart contracts>"
|
||||
export MOONSTREAM_S3_SMARTCONTRACTS_BUCKET="<AWS S3 bucket for smart contracts>"
|
||||
export MOONSTREAM_S3_SMARTCONTRACTS_ABI_PREFIX="<Previx for AWS S3 bucket (prod,dev,..)>"
|
||||
export MOONSTREAM_HUMBUG_TOKEN="<Token for crawlers store data via Humbug>"
|
||||
export COINMARKETCAP_API_KEY="<API key to parse conmarketcap>"
|
||||
|
|
|
@ -0,0 +1,225 @@
|
|||
# Created by https://www.toptal.com/developers/gitignore/api/vim,visualstudiocode,linux,python
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=vim,visualstudiocode,linux,python
|
||||
|
||||
### Linux ###
|
||||
*~
|
||||
|
||||
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||
.fuse_hidden*
|
||||
|
||||
# KDE directory preferences
|
||||
.directory
|
||||
|
||||
# Linux trash folder which might appear on any partition or disk
|
||||
.Trash-*
|
||||
|
||||
# .nfs files are created when an open file is removed but is still being accessed
|
||||
.nfs*
|
||||
|
||||
### Python ###
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
pip-wheel-metadata/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
### Vim ###
|
||||
# Swap
|
||||
[._]*.s[a-v][a-z]
|
||||
!*.svg # comment out if you don't need vector files
|
||||
[._]*.sw[a-p]
|
||||
[._]s[a-rt-v][a-z]
|
||||
[._]ss[a-gi-z]
|
||||
[._]sw[a-p]
|
||||
|
||||
# Session
|
||||
Session.vim
|
||||
Sessionx.vim
|
||||
|
||||
# Temporary
|
||||
.netrwhist
|
||||
# Auto-generated tag files
|
||||
tags
|
||||
# Persistent undo
|
||||
[._]*.un~
|
||||
|
||||
### VisualStudioCode ###
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
*.code-workspace
|
||||
|
||||
### Pycharm ###
|
||||
.idea
|
||||
|
||||
### VisualStudioCode Patch ###
|
||||
# Ignore all local history of files
|
||||
.history
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/vim,visualstudiocode,linux,python
|
||||
|
||||
### VSCode ###
|
||||
.vscode
|
||||
|
||||
# Git
|
||||
.git
|
||||
.github
|
||||
|
||||
# Envionments
|
||||
.moonstream
|
||||
.moonstreamdb
|
||||
.venv
|
||||
.env
|
||||
|
||||
# Environment variables
|
||||
test.env
|
||||
dev.env
|
||||
prod.env
|
||||
|
||||
configs/test.env
|
||||
configs/dev.env
|
||||
configs/prod.env
|
||||
configs/moonstreamdb.env
|
||||
configs/docker.env
|
||||
configs/docker.dev.env
|
||||
configs/docker.test.env
|
||||
configs/docker.prod.env
|
||||
|
||||
.secrets/
|
||||
|
||||
# Alembic migrations
|
||||
configs/alembic.test.ini
|
||||
configs/alembic.dev.ini
|
||||
configs/alembic.prod.ini
|
||||
|
||||
Dockerfile
|
||||
docker-compose.yml
|
|
@ -160,14 +160,29 @@ cython_debug/
|
|||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/python,visualstudiocode
|
||||
|
||||
# Custom
|
||||
dev.env
|
||||
prod.env
|
||||
dev.env.ps1
|
||||
prod.env.ps1
|
||||
alembic.dev.ini
|
||||
alembic.prod.ini
|
||||
# Envionments
|
||||
.moonstream/
|
||||
.moonstreamdb/
|
||||
.db/
|
||||
.venv/
|
||||
.env/
|
||||
|
||||
# Environment variables
|
||||
dev.env
|
||||
test.env
|
||||
prod.env
|
||||
moonstreamdb.env
|
||||
docker.env
|
||||
docker.dev.env
|
||||
docker.test.dev
|
||||
docker.prod.env
|
||||
docker.moonstreamdb.env
|
||||
|
||||
.secrets/
|
||||
.moonstreamdb
|
||||
|
||||
# Alembic migrations
|
||||
alembic.test.ini
|
||||
alembic.dev.ini
|
||||
alembic.prod.ini
|
||||
alembic.moonstreamdb.ini
|
||||
alembic.docker.ini
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
FROM python:3.8-slim-buster
|
||||
|
||||
# Update packages and
|
||||
# prepare alembic for docker compose setup
|
||||
RUN apt-get update && \
|
||||
apt-get install -y libpq-dev gcc && \
|
||||
rm -rf /var/lib/apt/lists/* && \
|
||||
pip3 install --no-cache-dir --upgrade pip setuptools && \
|
||||
pip3 install --no-cache-dir psycopg2-binary alembic
|
||||
|
||||
WORKDIR /usr/src/moonstreamdb
|
||||
|
||||
COPY . /usr/src/moonstreamdb
|
||||
|
||||
# Install Moonstream DB package
|
||||
RUN pip3 install --no-cache-dir -e .
|
||||
|
||||
ENTRYPOINT ["./migrate.sh"]
|
|
@ -0,0 +1,49 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Prepare Moonstream DB application for docker-compose use
|
||||
|
||||
# Print help message
|
||||
function usage {
|
||||
echo "Usage: $0 [-h] -d DATABASE_NAME"
|
||||
echo
|
||||
echo "CLI to generate environment variables"
|
||||
echo
|
||||
echo "Optional arguments:"
|
||||
echo " -h Show this help message and exit"
|
||||
echo " -d Database name for postgres in docker-compose setup"
|
||||
}
|
||||
|
||||
FLAG_DATABASE_NAME="moonstream_dev"
|
||||
|
||||
while getopts 'd:' flag; do
|
||||
case "${flag}" in
|
||||
d) FLAG_DATABASE_NAME="${OPTARG}" ;;
|
||||
h) usage
|
||||
exit 1 ;;
|
||||
*) usage
|
||||
exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(realpath $(dirname $0))"
|
||||
DOCKER_MOONSTREAMDB_DB_URI="postgresql://postgres:postgres@db/$FLAG_DATABASE_NAME"
|
||||
DOCKER_MOONSTREAMDB_ENV_FILE="docker.moonstreamdb.env"
|
||||
DOCKER_MOONSTREAMDB_ALEMBIC_FILE="alembic.moonstreamdb.ini"
|
||||
|
||||
# Generate environment variables
|
||||
|
||||
cp "$SCRIPT_DIR/sample.env" "$SCRIPT_DIR/$DOCKER_MOONSTREAMDB_ENV_FILE"
|
||||
|
||||
# Clean file with variables from export prefix and quotation marks
|
||||
sed --in-place 's|^export * ||' "$SCRIPT_DIR/$DOCKER_MOONSTREAMDB_ENV_FILE"
|
||||
sed --in-place 's|"||g' "$SCRIPT_DIR/$DOCKER_MOONSTREAMDB_ENV_FILE"
|
||||
|
||||
sed -i "s|^MOONSTREAM_DB_URI=.*|MOONSTREAM_DB_URI=$DOCKER_MOONSTREAMDB_DB_URI|" "$SCRIPT_DIR/$DOCKER_MOONSTREAMDB_ENV_FILE"
|
||||
|
||||
# Generate alembic config
|
||||
|
||||
cp "$SCRIPT_DIR/alembic.sample.ini" "$SCRIPT_DIR/$DOCKER_MOONSTREAMDB_ALEMBIC_FILE"
|
||||
|
||||
sed -i "s|^sqlalchemy.url =.*|sqlalchemy.url = $DOCKER_MOONSTREAMDB_DB_URI|" "$SCRIPT_DIR/$DOCKER_MOONSTREAMDB_ALEMBIC_FILE"
|
|
@ -1,2 +1,3 @@
|
|||
# Required environment variables to work with database CLI
|
||||
export MOONSTREAM_DB_URI="postgresql://<username>:<password>@<db_host>:<db_port>/<db_name>"
|
||||
export MOONSTREAM_POOL_SIZE=0
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
# Sets up Brood server for docker compose:
|
||||
# 1. Running alembic migrations to head (using config file specified
|
||||
# by the ALEMBIC_CONFIG environment variable)
|
||||
# 2. Running dev.sh (from the directory from which this script was called)
|
||||
|
||||
set -e
|
||||
|
||||
if [ -z "$ALEMBIC_CONFIG" ]
|
||||
then
|
||||
echo "Please explicitly set the ALEMBIC_CONFIG environment variable to point to an alembic configuration file"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ALEMBIC_CONFIG="$ALEMBIC_CONFIG" sh alembic.sh upgrade head
|
|
@ -2,4 +2,4 @@
|
|||
Moonstream database version.
|
||||
"""
|
||||
|
||||
MOONSTREAMDB_VERSION = "0.2.0"
|
||||
MOONSTREAMDB_VERSION = "0.2.1"
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
# Compose version
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
# Moonstream API application
|
||||
moonstreamapi:
|
||||
build:
|
||||
context: ./backend/
|
||||
dockerfile: ./Dockerfile
|
||||
image: moonstreamapi:latest
|
||||
ports:
|
||||
- "127.0.0.1:7481:7481"
|
||||
# Specify environment file for compose setup
|
||||
env_file: ./backend/configs/docker.moonstreamapi.env
|
||||
environment:
|
||||
MOONSTREAMAPI_HOST: 0.0.0.0
|
||||
MOONSTREAMAPI_PORT: 7481
|
||||
MOONSTREAMAPI_UVICORN_WORKERS: 1
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://moonstreamapi:7481/ping"]
|
||||
interval: 5s
|
||||
timeout: 1s
|
||||
retries: 2
|
||||
start_period: 2s
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
|
||||
# Moonstream DB application
|
||||
moonstreamdb:
|
||||
build:
|
||||
context: ./db/
|
||||
dockerfile: ./Dockerfile
|
||||
image: moonstreamdb:latest
|
||||
# Specify environment file for compose setup
|
||||
env_file: ./db/configs/docker.moonstreamdb.env
|
||||
environment:
|
||||
ALEMBIC_CONFIG: ./configs/alembic.moonstreamdb.ini
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
|
||||
# DB postgres application
|
||||
db:
|
||||
image: postgres:13
|
||||
ports:
|
||||
- "127.0.0.1:5432:5432"
|
||||
environment:
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_DB: moonstream_dev
|
||||
healthcheck:
|
||||
test: ["CMD", "psql", "-U", "postgres", "-c", "SELECT 1;"]
|
||||
interval: 5s
|
||||
timeout: 1s
|
||||
retries: 3
|
||||
start_period: 2s
|
|
@ -36,7 +36,7 @@ const NewDashboard = (props) => {
|
|||
"new_dashboard",
|
||||
{
|
||||
name: "",
|
||||
subscriptions: [
|
||||
subscription_settings: [
|
||||
{
|
||||
label: "",
|
||||
abi: false,
|
||||
|
@ -62,11 +62,11 @@ const NewDashboard = (props) => {
|
|||
const subscriptions = useSubscriptions();
|
||||
|
||||
const [pickerItems, setPickerItems] = React.useState(
|
||||
subscriptions.subscriptionsCache.data?.subscriptions
|
||||
subscriptions.subscriptionsCache?.data?.subscriptions
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
newDashboardForm.subscriptions.forEach((element, idx) => {
|
||||
newDashboardForm.subscription_settings.forEach((element, idx) => {
|
||||
const subscription =
|
||||
subscriptions.subscriptionsCache.data?.subscriptions.find(
|
||||
(subscription_item) =>
|
||||
|
@ -76,10 +76,10 @@ const NewDashboard = (props) => {
|
|||
if (
|
||||
element.subscription_id &&
|
||||
subscription &&
|
||||
newDashboardForm.subscriptions[idx].abi !== subscription?.abi
|
||||
newDashboardForm.subscription_settings[idx].abi !== subscription?.abi
|
||||
) {
|
||||
const newestDashboardForm = { ...newDashboardForm };
|
||||
newestDashboardForm.subscriptions[idx].abi = subscription.abi;
|
||||
newestDashboardForm.subscription_settings[idx].abi = subscription.abi;
|
||||
setNewDashboardForm(newestDashboardForm);
|
||||
}
|
||||
});
|
||||
|
@ -110,6 +110,8 @@ const NewDashboard = (props) => {
|
|||
item.address.toUpperCase().includes(inputValue.toUpperCase()) ||
|
||||
item.label.toUpperCase().includes(inputValue.toUpperCase()));
|
||||
|
||||
console.log("dbg", newDashboardForm);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack spacing="24px">
|
||||
|
@ -187,379 +189,400 @@ const NewDashboard = (props) => {
|
|||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{newDashboardForm?.subscriptions.map((subscibedItem, idx) => {
|
||||
return (
|
||||
<Tr key={`form-address-row-${idx}`}>
|
||||
<Td>
|
||||
{!subscriptions.subscriptionsCache.isLoading &&
|
||||
subscriptions.subscriptionsCache.data &&
|
||||
pickerItems && (
|
||||
<>
|
||||
<Downshift
|
||||
onSelect={(selectedItem) => {
|
||||
const newState = { ...newDashboardForm };
|
||||
newState.subscriptions[idx] = {
|
||||
label: selectedItem.label,
|
||||
address: selectedItem.address,
|
||||
subscription_id: selectedItem.id,
|
||||
abi: selectedItem.abi,
|
||||
isMethods: false,
|
||||
isEvents: false,
|
||||
generic: {
|
||||
transactions: {
|
||||
in: false,
|
||||
out: false,
|
||||
{newDashboardForm?.subscription_settings.map(
|
||||
(subscibedItem, idx) => {
|
||||
return (
|
||||
<Tr key={`form-address-row-${idx}`}>
|
||||
<Td>
|
||||
{!subscriptions.subscriptionsCache.isLoading &&
|
||||
subscriptions.subscriptionsCache.data &&
|
||||
pickerItems && (
|
||||
<>
|
||||
<Downshift
|
||||
onSelect={(selectedItem) => {
|
||||
const newState = { ...newDashboardForm };
|
||||
newState.subscription_settings[idx] = {
|
||||
label: selectedItem.label,
|
||||
address: selectedItem.address,
|
||||
subscription_id: selectedItem.id,
|
||||
abi: selectedItem.abi,
|
||||
isMethods: false,
|
||||
isEvents: false,
|
||||
generic: {
|
||||
transactions: {
|
||||
in: false,
|
||||
out: false,
|
||||
},
|
||||
value: {
|
||||
in: false,
|
||||
out: false,
|
||||
balance: false,
|
||||
},
|
||||
},
|
||||
value: {
|
||||
in: false,
|
||||
out: false,
|
||||
balance: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
setNewDashboardForm(newState);
|
||||
}}
|
||||
itemToString={(item) => (item ? item.label : "")}
|
||||
initialSelectedItem={subscibedItem ?? undefined}
|
||||
>
|
||||
{({
|
||||
getInputProps,
|
||||
getItemProps,
|
||||
getLabelProps,
|
||||
getMenuProps,
|
||||
getToggleButtonProps,
|
||||
isOpen,
|
||||
inputValue,
|
||||
highlightedIndex,
|
||||
getRootProps,
|
||||
}) => {
|
||||
const labelColor =
|
||||
subscibedItem.color &&
|
||||
color(`${subscibedItem.color}`);
|
||||
return (
|
||||
<Box pos="relative">
|
||||
<Box
|
||||
{...getRootProps(
|
||||
{},
|
||||
{ suppressRefError: true }
|
||||
)}
|
||||
>
|
||||
<InputGroup>
|
||||
<InputLeftAddon
|
||||
isTruncated
|
||||
maxW="60px"
|
||||
fontSize={
|
||||
ui.isMobileView ? "xs" : "sm"
|
||||
}
|
||||
bgColor={
|
||||
subscibedItem?.color ?? "gray.100"
|
||||
}
|
||||
>
|
||||
<FormLabel
|
||||
alignContent="center"
|
||||
my={2}
|
||||
{...getLabelProps()}
|
||||
color={
|
||||
labelColor
|
||||
? labelColor?.isDark()
|
||||
? "white"
|
||||
: labelColor.darken(0.6).hex()
|
||||
: "inherit"
|
||||
}
|
||||
>{`#${idx}:`}</FormLabel>
|
||||
</InputLeftAddon>
|
||||
|
||||
<Input
|
||||
placeholder="Subscription to use in dashboard"
|
||||
isTruncated
|
||||
fontSize="sm"
|
||||
{...getInputProps({
|
||||
defaultValue:
|
||||
subscibedItem?.label ?? "iha",
|
||||
})}
|
||||
></Input>
|
||||
<InputRightAddon>
|
||||
{" "}
|
||||
<button
|
||||
{...getToggleButtonProps()}
|
||||
aria-label={"toggle menu"}
|
||||
>
|
||||
↓
|
||||
</button>
|
||||
</InputRightAddon>
|
||||
</InputGroup>
|
||||
</Box>
|
||||
{isOpen ? (
|
||||
<Stack
|
||||
// display="flex"
|
||||
direction="column"
|
||||
className="menuListTim"
|
||||
{...getMenuProps()}
|
||||
bgColor="gray.300"
|
||||
borderRadius="md"
|
||||
boxShadow="lg"
|
||||
pos="absolute"
|
||||
left={0}
|
||||
right={0}
|
||||
spacing={2}
|
||||
zIndex={1000}
|
||||
py={2}
|
||||
};
|
||||
setNewDashboardForm(newState);
|
||||
}}
|
||||
itemToString={(item) =>
|
||||
item ? item.label : ""
|
||||
}
|
||||
initialSelectedItem={subscibedItem ?? undefined}
|
||||
>
|
||||
{({
|
||||
getInputProps,
|
||||
getItemProps,
|
||||
getLabelProps,
|
||||
getMenuProps,
|
||||
getToggleButtonProps,
|
||||
isOpen,
|
||||
inputValue,
|
||||
highlightedIndex,
|
||||
getRootProps,
|
||||
}) => {
|
||||
const labelColor =
|
||||
subscibedItem.color &&
|
||||
color(`${subscibedItem.color}`);
|
||||
return (
|
||||
<Box pos="relative">
|
||||
<Box
|
||||
{...getRootProps(
|
||||
{},
|
||||
{ suppressRefError: true }
|
||||
)}
|
||||
>
|
||||
{pickerItems &&
|
||||
pickerItems.filter((item) =>
|
||||
filterFn(item, inputValue)
|
||||
).length === 0 && (
|
||||
<InputGroup>
|
||||
<InputLeftAddon
|
||||
isTruncated
|
||||
maxW="60px"
|
||||
fontSize={
|
||||
ui.isMobileView ? "xs" : "sm"
|
||||
}
|
||||
bgColor={
|
||||
subscibedItem?.color ?? "gray.100"
|
||||
}
|
||||
>
|
||||
<FormLabel
|
||||
alignContent="center"
|
||||
my={2}
|
||||
{...getLabelProps()}
|
||||
color={
|
||||
labelColor
|
||||
? labelColor?.isDark()
|
||||
? "white"
|
||||
: labelColor
|
||||
.darken(0.6)
|
||||
.hex()
|
||||
: "inherit"
|
||||
}
|
||||
>{`#${idx}:`}</FormLabel>
|
||||
</InputLeftAddon>
|
||||
|
||||
<Input
|
||||
placeholder="Subscription to use in dashboard"
|
||||
isTruncated
|
||||
fontSize="sm"
|
||||
{...getInputProps({
|
||||
defaultValue:
|
||||
subscibedItem?.label ?? "iha",
|
||||
})}
|
||||
></Input>
|
||||
<InputRightAddon>
|
||||
{" "}
|
||||
<button
|
||||
{...getToggleButtonProps()}
|
||||
aria-label={"toggle menu"}
|
||||
>
|
||||
↓
|
||||
</button>
|
||||
</InputRightAddon>
|
||||
</InputGroup>
|
||||
</Box>
|
||||
{isOpen ? (
|
||||
<Stack
|
||||
// display="flex"
|
||||
direction="column"
|
||||
className="menuListTim"
|
||||
{...getMenuProps()}
|
||||
bgColor="gray.300"
|
||||
borderRadius="md"
|
||||
boxShadow="lg"
|
||||
pos="absolute"
|
||||
left={0}
|
||||
right={0}
|
||||
spacing={2}
|
||||
zIndex={1000}
|
||||
py={2}
|
||||
>
|
||||
{pickerItems &&
|
||||
pickerItems.filter((item) =>
|
||||
filterFn(item, inputValue)
|
||||
).length === 0 && (
|
||||
<Button
|
||||
colorScheme="orange"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
fontSize="sm"
|
||||
w="100%"
|
||||
m={0}
|
||||
isTruncated
|
||||
onClick={() => {
|
||||
overlay.toggleModal({
|
||||
type: MODAL_TYPES.NEW_SUBSCRIPTON,
|
||||
props: {
|
||||
initialValue: inputValue,
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
Subscribe to: {inputValue}{" "}
|
||||
</Button>
|
||||
)}
|
||||
{pickerItems &&
|
||||
pickerItems
|
||||
.filter((item) =>
|
||||
filterFn(item, inputValue)
|
||||
)
|
||||
.map((item, index) => {
|
||||
const badgeColor = color(
|
||||
`${item.color}`
|
||||
);
|
||||
|
||||
return (
|
||||
<Stack
|
||||
px={4}
|
||||
py={1}
|
||||
alignItems="center"
|
||||
key={item.value}
|
||||
{...getItemProps({
|
||||
key: item.value,
|
||||
index,
|
||||
item,
|
||||
})}
|
||||
direction="row"
|
||||
w="100%"
|
||||
bgColor={
|
||||
index === highlightedIndex
|
||||
? "orange.900"
|
||||
: "inherit"
|
||||
}
|
||||
color={
|
||||
index === highlightedIndex
|
||||
? "gray.100"
|
||||
: "inherit"
|
||||
}
|
||||
>
|
||||
<chakra.span whiteSpace="nowrap">
|
||||
{item.label}
|
||||
</chakra.span>
|
||||
<Badge
|
||||
size="sm"
|
||||
placeSelf="self-end"
|
||||
colorScheme={
|
||||
item.abi
|
||||
? "green"
|
||||
: "gray"
|
||||
}
|
||||
>
|
||||
ABI
|
||||
</Badge>
|
||||
<Badge
|
||||
isTruncated
|
||||
size="sm"
|
||||
placeSelf="self-end"
|
||||
bgColor={item.color}
|
||||
color={
|
||||
badgeColor.isDark()
|
||||
? badgeColor
|
||||
.lighten(100)
|
||||
.hex()
|
||||
: badgeColor
|
||||
.darken(0.6)
|
||||
.hex()
|
||||
}
|
||||
>
|
||||
{item.address}
|
||||
</Badge>
|
||||
</Stack>
|
||||
);
|
||||
})}
|
||||
{inputValue === "" && (
|
||||
<Button
|
||||
colorScheme="orange"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
fontSize="sm"
|
||||
w="100%"
|
||||
m={0}
|
||||
isTruncated
|
||||
onClick={() => {
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
overlay.toggleModal({
|
||||
type: MODAL_TYPES.NEW_SUBSCRIPTON,
|
||||
props: {
|
||||
initialValue: inputValue,
|
||||
},
|
||||
});
|
||||
}}
|
||||
})
|
||||
}
|
||||
>
|
||||
Subscribe to: {inputValue}{" "}
|
||||
New subscription
|
||||
{inputValue}{" "}
|
||||
</Button>
|
||||
)}
|
||||
{pickerItems &&
|
||||
pickerItems
|
||||
.filter((item) =>
|
||||
filterFn(item, inputValue)
|
||||
)
|
||||
.map((item, index) => {
|
||||
const badgeColor = color(
|
||||
`${item.color}`
|
||||
);
|
||||
|
||||
return (
|
||||
<Stack
|
||||
px={4}
|
||||
py={1}
|
||||
alignItems="center"
|
||||
key={item.value}
|
||||
{...getItemProps({
|
||||
key: item.value,
|
||||
index,
|
||||
item,
|
||||
})}
|
||||
direction="row"
|
||||
w="100%"
|
||||
bgColor={
|
||||
index === highlightedIndex
|
||||
? "orange.900"
|
||||
: "inherit"
|
||||
}
|
||||
color={
|
||||
index === highlightedIndex
|
||||
? "gray.100"
|
||||
: "inherit"
|
||||
}
|
||||
>
|
||||
<chakra.span whiteSpace="nowrap">
|
||||
{item.label}
|
||||
</chakra.span>
|
||||
<Badge
|
||||
size="sm"
|
||||
placeSelf="self-end"
|
||||
colorScheme={
|
||||
item.abi
|
||||
? "green"
|
||||
: "gray"
|
||||
}
|
||||
>
|
||||
ABI
|
||||
</Badge>
|
||||
<Badge
|
||||
isTruncated
|
||||
size="sm"
|
||||
placeSelf="self-end"
|
||||
bgColor={item.color}
|
||||
color={
|
||||
badgeColor.isDark()
|
||||
? badgeColor
|
||||
.lighten(100)
|
||||
.hex()
|
||||
: badgeColor
|
||||
.darken(0.6)
|
||||
.hex()
|
||||
}
|
||||
>
|
||||
{item.address}
|
||||
</Badge>
|
||||
</Stack>
|
||||
);
|
||||
})}
|
||||
{inputValue === "" && (
|
||||
<Button
|
||||
colorScheme="orange"
|
||||
variant="outline"
|
||||
w="100%"
|
||||
m={0}
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
overlay.toggleModal({
|
||||
type: MODAL_TYPES.NEW_SUBSCRIPTON,
|
||||
})
|
||||
}
|
||||
>
|
||||
New subscription
|
||||
{inputValue}{" "}
|
||||
</Button>
|
||||
)}
|
||||
</Stack>
|
||||
) : null}
|
||||
{/* </Menu> */}
|
||||
</Box>
|
||||
);
|
||||
}}
|
||||
</Downshift>
|
||||
</>
|
||||
</Stack>
|
||||
) : null}
|
||||
{/* </Menu> */}
|
||||
</Box>
|
||||
);
|
||||
}}
|
||||
</Downshift>
|
||||
</>
|
||||
)}
|
||||
</Td>
|
||||
<Td p={1} textAlign="center">
|
||||
{subscibedItem.abi && subscibedItem.address && (
|
||||
<CheckCircleIcon color="green" />
|
||||
)}
|
||||
</Td>
|
||||
<Td p={1} textAlign="center">
|
||||
{subscibedItem.abi && subscibedItem.address && (
|
||||
<CheckCircleIcon color="green" />
|
||||
)}
|
||||
{!subscibedItem.abi && (
|
||||
<Button
|
||||
colorScheme="orange"
|
||||
size="xs"
|
||||
py={2}
|
||||
disabled={!subscibedItem.address}
|
||||
onClick={() =>
|
||||
overlay.toggleModal({
|
||||
type: MODAL_TYPES.UPLOAD_ABI,
|
||||
props: { id: subscibedItem.subscription_id },
|
||||
})
|
||||
}
|
||||
>
|
||||
Upload
|
||||
</Button>
|
||||
)}
|
||||
</Td>
|
||||
{!subscibedItem.abi && (
|
||||
<Button
|
||||
colorScheme="orange"
|
||||
size="xs"
|
||||
py={2}
|
||||
disabled={!subscibedItem.address}
|
||||
onClick={() =>
|
||||
overlay.toggleModal({
|
||||
type: MODAL_TYPES.UPLOAD_ABI,
|
||||
props: { id: subscibedItem.subscription_id },
|
||||
})
|
||||
}
|
||||
>
|
||||
Upload
|
||||
</Button>
|
||||
)}
|
||||
</Td>
|
||||
|
||||
<Td p={1} textAlign="center">
|
||||
<Checkbox
|
||||
isDisabled={!subscibedItem.abi}
|
||||
onChange={() => {
|
||||
const newState = { ...newDashboardForm };
|
||||
newState.subscriptions[idx] = {
|
||||
...newState.subscriptions[idx],
|
||||
isMethods: !newState.subscriptions[idx].isMethods,
|
||||
};
|
||||
setNewDashboardForm(newState);
|
||||
}}
|
||||
isChecked={subscibedItem.isMethods}
|
||||
></Checkbox>
|
||||
</Td>
|
||||
<Td p={1} textAlign="center">
|
||||
<Checkbox
|
||||
isDisabled={
|
||||
!subscibedItem.address || !subscibedItem.abi
|
||||
}
|
||||
onChange={() => {
|
||||
const newState = { ...newDashboardForm };
|
||||
newState.subscriptions[idx] = {
|
||||
...newState.subscriptions[idx],
|
||||
isEvents: !newState.subscriptions[idx].isEvents,
|
||||
};
|
||||
setNewDashboardForm(newState);
|
||||
}}
|
||||
isChecked={subscibedItem.isEvents}
|
||||
></Checkbox>
|
||||
</Td>
|
||||
<Td p={1} textAlign="center">
|
||||
<Checkbox
|
||||
isDisabled={!subscibedItem.address}
|
||||
onChange={() => {
|
||||
const newState = { ...newDashboardForm };
|
||||
newState.subscriptions[idx].generic.transactions.in =
|
||||
!newState.subscriptions[idx].generic.transactions
|
||||
.in;
|
||||
setNewDashboardForm(newState);
|
||||
}}
|
||||
isChecked={subscibedItem.generic.transactions.in}
|
||||
></Checkbox>
|
||||
</Td>
|
||||
<Td p={1} textAlign="center">
|
||||
<Checkbox
|
||||
isDisabled={!subscibedItem.address}
|
||||
onChange={() => {
|
||||
const newState = { ...newDashboardForm };
|
||||
newState.subscriptions[idx].generic.transactions.out =
|
||||
!newState.subscriptions[idx].generic.transactions
|
||||
.out;
|
||||
setNewDashboardForm(newState);
|
||||
}}
|
||||
isChecked={subscibedItem.generic.transactions.out}
|
||||
></Checkbox>
|
||||
</Td>
|
||||
<Td p={1} textAlign="center">
|
||||
<Checkbox
|
||||
isDisabled={!subscibedItem.address}
|
||||
onChange={() => {
|
||||
const newState = { ...newDashboardForm };
|
||||
newState.subscriptions[idx].generic.value.in =
|
||||
!newState.subscriptions[idx].generic.value.in;
|
||||
setNewDashboardForm(newState);
|
||||
}}
|
||||
isChecked={subscibedItem.generic.value.in}
|
||||
></Checkbox>
|
||||
</Td>
|
||||
<Td p={1} textAlign="center">
|
||||
<Checkbox
|
||||
isDisabled={!subscibedItem.address}
|
||||
onChange={() => {
|
||||
const newState = { ...newDashboardForm };
|
||||
newState.subscriptions[idx].generic.value.out =
|
||||
!newState.subscriptions[idx].generic.value.out;
|
||||
setNewDashboardForm(newState);
|
||||
}}
|
||||
isChecked={subscibedItem.generic.value.out}
|
||||
></Checkbox>
|
||||
</Td>
|
||||
<Td p={1} textAlign="center">
|
||||
<Checkbox
|
||||
isDisabled={!subscibedItem.address}
|
||||
onChange={() => {
|
||||
const newState = { ...newDashboardForm };
|
||||
newState.subscriptions[idx].generic.balance =
|
||||
!newState.subscriptions[idx].generic.balance;
|
||||
setNewDashboardForm(newState);
|
||||
}}
|
||||
isChecked={subscibedItem.generic.balance}
|
||||
></Checkbox>
|
||||
</Td>
|
||||
|
||||
<Td p={1} textAlign="center">
|
||||
{idx > 0 && (
|
||||
<CloseButton
|
||||
onClick={() => {
|
||||
const hardcopy = [
|
||||
...newDashboardForm?.subscriptions,
|
||||
];
|
||||
hardcopy.splice(idx, 1);
|
||||
setNewDashboardForm((prevState) => {
|
||||
return {
|
||||
...prevState,
|
||||
subscriptions: [...hardcopy],
|
||||
};
|
||||
});
|
||||
<Td p={1} textAlign="center">
|
||||
<Checkbox
|
||||
isDisabled={!subscibedItem.abi}
|
||||
onChange={() => {
|
||||
const newState = { ...newDashboardForm };
|
||||
newState.subscription_settings[idx] = {
|
||||
...newState.subscription_settings[idx],
|
||||
isMethods:
|
||||
!newState.subscription_settings[idx].isMethods,
|
||||
};
|
||||
setNewDashboardForm(newState);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Td>
|
||||
</Tr>
|
||||
);
|
||||
})}
|
||||
isChecked={subscibedItem.isMethods}
|
||||
></Checkbox>
|
||||
</Td>
|
||||
<Td p={1} textAlign="center">
|
||||
<Checkbox
|
||||
isDisabled={
|
||||
!subscibedItem.address || !subscibedItem.abi
|
||||
}
|
||||
onChange={() => {
|
||||
const newState = { ...newDashboardForm };
|
||||
newState.subscription_settings[idx] = {
|
||||
...newState.subscription_settings[idx],
|
||||
isEvents:
|
||||
!newState.subscription_settings[idx].isEvents,
|
||||
};
|
||||
setNewDashboardForm(newState);
|
||||
}}
|
||||
isChecked={subscibedItem.isEvents}
|
||||
></Checkbox>
|
||||
</Td>
|
||||
<Td p={1} textAlign="center">
|
||||
<Checkbox
|
||||
isDisabled={!subscibedItem.address}
|
||||
onChange={() => {
|
||||
const newState = { ...newDashboardForm };
|
||||
newState.subscription_settings[
|
||||
idx
|
||||
].generic.transactions.in =
|
||||
!newState.subscription_settings[idx].generic
|
||||
.transactions.in;
|
||||
setNewDashboardForm(newState);
|
||||
}}
|
||||
isChecked={subscibedItem.generic.transactions.in}
|
||||
></Checkbox>
|
||||
</Td>
|
||||
<Td p={1} textAlign="center">
|
||||
<Checkbox
|
||||
isDisabled={!subscibedItem.address}
|
||||
onChange={() => {
|
||||
const newState = { ...newDashboardForm };
|
||||
newState.subscription_settings[
|
||||
idx
|
||||
].generic.transactions.out =
|
||||
!newState.subscription_settings[idx].generic
|
||||
.transactions.out;
|
||||
setNewDashboardForm(newState);
|
||||
}}
|
||||
isChecked={subscibedItem.generic.transactions.out}
|
||||
></Checkbox>
|
||||
</Td>
|
||||
<Td p={1} textAlign="center">
|
||||
<Checkbox
|
||||
isDisabled={!subscibedItem.address}
|
||||
onChange={() => {
|
||||
const newState = { ...newDashboardForm };
|
||||
newState.subscription_settings[
|
||||
idx
|
||||
].generic.value.in =
|
||||
!newState.subscription_settings[idx].generic.value
|
||||
.in;
|
||||
setNewDashboardForm(newState);
|
||||
}}
|
||||
isChecked={subscibedItem.generic.value.in}
|
||||
></Checkbox>
|
||||
</Td>
|
||||
<Td p={1} textAlign="center">
|
||||
<Checkbox
|
||||
isDisabled={!subscibedItem.address}
|
||||
onChange={() => {
|
||||
const newState = { ...newDashboardForm };
|
||||
newState.subscription_settings[
|
||||
idx
|
||||
].generic.value.out =
|
||||
!newState.subscription_settings[idx].generic.value
|
||||
.out;
|
||||
setNewDashboardForm(newState);
|
||||
}}
|
||||
isChecked={subscibedItem.generic.value.out}
|
||||
></Checkbox>
|
||||
</Td>
|
||||
<Td p={1} textAlign="center">
|
||||
<Checkbox
|
||||
isDisabled={!subscibedItem.address}
|
||||
onChange={() => {
|
||||
const newState = { ...newDashboardForm };
|
||||
newState.subscription_settings[
|
||||
idx
|
||||
].generic.balance =
|
||||
!newState.subscription_settings[idx].generic
|
||||
.balance;
|
||||
setNewDashboardForm(newState);
|
||||
}}
|
||||
isChecked={subscibedItem.generic.balance}
|
||||
></Checkbox>
|
||||
</Td>
|
||||
|
||||
<Td p={1} textAlign="center">
|
||||
{idx > 0 && (
|
||||
<CloseButton
|
||||
onClick={() => {
|
||||
const hardcopy = [
|
||||
...newDashboardForm?.subscription_settings,
|
||||
];
|
||||
hardcopy.splice(idx, 1);
|
||||
setNewDashboardForm((prevState) => {
|
||||
return {
|
||||
...prevState,
|
||||
subscriptions: [...hardcopy],
|
||||
};
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Td>
|
||||
</Tr>
|
||||
);
|
||||
}
|
||||
)}
|
||||
</Tbody>
|
||||
</Table>
|
||||
<Center>
|
||||
|
@ -572,7 +595,7 @@ const NewDashboard = (props) => {
|
|||
_active={{ textDecor: "none" }}
|
||||
onClick={() => {
|
||||
const newState = { ...newDashboardForm };
|
||||
newState.subscriptions.push({
|
||||
newState.subscription_settings.push({
|
||||
label: "",
|
||||
abi: false,
|
||||
subscription_id: null,
|
||||
|
|
Ładowanie…
Reference in New Issue