Add basic website
This commit is contained in:
parent
eec6919642
commit
f98437c636
6 changed files with 433 additions and 4 deletions
42
frontend/assets/main.css
Normal file
42
frontend/assets/main.css
Normal file
|
@ -0,0 +1,42 @@
|
|||
.card {
|
||||
border: 0;
|
||||
box-shadow: 0 .25rem 1rem rgba(48,55,66,.15);
|
||||
margin-top: 10px;
|
||||
|
||||
display: none;
|
||||
}
|
||||
|
||||
.card:target {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#tracker > .card-footer > a {
|
||||
float: right;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
#keyholder > .card-footer > a {
|
||||
float: left;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
#keyholder > .card-footer > button {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#has-key {
|
||||
color: #1ca847;
|
||||
}
|
||||
|
||||
#input-pass + p {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#input-pass.is-error + p {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#made-by-footer {
|
||||
text-align: center;
|
||||
margin-top: 10px;
|
||||
}
|
292
frontend/assets/main.js
Normal file
292
frontend/assets/main.js
Normal file
|
@ -0,0 +1,292 @@
|
|||
// === Core functions ===
|
||||
var api_base = "http://localhost:5000";
|
||||
var app_token = null;
|
||||
|
||||
function do_request(method, url, data, onsuccess, onerror) {
|
||||
var req = new XMLHttpRequest();
|
||||
req.open(method, window.encodeURI(api_base + url), true);
|
||||
req.onload = function() { onsuccess(this); }
|
||||
req.setRequestHeader("X-Auth-Token", app_token);
|
||||
req.onerror = function() { onerror(this); }
|
||||
if (data === null) {
|
||||
req.send();
|
||||
} else {
|
||||
req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
|
||||
req.send(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// == Events ===
|
||||
function oninit() {
|
||||
ui_keyholder_loading();
|
||||
|
||||
if (window.location.href.search("#tracker") < 0 &&
|
||||
window.location.href.search("#keyholder") < 0)
|
||||
window.location.href = "#tracker"
|
||||
|
||||
app_token = _get_token();
|
||||
|
||||
if (window.location.href.search("#keyholder") > 0)
|
||||
keyholder_onload();
|
||||
update_key_status();
|
||||
load_saved_tracker_data();
|
||||
|
||||
document.getElementById("input-contact").addEventListener("keyup", function(e) {
|
||||
if (e.key === "Enter")
|
||||
tracker_onsend();
|
||||
});
|
||||
document.getElementById("input-pass").addEventListener("keyup", function(e) {
|
||||
if (e.key === "Enter")
|
||||
password_submit();
|
||||
});
|
||||
}
|
||||
|
||||
function tracker_onsend(e) {
|
||||
ui_tracker_noerror();
|
||||
|
||||
var name = document.getElementById("input-name").value;
|
||||
var contact = document.getElementById("input-contact").value;
|
||||
var save_data = document.getElementById("input-tracker-save").checked;
|
||||
|
||||
if (name.length < 3 || contact.length < 3) {
|
||||
ui_tracker_error("Name und Kontakt müssen mindestens 3 Zeichen lang sein")
|
||||
return;
|
||||
}
|
||||
|
||||
if (app_token === null) {
|
||||
ui_tracker_error("Interner Fehler: kein Token. Bitte stelle sicher, dass du diese Seite durch Scannen den QR Codes erreicht hast");
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById("btn-tracker").classList.add("loading");
|
||||
do_request(
|
||||
"POST",
|
||||
"/claim",
|
||||
"name="+window.encodeURI(name)+"&contact="+window.encodeURI(contact),
|
||||
function(r) {
|
||||
document.getElementById("btn-tracker").classList.remove("loading");
|
||||
if (r.status == 200) {
|
||||
localStorage.setItem("last_cid", r.response);
|
||||
if (save_data) {
|
||||
localStorage.setItem("name", name);
|
||||
localStorage.setItem("contact", contact);
|
||||
} else {
|
||||
localStorage.removeItem("name");
|
||||
localStorage.removeItem("contact");
|
||||
}
|
||||
update_key_status();
|
||||
} else {
|
||||
ui_tracker_error("Fehlgeschlagen: Status code " + r.status);
|
||||
console.log("Claim request failed (Status code)");
|
||||
console.log(r);
|
||||
}
|
||||
},
|
||||
function(r) {
|
||||
document.getElementById("btn-tracker").classList.remove("loading");
|
||||
ui_tracker_error("Fehlgeschlagen: Bitte überprüfe deine Internetverbindung");
|
||||
console.log("Claim request failed");
|
||||
console.log(r);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function keyholder_onload() {
|
||||
ui_password_noerror();
|
||||
ui_keyholder_noerror();
|
||||
|
||||
do_request(
|
||||
"GET",
|
||||
"/keyholder",
|
||||
null,
|
||||
function(r) {
|
||||
if (r.status == 200) {
|
||||
ui_show_keyholder(JSON.parse(r.response));
|
||||
} else if (r.status == 403) {
|
||||
ui_request_password();
|
||||
} else {
|
||||
ui_keyholder_error("Fehlgeschlagen: Status code " + r.status);
|
||||
console.log("Keyholder request failed (Status code)");
|
||||
console.log(r);
|
||||
}
|
||||
},
|
||||
function(r) {
|
||||
ui_keyholder_error("Fehlgeschlagen: Bitte überprüfe deine Internetverbindung");
|
||||
console.log("Keyholder request failed");
|
||||
console.log(r);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function password_submit() {
|
||||
ui_password_noerror();
|
||||
ui_keyholder_noerror();
|
||||
|
||||
var pass = document.getElementById("input-pass").value;
|
||||
var save_token = document.getElementById("input-pass-save").checked;
|
||||
|
||||
document.getElementById("btn-send-pw").classList.add("loading");
|
||||
do_request(
|
||||
"POST",
|
||||
"/auth",
|
||||
"pass=" + window.encodeURI(pass),
|
||||
function(r) {
|
||||
document.getElementById("btn-send-pw").classList.remove("loading");
|
||||
if (r.status == 200) {
|
||||
app_token = r.response;
|
||||
if (save_token)
|
||||
localStorage.setItem("token", app_token);
|
||||
else
|
||||
localStorage.removeItem("token");
|
||||
|
||||
ui_keyholder_loading();
|
||||
keyholder_onload();
|
||||
|
||||
} else if (r.status == 401) {
|
||||
ui_password_error();
|
||||
} else {
|
||||
ui_keyholder_error("Fehlgeschlagen: Status code " + r.status);
|
||||
console.log("Auth request failed (Status code)");
|
||||
console.log(r);
|
||||
}
|
||||
},
|
||||
function(r) {
|
||||
document.getElementById("btn-send-pw").classList.remove("loading");
|
||||
ui_keyholder_error("Fehlgeschlagen: Bitte überprüfe deine Internetverbindung");
|
||||
console.log("Auth request failed");
|
||||
console.log(r);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// === Helper functions ===
|
||||
function update_key_status() {
|
||||
if (localStorage.getItem("last_cid") !== null) {
|
||||
do_request(
|
||||
"GET",
|
||||
"/status/" + localStorage.getItem("last_cid"),
|
||||
null,
|
||||
function(r) {
|
||||
if (r.status == 200 && r.response == "latest")
|
||||
ui_set_has_key();
|
||||
else
|
||||
ui_set_has_not_key();
|
||||
},
|
||||
function(r) { console.log("Status request failed"); console.log(r) }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function load_saved_tracker_data() {
|
||||
if (localStorage.getItem("name") !== null)
|
||||
document.getElementById("input-name").value = localStorage.getItem("name");
|
||||
if (localStorage.getItem("contact") !== null)
|
||||
document.getElementById("input-contact").value = localStorage.getItem("contact");
|
||||
}
|
||||
|
||||
function _get_token() {
|
||||
if (localStorage.getItem("token") === null) {
|
||||
// Very dirty parsing as of now
|
||||
var params_str = window.location.search;
|
||||
if (params_str.search("&") >= 0)
|
||||
return null;
|
||||
|
||||
var parts = params_str.split("token=");
|
||||
if (parts.length != 2)
|
||||
return null
|
||||
else
|
||||
return parts[1];
|
||||
} else {
|
||||
return localStorage.getItem("token");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === Ugly UI manipulation code ===
|
||||
function ui_set_has_key() {
|
||||
document.getElementById("has-key").style.display = "inline"
|
||||
document.getElementById("btn-tracker").disabled = true
|
||||
document.getElementById("input-name").disabled = true
|
||||
document.getElementById("input-contact").disabled = true
|
||||
document.getElementById("input-tracker-save").disabled = true
|
||||
document.getElementById("tracker-formgroup").style.opacity = 0.5
|
||||
}
|
||||
|
||||
function ui_set_has_not_key() {
|
||||
document.getElementById("has-key").style.display = "none"
|
||||
document.getElementById("btn-tracker").disabled = false
|
||||
document.getElementById("input-name").disabled = false
|
||||
document.getElementById("input-contact").disabled = false
|
||||
document.getElementById("input-tracker-save").disabled = false
|
||||
document.getElementById("tracker-formgroup").style.opacity = 1
|
||||
}
|
||||
|
||||
function ui_keyholder_loading() {
|
||||
document.getElementById("keyholder-loading").style.display = "block"
|
||||
document.getElementById("btn-send-pw").style.display = "none"
|
||||
document.getElementById("btn-update").style.display = "none"
|
||||
document.getElementById("pass-formgroup").style.display = "none"
|
||||
document.getElementById("keyholder-table-container").style.display = "none"
|
||||
}
|
||||
|
||||
function ui_request_password() {
|
||||
document.getElementById("keyholder-loading").style.display = "none"
|
||||
document.getElementById("btn-send-pw").style.display = "block"
|
||||
document.getElementById("btn-update").style.display = "none"
|
||||
document.getElementById("pass-formgroup").style.display = "block"
|
||||
document.getElementById("keyholder-table-container").style.display = "none"
|
||||
}
|
||||
|
||||
function ui_show_keyholder(keyholder) {
|
||||
document.getElementById("keyholder-tbody").innerHTML = "";
|
||||
for (var i = 0; i < keyholder.length; i++) {
|
||||
var date = new Date(keyholder[i]["timestamp"] * 1000);
|
||||
|
||||
var tr = document.createElement("tr");
|
||||
if (i > 0)
|
||||
tr.style.color = "#aaa";
|
||||
var td_name = document.createElement("td");
|
||||
var td_contact = document.createElement("td");
|
||||
var td_timestamp = document.createElement("td");
|
||||
td_name.appendChild(document.createTextNode(keyholder[i]["name"]));
|
||||
td_contact.appendChild(document.createTextNode(keyholder[i]["contact"]));
|
||||
td_timestamp.appendChild(document.createTextNode(date.toLocaleString()));
|
||||
tr.appendChild(td_name);
|
||||
tr.appendChild(td_contact);
|
||||
tr.appendChild(td_timestamp);
|
||||
document.getElementById("keyholder-tbody").appendChild(tr);
|
||||
}
|
||||
|
||||
document.getElementById("keyholder-loading").style.display = "none"
|
||||
document.getElementById("btn-send-pw").style.display = "none"
|
||||
document.getElementById("btn-update").style.display = "block"
|
||||
document.getElementById("pass-formgroup").style.display = "none"
|
||||
document.getElementById("keyholder-table-container").style.display = "block"
|
||||
}
|
||||
|
||||
function ui_tracker_error(msg) {
|
||||
document.getElementById("tracker-error").innerHTML = msg;
|
||||
document.getElementById("tracker-error").style.display = "block";
|
||||
}
|
||||
|
||||
function ui_tracker_noerror(msg) {
|
||||
document.getElementById("tracker-error").style.display = "none";
|
||||
}
|
||||
|
||||
function ui_keyholder_error(msg) {
|
||||
document.getElementById("keyholder-error").innerHTML = msg;
|
||||
document.getElementById("keyholder-error").style.display = "block";
|
||||
}
|
||||
|
||||
function ui_keyholder_noerror(msg) {
|
||||
document.getElementById("keyholder-error").style.display = "none";
|
||||
}
|
||||
|
||||
function ui_password_error() {
|
||||
document.getElementById("input-pass").classList.add("is-error")
|
||||
}
|
||||
|
||||
function ui_password_noerror() {
|
||||
document.getElementById("input-pass").classList.remove("is-error")
|
||||
}
|
1
frontend/assets/spectre-icons.min.css
vendored
Normal file
1
frontend/assets/spectre-icons.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
frontend/assets/spectre.min.css
vendored
Normal file
1
frontend/assets/spectre.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
82
frontend/index.html
Normal file
82
frontend/index.html
Normal file
|
@ -0,0 +1,82 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title>Schlüsseltracker</title>
|
||||
|
||||
<link rel="stylesheet" href="assets/spectre.min.css">
|
||||
<link rel="stylesheet" href="assets/spectre-icons.min.css">
|
||||
<link rel="stylesheet" href="assets/main.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container grid-xs">
|
||||
<div class="hide-xs" style="height: 70px"></div>
|
||||
<div class="card" id="tracker">
|
||||
<div class="card-header">
|
||||
<div class="card-title h5">Schlüsseltracker</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<span id="has-key" style="display: none"><i class="icon icon-check"></i> Der Schlüssel ist aktuell auf dich eingetragen</span>
|
||||
<div class="form-group" id="tracker-formgroup">
|
||||
<label class="form-label" for="input-name">Name</label>
|
||||
<input class="form-input" type="text" id="input-name" placeholder="Name">
|
||||
<label class="form-label" for="input-contact">Kontaktmöglichkeit</label>
|
||||
<input class="form-input" type="text" id="input-contact" placeholder="Handynummer oder E-Mail">
|
||||
<label class="form-checkbox">
|
||||
<input type="checkbox" id="input-tracker-save" checked>
|
||||
<i class="form-icon"></i> Daten im Browser speichern
|
||||
</label>
|
||||
<span id="tracker-error" class="text-error" style="display: none">Fehler</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<button id="btn-tracker" class="btn btn-primary" onclick="tracker_onsend()">Senden</button>
|
||||
<a href="#keyholder" onclick="keyholder_onload()">Wer hat den Schlüssel <i class="icon icon-arrow-right"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card" id="keyholder">
|
||||
<div class="card-header">
|
||||
<div class="card-title h5">Wer hat den Schlüssel</div>
|
||||
<span id="keyholder-error" class="text-error" style="display: none">Fehler</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="keyholder-loading" class="loading loading-lg"></div>
|
||||
<div class="form-group" id="pass-formgroup">
|
||||
<label class="form-label" for="input-pass">Passwort</label>
|
||||
<input class="form-input" type="password" id="input-pass">
|
||||
<p id= "input-hint-pass" class="form-input-hint">Passwort falsch</p>
|
||||
<label class="form-checkbox">
|
||||
<input type="checkbox" id="input-pass-save" checked>
|
||||
<i class="form-icon"></i> Im Browser speichern
|
||||
</label>
|
||||
</div>
|
||||
<div id="keyholder-table-container" style="display: none">
|
||||
<p>Folgende Personen hatten zuletzt den Schlüssel</p>
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Kontaktmöglichkeit</th>
|
||||
<th>ab</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="keyholder-tbody">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<a href="#tracker"><i class="icon icon-arrow-left"></i> Schlüsseltracker</a>
|
||||
<button id="btn-send-pw" class="btn btn-primary" onclick="password_submit()">Senden</button>
|
||||
<button id="btn-update" class="btn btn-primary" onclick="keyholder_onload()">Aktualisieren</button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-gray" id="made-by-footer">Made by HACC</p>
|
||||
</div>
|
||||
|
||||
<script src="assets/main.js" onload="oninit();"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -10,6 +10,7 @@ DB_PATH = "history.db"
|
|||
PASS_FILE = "pass"
|
||||
PASS_SALT = b"IgTp9iQH"
|
||||
CLAIM_TOKEN_FILE = "claim_token"
|
||||
CORS_ORIGIN = "*"
|
||||
|
||||
|
||||
app = Flask("Schluesselverfolgung")
|
||||
|
@ -42,8 +43,12 @@ def claim():
|
|||
if "name" not in request.form or "contact" not in request.form:
|
||||
abort(400)
|
||||
|
||||
name = request.form["name"]
|
||||
contact = request.form["contact"]
|
||||
name = request.form["name"].strip()
|
||||
contact = request.form["contact"].strip()
|
||||
|
||||
# These are arbitrary values but there to prevent sending an empty form
|
||||
if len(name) < 3 or len(contact) < 3:
|
||||
abort(400)
|
||||
|
||||
claim_id = _add_claim(name, contact)
|
||||
return claim_id
|
||||
|
@ -63,6 +68,14 @@ def keyholder():
|
|||
return jsonify(_get_keyholder())
|
||||
|
||||
|
||||
@app.after_request
|
||||
def add_header(response):
|
||||
if CORS_ORIGIN is not None:
|
||||
response.headers['Access-Control-Allow-Origin'] = CORS_ORIGIN
|
||||
response.headers['Access-Control-Allow-Headers'] = 'X-Auth-Token'
|
||||
return response
|
||||
|
||||
|
||||
def _init_db():
|
||||
c = sqlite3.connect(DB_PATH)
|
||||
c.execute("""
|
||||
|
@ -106,8 +119,6 @@ def _check_token():
|
|||
else:
|
||||
abort(401)
|
||||
|
||||
print(repr(token))
|
||||
|
||||
c = sqlite3.connect(DB_PATH)
|
||||
conn = c.cursor()
|
||||
conn.execute("SELECT Permissions FROM Token WHERE Token=?", (token,))
|
||||
|
|
Loading…
Reference in a new issue