Many api changes

- Added basic jwt auth
- Added keyword endpoints
- added share/symbol endpoints
- updated postman
- refactoring
This commit is contained in:
Administrator 2022-03-14 17:10:00 +01:00
parent 412ec06144
commit 6923095939
8 changed files with 457 additions and 115 deletions

View File

@ -4,6 +4,9 @@
"name": "AktienBot",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
{
"name": "User",
"item": [
{
"name": "/api/register",
@ -119,7 +122,180 @@
},
"response": []
}
]
},
{
"name": "Share",
"item": [
{
"name": "/api/shares",
"protocolProfileBehavior": {
"disableBodyPruning": true
},
"request": {
"method": "GET",
"header": [],
"body": {
"mode": "raw",
"raw": ""
},
"url": {
"raw": "{{BASE_URL}}/api/shares",
"host": [
"{{BASE_URL}}"
],
"path": [
"api",
"shares"
]
}
},
"response": []
},
{
"name": "/api/share",
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"symbol\": \"DTEGY\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{BASE_URL}}/api/share",
"host": [
"{{BASE_URL}}"
],
"path": [
"api",
"share"
]
}
},
"response": []
},
{
"name": "/api/share",
"request": {
"method": "DELETE",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"symbol\": \"DTEGY\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{BASE_URL}}/api/share",
"host": [
"{{BASE_URL}}"
],
"path": [
"api",
"share"
]
}
},
"response": []
}
]
},
{
"name": "Keyword",
"item": [
{
"name": "/api/keywords",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{BASE_URL}}/api/keywords",
"host": [
"{{BASE_URL}}"
],
"path": [
"api",
"keywords"
]
}
},
"response": []
},
{
"name": "/api/keyword",
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"keyword\": \"Elon\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{BASE_URL}}/api/keyword",
"host": [
"{{BASE_URL}}"
],
"path": [
"api",
"keyword"
]
}
},
"response": []
},
{
"name": "/api/keyword",
"request": {
"method": "DELETE",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"keyword\": \"Elon\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{BASE_URL}}/api/keyword",
"host": [
"{{BASE_URL}}"
],
"path": [
"api",
"keyword"
]
}
},
"response": []
}
]
}
],
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6IlVzZXJuYW1lIiwiZXhwIjoxNjQ3Mjc1OTI2fQ.MhXTrfeLQmZ8dPzMOcNSTg1PUw4AU-aZ7h_zRmk8ibc",
"type": "string"
}
]
},
"event": [
{
"listen": "prerequest",

View File

@ -1,9 +1,11 @@
from flask import Flask
from dotenv import load_dotenv
from models import *
from api import api
from interface import interface
from webservice.models import *
from webservice.interface import interface
from webservice.blueprints.keyword import keyword_blueprint
from webservice.blueprints.shares import shares_blueprint
from webservice.blueprints.user import users_blueprint
def create_app():
@ -20,7 +22,12 @@ def create_app():
# Create all tables
db.create_all()
application.register_blueprint(api)
# api blueprints
application.register_blueprint(keyword_blueprint)
application.register_blueprint(shares_blueprint)
application.register_blueprint(users_blueprint)
# interface blueprint
application.register_blueprint(interface)
return application

View File

@ -0,0 +1,62 @@
import os
from flask import Blueprint, jsonify, request
from webservice.db import db
from webservice.helper_functions import get_username_from_token_data, extract_token_data, get_token, get_user_id_from_username
from webservice.models import Keyword
keyword_blueprint = Blueprint('keyword', __name__, url_prefix='/api')
__location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))
@keyword_blueprint.route('/keyword', methods=['POST'])
def add_keyword():
request_data = request.get_json()
key = request_data['keyword']
# get username from jwt token
username = get_username_from_token_data(extract_token_data(get_token()))
check_keyword = db.session.query(Keyword).filter_by(keyword=key, user_id=get_user_id_from_username(username)).first()
if check_keyword is None:
# Keyword doesn't exist yet for this user
new_keyword = Keyword(
user_id=get_user_id_from_username(username),
keyword=key
)
db.session.add(new_keyword)
db.session.commit()
return jsonify({"status": 200, "text": "Successfully added keyword", "data": new_keyword.as_dict()})
else:
return jsonify({"status": 500, "text": "Keyword already exist for this user"})
@keyword_blueprint.route('/keyword', methods=['DELETE'])
def remove_keyword():
request_data = request.get_json()
key = request_data['keyword']
# get username from jwt token
username = get_username_from_token_data(extract_token_data(get_token()))
db.session.query(Keyword).filter_by(keyword=key, user_id=get_user_id_from_username(username)).delete()
db.session.commit()
return jsonify({"status": 200, "text": "Successfully removed keyword"})
@keyword_blueprint.route('/keywords', methods=['GET'])
def get_keywords():
# get username from jwt token
username = get_username_from_token_data(extract_token_data(get_token()))
return_keywords = []
keywords = db.session.query(Keyword).filter_by(user_id=get_user_id_from_username(username)).all()
if keywords is not None:
for row in keywords:
return_keywords.append(row.as_dict())
return jsonify({"status": 200, "text": "Successfully loaded keywords", "data": return_keywords})

