summaryrefslogtreecommitdiff
path: root/web-server/main.py
diff options
context:
space:
mode:
authorGeorgios Andreadis <info@gandreadis.com>2020-06-29 16:05:23 +0200
committerFabian Mastenbroek <mail.fabianm@gmail.com>2020-08-24 16:18:36 +0200
commit4f9a40abdc7836345113c047f27fcc96800cb3f5 (patch)
treee443d14e34a884b1a4d9c549f81d51202eddd5f7 /web-server/main.py
parentcd5f7bf3a72913e1602cb4c575e61ac7d5519be0 (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.py186
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)