diff --git a/sternwarte/DB4js.php b/sternwarte/DB4js.php index 1baa466..4707877 100644 --- a/sternwarte/DB4js.php +++ b/sternwarte/DB4js.php @@ -1,39 +1,11 @@ bis zu diesem Datum // Retunrn: // Array mit den Daten in ISO8601 -function getFuehrungen($start, $end, $typ) { +function getFuehrungen($start, $end) { global $db; - $erg = []; - $table = 'fdatum1'; - if ($typ == 'sonnen') { - $table = 'sonnedatum'; - } - $sql_sel = "SELECT * FROM $table where datum >= '$start' AND datum <= '$end' ORDER BY datum ASC"; + $erg = array(); + $sql_sel = "SELECT * FROM fdatum1 where datum >= '$start' AND datum <= '$end' ORDER BY datum ASC"; $result = mysqli_query($db, $sql_sel) or die(mysqli_error($db)); while ($row = mysqli_fetch_assoc($result)) { foreach ($row as $key => $value) { @@ -225,7 +193,7 @@ function getNextFuehrungen($soviel, $fid) { function updateTeilnehmer_fdate($id, $fdatum, $fid) { global $db; - $sql_stmt = "UPDATE anmeldungen SET fdatum=$fdatum,fid=$fid, abgesagt=NULL where id=$id"; + $sql_stmt = "UPDATE anmeldungen SET fdatum=$fdatum,fid=$fid where id=$id"; $result = mysqli_query($db, $sql_stmt) or die(mysqli_error($db)); return $result; } @@ -298,17 +266,6 @@ function getOneDate($id) { return $data; } -// aus fdatum die div. Datumsteile hole -function getTimeByDate($dt, $typ) { - global $db; - if ($typ == 'sonnen') - return '11 Uhr'; - $sql_stmt = "SELECT uhrzeit FROM fdatum1 WHERE datum='$dt'"; - $result = mysqli_query($db, $sql_stmt) or die(mysqli_error($db)); - $data = mysqli_fetch_assoc($result); - return $data['uhrzeit']; -} - function insertteilnehmer($data) { global $db; $name = $data['name']; @@ -464,15 +421,31 @@ function getOneRecordTermin($termin) { return $erg; } -/* + +// Ein Record von den Sonderführungen mit der ID $id holen und kompleet übermitteln +function getOneSonderTeilnehmer($id) { + global $db; + $query = "select * from SoFue2 where id = $id"; + $result = mysqli_query($db, $query) or die(mysqli_error($db)); + while ($row = mysqli_fetch_assoc($result)) { + foreach ($row as $key => $value) { + $entry[$key] = $value; + } + $erg[] = $entry; + } + return $erg; +} + + + + $_POST = json_decode(file_get_contents('php://input'), true); $erg = ""; if ($_SERVER['REQUEST_METHOD'] == 'POST') { $cmd = $_POST["cmd"]; -*/ - +/* $x = "["; foreach ($_POST as $key => $value) { if(gettype($value) == "array") { @@ -481,7 +454,7 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') { $x = $x . $key . " => " . $value . ","; } $x = $x . "]"; - +*/ switch ($cmd) { case 'GET_ANMELD': $erg = getAnmeldungen($_POST['id']); @@ -521,9 +494,6 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') { case 'GET_ONE_DATE': $erg = getOneDate($_POST['fid']); break; - case 'GET_TIME_BY_DATE': - $erg = getTimeByDate($_POST['dt'], $_POST['typ']); - break; case 'GET_ALLTEILN': $erg = getAllTeilnehmer($_POST['fdatum']); break; @@ -554,7 +524,7 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') { $erg = getTeilnehmer(-1, true, false); break; case 'GET_FUEHRUNGEN': - $erg = getFuehrungen($_POST['start'], $_POST['end'], $_POST['typ']); + $erg = getFuehrungen($_POST['start'], $_POST['end']); break; case 'PUT_FDATES': $erg = putFdates($_POST['data']); @@ -594,9 +564,14 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') { case 'GET_ONETERMIN': $erg = getOneRecordTermin($_POST["termin"]); break; + case 'GET_ONESONDERTEILNEHMER': + $erg = getOneSonderTeilnehmer($_POST["id"]); + break; + default: $erg = ['error' => 'Unknown POST-Command', 'cmd' => $cmd, 'params' => $x]; } +} else { /* $x = "["; foreach ($_GET as $key => $value) { @@ -604,7 +579,6 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') { } $x = $x . "]"; */ -if ($_SERVER['REQUEST_METHOD'] == 'GET') { $cmd = $_GET['cmd']; switch ($cmd) { case 'GET_FDATES': diff --git a/sternwarte/DB4js_all.php b/sternwarte/DB4js_all.php index ab60a35..e239e08 100644 --- a/sternwarte/DB4js_all.php +++ b/sternwarte/DB4js_all.php @@ -48,9 +48,70 @@ if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { exit; } +// Früher Lebenszeichen-Check: hilft zu unterscheiden, ob der Parser/Include scheitert +if (isset($_GET['alive']) && $_GET['alive'] === '1') { + header('Content-Type: application/json; charset=utf-8'); + echo json_encode(['alive' => true, 'ts' => date('c')]); + exit; +} + // ---- Fehlerbehandlung ---- error_reporting(E_ALL); ini_set('display_errors', 0); // Keine direkten Fehlerausgaben +ini_set('log_errors', '1'); +// Schreibe Fehler in ein projektlokales Logfile, damit /var/log nicht nötig ist +// Stelle sicher, dass der Webserver Schreibrechte hat (www-data/apache user) +ini_set('error_log', __DIR__ . '/db4js_error.log'); + +// Signalisiere dem config_stern.php, dass kein mysqli-Connect durchgeführt werden soll +if (!defined('DB4JS_ALL')) { + define('DB4JS_ALL', true); +} + +// Lokales Logging initialisieren: Logdatei anlegen, wenn möglich +function setupLocalLogging(): void { + try { + $logFile = ini_get('error_log'); + if (!$logFile) { + $logFile = __DIR__ . '/db4js_error.log'; + ini_set('error_log', $logFile); + } + if (!file_exists($logFile)) { + // create empty file + @file_put_contents($logFile, ""); + } + // Testeintrag + @error_log('DB4js_all LOG-INIT OK -> ' . $logFile); + } catch (Throwable $e) { + // still attempt a fallback + @file_put_contents(__DIR__ . '/db4js_error.log', 'DB4js_all LOG-INIT FAIL: ' . $e->getMessage() . "\n", FILE_APPEND); + } +} +setupLocalLogging(); +// Zusätzliche robuste Fehler- und Request-Logs für Server-Diagnose +register_shutdown_function(function () { + $err = error_get_last(); + if ($err && in_array($err['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) { + error_log('DB4js_all FATAL: ' . $err['message'] . ' @' . ($err['file'] ?? '-') . ':' . ($err['line'] ?? '-')); + // Liefere eine JSON-Antwort, damit Clients keinen leeren Body sehen + http_response_code(500); + echo json_encode(['error' => 'Internal error (fatal)', 'details' => 'See server logs'], JSON_UNESCAPED_UNICODE); + } +}); + +// Basis-Request-Logging (Content-Type, Methode, Rohdaten) zur 500-Analyse +try { + $ct = $_SERVER['CONTENT_TYPE'] ?? $_SERVER['HTTP_CONTENT_TYPE'] ?? ''; + $meth = $_SERVER['REQUEST_METHOD'] ?? ''; + $rawPreview = ''; + $rawBody = file_get_contents('php://input'); + if ($rawBody !== false) { + $rawPreview = substr($rawBody, 0, 512); + } + error_log('DB4js_all REQ: method=' . $meth . ' ct=' . $ct . ' raw=' . $rawPreview); +} catch (Throwable $e) { + // Ignoriere Logging-Fehler +} // ---- Konstanten für Tabellen ---- const TBL_SOFUE = 'SoFue2'; @@ -105,7 +166,7 @@ function ensureAuth(): void } // ---- Input Laden (JSON bevorzugt, Fallback FormData/Query) ---- -$raw = file_get_contents('php://input'); +$raw = $rawBody ?? file_get_contents('php://input'); $input = []; if ($raw !== false && strlen(trim($raw)) > 0) { $decoded = json_decode($raw, true); @@ -135,7 +196,8 @@ class DB public static function conn(): PDO { if (self::$pdo === null) { - require_once __DIR__ . '/config_stern.php'; +// require_once __DIR__ . '/config_stern.php'; + include "config_stern.php"; // config_stern.php sollte $host,$dbase,$user,$pass setzen $hostEnv = getenv('DB_HOST') ?: ($host ?? 'localhost'); $nameEnv = getenv('DB_NAME') ?: ($dbase ?? 'sternwarte'); @@ -915,6 +977,8 @@ class Commands { public const MAP = [ 'PING' => 'Health-Check', + 'PING_LOG' => 'Schreibt Testzeile ins Log', + 'READ_LOG' => 'Liest letzte N Zeilen aus lokalem Log', 'GET_ANMELD' => 'Liste Anmeldungen für fid/Datum', 'GET_ANMELDNEW' => 'Anmeldungen-Varianten (special/date)', 'GET_TEILN_ID' => 'Teilnehmer nach id', @@ -977,6 +1041,27 @@ try { case 'PING': respond(['pong' => true, 'timestamp' => date('c')]); + case 'PING_LOG': + $logFile = ini_get('error_log') ?: (__DIR__ . '/db4js_error.log'); + error_log('DB4js_all PING_LOG at ' . date('c')); + $ok = file_exists($logFile); + respond(['ok' => $ok, 'logfile' => $logFile]); + + case 'READ_LOG': + $logFile = ini_get('error_log') ?: (__DIR__ . '/db4js_error.log'); + $lines = (int)($input['lines'] ?? 100); + $lines = max(10, min($lines, 1000)); + if (!is_readable($logFile)) { + respond(['error' => 'Logfile not readable', 'logfile' => $logFile], 404); + } + // Tail simple implementation + $content = @file($logFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + $out = []; + if (is_array($content)) { + $out = array_slice($content, -$lines); + } + respond(['logfile' => $logFile, 'lines' => $lines, 'data' => $out]); + // Öffentliche Führungen case 'GET_ANMELD': // Backcompat: if an 8+ digit numeric 'id' is passed, treat it as date (fdatum) @@ -1152,10 +1237,12 @@ try { respond(['success' => true]); case 'GET_FDATES': // Returns führungen for calendar display - $start = $input['start'] ?? date('Ymd'); - $end = $input['end'] ?? date('Ymd', strtotime('+1 year')); - $s = date('Ymd', strtotime($start)); - $e = date('Ymd', strtotime($end)); + if (!isset($input['start'], $input['end'])) respondError('start and end required'); + // Convert ISO date strings to YYYYMMDD format + $startObj = new DateTime($input['start']); + $endObj = new DateTime($input['end']); + $s = $startObj->format('Ymd'); + $e = $endObj->format('Ymd'); $sql = "SELECT * FROM " . TBL_FDATUM . " WHERE datum >= ? AND datum <= ? ORDER BY datum ASC"; $rows = DB::all($sql, [$s, $e]); $result = []; @@ -1164,12 +1251,13 @@ try { $result[] = [ 'start' => $r['datum'], 'uhr' => substr($r['uhrzeit'] ?? '', 0, 2), - 'title' => $r['gruppe'] ?? '', + 'title' => $r['grp'] ?? '', 'count' => $count ]; } respond($result); case 'GET_CALENTRIES': + if (!isset($input['start'], $input['end'])) respondError('start and end required'); respond(RepoKalender::getEntries($input['start'], $input['end'])); case 'PUT_CALENTRY': RepoKalender::insert($input['data']); diff --git a/sternwarte/intern/anmeld/js/anmeld.js b/sternwarte/intern/anmeld/js/anmeld.js index a9af741..1066d53 100644 --- a/sternwarte/intern/anmeld/js/anmeld.js +++ b/sternwarte/intern/anmeld/js/anmeld.js @@ -70,7 +70,7 @@ Beobachtergruppe Sternwarte Welzheim` body.typ = query.typ; const response = await fetch(ajaxURL, { method: 'POST', - headers: {'Content-Type': 'application/js'}, + headers: {'Content-Type': 'application/json'}, body: JSON.stringify(body) }); return await response.json(); @@ -80,7 +80,7 @@ Beobachtergruppe Sternwarte Welzheim` body.typ = query.typ; const response = await fetch(ajaxURL, { method: 'POST', - headers: {'Content-Type': 'application/js'}, + headers: {'Content-Type': 'application/json'}, body: JSON.stringify(body) }); return await response.json(); diff --git a/sternwarte/kalender/index.html b/sternwarte/kalender/index.html index 603a792..4894321 100644 --- a/sternwarte/kalender/index.html +++ b/sternwarte/kalender/index.html @@ -10,10 +10,10 @@ - + - + diff --git a/sternwarte/kalender/js/kalender.js b/sternwarte/kalender/js/kalender.js index fac2533..8f6e8e1 100644 --- a/sternwarte/kalender/js/kalender.js +++ b/sternwarte/kalender/js/kalender.js @@ -1,10 +1,9 @@ // calendar.js document.addEventListener('DOMContentLoaded', async function () { - const DateTime = luxon.DateTime; const months_short = [0, 'Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez']; const weekdays = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstg', 'Freitag', 'Samstag', 'Sonntag']; - const url = '../DB4js_all.php'; + const url = '../DB4js.php'; const default_dauer = 90; // Standardführung dauert 90min let fuehrungszeiten; let isedit; @@ -231,7 +230,7 @@ document.addEventListener('DOMContentLoaded', async function () { $('#txtTitle').val(''); $('#txtBeschr').val(''); let ret = await putToDbase({cmd: 'DEL_CALENTRY', id: id}); - if (ret.success != true) { + if (ret != true) { alert(`Fehler beim Löschen des Eintrages !`); } calendar.refetchEvents(); @@ -292,12 +291,9 @@ document.addEventListener('DOMContentLoaded', async function () { // Return: // entweder die Startzeit, falls gefunden oder aber 20 [Uhr] function getFuehrungszeitZeit() { - if (fuehrungszeiten && fuehrungszeiten.length > 3 && fuehrungszeiten[3].start) { - let st = DateTime.fromFormat(String(fuehrungszeiten[3].start), 'yyyyMMdd'); - if (fuehrungszeiten[3].uhr) { - st = st.plus({ hours: fuehrungszeiten[3].uhr }); - } - return st.hour; + if (fuehrungszeiten[3].start !== undefined) { + let st = moment(fuehrungszeiten[3].start); + return st.hour(); } else { return 20; } @@ -313,43 +309,46 @@ document.addEventListener('DOMContentLoaded', async function () { function fillModal(st_seldate, end_seldate) { let start, end; if (isedit) { - start = DateTime.fromJSDate(st_seldate); - end = DateTime.fromJSDate(end_seldate); + start = moment(st_seldate); + end = moment(end_seldate); } else { - start = DateTime.fromJSDate(st_seldate).set({ hour: getFuehrungszeitZeit() }); - end = DateTime.fromJSDate(st_seldate).set({ hour: getFuehrungszeitZeit() }).plus({ minutes: default_dauer }); + start = moment(st_seldate); + start.hour(getFuehrungszeitZeit()); + end = moment(st_seldate); + end.hour(getFuehrungszeitZeit()); + end = end.add(default_dauer, 'm'); $('#txtTitle').val(''); $('#txtBeschr').val(''); } $('#st_date_day #end_date_day').html(''); $('#end_date_day').html(''); for (let i = 1; i <= 31; i++) { - $('#st_date_day').append(``); - $('#end_date_day').append(``); + $('#st_date_day').append(``); + $('#end_date_day').append(``); } $('#st_date_month #end_date_month').html(''); $('#end_date_month').html(''); for (let i = 1; i <= 12; i++) { - $('#st_date_month').append(``); - $('#end_date_month').append(``); + $('#st_date_month').append(``); + $('#end_date_month').append(``); } $('#st_date_year #end_date_year').html(''); $('#end_date_year').html(''); for (let i = 2020; i <= 2030; i++) { - $('#st_date_year').append(``); - $('#end_date_year').append(``); + $('#st_date_year').append(``); + $('#end_date_year').append(``); } $('#st_date_hours').html(''); $('#end_date_hours').html(''); for (let i = 0; i <= 23; i++) { - $('#st_date_hours').append(``); - $('#end_date_hours').append(``); + $('#st_date_hours').append(``); + $('#end_date_hours').append(``); } $('#st_date_minutes').html(''); $('#end_date_minutes').html(''); for (let i = 0; i <= 50; i += 10) { - $('#st_date_minutes').append(``); - $('#end_date_minutes').append(``); + $('#st_date_minutes').append(``); + $('#end_date_minutes').append(``); } } @@ -388,9 +387,9 @@ document.addEventListener('DOMContentLoaded', async function () { // Zuerst die regelmäßigen Führungen hole fuehrungszeiten = await fetchFromDbase({cmd: 'GET_FDATES', start: start, end: end}); for (let i = 0; i < fuehrungszeiten.length; i++) { - let start = DateTime.fromFormat(fuehrungszeiten[i].start, 'yyyyMMdd'); - start = start.plus({ hours: fuehrungszeiten[i].uhr }); - fuehrungszeiten[i].start = start.toISO(); + let start = moment(fuehrungszeiten[i].start); + start.add(fuehrungszeiten[i].uhr,'h') + fuehrungszeiten[i].start = moment(start).format() let kurz = checkGroupinBeos(beos, fuehrungszeiten[i]); if (kurz.k != "") { fuehrungszeiten[i].title += " " + kurz.k; @@ -434,16 +433,16 @@ document.addEventListener('DOMContentLoaded', async function () { // Array mit den Feiertagen async function getHolidays(start, end) { let data = []; - let startyear = DateTime.fromJSDate(start).year; - let endyear = DateTime.fromJSDate(end).year; + let startyear = moment(start).year(); + let endyear = moment(end).year(); let response = await fetch('https://feiertage-api.de/api/?jahr=' + startyear + '&nur_land=BW'); let holidays = await response.json(); for (const [k, v] of Object.entries(holidays)) { if(k == "Ostermontag") { - data.push({start: DateTime.fromISO(v.datum).minus({ days: 1 }).toFormat('yyyy-MM-dd'), title: "Ostersonntag"}); + data.push({start: moment(v.datum).subtract(1,'days').format("YYYY-MM-DD"), title: "Ostersonntag"}); } if(k == "Pfingstmontag") { - data.push({start: DateTime.fromISO(v.datum).minus({ days: 1 }).toFormat('yyyy-MM-dd'), title: "Pfingstsonntag"}); + data.push({start: moment(v.datum).subtract(1,'days').format("YYYY-MM-DD"), title: "Pfingstsonntag"}); } data.push({start: v.datum, title: k}); } @@ -463,20 +462,20 @@ document.addEventListener('DOMContentLoaded', async function () { // Return: // Array mit den Sonntagen function getSundays(dt) { - let year = DateTime.fromJSDate(dt).year; - const cm = DateTime.fromJSDate(dt).month; - if (DateTime.fromJSDate(dt).month == 12) { + let year = moment(dt).year(); + const cm = moment(dt).month() + if (moment(dt).month() == 11) { year += 1; } let data = []; for (let i = 1; i <= 12; i++) { - let d = DateTime.fromObject({ year: year, month: i, day: 1 }); - let w = d.weekday; - let dt = ''; + let d = moment(year + '-' + i + '-01'); + let w = d.isoWeekday(); + let dt = '' if (w == 7) { - dt = d.toFormat('yyyy-MM-dd'); + dt = d.format('YYYY-MM-DD'); } else { - dt = d.plus({ days: 7 - w }).toFormat('yyyy-MM-dd'); + dt = d.add(7 - w, 'd').format('YYYY-MM-DD') } data.push({start: dt + 'T11:00:00', uhr: 11, title: 'Sonnenführung' }) } @@ -511,18 +510,17 @@ document.addEventListener('DOMContentLoaded', async function () { // Informationen einer Regelführung für das Popup zusammenbauen function getRegularInfo(entryInfo) { - let dt = DateTime.fromJSDate(entryInfo.start); - let curDate = dt.toFormat('yyyyMMdd'); + let curDate = moment(entryInfo.start).format('YYYYMMDD'); let besucher = entryInfo._def.extendedProps.count; if (besucher == null) { besucher = "0"} besucher = parseInt(besucher) - let wtg = dt.weekday; + let wtg = moment(entryInfo.start).isoWeekday(); let nbr = entryInfo.title.split(' '); let str = `