mirror of
https://git.wownero.com/lza_menace/wowstash.git
synced 2024-08-15 00:33:15 +00:00
introducing auth blueprints, refactoring app init, setting up forms and sqlalchemy, starting user registration process
This commit is contained in:
parent
f245272796
commit
218500c20c
20 changed files with 225 additions and 100 deletions
2
bin/dev
2
bin/dev
|
@ -1,7 +1,7 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
source .venv/bin/activate
|
source .venv/bin/activate
|
||||||
export FLASK_APP=wowstash/run.py
|
export FLASK_APP=wowstash/app.py
|
||||||
export FLASK_SECRETS=config.py
|
export FLASK_SECRETS=config.py
|
||||||
export FLASK_DEBUG=1
|
export FLASK_DEBUG=1
|
||||||
flask run
|
flask run
|
||||||
|
|
|
@ -3,3 +3,6 @@ psycopg2-binary
|
||||||
redis
|
redis
|
||||||
flask_session
|
flask_session
|
||||||
requests
|
requests
|
||||||
|
Flask-WTF
|
||||||
|
flask_sqlalchemy
|
||||||
|
flask-bcrypt
|
||||||
|
|
6
wowstash/app.py
Normal file
6
wowstash/app.py
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
from wowstash.factory import create_app
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app = create_app()
|
||||||
|
app.run()
|
|
@ -1,2 +1,3 @@
|
||||||
from .account import account_bp
|
from .account import account_bp
|
||||||
from .authentication import authentication_bp
|
from .authentication import authentication_bp
|
||||||
|
from .meta import meta_bp
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
from flask import request, render_template, session
|
from flask import request, render_template, session
|
||||||
from flask import redirect, url_for, current_app
|
from flask import redirect, url_for, current_app
|
||||||
from wallet.blueprints.account import account_bp
|
from wowstash.blueprints.account import account_bp
|
||||||
from wallet.library.daemon import daemon
|
|
||||||
from wallet.library.wallet import wallet
|
|
||||||
|
|
||||||
@account_bp.route("/account")
|
@account_bp.route("/account")
|
||||||
def overview():
|
def overview():
|
||||||
|
|
|
@ -1,42 +1,25 @@
|
||||||
from flask import request, render_template, session, redirect, url_for
|
from flask import request, render_template, session, redirect, url_for
|
||||||
from wallet.blueprints.authentication import authentication_bp
|
from wowstash.blueprints.authentication import authentication_bp
|
||||||
from wallet.library.daemon import daemon
|
from wowstash.forms import Register
|
||||||
from wallet.library.wallet import wallet
|
from wowstash.models import User
|
||||||
from monero.seed import Seed
|
|
||||||
from binascii import hexlify
|
|
||||||
from datetime import datetime
|
@authentication_bp.route("/register", methods=["GET", "POST"])
|
||||||
from os import urandom
|
def register():
|
||||||
|
form = Register()
|
||||||
|
if form.validate_on_submit():
|
||||||
|
print(dir(User))
|
||||||
|
# user = User.query
|
||||||
|
user = User.objects.filter(email=form.email.data)
|
||||||
|
print(user)
|
||||||
|
return "ok"
|
||||||
|
else:
|
||||||
|
print(form)
|
||||||
|
return render_template("authentication/register.html", form=form)
|
||||||
|
|
||||||
@authentication_bp.route("/login", methods=["GET", "POST"])
|
@authentication_bp.route("/login", methods=["GET", "POST"])
|
||||||
def login():
|
def login():
|
||||||
error = None
|
return render_template("authentication/login.html")
|
||||||
if request.method == "POST":
|
|
||||||
if request.form.get('seed'):
|
|
||||||
try:
|
|
||||||
seed = Seed(str(request.form['seed']))
|
|
||||||
|
|
||||||
session['seed'] = seed.phrase
|
|
||||||
session['start_time'] = datetime.utcnow()
|
|
||||||
session['public_address'] = seed.public_address()
|
|
||||||
session['private_spend_key'] = seed.secret_spend_key()
|
|
||||||
session['public_spend_key'] = seed.public_spend_key()
|
|
||||||
session['private_view_key'] = seed.secret_view_key()
|
|
||||||
session['public_view_key'] = seed.public_view_key()
|
|
||||||
session['wallet_password'] = hexlify(urandom(64))
|
|
||||||
if request.form.get('persistence'):
|
|
||||||
session['wallet_persistence'] = "Enabled"
|
|
||||||
else:
|
|
||||||
session['wallet_persistence'] = "Disabled"
|
|
||||||
return redirect(url_for('account.overview'))
|
|
||||||
except AssertionError:
|
|
||||||
error = "Invalid seed checksum"
|
|
||||||
except Exception as e:
|
|
||||||
error = "Invalid seed {0}".format(e)
|
|
||||||
|
|
||||||
else:
|
|
||||||
error = "Must provide a seed"
|
|
||||||
|
|
||||||
return render_template("login.html", error=error)
|
|
||||||
|
|
||||||
@authentication_bp.route("/logout")
|
@authentication_bp.route("/logout")
|
||||||
def logout():
|
def logout():
|
||||||
|
|
5
wowstash/blueprints/meta/__init__.py
Normal file
5
wowstash/blueprints/meta/__init__.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
from flask import Blueprint
|
||||||
|
|
||||||
|
meta_bp = Blueprint("meta", __name__)
|
||||||
|
|
||||||
|
from . import routes
|
23
wowstash/blueprints/meta/routes.py
Normal file
23
wowstash/blueprints/meta/routes.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
from flask import request, render_template, session, redirect, url_for, make_response, jsonify
|
||||||
|
from wowstash.blueprints.meta import meta_bp
|
||||||
|
from wowstash.library.jsonrpc import daemon
|
||||||
|
from wowstash.library.info import info
|
||||||
|
from wowstash.library.db import Database
|
||||||
|
|
||||||
|
|
||||||
|
@meta_bp.route('/')
|
||||||
|
def index():
|
||||||
|
return render_template('index.html', node=daemon.info(), info=info.get_info())
|
||||||
|
|
||||||
|
@meta_bp.route('/health')
|
||||||
|
def health():
|
||||||
|
return make_response(jsonify({
|
||||||
|
'cache': info.redis.ping(),
|
||||||
|
'db': Database().connected
|
||||||
|
}), 200)
|
||||||
|
|
||||||
|
# @app.errorhandler(404)
|
||||||
|
# def not_found(error):
|
||||||
|
# return make_response(jsonify({
|
||||||
|
# 'error': 'Page not found'
|
||||||
|
# }), 404)
|
12
wowstash/cli.py
Normal file
12
wowstash/cli.py
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
|
||||||
|
@app.errorhandler(404)
|
||||||
|
def not_found(error):
|
||||||
|
return make_response(jsonify({
|
||||||
|
'error': 'Page not found'
|
||||||
|
}), 404)
|
||||||
|
|
||||||
|
@app.cli.command('initdb')
|
||||||
|
def initdb():
|
||||||
|
from wowstash.models import *
|
||||||
|
db.create_all()
|
61
wowstash/factory.py
Normal file
61
wowstash/factory.py
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
import wowstash.models
|
||||||
|
from flask import Flask
|
||||||
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
|
from flask_session import Session
|
||||||
|
from flask_bcrypt import Bcrypt
|
||||||
|
from redis import Redis
|
||||||
|
from wowstash.blueprints.authentication import authentication_bp
|
||||||
|
from wowstash.blueprints.account import account_bp
|
||||||
|
from wowstash.blueprints.meta import meta_bp
|
||||||
|
from wowstash import config
|
||||||
|
|
||||||
|
|
||||||
|
app = None
|
||||||
|
db = None
|
||||||
|
bcrypt = None
|
||||||
|
|
||||||
|
def _setup_db(app: Flask):
|
||||||
|
global db
|
||||||
|
uri = 'postgresql+psycopg2://{user}:{pw}@{url}/{db}'.format(
|
||||||
|
user=config.DB_USER,
|
||||||
|
pw=config.DB_PASS,
|
||||||
|
url=config.DB_HOST,
|
||||||
|
db=config.DB_NAME
|
||||||
|
)
|
||||||
|
app.config['SQLALCHEMY_DATABASE_URI'] = uri
|
||||||
|
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
||||||
|
db = SQLAlchemy(app)
|
||||||
|
db.create_all()
|
||||||
|
|
||||||
|
def _setup_session(app: Flask):
|
||||||
|
app.config['SESSION_TYPE'] = 'redis'
|
||||||
|
app.config['SESSION_COOKIE_NAME'] = app.config.get('SESSION_COOKIE_NAME', 'wowstash')
|
||||||
|
app.config['SESSION_REDIS'] = Redis(
|
||||||
|
host=app.config['REDIS_HOST'],
|
||||||
|
port=app.config['REDIS_PORT']
|
||||||
|
)
|
||||||
|
Session(app)
|
||||||
|
|
||||||
|
def _setup_bcrypt(app: Flask):
|
||||||
|
bcrypt = Bcrypt(app)
|
||||||
|
|
||||||
|
def create_app():
|
||||||
|
global app
|
||||||
|
global db
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.config.from_envvar('FLASK_SECRETS')
|
||||||
|
app.secret_key = app.config['SECRET_KEY']
|
||||||
|
|
||||||
|
# Setup backends
|
||||||
|
_setup_db(app)
|
||||||
|
_setup_session(app)
|
||||||
|
_setup_bcrypt(app)
|
||||||
|
|
||||||
|
# Routes
|
||||||
|
app.register_blueprint(meta_bp)
|
||||||
|
app.register_blueprint(authentication_bp)
|
||||||
|
app.register_blueprint(account_bp)
|
||||||
|
|
||||||
|
app.app_context().push()
|
||||||
|
return app
|
8
wowstash/forms.py
Normal file
8
wowstash/forms.py
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from wtforms import StringField
|
||||||
|
from wtforms.validators import DataRequired
|
||||||
|
|
||||||
|
|
||||||
|
class Register(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"})
|
|
@ -68,7 +68,7 @@ class Wallet(object):
|
||||||
_account = self.make_wallet_rpc('create_account', {'label': label})
|
_account = self.make_wallet_rpc('create_account', {'label': label})
|
||||||
return _account['account_index']
|
return _account['account_index']
|
||||||
|
|
||||||
def addresses(self, account, addr_indices=None):
|
def addresses(self, account=0, addr_indices=None):
|
||||||
qdata = {'account_index': account}
|
qdata = {'account_index': account}
|
||||||
if addr_indices:
|
if addr_indices:
|
||||||
qdata['address_index'] = addr_indices
|
qdata['address_index'] = addr_indices
|
||||||
|
@ -78,12 +78,12 @@ class Wallet(object):
|
||||||
addresses[_addr['address_index']] = _addr['address']
|
addresses[_addr['address_index']] = _addr['address']
|
||||||
return addresses
|
return addresses
|
||||||
|
|
||||||
def new_address(self, account, label=None):
|
def new_address(self, account=0, label=None):
|
||||||
data = {'account_index': account, 'label': label}
|
data = {'account_index': account, 'label': label}
|
||||||
_address = self.make_wallet_rpc('create_address', data)
|
_address = self.make_wallet_rpc('create_address', data)
|
||||||
return (_address['address_index'], _address['address'])
|
return (_address['address_index'], _address['address'])
|
||||||
|
|
||||||
def balances(self, account):
|
def balances(self, account=0):
|
||||||
data = {'account_index': account}
|
data = {'account_index': account}
|
||||||
_balance = self.make_wallet_rpc('get_balance', data)
|
_balance = self.make_wallet_rpc('get_balance', data)
|
||||||
return (from_atomic(_balance['balance']), from_atomic(_balance['unlocked_balance']))
|
return (from_atomic(_balance['balance']), from_atomic(_balance['unlocked_balance']))
|
||||||
|
|
23
wowstash/models.py
Normal file
23
wowstash/models.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
from sqlalchemy import Column, Integer, DateTime, String
|
||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
from sqlalchemy.sql import func
|
||||||
|
from flask_bcrypt import generate_password_hash, check_password_hash
|
||||||
|
from wowstash.factory import db
|
||||||
|
|
||||||
|
|
||||||
|
Base = declarative_base()
|
||||||
|
|
||||||
|
class User(db.Model):
|
||||||
|
__tablename__ = 'users'
|
||||||
|
|
||||||
|
id = db.Column('user_id', db.Integer, primary_key=True)
|
||||||
|
password = db.Column(db.String(120))
|
||||||
|
email = db.Column(db.String(50), unique=True, index=True)
|
||||||
|
subaddress_index = db.Column(db.Integer)
|
||||||
|
registered_on = db.Column(db.DateTime, server_default=func.now())
|
||||||
|
|
||||||
|
def hash_password(self):
|
||||||
|
self.password = generate_password_hash(self.password).decode('utf8')
|
||||||
|
|
||||||
|
def check_password(self, password):
|
||||||
|
return check_password_hash(self.password, password)
|
|
@ -1,51 +0,0 @@
|
||||||
from flask import Flask, jsonify, request, make_response, render_template, session, redirect, url_for, escape
|
|
||||||
from flask_session import Session
|
|
||||||
from datetime import timedelta, datetime
|
|
||||||
from redis import Redis
|
|
||||||
from wowstash.library.jsonrpc import daemon
|
|
||||||
from wowstash.library.info import info
|
|
||||||
from wowstash.library.db import Database
|
|
||||||
from wowstash import config
|
|
||||||
# from wowstash.blueprints.account import account_bp
|
|
||||||
# from wowstash.blueprints.authentication import authentication_bp
|
|
||||||
|
|
||||||
# Setup app
|
|
||||||
app = Flask(__name__)
|
|
||||||
app.config.from_envvar('FLASK_SECRETS')
|
|
||||||
app.secret_key = app.config['SECRET_KEY']
|
|
||||||
|
|
||||||
# Setup sessions
|
|
||||||
app.config['SESSION_REDIS'] = Redis(
|
|
||||||
host=app.config['REDIS_HOST'],
|
|
||||||
port=app.config['REDIS_PORT']
|
|
||||||
)
|
|
||||||
sess = Session()
|
|
||||||
sess.init_app(app)
|
|
||||||
|
|
||||||
# app.register_blueprint(account_bp)
|
|
||||||
# app.register_blueprint(authentication_bp)
|
|
||||||
|
|
||||||
@app.route('/')
|
|
||||||
def index():
|
|
||||||
return render_template('home.html', node=daemon.info(), info=info.get_info())
|
|
||||||
|
|
||||||
@app.route('/health')
|
|
||||||
def health():
|
|
||||||
return make_response(jsonify({
|
|
||||||
'cache': info.redis.ping(),
|
|
||||||
'db': Database().connected
|
|
||||||
}), 200)
|
|
||||||
|
|
||||||
|
|
||||||
@app.errorhandler(404)
|
|
||||||
def not_found(error):
|
|
||||||
return make_response(jsonify({
|
|
||||||
'error': 'Page not found'
|
|
||||||
}), 404)
|
|
||||||
|
|
||||||
@app.cli.command('eviscerate')
|
|
||||||
def eviscerate():
|
|
||||||
print('Eviscerate the proletariat')
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
app.run()
|
|
|
@ -78,7 +78,6 @@ section h2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
#mainNav .navbar-brand {
|
#mainNav .navbar-brand {
|
||||||
color: white;
|
|
||||||
font-family: 'Catamaran', 'Helvetica', 'Arial', 'sans-serif';
|
font-family: 'Catamaran', 'Helvetica', 'Arial', 'sans-serif';
|
||||||
font-weight: 200;
|
font-weight: 200;
|
||||||
letter-spacing: 1px;
|
letter-spacing: 1px;
|
||||||
|
@ -126,7 +125,7 @@ section h2 {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
#mainNav .navbar-brand {
|
#mainNav .navbar-brand {
|
||||||
color: fade(white, 70%);
|
color: white;
|
||||||
}
|
}
|
||||||
#mainNav .navbar-brand:hover, #mainNav .navbar-brand:focus {
|
#mainNav .navbar-brand:hover, #mainNav .navbar-brand:focus {
|
||||||
color: white;
|
color: white;
|
||||||
|
|
|
@ -30,8 +30,10 @@
|
||||||
var navbarCollapse = function() {
|
var navbarCollapse = function() {
|
||||||
if ($("#mainNav").offset().top > 100) {
|
if ($("#mainNav").offset().top > 100) {
|
||||||
$("#mainNav").addClass("navbar-shrink");
|
$("#mainNav").addClass("navbar-shrink");
|
||||||
|
$("#navbar-brand").addClass("navbar-brand");
|
||||||
} else {
|
} else {
|
||||||
$("#mainNav").removeClass("navbar-shrink");
|
$("#mainNav").removeClass("navbar-shrink");
|
||||||
|
// $("#navbar-brand").removeClass("navbar-brand");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Collapse now if page is not at top
|
// Collapse now if page is not at top
|
||||||
|
|
|
@ -27,10 +27,11 @@
|
||||||
<br>
|
<br>
|
||||||
<span>
|
<span>
|
||||||
<button type="submit" class="btn btn-link btn-outline btn-xl">Submit</button>
|
<button type="submit" class="btn btn-link btn-outline btn-xl">Submit</button>
|
||||||
<a href="/new" class="btn btn-outline btn-xl">Create Account</a>
|
|
||||||
</span>
|
</span>
|
||||||
</form>
|
</form>
|
||||||
|
<hr><br>
|
||||||
|
<p>Need an account? Register below:</p>
|
||||||
|
<a href="{{ url_for('authentication.register') }}" class="btn btn-outline btn-xl">Register</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
44
wowstash/templates/authentication/register.html
Normal file
44
wowstash/templates/authentication/register.html
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
{% include 'head.html' %}
|
||||||
|
|
||||||
|
<body id="page-top">
|
||||||
|
|
||||||
|
{% include 'navbar.html' %}
|
||||||
|
|
||||||
|
<header class="masthead">
|
||||||
|
<div class="container h-100">
|
||||||
|
<div class="row h-100">
|
||||||
|
<div class="col-lg-12 my-auto">
|
||||||
|
<div class="header-content mx-auto">
|
||||||
|
<form method="POST" action="{{ url_for('authentication.register') }}">
|
||||||
|
{{ form.csrf_token }}
|
||||||
|
<div class="form-group">
|
||||||
|
{{ form.email.label }}
|
||||||
|
{{ form.email }}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
{{ form.password.label }}
|
||||||
|
{{ form.password }}
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
{% for field, errors in form.errors.items() %}
|
||||||
|
<li>{{ form[field].label }}: {{ ', '.join(errors) }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
<input type="submit" value="Register" class="btn btn-link btn-outline btn-xl">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{% include 'footer.html' %}
|
||||||
|
|
||||||
|
{% include 'scripts.html' %}
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -13,8 +13,8 @@
|
||||||
<div class="col-lg-7 my-auto">
|
<div class="col-lg-7 my-auto">
|
||||||
<div class="header-content mx-auto">
|
<div class="header-content mx-auto">
|
||||||
<h1 class="mb-5">Manage your Wownero funds securely and anonymously.</h1>
|
<h1 class="mb-5">Manage your Wownero funds securely and anonymously.</h1>
|
||||||
<a href="/register" class="btn btn-outline btn-xl">Register</a>
|
<a href="{{ url_for('authentication.register') }}" class="btn btn-outline btn-xl">Register</a>
|
||||||
<a href="/login" class="btn btn-outline btn-xl">Login</a>
|
<a href="{{ url_for('authentication.login') }}" class="btn btn-outline btn-xl">Login</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-5 my-auto">
|
<div class="col-lg-5 my-auto">
|
|
@ -1,6 +1,6 @@
|
||||||
<nav class="navbar navbar-expand-lg navbar-light fixed-top" id="mainNav">
|
<nav class="navbar navbar-expand-lg navbar-light fixed-top" id="mainNav">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<a class="navbar-brand js-scroll-trigger" href="#page-top">{{ config.SITE_NAME }}</a>
|
<a id="navbar-brand" class="navbar-brand js-scroll-trigger" href="#page-top">{{ config.SITE_NAME }}</a>
|
||||||
<button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
|
<button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
Menu
|
Menu
|
||||||
<i class="fa fa-bars"></i>
|
<i class="fa fa-bars"></i>
|
||||||
|
@ -13,6 +13,12 @@
|
||||||
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="#statistics">Statistics</a></li>
|
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="#statistics">Statistics</a></li>
|
||||||
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="#contact">Contact</a></li>
|
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="#contact">Contact</a></li>
|
||||||
<li class="nav-item"><a class="nav-link" href="/login">Login</a></li>
|
<li class="nav-item"><a class="nav-link" href="/login">Login</a></li>
|
||||||
|
{% else %}
|
||||||
|
<li class="nav-item"><a class="nav-link" href="/">Home</a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="/#about">About</a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="/#statistics">Statistics</a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="/#contact">Contact</a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="/login">Login</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue