diff --git a/api/api.py b/api/api.py index 5b1e268..e27790a 100644 --- a/api/api.py +++ b/api/api.py @@ -3,7 +3,6 @@ from .users.user_endpoint import UserEndpoint api = Blueprint('api', __name__, url_prefix='/api') - @api.route('/home', methods=['GET']) def api_home(): response = {"message": "home page"} diff --git a/api/users/user_endpoint.py b/api/users/user_endpoint.py index e147fed..7b89b6f 100644 --- a/api/users/user_endpoint.py +++ b/api/users/user_endpoint.py @@ -1,5 +1,7 @@ +from models.User import User, user_schema class UserEndpoint(object): def users(): - response = {"message": "users page"} + user = User.query.first() + response = user_schema.dumps(user) return response diff --git a/app.py b/app.py index a689d04..e1b6270 100644 --- a/app.py +++ b/app.py @@ -1,57 +1,20 @@ import os +from database import db, ma from flask import Flask -# ! SQLAlchemy > Marshmallow - these must be imported in this order -from flask_sqlalchemy import SQLAlchemy -from flask_marshmallow import Marshmallow - -from flask_migrate import Migrate +from configuration.config import DevelopmentConfig from flask_bcrypt import Bcrypt from flask_cors import CORS -app = Flask(__name__) -CORS(app) +def create_app(): + app = Flask(__name__) + CORS(app) -# config database -app_settings = os.getenv( - 'APP_SETTINGS', - 'config.DevelopmentConfig' -) + app.config.from_object(DevelopmentConfig) + db.init_app(app) + ma.init_app(app) + return app -app.config.from_object(app_settings) - -# init bcrypt -bcrypt = Bcrypt(app) - -# init database -db = SQLAlchemy(app) - -# init marshmallow -ma = Marshmallow(app) - -# init all db models -import models - -migrate = Migrate(app, db) - - -# dev server -DEBUG = True -PORT = 8000 - -# Routes - -@app.route('/') -def hello_world(): - return 'Hello World' - -# Blue prints -from api.api import api - -app.register_blueprint(api) - - -if __name__ == '__main__': - app.run(debug=DEBUG, port=PORT) \ No newline at end of file +bcrypt = Bcrypt(create_app()) \ No newline at end of file diff --git a/auth/__init__.py b/auth/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/auth/auth.py b/auth/auth.py new file mode 100644 index 0000000..b199d24 --- /dev/null +++ b/auth/auth.py @@ -0,0 +1,49 @@ +from flask import Blueprint, request, jsonify, session + +from database import db +from models.User import User + +auth = Blueprint('auth', __name__, url_prefix='/auth') + +@auth.route('/signup', methods=['POST']) +def auth_signup(): + data = request.get_json() + user = User.query.filter_by(email=data.get('email')).first() + if not user: + try: + print('getting here 1') + user = User( + email = data['email'], + password = data['password'], + ) + print('getting here 2') + db.session.add(user) + print('wtf') + db.session.commit() + print('user') + auth_token = user.encode_auth_token(user.id) + print('getting here 4') + response = { + 'status': 'success', + 'message': 'Succesfully registered.', + 'auth_token': auth_token.decode() + } + return jsonify(response), 201 + except Exception as e: + print(e.__dict__) + response = { + 'status': 'fail', + 'message': 'There was an error. Please try again.' + } + return jsonify(response), 401 + else: + response = { + 'status': 'fail', + 'message': 'User already exists. Please login.' + } + return jsonify(response), 202 + +@auth.route('/login', methods=['POST']) +def auth_login(): + response = {"message": "login post"} + return jsonify(response) \ No newline at end of file diff --git a/configuration/__init__.py b/configuration/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/config.py b/configuration/config.py similarity index 97% rename from config.py rename to configuration/config.py index 0b3f00f..55c5173 100644 --- a/config.py +++ b/configuration/config.py @@ -14,6 +14,7 @@ class DevelopmentConfig(BaseConfig): DEBUG = True BCRYPT_LOG_ROUNDS = 4 SQLALCHEMY_DATABASE_URI = DATABASE + PORT = 8000 class TestingConfig(BaseConfig): diff --git a/configuration/models_mount.py b/configuration/models_mount.py new file mode 100644 index 0000000..fa36978 --- /dev/null +++ b/configuration/models_mount.py @@ -0,0 +1,7 @@ +if __name__ == '__main__': + from ..models.User import User + from ..models.GameRoom import GameRoom + from ..models.TimeSettings import TimeSettings + from ..models.Game import Game + from ..models.Move import Move + from ..models.Message import Message \ No newline at end of file diff --git a/database.py b/database.py new file mode 100644 index 0000000..093d45e --- /dev/null +++ b/database.py @@ -0,0 +1,10 @@ + +# ! SQLAlchemy > Marshmallow - these must be imported in this order +from flask_sqlalchemy import SQLAlchemy +from flask_marshmallow import Marshmallow + +# init database +db = SQLAlchemy() + +# init marshmallow +ma = Marshmallow() diff --git a/manage.py b/manage.py index 70172ba..35d0d9d 100644 --- a/manage.py +++ b/manage.py @@ -4,11 +4,20 @@ import unittest from flask_script import Manager from flask_migrate import Migrate, MigrateCommand -from app import app, db +from database import db +from app import create_app +app = create_app() migrate = Migrate(app, db) manager = Manager(app) +from models.Game import Game +from models.GameRoom import GameRoom +from models.Message import Message +from models.Move import Move +from models.TimeSettings import TimeSettings +from models.User import User + # migrations manager.add_command('db', MigrateCommand) diff --git a/migrations/versions/50aab465cf44_.py b/migrations/versions/50aab465cf44_.py new file mode 100644 index 0000000..6aade5b --- /dev/null +++ b/migrations/versions/50aab465cf44_.py @@ -0,0 +1,28 @@ +"""empty message + +Revision ID: 50aab465cf44 +Revises: bfa4b406e22f +Create Date: 2019-10-05 22:37:07.643522 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '50aab465cf44' +down_revision = 'bfa4b406e22f' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('users', sa.Column('password', sa.String(length=255), nullable=False)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('users', 'password') + # ### end Alembic commands ### diff --git a/migrations/versions/9519d28f13f0_.py b/migrations/versions/9519d28f13f0_.py index 1f327ae..8633673 100644 --- a/migrations/versions/9519d28f13f0_.py +++ b/migrations/versions/9519d28f13f0_.py @@ -39,7 +39,7 @@ def upgrade(): op.create_table('users', sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), sa.Column('email', sa.String(length=255), nullable=False), - sa.Column('password', sa.DateTime(), nullable=False), + sa.Column('password', sa.Integer(length=255), nullable=False), sa.Column('registered_on', sa.DateTime(), nullable=False), sa.Column('admin', sa.Boolean(), nullable=False), sa.Column('rank', sa.Enum('D7', 'D6', 'D5', 'D4', 'D3', 'D2', 'D1', 'K1', 'K2', 'K3', 'K4', 'K5', 'K6', 'K7', 'K8', 'K9', 'K10', 'K11', 'K12', 'K13', 'K14', 'K15', 'K16', 'K17', 'K18', 'K19', 'K20', 'K21', 'K22', 'K23', 'K24', 'K25', 'K26', 'K27', 'K28', 'K29', 'K30', name='ranks'), nullable=True), diff --git a/migrations/versions/bdc3cd5d7499_.py b/migrations/versions/bdc3cd5d7499_.py new file mode 100644 index 0000000..d094cdc --- /dev/null +++ b/migrations/versions/bdc3cd5d7499_.py @@ -0,0 +1,28 @@ +"""empty message + +Revision ID: bdc3cd5d7499 +Revises: 16c70b11242e +Create Date: 2019-10-05 22:34:04.358841 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'bdc3cd5d7499' +down_revision = '16c70b11242e' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('moves', sa.Column('is_pass', sa.Boolean(), nullable=False)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('moves', 'is_pass') + # ### end Alembic commands ### diff --git a/migrations/versions/bfa4b406e22f_.py b/migrations/versions/bfa4b406e22f_.py new file mode 100644 index 0000000..525da5f --- /dev/null +++ b/migrations/versions/bfa4b406e22f_.py @@ -0,0 +1,28 @@ +"""empty message + +Revision ID: bfa4b406e22f +Revises: bdc3cd5d7499 +Create Date: 2019-10-05 22:36:45.420054 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = 'bfa4b406e22f' +down_revision = 'bdc3cd5d7499' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('users', 'password') + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('users', sa.Column('password', postgresql.TIMESTAMP(), autoincrement=False, nullable=False)) + # ### end Alembic commands ### diff --git a/models.py b/models.py deleted file mode 100644 index 5fa00f1..0000000 --- a/models.py +++ /dev/null @@ -1,6 +0,0 @@ -from models.User import User -from models.GameRoom import GameRoom -from models.TimeSettings import TimeSettings -from models.Game import Game -from models.Move import Move -from models.Message import Message \ No newline at end of file diff --git a/models/User.py b/models/User.py index 83a9302..203c25a 100644 --- a/models/User.py +++ b/models/User.py @@ -1,6 +1,9 @@ -from app import db, ma, bcrypt +from database import db, ma +from app import bcrypt +from configuration import config import datetime import enum +import jwt class Ranks(enum.Enum): # with minimal Elo rating D7 = "Seven Dan" # Elo 2700+ @@ -47,7 +50,7 @@ class User(db.Model): id = db.Column(db.Integer, primary_key=True, autoincrement=True) email = db.Column(db.String(255), unique=True, nullable=False) - password = db.Column(db.DateTime, nullable=False) + password = db.Column(db.String(255), nullable=False) registered_on = db.Column(db.DateTime, nullable=False) admin = db.Column(db.Boolean, nullable=False, default=False) rank = db.Column(db.Enum(Ranks)) @@ -55,9 +58,60 @@ class User(db.Model): rank_certainty = db.Column(db.Boolean, nullable=False, default=False) def __init__(self, email, password, admin=False,): + print('user init') self.email = email + print('user email init') self.password = bcrypt.generate_password_hash( - password, app.config.get('BCRYPT_LOG_ROUNDS') + password, 13 ).decode() + print('user password init') self.registered_on = datetime.datetime.now() self.admin = admin + + def encode_auth_token(self, user_id): + """ + Generates the Auth Token + :return: string + """ + try: + payload = { + 'exp': datetime.datetime.utcnow() + datetime.timedelta(days=0, seconds=5), + 'iat': datetime.datetime.utcnow(), + 'sub': user_id + } + return jwt.encode( + payload, + app.config.get('SECRET_KEY'), + algorithm='HS256' + ) + except Exception as e: + return e + + @staticmethod + def decode_auth_token(auth_token): + """ + Decodes the auth token + :param auth_token: + :return: integer|string + """ + try: + payload = jwt.decode(auth_token, app.config.get('SECRET_KEY')) + return payload['sub'] + except jwt.ExpiredSignatureError: + return 'Signature expired. Please log in again.' + except jwt.InvalidTokenError: + return 'Invalid token. Please log in again.' + +class UserSchema(ma.ModelSchema): + class Meta: + fields = ( + 'id', + 'name', + 'registered_on', + 'rank', + 'rank_certainty', + 'elo' + ) + +user_schema = UserSchema() +users_schema = UserSchema(many=True) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index f300d47..fab2ace 100644 --- a/requirements.txt +++ b/requirements.txt @@ -23,6 +23,7 @@ mccabe==0.6.1 numpy==1.17.2 psycopg2==2.8.3 pycparser==2.19 +PyJWT==1.7.1 pylint==2.4.2 python-dateutil==2.8.0 python-editor==1.0.4 diff --git a/server.py b/server.py new file mode 100644 index 0000000..d38827c --- /dev/null +++ b/server.py @@ -0,0 +1,17 @@ +from app import create_app, db + +# Blueprints +from api.api import api +from auth.auth import auth + +import configuration.models_mount +from flask_migrate import Migrate + + + +if __name__ == '__main__': + app = create_app() + app.register_blueprint(api) + app.register_blueprint(auth) + migrate = Migrate(app, db) + app.run(port=8000, debug=True) \ No newline at end of file