From f2f734194c03dfff2024cf417c502515ddb7a855 Mon Sep 17 00:00:00 2001 From: jwansek Date: Sat, 21 May 2022 22:38:52 +0100 Subject: Added running as an API --- API/app.py | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 API/app.py (limited to 'API/app.py') diff --git a/API/app.py b/API/app.py new file mode 100644 index 0000000..e4cb769 --- /dev/null +++ b/API/app.py @@ -0,0 +1,109 @@ +from paste.translogger import TransLogger +from waitress import serve +import configparser +import werkzeug +import helpers +import flask +import sys +import os + +# os.environ["UPLOADS_DIR"] = "/media/veracrypt1/Edencloud/UniStuff/3.0 - CMP 3rd Year Project/Smarker/API/.uploads" + +sys.path.insert(1, os.path.join("..", "Smarker")) +import database + +app = flask.Flask(__name__) +app.config['UPLOAD_FOLDER'] = ".uploads" +API_CONF = configparser.ConfigParser() +API_CONF.read("api.conf") + + +@app.route("/api/helloworld") +def helloworld(): + """GET ``/api/helloworld`` + + Returns a friendly message to check the server is working + """ + return flask.jsonify({"hello": "world!"}) + +@app.route("/api/mark", methods = ["post"]) +def mark(): + """POST ``/api/mark`` + + Expects to be a POST request of ``Content-Type: multipart/form-data``. + + * Expects a valid API key under the key ``key`` + + * Expects an assessment name under the key ``assessment`` + + * Expects a submission zip file under the key ``zip`` + + * File dependences can be added with keys prefixed with ``filedep``, but a corrisponding key must also be present with the location of this file in the sandboxed environment: e.g. ``-F "filedep1=@../../aDependency.txt" -F "aDependency.txt=/aDependency.txt"`` + + Returns a report in the JSON format. + """ + # try: + assessment_name = flask.request.form.get('assessment') + api_key = flask.request.form.get('key') + files = [] + + with database.SmarkerDatabase( + host = API_CONF.get("mysql", "host"), + user = API_CONF.get("mysql", "user"), + passwd = API_CONF.get("mysql", "passwd"), + db = "Smarker", + port = API_CONF.getint("mysql", "port")) as db: + + if db.valid_apikey(api_key): + f = flask.request.files["zip"] + zip_name = f.filename + f.save(os.path.join(".uploads/", f.filename)) + # even though this is inside docker, we are accessing the HOST docker daemon + # so we have to pass through the HOST location for volumes... very annoying I know + # so we set this environment variable + # https://serverfault.com/questions/819369/mounting-a-volume-with-docker-in-docker + files.append("%s:/tmp/%s" % (os.path.join(os.environ["UPLOADS_DIR"], zip_name), zip_name)) + + for file_dep in flask.request.files.keys(): + if file_dep.startswith("filedep"): + f = flask.request.files[file_dep] + f.save(os.path.join(".uploads/", f.filename)) + dep_name = os.path.split(f.filename)[-1] + client_loc = flask.request.form.get(dep_name) + if client_loc is None: + return flask.abort(flask.Response("You need to specify a location to put file dependency '%s' e.g. '%s=/%s'" % (dep_name, dep_name, dep_name), status=500)) + + files.append("%s:%s" % (os.path.join(os.environ["UPLOADS_DIR"], dep_name), client_loc)) + + + try: + return flask.jsonify(helpers.run_smarker_simple(db, zip_name, assessment_name, files)) + except Exception as e: + flask.abort(flask.Response(str(e), status=500)) + else: + flask.abort(403) + # except (KeyError, TypeError, ValueError): + # flask.abort(400) + + +if __name__ == "__main__": + try: + if sys.argv[1] == "--production": + serve( + TransLogger(app), + host = API_CONF.get("production", "host"), + port = API_CONF.getint("production", "port"), + threads = 32 + ) + else: + app.run( + host = API_CONF.get("testing", "host"), + port = API_CONF.getint("testing", "port"), + debug = True + ) + except IndexError: + app.run( + host = API_CONF.get("testing", "host"), + port = API_CONF.getint("testing", "port"), + debug = True + ) \ No newline at end of file -- cgit v1.2.3