diff options
-rw-r--r-- | .gitignore | 7 | ||||
-rw-r--r-- | app.py | 71 | ||||
-rw-r--r-- | requirements.txt | 7 | ||||
-rw-r--r-- | static/styles/base.css | 11 | ||||
-rw-r--r-- | static/styles/join.css | 54 | ||||
-rw-r--r-- | static/styles/join_common.css | 44 | ||||
-rw-r--r-- | static/styles/join_form.css | 33 | ||||
-rw-r--r-- | views/base.html | 3 | ||||
-rw-r--r-- | views/join_form.html (renamed from views/join.html) | 8 | ||||
-rw-r--r-- | views/join_intro.html | 24 | ||||
-rw-r--r-- | views/join_success.html | 18 |
11 files changed, 198 insertions, 82 deletions
@@ -1,4 +1,9 @@ +# Secrets .env + # Cert files cert.pem -key.pem
\ No newline at end of file +key.pem + +# Database +thisisadatabasethatcontainsdata.db @@ -1,3 +1,4 @@ +from gevent import monkey; monkey.patch_all() # MUST BE FIRST IMPORT from bottle import Bottle, run, debug, static_file, request, redirect, response, HTTPError from bottle import jinja2_template as template from oauthlib.oauth2 import WebApplicationClient @@ -11,25 +12,29 @@ from bottle.ext import sqlite load_dotenv() CLIENT_ID = os.environ.get("CLIENT_ID") # DOTENV ligger paa discorden, repoet er publkic saa det -CLIENT_SECRET = os.environ.get("CLIENT_ID") # DOTENV PAHAHAH +CLIENT_SECRET = os.environ.get("CLIENT_SECRET") # DOTENV PAHAHAH REDIRECT_URI = "https://localhost:8080/callback" AUTH_BASE_URL = 'https://oauth.battle.net/authorize' TOKEN_URL = "https://oauth.battle.net/token" client = WebApplicationClient(CLIENT_ID) -db = sqlite3.connect("thisisadatabasethatcontainsdata.db") -db.execute(""" +DB_PATH = "thisisadatabasethatcontainsdata.db" + +connection = sqlite3.connect(DB_PATH) +cursor = connection.cursor() +cursor.executescript(""" CREATE TABLE IF NOT EXISTS applications ( - name VARCHAR(32), - role VARCHAR(32), - motivation TEXT - ) + username VARCHAR(12) NOT NULL, + preferredRole VARCHAR(6) NOT NULL, + motivation TEXT NOT NULL, + userId INTEGER UNIQUE NOT NULL + ); """) -db.commit() -db.close() +cursor.close() +connection.close() app = Bottle() -plugin = sqlite.Plugin(dbfile="thisisadatabasethatcontainsdata.db") +plugin = sqlite.Plugin(dbfile=DB_PATH) app.install(plugin) @app.route("/") @@ -37,6 +42,10 @@ app.install(plugin) def index(): return template("index") [email protected]("/join_intro.html") +def join_intro(): + return template("join_intro") + @app.route("/battle") def battle(): state = secrets.token_urlsafe(16) @@ -45,23 +54,32 @@ def battle(): return redirect(authorization_url) @app.route('/callback') -def callback(): +def join_form(): state = request.get_cookie('oauth_state') - code = request.query.get('code') oauth2_session = OAuth2Session(CLIENT_ID, state=state, redirect_uri=REDIRECT_URI) token_response = oauth2_session.fetch_token(TOKEN_URL, authorization_response=request.url, client_secret=CLIENT_SECRET) - return f'Access token: {token_response.get("access_token")}' + # Get the user ID of the just authenticated user. As per the API + # documentation, this should be used to identify users. + # + # See: https://develop.battle.net/documentation/guides/regionality-and-apis#:~:text=Developers%20should%20use%20an%20accountId + query_parameters = { + "region": "eu", + } + response = oauth2_session.get("https://oauth.battle.net/oauth/userinfo", params=query_parameters) + response.raise_for_status() + user_info = response.json() + user_id = user_info["id"] [email protected]("/join.html") -def join_form(): - return template("join") + # We pass the token retrieved here so it can be submitted with the rest of the application. + return template("join_form", user_id=user_id) [email protected]("/join.html", method="POST") [email protected]("/callback", method="POST") def join_submission(db: sqlite3.Connection): name = request.forms.get("name") preferred_role = request.forms.get("preferredRole") motivation = request.forms.get("motivation") + user_id = request.forms.get("userId") if name == None or name.strip() == "": raise HTTPError(400, "Namefield is empty or missing. ( warning: this is not good )") @@ -71,10 +89,21 @@ def join_submission(db: sqlite3.Connection): raise HTTPError(400, "Preferred role must be one of the options (DPS, Tank, Healer) ( idiot )") if motivation == None or motivation.strip() == "": raise HTTPError(400, "Motivitaion field is empty or missing.") - - db.execute("SELECT * FROM applications").fetchone() + if user_id == None or not user_id.isdigit(): + raise HTTPError(400, "Missing or invalid user id") + + try: + db.execute("INSERT INTO applications(username, preferredRole, motivation, userId) VALUES (?, ?, ?, ?)", (name, preferred_role, motivation, user_id)) + except sqlite3.IntegrityError as e: + print(e.sqlite_errorcode == sqlite3.SQLITE_CONSTRAINT_UNIQUE) + print(str(e)) + if e.sqlite_errorcode == sqlite3.SQLITE_CONSTRAINT_UNIQUE: + # The database (model) rejected the application because the unique constraint wasn't met! + raise HTTPError(400, "You've already submitted an application!") + else: + raise - db.execute("INSERT INTO applications(name, role, motivation) VALUES (?, ?, ?)", (name, preferred_role, motivation)) + return template("join_success") @app.route("/<type:re:styles|images>/<filename>") def server_static(type, filename): @@ -82,4 +111,4 @@ def server_static(type, filename): debug(True) run(app, host='localhost', port=8080, reloader=True, - server="waitress", keyfile="./pki/server.key", certfile="./pki/server.crt") + server="gevent", keyfile="./pki/server.key", certfile="./pki/server.crt") diff --git a/requirements.txt b/requirements.txt index 1544077..0fa0176 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,12 +1,17 @@ bottle==0.12.25 +bottle-sqlite==0.2.0 certifi==2024.2.2 charset-normalizer==3.3.2 -gunicorn==21.2.0 +gevent==24.2.1 +greenlet==3.0.3 idna==3.7 Jinja2==3.1.3 MarkupSafe==2.1.5 oauthlib==3.2.2 packaging==24.0 +python-dotenv==1.0.1 requests==2.31.0 requests-oauthlib==2.0.0 urllib3==2.2.1 +zope.event==5.0 +zope.interface==6.3 diff --git a/static/styles/base.css b/static/styles/base.css index f9b1ae6..2ae562e 100644 --- a/static/styles/base.css +++ b/static/styles/base.css @@ -60,3 +60,14 @@ footer { font-size: small; margin: 1rem; } + +/* Chill link styling */ +a { + /* dark and twisted color, lighter for contrast */ + color: #a27d7d; + text-decoration: underline; +} + +a:visited { + color: inherit; +} diff --git a/static/styles/join.css b/static/styles/join.css deleted file mode 100644 index f958aad..0000000 --- a/static/styles/join.css +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This file contains styles specific to the "join" view. The corresponding - * HTML/template file is `views/join.html`. - */ - -main { - width: 100%; - max-width: 500px; - margin: 2rem auto; -} - -.signup { - -} - -.signup__box { - margin-bottom: 1reM; -} - -.signup__label { - font-weight: bold; -} - -.signup__input { - width: 100%; - padding: .5rem; - border-radius: 5px; -} - -textarea.signup__input { - /* Remove UA's default monospace font */ - font-family: inherit; - - /* Horizontal resizing totally breaks our layout :( */ - resize: vertical; -} - -.signup__submit { - /* Horizontally center the element */ - display: block; - margin-inline: auto; - - /* Make it look dark and twisted */ - background-color: #6e1818; - color: white; - border: none; - border-radius: 500px; - padding: 1rem; - font-size: large; -} - -/* The usual dimming effect makes interaction feel more tactile */ -.signup__submit:hover { filter: brightness(90%); } -.signup__submit:active { filter: brightness(60%); }
\ No newline at end of file diff --git a/static/styles/join_common.css b/static/styles/join_common.css new file mode 100644 index 0000000..93bdb1d --- /dev/null +++ b/static/styles/join_common.css @@ -0,0 +1,44 @@ +/* + * This file contains styles specific to the "join" views. There are a couple + * of views which are all part of the same user flow and reuse some styles. The + * corresponding HTML/template files are `views/join_*.html`. + */ + +main { + /* Center align and keep width readable. */ + width: 100%; + max-width: 500px; + margin: 2rem auto; + +} + +/* Some pages are text heavy. Keep a nice line distance. */ +p { line-height: 1.5; } + +.button { + /* Horizontally center the element */ + display: block; + margin-inline: auto; + + /* Make it look dark and twisted */ + background-color: #6e1818; + color: white; + border: none; + border-radius: 500px; + padding: 1rem; + font-size: large; +} + +/* The usual dimming effect makes interaction feel more tactile */ +.button:hover { filter: brightness(90%); } +.button:active { filter: brightness(60%); } + +/* We use button-styles anchor-tags to preserve HTML semantics. */ +a.button { + /* Remove link styling */ + color: inherit; + text-decoration: none; + + /* Inline elements expand by default. */ + width: fit-content; +} diff --git a/static/styles/join_form.css b/static/styles/join_form.css new file mode 100644 index 0000000..f4ba0f1 --- /dev/null +++ b/static/styles/join_form.css @@ -0,0 +1,33 @@ +/* + * This file contains styles specific to the "join_form" view. The corresponding + * HTML/template file is `views/join_form.html`. + * + * See also the file `static/styles/join_common.css` which contains some styles + * which are reused among the "join_" group of views. + */ + +.signup { + +} + +.signup__box { + margin-bottom: 1reM; +} + +.signup__label { + font-weight: bold; +} + +.signup__input { + width: 100%; + padding: .5rem; + border-radius: 5px; +} + +textarea.signup__input { + /* Remove UA's default monospace font */ + font-family: inherit; + + /* Horizontal resizing totally breaks our layout :( */ + resize: vertical; +} diff --git a/views/base.html b/views/base.html index 9133090..3f65912 100644 --- a/views/base.html +++ b/views/base.html @@ -14,8 +14,7 @@ <ul class="navbar__links" role="navigation" aria-label="Main"> <li><a class="navbar__location" href="/index.html">About us</a></li> <li><a class="navbar__location" href="/history.html">History</a></li> - <li><a class="navbar__location" href="/join.html">Join</a></li> - <li><a class="navbar__location" href="/battle">Log in</a></li> + <li><a class="navbar__location" href="/join_intro.html">Join</a></li> </ul> </header> <main>{% block content %}{% endblock %}</main> diff --git a/views/join.html b/views/join_form.html index 784019c..440c993 100644 --- a/views/join.html +++ b/views/join_form.html @@ -1,7 +1,8 @@ {% extends "base.html" %} {% block head %} - <link rel="stylesheet" href="/styles/join.css"> + <link rel="stylesheet" href="/styles/join_common.css"> + <link rel="stylesheet" href="/styles/join_form.css"> <script type="module"> var dropdown = document.getElementById("preferredRole"); document.getElementById("applicationForm").addEventListener("submit", (event) => { @@ -15,6 +16,7 @@ {% block content %} <form method="POST" class="signup" id="applicationForm"> + <input type="hidden" name="userId" value="{{ user_id | e }}"> <div class="signup__box"> <label class="signup__label" for="name">Name</label> <p> @@ -51,7 +53,7 @@ <textarea required class="signup__input" id="motivation" name="motivation" rows="20" placeholder="I would like to join because..."></textarea> </div> <div> - <button class="signup__submit" type="submit">Submit</button> + <button class="button" type="submit">Submit</button> </div> </form> -{% endblock %}
\ No newline at end of file +{% endblock %} diff --git a/views/join_intro.html b/views/join_intro.html new file mode 100644 index 0000000..abea4fd --- /dev/null +++ b/views/join_intro.html @@ -0,0 +1,24 @@ +{% extends "base.html" %} + +{% block head %} + <link rel="stylesheet" href="/styles/join_common.css"> +{% endblock %} + +{% block content %} + <h1>Joining the Blind Guild</h1> + <p> + So you want to join the Blind Guild. + Be warned, + we are a very exclusive club + and we aren't actively seeking new members. + However, if you're a skilled player + and feel like you could help elevate the Blind Guild, + feel free to send us an application! + </p> + <p> + In order to sync up, we'll need you to connect your battle.net account. + Click the button below to sign in with your account. + Then you'll be taken to the application form. + </p> + <a class="button" href="/battle">Sign in</a> +{% endblock %} diff --git a/views/join_success.html b/views/join_success.html new file mode 100644 index 0000000..7e9dace --- /dev/null +++ b/views/join_success.html @@ -0,0 +1,18 @@ +{% extends "base.html" %} + +{% block head %} + <link rel="stylesheet" href="/styles/join_common.css"> +{% endblock %} + +{% block content %} + <h1>Application recieved</h1> + <p> + Your application was submitted successfully! + We'll review it and decide if you're cool enough to join our exclusive club. + Please note that it may take a few days for us to get to your application. + </p> + <p> + If you're accepted into our elite gang of high-tier gamers, + you'll receive an in-game invite to the Blind Guild. + </p> +{% endblock %} |