Initial version
This commit is contained in:
commit
73ef9bcfaa
16
README.md
Normal file
16
README.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Sample application for verifying attendance through a unique QR code
|
||||||
|
|
||||||
|
To run in a Python virtual environment, using **Flask**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkvenv flask
|
||||||
|
venv flask
|
||||||
|
pip install Flask Flask-QRcode Flask-UUID
|
||||||
|
```
|
||||||
|
|
||||||
|
To create an empty `database.db` sqlite file with the table described in
|
||||||
|
`schema.sql`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./init_db.py
|
||||||
|
```
|
||||||
BIN
__pycache__/main.cpython-310.pyc
Normal file
BIN
__pycache__/main.cpython-310.pyc
Normal file
Binary file not shown.
BIN
database.db
Normal file
BIN
database.db
Normal file
Binary file not shown.
10
html/index.html
Normal file
10
html/index.html
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Test</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Hi!</h1>
|
||||||
|
<p>OK.</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
12
init_db.py
Executable file
12
init_db.py
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import sqlite3
|
||||||
|
|
||||||
|
connection = sqlite3.connect('database.db')
|
||||||
|
|
||||||
|
with open('schema.sql') as f:
|
||||||
|
connection.executescript(f.read())
|
||||||
|
|
||||||
|
connection.commit()
|
||||||
|
connection.close()
|
||||||
|
|
||||||
60
main.py
Executable file
60
main.py
Executable file
@ -0,0 +1,60 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
import sqlite3
|
||||||
|
from datetime import datetime
|
||||||
|
from flask import Flask, request, render_template
|
||||||
|
from flask_uuid import FlaskUUID
|
||||||
|
from flask_qrcode import QRcode
|
||||||
|
# TODO: use sockets to broadcast when a hit to the expected uuid has been
|
||||||
|
# received, so that connected clients (the Chromium public browser) can
|
||||||
|
# refresh the qr code and update the list of users
|
||||||
|
# https://flask-socketio.readthedocs.io
|
||||||
|
|
||||||
|
app = Flask(__name__, static_folder='html')
|
||||||
|
FlaskUUID(app)
|
||||||
|
QRcode(app)
|
||||||
|
|
||||||
|
def get_db_connection():
|
||||||
|
conn = sqlite3.connect('database.db')
|
||||||
|
conn.row_factory = sqlite3.Row
|
||||||
|
return conn
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def show_qr_and_list():
|
||||||
|
# TODO: reject direct connections to server; allow access only via proxy
|
||||||
|
conn = get_db_connection()
|
||||||
|
data = conn.execute('SELECT * FROM hits ORDER BY id DESC LIMIT 10').fetchall()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
# TODO: next_ uuid should be in global state, to verify it when it's received
|
||||||
|
return render_template("template.html",
|
||||||
|
next_uuid=str(uuid.uuid1()),
|
||||||
|
hits=data)
|
||||||
|
|
||||||
|
@app.route('/<uuid:id>')
|
||||||
|
def catch_uuids(id):
|
||||||
|
ua = request.headers.get('User-Agent')
|
||||||
|
# TODO: use the Remote-User header that Authelia should set after authentication
|
||||||
|
# (check if "Authorization" in request.headers, or request.authorization)
|
||||||
|
time = datetime.now().strftime("%A %Y-%m-%d %H:%M:%S")
|
||||||
|
error = None
|
||||||
|
conn = get_db_connection()
|
||||||
|
existing = conn.execute(
|
||||||
|
'SELECT * FROM hits WHERE uuid = ?', (str(id),)).fetchone()
|
||||||
|
# TODO: verify that the uuid was generated by us (otherwise any uuid,
|
||||||
|
# like one generated by the user, would be accepted)
|
||||||
|
if existing:
|
||||||
|
error = 'ALREADY_USED'
|
||||||
|
else:
|
||||||
|
data = conn.execute(
|
||||||
|
'SELECT * FROM hits WHERE user = ? ORDER BY id DESC LIMIT 10', (ua,)
|
||||||
|
).fetchall()
|
||||||
|
conn.execute("INSERT INTO hits (uuid, user) VALUES (?, ?)", (str(id), ua))
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
return render_template('thanks.html', user=ua, time=time, error=error, hits=data)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run()
|
||||||
10
requirements.txt
Normal file
10
requirements.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
click==8.1.3
|
||||||
|
Flask==2.1.3
|
||||||
|
Flask-QRcode==3.1.0
|
||||||
|
Flask-UUID==0.2
|
||||||
|
itsdangerous==2.1.2
|
||||||
|
Jinja2==3.1.2
|
||||||
|
MarkupSafe==2.1.1
|
||||||
|
Pillow==9.2.0
|
||||||
|
qrcode==7.3.1
|
||||||
|
Werkzeug==2.2.1
|
||||||
9
schema.sql
Normal file
9
schema.sql
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
DROP TABLE IF EXISTS hits;
|
||||||
|
|
||||||
|
CREATE TABLE hits (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
uuid TEXT NOT NULL,
|
||||||
|
user TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
18
templates/template.html
Normal file
18
templates/template.html
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Example</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>QR Code:</h1>
|
||||||
|
<p>For URL <a href="{{ request.url_root + next_uuid }}">{{ request.url_root + next_uuid }}</a></p>
|
||||||
|
<img src="{{ qrcode(request.url_root + next_uuid, box_size=12, border=5) }}">
|
||||||
|
<h1>Last 10 users:</h1>
|
||||||
|
<li>
|
||||||
|
{% for hit in hits %}
|
||||||
|
<ul>{{ hit['created'] }}, {{ hit['user'] }}</ul>
|
||||||
|
{% endfor %}
|
||||||
|
</li>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
23
templates/thanks.html
Normal file
23
templates/thanks.html
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Example</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{% if error %}
|
||||||
|
<h1>Error</h1>
|
||||||
|
<p>Code has been used already.</p>
|
||||||
|
{% else %}
|
||||||
|
<h1>Thanks</h1>
|
||||||
|
<p><strong>At {{ time }}</strong>,</p>
|
||||||
|
<p>Registered (entrance? exit?) for user {{ user }} .</p>
|
||||||
|
<h2>Last 10 registers for {{ user }}:</h2>
|
||||||
|
<li>
|
||||||
|
{% for hit in hits %}
|
||||||
|
<ul>{{ hit['created'] }}</ul>
|
||||||
|
{% endfor %}
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user