Skip to content

Commit

Permalink
Merge pull request #15 from dsc-x/feature-mail-service
Browse files Browse the repository at this point in the history
Upd: Email service for forgot password added
  • Loading branch information
arnabsen1729 authored Dec 28, 2020
2 parents 4640e85 + e07f424 commit fcded06
Show file tree
Hide file tree
Showing 11 changed files with 750 additions and 83 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# IDE specific config files
.vscode/

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand Down
41 changes: 21 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# omg-frames-api
# IWasAt

This is the backend of the [OMG Frames](https://github.com/dsc-x/omg-frames) having the register/login routes, and other related ones.

Link to the front-end repo: [omg-frames](https://github.com/dsc-x/omg-frames)

Backend is running at: [http://104.236.25.178/api/v1](http://104.236.25.178/api/v1)
Backend is running at: [https://api.iwasat.events/api/v1](https://api.iwasat.events/api/v1/)

All the API endpoints are prefixed by `/api/v1` e.g `http://104.236.25.178/api/v1/login`
All the API endpoints are prefixed by `/api/v1` e.g `https://api.iwasat.events/api/v1/login`

## Technologies used

Expand All @@ -18,7 +18,7 @@ All the API endpoints are prefixed by `/api/v1` e.g `http://104.236.25.178/api/v

## API Endpoints

For API documentation go to [http://104.236.25.178/apidocs/](http://104.236.25.178/apidocs/).
For API documentation go to [https://api.iwasat.events/apidocs/](https://api.iwasat.events/apidocs/).

## Development

Expand Down Expand Up @@ -51,21 +51,22 @@ This will start the local server in port 5000.
## Project structure

```
omg-frames-api
.
├── app
│   ├── db.py
│   ├── __init__.py
│   └── routes.py
├── config.py
├── docs
│   ├── getframes.yml
│   ├── login.yml
│   ├── postframes.yml
│   └── register.yml
├── README.md
├── requirements.txt
├── sample.env
└── server.py
omg-frames-api
├── app
│   ├── db.py
│   ├── __init__.py
│   └── routes.py
├── config.py
├── docs
│   ├── deleteframes.yml
│   ├── getframes.yml
│   ├── login.yml
│   ├── postframes.yml
│   ├── register.yml
│   └── updateframes.yml
├── README.md
├── requirements.txt
├── sample.env
└── server.py
```

8 changes: 4 additions & 4 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from app.db import Db
from flasgger import Swagger
from flask_cors import CORS
from flask_mail import Mail

template = {
"swagger": "2.0",
Expand All @@ -19,18 +20,17 @@
"http",
"https"
],
'securityDefinitions': {
'basicAuth': { 'type': 'apiKey', 'name': 'Authorization', 'in': 'header'}
'securityDefinitions': {
'basicAuth': {'type': 'apiKey', 'name': 'Authorization', 'in': 'header'}
}
}


app = Flask(__name__)
CORS(app)
app.config.from_object(Config)
mail = Mail(app)

swagger = Swagger(app, template=template)

from app import routes


84 changes: 37 additions & 47 deletions app/db.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,7 @@
from config import Config, FirebaseConfig
from config import FirebaseConfig
from passlib.hash import pbkdf2_sha256
from utils import Utils
import pyrebase
import jwt
import datetime


def encode_auth_token(user_id):
try:
payload = {
'exp': datetime.datetime.utcnow() + datetime.timedelta(days=1, minutes=0),
'iat': datetime.datetime.utcnow(),
'id': user_id
}
return jwt.encode(
payload,
Config.SECRET_KEY,
algorithm='HS256'
)
except Exception as e:
return e


def decode_auth_token(auth_token):
try:
payload = jwt.decode(auth_token, Config.SECRET_KEY)
return payload['id']
except jwt.ExpiredSignatureError:
print('ERROR: Signature expired. Please log in again.')
return None
except jwt.InvalidTokenError:
print('ERROR: Invalid token. Please log in again.')
return None


class Db:
Expand Down Expand Up @@ -59,7 +30,7 @@ def add_participants(db, userDetails):
print(' * Participant added to db')
return True
except Exception as e:
print('ERROR: <add_participants>' , e)
print('ERROR: <add_participants>', e)
return False

@staticmethod
Expand All @@ -70,7 +41,7 @@ def check_valid_details(db, userDetails):
assert len(userDetails["password"]) > 6

participants = db.child('participants').get().val()
if participants == None:
if participants is None:
# no participant data
return True
for user_id in participants:
Expand Down Expand Up @@ -99,7 +70,7 @@ def authorise_participants(db, userDetails):
@staticmethod
def get_token(db, user_id):
try:
token = encode_auth_token(user_id)
token = Utils.encode_auth_token(user_id)
return token.decode('UTF-8')
except Exception as e:
print('ERROR:', e)
Expand All @@ -108,10 +79,10 @@ def get_token(db, user_id):
@staticmethod
def save_frame(db, token, frame):
try:
user_id = decode_auth_token(token)
if user_id!= None:
user_id = Utils.decode_auth_token(token)
if user_id is not None:
frame_id = db.child('participants').child(user_id).child('frames').push(frame)
frame_obj={
frame_obj = {
"frame_data": frame,
"frame_id": frame_id["name"]
}
Expand All @@ -127,12 +98,12 @@ def save_frame(db, token, frame):
@staticmethod
def get_frames(db, token):
try:
user_id = decode_auth_token(token)
if user_id != None:
user_id = Utils.decode_auth_token(token)
if user_id is not None:
frames = db.child('participants').child(user_id).child('frames').get().val()
frame_arr = []
if frames!= None:
frame_arr = [{"frame_id":fid, "frame_data":frames[fid]} for fid in frames]
if frames is not None:
frame_arr = [{"frame_id": fid, "frame_data": frames[fid]} for fid in frames]
return frame_arr
else:
print('ERROR: Token Value is None')
Expand All @@ -144,8 +115,8 @@ def get_frames(db, token):
@staticmethod
def delete_frames(db, token, frame_id):
try:
user_id = decode_auth_token(token)
if user_id != None:
user_id = Utils.decode_auth_token(token)
if user_id is not None:
db.child('participants').child(user_id).child('frames').child(frame_id).remove()
return True
else:
Expand All @@ -157,12 +128,31 @@ def delete_frames(db, token, frame_id):

@staticmethod
def update_frames(db, token, frame_id, frame_data):
user_id = decode_auth_token(token)
if user_id != None:
user_id = Utils.decode_auth_token(token)
if user_id is not None:
db.child('participants').child(user_id).child('frames').child(frame_id).remove()
upd_frame_id = db.child('participants').child(user_id).child('frames').push(frame_data)
frame = {"frame_id":upd_frame_id['name'], "frame_data":frame_data}
frame = {"frame_id": upd_frame_id['name'], "frame_data": frame_data}
return frame
else:
print('ERROR: Token Value is None')
return None
return None

@staticmethod
def check_email_address(db, email):
participants = db.child('participants').get().val()
if participants is None:
# No records found
return None
else:
for user_id in participants:
user = participants[user_id]
if user and (user["email"] == email):
return user_id
return None

@staticmethod
def change_password(db, user_id, password):
user_details = db.child('participants').child(user_id).get().val()
user_details['password'] = pbkdf2_sha256.hash(password)
db.child('participants').child(user_id).update(user_details)
73 changes: 63 additions & 10 deletions app/routes.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,71 @@
from flask import request, jsonify, make_response, abort
from app import app, Db
from flask import request, jsonify, make_response
from app import app, Db, mail
from utils import Utils
from flasgger.utils import swag_from

BASE_URL = '/api/v1'

database = Db.init_db()


@app.route(BASE_URL+'/')
@app.route(BASE_URL + '/')
def index():
return make_response(jsonify({"message": "DSC Frames API"})), 201


@app.route(BASE_URL+'/register', methods=['POST'])
@app.route(BASE_URL + '/send-reset-mail', methods=['POST'])
@swag_from('../docs/sendresetmail.yml')
def send_reset_mail():
data = request.json
if 'email' not in data.keys():
responseObject = {
'message': 'email not specified in the body'
}
return make_response(jsonify(responseObject)), 400
else:
emailAddr = data['email']
userId = Db.check_email_address(database, emailAddr)
if userId is not None:
resetLink = f'https://iwasat.events/reset.html?token={Utils.get_reset_token(userId)}'
Utils.send_reset_password_mail(mail, resetLink, emailAddr)
responseObject = {
'message': 'reset link was sent to the respective email address'
}
return make_response(jsonify(responseObject)), 200
else:
responseObject = {
'message': 'email doesnot match'
}
return make_response(jsonify(responseObject)), 401


@app.route(BASE_URL + '/update-password', methods=['POST'])
@swag_from('../docs/updatepassword.yml')
def update_password():
data = request.json
if 'token' not in data.keys() and 'password' not in data.keys():
responseObject = {
'message': 'token or password is absent in the request body'
}
return make_response(jsonify(responseObject)), 400
else:
token = data['token']
password = data['password']
userId = Utils.verify_reset_token(token)
if userId is None:
responseObject = {
'message': 'invalid reset token'
}
return make_response(jsonify(responseObject)), 401
else:
Db.change_password(database, userId, password)
responseObject = {
'message': 'password updated successfully'
}
return make_response(jsonify(responseObject)), 200


@app.route(BASE_URL + '/register', methods=['POST'])
@swag_from('../docs/register.yml')
def register():
user = request.json
Expand All @@ -24,19 +77,19 @@ def register():
return make_response(jsonify({"message": "Internal Server error"})), 500


@app.route(BASE_URL+'/login', methods=['POST'])
@app.route(BASE_URL + '/login', methods=['POST'])
@swag_from('../docs/login.yml')
def login():
user = request.json
user_data = Db.authorise_participants(database, user)
if (user_data != None and user_data[0] != None):
if (user_data is not None and user_data[0] is not None):
user_token = Db.get_token(database, user_data[0])
return make_response(jsonify({"token": user_token, "data": user_data[1]})), 202
else:
return make_response(jsonify({"message": "Login failed"})), 401


@app.route(BASE_URL+'/frames', methods=['POST', 'GET', 'DELETE', 'PUT'])
@app.route(BASE_URL + '/frames', methods=['POST', 'GET', 'DELETE', 'PUT'])
@swag_from('../docs/getframes.yml', methods=['GET'])
@swag_from('../docs/postframes.yml', methods=['POST'])
@swag_from('../docs/deleteframes.yml', methods=['DELETE'])
Expand All @@ -58,7 +111,7 @@ def frames():
frame = request.json['frame']
if frame:
responseObject = Db.save_frame(database, auth_token, frame)
if responseObject != None:
if responseObject is not None:
return make_response(jsonify(responseObject)), 201
else:
responseObject = {
Expand All @@ -72,7 +125,7 @@ def frames():
return make_response(jsonify(responseObject)), 400
elif request.method == 'GET':
frames_arr = Db.get_frames(database, auth_token)
if frames_arr != None:
if frames_arr is not None:
responseObject = {
'frames': frames_arr
}
Expand All @@ -98,7 +151,7 @@ def frames():
frame_id = request.json['frame_id']
frame_data = request.json['frame_data']
upd_frame = Db.update_frames(database, auth_token, frame_id, frame_data)
if upd_frame != None:
if upd_frame is not None:
responseObject = {
'message': 'Frame was updated successfully',
'data': upd_frame
Expand Down
Loading

0 comments on commit fcded06

Please sign in to comment.