From 0795579902a5268acff13513dfcc8781934bad60 Mon Sep 17 00:00:00 2001 From: "NGnius (Graham)" Date: Wed, 11 Sep 2019 16:29:31 -0400 Subject: [PATCH] Implement update check --- requirements.txt | 1 + rxsmserver/__init__.py | 52 ++++++++++++++++++++++++--------- rxsmserver/make-token.py | 8 +++++ rxsmserver/server-tester.py | 34 +++++++++++++++++++++ scripts/install-requirements.sh | 2 +- scripts/test.sh | 4 +++ 6 files changed, 87 insertions(+), 14 deletions(-) create mode 100644 rxsmserver/make-token.py create mode 100644 rxsmserver/server-tester.py create mode 100755 scripts/test.sh diff --git a/requirements.txt b/requirements.txt index 7e10602..30692b7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ flask +requests diff --git a/rxsmserver/__init__.py b/rxsmserver/__init__.py index 0d1d863..72e6b0c 100644 --- a/rxsmserver/__init__.py +++ b/rxsmserver/__init__.py @@ -6,10 +6,10 @@ from os import getcwd from threading import get_ident from time import time -database_path = "rxsm-update.db" +database_path = 'rxsm-update.db' database_connections = dict() -app = Flask("rxsm-update-server") +app = Flask('rxsm-update-server') def get_or_create_connection(): global database_connections, database_path @@ -24,46 +24,72 @@ def create_or_modify_release(): req_json = request.get_json(force=True) token = req_json['token'] version = req_json['version'] + platform = req_json['platform'] url = req_json['url'] except: return jsonify({'status':400, 'reason':'Invalid request; missing parameter or invalid JSON'}), 400 - with open(".token", "r") as f: + with open('.token', 'r') as f: master_token = f.read() if sha512(token.encode()).hexdigest() != master_token: return jsonify({'status':403, 'reason':'Permission denied; invalid token'}), 403 db_connection = get_or_create_connection() cursor = db_connection.cursor() - cursor.execute("SELECT version FROM releases WHERE version=?", (version,)) + cursor.execute('SELECT version FROM releases WHERE version=? AND platform=?', (version, platform)) result = cursor.fetchone() - operation = "" + operation = '' if result is None: - cursor.execute("INSERT INTO releases (version, url, created_date) VALUES (?,?,?)", (version, url, int(time()))) - operation = "create" + cursor.execute('INSERT INTO releases (version, platform, url, created_date) VALUES (?,?,?,?)', (version, platform, url, int(time()))) + operation = 'create' else: - cursor.execute("UPDATE releases SET url=?, created_date=? WHERE version=?", (url, int(time()), version)) - operation = "update" + cursor.execute('UPDATE releases SET url=?, created_date=? WHERE version=? and platform=?', (url, int(time()), version, platform)) + operation = 'update' db_connection.commit() return jsonify({'status':200, 'reason':'Version %s %sd successfully' % (version, operation)}), 200 +@app.route('/db', methods=['GET']) @app.route('/database', methods=['GET']) def download_database(): - return send_file(database_path, cache_timeout=1, as_attachment=True, attachment_filename="rxsm.sqlite") + return send_file(database_path, cache_timeout=1, as_attachment=True, attachment_filename='rxsm.sqlite') + +@app.route('/update', methods=['POST']) +@app.route('/check-for-update', methods=['POST']) +def check_for_update(): + start = time() + collect_anonymous_stats = str(request.headers.get('DNT')).strip() != '1' + # TODO: add hit counter + try: + req_json = request.get_json(force=True) + current_version = req_json['version'] + platform = req_json['platform'] + except: + return jsonify({'status':400, 'reason':'Invalid request; missing parameter or invalid JSON'}), 400 + db_connection = get_or_create_connection() + cursor = db_connection.cursor() + cursor.execute('SELECT version, url FROM releases WHERE platform=? ORDER BY created_date DESC', (platform,)) + result = cursor.fetchone() + if result is None: + return jsonify({'status':404, 'reason':'Platform not found (%ss)' % (time()-start), 'url':'', 'out-of-date':False}), 404 + elif result[0] != current_version: + return jsonify({'status':200, 'reason':'Ok (%ss)' % (time()-start), 'url':result[1], 'out-of-date':True}), 200 + else: + return jsonify({'status':200, 'reason':'Ok (%ss)' % (time()-start), 'url':result[1], 'out-of-date':False}), 200 # always try to create db table(s) conn = get_or_create_connection() cursor = conn.cursor() cursor.execute(\ - """CREATE TABLE IF NOT EXISTS releases ( + '''CREATE TABLE IF NOT EXISTS releases ( id INTEGER PRIMARY KEY, version TEXT UNIQUE NOT NULL, url TEXT NOT NULL, + platform TEXT NOT NULL, created_date INTEGER NOT NULL - )""" ) + )''' ) conn.commit() if __name__ == '__main__': - print("Working directory: " + getcwd()) + print('Working directory: ' + getcwd()) app.run(host='0.0.0.0', port=9080, threaded=True) diff --git a/rxsmserver/make-token.py b/rxsmserver/make-token.py new file mode 100644 index 0000000..7daf9fb --- /dev/null +++ b/rxsmserver/make-token.py @@ -0,0 +1,8 @@ +# Created 2019-09-11 by NGnius + +from hashlib import sha512 + +password = input('password: ') + +with open('.token', 'w') as f: + f.write(sha512(password.encode()).hexdigest()) diff --git a/rxsmserver/server-tester.py b/rxsmserver/server-tester.py new file mode 100644 index 0000000..fe319f4 --- /dev/null +++ b/rxsmserver/server-tester.py @@ -0,0 +1,34 @@ +# Created 2019-09-11 by NGnius + +import requests + +# this is mainly smoke-testing since +# writing complete and proper unit testing for a single-file server is out of scope + +# /release test +print('Testing /release') +raw_token = input('token password: ') +release_resp = requests.post('http://localhost:9080/release', json={'token':raw_token, 'version':'v42.0.0a', 'platform':'fake-i386', 'url':'https://google.com'}) +print(release_resp.json()) +assert release_resp.status_code == 200 +print('Release Ok') + +# /database test +print('Testing /database') +database_resp = requests.get('http://localhost:9080/database') +assert database_resp.status_code == 200 +print('Database Ok') + +# /update test +print('Testing /update existing platform') +update_resp = requests.post('http://localhost:9080/update', json={'version':'v0.42.0a', 'platform':'fake-i386'}) +print(update_resp.json()) +assert update_resp.status_code == 200 +assert update_resp.json()['url'] == 'https://google.com' + +print('Testing /update missing platform') +update_resp = requests.post('http://localhost:9080/update', json={'version':'v0.42.0a', 'platform':'fake-amd64'}) +print(update_resp.json()) +assert update_resp.status_code == 404 +assert update_resp.json()['url'] == '' +print('Update Ok') diff --git a/scripts/install-requirements.sh b/scripts/install-requirements.sh index 8625ba6..22f0160 100755 --- a/scripts/install-requirements.sh +++ b/scripts/install-requirements.sh @@ -1,3 +1,3 @@ #!/bin/sh -sudo python3 -m install -r requirements.txt +python3 -m pip install -r requirements.txt diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 0000000..db1b196 --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +python3 rxsmserver/make-token.py +python3 rxsmserver/server-tester.py