diff options
| author | Georgios Andreadis <info@gandreadis.com> | 2020-06-29 16:05:23 +0200 |
|---|---|---|
| committer | Fabian Mastenbroek <mail.fabianm@gmail.com> | 2020-08-24 16:18:36 +0200 |
| commit | 4f9a40abdc7836345113c047f27fcc96800cb3f5 (patch) | |
| tree | e443d14e34a884b1a4d9c549f81d51202eddd5f7 /web-server/main.py | |
| parent | cd5f7bf3a72913e1602cb4c575e61ac7d5519be0 (diff) | |
Prepare web-server repository for monorepo
This change prepares the web-server Git repository for the monorepo residing at
https://github.com/atlarge-research.com/opendc. To accomodate for this, we
move all files into a web-server subdirectory.
Diffstat (limited to 'web-server/main.py')
| -rw-r--r-- | web-server/main.py | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/web-server/main.py b/web-server/main.py new file mode 100644 index 00000000..cd9394d5 --- /dev/null +++ b/web-server/main.py @@ -0,0 +1,186 @@ +import flask_socketio +import json +import os +import sys +import traceback +import urllib.request +from flask import Flask, request, send_from_directory, jsonify +from flask_compress import Compress +from oauth2client import client, crypt +from flask_cors import CORS + +from opendc.models_old.user import User +from opendc.util import rest, path_parser, database +from opendc.util.exceptions import AuthorizationTokenError, RequestInitializationError + +TEST_MODE = "OPENDC_FLASK_TESTING" in os.environ + +if TEST_MODE: + STATIC_ROOT = os.curdir +else: + database.DB.initialize_database(user=os.environ['OPENDC_DB_USERNAME'], + password=os.environ['OPENDC_DB_PASSWORD'], + database=os.environ['OPENDC_DB'], + host='localhost') + STATIC_ROOT = os.path.join(os.environ['OPENDC_ROOT_DIR'], 'opendc-frontend', 'build') + +FLASK_CORE_APP = Flask(__name__, static_url_path='', static_folder=STATIC_ROOT) +FLASK_CORE_APP.config['SECRET_KEY'] = os.environ['OPENDC_FLASK_SECRET'] +if 'localhost' in os.environ['OPENDC_SERVER_BASE_URL']: + CORS(FLASK_CORE_APP) + +compress = Compress() +compress.init_app(FLASK_CORE_APP) + +if 'OPENDC_SERVER_BASE_URL' in os.environ or 'localhost' in os.environ['OPENDC_SERVER_BASE_URL']: + SOCKET_IO_CORE = flask_socketio.SocketIO(FLASK_CORE_APP, cors_allowed_origins="*") +else: + SOCKET_IO_CORE = flask_socketio.SocketIO(FLASK_CORE_APP) + + +@FLASK_CORE_APP.errorhandler(404) +def page_not_found(e): + return send_from_directory(STATIC_ROOT, 'index.html') + + +@FLASK_CORE_APP.route('/tokensignin', methods=['POST']) +def sign_in(): + """Authenticate a user with Google sign in""" + + try: + token = request.form['idtoken'] + except KeyError: + return 'No idtoken provided', 401 + + try: + idinfo = client.verify_id_token(token, os.environ['OPENDC_OAUTH_CLIENT_ID']) + + if idinfo['aud'] != os.environ['OPENDC_OAUTH_CLIENT_ID']: + raise crypt.AppIdentityError('Unrecognized client.') + + if idinfo['iss'] not in ['accounts.google.com', 'https://accounts.google.com']: + raise crypt.AppIdentityError('Wrong issuer.') + except ValueError: + url = "https://www.googleapis.com/oauth2/v3/tokeninfo?id_token={}".format(token) + req = urllib.request.Request(url) + response = urllib.request.urlopen(url=req, timeout=30) + res = response.read() + idinfo = json.loads(res) + except crypt.AppIdentityError as e: + return 'Did not successfully authenticate' + + user = User.from_google_id(idinfo['sub']) + + data = {'isNewUser': not user.exists()} + + if user.exists(): + data['userId'] = user.id + + return jsonify(**data) + + +@FLASK_CORE_APP.route('/api/<string:version>/<path:endpoint_path>', methods=['GET', 'POST', 'PUT', 'DELETE']) +def api_call(version, endpoint_path): + """Call an API endpoint directly over HTTP.""" + + # Get path and parameters + (path, path_parameters) = path_parser.parse(version, endpoint_path) + + query_parameters = request.args.to_dict() + for param in query_parameters: + try: + query_parameters[param] = int(query_parameters[param]) + except: + pass + + try: + body_parameters = json.loads(request.get_data()) + except: + body_parameters = {} + + # Create and call request + (req, response) = _process_message({ + 'id': 0, + 'method': request.method, + 'parameters': { + 'body': body_parameters, + 'path': path_parameters, + 'query': query_parameters + }, + 'path': path, + 'token': request.headers.get('auth-token') + }) + + print( + f'HTTP:\t{req.method} to `/{req.path}` resulted in {response.status["code"]}: {response.status["description"]}') + sys.stdout.flush() + + flask_response = jsonify(json.loads(response.to_JSON())) + flask_response.status_code = response.status['code'] + return flask_response + + +@FLASK_CORE_APP.route('/my-auth-token') +def serve_web_server_test(): + """Serve the web server test.""" + return send_from_directory(STATIC_ROOT, 'index.html') + + +@FLASK_CORE_APP.route('/') +@FLASK_CORE_APP.route('/simulations') +@FLASK_CORE_APP.route('/simulations/<path:simulation_id>') +@FLASK_CORE_APP.route('/simulations/<path:simulation_id>/experiments') +@FLASK_CORE_APP.route('/simulations/<path:simulation_id>/experiments/<path:experiment_id>') +@FLASK_CORE_APP.route('/profile') +def serve_index(simulation_id=None, experiment_id=None): + return send_from_directory(STATIC_ROOT, 'index.html') + + +@SOCKET_IO_CORE.on('request') +def receive_message(message): + """"Receive a SocketIO request""" + (req, res) = _process_message(message) + + print(f'Socket:\t{req.method} to `/{req.path}` resulted in {res.status["code"]}: {res.status["description"]}') + sys.stdout.flush() + + flask_socketio.emit('res', res.to_JSON(), json=True) + + +def _process_message(message): + """Process a request message and return the response.""" + + try: + req = rest.Request(message) + res = req.process() + + return req, res + + except AuthorizationTokenError: + res = rest.Response(401, 'Authorization error') + res.id = message['id'] + + except RequestInitializationError as e: + res = rest.Response(400, str(e)) + res.id = message['id'] + + if not 'method' in message: + message['method'] = 'UNSPECIFIED' + if not 'path' in message: + message['path'] = 'UNSPECIFIED' + + except Exception: + res = rest.Response(500, 'Internal server error') + if 'id' in message: + res.id = message['id'] + traceback.print_exc() + + req = rest.Request() + req.method = message['method'] + req.path = message['path'] + + return req, res + + +if __name__ == '__main__': + SOCKET_IO_CORE.run(FLASK_CORE_APP, host='0.0.0.0', port=8081) |
