Initial commit

main
lalanza808 4 years ago
commit ba660d7239

129
.gitignore vendored

@ -0,0 +1,129 @@
# 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/

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 lalanza808
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -0,0 +1,17 @@
.PHONY: format help
# Help system from https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html
.DEFAULT_GOAL := help
help:
@grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
setup: ## Establish local environment with dependencies installed
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
up: ## Build and run the required containers by fetching binaries
docker-compose -f docker-compose.yaml up -d
shell: ## Start Flask CLI shell
FLASK_APP=app/app.py FLASK_SECRETS=config.py FLASK_DEBUG=0 FLASK_ENV=production .venv/bin/flask shell

@ -0,0 +1,2 @@
# lza-quart-app
Template project for Quart (Python/Flask) applications.

@ -0,0 +1,7 @@
from myapp.factory import create_app
app = create_app()
if __name__ == '__main__':
app.run()

@ -0,0 +1,16 @@
version: '3'
services:
db:
image: postgres:9.6.15-alpine
ports:
- 127.0.0.1:5432:5432
environment:
POSTGRES_PASSWORD: ${DB_PASS}
POSTGRES_USER: ${DB_USER:-myapp}
POSTGRES_DB: ${DB_NAME:-myapp}
volumes:
- ${DATA_DIR:-./data/postgresql}:/var/lib/postgresql/data
cache:
image: redis:latest
ports:
- 127.0.0.1:6379:6379

@ -0,0 +1,16 @@
DB_PASS=xxxxxxxxxxxxxxxxxxx
DB_USER=myapp
DB_NAME=myapp
DB_HOST=localhost
XMR_WALLET_PATH=/data/xmr-wallet
XMR_WALLET_PASS=xxxxxxxxxxxxxxxxxxx
XMR_WALLET_RPC_USER=xxxxxxxxxx
XMR_WALLET_RPC_PASS=xxxxxxxxxxxxxxxxxxx
XMR_WALLET_RPC_ENDPOINT=http://localhost:9090
XMR_DAEMON_URI=http://super.fast.node.xmr.pm:38089
SITE_NAME=myapp
SECRET_KEY=xxxxxxxxxxxxxxxxxxx
STATS_TOKEN=xxxxxxxxxxxxxxxxxxxx
SERVER_NAME=localhost:5000

@ -0,0 +1,30 @@
import click
from quart import Blueprint, current_app
from myapp.models import MyThing
from myapp.factory import db
bp = Blueprint('filters', 'filters')
@bp.cli.command('init')
def init():
import app.models
db.create_all()
@bp.cli.command('delete')
@click.argument('thing_id')
def delete(thing_id):
thing = MyThing.query.get(thing_id)
if thing:
db.session.delete(thing)
db.session.commit()
click.echo(f'MyThing {thing.id} was deleted')
else:
click.echo('MyThing ID does not exist')
@bp.cli.command('list')
def list_things():
thing = MyThing.query.all()
for i in thing:
click.echo(i.id)

@ -0,0 +1,33 @@
from dotenv import load_dotenv
from secrets import token_urlsafe
from os import getenv
load_dotenv()
# Site meta
SITE_NAME = getenv('SITE_NAME', 'MyApp')
SECRET_KEY = getenv('SECRET_KEY')
STATS_TOKEN = getenv('STATS_TOKEN', token_urlsafe(8))
SERVER_NAME = getenv('SERVER_NAME', 'localhost:5000')
# Crypto RPC
XMR_WALLET_PASS = getenv('XMR_WALLET_PASS')
XMR_WALLET_RPC_USER = getenv('XMR_WALLET_RPC_USER')
XMR_WALLET_RPC_PASS = getenv('XMR_WALLET_RPC_PASS')
XMR_WALLET_RPC_ENDPOINT = getenv('XMR_WALLET_RPC_ENDPOINT')
XMR_DAEMON_URI = getenv('XMR_DAEMON_URI')
# Database
DB_HOST = getenv('DB_HOST', 'localhost')
DB_PORT = getenv('DB_PORT', 5432)
DB_NAME = getenv('DB_NAME', 'myapp')
DB_USER = getenv('DB_USER', 'myapp')
DB_PASS = getenv('DB_PASS')
# Redis
REDIS_HOST = getenv('REDIS_HOST', 'localhost')
REDIS_PORT = getenv('REDIS_PORT', 6379)
# Development
TEMPLATES_AUTO_RELOAD = True

@ -0,0 +1,37 @@
import quart.flask_patch
from quart import Quart
from flask_sqlalchemy import SQLAlchemy
from myapp import config
db = SQLAlchemy()
async def _setup_db(app: Quart):
uri = 'postgresql+psycopg2://{user}:{pw}@{host}:{port}/{db}'.format(
user=config.DB_USER,
pw=config.DB_PASS,
host=config.DB_HOST,
port=config.DB_PORT,
db=config.DB_NAME
)
app.config['SQLALCHEMY_DATABASE_URI'] = uri
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
def create_app():
app = Quart(__name__)
app.config.from_envvar('QUART_SECRETS')
@app.before_serving
async def startup():
from myapp.routes import meta, api
from myapp import filters
await _setup_db(app)
app.register_blueprint(meta.bp)
app.register_blueprint(api.bp)
app.register_blueprint(filters.bp)
# app.register_blueprint(cli.bp)
return app

@ -0,0 +1,15 @@
from datetime import datetime
from quart import Blueprint, current_app
bp = Blueprint('filters', 'filters')
@bp.app_template_filter('ts')
def from_ts(v):
return datetime.fromtimestamp(v)
@bp.app_template_filter('xmr_block_explorer')
def xmr_block_explorer(v):
return f'https://www.exploremonero.com/transaction/{v}'

