mirror of
https://github.com/supleed2/ELEC60013-ES-CW1.git
synced 2024-12-22 21:45:48 +00:00
Merge remote-tracking branch 'server/main'
This commit is contained in:
commit
e507bb60b5
19
.gcloudignore
Normal file
19
.gcloudignore
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# This file specifies files that are *not* uploaded to Google Cloud
|
||||||
|
# using gcloud. It follows the same syntax as .gitignore, with the addition of
|
||||||
|
# "#!include" directives (which insert the entries of the given .gitignore-style
|
||||||
|
# file at that point).
|
||||||
|
#
|
||||||
|
# For more information, run:
|
||||||
|
# $ gcloud topic gcloudignore
|
||||||
|
#
|
||||||
|
.gcloudignore
|
||||||
|
# If you would like to upload your .git directory, .gitignore file or files
|
||||||
|
# from your .gitignore file, remove the corresponding line
|
||||||
|
# below:
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
# Python pycache:
|
||||||
|
__pycache__/
|
||||||
|
# Ignored by the build system
|
||||||
|
/setup.cfg
|
132
.gitignore
vendored
Normal file
132
.gitignore
vendored
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
# Firebase key
|
||||||
|
firebase-key.json
|
||||||
|
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
pip-wheel-metadata/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
.python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
8
.idea/.gitignore
vendored
Normal file
8
.idea/.gitignore
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
21
.idea/ELEC60013-ES-CW1-Server.iml
Normal file
21
.idea/ELEC60013-ES-CW1-Server.iml
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="Flask">
|
||||||
|
<option name="enabled" value="true" />
|
||||||
|
</component>
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
<component name="TemplatesService">
|
||||||
|
<option name="TEMPLATE_CONFIGURATION" value="Jinja2" />
|
||||||
|
<option name="TEMPLATE_FOLDERS">
|
||||||
|
<list>
|
||||||
|
<option value="$MODULE_DIR$/templates" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</module>
|
6
.idea/inspectionProfiles/profiles_settings.xml
Normal file
6
.idea/inspectionProfiles/profiles_settings.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
4
.idea/misc.xml
Normal file
4
.idea/misc.xml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.8 (ELEC60013-ES-CW1-Server)" project-jdk-type="Python SDK" />
|
||||||
|
</project>
|
8
.idea/modules.xml
Normal file
8
.idea/modules.xml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/ELEC60013-ES-CW1-Server.iml" filepath="$PROJECT_DIR$/.idea/ELEC60013-ES-CW1-Server.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
93
api/authentication.py
Normal file
93
api/authentication.py
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
import json
|
||||||
|
import lib.utils
|
||||||
|
from flask import Response, Blueprint, request
|
||||||
|
from firebase_admin import firestore, auth
|
||||||
|
from firebase_admin._auth_utils import EmailAlreadyExistsError
|
||||||
|
|
||||||
|
authentication = Blueprint('authentication', __name__)
|
||||||
|
|
||||||
|
@authentication.route('/authentication/register', methods=['POST'])
|
||||||
|
def register():
|
||||||
|
body = request.json
|
||||||
|
if body is None:
|
||||||
|
resp = {'error': 'Invalid request - please provide a body'}
|
||||||
|
return Response(json.dumps(resp), status=400, mimetype='application/json')
|
||||||
|
|
||||||
|
email = body['email']
|
||||||
|
password = body['password']
|
||||||
|
name = body['name']
|
||||||
|
deviceId = body['deviceid']
|
||||||
|
|
||||||
|
# Some fields are not present
|
||||||
|
if email is None or password is None or name is None or deviceId is None:
|
||||||
|
resp = {'error': 'Entries missing'}
|
||||||
|
return Response(json.dumps(resp), status=400, mimetype='application/json')
|
||||||
|
|
||||||
|
# Register user with Firebase authentication
|
||||||
|
try:
|
||||||
|
user = auth.create_user(
|
||||||
|
email=email,
|
||||||
|
email_verified=False,
|
||||||
|
password=password,
|
||||||
|
display_name=name,
|
||||||
|
disabled=False)
|
||||||
|
except EmailAlreadyExistsError:
|
||||||
|
resp = {'error': 'User with given email address already exists'}
|
||||||
|
return Response(json.dumps(resp), status=409, mimetype='application/json')
|
||||||
|
# Prompt the user to get verified
|
||||||
|
code = lib.utils.saveVerificationCode(user.uid)
|
||||||
|
lib.utils.sendVerificationMail(name, email, code)
|
||||||
|
|
||||||
|
# Link the user to the device
|
||||||
|
data = {
|
||||||
|
u'devices': [deviceId]
|
||||||
|
}
|
||||||
|
firestore.client().collection(u'devices').document(user.uid).set(data)
|
||||||
|
|
||||||
|
# User successfully created and linked to device, return 201
|
||||||
|
resp = {"uid": user.uid}
|
||||||
|
return Response(json.dumps(resp), status=201, mimetype='application/json')
|
||||||
|
|
||||||
|
@authentication.route('/authentication/verify', methods=['POST'])
|
||||||
|
def verify():
|
||||||
|
body = request.json
|
||||||
|
if body is None:
|
||||||
|
resp = {'error': 'Invalid request - please provide a body'}
|
||||||
|
return Response(json.dumps(resp), status=400, mimetype='application/json')
|
||||||
|
|
||||||
|
uid = body['uid']
|
||||||
|
code = body['code']
|
||||||
|
|
||||||
|
doc = firestore.client().collection(u'verification').document(uid).get()
|
||||||
|
if doc.exists:
|
||||||
|
if doc.to_dict()['code'] == code:
|
||||||
|
auth.update_user(uid, email_verified=True)
|
||||||
|
firestore.client().collection(u'verification').document(uid).delete()
|
||||||
|
resp = {'success': 'User verified'}
|
||||||
|
return Response(json.dumps(resp), status=200, mimetype='application/json')
|
||||||
|
else:
|
||||||
|
resp = {'error': 'Invalid code'}
|
||||||
|
return Response(json.dumps(resp), status=400, mimetype='application/json')
|
||||||
|
else:
|
||||||
|
user = auth.get_user(uid)
|
||||||
|
code = lib.utils.saveVerificationCode(user.uid)
|
||||||
|
lib.utils.sendVerificationMail(user.display_name, user.email, code)
|
||||||
|
resp = {'error': 'Server could not find code, creating new one and sending email'}
|
||||||
|
return Response(json.dumps(resp), status=500, mimetype='application/json')
|
||||||
|
|
||||||
|
@authentication.route('/authentication/get-user-devices', methods=['GET'])
|
||||||
|
def uploadReadings():
|
||||||
|
uid = request.headers.get('UID')
|
||||||
|
if uid is None:
|
||||||
|
resp = {'error': 'UID not specified'}
|
||||||
|
return Response(json.dumps(resp), status=400, mimetype='application/json')
|
||||||
|
|
||||||
|
# Save all the measurements
|
||||||
|
doc = firestore.client().collection(u'devices').document(uid).get()
|
||||||
|
if doc.exists:
|
||||||
|
list = doc.to_dict()['devices']
|
||||||
|
data = list
|
||||||
|
else:
|
||||||
|
data = []
|
||||||
|
res = {'devices': data}
|
||||||
|
return Response(json.dumps(res), status=200, mimetype='application/json')
|
156
api/data.py
Normal file
156
api/data.py
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
import time
|
||||||
|
import json
|
||||||
|
from datetime import datetime, time, timedelta
|
||||||
|
from flask import Response, Blueprint, request
|
||||||
|
from firebase_admin import firestore
|
||||||
|
|
||||||
|
data = Blueprint('data', __name__)
|
||||||
|
|
||||||
|
@data.route('/readings/save', methods=['POST'])
|
||||||
|
def uploadReadings():
|
||||||
|
deviceId = request.headers.get('Device-ID')
|
||||||
|
if deviceId is None:
|
||||||
|
resp = {'error': 'Device not specified'}
|
||||||
|
return Response(json.dumps(resp), status=400, mimetype='application/json')
|
||||||
|
|
||||||
|
# Check that measurements are provided
|
||||||
|
body = request.json
|
||||||
|
if body is None:
|
||||||
|
resp = {'error': 'Invalid request - please provide a body'}
|
||||||
|
return Response(json.dumps(resp), status=400, mimetype='application/json')
|
||||||
|
body['timestamp'] = datetime.now().timestamp()
|
||||||
|
|
||||||
|
# Save all the measurements
|
||||||
|
doc = firestore.client().collection(u'readings').document(deviceId).get()
|
||||||
|
if doc.exists:
|
||||||
|
list = doc.to_dict()['data']
|
||||||
|
list.append(body)
|
||||||
|
data = list
|
||||||
|
else:
|
||||||
|
data = [body]
|
||||||
|
upload = {'data': data}
|
||||||
|
firestore.client().collection(u'readings').document(deviceId).set(upload)
|
||||||
|
|
||||||
|
resp = {'success': 'Data saved'}
|
||||||
|
return Response(json.dumps(resp), status=200, mimetype='application/json')
|
||||||
|
|
||||||
|
@data.route('/readings/getall', methods=['GET'])
|
||||||
|
def getAllReadings():
|
||||||
|
deviceId = request.headers.get('Device-ID')
|
||||||
|
if deviceId is None:
|
||||||
|
resp = {'error': 'Device not specified'}
|
||||||
|
return Response(json.dumps(resp), status=400, mimetype='application/json')
|
||||||
|
|
||||||
|
doc = firestore.client().collection(u'readings').document(deviceId).get()
|
||||||
|
if doc.exists:
|
||||||
|
data = doc.to_dict()['data']
|
||||||
|
else:
|
||||||
|
data = []
|
||||||
|
|
||||||
|
results = {'data': data}
|
||||||
|
return Response(json.dumps(results), status=200, mimetype='application/json')
|
||||||
|
|
||||||
|
@data.route('/readings/location/last', methods=['GET'])
|
||||||
|
def getLastLocation():
|
||||||
|
deviceId = request.headers.get('Device-ID')
|
||||||
|
if deviceId is None:
|
||||||
|
resp = {'error': 'Device not specified'}
|
||||||
|
return Response(json.dumps(resp), status=400, mimetype='application/json')
|
||||||
|
|
||||||
|
doc = firestore.client().collection(u'readings').document(deviceId).get()
|
||||||
|
if doc.exists:
|
||||||
|
data = doc.to_dict()['data']
|
||||||
|
lastEntry = data[-1]
|
||||||
|
lat = lastEntry['latitude']
|
||||||
|
lon = lastEntry['longitude']
|
||||||
|
else:
|
||||||
|
lat = -1.0
|
||||||
|
lon = -1.0
|
||||||
|
|
||||||
|
results = {'latitude': lat, 'longitude': lon}
|
||||||
|
return Response(json.dumps(results), status=200, mimetype='application/json')
|
||||||
|
|
||||||
|
@data.route('/readings/steps/today', methods=['GET'])
|
||||||
|
def getStepsToday():
|
||||||
|
deviceId = request.headers.get('Device-ID')
|
||||||
|
if deviceId is None:
|
||||||
|
resp = {'error': 'Device not specified'}
|
||||||
|
return Response(json.dumps(resp), status=400, mimetype='application/json')
|
||||||
|
|
||||||
|
doc = firestore.client().collection(u'readings').document(deviceId).get()
|
||||||
|
if doc.exists:
|
||||||
|
data = doc.to_dict()['data']
|
||||||
|
lastEntry = data[-1]
|
||||||
|
steps = lastEntry['cumulative_steps_today']
|
||||||
|
else:
|
||||||
|
steps = 0
|
||||||
|
|
||||||
|
results = {'cumulative_steps_today': steps}
|
||||||
|
return Response(json.dumps(results), status=200, mimetype='application/json')
|
||||||
|
|
||||||
|
@data.route('/readings/steps/last-five-days', methods=['GET'])
|
||||||
|
def getStepsLastFiveDays():
|
||||||
|
deviceId = request.headers.get('Device-ID')
|
||||||
|
if deviceId is None:
|
||||||
|
resp = {'error': 'Device not specified'}
|
||||||
|
return Response(json.dumps(resp), status=400, mimetype='application/json')
|
||||||
|
|
||||||
|
upcomingMidnight = datetime.combine(datetime.today(), time.min) + timedelta(days=1)
|
||||||
|
doc = firestore.client().collection(u'readings').document(deviceId).get()
|
||||||
|
|
||||||
|
if doc.exists:
|
||||||
|
data = doc.to_dict()['data']
|
||||||
|
listOfDailySteps = []
|
||||||
|
|
||||||
|
for i in range(0, 5):
|
||||||
|
found = False
|
||||||
|
previousMidnight = upcomingMidnight - timedelta(days=1)
|
||||||
|
print(previousMidnight.timestamp())
|
||||||
|
steps = 0
|
||||||
|
for reading in reversed(data):
|
||||||
|
if reading['timestamp'] <= upcomingMidnight.timestamp() and reading['timestamp'] >= previousMidnight.timestamp() and not found:
|
||||||
|
steps = reading['cumulative_steps_today']
|
||||||
|
found = True
|
||||||
|
listOfDailySteps.append(steps)
|
||||||
|
upcomingMidnight = previousMidnight
|
||||||
|
else:
|
||||||
|
listOfDailySteps = [0] * 5
|
||||||
|
|
||||||
|
results = {'daily_steps': listOfDailySteps}
|
||||||
|
return Response(json.dumps(results), status=200, mimetype='application/json')
|
||||||
|
|
||||||
|
@data.route('/readings/metrics-summary', methods=['GET'])
|
||||||
|
def getMetricsSummary():
|
||||||
|
deviceId = request.headers.get('Device-ID')
|
||||||
|
if deviceId is None:
|
||||||
|
resp = {'error': 'Device not specified'}
|
||||||
|
return Response(json.dumps(resp), status=400, mimetype='application/json')
|
||||||
|
|
||||||
|
upcomingMidnight = datetime.combine(datetime.today(), time.min) + timedelta(days=1)
|
||||||
|
lastMidnight = datetime.combine(datetime.today(), time.min)
|
||||||
|
doc = firestore.client().collection(u'readings').document(deviceId).get()
|
||||||
|
|
||||||
|
if doc.exists:
|
||||||
|
allData = doc.to_dict()['data']
|
||||||
|
currentDayData = [x for x in allData if x['timestamp'] <= upcomingMidnight.timestamp() and x['timestamp'] >= lastMidnight.timestamp()]
|
||||||
|
if len(currentDayData) >= 1:
|
||||||
|
maxAirTemp = max(currentDayData, key=lambda x: x['air_temp'])['air_temp']
|
||||||
|
maxSkinTemp = max(currentDayData, key=lambda x: x['skin_temp'])['skin_temp']
|
||||||
|
maxHumidity = max(currentDayData, key=lambda x: x['humidity'])['humidity']
|
||||||
|
minAirTemp = min(currentDayData, key=lambda x: x['air_temp'])['air_temp']
|
||||||
|
minSkinTemp = min(currentDayData, key=lambda x: x['skin_temp'])['skin_temp']
|
||||||
|
minHumidity = min(currentDayData, key=lambda x: x['humidity'])['humidity']
|
||||||
|
results = {
|
||||||
|
'last_air_temp': currentDayData[-1]['air_temp'], 'min_air_temp': minAirTemp, 'max_air_temp': maxAirTemp,
|
||||||
|
'last_skin_temp': currentDayData[-1]['skin_temp'], 'min_skin_temp': minSkinTemp, 'max_skin_temp': maxSkinTemp,
|
||||||
|
'last_humidity': currentDayData[-1]['humidity'], 'min_humidity': minHumidity, 'max_humidity': maxHumidity
|
||||||
|
}
|
||||||
|
return Response(json.dumps(results), status=200, mimetype='application/json')
|
||||||
|
else:
|
||||||
|
return Response(json.dumps({'error': 'Could not get data from database'}), status=500, mimetype='application/json')
|
||||||
|
else:
|
||||||
|
return Response(json.dumps({'error': 'Could not get data from database'}), status=500, mimetype='application/json')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
9
config/variables.py
Normal file
9
config/variables.py
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# App config file with various variables and settings
|
||||||
|
|
||||||
|
# Email config
|
||||||
|
MAIL_SERVER = 'smtp.gmail.com'
|
||||||
|
MAIL_PORT = 465
|
||||||
|
MAIL_USERNAME = 'legbarkr@gmail.com'
|
||||||
|
MAIL_PASSWORD = '!Password123'
|
||||||
|
MAIL_USE_TLS = False
|
||||||
|
MAIL_USE_SSL = True
|
26
lib/utils.py
Normal file
26
lib/utils.py
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
from random import randint
|
||||||
|
from flask import current_app
|
||||||
|
from flask_mail import Mail, Message
|
||||||
|
from firebase_admin import firestore
|
||||||
|
|
||||||
|
def sendMail(subject, sender, recipients, body):
|
||||||
|
mail = Mail(current_app)
|
||||||
|
msg = Message(subject, sender=sender, recipients=recipients)
|
||||||
|
msg.body = body
|
||||||
|
mail.send(msg)
|
||||||
|
|
||||||
|
def saveVerificationCode(uid):
|
||||||
|
code = randint(100000, 999999)
|
||||||
|
data = {
|
||||||
|
u'code': code
|
||||||
|
}
|
||||||
|
firestore.client().collection(u'verification').document(uid).set(data)
|
||||||
|
return code
|
||||||
|
|
||||||
|
def sendVerificationMail(name, email, code):
|
||||||
|
subject = 'Please verify your email for BarkFinder'
|
||||||
|
sender = 'legbarkr@gmail.com'
|
||||||
|
recipients = [email]
|
||||||
|
body = '''Hey {}! Thank you for signing up for BarkFinder.
|
||||||
|
In order to use our sevices, could you please verify your email address by logging in and entering this code {}'''.format(name, code)
|
||||||
|
sendMail(subject, sender, recipients, body)
|
28
main.py
Normal file
28
main.py
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
from config.variables import MAIL_SERVER, MAIL_PORT, MAIL_USERNAME, MAIL_PASSWORD, MAIL_USE_SSL, MAIL_USE_TLS
|
||||||
|
from flask import Flask
|
||||||
|
from firebase_admin import credentials, initialize_app
|
||||||
|
from api.authentication import authentication
|
||||||
|
from api.data import data
|
||||||
|
|
||||||
|
# Initialize Flask app and register all the endpoints
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.register_blueprint(authentication)
|
||||||
|
app.register_blueprint(data)
|
||||||
|
|
||||||
|
# Initialize Mail instance
|
||||||
|
app.config['MAIL_SERVER'] = MAIL_SERVER
|
||||||
|
app.config['MAIL_PORT'] = MAIL_PORT
|
||||||
|
app.config['MAIL_USERNAME'] = MAIL_USERNAME
|
||||||
|
app.config['MAIL_PASSWORD'] = MAIL_PASSWORD
|
||||||
|
app.config['MAIL_USE_TLS'] = MAIL_USE_TLS
|
||||||
|
app.config['MAIL_USE_SSL'] = MAIL_USE_SSL
|
||||||
|
|
||||||
|
# Initialize Firebase
|
||||||
|
firebase = initialize_app(credentials.Certificate('firebase-key.json'))
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def hello():
|
||||||
|
return 'Hello World'
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run()
|
3
requirements.txt
Normal file
3
requirements.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
flask
|
||||||
|
flask_mail
|
||||||
|
firebase_admin
|
Loading…
Reference in a new issue