diff --git a/app.py b/app.py index 5b0a664a46c315fe9d73b84fe7ade2a5f9848c5c..62cf5ef89d407f31cf860f505f82396a83745673 100644 --- a/app.py +++ b/app.py @@ -6,6 +6,7 @@ from modules.login import login_route from modules.comment import comment_route from modules.register import register_route from modules.admin import admin_route +from modules.page import page_route from modules.tools.token import validate_login_token app = Flask(__name__) @@ -16,6 +17,7 @@ app.register_blueprint(login_route) app.register_blueprint(register_route) app.register_blueprint(comment_route) app.register_blueprint(admin_route) +app.register_blueprint(page_route) cors = CORS(app) @@ -33,32 +35,28 @@ def visualisasi(): return render_template("test-visualisasi.html") # User -@app.route("/api/user/", methods=['POST', 'GET', 'DELETE', 'PUT']) -def user(): - if request.method == 'POST': - return "Masukin user baru" - elif request.method == 'GET': - return "Ambil data user sesuai id" - elif request.method == 'DELETE': - return "Hapus user sesuai id" - elif request.method == 'PUT': - return "Edit user" +# @app.route("/api/user/", methods=['POST', 'GET', 'DELETE', 'PUT']) +# def user(): +# if request.method == 'POST': +# return "Masukin user baru" +# elif request.method == 'GET': +# return "Ambil data user sesuai id" +# elif request.method == 'DELETE': +# return "Hapus user sesuai id" +# elif request.method == 'PUT': +# return "Edit user" -# Comment -@app.route("/api/comment/", methods=['POST', 'GET', 'DELETE', 'PUT']) -def comment(): - if request.method == 'POST': - return "Masukin comment baru" - elif request.method == 'GET': - return "Ambil satu comment sesuai id" - elif request.method == 'DELETE': - return "Hapus comment sesuai id" - elif request.method == 'PUT': - return "Edit comment sesuai id" - -# @app.route("/api/comments/", methods=['GET']) -# def - # Ambil komentar-komentar sesuai filter (levelnya) +# # Comment +# @app.route("/api/comment/", methods=['POST', 'GET', 'DELETE', 'PUT']) +# def comment(): +# if request.method == 'POST': +# return "Masukin comment baru" +# elif request.method == 'GET': +# return "Ambil satu comment sesuai id" +# elif request.method == 'DELETE': +# return "Hapus comment sesuai id" +# elif request.method == 'PUT': +# return "Edit comment sesuai id" @app.after_request def after_request(response): diff --git a/database/database.py b/database/database.py index c1a57bb2f1baf0961414cc66a561a9972e5179a1..c83b250098c90e1b2c0b46936a06713394313892 100644 --- a/database/database.py +++ b/database/database.py @@ -1,6 +1,7 @@ from flask import Flask from flask_mongoengine import MongoEngine from bson import json_util +from datetime import datetime import json mongo = MongoEngine() @@ -11,7 +12,7 @@ class User(mongo.Document): password = mongo.StringField(required=True) name = mongo.StringField(required=True) role = mongo.IntField(required=True) - page_list = mongo.ListField(mongo.ReferenceField("Page")) + page_list = mongo.ListField(mongo.ReferenceField("VizData")) def to_json(self): data = self.to_mongo() @@ -25,20 +26,16 @@ class Reply(mongo.EmbeddedDocument): class Comment(mongo.Document): commenter_id = mongo.StringField(max_length=20, required=True) comment_text = mongo.StringField(required=True) - page = mongo.ReferenceField('Page', required=True) + page = mongo.ReferenceField('VizData', required=True) + date = mongo.DateTimeField(default = datetime.utcnow) reply = mongo.EmbeddedDocumentField('Reply') -class Data(mongo.Document): +class VizData(mongo.Document): name = mongo.StringField(required = True) - value = mongo.IntField(required = True) - categories = mongo.ListField(mongo.StringField(), default=[]) - detail = mongo.StringField() - -class Page(mongo.Document): - level = mongo.IntField(required=True) - data = mongo.ListField(mongo.ReferenceField("Data", required=True)) - comments = mongo.ListField(mongo.ReferenceField("Comment"), default=[]) - + level = mongo.IntField(required= True) + value = mongo.LongField(required = True) + year = mongo.IntField(required=True) + subdata = mongo.ListField(mongo.ReferenceField("VizData")) if __name__ == "__main__": diff --git a/modules/admin.py b/modules/admin.py index 851384101784f275a2e6ee7becb2ae8ecc615e66..4af0cf49a5e4d1fa37eabda0cec56702008ce6d5 100644 --- a/modules/admin.py +++ b/modules/admin.py @@ -15,7 +15,7 @@ def add_page(): data = request.get_json() admin = db.User.objects.with_id(data.get("admin_id")) - admin.page_list.append(db.Page.objects.with_id(data.get("page_id"))) + admin.page_list.append(db.VizData.objects.with_id(data.get("page_id"))) admin.save() return jsonify({ "status":200, @@ -29,7 +29,7 @@ def remove_page(): data = request.get_json() admin = db.User.objects.with_id(data.get("admin_id")) - admin.update(pull__page_list = db.Page.objects.with_id(data.get("page_id"))) + admin.update(pull__page_list = db.VizData.objects.with_id(data.get("page_id"))) return jsonify({ "status":200, diff --git a/modules/comment.py b/modules/comment.py index c4a7cf6a01d4a8becc87eb6c33b7d950fe050dff..d03e9cb03cb32e186322568bc85a0363c20fa625 100644 --- a/modules/comment.py +++ b/modules/comment.py @@ -1,3 +1,69 @@ -from flask import Blueprint +import json +import jwt +from datetime import datetime +from flask import Blueprint, jsonify, request, current_app +from database import database as db +from modules.tools.token import Token, validate_login_token +from modules.tools.roles import Roles -comment_route = Blueprint('comment', __name__, template_folder="templates") \ No newline at end of file +comment_route = Blueprint('comment', __name__, template_folder="templates") + +@comment_route.route("/api/comment/get", methods = ["POST"]) +def get_comment(): + page_id = request.get_json().get("page_id") + page = db.VizData.objects.with_id(page_id) + comments = db.Comment.objects(page=page).order_by('-date') + return jsonify({ + "status":200, + "data":comments + }) + +@comment_route.route("/api/comment/get-unreplied", methods=["POST"]) +def get_unreplied_comment(): + page_id = request.get_json().get("page_id") + page = db.VizData.objects.with_id(page_id) + comments = db.Comment.objects(page=page, reply__exists=False).order_by('-date') + return jsonify({ + "status":200, + "data":comments + }) + + +@comment_route.route("/api/comment/add", methods=["POST"]) +@validate_login_token(pass_user=True) +def add_comment(user): + req = request.get_json() + page_id = req.get("page_id") + text = req.get("text") + try: + page = db.VizData.objects.with_id(page_id) + new_comment = db.Comment(commenter_id = user.id, comment_text=text, page = page) + new_comment.save() + return jsonify({ + "status":200, + "message":"comment added successfully" + }) + except Exception as e: + return jsonify({ + "status":500, + "message":str(e) + }) + +@comment_route.route("/api/comment/add-reply", methods=["POST"]) +@validate_login_token(min_access_level=Roles.ADMIN, pass_user=True) +def add_reply(user): + req = request.get_json() + comment_id = req.get("comment_id") + reply_text = req.get("text") + + comment = db.Comment.objects.with_id(comment_id) + if comment.page not in user.page_list: + return jsonify({ + "status":401, + "message":"Unauthorized reply" + }) + comment.update(reply=db.Reply(replier_id = user.id, reply_text=reply_text)) + return jsonify({ + "status":200, + "message":"Reply added successfully" + }) \ No newline at end of file diff --git a/modules/login.py b/modules/login.py index 05b14c24e54f26389b6203a274de7e161a73a513..221a76778fa8dccf344ec6aa8d0368251eb83107 100644 --- a/modules/login.py +++ b/modules/login.py @@ -61,8 +61,22 @@ def check_user_existence(): @login_route.route("/api/get-user", methods=["POST"]) @validate_login_token(pass_user=True) def get_user_from_token(user): + del user.password return jsonify({ "status": 200, + "data": user #sends back all information about user except password + }) + +@login_route.route("/api/get-user-public", methods=["POST"]) +def get_user_from_id(): + req = request.get_json() + user_id = req.get("user_id") + user = db.User.objecs.with_id(user_id) + del user.password + del user.username + del user.page_list + del user.role + return jsonify({ + "status":200, "data": user }) - \ No newline at end of file diff --git a/modules/page.py b/modules/page.py index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ca7092fbf73b7eb18737edc7c4d4704bd907f079 100644 --- a/modules/page.py +++ b/modules/page.py @@ -0,0 +1,62 @@ +import json +import jwt +import datetime +from flask import Blueprint, jsonify, request, current_app +from database import database as db +from modules.tools.token import Token, validate_login_token +from modules.tools.roles import Roles + +page_route = Blueprint('page', __name__, template_folder="templates") + +@page_route.route("/api/page/get-top", methods=["POST"]) +def get_top_page(): + year = request.get_json().get("year") + try: + vdata = db.VizData.objects.get(level = 0, year = year) + return jsonify({ + "status":200, + "data": vdata, + "subdata": [child for child in vdata.subdata] + }) + except Exception as e: + return jsonify({ + "status": 500, + "message": str(e) + }) + +@page_route.route("/api/page/get", methods=["POST"]) +def get_page(): + data_id = request.get_json().get("page_id") + try: + vdata = db.VizData.objects.with_id(data_id) + if vdata is not None: + return jsonify({ + "status":200, + "data":vdata, + "subdata":[child for child in vdata.subdata] + }) + else: + return jsonify({ + "status":404, + "message":"page not found" + }) + except Exception as e: + return jsonify({ + "status": 500, + "message": str(e) + }) + + +@page_route.route("/api/hidden/add-data", methods=["POST"]) +def add_page(): + data = request.get_json() + name = data.get("name") + value = data.get("value") + level = data.get("level") + year = data.get("year") + + db.VizData(name=name, value=value, level=level, year=year).save() + return jsonify({ + "status":200, + "message":"data added, but this endpoint is only for testing" + }) diff --git a/modules/register.py b/modules/register.py index 7f4bdfe6191b5cfe50593019b7cf6636c109a3e7..aa30c164daa15dc962d73e93db850497d21f3631 100644 --- a/modules/register.py +++ b/modules/register.py @@ -1,6 +1,7 @@ from flask import Blueprint, jsonify, request, current_app from database import database as db -from modules.tools.token import Token +from modules.tools.token import Token, validate_login_token +from modules.tools.roles import Roles register_route = Blueprint('register', __name__, template_folder="templates") @@ -33,20 +34,20 @@ def add_new_user(): @register_route.route('/api/unregister', methods=["POST"]) -def delete_user(): +@validate_login_token(pass_user=True) +def delete_user(user): try: - token = request.headers.get("Authorization") - password = request.get_json().get("password") - if token is not None: - code, user_id = Token.decode_token(token) - if code > 0: - raise Exception(user_id) + user_id = request.get_json().get("user_id") + if user_id is not None: + del_user = db.User.objects.with_id(user_id) + if del_user != user and user.role < Roles.ADMIN: + return jsonify({ + "status":401, + "message":"unauthorized delete" + }) else: - user_id = request.get_json().get("user_id") - if user_id is None: - raise Exception("Invalid credentials") - check = db.User.objects.with_id(user_id) - check.delete() + del_user = user + del_user.delete() return jsonify({ "status": 200, "message": "User deleted successfully" diff --git a/readme.md b/readme.md index 17759fbc793e871dd1bc7760c08aa2c42fde3b26..f9e8344e0623898ab83b11aebe0ed0a4e65400e1 100644 --- a/readme.md +++ b/readme.md @@ -10,4 +10,100 @@ Server ini akan menyediakan api yang menangani akun-akun pengguna dan pemrosesan pip install flask pip install flask_mongoengine pip install PyJWT -``` \ No newline at end of file +``` + +## API Endpoints + +Berikut adalah API endpoints dari backend server VIS-MASY: + +### Login dan Register + +* #### /api/login + Method: POST + Data request: username, password + Response: status, token + Melakukan login dan mengirim balik token login + +* #### /api/check-user + Method: POST + Data request: username + Response: status, message, exist + Mengecek apakah suatu username sudah terdaftar + +* #### /api/get-user + Method: POST + Data request: Authorization header + Response: status, data + Mengirim data user lengkap berdasarkan authorization token + +* #### /api/get-user-public + Method: POST + Data request: user_id + Response: status, data + Mengirim data user yang bersifat publik + +* #### /api/register + Method: POST + Data request: user_id, username, name, password, role + Response: status, message + Menambah user ke database pengguna + +* #### /api/unregister + Method: POST + Data request: Authorization header \[, user_id] + Response: status, message + Menghapus pengguna dengan user_id jika authorization header untuk admin, atau menghapus pengguna dalam authorization header jika user_id tidak diberikan. + +### Page + +* #### /api/page/get-top + Method: POST + Data request: year + Response: status, data, subdata + Mengirim data untuk halaman data paling atas dan subdata yang akan menjadi visualisasi + +* #### /api/page/get + Method: POST + Data request: page_id + Response: status, data, subdata + Mengirim data yang akan menjadi halaman dan subdata yang akan menjadi visualisasi + +### Comment and Reply + +* #### /api/comment/get + Method: POST + Data req: page_id + Response: status, data + Mengirim list comment dan reply-nya (jika ada) pada sebuah page dengan terurut waktu (yang terbaru paling awal) + +* #### /api/comment/get-unreplied + Method: POST + Data req: page_id + Response: status, data + Mengirim list comment yang belum direply pada page dengan terurut waktu (yang terbaru paling awal) + +* #### /api/comment/add + Method: POST + Data req: Authorization header, page_id, text + Response: status, message + Menambahkan sebuah comment dari user yang sesuai authorization pada sebuah page + +* #### /api/comment/add-reply + Method: POST + Data req: Authorization header, comment_id, text + Response: status, message + Menambahkan sebuah reply pada sebuah comment jika comment tersebut dalam wewenang user pada authorization + +### Administration + +* #### /api/admin/add-page + Method: POST + Data req: Authorization header, admin_id, page_id + Response: status, message + Menambah page yang diadministrasi seorang admin jika authorization level SUPERADMIN + +* #### /api/admin/remove-page + Method: POST + Data req: Authorization header, admin_id, page_id + Response: status, message + Mengurangi page yang diadministrasi seorang admin jika authorization level SUPERADMIN \ No newline at end of file