diff --git a/.gitignore b/.gitignore
index ea276b4..88ea054 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
server/config.php
+api/config.php
.htaccess
diff --git a/api/.htaccess_example b/api/.htaccess_example
new file mode 100644
index 0000000..a02d3cc
--- /dev/null
+++ b/api/.htaccess_example
@@ -0,0 +1,8 @@
+RewriteEngine on
+# root directory:
+RewriteBase /projects/RegattenApp/api/
+
+
+
+# Show site
+RewriteRule ^(.*)$ index.php?request=$1 [QSA]
\ No newline at end of file
diff --git a/api/config_example.php b/api/config_example.php
new file mode 100644
index 0000000..bcd2ceb
--- /dev/null
+++ b/api/config_example.php
@@ -0,0 +1,44 @@
+
\ No newline at end of file
diff --git a/api/database.php b/api/database.php
new file mode 100644
index 0000000..108d3c1
--- /dev/null
+++ b/api/database.php
@@ -0,0 +1,157 @@
+num_rows > 0) {
+ $i = 0;
+ while ($row = $response->fetch_assoc()) {
+ if (isset($row['id'])) {
+ $id = $row['id'];
+ } else {
+ $id = $i;
+ $i ++;
+ }
+ foreach ($row as $key => $value) {
+ $result[$id][$key] = $value;
+ }
+ }
+ }
+ return $result;
+ } else {
+ logE("database", "get_data\nInvalid request\n" . $query . "\n" . mysqli_error($mysqli));
+ return false;
+ }
+ }
+
+ function db_update_data($mysqli, $table, $data, $where, $limit = false) {
+ $rest = '';
+ if ($where != false) {
+ $rest .= ' WHERE ' . $where;
+ }
+ if ($limit != false) {
+ $rest .= sprintf(' LIMIT %d', $limit);
+ }
+ $set = '';
+ $first = true;
+ foreach ($data as $key => $value) {
+ if ($first) {
+ $first = false;
+ } else {
+ $set .= ', ';
+ }
+ if ($value === null) {
+ $set .= '`' . mysqli_real_escape_string($mysqli, $key) . '`=NULL';
+ } else {
+ $set .= '`' . mysqli_real_escape_string($mysqli, $key) . '`="' . mysqli_real_escape_string($mysqli, $value) . '"';
+ }
+ }
+ if (defined('DB_CHANGE_TIME')) $set .= ', `changed`=NOW()';
+ $query = 'UPDATE ' . mysqli_real_escape_string($mysqli, $table) . ' SET ' . $set . $rest . ';';
+ $response = mysqli_query($mysqli, $query);
+
+ if ($response === false) {
+ logE("database", "update_data\nInvalid request\n" . $query . "\n" . mysqli_error($mysqli));
+ } elseif (defined('DB_CHANGE_TIME')) {
+ mysqli_query($mysqli, 'UPDATE `_updatetimes` SET `update`=NOW() WHERE `table`="' . mysqli_real_escape_string($mysqli, $table) . '";');
+ }
+
+ return $response;
+ }
+
+ function db_insert_data($mysqli, $table, $data) {
+ $fields = '';
+ $values = '';
+ $first = true;
+ foreach ($data as $key => $value) {
+ if ($first) {
+ $first = false;
+ } else {
+ $fields .= ', ';
+ $values .= ', ';
+ }
+ $fields .= '`' . mysqli_real_escape_string($mysqli, $key) . '`';
+ if ($value === null) {
+ $values .= 'NULL';
+ } else {
+ $values .= '"' . mysqli_real_escape_string($mysqli, $value) . '"';
+ }
+ }
+ if (defined('DB_CHANGE_TIME')) {
+ $fields .= ', `changed`';
+ $values .= ', NOW()';
+ }
+ $query = 'INSERT INTO `' . mysqli_real_escape_string($mysqli, $table) . '` (' . $fields . ') VALUES (' . $values . ');';
+ $response = mysqli_query($mysqli, $query);
+ if ($response === false) {
+ logE("database", "insert_data\nInvalid request\n" . $query . "\n" . mysqli_error($mysqli));
+ } else {
+ $response = mysqli_insert_id($mysqli);
+ if (defined('DB_CHANGE_TIME')) {
+ mysqli_query($mysqli, 'UPDATE `_updatetimes` SET `update`=NOW() WHERE `table`="' . mysqli_real_escape_string($mysqli, $table) . '";');
+ }
+ }
+
+ return $response;
+ }
+
+ function db_delete_data($mysqli, $table, $where, $limit = false) {
+ $rest = '';
+ if ($where != false) {
+ $rest .= ' WHERE ' . $where;
+ }
+ if ($limit != false) {
+ $rest .= sprintf(' LIMIT %d', $limit);
+ }
+ $query = 'DELETE FROM `' . mysqli_real_escape_string($mysqli, $table) . '`' . $rest . ';';
+ $response = mysqli_query($mysqli, $query);
+ if ($response === false) {
+ logE("database", "delete_data\nInvalid request\n" . $query . "\n" . mysqli_error($mysqli));
+ } elseif (defined('DB_CHANGE_TIME')) {
+ mysqli_query($mysqli, 'UPDATE `_updatetimes` SET `update`=NOW() WHERE `table`="' . mysqli_real_escape_string($mysqli, $table) . '";');
+ }
+
+ return $response;
+ }
+
+?>
\ No newline at end of file
diff --git a/api/index.php b/api/index.php
new file mode 100644
index 0000000..4102512
--- /dev/null
+++ b/api/index.php
@@ -0,0 +1,123 @@
+= 1) {
+ $action = array_shift($request);
+ } else {
+ $action = '';
+ }
+
+ define('DONE_OKAY', 0);
+ define('DONE_EMPTY', 1);
+ define('DONE_DATABASE', 2);
+ define('DONE_UNAUTHORIZED', 3);
+ define('DONE_BAD_REQUEST', 4);
+ define('DONE_CONFLICT', 5);
+ define('DONE_SERVER_ERROR', 6);
+ function done($donecode, $content = null) {
+ switch ($donecode) {
+ case DONE_OKAY:
+ header('HTTP/1.0 200 OK');
+ break;
+ case DONE_EMPTY:
+ header('HTTP/1.0 204 No Content');
+ break;
+ case DONE_DATABASE:
+ header('HTTP/1.0 500 Internal Server Error');
+ if ($content === null) {
+ $content = array('error' => 'database error');
+ }
+ break;
+ case DONE_UNAUTHORIZED:
+ header('HTTP/1.0 401 Unauthorized');
+ if ($content === null) {
+ $content = array('error' => 'unauthorized');
+ }
+ break;
+ case DONE_BAD_REQUEST:
+ header('HTTP/1.0 400 Bad Request');
+ if ($content === null) {
+ $content = array('error' => 'bad request');
+ }
+ break;
+ case DONE_CONFLICT:
+ header('HTTP/1.0 409 Conflict');
+ break;
+ case DONE_SERVER_ERROR:
+ header('HTTP/1.0 500 Internal Server Error');
+ break;
+ default:
+ header('HTTP/1.0 500 Internal Server Error');
+ break;
+ }
+ header('Content-Type: application/json');
+ if ($content !== null) {
+ echo json_encode($content);
+ } else {
+ echo '{ }';
+ }
+ exit;
+ }
+
+ if (isset($_REQUEST['auth']['id'], $_REQUEST['auth']['hash'])) {
+ $user_id = auth_check($mysqli, $_REQUEST['auth']['id'], $_REQUEST['auth']['hash']);
+ } else {
+ $user_id = false;
+ }
+ $perm = get_perm($mysqli, $user_id);
+
+ function has_perm($permission) {
+ global $perm;
+ return ($perm & $permission) == $permission;
+ }
+
+ function checkPermission($perm) {
+ if (!has_perm($perm)) done(DONE_UNAUTHORIZED, ['error' => 'permission denied']);
+ }
+
+ function checkRequest($param) {
+ if (!isset($_REQUEST[$param])) done(DONE_BAD_REQUEST, ['error' => 'missing parameter: ' . $param]);
+ }
+
+ function replaceChanged($array) {
+ return array_map(function ($entry) {
+ unset($entry['changed']);
+ return $entry;
+ }, $array);
+ }
+
+ switch ($action) {
+
+ case 'login':
+ checkRequest('username');
+ checkRequest('password');
+ checkRequest('device');
+ $auth = auth_login($mysqli, $_REQUEST['username'], $_REQUEST['password'], $_REQUEST['device']);
+ if ($auth === false) done(DONE_UNAUTHORIZED);
+ done(DONE_OKAY, $auth);
+ break;
+
+ case 'logout':
+ checkPermission(PERM_REGISTERED);
+ auth_logout($mysqli, $_REQUEST['auth']['id']);
+ done(DONE_OKAY);
+ break;
+
+ default:
+ done(DONE_BAD_REQUEST, ['error' => 'action invalid']);
+
+ }
+
+?>
\ No newline at end of file
diff --git a/api/login.php b/api/login.php
new file mode 100644
index 0000000..852f82a
--- /dev/null
+++ b/api/login.php
@@ -0,0 +1,107 @@
+ $user['id'],
+ 'salt' => $salt,
+ 'authhash' => $hash,
+ 'device' => $device
+ ];
+ $auth['id'] = db_insert_data($mysqli, DB_TABLE_LOGINS, $data);
+ return $auth;
+ }
+
+ function auth_logout($mysqli, $id) {
+ db_delete_data($mysqli, DB_TABLE_LOGINS, 'id = "' . mysqli_real_escape_string($mysqli, $id) . '"', 1);
+ return true;
+ }
+
+ function auth_check($mysqli, $id, $hash) {
+ $auth = db_get_data($mysqli, DB_TABLE_LOGINS, '*', 'id="' . mysqli_real_escape_string($mysqli, $id) . '"', 1);
+ if (($auth === false) or (count($auth) != 1)) return false;
+ $auth = array_values($auth)[0];
+ $hash = hash('sha512', $hash . $auth['salt']);
+ if ($hash != $auth['authhash']) return false;
+ db_update_data($mysqli, DB_TABLE_LOGINS, ['id' => $auth['id']], 'id="' . $auth['id'] . '"', 1); // update changed field => last login
+ return $auth['user'];
+ }
+
+?>
\ No newline at end of file
diff --git a/client/scripts/custom.js.php b/client/scripts/custom.js.php
index 6adc5d4..51229a1 100644
--- a/client/scripts/custom.js.php
+++ b/client/scripts/custom.js.php
@@ -31,7 +31,7 @@ $(document).ready(function(){
'use strict'
var isAJAX = true; //Enables or disable AJAX page transitions and loading.
- var isDevelopment = true; // Enables development mode. Clean cache & Stops BG & Highlights from changing defaults.
+ var isDevelopment = false; // Enables development mode. Clean cache & Stops BG & Highlights from changing defaults.
function init_template(){
diff --git a/client/scripts/pwa.js.php b/client/scripts/pwa.js.php
index f306efc..7b35cfc 100644
--- a/client/scripts/pwa.js.php
+++ b/client/scripts/pwa.js.php
@@ -18,7 +18,7 @@ $(document).ready(function(){
var pwaVersion = ''; //must be identical to _manifest.json version. If not it will create update window loop
var pwaCookie = true; // if set to false, the PWA prompt will appear even if the user selects "maybe later"
- var pwaNoCache = true; // always keep the cache clear to serve the freshest possible content
+ var pwaNoCache = false; // always keep the cache clear to serve the freshest possible content
$('[data-pwa-version]').data('pwa-version', pwaVersion);
@@ -132,7 +132,7 @@ $(document).ready(function(){
caches.delete('workbox-runtime').then(function() {
console.log('Content Updated - Cache Removed!');
});
- localStorage.clear();
+ //localStorage.clear();
sessionStorage.clear()
caches.keys().then(cacheNames => {
cacheNames.forEach(cacheName => {
diff --git a/client/scripts/regatten.js.php b/client/scripts/regatten.js.php
index bb702e1..1885768 100644
--- a/client/scripts/regatten.js.php
+++ b/client/scripts/regatten.js.php
@@ -6,6 +6,8 @@
?>
+const apiUrl = '/api/';
+
var randomId = function() { return '_' + Math.random().toString(36).substr(2, 9); }
var badges = {
@@ -96,8 +98,115 @@ var toastWarn = function (text, time = 3000) { return makeToast('bg-yellow1-dar
var toastInfo = function (text, time = 3000) { return makeToast('bg-blue2-dark', 'fa-info', text, time); }
var toastError = function (text, time = 3000) { return makeToast('bg-red2-dark', 'fa-times', text, time); }
+var login = function() {
+ showLoader();
+ var username = $('#input-login-username').val();
+ var password = $('#input-login-password').val();
+ $('#input-login-username').val('');
+ $('#input-login-password').val('');
+ $.ajax({
+ url: apiUrl + 'login',
+ method: 'POST',
+ data: {
+ username: username,
+ password: password,
+ device: navigator.userAgent
+ },
+ error: function (xhr, status, error) {
+ if (xhr.status == 401) {
+ toastError('Benutzername oder Passwort falsch');
+ $('#input-login-username').val(username);
+ } else if (xhr.status == 0) {
+ toastError('Du bist momentan offline.
Stelle eine Internetverbindung her, um Dich anzumelden');
+ $('#menu-login').hideMenu();
+ } else {
+ console.log('Login: unbekannter Fehler', status, error);
+ console.log(xhr);
+ toastError('Ein unbekannter Fehler ist aufgetreten. Bitte versuche es noch einmal', 5000);
+ }
+ hideLoader();
+ },
+ success: function (data, status, xhr) {
+ localStorage.setItem('auth_id', data.id);
+ localStorage.setItem('auth_hash', data.auth);
+ localStorage.setItem('auth_user', data.user);
+ localStorage.setItem('auth_username', data.username);
+ location.reload();
+ }
+ });
+}
+
+var logoutClearStorage = function() {
+ localStorage.removeItem('auth_id');
+ localStorage.removeItem('auth_hash');
+ localStorage.removeItem('auth_user');
+ localStorage.removeItem('auth_username');
+ location.reload();
+}
+
+var logout = function() {
+ showLoader();
+ var auth = {
+ id: localStorage.getItem('auth_id'),
+ hash: localStorage.getItem('auth_hash')
+ }
+ if ((auth.id === null) || (auth.hash === null)) {
+ console.log('Not logged in');
+ logoutClearStorage();
+ return;
+ }
+ $.ajax({
+ url: apiUrl + 'logout',
+ method: 'POST',
+ data: {
+ auth: auth
+ },
+ error: function (xhr, status, error) {
+ if (xhr.status == 401) {
+ console.log('Not logged in');
+ logoutClearStorage();
+ } else if (xhr.status == 0) {
+ console.log('Could not delete auth from server');
+ logoutClearStorage();
+ } else {
+ console.log('Logout: unbekannter Fehler', status, error);
+ console.log(xhr);
+ toastError('Ein unbekannter Fehler ist aufgetreten. Bitte versuche es noch einmal', 5000);
+ hideLoader();
+ }
+ },
+ success: function (data, status, xhr) {
+ logoutClearStorage();
+ }
+ });
+}
+
var initRegatten = function() {
- loggedin = true;
+ loggedin = (localStorage.getItem('auth_id') !== null);
+
+ if (loggedin) {
+ var auth = {
+ id: localStorage.getItem('auth_id'),
+ hash: localStorage.getItem('auth_hash')
+ }
+ var user = {
+ id: localStorage.getItem('auth_user'),
+ name: localStorage.getItem('auth_username')
+ }
+ if ((auth.hash === null) || (user.id === null) || (user.name === null)) {
+ logoutClearStorage();
+ return;
+ }
+ }
+
+ if (loggedin) {
+ $('.show-notloggedin').css('display', 'none');
+ $('.replace-userid-href').attr('href', $('.replace-userid-href').attr('href').replace('%USERID%', user.id));
+ $('.replace-username').html(user.name);
+ } else {
+ $('.show-loggedin').css('display', 'none');
+ }
+
if (typeof siteScript !== 'undefined') {
siteScript();
}
diff --git a/content/index.php b/content/index.php
index 09047f8..9baa79b 100644
--- a/content/index.php
+++ b/content/index.php
@@ -35,11 +35,14 @@
$sp['output'] .= $tpl->load('card', [$content, 'html-id' => 'card-last']);
// Calendar
- $content = "
Du willst alle Regatta-Termine in deinem Kalender, aber nicht alles abtippen?
Kein Problem! Abonniere einfach unseren ics-Kalender.
Nur die Regatten, zu denen Du gehst?
Auch kein Problem! Erstelle einfach eine Saison-Planung und abonniere Deinen persönlichen Kalender.
Du willst alle Regatta-Termine in deinem Kalender, aber nicht alles abtippen?
Kein Problem! Abonniere einfach unseren ics-Kalender.
Nur die Regatten, zu denen Du gehst?
Auch kein Problem! ';
+ $content .= 'Erstelle einfach eine Saison-Planung und abonniere Deinen persönlichen Kalender.';
+ $content .= 'Registriere Dich einfach kostenlos, erstelle eine Saison-Planung und wir erstellen Dir einen persönlichen Kalender.';
+ $content .= '