@ -0,0 +1,8 @@
from flask_wtf import FlaskForm
from wtforms import StringField, BooleanField
from wtforms.validators import DataRequired
class Login(FlaskForm):
email = StringField('Email Address:', validators=[DataRequired()], render_kw={"placeholder": "Email", "class": "form-control", "type": "email"})
password = StringField('Password:', validators=[DataRequired()], render_kw={"placeholder": "Password", "class": "form-control", "type": "password"})

@ -0,0 +1,35 @@
from json import loads as json_loads
from json import dumps as json_dumps
from datetime import timedelta
from redis import Redis
from app.library.market import get_market_data
from app import config
class Cache(object):
def __init__(self):
self.redis = Redis(host=config.REDIS_HOST, port=config.REDIS_PORT)
def store_data(self, item_name, expiration_minutes, data):
self.redis.setex(
item_name,
timedelta(minutes=expiration_minutes),
value=data
)
def get_coin_price(self, coin_name):
key_name = f'{coin_name}_price'
data = self.redis.get(key_name)
if data:
return json_loads(data)
else:
d = get_market_data(coin_name)
data = {
key_name: d['market_data']['current_price'],
}
self.store_data(key_name, 4, json_dumps(data))
return data
cache = Cache()

@ -0,0 +1,16 @@
from requests import get as r_get
def get_market_data(coin_name="monero"):
data = {
'localization': False,
'tickers': False,
'market_data': True,
'community_data': False,
'developer_data': False,
'sparkline': False
}
headers = {'accept': 'application/json'}
url = f'https://api.coingecko.com/api/v3/coins/{coin_name}'
r = r_get(url, headers=headers, data=data)
return r.json()

@ -0,0 +1,38 @@
from datetime import datetime
from uuid import uuid4
from sqlalchemy.sql import func
from myapp.factory import db
from myapp import config
def rand_id():
return uuid4().hex
class MyThing(db.Model):
__tablename__ = 'swaps'
# Meta
id = db.Column(db.Integer, primary_key=True)
# id = db.Column(db.String(80), primary_key=True, default=rand_id) # hex based id
date = db.Column(db.DateTime, server_default=func.now())
my_bool = db.Column(db.Boolean)
my_int = db.Column(db.Integer)
my_str = db.Column(db.String(150))
completed = db.Column(db.Boolean, default=False)
completed_date = db.Column(db.DateTime, nullable=True)
def __repr__(self):
return self.id
def hours_elapsed(self):
now = datetime.utcnow()
if since_completed:
if self.completed_date:
diff = now - self.completed_date
else:
return 0
else:
diff = now - self.date
return diff.total_seconds() / 60 / 60

@ -0,0 +1,11 @@
from quart import Blueprint, jsonify
bp = Blueprint('api', 'api')
@bp.route('/api/test')
async def get_prices():
return jsonify({
'test': True,
'message': 'This is only a test.'
})

@ -0,0 +1,8 @@
from quart import Blueprint, render_template
bp = Blueprint('meta', 'meta')
@bp.route('/')
async def index():
return await render_template('index.html')

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

@ -0,0 +1,7 @@
<footer id="footer">
<ul class="icons">
</ul>
<ul class="copyright">
<li>&copy; {{ config.SITE_NAME }}. All rights reserved.</li>
</ul>
</footer>

@ -0,0 +1,20 @@
<head>
<title>{{ config.SITE_NAME }}</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
<link rel="shortcut icon" href="/static/images/favicon.ico" type="image/png">
<meta itemprop="name" content="MyThing app">
<meta itemprop="description" content="This is a sample application making using of Quart, a Flask replacement for Python">
<meta itemprop="image" content="">
<meta property="og:url" content="">
<meta property="og:type" content="website">
<meta property="og:title" content="">
<meta property="og:description" content="This is a sample application making using of Quart, a Flask replacement for Python">
<meta property="og:image" content="">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="">
<meta name="twitter:description" content="This is a sample application making using of Quart, a Flask replacement for Python">
<meta name="twitter:image" content="">
<meta name="keywords" content="Wownero, Monero, crypto, swap">
<link rel="stylesheet" href="/static/css/main.css" />
</head>

@ -0,0 +1,9 @@
<header id="header">
<h1 id="logo"><a href="/">{{ config.SITE_NAME }}</a></h1>
<nav id="nav">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/#search" class="button primary">Search</a></li>
</ul>
</nav>
</header>

@ -0,0 +1,9 @@
<script src="/static/js/main.js"></script>
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
<p>{{ message }}</p>
{% endfor %}
{% endif %}
{% endwith %}

@ -0,0 +1,37 @@
<!DOCTYPE HTML>
<html>
{% include 'includes/head.html' %}
<body class="is-preload landing">
<div id="page-wrapper">
<header id="header">
<h1 id="logo"><a href="/">MyThing sample app</a></h1>
<nav id="nav">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/#search" class="button primary">Search</a></li>
</ul>
</nav>
</header>
<section id="banner">
<div class="content">
<header>
<h2>MyThing</h2>
<p>This is a sample app.</p>
</header>
<span class="image"><img src="/static/images/monero-logo.png" width=150px></span>
</div>
<a href="#swap" class="goto-next scrolly">Next</a>
</section>
{% include 'includes/footer.html' %}
</div>
{% include 'includes/scripts.html' %}
</body>
</html>

@ -0,0 +1,14 @@
Flask
Flask-SQLAlchemy
Flask-WTF
flask-login
gunicorn
Pillow
psycopg2-binary
python-dotenv
qrcode
redis
requests
SQLAlchemy
WTForms
quart
Loading…
Cancel
Save