implement registration, login, starting wallet dashboard
This commit is contained in:
parent
561d0155f3
commit
59ea26b8f1
|
@ -6,3 +6,4 @@ requests
|
|||
Flask-WTF
|
||||
flask_sqlalchemy
|
||||
flask-bcrypt
|
||||
flask-login
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
from .account import account_bp
|
||||
from .wallet import wallet_bp
|
||||
from .authentication import authentication_bp
|
||||
from .meta import meta_bp
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
from flask import request, render_template, session
|
||||
from flask import redirect, url_for, current_app
|
||||
from wowstash.blueprints.account import account_bp
|
||||
|
||||
|
||||
@account_bp.route("/account")
|
||||
def overview():
|
||||
if session.get("public_address"):
|
||||
return render_template("account.html",
|
||||
session_data=session,
|
||||
h=daemon.get_height(),
|
||||
wallet=wallet)
|
||||
else:
|
||||
return redirect(url_for("index"))
|
||||
|
||||
@account_bp.route("/account/wallet")
|
||||
def connect_wallet():
|
||||
if session.get("public_address"):
|
||||
wallet.init(host=current_app.config['DAEMON_HOST'],
|
||||
port=current_app.config['DAEMON_PORT'],
|
||||
public_view_key=session['public_view_key'],
|
||||
wallet_password=session['wallet_password'],
|
||||
mnemonic_seed=session['seed'],
|
||||
restore_height=daemon.get_height(),
|
||||
path=current_app.config['BINARY_PATH'])
|
||||
return redirect(url_for("account.overview"))
|
||||
else:
|
||||
return redirect(url_for("index"))
|
|
@ -1,27 +1,79 @@
|
|||
from flask import request, render_template, session, redirect, url_for
|
||||
from flask import request, render_template, session, redirect, url_for, flash
|
||||
from flask_login import login_user, logout_user, current_user
|
||||
from wowstash.blueprints.authentication import authentication_bp
|
||||
from wowstash.forms import Register
|
||||
from wowstash.forms import Register, Login
|
||||
from wowstash.models import User
|
||||
from wowstash.library.jsonrpc import wallet
|
||||
from wowstash.factory import db, bcrypt
|
||||
|
||||
|
||||
@authentication_bp.route("/register", methods=["GET", "POST"])
|
||||
def register():
|
||||
form = Register()
|
||||
if current_user.is_authenticated:
|
||||
flash('Already registered and authenticated.')
|
||||
return redirect(url_for('wallet.dashboard'))
|
||||
|
||||
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)
|
||||
# Check if Wownero wallet is available
|
||||
if wallet.connected is False:
|
||||
flash('Wallet RPC interface is unavailable at this time. Try again later.')
|
||||
return redirect(url_for('authentication.register'))
|
||||
|
||||
# Check if email already exists
|
||||
user = User.query.filter_by(email=form.email.data).first()
|
||||
if user:
|
||||
flash('This email is already registered.')
|
||||
return redirect(url_for('authentication.login'))
|
||||
|
||||
# Create new subaddress
|
||||
subaddress = wallet.new_address(label=form.email.data)
|
||||
|
||||
# Save new user
|
||||
user = User(
|
||||
email=form.email.data,
|
||||
password=bcrypt.generate_password_hash(form.password.data).decode('utf8'),
|
||||
subaddress_index=subaddress[0]
|
||||
)
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
|
||||
# Login user and redirect to wallet page
|
||||
login_user(user)
|
||||
return redirect(url_for('wallet.dashboard'))
|
||||
|
||||
return render_template("authentication/register.html", form=form)
|
||||
|
||||
@authentication_bp.route("/login", methods=["GET", "POST"])
|
||||
def login():
|
||||
return render_template("authentication/login.html")
|
||||
form = Login()
|
||||
if current_user.is_authenticated:
|
||||
flash('Already registered and authenticated.')
|
||||
return redirect(url_for('wallet.dashboard'))
|
||||
|
||||
if form.validate_on_submit():
|
||||
# Check if user doesn't exist
|
||||
user = User.query.filter_by(email=form.email.data).first()
|
||||
if not user:
|
||||
flash('Invalid username or password.')
|
||||
return redirect(url_for('authentication.login'))
|
||||
|
||||
# Check if password is correct
|
||||
password_matches = bcrypt.check_password_hash(
|
||||
user.password,
|
||||
form.password.data
|
||||
)
|
||||
if not password_matches:
|
||||
flash('Invalid username or password.')
|
||||
return redirect(url_for('authentication.login'))
|
||||
|
||||
# Login user and redirect to wallet page
|
||||
login_user(user)
|
||||
return redirect(url_for('wallet.dashboard'))
|
||||
|
||||
return render_template("authentication/login.html", form=form)
|
||||
|
||||
@authentication_bp.route("/logout")
|
||||
def logout():
|
||||
session.clear()
|
||||
return redirect(url_for('index'))
|
||||
logout_user()
|
||||
return redirect(url_for('meta.index'))
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from flask import Blueprint
|
||||
|
||||
account_bp = Blueprint("account", __name__)
|
||||
wallet_bp = Blueprint("wallet", __name__)
|
||||
|
||||
from . import routes
|
|
@ -0,0 +1,21 @@
|
|||
from flask import request, render_template, session, redirect, url_for, current_app
|
||||
from flask_login import login_required, current_user
|
||||
from wowstash.blueprints.wallet import wallet_bp
|
||||
from wowstash.library.jsonrpc import wallet, daemon
|
||||
from wowstash.factory import login_manager
|
||||
from wowstash.models import User
|
||||
|
||||
|
||||
@wallet_bp.route("/wallet/dashboard")
|
||||
@login_required
|
||||
def dashboard():
|
||||
user = User.query.get(current_user.id)
|
||||
wallet_height = wallet.height()['height']
|
||||
daemon_height = daemon.height()['height']
|
||||
subaddress = wallet.get_address(0, user.subaddress_index)['addresses'][0]['address']
|
||||
return render_template(
|
||||
"account/dashboard.html",
|
||||
wallet_height=wallet_height,
|
||||
daemon=daemon_height,
|
||||
subaddress=subaddress
|
||||
)
|
|
@ -11,7 +11,7 @@ DAEMON_PASS = ''
|
|||
# Wallet
|
||||
WALLET_PROTO = 'http'
|
||||
WALLET_HOST = 'localhost'
|
||||
WALLET_PORT = 8888
|
||||
WALLET_PORT = 9999
|
||||
WALLET_USER = 'yyyyy'
|
||||
WALLET_PASS = 'xxxxx'
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ from flask import Flask
|
|||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_session import Session
|
||||
from flask_bcrypt import Bcrypt
|
||||
from flask_login import LoginManager
|
||||
from redis import Redis
|
||||
from wowstash import config
|
||||
|
||||
|
@ -40,6 +41,8 @@ def _setup_bcrypt(app: Flask):
|
|||
def create_app():
|
||||
global app
|
||||
global db
|
||||
global bcrypt
|
||||
global login_manager
|
||||
app = Flask(__name__)
|
||||
app.config.from_envvar('FLASK_SECRETS')
|
||||
app.secret_key = app.config['SECRET_KEY']
|
||||
|
@ -49,13 +52,22 @@ def create_app():
|
|||
_setup_session(app)
|
||||
_setup_bcrypt(app)
|
||||
|
||||
login_manager = LoginManager()
|
||||
login_manager.init_app(app)
|
||||
login_manager.login_view = 'authentication.login'
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(user_id):
|
||||
from wowstash.models import User
|
||||
return User.query.get(user_id)
|
||||
|
||||
# Routes
|
||||
from wowstash.blueprints.authentication import authentication_bp
|
||||
from wowstash.blueprints.account import account_bp
|
||||
from wowstash.blueprints.wallet import wallet_bp
|
||||
from wowstash.blueprints.meta import meta_bp
|
||||
app.register_blueprint(meta_bp)
|
||||
app.register_blueprint(authentication_bp)
|
||||
app.register_blueprint(account_bp)
|
||||
app.register_blueprint(wallet_bp)
|
||||
|
||||
app.app_context().push()
|
||||
return app
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
from flask_wtf import FlaskForm
|
||||
from wtforms import StringField
|
||||
from wtforms import StringField, BooleanField
|
||||
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"})
|
||||
faq_reviewed = BooleanField('FAQ Reviewed:', validators=[DataRequired()], render_kw={"class": "form-control-span"})
|
||||
terms_reviewed = BooleanField('Terms Reviewed:', validators=[DataRequired()], render_kw={"class": "form-control-span"})
|
||||
privacy_reviewed = BooleanField('Privacy Policy Reviewed:', validators=[DataRequired()], render_kw={"class": "form-control-span"})
|
||||
|
||||
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"})
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import json
|
||||
import requests
|
||||
import operator
|
||||
from wowstash import config
|
||||
|
||||
|
||||
class JSONRPC(object):
|
||||
def __init__(self, proto, host, port, username='', password=''):
|
||||
self.endpoint = '{}://{}:{}/'.format(
|
||||
|
@ -32,11 +34,32 @@ class JSONRPC(object):
|
|||
except:
|
||||
return {}
|
||||
|
||||
|
||||
class Wallet(JSONRPC):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
if 'height' in self.height():
|
||||
self.connected = True
|
||||
else:
|
||||
self.connected = False
|
||||
|
||||
def height(self):
|
||||
return self.make_rpc('get_height', {})
|
||||
|
||||
def new_address(self, account_index=0, label=None):
|
||||
data = {'account_index': account_index, 'label': label}
|
||||
_address = self.make_rpc('create_address', data)
|
||||
return (_address['address_index'], _address['address'])
|
||||
|
||||
def get_address(self, account_index, subaddress_index):
|
||||
data = {'account_index': account_index, 'address_index': [subaddress_index]}
|
||||
return self.make_rpc('get_address', data)
|
||||
|
||||
|
||||
class Daemon(JSONRPC):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def info(self):
|
||||
return self.make_rpc('get_info', {}, json_rpc=False)
|
||||
|
||||
|
@ -44,4 +67,18 @@ class Daemon(JSONRPC):
|
|||
return self.make_rpc('get_height', {}, json_rpc=False)
|
||||
|
||||
|
||||
daemon = Daemon(proto=config.DAEMON_PROTO, host=config.DAEMON_HOST, port=config.DAEMON_PORT)
|
||||
daemon = Daemon(
|
||||
proto=config.DAEMON_PROTO,
|
||||
host=config.DAEMON_HOST,
|
||||
port=config.DAEMON_PORT,
|
||||
username=config.DAEMON_USER,
|
||||
password=config.DAEMON_PASS
|
||||
)
|
||||
|
||||
wallet = Wallet(
|
||||
proto=config.WALLET_PROTO,
|
||||
host=config.WALLET_HOST,
|
||||
port=config.WALLET_PORT,
|
||||
username=config.WALLET_USER,
|
||||
password=config.WALLET_PASS
|
||||
)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
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
|
||||
|
||||
|
||||
|
@ -16,8 +15,24 @@ class User(db.Model):
|
|||
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')
|
||||
@property
|
||||
def is_authenticated(self):
|
||||
return True
|
||||
|
||||
def check_password(self, password):
|
||||
return check_password_hash(self.password, password)
|
||||
@property
|
||||
def is_active(self):
|
||||
return True
|
||||
|
||||
@property
|
||||
def is_anonymous(self):
|
||||
return False
|
||||
|
||||
@property
|
||||
def is_admin(self):
|
||||
return self.admin
|
||||
|
||||
def get_id(self):
|
||||
return self.id
|
||||
|
||||
def __repr__(self):
|
||||
return self.username
|
||||
|
|
|
@ -7,21 +7,30 @@
|
|||
|
||||
{% include 'navbar.html' %}
|
||||
|
||||
<header class="masthead">
|
||||
<!-- Height ({{ daemon_height }} / {{ wallet_height }})
|
||||
|
||||
Transactions - Send - Receive
|
||||
|
||||
Your Address
|
||||
|
||||
Transaction History
|
||||
|
||||
Balance: -->
|
||||
|
||||
<!-- <header class="masthead">
|
||||
<div class="container h-100">
|
||||
<div class="row h-100">
|
||||
<div class="col-lg-4 my-auto">
|
||||
<div class="header-content mx-auto">
|
||||
<h1 class="mb-5">Welcome Back</h1>
|
||||
<h2 class="mb-5">Welcome Back {{ current_user.email }}</h2>
|
||||
<h3 class="sm">Account Address</h3>
|
||||
<p class="small">{{ session_data.public_address }}</p>
|
||||
<p class="small">address</p>
|
||||
<h3 class="sm">Wallet Persistence</h3>
|
||||
<p class="small">{{ session_data.wallet_persistence }}</p>
|
||||
{% if wallet.connected %}
|
||||
{% if wallet %}
|
||||
<h3 class="sm">Account Balance</h3>
|
||||
<p class="small">{{ h }}</p>
|
||||
<h3 class="sm">Wallet File</h3>
|
||||
<p class="small">{{ config.WALLET_PATH }}/{{ session_data.wallet_path }}</p>
|
||||
<p class="small">{{ config.WALLET_PATH }}</p>
|
||||
{% else %}
|
||||
<h3 class="sm">Wallet Status</h3>
|
||||
<p class="small">Not Connected</p>
|
||||
|
@ -30,20 +39,40 @@
|
|||
<a href="/account/wallet/" class="btn btn-outline btn-xl">Connect Wallet</a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="col-lg-6 my-auto">
|
||||
<div class="col-lg-6 my-auto">
|
||||
<div class="header-content mx-auto">
|
||||
<h1 class="mb-5">Logs</h1>
|
||||
<h3 class="sm">Transaction Log</h3>
|
||||
<p class="small">{{ session_data.public_address }}</p>
|
||||
<p class="small">{{ session_data.public_address }}</p>
|
||||
<p class="small">{{ session_data.public_address }}</p>
|
||||
<p class="small">{{ session_data.public_address }}</p>
|
||||
<p class="small">{{ session_data.public_address }}</p>
|
||||
<p class="small">asd</p>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
</header> -->
|
||||
|
||||
<section class="section2" id="">
|
||||
<div class="container">
|
||||
<!-- <div class="section-heading text-center">
|
||||
<h2>Welcome</h2>
|
||||
<p>Prices and network information</p>
|
||||
<hr>
|
||||
</div> -->
|
||||
<div class="row fp-row">
|
||||
<div class="col-lg-4">
|
||||
<a class="btn btn-lg btn-link btn-outline btn-xl" href="#">Transactions</a>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<a class="btn btn-lg btn-link btn-outline btn-xl" href="#">Send</a>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<a class="btn btn-lg btn-link btn-outline btn-xl" href="#">Receive</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row fp-row">
|
||||
<p>Your address: {{ subaddress }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- <section class="section1" id="seed">
|
||||
<div class="container">
|
|
@ -12,26 +12,26 @@
|
|||
<div class="row h-100">
|
||||
<div class="col-lg-12 my-auto">
|
||||
<div class="header-content mx-auto">
|
||||
<form method="post" action="/login">
|
||||
<div class="form-group">
|
||||
<label for="seed">Account Seed</label>
|
||||
<input type="text" class="form-control" name="seed" placeholder="Enter account seed">
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input" name="persistence">
|
||||
<label class="form-check-label" for="persistence">Wallet Persistence</label>
|
||||
</div>
|
||||
{% if error %}
|
||||
<p>{{ error }}</p>
|
||||
{% endif %}
|
||||
<br>
|
||||
<span>
|
||||
<button type="submit" class="btn btn-link btn-outline btn-xl">Submit</button>
|
||||
</span>
|
||||
<form method="POST" action="{{ url_for('authentication.login') }}">
|
||||
{{ form.csrf_token }}
|
||||
{% for f in form %}
|
||||
{% if f.name != 'csrf_token' %}
|
||||
<div class="form-group">
|
||||
{{ f.label }}
|
||||
{{ f }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<ul>
|
||||
{% for field, errors in form.errors.items() %}
|
||||
<li>{{ form[field].label }}: {{ ', '.join(errors) }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<input type="submit" value="Login" class="btn btn-link btn-outline btn-xl">
|
||||
</form>
|
||||
<hr><br>
|
||||
<p>Need an account? Register below:</p>
|
||||
<a href="{{ url_for('authentication.register') }}" class="btn btn-outline btn-xl">Register</a>
|
||||
<hr>
|
||||
<p class="small">Click <a href="{{ url_for('authentication.register') }}" class="">here</a> if you need to register.</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -13,8 +13,12 @@
|
|||
<div class="col-lg-7 my-auto">
|
||||
<div class="header-content mx-auto">
|
||||
<h1 class="mb-5">Manage your Wownero funds securely and anonymously.</h1>
|
||||
{% if current_user.is_authenticated %}
|
||||
<a href="{{ url_for('wallet.dashboard') }}" class="btn btn-outline btn-xl">Wallet Dashboard</a>
|
||||
{% else %}
|
||||
<a href="{{ url_for('authentication.register') }}" class="btn btn-outline btn-xl">Register</a>
|
||||
<a href="{{ url_for('authentication.login') }}" class="btn btn-outline btn-xl">Login</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-5 my-auto">
|
||||
|
|
|
@ -12,13 +12,17 @@
|
|||
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="#about">About</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" 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 %}
|
||||
{% if current_user.is_authenticated %}
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('wallet.dashboard') }}">Wallet</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('authentication.logout') }}">Logout</a></li>
|
||||
{% else %}
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('authentication.login') }}">Login</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue