diff --git a/bin/dev b/bin/dev index ddcff3e..11a0476 100755 --- a/bin/dev +++ b/bin/dev @@ -1,7 +1,7 @@ #!/bin/bash source .venv/bin/activate -export FLASK_APP=wowstash/run.py +export FLASK_APP=wowstash/app.py export FLASK_SECRETS=config.py export FLASK_DEBUG=1 flask run diff --git a/requirements.txt b/requirements.txt index 9772b45..bcce1c1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,6 @@ psycopg2-binary redis flask_session requests +Flask-WTF +flask_sqlalchemy +flask-bcrypt diff --git a/wowstash/app.py b/wowstash/app.py new file mode 100644 index 0000000..e6f5959 --- /dev/null +++ b/wowstash/app.py @@ -0,0 +1,6 @@ +from wowstash.factory import create_app + + +if __name__ == '__main__': + app = create_app() + app.run() diff --git a/wowstash/blueprints/__init__.py b/wowstash/blueprints/__init__.py index 1bd2b40..ea7c7f6 100644 --- a/wowstash/blueprints/__init__.py +++ b/wowstash/blueprints/__init__.py @@ -1,2 +1,3 @@ from .account import account_bp from .authentication import authentication_bp +from .meta import meta_bp diff --git a/wowstash/blueprints/account/routes.py b/wowstash/blueprints/account/routes.py index 1ac1fbc..458f0cf 100644 --- a/wowstash/blueprints/account/routes.py +++ b/wowstash/blueprints/account/routes.py @@ -1,8 +1,7 @@ from flask import request, render_template, session from flask import redirect, url_for, current_app -from wallet.blueprints.account import account_bp -from wallet.library.daemon import daemon -from wallet.library.wallet import wallet +from wowstash.blueprints.account import account_bp + @account_bp.route("/account") def overview(): diff --git a/wowstash/blueprints/authentication/routes.py b/wowstash/blueprints/authentication/routes.py index 43d06db..5614cd6 100644 --- a/wowstash/blueprints/authentication/routes.py +++ b/wowstash/blueprints/authentication/routes.py @@ -1,42 +1,25 @@ from flask import request, render_template, session, redirect, url_for -from wallet.blueprints.authentication import authentication_bp -from wallet.library.daemon import daemon -from wallet.library.wallet import wallet -from monero.seed import Seed -from binascii import hexlify -from datetime import datetime -from os import urandom +from wowstash.blueprints.authentication import authentication_bp +from wowstash.forms import Register +from wowstash.models import User -@authentication_bp.route("/login", methods=["GET", "POST"]) -def login(): - error = None - 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" +@authentication_bp.route("/register", methods=["GET", "POST"]) +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) - return render_template("login.html", error=error) +@authentication_bp.route("/login", methods=["GET", "POST"]) +def login(): + return render_template("authentication/login.html") @authentication_bp.route("/logout") def logout(): diff --git a/wowstash/blueprints/meta/__init__.py b/wowstash/blueprints/meta/__init__.py new file mode 100644 index 0000000..cbe8f3f --- /dev/null +++ b/wowstash/blueprints/meta/__init__.py @@ -0,0 +1,5 @@ +from flask import Blueprint + +meta_bp = Blueprint("meta", __name__) + +from . import routes diff --git a/wowstash/blueprints/meta/routes.py b/wowstash/blueprints/meta/routes.py new file mode 100644 index 0000000..db3dbdf --- /dev/null +++ b/wowstash/blueprints/meta/routes.py @@ -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) diff --git a/wowstash/cli.py b/wowstash/cli.py new file mode 100644 index 0000000..4e3e8f2 --- /dev/null +++ b/wowstash/cli.py @@ -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() diff --git a/wowstash/factory.py b/wowstash/factory.py new file mode 100644 index 0000000..8f84995 --- /dev/null +++ b/wowstash/factory.py @@ -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 diff --git a/wowstash/forms.py b/wowstash/forms.py new file mode 100644 index 0000000..f8e4a70 --- /dev/null +++ b/wowstash/forms.py @@ -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"}) diff --git a/wowstash/library/wownero.py b/wowstash/library/wownero.py index 9169a00..6166bce 100644 --- a/wowstash/library/wownero.py +++ b/wowstash/library/wownero.py @@ -68,7 +68,7 @@ class Wallet(object): _account = self.make_wallet_rpc('create_account', {'label': label}) return _account['account_index'] - def addresses(self, account, addr_indices=None): + def addresses(self, account=0, addr_indices=None): qdata = {'account_index': account} if addr_indices: qdata['address_index'] = addr_indices @@ -78,12 +78,12 @@ class Wallet(object): addresses[_addr['address_index']] = _addr['address'] return addresses - def new_address(self, account, label=None): + def new_address(self, account=0, label=None): data = {'account_index': account, 'label': label} _address = self.make_wallet_rpc('create_address', data) return (_address['address_index'], _address['address']) - def balances(self, account): + def balances(self, account=0): data = {'account_index': account} _balance = self.make_wallet_rpc('get_balance', data) return (from_atomic(_balance['balance']), from_atomic(_balance['unlocked_balance'])) diff --git a/wowstash/models.py b/wowstash/models.py new file mode 100644 index 0000000..04ea861 --- /dev/null +++ b/wowstash/models.py @@ -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) diff --git a/wowstash/run.py b/wowstash/run.py deleted file mode 100644 index 82299ca..0000000 --- a/wowstash/run.py +++ /dev/null @@ -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() diff --git a/wowstash/static/css/new-age.css b/wowstash/static/css/new-age.css index 4282f84..132e951 100644 --- a/wowstash/static/css/new-age.css +++ b/wowstash/static/css/new-age.css @@ -78,7 +78,6 @@ section h2 { } #mainNav .navbar-brand { - color: white; font-family: 'Catamaran', 'Helvetica', 'Arial', 'sans-serif'; font-weight: 200; letter-spacing: 1px; @@ -126,7 +125,7 @@ section h2 { background-color: transparent; } #mainNav .navbar-brand { - color: fade(white, 70%); + color: white; } #mainNav .navbar-brand:hover, #mainNav .navbar-brand:focus { color: white; diff --git a/wowstash/static/js/new-age.js b/wowstash/static/js/new-age.js index 8749275..634a789 100644 --- a/wowstash/static/js/new-age.js +++ b/wowstash/static/js/new-age.js @@ -30,8 +30,10 @@ var navbarCollapse = function() { if ($("#mainNav").offset().top > 100) { $("#mainNav").addClass("navbar-shrink"); + $("#navbar-brand").addClass("navbar-brand"); } else { $("#mainNav").removeClass("navbar-shrink"); + // $("#navbar-brand").removeClass("navbar-brand"); } }; // Collapse now if page is not at top diff --git a/wowstash/templates/login.html b/wowstash/templates/authentication/login.html similarity index 87% rename from wowstash/templates/login.html rename to wowstash/templates/authentication/login.html index 3b08188..7d621e8 100644 --- a/wowstash/templates/login.html +++ b/wowstash/templates/authentication/login.html @@ -27,10 +27,11 @@
- Create Account - +