View File

@ -0,0 +1,62 @@
import os
from flask import Blueprint, jsonify, request
from webservice.db import db
from webservice.helper_functions import get_username_from_token_data, extract_token_data, get_token, get_user_id_from_username
from webservice.models import Keyword, Share
shares_blueprint = Blueprint('share', __name__, url_prefix='/api')
__location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))
@shares_blueprint.route('/share', methods=['POST'])
def add_symbol():
request_data = request.get_json()
symbol = request_data['symbol']
# get username from jwt token
username = get_username_from_token_data(extract_token_data(get_token()))
check_share = db.session.query(Share).filter_by(symbol=symbol, user_id=get_user_id_from_username(username)).first()
if check_share is None:
# Keyword doesn't exist yet for this user
new_symbol = Share(
user_id=get_user_id_from_username(username),
symbol=symbol
)
db.session.add(new_symbol)
db.session.commit()
return jsonify({"status": 200, "text": "Successfully added symbol", "data": new_symbol.as_dict()})
else:
return jsonify({"status": 500, "text": "Symbol already exist for this user"})
@shares_blueprint.route('/share', methods=['DELETE'])
def remove_symbol():
request_data = request.get_json()
symbol = request_data['symbol']
# get username from jwt token
username = get_username_from_token_data(extract_token_data(get_token()))
db.session.query(Share).filter_by(symbol=symbol, user_id=get_user_id_from_username(username)).delete()
db.session.commit()
return jsonify({"status": 200, "text": "Successfully removed symbol"})
@shares_blueprint.route('/shares', methods=['GET'])
def get_symbol():
# get username from jwt token
username = get_username_from_token_data(extract_token_data(get_token()))
return_symbols = []
symbols = db.session.query(Share).filter_by(user_id=get_user_id_from_username(username)).all()
if symbols is not None:
for row in symbols:
return_symbols.append(row.as_dict())
return jsonify({"status": 200, "text": "Successfully loaded symbols", "data": return_symbols})

View File

@ -1,16 +1,18 @@
import datetime
import os
import jwt
from flask import Blueprint, jsonify, request
from db import db
from helper_functions import check_password, hash_password
from models import User
from webservice.db import db
from webservice.helper_functions import check_password, hash_password, get_token, extract_token_data
from webservice.models import User
api = Blueprint('api', __name__, url_prefix='/api')
users_blueprint = Blueprint('users', __name__, url_prefix='/api')
__location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))
@api.route('/users', methods=['GET'])
@users_blueprint.route('/users', methods=['GET'])
def users():
res = []
for i in User.query.all():
@ -19,7 +21,7 @@ def users():
return jsonify({"status": 200, "data": res})
@api.route('/login', methods=['POST'])
@users_blueprint.route('/login', methods=['POST'])
def login():
request_data = request.get_json()
username = request_data['username']
@ -27,19 +29,20 @@ def login():
user = db.session.query(User).filter_by(username=username).first()
if check_password(user.password, password):
# TODO Return token
return jsonify({"status": 200, "text": "Successfully logged in"})
token = jwt.encode({'username': user.username, 'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=45)}, os.getenv('SECRET_KEY'), "HS256")
return jsonify({"status": 200, "text": "Successfully logged in", "data": token})
else:
return jsonify({"status": 500, "text": "Unable to login"})
@api.route('/logout', methods=['GET'])
@users_blueprint.route('/logout', methods=['GET'])
def logout():
# TODO
return jsonify({"status": 200, "text": "Successfully logged out"})
@api.route('/register', methods=['POST'])
@users_blueprint.route('/register', methods=['POST'])
def register():
request_data = request.get_json()
username = request_data['username']

View File

@ -1,6 +1,13 @@
import hashlib
import os
import uuid
import jwt
from flask import request
from webservice.db import db
from webservice.models import User
def hash_password(password):
salt = uuid.uuid4().hex
@ -10,3 +17,27 @@ def hash_password(password):
def check_password(hashed_password, user_password):
password, salt = hashed_password.split(':')
return password == hashlib.sha256(salt.encode() + user_password.encode()).hexdigest()
def get_token():
token = None
if 'Authorization' in request.headers:
token = request.headers['Authorization'].split(" ")[1]
return token
def extract_token_data(token):
if token is not None:
try:
return jwt.decode(token, os.getenv('SECRET_KEY'), algorithms=["HS256"])
except:
return None
def get_username_from_token_data(token_data):
return token_data['username']
def get_user_id_from_username(username):
return db.session.query(User).filter_by(username=username).first().user_id

View File

@ -8,5 +8,5 @@ __location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file
# Return all tags
@interface.route('/', methods=['GET'])
def get_tags():
render_template("index.html")
def get_html():
return render_template("index.html")

View File

@ -5,3 +5,4 @@ uwsgi==2.0.20
Flask_SQLAlchemy==2.5.1
python-dotenv==0.19.2
pymysql==1.0.2
pyjwt==2.0.0