From 5dfbbacf3958230b8ca76f41d175915f71968b25 Mon Sep 17 00:00:00 2001 From: Jukka Lampikoski Date: Sun, 8 Mar 2026 11:08:22 +0200 Subject: [PATCH] Add user registration/login, persistent likes, category hiding, and contact email MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - User auth: register (nickname + password + email), login, logout with PHP sessions - Persistent likes: logged-in users' likes saved to users.json, anonymous via session - "Tykkäämäni" filter button next to search — filter liked posts, combinable with search - Hide empty/sparse categories from filter buttons until posts exist - Replace broken contact form with simple mailto link (info@tykkaa.fi) Co-Authored-By: Claude Opus 4.6 --- api.php | 134 ++++++++++++++++++++++++++++++---- index.html | 21 ++++-- script.js | 211 +++++++++++++++++++++++++++++++++++++++++++++-------- style.css | 147 +++++++++++++++++++++++++++++++------ 4 files changed, 443 insertions(+), 70 deletions(-) diff --git a/api.php b/api.php index ee3ed6b..17097fb 100644 --- a/api.php +++ b/api.php @@ -52,6 +52,19 @@ function isAdmin(): bool { return !empty($_SESSION['tykkaafi_admin']); } +function isLoggedIn(): bool { + return !empty($_SESSION['tykkaafi_user_id']); +} + +function getLoggedInUser(): ?array { + if (!isLoggedIn()) return null; + $users = readData('users.json', []); + foreach ($users as $u) { + if ($u['id'] === $_SESSION['tykkaafi_user_id']) return $u; + } + return null; +} + // ─── Oletusdata ──────────────────────────────────────────────── // Kategoriat jotka on poistettu — poistetaan automaattisesti live-serveriltä const REMOVED_CATEGORIES = ['knitting', 'kasvit', 'matkustus', 'tips']; @@ -691,26 +704,55 @@ switch ($action) { ok(['categories' => getOrInitCategories()]); case 'likes': - $likes = readData('likes.json', new stdClass()); - $userLikes = $_SESSION['user_likes'] ?? []; + $likes = readData('likes.json', new stdClass()); + $user = getLoggedInUser(); + if ($user) { + $userLikes = $user['likes'] ?? []; + } else { + $userLikes = $_SESSION['user_likes'] ?? []; + } ok(['likes' => $likes, 'userLikes' => $userLikes]); case 'toggle_like': $postId = $body['postId'] ?? ''; if (!$postId) err('Missing postId'); - $likes = readData('likes.json', []); - $userLikes = $_SESSION['user_likes'] ?? []; - $idx = array_search($postId, $userLikes, true); - if ($idx === false) { - $likes[$postId] = ($likes[$postId] ?? 0) + 1; - $userLikes[] = $postId; - $liked = true; + $likes = readData('likes.json', []); + $user = getLoggedInUser(); + + if ($user) { + $users = readData('users.json', []); + $userLikes = $user['likes'] ?? []; + $idx = array_search($postId, $userLikes, true); + if ($idx === false) { + $likes[$postId] = ($likes[$postId] ?? 0) + 1; + $userLikes[] = $postId; + $liked = true; + } else { + $likes[$postId] = max(0, ($likes[$postId] ?? 1) - 1); + array_splice($userLikes, $idx, 1); + $liked = false; + } + foreach ($users as &$u) { + if ($u['id'] === $user['id']) { $u['likes'] = array_values($userLikes); break; } + } + unset($u); + writeData('users.json', $users); + $_SESSION['user_likes'] = array_values($userLikes); } else { - $likes[$postId] = max(0, ($likes[$postId] ?? 1) - 1); - array_splice($userLikes, $idx, 1); - $liked = false; + $userLikes = $_SESSION['user_likes'] ?? []; + $idx = array_search($postId, $userLikes, true); + if ($idx === false) { + $likes[$postId] = ($likes[$postId] ?? 0) + 1; + $userLikes[] = $postId; + $liked = true; + } else { + $likes[$postId] = max(0, ($likes[$postId] ?? 1) - 1); + array_splice($userLikes, $idx, 1); + $liked = false; + } + $_SESSION['user_likes'] = array_values($userLikes); } - $_SESSION['user_likes'] = array_values($userLikes); + writeData('likes.json', $likes); ok(['liked' => $liked, 'count' => $likes[$postId] ?? 0]); @@ -801,6 +843,72 @@ switch ($action) { case 'admin_check': ok(['loggedIn' => isAdmin()]); + // ─── Käyttäjätunnukset ───────────────────────────────────── + case 'user_register': + $nickname = trim($body['nickname'] ?? ''); + $email = trim($body['email'] ?? ''); + $password = $body['password'] ?? ''; + + if (!$nickname || !$password) err('Nimimerkki ja salasana vaaditaan.'); + if (mb_strlen($nickname) < 2 || mb_strlen($nickname) > 30) err('Nimimerkin tulee olla 2–30 merkkiä.'); + if (mb_strlen($password) < 6) err('Salasanan tulee olla vähintään 6 merkkiä.'); + if ($email && !filter_var($email, FILTER_VALIDATE_EMAIL)) err('Sähköpostiosoite ei kelpaa.'); + + $users = readData('users.json', []); + foreach ($users as $u) { + if (mb_strtolower($u['nickname']) === mb_strtolower($nickname)) { + err('Nimimerkki on jo käytössä.'); + } + } + + $user = [ + 'id' => 'user_' . time() . '_' . random_int(1000, 9999), + 'nickname' => htmlspecialchars($nickname, ENT_QUOTES | ENT_HTML5, 'UTF-8'), + 'email' => htmlspecialchars($email, ENT_QUOTES | ENT_HTML5, 'UTF-8'), + 'password' => password_hash($password, PASSWORD_DEFAULT), + 'likes' => $_SESSION['user_likes'] ?? [], + 'created' => date('Y-m-d'), + ]; + + $users[] = $user; + writeData('users.json', $users); + $_SESSION['tykkaafi_user_id'] = $user['id']; + + ok(['user' => ['id' => $user['id'], 'nickname' => $user['nickname'], 'likes' => $user['likes']]]); + + case 'user_login': + $nickname = trim($body['nickname'] ?? ''); + $password = $body['password'] ?? ''; + if (!$nickname || !$password) err('Nimimerkki ja salasana vaaditaan.'); + + $users = readData('users.json', []); + foreach ($users as $u) { + if (mb_strtolower($u['nickname']) === mb_strtolower($nickname)) { + if (password_verify($password, $u['password'])) { + $_SESSION['tykkaafi_user_id'] = $u['id']; + $_SESSION['user_likes'] = $u['likes'] ?? []; + ok(['user' => ['id' => $u['id'], 'nickname' => $u['nickname'], 'likes' => $u['likes'] ?? []]]); + } + err('Väärä salasana.'); + } + } + err('Nimimerkkiä ei löydy.'); + + case 'user_logout': + unset($_SESSION['tykkaafi_user_id']); + ok(); + + case 'user_check': + $user = getLoggedInUser(); + if ($user) { + ok(['loggedIn' => true, 'user' => [ + 'id' => $user['id'], + 'nickname' => $user['nickname'], + 'likes' => $user['likes'] ?? [], + ]]); + } + ok(['loggedIn' => false]); + default: err('Unknown action'); } diff --git a/index.html b/index.html index 27af540..e11781e 100644 --- a/index.html +++ b/index.html @@ -17,6 +17,7 @@

tykkää.fi

+