feather-ws -> wowlet-backend

This commit is contained in:
dsc 2021-04-05 19:49:02 +02:00
parent 04e10f6d2b
commit abfe22e89c
20 changed files with 72 additions and 75 deletions

View file

@ -10,7 +10,7 @@ Back-end websocket server for Feather wallet.
- Monero
- Wownero
See also the environment variables `FEATHER_COIN_NAME`, `FEATHER_COIN_SYMBOL`, etc. in `settings.py`.
See also the environment variables `WOWLET_COIN_NAME`, `WOWLET_COIN_SYMBOL`, etc. in `settings.py`.
## Tasks
@ -26,7 +26,7 @@ When Feather wallet starts up, it will connect to
this websocket server and receive the information
listed above which is necessary for normal operation.
See `fapi.tasks.*` for the various tasks.
See `wowlet_backend.tasks.*` for the various tasks.
## Development
@ -37,7 +37,7 @@ virtualenv -p /usr/bin/python3 venv
source venv/bin/activate
pip install -r requirements.txt
export FEATHER_DEBUG=true
export WOWLET_DEBUG=true
python run.py
```

View file

@ -2,5 +2,5 @@
# Copyright (c) 2020, The Monero Project.
# Copyright (c) 2020, dsc@xmr.pm
from fapi.factory import create_app
from wowlet_backend_backend.factory import create_app
app = create_app()

View file

@ -16,12 +16,12 @@ services:
context: .
dockerfile: Dockerfile
environment:
- FEATHER_DEBUG=false
- FEATHER_PORT=1337
- FEATHER_REDIS_ADDRESS=redis://redis
- FEATHER_TOR_SOCKS_PROXY=socks5://tor-node:9050
- FEATHER_COIN_NAME=monero
- FEATHER_COIN_SYMBOL=xmr
- FEATHER_COIN_MODE=mainnet
- WOWLET_DEBUG=false
- WOWLET_PORT=1337
- WOWLET_REDIS_ADDRESS=redis://redis
- WOWLET_TOR_SOCKS_PROXY=socks5://tor-node:9050
- WOWLET_COIN_NAME=monero
- WOWLET_COIN_SYMBOL=xmr
- WOWLET_COIN_MODE=mainnet
ports:
- "1337:1337"

2
run.py
View file

@ -2,7 +2,7 @@
# Copyright (c) 2020, The Monero Project.
# Copyright (c) 2020, dsc@xmr.pm
from fapi.factory import create_app
from wowlet_backend.factory import create_app
import settings
app = create_app()

View file

@ -10,19 +10,19 @@ def bool_env(val):
return val is True or (isinstance(val, str) and (val.lower() == 'true' or val == '1'))
DEBUG = bool_env(os.environ.get("FEATHER_DEBUG", False))
HOST = os.environ.get("FEATHER_HOST", "127.0.0.1")
PORT = int(os.environ.get("FEATHER_PORT", 1337))
DEBUG = bool_env(os.environ.get("WOWLET_DEBUG", False))
HOST = os.environ.get("WOWLET_HOST", "127.0.0.1")
PORT = int(os.environ.get("WOWLET_PORT", 1337))
REDIS_ADDRESS = os.environ.get("FEATHER_REDIS_ADDRESS", "redis://localhost")
REDIS_PASSWORD = os.environ.get("FEATHER_REDIS_PASSWORD")
REDIS_ADDRESS = os.environ.get("WOWLET_REDIS_ADDRESS", "redis://localhost")
REDIS_PASSWORD = os.environ.get("WOWLET_REDIS_PASSWORD")
COIN_NAME = os.environ.get("FEATHER_COIN_NAME", "monero").lower() # as per coingecko
COIN_SYMBOL = os.environ.get("FEATHER_COIN_SYMBOL", "xmr").lower() # as per coingecko
COIN_GENESIS_DATE = os.environ.get("FEATHER_COIN_GENESIS_DATE", "20140418")
COIN_MODE = os.environ.get("FEATHER_COIN_MODE", "mainnet").lower()
COIN_NAME = os.environ.get("WOWLET_COIN_NAME", "monero").lower() # as per coingecko
COIN_SYMBOL = os.environ.get("WOWLET_COIN_SYMBOL", "xmr").lower() # as per coingecko
COIN_GENESIS_DATE = os.environ.get("WOWLET_COIN_GENESIS_DATE", "20140418")
COIN_MODE = os.environ.get("WOWLET_COIN_MODE", "mainnet").lower()
TOR_SOCKS_PROXY = os.environ.get("FEATHER_TOR_SOCKS_PROXY", "socks5://127.0.0.1:9050")
TOR_SOCKS_PROXY = os.environ.get("WOWLET_TOR_SOCKS_PROXY", "socks5://127.0.0.1:9050")
# while fetching USD price from coingecko, also include these extra coins:
CRYPTO_RATES_COINS_EXTRA = {

View file

@ -11,7 +11,7 @@ from quart import Quart
from quart_session import Session
import aioredis
from fapi.utils import current_worker_thread_is_primary, print_banner
from wowlet_backend.utils import current_worker_thread_is_primary, print_banner
import settings
now = datetime.now()
@ -56,7 +56,7 @@ async def _setup_tasks(app: Quart):
if not _is_primary_worker_thread:
return
from fapi.tasks import (
from wowlet_backend.tasks import (
BlockheightTask, HistoricalPriceTask, FundingProposalsTask,
CryptoRatesTask, FiatRatesTask, RedditTask, RPCNodeCheckTask,
XmrigTask, XmrToTask)
@ -72,9 +72,6 @@ async def _setup_tasks(app: Quart):
if settings.COIN_SYMBOL in ["xmr", "wow"]:
asyncio.create_task(FundingProposalsTask().start())
if settings.COIN_SYMBOL == "xmr":
asyncio.create_task(XmrToTask().start())
def _setup_logging():
from logging import Formatter
@ -111,6 +108,6 @@ def create_app():
await _setup_user_agents(app)
await _setup_tasks(app)
import fapi.routes
import wowlet_backend.routes
return app

View file

@ -7,9 +7,9 @@ import json
from quart import websocket, jsonify
from fapi.factory import app
from fapi.wsparse import WebsocketParse
from fapi.utils import collect_websocket, feather_data
from wowlet_backend.factory import app
from wowlet_backend.wsparse import WebsocketParse
from wowlet_backend.utils import collect_websocket, feather_data
@app.route("/")

View file

@ -39,7 +39,7 @@ class FeatherTask:
self._running = False
async def start(self, *args, **kwargs):
from fapi.factory import app, connected_websockets
from wowlet_backend.factory import app, connected_websockets
if not self._active:
# invalid task
return
@ -124,7 +124,7 @@ class FeatherTask:
raise NotImplementedError()
async def cache_json_get(self, key: str, path="."):
from fapi.factory import app, cache
from wowlet_backend.factory import app, cache
try:
data = await cache.execute('JSON.GET', key, path)
@ -134,7 +134,7 @@ class FeatherTask:
app.logger.error(f"Redis error: {ex}")
async def cache_get(self, key: str) -> dict:
from fapi.factory import app, cache
from wowlet_backend.factory import app, cache
try:
data = await cache.get(key)
@ -145,7 +145,7 @@ class FeatherTask:
app.logger.error(f"Redis GET error with key '{key}': {ex}")
async def cache_set(self, key, val: Union[dict, int], expiry: int = 0) -> bool:
from fapi.factory import app, cache
from wowlet_backend.factory import app, cache
try:
data = json.dumps(val)
if isinstance(expiry, int) and expiry > 0:
@ -157,12 +157,12 @@ class FeatherTask:
app.logger.error(f"Redis SET error with key '{key}': {ex}")
from fapi.tasks.proposals import FundingProposalsTask
from fapi.tasks.historical_prices import HistoricalPriceTask
from fapi.tasks.blockheight import BlockheightTask
from fapi.tasks.rates_fiat import FiatRatesTask
from fapi.tasks.rates_crypto import CryptoRatesTask
from fapi.tasks.reddit import RedditTask
from fapi.tasks.rpc_nodes import RPCNodeCheckTask
from fapi.tasks.xmrig import XmrigTask
from fapi.tasks.xmrto import XmrToTask
from wowlet_backend.tasks.proposals import FundingProposalsTask
from wowlet_backend.tasks.historical_prices import HistoricalPriceTask
from wowlet_backend.tasks.blockheight import BlockheightTask
from wowlet_backend.tasks.rates_fiat import FiatRatesTask
from wowlet_backend.tasks.rates_crypto import CryptoRatesTask
from wowlet_backend.tasks.reddit import RedditTask
from wowlet_backend.tasks.rpc_nodes import RPCNodeCheckTask
from wowlet_backend.tasks.xmrig import XmrigTask
from wowlet_backend.tasks.xmrto import XmrToTask

View file

@ -8,8 +8,8 @@ from collections import Counter
from functools import partial
import settings
from fapi.utils import httpget, popularity_contest
from fapi.tasks import FeatherTask
from wowlet_backend.utils import httpget, popularity_contest
from wowlet_backend.tasks import FeatherTask
class BlockheightTask(FeatherTask):
@ -81,7 +81,7 @@ class BlockheightTask(FeatherTask):
}
async def task(self) -> Union[dict, None]:
from fapi.factory import app
from wowlet_backend.factory import app
coin_network_types = ["mainnet", "stagenet", "testnet"]
data = {t: 0 for t in coin_network_types}

View file

@ -11,8 +11,8 @@ from datetime import datetime
import aiofiles
import settings
from fapi.utils import httpget
from fapi.tasks import FeatherTask
from wowlet_backend.utils import httpget
from wowlet_backend.tasks import FeatherTask
class HistoricalPriceTask(FeatherTask):
@ -91,7 +91,7 @@ class HistoricalPriceTask(FeatherTask):
"""This function is called when a Feather wallet client asks
for (a range of) historical fiat information. It returns the
data filtered by the parameters."""
from fapi.factory import cache
from wowlet_backend.factory import cache
blob = await cache.get("historical_fiat")
blob = json.loads(blob)

View file

@ -6,14 +6,14 @@ from bs4 import BeautifulSoup
from typing import List
import settings
from fapi.utils import httpget
from fapi.tasks import FeatherTask
from wowlet_backend.utils import httpget
from wowlet_backend.tasks import FeatherTask
class FundingProposalsTask(FeatherTask):
"""Fetch funding proposals made by the community."""
def __init__(self, interval: int = 600):
from fapi.factory import app
from wowlet_backend.factory import app
super(FundingProposalsTask, self).__init__(interval)
self._cache_key = "funding_proposals"
@ -58,7 +58,7 @@ class FundingProposalsTask(FeatherTask):
# - API does not allow filtering
# - API sometimes breaks; https://hackerone.com/reports/934231
# we'll web scrape instead
from fapi.factory import app
from wowlet_backend.factory import app
content = await httpget(f"{self._http_endpoint}/funding-required/", json=False)
soup = BeautifulSoup(content, "html.parser")

View file

@ -5,8 +5,8 @@
from typing import List, Union
import settings
from fapi.utils import httpget
from fapi.tasks import FeatherTask
from wowlet_backend.utils import httpget
from wowlet_backend.tasks import FeatherTask
class CryptoRatesTask(FeatherTask):
@ -22,7 +22,7 @@ class CryptoRatesTask(FeatherTask):
async def task(self) -> Union[List[dict], None]:
"""Fetch USD prices for various coins"""
from fapi.factory import app
from wowlet_backend.factory import app
url = f"{self._http_api_gecko}/coins/markets?vs_currency=usd"
rates = await httpget(url, json=True)

View file

@ -2,8 +2,8 @@
# Copyright (c) 2020, The Monero Project.
# Copyright (c) 2020, dsc@xmr.pm
from fapi.utils import httpget
from fapi.tasks import FeatherTask
from wowlet_backend.utils import httpget
from wowlet_backend.tasks import FeatherTask
class FiatRatesTask(FeatherTask):

View file

@ -4,13 +4,13 @@
import html
import settings
from fapi.utils import httpget
from fapi.tasks import FeatherTask
from wowlet_backend.utils import httpget
from wowlet_backend.tasks import FeatherTask
class RedditTask(FeatherTask):
def __init__(self, interval: int = 900):
from fapi.factory import app
from wowlet_backend.factory import app
super(RedditTask, self).__init__(interval)
self._cache_key = "reddit"
@ -36,7 +36,7 @@ class RedditTask(FeatherTask):
self._http_endpoint = self._http_endpoint[:-1]
async def task(self):
from fapi.factory import app
from wowlet_backend.factory import app
url = f"{self._http_endpoint}/new.json?limit=15"
try:

View file

@ -6,8 +6,8 @@ import json
from typing import List
import settings
from fapi.utils import httpget, popularity_contest
from fapi.tasks import FeatherTask
from wowlet_backend.utils import httpget, popularity_contest
from wowlet_backend.tasks import FeatherTask
class RPCNodeCheckTask(FeatherTask):
@ -24,7 +24,7 @@ class RPCNodeCheckTask(FeatherTask):
async def task(self) -> List[dict]:
"""Check RPC nodes status"""
from fapi.factory import app, cache
from wowlet_backend.factory import app, cache
try:
heights = json.loads(await cache.get("blockheights"))

View file

@ -5,8 +5,8 @@
from dateutil.parser import parse
import settings
from fapi.utils import httpget
from fapi.tasks import FeatherTask
from wowlet_backend.utils import httpget
from wowlet_backend.tasks import FeatherTask
class XmrigTask(FeatherTask):

View file

@ -3,8 +3,8 @@
# Copyright (c) 2020, dsc@xmr.pm
import settings
from fapi.utils import httpget
from fapi.tasks import FeatherTask
from wowlet_backend.utils import httpget
from wowlet_backend.tasks import FeatherTask
class XmrToTask(FeatherTask):

View file

@ -35,7 +35,7 @@ def print_banner():
def collect_websocket(func):
@wraps(func)
async def wrapper(*args, **kwargs):
from fapi.factory import connected_websockets
from wowlet_backend.factory import connected_websockets
queue = asyncio.Queue()
connected_websockets.add(queue)
try:
@ -63,14 +63,14 @@ async def httpget(url: str, json=True, timeout: int = 5, socks5: str = None, rai
def random_agent():
from fapi.factory import user_agents
from wowlet_backend.factory import user_agents
return random.choice(user_agents)
async def feather_data():
"""A collection of data collected by
`FeatherTask`, for Feather wallet clients."""
from fapi.factory import cache, now
from wowlet_backend.factory import cache, now
data = await cache.get("data")
if data:
data = json.loads(data)
@ -110,7 +110,7 @@ def current_worker_thread_is_primary() -> bool:
current instance is responsible for the
recurring Feather tasks.
"""
from fapi.factory import app
from wowlet_backend.factory import app
current_pid = os.getpid()
parent_pid = os.getppid()

View file

@ -21,5 +21,5 @@ class WebsocketParse:
year = data.get('year')
month = data.get('month')
from fapi.tasks.historical_prices import HistoricalPriceTask
from wowlet_backend.tasks.historical_prices import HistoricalPriceTask
return await HistoricalPriceTask.get(year, month)