+

Need an account? Register below:

+ Register diff --git a/wowstash/templates/authentication/register.html b/wowstash/templates/authentication/register.html new file mode 100644 index 0000000..55e9938 --- /dev/null +++ b/wowstash/templates/authentication/register.html @@ -0,0 +1,44 @@ + + + + {% include 'head.html' %} + + + + {% include 'navbar.html' %} + +
+
+
+
+
+
+ {{ form.csrf_token }} +
+ {{ form.email.label }} + {{ form.email }} +
+
+ {{ form.password.label }} + {{ form.password }} +
+
    + {% for field, errors in form.errors.items() %} +
  • {{ form[field].label }}: {{ ', '.join(errors) }}
  • + {% endfor %} +
+ +
+
+
+
+
+
+ + {% include 'footer.html' %} + + {% include 'scripts.html' %} + + + + diff --git a/wowstash/templates/home.html b/wowstash/templates/index.html similarity index 95% rename from wowstash/templates/home.html rename to wowstash/templates/index.html index 2c35c7e..df0667f 100644 --- a/wowstash/templates/home.html +++ b/wowstash/templates/index.html @@ -13,8 +13,8 @@

Manage your Wownero funds securely and anonymously.

- Register - Login + Register + Login
diff --git a/wowstash/templates/navbar.html b/wowstash/templates/navbar.html index 5fd1526..9594ad5 100644 --- a/wowstash/templates/navbar.html +++ b/wowstash/templates/navbar.html @@ -1,6 +1,6 @@