From 56b842b6da9baa8dafe02f13093d272ed0b3ef2c Mon Sep 17 00:00:00 2001 From: William Tan Date: Tue, 20 Feb 2018 00:55:19 +0800 Subject: [PATCH 01/76] Add names of team members to readme --- README.md | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 9a814e3..eac30da 100644 --- a/README.md +++ b/README.md @@ -96,10 +96,9 @@ Please fill out this section with details relevant to your team. ### Team Members -1. Member 1 Name -2. Member 2 Name -3. Member 3 Name -4. Member 4 Name +1. Chai Ming Xuan +2. Tan Yi Yan +3. Tan Wee Chen William ### Short Answer Questions @@ -131,13 +130,10 @@ Answer: Please replace this sentence with your answer. #### Please declare your individual contributions to the assignment: -1. Member 1 Name +1. Chai Ming Xuan - Integrated feature x into component y - Implemented z -2. Member 2 Name +2. Tan Yi Yan - Wrote the front-end code -3. Member 3 Name +3. Tan Wee Chen William - Designed the database schema -4. Member 4 Name - - Implemented x - From d836518f726374985badff63c2e8fceaca0cff7e Mon Sep 17 00:00:00 2001 From: William Tan Date: Mon, 26 Feb 2018 13:21:33 +0800 Subject: [PATCH 02/76] Update files to start DB container and link app to it --- Dockerfile | 1 + run.sh | 24 +++++++++++++++++++++++- src/service/app.py | 11 ++++++++++- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 77837fc..c65dcd4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,6 +5,7 @@ RUN apt-get install -y apache2 RUN pip install -U pip RUN pip install -U flask RUN pip install -U flask-cors +RUN pip install flask-mysql RUN echo "ServerName localhost " >> /etc/apache2/apache2.conf RUN echo "$user hard nproc 20" >> /etc/security/limits.conf ADD ./src/service /service diff --git a/run.sh b/run.sh index 97e50da..38434a9 100755 --- a/run.sh +++ b/run.sh @@ -5,8 +5,30 @@ if [ "$EUID" -ne 0 ] exit fi +#Apache2 might run on the same port, just kill it +ps auxw | grep apache2 | grep -v grep > /dev/null + +if [ $? == 0 ] +then + /etc/init.d/apache2 stop > /dev/null +fi + +ps auxw | grep mysql | grep -v grep > /dev/null + +if [ $? == 0 ] +then + service stop > /dev/null +fi + TEAMID=`md5sum README.md | cut -d' ' -f 1` docker kill $(docker ps -q) docker rm $(docker ps -a -q) + +docker pull mysql + docker build . -t $TEAMID -docker run -p 80:80 -p 8080:8080 -t $TEAMID + +echo "Starting app and database" + +docker run -p 3306:3306 -v /var/lib/mysql --name=testsql -e MYeSQL_ROOT_PASSWORD=password -d mysql & +docker run -p 80:80 -p 8080:8080 -t $TEAMID \ No newline at end of file diff --git a/src/service/app.py b/src/service/app.py index ade6772..09189a0 100644 --- a/src/service/app.py +++ b/src/service/app.py @@ -4,11 +4,21 @@ from flask_cors import CORS import json import os +from flaskext.mysql import MySQL app = Flask(__name__) # Enable cross origin sharing for all endpoints CORS(app) +mysql = MySQL() +app.config['MYSQL_DATABASE_PASSWORD'] = 'password' +app.config['MYSQL_DATABASE_DB'] = 'testsql' +app.config['MYSQL_DATABASE_HOST'] = '0.0.0.0' +app.config['MYSQL_DATABASE_PORT'] = '32772' + +mysql.init_app(app) +conn = mysql.connect() + # Remember to update this list ENDPOINT_LIST = ['/', '/meta/heartbeat', '/meta/members'] @@ -30,7 +40,6 @@ def make_json_response(data, status=True, code=200): ) return response - @app.route("/") def index(): """Returns a list of implemented endpoints.""" From b27342ab46f5ffd0ba4dc084f426147666e5eb4c Mon Sep 17 00:00:00 2001 From: William Tan Date: Mon, 26 Feb 2018 16:16:22 +0800 Subject: [PATCH 03/76] Add sleep, wait for db to finish loading --- run.sh | 10 +++++++--- src/service/app.py | 8 ++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/run.sh b/run.sh index 38434a9..f5c8daa 100755 --- a/run.sh +++ b/run.sh @@ -17,7 +17,7 @@ ps auxw | grep mysql | grep -v grep > /dev/null if [ $? == 0 ] then - service stop > /dev/null + service mysql stop > /dev/null fi TEAMID=`md5sum README.md | cut -d' ' -f 1` @@ -30,5 +30,9 @@ docker build . -t $TEAMID echo "Starting app and database" -docker run -p 3306:3306 -v /var/lib/mysql --name=testsql -e MYeSQL_ROOT_PASSWORD=password -d mysql & -docker run -p 80:80 -p 8080:8080 -t $TEAMID \ No newline at end of file +docker run -v /var/lib/mysql --name=testsql -e MYSQL_ROOT_PASSWORD=password -d mysql + +echo "Loading Database" +sleep 30 + +docker run -p 80:80 -p 8080:8080 --link=testsql -t $TEAMID \ No newline at end of file diff --git a/src/service/app.py b/src/service/app.py index 09189a0..65a230a 100644 --- a/src/service/app.py +++ b/src/service/app.py @@ -12,10 +12,10 @@ mysql = MySQL() app.config['MYSQL_DATABASE_PASSWORD'] = 'password' -app.config['MYSQL_DATABASE_DB'] = 'testsql' -app.config['MYSQL_DATABASE_HOST'] = '0.0.0.0' -app.config['MYSQL_DATABASE_PORT'] = '32772' - +app.config['MYSQL_DATABASE_DB'] = 'diary_db' +app.config['MYSQL_DATABASE_HOST'] = '172.17.0.2' +app.config['MYSQL_DATABASE_PORT'] = 3306 + mysql.init_app(app) conn = mysql.connect() From 473678a31dcabd9e9b7d1057c351588d8c5ed928 Mon Sep 17 00:00:00 2001 From: William Tan Date: Tue, 27 Feb 2018 15:46:08 +0800 Subject: [PATCH 04/76] Allow persistent storage for db --- README.md | 12 ++++++++++++ run.sh | 8 ++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9a814e3..efe8cfc 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,18 @@ sudo docker run hello-world sudo ./run.sh ``` +# Accessing Database + +1. Start the server with ./run.sh +2. Open another terminal window and enter the following commands: + +``` +sudo docker exec -it diary_db_container bash +mysql -u root -p + +``` +Enter the password to access the database when promtped. + (Docker CE installation instructions are from this [link](https://docs.docker.com/install/linux/docker-ce/ubuntu/#install-using-the-repository).) diff --git a/run.sh b/run.sh index f5c8daa..30ce0bf 100755 --- a/run.sh +++ b/run.sh @@ -13,6 +13,7 @@ then /etc/init.d/apache2 stop > /dev/null fi +#Kill any running mysql first ps auxw | grep mysql | grep -v grep > /dev/null if [ $? == 0 ] @@ -20,6 +21,9 @@ then service mysql stop > /dev/null fi +#Prepare database directory +mkdir -p /tmp/diary_db + TEAMID=`md5sum README.md | cut -d' ' -f 1` docker kill $(docker ps -q) docker rm $(docker ps -a -q) @@ -30,9 +34,9 @@ docker build . -t $TEAMID echo "Starting app and database" -docker run -v /var/lib/mysql --name=testsql -e MYSQL_ROOT_PASSWORD=password -d mysql +docker run -v /tmp/diary_db:/var/lib/mysql --name=diary_db_container -e MYSQL_ROOT_PASSWORD=password -d mysql echo "Loading Database" sleep 30 -docker run -p 80:80 -p 8080:8080 --link=testsql -t $TEAMID \ No newline at end of file +docker run -p 80:80 -p 8080:8080 --link=diary_db_container -t $TEAMID \ No newline at end of file From 2bc94d1133af5aecbf2abaa99087e29702773be9 Mon Sep 17 00:00:00 2001 From: William Tan Date: Tue, 27 Feb 2018 16:50:22 +0800 Subject: [PATCH 05/76] Update README.md --- README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index efe8cfc..e594081 100644 --- a/README.md +++ b/README.md @@ -49,11 +49,16 @@ sudo docker run hello-world sudo ./run.sh ``` +(Docker CE installation instructions are from this +[link](https://docs.docker.com/install/linux/docker-ce/ubuntu/#install-using-the-repository).) + +**Please consult your assignment hand-out for detailed setup information.** -# Accessing Database +## Accessing Database +To access and manipulate the database outside the app: 1. Start the server with ./run.sh -2. Open another terminal window and enter the following commands: +2. Open another terminal window and enter the commands below: ``` sudo docker exec -it diary_db_container bash @@ -62,10 +67,7 @@ mysql -u root -p ``` Enter the password to access the database when promtped. -(Docker CE installation instructions are from this -[link](https://docs.docker.com/install/linux/docker-ce/ubuntu/#install-using-the-repository).) -**Please consult your assignment hand-out for detailed setup information.** ## Grading From f5d47f43ece38a621948ce5d8425a43897174517 Mon Sep 17 00:00:00 2001 From: MX Chai Date: Wed, 28 Feb 2018 15:44:28 +0800 Subject: [PATCH 06/76] Update run.sh --- run.sh | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/run.sh b/run.sh index 30ce0bf..acceb82 100755 --- a/run.sh +++ b/run.sh @@ -1,42 +1,41 @@ #!/bin/bash if [ "$EUID" -ne 0 ] - then echo "Please run as root" - exit + then echo "Please run as root" + exit fi -#Apache2 might run on the same port, just kill it +# Apache2 might run on the same port, just kill it ps auxw | grep apache2 | grep -v grep > /dev/null if [ $? == 0 ] then - /etc/init.d/apache2 stop > /dev/null + /etc/init.d/apache2 stop > /dev/null fi -#Kill any running mysql first +# Kill any running mysql first ps auxw | grep mysql | grep -v grep > /dev/null if [ $? == 0 ] then - service mysql stop > /dev/null + service mysql stop > /dev/null fi -#Prepare database directory -mkdir -p /tmp/diary_db - -TEAMID=`md5sum README.md | cut -d' ' -f 1` +# Tears down any running containers docker kill $(docker ps -q) docker rm $(docker ps -a -q) -docker pull mysql - -docker build . -t $TEAMID +# Prepare database directory +mkdir -p /tmp/diary_db +# Pull mysql image and runs MySQL container +docker pull mysql echo "Starting app and database" - docker run -v /tmp/diary_db:/var/lib/mysql --name=diary_db_container -e MYSQL_ROOT_PASSWORD=password -d mysql - echo "Loading Database" -sleep 30 +sleep 5 +# Builds web application image and runs webapp container +TEAMID=`md5sum README.md | cut -d' ' -f 1` +docker build . -t $TEAMID docker run -p 80:80 -p 8080:8080 --link=diary_db_container -t $TEAMID \ No newline at end of file From 85a09513f0046224600e02b4870cfdc40770a3aa Mon Sep 17 00:00:00 2001 From: MX Chai Date: Wed, 28 Feb 2018 21:32:24 +0800 Subject: [PATCH 07/76] Update comments, replace python packages --- Dockerfile | 3 ++- run.sh | 3 +++ src/service/start_services.sh | 7 +++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index c65dcd4..6589d9c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,8 @@ RUN apt-get install -y apache2 RUN pip install -U pip RUN pip install -U flask RUN pip install -U flask-cors -RUN pip install flask-mysql +RUN pip install flask-sqlalchemy +ENV FLASK_APP app.py RUN echo "ServerName localhost " >> /etc/apache2/apache2.conf RUN echo "$user hard nproc 20" >> /etc/security/limits.conf ADD ./src/service /service diff --git a/run.sh b/run.sh index acceb82..54b30d5 100755 --- a/run.sh +++ b/run.sh @@ -35,6 +35,9 @@ docker run -v /tmp/diary_db:/var/lib/mysql --name=diary_db_container -e MYSQL_RO echo "Loading Database" sleep 5 +# Creates the database "diary_db" +# TODO: add code here to automate the creation of the MySQL database + # Builds web application image and runs webapp container TEAMID=`md5sum README.md | cut -d' ' -f 1` docker build . -t $TEAMID diff --git a/src/service/start_services.sh b/src/service/start_services.sh index a970fe8..a9bcb4d 100644 --- a/src/service/start_services.sh +++ b/src/service/start_services.sh @@ -1,4 +1,11 @@ #!/bin/bash +# This script is executed WITHIN the Docker container +# Everything in the /src/service folder is now found in /service of the container +# Everything in the /src/html folder is now found in /var/www/html of the container + +# Start the apache server to serve files over port 80 apachectl start + +# Start the Flask application python /service/app.py From c58e437cfb4f824488182bb3531e13df060687e8 Mon Sep 17 00:00:00 2001 From: MX Chai Date: Wed, 28 Feb 2018 21:33:03 +0800 Subject: [PATCH 08/76] Update app, add database and models --- src/service/app.py | 39 ++++++++++++++++++++++++++++----------- src/service/database.py | 3 +++ src/service/models.py | 12 ++++++++++++ 3 files changed, 43 insertions(+), 11 deletions(-) create mode 100644 src/service/database.py create mode 100644 src/service/models.py diff --git a/src/service/app.py b/src/service/app.py index 65a230a..4728c1b 100644 --- a/src/service/app.py +++ b/src/service/app.py @@ -1,27 +1,37 @@ #!/usr/bin/python +from flask_sqlalchemy import SQLAlchemy +from database import db from flask import Flask from flask_cors import CORS +from models import User import json import os -from flaskext.mysql import MySQL +########################################## +## Set up Flask application and database +########################################## app = Flask(__name__) +app.config['DEBUG'] = True +# TODO: migrate to MySQL after development is done, or maybe not? +# app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:password@localhost/diary_db' +app.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:////tmp/test.db" +app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False # Enable cross origin sharing for all endpoints CORS(app) - -mysql = MySQL() -app.config['MYSQL_DATABASE_PASSWORD'] = 'password' -app.config['MYSQL_DATABASE_DB'] = 'diary_db' -app.config['MYSQL_DATABASE_HOST'] = '172.17.0.2' -app.config['MYSQL_DATABASE_PORT'] = 3306 - -mysql.init_app(app) -conn = mysql.connect() +db.init_app(app) # Remember to update this list ENDPOINT_LIST = ['/', '/meta/heartbeat', '/meta/members'] +############################# +## Helper functions +############################# + +def setup_database(app): + with app.app_context(): + db.create_all() + def make_json_response(data, status=True, code=200): """Utility function to create the JSON responses.""" @@ -40,6 +50,10 @@ def make_json_response(data, status=True, code=200): ) return response +############################# +## Routes +############################# + @app.route("/") def index(): """Returns a list of implemented endpoints.""" @@ -66,5 +80,8 @@ def meta_members(): dname = os.path.dirname(abspath) os.chdir(dname) - # Run the application + # Checks if the sqlite database already exists + # TODO: need to change this when we move back to MySQL + if not os.path.isfile('/tmp/test.db'): + setup_database(app) app.run(debug=False, port=8080, host="0.0.0.0") diff --git a/src/service/database.py b/src/service/database.py new file mode 100644 index 0000000..2e1eeb6 --- /dev/null +++ b/src/service/database.py @@ -0,0 +1,3 @@ +from flask_sqlalchemy import SQLAlchemy + +db = SQLAlchemy() \ No newline at end of file diff --git a/src/service/models.py b/src/service/models.py new file mode 100644 index 0000000..27ac0d7 --- /dev/null +++ b/src/service/models.py @@ -0,0 +1,12 @@ +from database import db + +class User(db.Model): + id = db.Column(db.Integer, primary_key=True) + username = db.Column(db.String(64), index=True, unique=True) + password_hash = db.Column(db.String(128)) + fullname = db.Column(db.String(128)) + age = db.Column(db.Integer) + + def __repr__(self): + return "".format(self.username) + \ No newline at end of file From 9eb639f08874f3b6097de0d35908b2c3790f9e19 Mon Sep 17 00:00:00 2001 From: garbanzos Date: Wed, 28 Feb 2018 22:25:02 +0800 Subject: [PATCH 09/76] add entry and token model and their relationships with user --- src/service/models.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/service/models.py b/src/service/models.py index 27ac0d7..1697ddd 100644 --- a/src/service/models.py +++ b/src/service/models.py @@ -6,7 +6,25 @@ class User(db.Model): password_hash = db.Column(db.String(128)) fullname = db.Column(db.String(128)) age = db.Column(db.Integer) + entries = db.relationship('Entry', backref='author', lazy='dynamic') + token = db.relationship('Token', backref='user', lazy='dynamic', uselist=False) def __repr__(self): return "".format(self.username) - \ No newline at end of file + +class Token(db.Model): + id = db.Column(db.Integer, primary_key=True) + token = db.Column(db.String(36), index=True, unique=True) + user_id = db.Column(db.Integer, db.ForeignKey('user.id')) + +class Entry(db.Model): + id = db.Column(db.Integer, primary_key=True) + title = db.Column(db.String(300)) + user_id = db.Column(db.Integer, db.ForeignKey('user.id')) + # author = db.Column(db.String(64), index=True) # username of author, maybe use foreign key user_id + publish_date = db.Column(db.DateTime) + public = db.Column(db.Boolean) + text = db.Column(db.Text) + + def __repr__(self): + return "".format(self.title) From 9bd0e62b14d90122981bae764afd1f775e88d438 Mon Sep 17 00:00:00 2001 From: garbanzos Date: Wed, 28 Feb 2018 23:18:04 +0800 Subject: [PATCH 10/76] added diary routes skeleton code --- src/service/app.py | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/service/app.py b/src/service/app.py index 4728c1b..79ffc25 100644 --- a/src/service/app.py +++ b/src/service/app.py @@ -22,7 +22,7 @@ db.init_app(app) # Remember to update this list -ENDPOINT_LIST = ['/', '/meta/heartbeat', '/meta/members'] +ENDPOINT_LIST = ['/', '/meta/heartbeat', '/meta/members', '/diary', '/diary/create', '/diary/delete', 'diary/permission'] ############################# ## Helper functions @@ -59,13 +59,11 @@ def index(): """Returns a list of implemented endpoints.""" return make_json_response(ENDPOINT_LIST) - @app.route("/meta/heartbeat") def meta_heartbeat(): """Returns true""" return make_json_response(None) - @app.route("/meta/members") def meta_members(): """Returns a list of team members""" @@ -73,6 +71,33 @@ def meta_members(): team_members = f.read().strip().split("\n") return make_json_response(team_members) +''' +Diary APIs +''' + +@app.route('/diary', methods=['GET', 'POST']) +def diary(): + if request.method == 'GET': + # TODO retrieve all public entries + return "get /diary" + else: + # TODO retrieve all entries of authenicated user + return "post /diary" + +@app.route('/diary/create', methods=['POST']) +def diary_create(): + # TODO create a new diary entry + return "create!" + +@app.route('/diary/delete', methods=['POST']) +def diary_delete(): + # TODO delete an existing diary entry + return "delete!" + +@app.route('/diary/permission', methods=['POST']) +def diary_permission(): + # TODO change permission of diary entry + return "change permissions!" if __name__ == '__main__': # Change the working directory to the script directory From 97c6226db8a6a6a6433e2bc6cd4311ec27deec9f Mon Sep 17 00:00:00 2001 From: MX Chai Date: Thu, 1 Mar 2018 00:22:00 +0800 Subject: [PATCH 11/76] Update User model for password hashing --- src/service/models.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/service/models.py b/src/service/models.py index 27ac0d7..3522ae0 100644 --- a/src/service/models.py +++ b/src/service/models.py @@ -1,4 +1,5 @@ from database import db +from werkzeug.security import generate_password_hash, check_password_hash class User(db.Model): id = db.Column(db.Integer, primary_key=True) @@ -9,4 +10,10 @@ class User(db.Model): def __repr__(self): return "".format(self.username) - \ No newline at end of file + + def set_password(self, password): + self.password_hash = generate_password_hash(password) + + def check_password(self, password): + return check_password_hash(self.password_hash, password) + From d4f425becad9fa7301f600ceef4498e678730369 Mon Sep 17 00:00:00 2001 From: MX Chai Date: Thu, 1 Mar 2018 00:22:12 +0800 Subject: [PATCH 12/76] Add /users/register route --- src/service/app.py | 43 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/src/service/app.py b/src/service/app.py index 4728c1b..8c32c30 100644 --- a/src/service/app.py +++ b/src/service/app.py @@ -1,12 +1,14 @@ #!/usr/bin/python +import json +import os + from flask_sqlalchemy import SQLAlchemy from database import db -from flask import Flask +from flask import Flask, request from flask_cors import CORS + from models import User -import json -import os ########################################## ## Set up Flask application and database @@ -50,6 +52,16 @@ def make_json_response(data, status=True, code=200): ) return response +def verify_user_registration(request): + # Checks if registration fields are all filled in + if len(request.form) < 4: + import pdb; pdb.set_trace() + return False + elif (request.form['username'] and request.form['password'] + and request.form['fullname'] and request.form['age']): + return True + return False + ############################# ## Routes ############################# @@ -73,6 +85,31 @@ def meta_members(): team_members = f.read().strip().split("\n") return make_json_response(team_members) +@app.route("/users/register", methods=['POST']) +def register_user(): + if request.method == 'POST': + if not verify_user_registration(request): + return make_json_response("Please fill in all the required information", False) + try: + user = User(username=request.form['username'], fullname=request.form['fullname'], age=request.form['age']) + user.set_password(request.form['password']) + db.session.add(user) + db.session.commit() + except Exception as e: + return make_json_response("User already exists!", False) + return make_json_response(None) + +@app.route("/users/authenticate", methods=['POST']) +def auth_user(): + pass + +@app.route("/users/expire", methods=['POST']) +def expire_token(): + pass + +@app.route("/users", methods=['POST']) +def retrieve_user_info(): + pass if __name__ == '__main__': # Change the working directory to the script directory From 883da1aa65eca7872e7bfe184eba149251cc5f6d Mon Sep 17 00:00:00 2001 From: garbanzos Date: Thu, 1 Mar 2018 01:46:05 +0800 Subject: [PATCH 13/76] added base ui html that is the parent of all htmls --- src/html/base.html | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/html/base.html diff --git a/src/html/base.html b/src/html/base.html new file mode 100644 index 0000000..3d6fb94 --- /dev/null +++ b/src/html/base.html @@ -0,0 +1,33 @@ + + + + + + Secret Diary + + + + + + + + +
+ {% block content %} {% endblock %} + + + + {% block additional_scripts %} {% endblock %} +
+ + + From 55d1c02a70ab4e7b95c66a0d976cf05da32374d3 Mon Sep 17 00:00:00 2001 From: MX Chai Date: Thu, 1 Mar 2018 21:37:14 +0800 Subject: [PATCH 14/76] Update User model --- src/service/models.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/service/models.py b/src/service/models.py index e247f71..a0379d8 100644 --- a/src/service/models.py +++ b/src/service/models.py @@ -13,14 +13,12 @@ class User(db.Model): def __repr__(self): return "".format(self.username) -<<<<<<< HEAD def set_password(self, password): self.password_hash = generate_password_hash(password) def check_password(self, password): return check_password_hash(self.password_hash, password) -======= class Token(db.Model): id = db.Column(db.Integer, primary_key=True) token = db.Column(db.String(36), index=True, unique=True) @@ -37,4 +35,3 @@ class Entry(db.Model): def __repr__(self): return "".format(self.title) ->>>>>>> origin/diary-api From 645e69e3b68618ed9e18fc715a00e60237516ec2 Mon Sep 17 00:00:00 2001 From: MX Chai Date: Thu, 1 Mar 2018 21:37:29 +0800 Subject: [PATCH 15/76] Add /users/authenticate --- src/service/app.py | 52 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/src/service/app.py b/src/service/app.py index d50bf6e..977e3b9 100644 --- a/src/service/app.py +++ b/src/service/app.py @@ -2,13 +2,14 @@ import json import os +from uuid import uuid4 from flask_sqlalchemy import SQLAlchemy from database import db from flask import Flask, request from flask_cors import CORS -from models import User +from models import User, Token, Entry ########################################## ## Set up Flask application and database @@ -24,7 +25,8 @@ db.init_app(app) # Remember to update this list -ENDPOINT_LIST = ['/', '/meta/heartbeat', '/meta/members', '/diary', '/diary/create', '/diary/delete', 'diary/permission'] +ENDPOINT_LIST = ['/', '/meta/heartbeat', '/meta/members', '/diary', + '/diary/create', '/diary/delete', '/diary/permission'] ############################# ## Helper functions @@ -52,16 +54,39 @@ def make_json_response(data, status=True, code=200): ) return response -def verify_user_registration(request): +def make_json_false_response(): + # Helper function just to return False, since it's not handled by + # make_json_response, which requires an explicit Error message + response = app.response_class( + response=json.dumps({'status': False}), + status=200, + mimetype='application/json' + ) + return response + +def verify_user_registration(): # Checks if registration fields are all filled in if len(request.form) < 4: - import pdb; pdb.set_trace() return False elif (request.form['username'] and request.form['password'] and request.form['fullname'] and request.form['age']): return True return False +def add_user_token(): + # Creates a fresh UUID and stores it in the database + # If a token for a user_id already exists, return the existing token + user_id = User.query.filter_by(username=request.form['username']).first().id + user_token = Token.query.filter_by(user_id=user_id).first() + if user_token: + return user_token.token + else: + new_uuid = str(uuid4()) + token = Token(token=new_uuid, user_id=user_id) + db.session.add(token) + db.session.commit() + return new_uuid + ############################# ## Admin Routes ############################# @@ -90,10 +115,12 @@ def meta_members(): @app.route("/users/register", methods=['POST']) def register_user(): if request.method == 'POST': - if not verify_user_registration(request): + if not verify_user_registration(): return make_json_response("Please fill in all the required information", False) try: - user = User(username=request.form['username'], fullname=request.form['fullname'], age=request.form['age']) + user = User(username=request.form['username'], + fullname=request.form['fullname'], + age=request.form['age']) user.set_password(request.form['password']) db.session.add(user) db.session.commit() @@ -103,7 +130,14 @@ def register_user(): @app.route("/users/authenticate", methods=['POST']) def auth_user(): - pass + if not (request.form['username'] and request.form['password']): + return make_json_response("Please fill in all the required information", False) + auth_user = User.query.filter_by(username=request.form['username']).first() + if auth_user: + if auth_user.check_password(request.form['password']): + token = add_user_token() + return make_json_response(token) + return make_json_false_response() @app.route("/users/expire", methods=['POST']) def expire_token(): @@ -148,7 +182,7 @@ def diary_permission(): os.chdir(dname) # Checks if the sqlite database already exists - # TODO: need to change this when we move back to MySQL + # TODO: need to change this when we move back to MySQL if not os.path.isfile('/tmp/test.db'): - setup_database(app) + setup_database(app) app.run(debug=False, port=8080, host="0.0.0.0") From 08d839302e6857c9d7f18bd4fbfbe01a0c59e8e4 Mon Sep 17 00:00:00 2001 From: MX Chai Date: Thu, 1 Mar 2018 21:43:25 +0800 Subject: [PATCH 16/76] Fix error in User model --- src/service/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/service/models.py b/src/service/models.py index a0379d8..c0ede64 100644 --- a/src/service/models.py +++ b/src/service/models.py @@ -8,7 +8,7 @@ class User(db.Model): fullname = db.Column(db.String(128)) age = db.Column(db.Integer) entries = db.relationship('Entry', backref='author', lazy='dynamic') - token = db.relationship('Token', backref='user', lazy='dynamic', uselist=False) + token = db.relationship('Token', backref='user', uselist=False) def __repr__(self): return "".format(self.username) From a54fa4737cce8c3a1396e196e1f7437e954e90ec Mon Sep 17 00:00:00 2001 From: MX Chai Date: Thu, 1 Mar 2018 22:00:07 +0800 Subject: [PATCH 17/76] Add /users/expire --- src/service/app.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/service/app.py b/src/service/app.py index 977e3b9..a67822e 100644 --- a/src/service/app.py +++ b/src/service/app.py @@ -87,6 +87,12 @@ def add_user_token(): db.session.commit() return new_uuid +def remove_user_token(token_str): + # Removes the token from Token table + token = Token.query.filter_by(token=token_str).first() + db.session.delete(token) + db.session.commit() + ############################# ## Admin Routes ############################# @@ -141,7 +147,13 @@ def auth_user(): @app.route("/users/expire", methods=['POST']) def expire_token(): - pass + if request.form['token']: + token = Token.query.filter_by(token=request.form['token']).first() + if token: + token_str = token.token + remove_user_token(token_str) + return make_json_response(None) + return make_json_false_response() @app.route("/users", methods=['POST']) def retrieve_user_info(): From 2f14c5dd1924b5d1a877c6f5174b67e7614c2615 Mon Sep 17 00:00:00 2001 From: MX Chai Date: Thu, 1 Mar 2018 22:38:55 +0800 Subject: [PATCH 18/76] Add /users --- src/service/app.py | 8 +++++++- src/service/models.py | 11 +++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/service/app.py b/src/service/app.py index a67822e..af777e1 100644 --- a/src/service/app.py +++ b/src/service/app.py @@ -157,7 +157,13 @@ def expire_token(): @app.route("/users", methods=['POST']) def retrieve_user_info(): - pass + if request.form['token']: + token = Token.query.filter_by(token=request.form['token']).first() + if token: + user_id = token.user_id + user = User.query.filter_by(id=user_id).first() + return make_json_response(user.json_dict()) + return make_json_response("Invalid authentication token", False) ############################# ## Diary Routes diff --git a/src/service/models.py b/src/service/models.py index c0ede64..0bb1986 100644 --- a/src/service/models.py +++ b/src/service/models.py @@ -1,3 +1,5 @@ +import json + from database import db from werkzeug.security import generate_password_hash, check_password_hash @@ -19,6 +21,15 @@ def set_password(self, password): def check_password(self, password): return check_password_hash(self.password_hash, password) + def json_dict(self): + # Returns a Python dictionary of public attributes + dct = { + 'username': self.username, + 'fullname': self.fullname, + 'age': self.age + } + return dct + class Token(db.Model): id = db.Column(db.Integer, primary_key=True) token = db.Column(db.String(36), index=True, unique=True) From 2d3076d503baca57e8fcc6665f9c545f8e1c04ae Mon Sep 17 00:00:00 2001 From: MX Chai Date: Thu, 1 Mar 2018 23:06:31 +0800 Subject: [PATCH 19/76] Fix minor route errors --- src/service/app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/service/app.py b/src/service/app.py index af777e1..7ff78ff 100644 --- a/src/service/app.py +++ b/src/service/app.py @@ -132,7 +132,7 @@ def register_user(): db.session.commit() except Exception as e: return make_json_response("User already exists!", False) - return make_json_response(None) + return make_json_response(None, code=201) @app.route("/users/authenticate", methods=['POST']) def auth_user(): @@ -163,7 +163,7 @@ def retrieve_user_info(): user_id = token.user_id user = User.query.filter_by(id=user_id).first() return make_json_response(user.json_dict()) - return make_json_response("Invalid authentication token", False) + return make_json_response("Invalid authentication token.", False) ############################# ## Diary Routes From 9b1f54d38cef0b9f6da2e4f777ce074408949632 Mon Sep 17 00:00:00 2001 From: MX Chai Date: Thu, 1 Mar 2018 23:22:24 +0800 Subject: [PATCH 20/76] Update run.sh to remove MySQL-related commands --- run.sh | 31 +------------------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/run.sh b/run.sh index 54b30d5..e7f2e79 100755 --- a/run.sh +++ b/run.sh @@ -5,40 +5,11 @@ if [ "$EUID" -ne 0 ] exit fi -# Apache2 might run on the same port, just kill it -ps auxw | grep apache2 | grep -v grep > /dev/null - -if [ $? == 0 ] -then - /etc/init.d/apache2 stop > /dev/null -fi - -# Kill any running mysql first -ps auxw | grep mysql | grep -v grep > /dev/null - -if [ $? == 0 ] -then - service mysql stop > /dev/null -fi - # Tears down any running containers docker kill $(docker ps -q) docker rm $(docker ps -a -q) -# Prepare database directory -mkdir -p /tmp/diary_db - -# Pull mysql image and runs MySQL container -docker pull mysql -echo "Starting app and database" -docker run -v /tmp/diary_db:/var/lib/mysql --name=diary_db_container -e MYSQL_ROOT_PASSWORD=password -d mysql -echo "Loading Database" -sleep 5 - -# Creates the database "diary_db" -# TODO: add code here to automate the creation of the MySQL database - # Builds web application image and runs webapp container TEAMID=`md5sum README.md | cut -d' ' -f 1` docker build . -t $TEAMID -docker run -p 80:80 -p 8080:8080 --link=diary_db_container -t $TEAMID \ No newline at end of file +docker run -p 80:80 -p 8080:8080 -t $TEAMID \ No newline at end of file From 630dbadf591eb12ffc792822a327455cc6130cdf Mon Sep 17 00:00:00 2001 From: MX Chai Date: Thu, 1 Mar 2018 23:22:31 +0800 Subject: [PATCH 21/76] Update team members --- src/service/team_members.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/service/team_members.txt b/src/service/team_members.txt index 50bdea8..f3a671c 100644 --- a/src/service/team_members.txt +++ b/src/service/team_members.txt @@ -1,3 +1,3 @@ -Jeremy Heng -John Galt -Audrey Shida +Chai Ming Xuan +Tan Yi Yan +Tan Wee Chen Willian \ No newline at end of file From 9f9cbda726ebb95d6331a3e4973e6171ab6bee56 Mon Sep 17 00:00:00 2001 From: MX Chai Date: Thu, 1 Mar 2018 23:49:46 +0800 Subject: [PATCH 22/76] Update ENDPOINT_LIST --- src/service/app.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/service/app.py b/src/service/app.py index 7ff78ff..bf8aad7 100644 --- a/src/service/app.py +++ b/src/service/app.py @@ -25,8 +25,17 @@ db.init_app(app) # Remember to update this list -ENDPOINT_LIST = ['/', '/meta/heartbeat', '/meta/members', '/diary', - '/diary/create', '/diary/delete', '/diary/permission'] +ENDPOINT_LIST = ['/', + '/meta/heartbeat', + '/meta/members', + '/users/register', + '/users/authenticate', + '/users/expire', + '/users', + '/diary', + '/diary/create', + '/diary/delete', + '/diary/permission'] ############################# ## Helper functions From e58645c9211f51c91fe897032ac377d3c2208f7d Mon Sep 17 00:00:00 2001 From: MX Chai Date: Thu, 1 Mar 2018 23:50:18 +0800 Subject: [PATCH 23/76] Rewrite to handle application/json instead of form-data --- src/service/app.py | 45 ++++++++++++++++++++------------------------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/src/service/app.py b/src/service/app.py index bf8aad7..337f3f0 100644 --- a/src/service/app.py +++ b/src/service/app.py @@ -73,19 +73,11 @@ def make_json_false_response(): ) return response -def verify_user_registration(): - # Checks if registration fields are all filled in - if len(request.form) < 4: - return False - elif (request.form['username'] and request.form['password'] - and request.form['fullname'] and request.form['age']): - return True - return False - def add_user_token(): # Creates a fresh UUID and stores it in the database # If a token for a user_id already exists, return the existing token - user_id = User.query.filter_by(username=request.form['username']).first().id + req_data = request.get_json() + user_id = User.query.filter_by(username=req_data['username']).first().id user_token = Token.query.filter_by(user_id=user_id).first() if user_token: return user_token.token @@ -130,34 +122,37 @@ def meta_members(): @app.route("/users/register", methods=['POST']) def register_user(): if request.method == 'POST': - if not verify_user_registration(): + req_data = request.get_json() + if not all(k in req_data.keys() for k in ['username', 'password', 'fullname', 'age']): return make_json_response("Please fill in all the required information", False) try: - user = User(username=request.form['username'], - fullname=request.form['fullname'], - age=request.form['age']) - user.set_password(request.form['password']) + user = User(username=req_data['username'], + fullname=req_data['fullname'], + age=req_data['age']) + user.set_password(req_data['password']) db.session.add(user) db.session.commit() - except Exception as e: + except: return make_json_response("User already exists!", False) return make_json_response(None, code=201) @app.route("/users/authenticate", methods=['POST']) def auth_user(): - if not (request.form['username'] and request.form['password']): + req_data = request.get_json() + if not all(k in req_data.keys() for k in ['username', 'password']): return make_json_response("Please fill in all the required information", False) - auth_user = User.query.filter_by(username=request.form['username']).first() + auth_user = User.query.filter_by(username=req_data['username']).first() if auth_user: - if auth_user.check_password(request.form['password']): + if auth_user.check_password(req_data['password']): token = add_user_token() return make_json_response(token) return make_json_false_response() @app.route("/users/expire", methods=['POST']) def expire_token(): - if request.form['token']: - token = Token.query.filter_by(token=request.form['token']).first() + req_data = request.get_json() + if req_data['token']: + token = Token.query.filter_by(token=req_data['token']).first() if token: token_str = token.token remove_user_token(token_str) @@ -166,8 +161,9 @@ def expire_token(): @app.route("/users", methods=['POST']) def retrieve_user_info(): - if request.form['token']: - token = Token.query.filter_by(token=request.form['token']).first() + req_data = request.get_json() + if req_data['token']: + token = Token.query.filter_by(token=req_data['token']).first() if token: user_id = token.user_id user = User.query.filter_by(id=user_id).first() @@ -208,8 +204,7 @@ def diary_permission(): dname = os.path.dirname(abspath) os.chdir(dname) - # Checks if the sqlite database already exists - # TODO: need to change this when we move back to MySQL + # If sqlite database does not exist, create it if not os.path.isfile('/tmp/test.db'): setup_database(app) app.run(debug=False, port=8080, host="0.0.0.0") From d732073564b29f53fa381a053714264775c31074 Mon Sep 17 00:00:00 2001 From: MX Chai Date: Fri, 2 Mar 2018 11:17:57 +0800 Subject: [PATCH 24/76] Update Entry model to change 'publish_date', add method to dump JSON --- src/service/models.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/service/models.py b/src/service/models.py index 0bb1986..fdcbd33 100644 --- a/src/service/models.py +++ b/src/service/models.py @@ -40,9 +40,22 @@ class Entry(db.Model): title = db.Column(db.String(300)) user_id = db.Column(db.Integer, db.ForeignKey('user.id')) # author = db.Column(db.String(64), index=True) # username of author, maybe use foreign key user_id - publish_date = db.Column(db.DateTime) + publish_date = db.Column(db.String(26)) public = db.Column(db.Boolean) text = db.Column(db.Text) def __repr__(self): return "".format(self.title) + + def json_dict(self): + # Returns a Python dictionary of public attributes + username = User.query.filter_by(id=self.user_id).first().username + dct = { + 'id': self.id, + 'title': self.title, + 'author': username, + 'publish_date': self.publish_date, + 'public': self.public, + 'text': self.text + } + return dct From 267cc588dd90dc4b28fac1d13b3f8cacb5bfc3e4 Mon Sep 17 00:00:00 2001 From: MX Chai Date: Fri, 2 Mar 2018 11:19:54 +0800 Subject: [PATCH 25/76] Add /diary and /diary/create --- src/service/app.py | 48 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/src/service/app.py b/src/service/app.py index 337f3f0..9bfc996 100644 --- a/src/service/app.py +++ b/src/service/app.py @@ -2,6 +2,7 @@ import json import os +import datetime from uuid import uuid4 from flask_sqlalchemy import SQLAlchemy @@ -94,6 +95,31 @@ def remove_user_token(token_str): db.session.delete(token) db.session.commit() +def check_valid_token(token_str): + # Checks if the token is valid + token = Token.query.filter_by(token=token_str).first() + return bool(token) + +def retrieve_user_id(token_str): + # Returns the user_id of the user who currently owns token_str + token = Token.query.filter_by(token=token_str).first() + user_id = token.user_id + return user_id + +def create_diary_entry(req_data): + # Gets attributes of entry + title = req_data['title'] + user_id = retrieve_user_id(req_data['token']) + publish_date = datetime.datetime.utcnow().isoformat() + public = req_data['public'] + text = req_data['text'] + # Adds entry to database + entry = Entry(title=title, user_id=user_id, publish_date=publish_date, + public=public, text=text) + db.session.add(entry) + db.session.commit() + return entry.id + ############################# ## Admin Routes ############################# @@ -177,16 +203,26 @@ def retrieve_user_info(): @app.route('/diary', methods=['GET', 'POST']) def diary(): if request.method == 'GET': - # TODO retrieve all public entries - return "get /diary" + entries = Entry.query.filter_by(public=True).all() + return make_json_response([e.json_dict() for e in entries]) else: - # TODO retrieve all entries of authenicated user - return "post /diary" + req_data = request.get_json() + if check_valid_token(req_data['token']): + user_id = retrieve_user_id(req_data['token']) + entries = Entry.query.filter_by(user_id=user_id).all() + return make_json_response([e.json_dict() for e in entries]) + return make_json_response("Invalid authentication token.", False) @app.route('/diary/create', methods=['POST']) def diary_create(): - # TODO create a new diary entry - return "create!" + req_data = request.get_json() + if not all(k in req_data.keys() for k in ['token', 'title', 'public', 'text']): + return make_json_response("Invalid authentication token.", False) + # Check if token is valid + if check_valid_token(req_data['token']): + entry_id = create_diary_entry(req_data) + return make_json_response({'id': entry_id}, status=201) + return make_json_response("Invalid authentication token.", False) @app.route('/diary/delete', methods=['POST']) def diary_delete(): From 7a211169b3a000b52084b08b3f6fefbb5bcfe9e4 Mon Sep 17 00:00:00 2001 From: MX Chai Date: Fri, 2 Mar 2018 11:35:16 +0800 Subject: [PATCH 26/76] Clean and update README --- README.md | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 6610a6c..cbe8677 100644 --- a/README.md +++ b/README.md @@ -54,21 +54,6 @@ sudo ./run.sh **Please consult your assignment hand-out for detailed setup information.** -## Accessing Database -To access and manipulate the database outside the app: - -1. Start the server with ./run.sh -2. Open another terminal window and enter the commands below: - -``` -sudo docker exec -it diary_db_container bash -mysql -u root -p - -``` -Enter the password to access the database when promtped. - - - ## Grading The implementation will be graded in an automated fashion on an Ubuntu 16.04 @@ -118,7 +103,12 @@ Please fill out this section with details relevant to your team. #### Question 1: Briefly describe the web technology stack used in your implementation. -Answer: Please replace this sentence with your answer. +Answer: +On the backend, we used the Flask microframework for the web application, +sqlite as the database and Flask-SQLAlchemy as the database ORM. + +As for the frontend, we used basic HTML, Bootstrap CSS for styling, and mostly +vanilla JS without any frontend libraries or frameworks. #### Question 2: Are there any security considerations your team thought about? @@ -145,8 +135,7 @@ Answer: Please replace this sentence with your answer. #### Please declare your individual contributions to the assignment: 1. Chai Ming Xuan - - Integrated feature x into component y - - Implemented z + - Implemented the Users and some of Diary routes 2. Tan Yi Yan - Wrote the front-end code 3. Tan Wee Chen William From 48f75458eef9432ffd3d7e488fa5ee68740df3b6 Mon Sep 17 00:00:00 2001 From: MX Chai Date: Fri, 2 Mar 2018 11:42:51 +0800 Subject: [PATCH 27/76] Clean up --- src/service/app.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/service/app.py b/src/service/app.py index 9bfc996..97646b2 100644 --- a/src/service/app.py +++ b/src/service/app.py @@ -6,19 +6,18 @@ from uuid import uuid4 from flask_sqlalchemy import SQLAlchemy -from database import db from flask import Flask, request from flask_cors import CORS +from database import db from models import User, Token, Entry ########################################## ## Set up Flask application and database ########################################## app = Flask(__name__) +# TODO: change this to False when submitting app.config['DEBUG'] = True -# TODO: migrate to MySQL after development is done, or maybe not? -# app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:password@localhost/diary_db' app.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:////tmp/test.db" app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False # Enable cross origin sharing for all endpoints From 6ebe2013788943df3deceb8a0722a5f1b1525929 Mon Sep 17 00:00:00 2001 From: garbanzos Date: Fri, 2 Mar 2018 14:02:29 +0800 Subject: [PATCH 28/76] add delete and change_permissions api for diary --- src/service/app.py | 53 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/src/service/app.py b/src/service/app.py index 97646b2..6c9588d 100644 --- a/src/service/app.py +++ b/src/service/app.py @@ -25,16 +25,16 @@ db.init_app(app) # Remember to update this list -ENDPOINT_LIST = ['/', - '/meta/heartbeat', - '/meta/members', +ENDPOINT_LIST = ['/', + '/meta/heartbeat', + '/meta/members', '/users/register', '/users/authenticate', '/users/expire', '/users', - '/diary', - '/diary/create', - '/diary/delete', + '/diary', + '/diary/create', + '/diary/delete', '/diary/permission'] ############################# @@ -64,7 +64,7 @@ def make_json_response(data, status=True, code=200): return response def make_json_false_response(): - # Helper function just to return False, since it's not handled by + # Helper function just to return False, since it's not handled by # make_json_response, which requires an explicit Error message response = app.response_class( response=json.dumps({'status': False}), @@ -113,7 +113,7 @@ def create_diary_entry(req_data): public = req_data['public'] text = req_data['text'] # Adds entry to database - entry = Entry(title=title, user_id=user_id, publish_date=publish_date, + entry = Entry(title=title, user_id=user_id, publish_date=publish_date, public=public, text=text) db.session.add(entry) db.session.commit() @@ -151,8 +151,8 @@ def register_user(): if not all(k in req_data.keys() for k in ['username', 'password', 'fullname', 'age']): return make_json_response("Please fill in all the required information", False) try: - user = User(username=req_data['username'], - fullname=req_data['fullname'], + user = User(username=req_data['username'], + fullname=req_data['fullname'], age=req_data['age']) user.set_password(req_data['password']) db.session.add(user) @@ -225,13 +225,38 @@ def diary_create(): @app.route('/diary/delete', methods=['POST']) def diary_delete(): - # TODO delete an existing diary entry - return "delete!" + req_data = request.get_json() + if not all(k in req_data.keys() for k in ['token', 'id']): + return make_json_response("Invalid authentication token.", False) + if not check_valid_token(req_data['token']): + return make_json_response("Invalid authentication token.", False) + + entry = Entry.query.get(req_data['id']) # TODO maybe use get_or_404 + req_user_id = retrieve_user_id(req_data['token']) + if entry.user_id != req_user_id: # only owner of entry can delete entry + return make_json_response("Invalid authentication token.", False) + + db.session.delete(entry) + db.session.commit() + + return make_json_response(None) @app.route('/diary/permission', methods=['POST']) def diary_permission(): - # TODO change permission of diary entry - return "change permissions!" + req_data = request.get_json() + if not all(k in req_data.keys() for k in ['token', 'id', 'public']): + return make_json_response("Invalid authentication token.", False) + if not check_valid_token(req_data['token']): + return make_json_response("Invalid authentication token.", False) + + entry = Entry.query.get(req_data['id']) # TODO maybe use get_or_404 + req_user_id = retrieve_user_id(req_data['token']) + if entry.user_id != req_user_id: # only owner of entry can change the permission + return make_json_response("Invalid authentication token.", False) + + entry.public = req_data['public'] + db.session.commit() + return make_json_response(None) if __name__ == '__main__': # Change the working directory to the script directory From d38f19bb4a0e0babd35bac57a3f251c42d3eb65e Mon Sep 17 00:00:00 2001 From: garbanzos Date: Fri, 2 Mar 2018 14:38:13 +0800 Subject: [PATCH 29/76] moved templates into some dir as app.py so that they can be rendered by flask --- src/service/app.py | 7 ++++++- src/{html => service/templates}/base.html | 24 ++++++++++++++++------- src/service/templates/create_entry.html | 8 ++++++++ 3 files changed, 31 insertions(+), 8 deletions(-) rename src/{html => service/templates}/base.html (52%) create mode 100644 src/service/templates/create_entry.html diff --git a/src/service/app.py b/src/service/app.py index 6c9588d..104fd20 100644 --- a/src/service/app.py +++ b/src/service/app.py @@ -6,7 +6,7 @@ from uuid import uuid4 from flask_sqlalchemy import SQLAlchemy -from flask import Flask, request +from flask import Flask, request, render_template from flask_cors import CORS from database import db @@ -258,6 +258,11 @@ def diary_permission(): db.session.commit() return make_json_response(None) +@app.route('/diary/create_form') +def diary_create_form(): + return render_template('create_entry.html') + + if __name__ == '__main__': # Change the working directory to the script directory abspath = os.path.abspath(__file__) diff --git a/src/html/base.html b/src/service/templates/base.html similarity index 52% rename from src/html/base.html rename to src/service/templates/base.html index 3d6fb94..401310e 100644 --- a/src/html/base.html +++ b/src/service/templates/base.html @@ -11,14 +11,24 @@ -