From 581888e1428adad42da8cde07a2d808c053ee5a0 Mon Sep 17 00:00:00 2001 From: hafroese Date: Thu, 2 Apr 2026 23:05:27 +0200 Subject: [PATCH] feat: PHP API foundation - router, config, helpers, SM-2 algorithm --- edu/api/config.php | 30 ++++++++++++++++++++++++++++++ edu/api/helpers.php | 41 +++++++++++++++++++++++++++++++++++++++++ edu/api/index.php | 42 ++++++++++++++++++++++++++++++++++++++++++ edu/api/sm2.php | 39 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 152 insertions(+) create mode 100644 edu/api/config.php create mode 100644 edu/api/helpers.php create mode 100644 edu/api/index.php create mode 100644 edu/api/sm2.php diff --git a/edu/api/config.php b/edu/api/config.php new file mode 100644 index 0000000..3824b31 --- /dev/null +++ b/edu/api/config.php @@ -0,0 +1,30 @@ + PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + PDO::ATTR_EMULATE_PREPARES => false + ] + ); +} catch (PDOException $e) { + http_response_code(500); + echo json_encode(['error' => 'Datenbankverbindung fehlgeschlagen']); + exit; +} + +ini_set('session.cookie_httponly', 1); +ini_set('session.cookie_secure', 1); +ini_set('session.cookie_samesite', 'Strict'); +ini_set('session.use_strict_mode', 1); +session_start(); + +define('CONTENT_DIR', __DIR__ . '/../content'); diff --git a/edu/api/helpers.php b/edu/api/helpers.php new file mode 100644 index 0000000..4501e8d --- /dev/null +++ b/edu/api/helpers.php @@ -0,0 +1,41 @@ + $message], JSON_UNESCAPED_UNICODE); + exit; +} + +function require_auth() { + if (empty($_SESSION['user_id'])) { + json_error('Nicht angemeldet', 401); + } + return $_SESSION['user_id']; +} + +function require_admin() { + require_auth(); + if (empty($_SESSION['is_admin'])) { + json_error('Keine Admin-Berechtigung', 403); + } + return $_SESSION['user_id']; +} + +function get_method() { + return $_SERVER['REQUEST_METHOD']; +} + +function get_json_body() { + $body = file_get_contents('php://input'); + return json_decode($body, true) ?: []; +} + +function get_param($name, $default = null) { + return $_GET[$name] ?? $default; +} diff --git a/edu/api/index.php b/edu/api/index.php new file mode 100644 index 0000000..8e8808a --- /dev/null +++ b/edu/api/index.php @@ -0,0 +1,42 @@ + ['decks', 'omnis-commands'] +$request_uri = $_SERVER['REQUEST_URI']; +$path = parse_url($request_uri, PHP_URL_PATH); +$path = preg_replace('#^/api/?#', '', $path); +$path = trim($path, '/'); +$segments = $path ? explode('/', $path) : []; +$resource = $segments[0] ?? ''; + +switch ($resource) { + case 'login': + case 'logout': + case 'me': + require __DIR__ . '/auth.php'; + break; + case 'decks': + require __DIR__ . '/decks.php'; + break; + case 'cards': + require __DIR__ . '/cards.php'; + break; + case 'tutorials': + require __DIR__ . '/tutorials.php'; + break; + case 'quiz': + require __DIR__ . '/quiz.php'; + break; + case 'cheatsheets': + require __DIR__ . '/cheatsheets.php'; + break; + case 'dashboard': + require __DIR__ . '/dashboard.php'; + break; + case 'admin': + require __DIR__ . '/admin.php'; + break; + default: + json_error('Unbekannter Endpunkt', 404); +} diff --git a/edu/api/sm2.php b/edu/api/sm2.php new file mode 100644 index 0000000..a6e95a0 --- /dev/null +++ b/edu/api/sm2.php @@ -0,0 +1,39 @@ + 0, 'hard' => 2, 'medium' => 3, 'easy' => 5]; + $q = $rating_map[$rating_name] ?? 3; + + $ease = $current_ease; + $interval = $current_interval; + $reps = $current_reps; + + if ($q < 3) { + $reps = 0; + $interval = 1; + } else { + if ($reps === 0) { + $interval = 1; + } elseif ($reps === 1) { + $interval = 3; + } else { + $interval = (int) round($interval * $ease); + } + $reps++; + } + + $ease = $ease + 0.1 - (5 - $q) * (0.08 + (5 - $q) * 0.02); + if ($ease < 1.3) $ease = 1.3; + + $next_review = date('Y-m-d', strtotime("+{$interval} days")); + + return [ + 'ease_factor' => round($ease, 2), + 'interval_days' => $interval, + 'repetitions' => $reps, + 'next_review' => $next_review + ]; +}