$e->getMessage(), 'file' => basename($e->getFile()), 'line' => $e->getLine()]); exit; }); // ─── VAIHDA TÄMÄ SALASANA! ────────────────────────────────────── define('ADMIN_PASSWORD', 'passus'); // ──────────────────────────────────────────────────────────────── define('DATA_DIR', __DIR__ . '/data/'); if (!is_dir(DATA_DIR)) mkdir(DATA_DIR, 0755, true); $action = $_GET['action'] ?? ''; $body = []; if ($_SERVER['REQUEST_METHOD'] === 'POST') { $raw = file_get_contents('php://input'); $body = json_decode($raw, true) ?? []; if (!$action) $action = $body['action'] ?? ''; } // ─── Apufunktiot ─────────────────────────────────────────────── function readData(string $file, $default = []) { $path = DATA_DIR . $file; if (!file_exists($path)) return $default; $d = json_decode(file_get_contents($path), true); return $d !== null ? $d : $default; } function writeData(string $file, $data): void { $path = DATA_DIR . $file; $dir = dirname($path); if (!is_dir($dir)) @mkdir($dir, 0755, true); $result = @file_put_contents( $path, json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT) ); if ($result === false) { http_response_code(500); echo json_encode(['error' => "Tiedoston kirjoitus epäonnistui: $file (kansio: " . (is_writable($dir) ? 'kirjoitettava' : 'EI kirjoitettava') . ")"]); exit; } } function ok($data = []): void { echo json_encode(array_merge(['ok' => true], $data)); exit; } function err(string $msg, int $code = 400): void { http_response_code($code); echo json_encode(['error' => $msg]); exit; } 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']; // Vanhojen postausten kategoria-migraatiot (null = poista postaus) const POST_CATEGORY_MAP = [ 'knitting' => 'kasityot', 'kasvit' => 'puutarha', 'tips' => 'hyvinvointi', 'matkustus' => null, ]; function defaultCategories(): array { return [ ['id' => 'recipes', 'fi' => 'Reseptit', 'en' => 'Recipes', 'emoji' => '🍳', 'subcategories' => [ ['id' => 'kasvis', 'fi' => 'Kasvis'], ['id' => 'vegaaniset', 'fi' => 'Vegaaniset'], ['id' => 'jalkiruuat', 'fi' => 'Jälkiruuat'], ['id' => 'muut', 'fi' => 'Muut'], ]], ['id' => 'kasityot', 'fi' => 'Käsityöt', 'en' => 'Crafts', 'emoji' => '✂️', 'subcategories' => [ ['id' => 'neulominen', 'fi' => 'Neulominen'], ['id' => 'virkkaus', 'fi' => 'Virkkaus'], ['id' => 'ommelu', 'fi' => 'Ommelu'], ['id' => 'askartelu', 'fi' => 'Askartelu'], ['id' => 'muut', 'fi' => 'Muut'], ]], ['id' => 'puutarha', 'fi' => 'Puutarha & Kasvit', 'en' => 'Garden & Plants', 'emoji' => '🌱', 'subcategories' => [ ['id' => 'huonekasvit', 'fi' => 'Huonekasvit'], ['id' => 'parvekeviljeily', 'fi' => 'Parvekeviljeily'], ['id' => 'vihannekset', 'fi' => 'Vihannekset'], ['id' => 'kukat', 'fi' => 'Kukat'], ['id' => 'muut', 'fi' => 'Muut'], ]], ['id' => 'sisustus', 'fi' => 'Sisustus', 'en' => 'Interior', 'emoji' => '🏠', 'subcategories' => [ ['id' => 'diy', 'fi' => 'DIY'], ['id' => 'valaistus', 'fi' => 'Valaistus'], ['id' => 'tekstiilit', 'fi' => 'Tekstiilit'], ['id' => 'muut', 'fi' => 'Muut'], ]], ['id' => 'kirjat', 'fi' => 'Kirjat', 'en' => 'Books', 'emoji' => '📚', 'subcategories' => [ ['id' => 'romaanit', 'fi' => 'Romaanit'], ['id' => 'tietokirjat', 'fi' => 'Tietokirjat'], ['id' => 'muut', 'fi' => 'Muut'], ]], ['id' => 'hyvinvointi', 'fi' => 'Hyvinvointi', 'en' => 'Wellbeing', 'emoji' => '🧘', 'subcategories' => [ ['id' => 'liikunta', 'fi' => 'Liikunta'], ['id' => 'uni', 'fi' => 'Uni'], ['id' => 'mieli', 'fi' => 'Mieli'], ['id' => 'arki', 'fi' => 'Arki'], ['id' => 'muut', 'fi' => 'Muut'], ]], ['id' => 'lemmikit', 'fi' => 'Lemmikit', 'en' => 'Pets', 'emoji' => '🐾', 'subcategories' => [ ['id' => 'koirat', 'fi' => 'Koirat'], ['id' => 'kissat', 'fi' => 'Kissat'], ['id' => 'muut', 'fi' => 'Muut'], ]], ]; } function defaultPosts(): array { return [ // ── RESEPTIT ────────────────────────────────────────────── [ 'id' => 'pancakes', 'emoji' => '🥞', 'title' => 'Kuohkeat letut', 'category' => 'recipes', 'author' => 'Maija-Liisa', 'time' => '20 min', 'servings' => '4 annosta', 'type' => 'recipe', 'desc' => 'Kultaisia, voisia lettuja, jotka sulavat suuhun. Täydellisiä laiskaan sunnuntaiaamuun.', 'ingredients' => ['1½ dl vehnäjauhoja','3½ tl leivinjauhetta','1 tl suolaa','1 rkl sokeria','3 dl maitoa','1 muna','3 rkl sulatettua voita','Voita tai öljyä paistamiseen'], 'steps' => ['Sekoita kulhossa jauhot, leivinjauhe, suola ja sokeri.','Tee keskelle kuoppa ja kaada sekaan maito, muna ja sulatettu voi.','Sekoita tasaiseksi — pienet paakut ovat ok.','Kuumenna pannua keskilämmöllä.','Kaada noin ¼ dl taikinaa per lettu.','Paista kunnes pintaan nousee kuplia, käännä ja paista kullanruskeaksi.','Tarjoile vaahterasiirapilla ja marjoilla.'], ], [ 'id' => 'bolognese', 'emoji' => '🍝', 'title' => 'Klassinen spagetti bolognese', 'category' => 'recipes', 'author' => 'Tiina K.', 'time' => '1 t 20 min', 'servings' => '6 annosta', 'type' => 'recipe', 'desc' => 'Runsas, hitaasti haudutettu lihamauste al dente -spagetin päällä. Ajaton italialainen klassikko.', 'ingredients' => ['500 g jauhelihaa','400 g spagettia','1 sipuli, hienonnettuna','3 valkosipulinkynttä','800 g murskattuja tomaatteja','2 dl punaviiniä','2 rkl tomaattipyrettä','Suolaa, pippuria, basilikaa, oreganoa','Parmesaanijuustoa tarjoiluun'], 'steps' => ['Kuullota sipuli ja valkosipuli oliiviöljyssä.','Ruskista jauheliha.','Lisää viini ja anna pelkistyä (5 min).','Lisää tomaatit ja mausteet.','Hauduta miedolla lämmöllä 1 tunti.','Keitä spagetti al denteksi.','Tarjoile parmesaanin kera.'], ], [ 'id' => 'cookies', 'emoji' => '🍪', 'title' => 'Suklaahippukeksit', 'category' => 'recipes', 'author' => 'Riitta H.', 'time' => '30 min', 'servings' => '24 keksiä', 'type' => 'recipe', 'desc' => 'Sitkeä sisältä, rapea reunoilta — täydellinen kotitekoinen keksi.', 'ingredients' => ['5½ dl vehnäjauhoja','1 tl ruokasoodaa','1 tl suolaa','225 g pehmeää voita','1½ dl sokeria','1½ dl ruskeaa sokeria','2 munaa','2 tl vaniljauutetta','4 dl suklaahippuja'], 'steps' => ['Kuumenna uuni 190°C.','Sekoita jauhot, sooda ja suola.','Vatkaa voi ja sokerit kuohkeaksi.','Lisää munat ja vanilja.','Yhdistä aineet ja lisää suklaa.','Lusikoi pellille.','Paista 9–11 min kullanruskeaksi.'], ], [ 'id' => 'soup', 'emoji' => '🍲', 'title' => 'Täyttävä kasviskeitto', 'category' => 'recipes', 'subcategory' => 'kasvis', 'author' => 'Sinikka', 'time' => '45 min', 'servings' => '4 annosta', 'type' => 'recipe', 'desc' => 'Lämmittävä kulhollinen paksuja kasviksia rikkaassa yrttiliemessä.', 'ingredients' => ['2 rkl oliiviöljyä','1 sipuli','3 valkosipulia','3 porkkanaa, siivuina','2 perunaa, kuutioina','1 kesäkurpitsa','400 g tomaattimurskaa','1½ l kasvislientä','Timjami, rosmariini, suola, pippuri'], 'steps' => ['Kuullota sipuli ja valkosipuli.','Lisää kasvikset ja sekoittele 5 min.','Kaada liemi ja tomaatit, lisää yrtit.','Hauduta 25 min.','Lisää kesäkurpitsa, keitä 10 min.','Mausta ja tarjoile.'], ], [ 'id' => 'banana_bread', 'emoji' => '🍌', 'title' => 'Mehevä banaanileipä', 'category' => 'recipes', 'author' => 'Tiina K.', 'time' => '1 t 10 min', 'servings' => '10 siivua', 'type' => 'recipe', 'desc' => 'Täydellinen tapa käyttää ylikypsät banaanit. Kosteaa, makeaa ja ihanan aromaattista.', 'ingredients' => ['3 ylikypsää banaania','2 dl vehnäjauhoja','1 tl ruokasoodaa','½ tl suolaa','75 g sulatettua voita','1½ dl sokeria','1 muna','1 tl vaniljauutetta','½ dl smetanaa tai piimää'], 'steps' => ['Kuumenna uuni 175°C ja voitele leipävuoka.','Soseuta banaanit haarukalla.','Sekoita jauhot, sooda ja suola kulhossa.','Yhdistä banaanisose, voi, sokeri, muna ja vanilja toisessa kulhossa.','Sekoita kuivat ja märät ainekset, lisää smetana.','Kaada taikina vuokaan.','Paista 60–65 min, kunnes tikku tulee puhtaana ulos.','Anna jäähtyä ennen leikkaamista.'], ], [ 'id' => 'salmon_soup', 'emoji' => '🐟', 'title' => 'Perinteinen lohikeitto', 'category' => 'recipes', 'author' => 'Sinikka', 'time' => '40 min', 'servings' => '4 annosta', 'type' => 'recipe', 'desc' => 'Kermainen ja täyteläinen lohikeitto — suomalainen sieluruoka parhaimmillaan.', 'ingredients' => ['500 g lohifileetä, kuutioina','4 perunaa, kuutioina','2 porkkanaa, siivuina','1 purjo, siivuina','1 l kalaliemi tai vesi','2 dl kuohukermaa','Tilliä, suolaa, valkopippuria','Voita paistamiseen'], 'steps' => ['Kuullota purjo voissa kattilassa.','Lisää porkkanat ja perunat, paistele 3 min.','Kaada liemi ja keitä kasvikset kypsiksi (15 min).','Lisää lohikuutiot ja keitä 5–7 min.','Kaada kerma ja kuumenna (ei kiehauta).','Mausta tillillä, suolalla ja pippurilla.','Tarjoile ruisleivän kera.'], ], [ 'id' => 'cinnamon_rolls', 'emoji' => '🌀', 'title' => 'Korvapuustit', 'category' => 'recipes', 'author' => 'Riitta H.', 'time' => '2 t 30 min', 'servings' => '20 kpl', 'type' => 'recipe', 'desc' => 'Pehmeät, kanelilla ja kardemummalla maustetut korvapuustit — suomalaiseen kahvipöytään kuuluvat klassikoista klassisin.', 'ingredients' => ['5 dl maitoa','50 g hiivaa','1½ dl sokeria','1 tl suolaa','2 tl jauhettua kardemummaa','noin 13 dl vehnäjauhoja','150 g pehmeää voita','Täyte: 100 g pehmeää voita, 1 dl sokeria, 2 rkl kanelia','Munaa voiteluun, karkeaa sokeria päälle'], 'steps' => ['Lämmitä maito 37°C ja liuota siihen hiiva.','Lisää sokeri, suola, kardemumma ja puolet jauhoista, sekoita.','Lisää voi ja loput jauhot, vaivaa pehmeäksi taikinaksi.','Anna kohota liinan alla 45 min.','Kauli taikina suorakaiteeksi, levitä täyte.','Kääri rullaksi ja leikkaa viipaleiksi, asettele pellille.','Anna kohota 30 min, voitele munalla, ripottele sokeria.','Paista 200°C 12–15 min kullanruskeaksi.'], ], [ 'id' => 'strawberry_quark', 'emoji' => '🍓', 'title' => 'Mansikkarahka', 'category' => 'recipes', 'subcategory' => 'jalkiruuat', 'author' => 'Tiina K.', 'time' => '15 min', 'servings' => '4 annosta', 'type' => 'recipe', 'desc' => 'Helppo ja raikas jälkiruoka kesän makeimmista mansikoista. Valmistuu vartin kymmenessä minuutissa!', 'ingredients' => ['500 g tuoreita mansikoita','400 g rahkaa tai maitorahkaa','2 dl kuohukermaa','3 rkl sokeria tai hunajaa','1 tl vaniljasokeria','Mintunlehtiä koristeluun'], 'steps' => ['Soseuta puolet mansikoista haarukalla tai sauvasekoittimella.','Vatkaa kerma löysäksi vaahdoksi.','Sekoita rahka, mansikasose, sokeri ja vaniljasokeri.','Nostele kermavaahto joukkoon.','Pilko loput mansikat paloiksi ja sekoita tai laita päälle.','Jäähdytä tunnin verran ennen tarjoilua.'], ], [ 'id' => 'carrot_ginger_soup', 'emoji' => '🥕', 'title' => 'Porkkana-inkiväärikeitto', 'category' => 'recipes', 'subcategory' => 'vegaaniset', 'author' => 'Maija-Liisa', 'time' => '35 min', 'servings' => '4 annosta', 'type' => 'recipe', 'desc' => 'Kirpeä inkivääri ja makea porkkana luovat täydellisen liiton tässä lämmittävässä keitossa.', 'ingredients' => ['700 g porkkanoita, siivuina','1 sipuli','3 valkosipulinkynttä','4 cm tuoretta inkivääriä, raastettuna','1 l kasvislientä','2 dl kookoskermaa','2 rkl oliiviöljyä','Suolaa, pippuria','Korianteria tai persiljaa tarjoiluun'], 'steps' => ['Kuullota sipuli ja valkosipuli öljyssä.','Lisää inkivääri, paistele 1 min.','Lisää porkkanat ja liemi.','Keitä 20 min kunnes porkkanat ovat pehmeitä.','Soseuta keitto sauvasekoittimella.','Lisää kookoskerma ja kuumenna.','Mausta ja tarjoile yrteillä koristeltuna.'], ], [ 'id' => 'homemade_pizza', 'emoji' => '🍕', 'title' => 'Kotitekoinen pizza', 'category' => 'recipes', 'author' => 'Sinikka', 'time' => '1 t 30 min', 'servings' => '2 pizzaa', 'type' => 'recipe', 'desc' => 'Rapea, ohut pohja ja täyteläinen tomaattikastike. Kotipizza voittaa aina.', 'ingredients' => ['3 dl lämmintä vettä','1 tl kuivahiivaa','1 tl sokeria','1 tl suolaa','5 dl vehnäjauhoja','2 rkl oliiviöljyä','Kastike: 400 g tomaattimurskaa, 2 valkosipulinkynttä, oreganoa','Täytteet: mozzarella, mitä haluat'], 'steps' => ['Liuota hiiva lämpimään veteen sokerin kera, odota 5 min.','Lisää suola, öljy ja jauhot, vaivaa 8 min.','Anna kohota 1 tunti.','Keitä tomaattikastike valkosipulilla ja oreganolla 10 min.','Kuumenna uuni 250°C.','Kauli taikina ohueksi, levitä kastike, lisää täytteet.','Paista 10–12 min kunnes pohja on rapea.'], ], [ 'id' => 'oat_blueberry_porridge', 'emoji' => '🫐', 'title' => 'Kaura-mustikkapuuro', 'category' => 'recipes', 'subcategory' => 'vegaaniset', 'author' => 'Maija-Liisa', 'time' => '15 min', 'servings' => '2 annosta', 'type' => 'recipe', 'desc' => 'Täyttävä aamiainen, joka antaa energiaa koko aamulle. Mustikka tekee siitä erityisen.', 'ingredients' => ['2 dl kaurahiutaleita','5 dl maitoa tai kasvijuomaa','½ tl suolaa','1 rkl voita','2 dl mustikkaa (tuore tai pakaste)','Hunajaa tai vaahterasiirappia','Mantelilastuja päälle'], 'steps' => ['Mittaa kaura ja maito kattilaan.','Kuumenna sekoittaen keskilämmöllä.','Kun puuro sakenee (n. 8 min), lisää suola ja voi.','Nosta liedeltä, lisää mustikka.','Sekoita kevyesti — mustikka saa jäädä osittain ehjäksi.','Tarjoile hunajalla ja mantelilastuilla.'], ], [ 'id' => 'chicken_risotto', 'emoji' => '🍚', 'title' => 'Kermainen kanarisotto', 'category' => 'recipes', 'author' => 'Tiina K.', 'time' => '45 min', 'servings' => '4 annosta', 'type' => 'recipe', 'desc' => 'Italialaisittain valmistettu risotto, joka muuttuu kermaisen täydelliseksi kärsivällisyydellä.', 'ingredients' => ['300 g risottoriisiä (arborio)','2 kananrintaa, paloina','1 sipuli','3 valkosipulinkynttä','1 l kuumaa kanaliemi','1 dl valkoviiniä','50 g parmesaania, raastettuna','2 rkl voita','Oliiviöljyä, suolaa, pippuria, timjamia'], 'steps' => ['Ruskista kanapalat öljyssä, ota sivuun.','Kuullota sipuli ja valkosipuli samassa pannussa.','Lisää riisi, paistele 2 min.','Kaada viini, sekoita kunnes imeytyy.','Lisää kuumaa lientä kauhallinen kerrallaan, sekoita jatkuvasti.','Jatka 20–25 min kunnes riisi on al dente.','Lisää kana, parmesaani ja voi. Mausta. Tarjoile.'], ], [ 'id' => 'blueberry_pie', 'emoji' => '🫐', 'title' => 'Mustikkapiirakka', 'category' => 'recipes', 'subcategory' => 'jalkiruuat', 'author' => 'Riitta H.', 'time' => '1 t 10 min', 'servings' => '8 palaa', 'type' => 'recipe', 'desc' => 'Mummolan klassikko — murea taikina ja täyteläinen mustikkatäyte. Parasta vaniljakastikkeen kera.', 'ingredients' => ['Pohja: 150 g voita, 1 dl sokeria, 1 muna, 3½ dl vehnäjauhoja, 1 tl leivinjauhetta','Täyte: 4 dl mustikkaa, ½ dl sokeria, 1 rkl perunajauhoja','Vaniljakastiketta tarjoiluun'], 'steps' => ['Kuumenna uuni 200°C.','Vatkaa voi ja sokeri kuohkeaksi, lisää muna.','Sekoita jauhot ja leivinjauhe joukkoon.','Painele ¾ taikinasta piirasvuoan pohjalle ja reunoille.','Sekoita mustikka, sokeri ja perunajauho, levitä täyte.','Murustele loppu taikina päälle.','Paista 35–40 min kullanruskeaksi.'], ], [ 'id' => 'avocado_pasta', 'emoji' => '🥑', 'title' => 'Kermainen avokadopasta', 'category' => 'recipes', 'subcategory' => 'kasvis', 'author' => 'Sinikka', 'time' => '20 min', 'servings' => '2 annosta', 'type' => 'recipe', 'desc' => 'Nopea arkipäivän pasta, jonka kastike valmistuu siinä ajassa kuin pasta kiehuu.', 'ingredients' => ['200 g pastaa','2 kypsää avokadoa','1 valkosipulinkynsi','1 sitruunan mehu','2 rkl oliiviöljyä','Suolaa, pippuria','Kirsikkatomaatteja, basilikanlehtiä tarjoiluun','Parmesaania päälle'], 'steps' => ['Keitä pasta al denteksi, säästä 1 dl keitinvettä.','Soseuta avokado, valkosipuli, sitruunanmehu ja öljy tehosekoittimessa.','Lisää keitinvettä tarpeen mukaan tasaisen kastikkeen saamiseksi.','Mausta suolalla ja pippurilla.','Sekoita pasta ja kastike.','Koristele tomaateilla, basilikaalla ja parmesaanilla.'], ], [ 'id' => 'meatballs', 'emoji' => '🍖', 'title' => 'Lihapullat ruskeassa kastikkeessa', 'category' => 'recipes', 'author' => 'Pekka', 'time' => '1 t', 'servings' => '4 annosta', 'type' => 'recipe', 'desc' => 'Perinteiset kotimaiset lihapullat kermaisen ruskean kastikkeen kera. Tarjoile muusiperunan ja puolukan kera.', 'ingredients' => ['500 g jauhelihaa (naudan ja sian sekoitus)','1 dl korppujauhoja','1 dl maitoa','1 muna','1 sipuli, hienonnettuna','Suolaa, pippuria, maustepippuria','Kastike: 3 dl lihalientä, 1½ dl kermaa, 1 rkl vehnäjauhoja'], 'steps' => ['Liota korppujauhot maidossa.','Sekoita kaikki lihapullan aineet tasaiseksi massaksi.','Pyörittele märillä käsillä tasaisia palloja.','Ruskista lihapullat voissa pannulla.','Siirrä uunivuokaan ja paista 175°C 15 min.','Tee kastike: sekoita jauhot kermaan, lisää liemi, keitä paksuksi.','Tarjoile lihapullat kastikkeessa muusiperunan kera.'], ], [ 'id' => 'cauliflower_soup', 'emoji' => '🥦', 'title' => 'Samettinen kukkakaalikeitto', 'category' => 'recipes', 'subcategory' => 'kasvis', 'author' => 'Sinikka', 'time' => '30 min', 'servings' => '4 annosta', 'type' => 'recipe', 'desc' => 'Silky-sileä kukkakaalikeitto — yllättävän täyteläinen ja herkullinen arki-illalliseksi.', 'ingredients' => ['1 kukkakaali, paloina','1 sipuli','2 valkosipulinkynttä','1 l kasvislientä','2 dl kermaa','2 rkl oliiviöljyä','Suolaa, valkopippuria','Paahdettuja pähkinöitä tai yrttejä tarjoiluun'], 'steps' => ['Kuullota sipuli ja valkosipuli öljyssä.','Lisää kukkakaalipalat ja liemi.','Keitä 20 min kunnes kukkakaali on pehmeää.','Soseuta keitto silkinsileäksi.','Lisää kerma ja kuumenna.','Mausta suolalla ja valkopippurilla.','Tarjoile paahdettujen pähkinöiden kera.'], ], [ 'id' => 'karjalanpaisti', 'emoji' => '🥘', 'title' => 'Karjalanpaisti', 'category' => 'recipes', 'author' => 'Pekka', 'time' => '3–4 t', 'servings' => '6 annosta', 'type' => 'recipe', 'desc' => 'Perinteinen suomalainen pataruoka, joka hautuu uunissa itsekseen. Tuoksu täyttää koko kodin.', 'ingredients' => ['600 g naudanlihaa (esim. lapa), kuutioina','400 g sianlihaa (esim. niska), kuutioina','2 sipulia, lohkottuna','2 porkkanaa, siivuina','2 laakerinlehteä','10 maustepippuria','Suolaa','Vettä tai lihalientä'], 'steps' => ['Kuumenna uuni 175°C.','Laita lihat, sipulit ja porkkanat uunipastaan tai pataan.','Lisää laakerinlehdet ja maustepippurit.','Kaada vettä tai lientä juuri lihan verran.','Mausta suolalla.','Peitä kannella tai foliolla.','Hauduta uunissa 3–4 tuntia kunnes liha on ylikypsää ja irtoaa.','Tarjoile perunoiden ja puolukkahillon kera.'], ], // ── KÄSITYÖT - Neulominen ──────────────────────────────── [ 'id' => 'knitting_scarf', 'emoji' => '🧶', 'title' => 'Helppo huivi aloittelijalle', 'category' => 'kasityot', 'subcategory' => ['neulominen'], 'author' => 'Riitta H.', 'type' => 'post', 'desc' => 'Neulo kaunis huivi muutamassa tunnissa — täydellinen ensimmäinen projekti!', 'body' => '
Tarvitset:
Ohje: Luo 20 silmukkaa. Neulo suoraan (edestakaisin oikein silmukoin) kunnes huivi on noin 150 cm pitkä. Päättele silmukat ja viimeistele päät. Valmis! 🎉
Vinkki: Paksuilla langoilla ja suurilla neuloilla saat huivista nopeasti valmiin, vaikka olisit aloittelija.
', ], [ 'id' => 'knitting_mittens', 'emoji' => '🧤', 'title' => 'Peukalolapaset aloittelijalle', 'category' => 'kasityot', 'subcategory' => ['neulominen'], 'author' => 'Maija-Liisa', 'type' => 'post', 'desc' => 'Yksinkertaiset lapaset jotka valmistuvat viikonloppuna — lämpimät ja persoonalliset.', 'body' => 'Tarvitset:
Ohje:
Vinkki: Käytä värillistä lankaa tekemään kirjoneuleraita, niin lapasista tulee yksilölliset! 🎨
', ], [ 'id' => 'knitting_hat', 'emoji' => '🎩', 'title' => 'Yksinkertainen pipo', 'category' => 'kasityot', 'subcategory' => ['neulominen'], 'author' => 'Sinikka', 'type' => 'post', 'desc' => 'Pipo syntyy parissa illassa — kiva lahja tai itselle talven varalle.', 'body' => 'Pipo on helppo ja nopea projekti, täydellinen aloittelijalle!
Tarvitset:
Ohje (koko S/M):
Villasukat ovat yksi neulomisen perinteisimmistä ja palkitsevimmista projekteista.
Tarvikeet:
Rakenne:
Ensimmäinen sukka näyttää hassulta — älä luovuta! Toinen onnistuu jo paremmin. 😄
', ], [ 'id' => 'knitting_basics', 'emoji' => '📚', 'title' => 'Neulomisen perusteet — näin aloitat', 'category' => 'kasityot', 'subcategory' => ['neulominen'], 'author' => 'Riitta H.', 'type' => 'post', 'desc' => 'Oletko aina haaveillut neulomisesta mutta et tiedä mistä aloittaa? Tässä kaikki mitä tarvitset tietää.', 'body' => 'Neulominen on rentouttava harrastus, jonka voi aloittaa hyvin pienellä välineistöllä.
Harjoittele ensin pienellä neliöllä ennen kuin alat varsinaiseen projektiin. Pian kädet muistavat liikkeet itsestään! 🧶
', ], [ 'id' => 'knitting_baby_hat', 'emoji' => '👶', 'title' => 'Vauvan neulottu pipo', 'category' => 'kasityot', 'subcategory' => ['neulominen'], 'author' => 'Sinikka', 'type' => 'post', 'desc' => 'Söpö ja pehmeä pipo vauvalle — syntyy muutamassa tunnissa ja on heti käyttövalmis.', 'body' => 'Vauvan pipo on ihanteellinen lahjaprojekti — pieni, nopea ja aina tarpeellinen. Sopii noin 0–6 kk ikäiselle.
Tarvikeet:
Ohje:
💡 Valitse aina pehmeä, ihoa ärsyttämätön lanka vauvoille!
', ], [ 'id' => 'knitting_dishcloth', 'emoji' => '🫧', 'title' => 'Neulottu tiskiliina', 'category' => 'kasityot', 'subcategory' => ['neulominen'], 'author' => 'Tiina K.', 'type' => 'post', 'desc' => 'Ekologinen vaihtoehto kertakäyttöisille tiskisienille. Valmistuu tunnissa ja kestää vuosia.', 'body' => 'Neulottu tiskiliina on täydellinen ensiprojekti — yksinkertainen, nopea ja hyödyllinen!
Tarvikeet:
Ohje:
Helmineule tarkoittaa, että molemmat puolet ovat samannäköisiä. Tee useampia eri väreissä! 🎨
', ], [ 'id' => 'knitting_cowl', 'emoji' => '🌀', 'title' => 'Kauluri talveen', 'category' => 'kasityot', 'subcategory' => ['neulominen'], 'author' => 'Sinikka', 'type' => 'post', 'desc' => 'Muhkea kauluri pitää lämpimänä ja on käytännöllisempi kuin huivi — ei liiku paikaltaan!', 'body' => 'Kauluri pysyy paikallaan eikä tipu, toisin kuin huivi. Tämä kauluri on nopea neuloa.
Tarvikeet:
Ohje:
Joustinpäättely antaa kauniin ja joustavan reunuksen. ✨
', ], [ 'id' => 'crochet_basket', 'emoji' => '🧺', 'title' => 'Virkkaa kori langantähteistä', 'category' => 'kasityot', 'subcategory' => ['virkkaus'], 'author' => 'Tiina K.', 'type' => 'post', 'desc' => 'Käytä lankatähteet hyödyksi! Kori syntyy muutamassa tunnissa ja sopii kaikkeen säilyttämiseen.', 'body' => 'Tässä pääset eroon lankatähteistä luovasti. Yhdistä eri värejä raidalliseen koriin!
Tarvikeet:
Ohje:
💡 Paksuilla langoilla kori kasvaa nopeasti. Kokeile tehdä eri kokoisia koreja sarjana!
', ], // ── HYVINVOINTI - vinkkejä ──────────────────────────────── [ 'id' => 'tip_morning', 'emoji' => '💡', 'title' => 'Rauhallinen aamurutiini', 'category' => 'hyvinvointi', 'subcategory' => ['mieli'], 'author' => 'Maija-Liisa', 'type' => 'post', 'desc' => 'Pienet muutokset aamurutiiniin tekevät koko päivästä paremman.', 'body' => 'Kokeile näitä vinkkejä parempaan aamuun:
Pienet muutokset, iso vaikutus!
', ], [ 'id' => 'tip_digital_detox', 'emoji' => '📵', 'title' => 'Digipaasto — kokeile viikon ajan', 'category' => 'hyvinvointi', 'subcategory' => ['mieli'], 'author' => 'Riitta H.', 'type' => 'post', 'desc' => 'Vähennä ruutuaikaa ja huomaa miten elämä muuttuu. Ei tarvitse lopettaa kokonaan!', 'body' => 'Kokeilin viime kuussa viikon digipaastoa ja muutos oli hämmästyttävä. Tässä omat vinkkini:
Nukuin paremmin jo ensimmäisestä yöstä alkaen. Ja parasta: tajusin, miten paljon aikaa minulla oikeasti on!
', ], [ 'id' => 'tip_budgeting', 'emoji' => '💰', 'title' => 'Säästövinkit arkeen', 'category' => 'hyvinvointi', 'subcategory' => ['arki'], 'author' => 'Sinikka', 'type' => 'post', 'desc' => 'Pienet muutokset arjessa voivat säästää satoja euroja vuodessa — ilman että elämänlaatu kärsii.', 'body' => 'Olen oppinut säästämään arjessa ilman tinkimistä mieluisista asioista. Tässä parhaat vinkkini:
Ja muista: paras kauppa on se johon et mene lainkaan! 😄
', ], [ 'id' => 'tip_balcony_garden', 'emoji' => '🌱', 'title' => 'Kasvimaa parvekkeella', 'category' => 'puutarha', 'subcategory' => ['parvekeviljeily'], 'author' => 'Maija-Liisa', 'type' => 'post', 'desc' => 'Oma tomaatti tai yrtit parvekkeen laatikoista — helpompaa kuin luulet!', 'body' => 'Parvekeviljelyä voi harrastaa vaikka asuisi kerrostalossa. Tässä helppo aloitus:
Oman sadon maku on aivan eri kuin kaupan — kannattaa ehdottomasti kokeilla! 🌞
', ], [ 'id' => 'tip_eco_cleaning', 'emoji' => '♻️', 'title' => 'Ekologinen siivous', 'category' => 'hyvinvointi', 'subcategory' => ['arki'], 'author' => 'Sinikka', 'type' => 'post', 'desc' => 'Puhdista koti turvallisilla aineilla — parempi sinulle, parempi ympäristölle ja halvempaa!', 'body' => 'Tehokkain siivousaine löytyy usein kaapistasi: etikka, ruokasooda ja sitruuna tekevät ihmeitä.
Klooripohjaiset aineet ovat tehokkaita mutta raskaita ympäristölle ja hengitysteille. Useimmissa kotitöissä pärjät ilman niitä. Säilytä ne vain tosi tarpeelliseen.
', ], // ── KÄSITYÖT ───────────────────────────────────────────── [ 'id' => 'craft_crochet_heart', 'emoji' => '🧶', 'title' => 'Virkattu sydän — helppo lahja', 'category' => 'kasityot', 'subcategory' => ['virkkaus'], 'author' => 'Riitta H.', 'type' => 'post', 'desc' => 'Pieni virkattu sydän syntyy tunnissa ja on täydellinen lahja tai koriste.', 'body' => 'Tämä söpö sydän on helppo myös aloittelijalle!
Tarvikeet:
Ohje:
💡 Kaksivärinen lanka tekee sydämestä erityisen kauniin!
', ], [ 'id' => 'craft_tote_bag', 'emoji' => '👜', 'title' => 'Ompele oma kangaskassi', 'category' => 'kasityot', 'subcategory' => ['ommelu'], 'author' => 'Tiina K.', 'type' => 'post', 'desc' => 'Ekologinen ja persoonallinen kangaskassi onnistuu jopa ilman ompelukonetta — mutta koneella nopeammin!', 'body' => 'Kangaskassi on helppo ensimmäinen ompelutyö. Tarvitset vain suorat saumat!
Tarvikeet:
Ohje:
Vinkki: Pese kangas ennen ompelemista, niin se ei enää kutistu käytössä. 🧺
', ], [ 'id' => 'craft_macrame', 'emoji' => '🎨', 'title' => 'Makramee-seinäkoriste', 'category' => 'kasityot', 'subcategory' => ['askartelu'], 'author' => 'Maija-Liisa', 'type' => 'post', 'desc' => 'Trendikäs makramee-koriste syntyy muutamassa tunnissa ja koristaa seinää vuosia.', 'body' => 'Makramee on solmimistaidetta joka näyttää vaikeammalta kuin onkaan!
Tarvikeet:
Perussolmut:
Ohje:
Tomaatit viihtyvät mainiosti ruukussa, kunhan valitset parvekelajikkeen.
Valitse parveketomaatti tai kirsikkatomaatti — ne kasvavat pieniksi ja sopivat ruukkuun. Isot beef-tomaatit tarvitsevat enemmän tilaa.
Ensimmäinen oma tomaatti on kuin pieni ihme! 🍅
', ], [ 'id' => 'garden_herbs', 'emoji' => '🌿', 'title' => 'Yrttipuutarha ikkunalaudalle', 'category' => 'puutarha', 'subcategory' => ['parvekeviljeily'], 'author' => 'Maija-Liisa', 'type' => 'post', 'desc' => 'Tuoreita yrttejä suoraan ikkunalaudalta — basilika, persilja ja ruohosipuli onnistuvat helposti.', 'body' => 'Yrttipuutarha on helpoin tapa aloittaa viljely. Pienikin ikkunalauta riittää!
Tuore basilika pastaan tai ruohosipuli munakkaaseen — yksinkertaiset ilot ovat parhaita! 🌞
', ], [ 'id' => 'garden_strawberries', 'emoji' => '🍓', 'title' => 'Mansikat parvekkeen laatikoissa', 'category' => 'puutarha', 'subcategory' => ['parvekeviljeily', 'vihannekset'], 'author' => 'Riitta H.', 'type' => 'post', 'desc' => 'Mansikat viihtyvät hyvin parvekkeen laatikoissa — kesä on makulomaa kun omat mansikat kypsyvät.', 'body' => 'Mansikka on yksi parhaista parvekekasveista — helppohoitoinen ja tuottava!
Ensimmäinen oma mansikka suusta — sen makua ei unohda! 🍓
', ], [ 'id' => 'garden_sunflowers', 'emoji' => '🌻', 'title' => 'Auringonkukat — ilon kasvattajia', 'category' => 'puutarha', 'subcategory' => ['kukat'], 'author' => 'Tiina K.', 'type' => 'post', 'desc' => 'Auringonkukka on helpoin kukkakasvi — siemenet maan ja kesän lopussa on metrin taimi.', 'body' => 'Auringonkukka piristää koko naapuruston ja kasvaa lähes itsekseen!
Elokuussa voit korjata siemenet — ne maistuvat paahdettuna! 🌻
', ], // ── SISUSTUS ───────────────────────────────────────────── [ 'id' => 'deco_flea_market', 'emoji' => '🏺', 'title' => 'Kirpputorilöydöt sisustukseen', 'category' => 'sisustus', 'subcategory' => ['muut'], 'author' => 'Tiina K.', 'type' => 'post', 'desc' => 'Parhaat sisustushelmet löytyvät kirpputoreilta — vinkit mitä etsiä ja miten hyödyntää.', 'body' => 'Kirpputorit ovat aarreaittoja sisustajalle. Tässä vinkkini parhaiden löytöjen tekemiseen!
Lähes mikä tahansa kirpparilöytö paranee maalaamalla. Kreidemaali tarttuu hyvin ilman pohjamaaliakin ja luo kauniin mattapinnan. Suosikkivärejäni ovat valkoinen, savia ja tummanvihreä.
Parhaat löydöt odottavat sinua! 🔍
', ], [ 'id' => 'deco_candles', 'emoji' => '🕯️', 'title' => 'Kynttilät tunnelman luojina', 'category' => 'sisustus', 'subcategory' => ['valaistus'], 'author' => 'Maija-Liisa', 'type' => 'post', 'desc' => 'Kynttilät ovat halvin ja tehokkain tapa muuttaa huoneen tunnelma — vinkit niiden käyttöön.', 'body' => 'Kynttilät ovat sisustuksen salaisin ase. Tässä miten käytän niitä kotonani:
Älä laita yksittäisiä kynttilöitä ympäri huonetta — ryhmittele niitä kolmen tai viiden ryhmiin. Eri korkuiset kynttilät samalla tasolla luovat kauniin asetelman.
Hämärässä kynttilänvalossa kaikki tuntuu paremmalta. 🕯️
', ], [ 'id' => 'deco_gallery_wall', 'emoji' => '🖼️', 'title' => 'Gallerioseinä kotiin — näin se onnistuu', 'category' => 'sisustus', 'subcategory' => ['diy'], 'author' => 'Sinikka', 'type' => 'post', 'desc' => 'Gallerioseinä on tehokkain tapa tuoda persoonallisuutta kotiin. Tässä ohjeet suunnitteluun ja toteutukseen.', 'body' => 'Gallerioseinä kuulostaa hankalalta mutta onnistuu helposti kun suunnittelee ensin!
Oma gallerioseinä on kuin kotimuseo parhaista muistoistasi! 🖼️
', ], // ── KIRJAT ─────────────────────────────────────────────── [ 'id' => 'book_summer_reads', 'emoji' => '📖', 'title' => 'Kesän lukuvinkit — kirjoja jotka upposi', 'category' => 'kirjat', 'subcategory' => ['romaanit'], 'author' => 'Riitta H.', 'type' => 'post', 'desc' => 'Kolme kirjaa joihin upposi tänä kesänä — vaihtelevaa luettavaa eri mielentiloihin.', 'body' => 'Kesä on parasta lukuaikaa. Tässä kolme kirjaa jotka pysäyttivät minut:
Pohjoismainen jännityskirjallisuus ei petä koskaan. Tunnelma, kylmä ilmasto ja monimutkaiset hahmot tekevät siitä koukuttavaa. Etsi kirjastosta suomalaisia tai ruotsalaisia rikosromaaneja.
Välillä tarvitaan kirja jossa on hyvä loppu. Romantiikka tai humoristinen kotimainen proosa sopii täydellisesti mökkilaiturin tunnelmaan.
Paras kesäkirja on se johon palaa ajatuksissaan vielä syksyllä. Sellainen löytyy usein yllättäen — anna kirjaston vinkata!
Hyvää lukuhetkeä! 📚
', ], [ 'id' => 'book_audiobooks', 'emoji' => '🎧', 'title' => 'Äänikirjat — luen nyt enemmän kuin koskaan', 'category' => 'kirjat', 'subcategory' => ['muut'], 'author' => 'Sinikka', 'type' => 'post', 'desc' => 'Äänikirjat muuttivat tapani "lukea" — kuuntelen nykyään keittiössä, lenkillä ja käsitöitä tehdessä.', 'body' => 'Äänikirjat ovat mullistaneet lukuharrastukseni. Tässä miksi suosittelen:
Vinkki: Aloita hitaammalla puhenopeudella (0.9×) jos et ole tottunut. Sopeutut nopeasti ja voit nostaa tahdin! 🎧
', ], // ── HYVINVOINTI ────────────────────────────────────────── [ 'id' => 'wellness_morning', 'emoji' => '🌅', 'title' => 'Aamurutiini joka oikeasti toimii', 'category' => 'hyvinvointi', 'subcategory' => ['mieli'], 'author' => 'Maija-Liisa', 'type' => 'post', 'desc' => 'Kolme pientä muutosta aamuun jotka muuttivat koko päivän laadun.', 'body' => 'Olen kokeillut monia aamurutiinejä. Tässä se mikä oikeasti toimii minulle:
En katso puhelinta ennen kuin olen juonut kahvin. Tuntuu aluksi vaikealta, mutta mieli on paljon rauhallisempi koko aamun.
Ennen kahvia. Keho on kuivunut yön aikana ja vesi käynnistää aineenvaihdunnan.
Minulle se on rauhallinen kahvihetki ikkunan ääressä. Sinulle se voi olla lenkki, meditaatio tai hyvä podcast. Viisi minuuttia riittää.
Et tarvitse täydellistä aamurutiinia — tarvitset vain kolme pientä asiaa. 🌅
', ], [ 'id' => 'wellness_yoga', 'emoji' => '🧘', 'title' => 'Kotijoogaa aloittelijalle — 15 min päivässä', 'category' => 'hyvinvointi', 'subcategory' => ['liikunta'], 'author' => 'Tiina K.', 'type' => 'post', 'desc' => 'Et tarvitse joogastudiota tai erityisiä vaatteita — tässä helppo aloitus kotona.', 'body' => 'Jooga tuntuu pelottavalta aloittaa mutta on itse asiassa hyvin helppo! Tässä oma 15 min aloittelijan ohjelma:
Polvillaan, istuen kantapäillä, kädet edessä lattialla. Hengitä syvään 5 kertaa. Rentouttaa selkää ja rauhoittaa mielen.
Kädet ja jalat lattialla, lantio ylös — muodostuu kolmio. Kantapäät kohti lattiaa (ei haittaa vaikka eivät yletä). 5 hengitystä.
Hyvä asento alaraajoihin. Yksi jalka eteen, toinen taakse, kädet ylös. Pidä 5 hengitystä, vaihda puoli.
YouTube on täynnä ilmaisia aloittelijaohjeita — hae "jooga aloittelijoille suomi"! 🧘
', ], [ 'id' => 'wellness_sleep', 'emoji' => '😴', 'title' => 'Parempi uni — vinkit jotka toimivat', 'category' => 'hyvinvointi', 'subcategory' => ['uni'], 'author' => 'Riitta H.', 'type' => 'post', 'desc' => 'Nukuin huonosti vuosia ennen kuin opin nämä yksinkertaiset asiat.', 'body' => 'Uni on tärkein hyvinvoinnin pilari, mutta niin monelle se on haasteellista. Tässä mitä minulle toimi:
Nukkuminen ei parane yhdessä yössä, mutta jo viikon rutiinilla huomaa eron. 😴
', ], [ 'id' => 'wellness_forest', 'emoji' => '🌲', 'title' => 'Metsäkävelyjen taika', 'category' => 'hyvinvointi', 'subcategory' => ['liikunta', 'mieli'], 'author' => 'Sinikka', 'type' => 'post', 'desc' => 'Tutkimukset vahvistavat sen minkä jokainen suomalainen jo tietää: metsässä oleminen tekee hyvää.', 'body' => 'Olen alkanut käydä metsässä vähintään kerran viikossa, ja muutos hyvinvoinnissani on selvä.
En lataa podcastia. En kuuntele musiikkia. Vain minä ja metsä. Aluksi hiljaisuus tuntuu oudolta — pian se tuntuu parhaimmalta asialta maailmassa.
Jopa 20 minuuttia metsässä laskee verenpainetta. Kokeile! 🌲
', ], // ── LEMMIKIT ───────────────────────────────────────────── [ 'id' => 'pet_dog_training', 'emoji' => '🐕', 'title' => 'Koiran peruskoulutus kotona', 'category' => 'lemmikit', 'subcategory' => ['koirat'], 'author' => 'Maija-Liisa', 'type' => 'post', 'desc' => 'Istu, maahan ja paikka onnistuvat kotona ilman kouluttajaa — kärsivällisyys on avain.', 'body' => 'Kouluttanut koiriani itse jo 15 vuotta. Tässä parhaat peruskäskyt ja vinkit:
Koiran kouluttaminen on mukavaa yhdessäoloa. Nauti siitä! 🐕
', ], [ 'id' => 'pet_cat_enrichment', 'emoji' => '🐈', 'title' => 'Kissan elämää rikastuttavat aktiviteetit', 'category' => 'lemmikit', 'subcategory' => ['kissat'], 'author' => 'Riitta H.', 'type' => 'post', 'desc' => 'Sisäkissa tarvitsee virikkeita — nämä helpot ideat pitävät kissan virkeänä ja onnellisena.', 'body' => 'Sisäkissa elää pitkän ja terveen elämän, mutta tarvitsee virikkeita. Tässä mitä meillä toimii:
15 minuuttia leikkiä päivässä tekee isoja ihmeitä! 🐈
', ], [ 'id' => 'pet_winter', 'emoji' => '❄️', 'title' => 'Lemmikki talvella — vinkit kylmään aikaan', 'category' => 'lemmikit', 'subcategory' => ['muut'], 'author' => 'Tiina K.', 'type' => 'post', 'desc' => 'Kylmä talvi tuo omat haasteensa lemmikkien hoidossa — nämä vinkit auttavat.', 'body' => 'Suomen talvi on rankka sekä meille että lemmikeillämme. Tässä mitä olen oppinut:
Lemmikki ei valita — sinun tehtäväsi on huomata! ❄️
', ], // ── PUUTARHA - Huonekasvit ─────────────────────────────── [ 'id' => 'plant_beginners', 'emoji' => '🪴', 'title' => 'Helpoimmat huonekasvit — ei voi tappaa', 'category' => 'puutarha', 'subcategory' => ['huonekasvit'], 'author' => 'Sinikka', 'type' => 'post', 'desc' => 'Jos olet tappanu jokaisen kasvin jonka olet ostanut, nämä lajikkeet ovat sinulle.', 'body' => 'Kaikki voivat kasvattaa huonekasveja! Avain on valita oikea kasvi oikeaan paikkaan.
Liikakastelu tappaa enemmän kasveja kuin kuivuus. Epäillessäsi — älä kastele. Enemmän on harvoin parempi. 💧
', ], [ 'id' => 'plant_monstera', 'emoji' => '🌿', 'title' => 'Monstera — kuningattaren hoito-opas', 'category' => 'puutarha', 'subcategory' => ['huonekasvit'], 'author' => 'Maija-Liisa', 'type' => 'post', 'desc' => 'Monstera deliciosa on tämän hetken suosituin huonekasvi — ja hyvästä syystä. Tässä kaikki mitä sen hoidosta pitää tietää.', 'body' => 'Monstera on upea kasvi johon rakastuin heti. Tässä mitä olen oppinut sen hoidosta:
Epäsuora kirkas valo on paras. Suora aurinko polttaa lehdet. Sopii hyvin ikkunan viereen etelä- tai länsiseinustalle.
Kastele kun ylimmäinen 5 cm multaa on kuivaa. Talvella harvemmin kuin kesällä. Käytä huoneenlämpöistä vettä.
Kasvukaudella (maalis–elokuu) kerran kuussa nesteylannoitteella. Talvella ei tarvita.
Monstera haluaa kiivetä. Sammalpatsas (moss pole) pitää kasvun pystysuorana ja lehdet kasvavat isommiksi!
Palkitsevin kasvi jonka minulla on. 🌿
', ], [ 'id' => 'plant_seeds', 'emoji' => '🌱', 'title' => 'Kasvien kasvatus siemenistä — kevätprojekti', 'category' => 'puutarha', 'subcategory' => ['vihannekset'], 'author' => 'Riitta H.', 'type' => 'post', 'desc' => 'Oman kasvin kasvattaminen siemenestä on tyydyttävintä mitä puutarhassa voi tehdä.', 'body' => 'Kun ensimmäinen oma taimi nousee mullasta, tuntuu kuin olisi luonut jotain ihmeellistä.
Helmikuu–maaliskuu on hyvä aika aloittaa sisäkylvöt. Taimet siirretään ulos toukokuussa hallojen jälkeen.
Aloita pienestä, nauti prosessista! 🌱
', ], // ── KÄSITYÖT - Ommelu ──────────────────────────────────── [ 'id' => 'craft_sew_apron', 'emoji' => '👘', 'title' => 'Ompele oma esiliina', 'category' => 'kasityot', 'subcategory' => ['ommelu'], 'author' => 'Riitta H.', 'type' => 'post', 'desc' => 'Kaunis ja käytännöllinen esiliina syntyy parissa tunnissa — täydellinen lahja tai itselle.', 'body' => 'Esiliina on yksi parhaimmista ensimmäisistä ommeluprojekteista: suorat saumat, näkyvä tulos, oikeasti hyödyllinen.
Tarvikeet:
Ohje:
Vinkki: Öljypohjainen kangas (wax cotton) on käytännöllinen keittiöesiliinaksi — pyyhkiytyy helposti. 👨🍳
', ], // ── KÄSITYÖT - Askartelu ───────────────────────────────── [ 'id' => 'craft_painted_pot', 'emoji' => '🪣', 'title' => 'Koristele ruukku — helppo askarteluprojekti', 'category' => 'kasityot', 'subcategory' => ['askartelu'], 'author' => 'Sinikka', 'type' => 'post', 'desc' => 'Tavallisesta saviastiaruukusta tulee taideteos muutamalla pensselin vedolla — kokeile rohkeasti!', 'body' => 'Koristeltu ruukku on täydellinen askarteluprojekti: halpa, nopea ja lopputulos on aina käyttökelpoinen.
Lakka loppuun ulkokäyttöä varten. Sisällä pärjää ilmankin. Hauska tehdä samaa aikaan lasten kanssa! 🎨
', ], // ── KÄSITYÖT - Muut ────────────────────────────────────── [ 'id' => 'craft_pressed_flowers', 'emoji' => '🌸', 'title' => 'Pressatut kukat — ikuistaa kesän muistot', 'category' => 'kasityot', 'subcategory' => ['muut'], 'author' => 'Maija-Liisa', 'type' => 'post', 'desc' => 'Kukkainen kesä kestää ikuisesti kun pressaat kukat. Upeita kortteihin, kehyksiin ja kirjanmerkkeihin.', 'body' => 'Kukkaisten pressaus on yksi vanhimmista käsityöperinteistä — ja se on helpompaa kuin luulet.
Pressattuja kukkia voi liimata postikortteihin, kehystää, laminoida kirjanmerkiksi tai koristella kynttilöitä. 🌷
', ], [ 'id' => 'craft_recycled_art', 'emoji' => '♻️', 'title' => 'Taidetta kierrätysmateriaalista', 'category' => 'kasityot', 'subcategory' => ['muut'], 'author' => 'Tiina K.', 'type' => 'post', 'desc' => 'Roskasta syntyy taidetta — kokeile kierrätysmateriaaleista askartelua lasten kanssa tai yksin.', 'body' => 'Paras askartelumateriaali löytyy usein roskakorista tai kierrätyksestä. Tässä suosikkiprojektejani:
Kierrätysaskartelun idea on, ettei tulosten tarvitse olla täydellisiä — prosessi on yhtä tärkeä kuin lopputulos! 🎨
', ], // ── PUUTARHA - Kukat ───────────────────────────────────── [ 'id' => 'garden_spring_bulbs', 'emoji' => '🌷', 'title' => 'Sipulikukat — istuta syksyllä, iloitse keväällä', 'category' => 'puutarha', 'subcategory' => ['kukat'], 'author' => 'Maija-Liisa', 'type' => 'post', 'desc' => 'Tulppaanit, narsissit ja krookukset: istuta lokakuussa ja unohda — ne kukkivat itsekseen keväällä.', 'body' => 'Sipulikukat ovat puutarhanpidon ihanteellisin juttu: teet työn syksyllä ja saat palkinnon keväällä.
Keväällä ensimmäiset versot ovat aina pieni ihme. 🌱
', ], // ── PUUTARHA - Muut ────────────────────────────────────── [ 'id' => 'garden_compost', 'emoji' => '🍂', 'title' => 'Kompostointi kotona — multa maksutta', 'category' => 'puutarha', 'subcategory' => ['muut'], 'author' => 'Riitta H.', 'type' => 'post', 'desc' => 'Kompostointi on helpoin tapa tehdä puutarhalle hyvää ja vähentää kotitalousjätettä samaan aikaan.', 'body' => 'Olen kompostoinut kolme vuotta ja se on yksinkertaisesti paras asia mitä puutarhurina olen tehnyt.
Tasapaino märän ja kuivan välillä. Liian märkä haisee — lisää kuivaa (lehtiä, paperia). Liian kuiva ei hajoa — kostuta. Käännä kauhalla tai haravan varrella kuukausittain, niin happea pääsee sisään.
Valmis komposti on tummaa, mureavaa ja haisee metsältä — ei koskaan pahalta. Käytä kasvien lannoitteena tai mullite! 🌱
', ], [ 'id' => 'garden_winter_prep', 'emoji' => '🍁', 'title' => 'Puutarhan syyskunnostus — näin valmistaudut talveen', 'category' => 'puutarha', 'subcategory' => ['muut'], 'author' => 'Sinikka', 'type' => 'post', 'desc' => 'Muutama tunti syksyllä tekee suuren eron keväällä. Tärkeimmät tehtävät puutarhan talvettamiseen.', 'body' => 'Syyskunnostus kuulostaa isolta projektilta mutta muutamassa tunnissa pärjää hyvin. Tässä omat rutiinini:
Älä leikkaa kaikkea siistiksi — korsien ja siemenpäiden alla talvehtii hyönteisiä. Luonnollinen puutarha on eläväinen myös talvella. ❄️
', ], // ── SISUSTUS - Tekstiilit ───────────────────────────────── [ 'id' => 'deco_cushion_covers', 'emoji' => '🛋️', 'title' => 'Vaihda tyynynpäälliset — halvin tapa uudistaa huone', 'category' => 'sisustus', 'subcategory' => ['tekstiilit'], 'author' => 'Tiina K.', 'type' => 'post', 'desc' => 'Uudet tyynynpäälliset muuttavat olohuoneen tunnelman kymmenessä minuutissa. Vinkit väreihin ja materiaaleihin.', 'body' => 'Jos haluat uudistaa olohuonetta ilman remonttia, tyynynpäälliset ovat halvin ja nopein tapa.
Ota yksi väri huoneesta (esim. maton sävy) ja toista se tyynyissä. Kolme tyynynpäällistä: 2 samaa väriä + 1 erilainen on helppo ja toimiva yhdistelmä.
50×50 cm on yleisin. Osta täyte pyöreänä — pehmeä täyte tekee tyynyistä pulleat ja houkuttelevan näköiset. Litteä täyte näyttää halvalta. 🛋️
', ], [ 'id' => 'deco_curtains', 'emoji' => '🪟', 'title' => 'Oikeat verhot muuttavat huoneen täysin', 'category' => 'sisustus', 'subcategory' => ['tekstiilit'], 'author' => 'Maija-Liisa', 'type' => 'post', 'desc' => 'Verhot ovat sisustuksen aliarvostetuimpia elementtejä. Tässä miten valitset oikeat ja ripustat ne oikein.', 'body' => 'Verhot voivat tehdä tai rikkoa huoneen tunnelman. Yksi virhe toistuu lähes joka kodissa — verhot ripustetaan liian alas ja ne ovat liian lyhyet.
Ripusta tanko mahdollisimman lähelle kattoa (5–10 cm katon alla). Verhot lattiapitkät tai juuri lattian yläpuolella. Tämä tekee huoneesta korkeamman näköisen vaikka se ei olisi.
Verhotangon tulisi olla 30–60 cm leveämpi kuin ikkuna molemmin puolin. Näin verhot eivät peitä ikkunaa valoisana aikana ja tila näyttää suuremmalta.
Pienet muutokset, iso vaikutus! 🪟
', ], // ── SISUSTUS - DIY ──────────────────────────────────────── [ 'id' => 'deco_diy_shelf', 'emoji' => '🪵', 'title' => 'Tee oma seinähylly — helppo aloittelijaprojekti', 'category' => 'sisustus', 'subcategory' => ['diy'], 'author' => 'Sinikka', 'type' => 'post', 'desc' => 'Yksinkertainen seinähylly syntyy parissa tunnissa minimaalisilla välineillä — ja pitää vuosikymmeniä.', 'body' => 'Ensimmäinen oma puutyöprojektini oli seinähylly. Se roikkuu vieläkin seinällä, seitsemän vuotta myöhemmin.
Seinähylly kestää parhaiten kun kiinnität sen seinärakenteeseen tai käytät oikeita puutappeja. Kysy rautakaupasta neuvoa! 🪛
', ], // ── SISUSTUS - Valaistus ────────────────────────────────── [ 'id' => 'deco_fairy_lights', 'emoji' => '✨', 'title' => 'Valosarjat sisätiloihin — tunnelmaa ympäri vuoden', 'category' => 'sisustus', 'subcategory' => ['valaistus'], 'author' => 'Riitta H.', 'type' => 'post', 'desc' => 'Valosarjat eivät ole vain joulukoristelu — oikein käytettynä ne luovat pysyvää tunnelmaa kotiin.', 'body' => 'Olen pitänyt valosarjat esillä ympäri vuoden jo kolme vuotta. Ei ole ainoa joka on löytänyt tämän!
Pimeänä syysiltana niiden valo on korvaamatonta! ✨
', ], // ── KIRJAT - Romaanit ──────────────────────────────────── [ 'id' => 'book_autumn_reads', 'emoji' => '🍂', 'title' => 'Syyskauden lukuvinkit — kirjoja takkavalkean äärelle', 'category' => 'kirjat', 'subcategory' => ['romaanit'], 'author' => 'Maija-Liisa', 'type' => 'post', 'desc' => 'Syysillan paras seura on hyvä kirja. Tässä kolme teemaa jotka sopivat täydellisesti tähän vuodenaikaan.', 'body' => 'Jokin syksyssä tekee lukemisesta erityistä. Tässä kolme lukuteemaa syksyyn:
Synkkä sää ulkona, jännittävä murhamysteeri kädessä — täydellinen yhdistelmä. Pohjoismaiset kirjailijat osaavat tunnelman. Etsi kirjastosta suomalaisia, ruotsalaisia tai norjalaisia dekkareita.
Paksu romaani jossa maailmaan voi uppoutua päiviksi. Historiallinen fiktio toimii tähän erinomaisesti — opit samalla ja tarina kuljettaa. Kysy kirjastovirkailijalta suosituksia.
Ei aina tarvita raskasta luettavaa. Hauska romaani tai kotimainen huumori piristää pimeitä syysiltoja. Nauraa saa myös yksin! 😄
Parasta syksyä — kahvi, viltti ja kirja. 📚
', ], // ── KIRJAT - Tietokirjat ───────────────────────────────── [ 'id' => 'book_nonfiction_picks', 'emoji' => '🧠', 'title' => 'Tietokirjoja jotka muuttivat tapani ajatella', 'category' => 'kirjat', 'subcategory' => ['tietokirjat'], 'author' => 'Sinikka', 'type' => 'post', 'desc' => 'Parhaat tietokirjat eivät vain opeta faktoja — ne muuttavat tavan katsoa maailmaa. Tässä omat suosikkini.', 'body' => 'Olen lukenut tietokirjoja innolla jo vuosia. Nämä kolme teemaa ovat antaneet eniten:
Kirjat ihmismielen toiminnasta ovat aina ajankohtaisia. Ne selittävät miksi teemme valintoja joita teemme, miksi muutokset ovat vaikeita, ja miten tottumukset syntyvät. Tätä aihetta käsittelevät kirjat ovat usein myös kirjoitettu selkeästi ja lukevat helposti.
Luontodokumentit televisiossa ovat hienoja — mutta kirjoissa pääsee vielä syvemmälle. Puiden elämää, lintujen vaellusta tai meren salaisuuksia käsittelevät tietokirjat ovat usein kaunokirjallisuutta kauniimmin kirjoitettuja.
Tavallisten ihmisten historiat ovat kiehtovampia kuin kuninkaiden elämäkerrat. Etsi muistelmia tai mikrohistoriaa — arjen historia on meistä jokaisen historiaa.
Kirjasto on paras paikka aloittaa. Kysy suosituksia — kirjastoammattilaiset ovat alan parhaita asiantuntijoita! 📚
', ], [ 'id' => 'book_history', 'emoji' => '📜', 'title' => 'Historia kertomuksena — tietokirjoja jotka vievät mukanaan', 'category' => 'kirjat', 'subcategory' => ['tietokirjat'], 'author' => 'Riitta H.', 'type' => 'post', 'desc' => 'Historiatietokirjat voivat olla yhtä koukuttavia kuin dekkarit — kun löytää oikeat kirjat.', 'body' => 'Koulussa historia tuntui tylsältä. Sitten löysin tietokirjat jotka kertoivat historian ihmisten kautta — ja koukku oli selvä.
Paras historiatietokirja seuraa ihmisiä, ei tapahtumia. Kun historia kerrotaan yksittäisen henkilön silmistä — tavallisen ihmisen, ei sotaherran — se muuttuu eläväksi.
Hyvä historiatietokirja ei päästä irti ennen kuin on valmis — ja jälkeen haluaa heti lisää. 📜
', ], // ── HYVINVOINTI - Uni ──────────────────────────────────── [ 'id' => 'wellness_power_nap', 'emoji' => '😴', 'title' => 'Päiväunet oikein — 20 minuuttia virkistää täydellisesti', 'category' => 'hyvinvointi', 'subcategory' => ['uni'], 'author' => 'Riitta H.', 'type' => 'post', 'desc' => 'Lyhyet päiväunet ovat tieteellisesti todistettu hyvinvoinnin työkalu — kunhan tietää miten ne otetaan.', 'body' => 'Päiväunet eivät ole laiskuutta — ne ovat tehokkuustyökalu. Tässä miten hyödyntää ne oikein:
Alle 20 minuuttia ja et ehdi mennä syväuneen — herääminen on helppoa ja tunnet olosi virkistyneeksi. Yli 30 minuuttia ja saatat herätä sekavana (unihorroksessa) ja yöunet kärsivät.
Kello 13–15 on luonnollinen energiatason lasku useimmille. Silloin lyhyt tauko toimii parhaiten. Älä nuku myöhemmin kuin kello 15, tai yöunet kärsivät.
Kokeile viikon ajan — ero on selvä! 😴
', ], // ── HYVINVOINTI - Muut ─────────────────────────────────── [ 'id' => 'wellness_hobbies', 'emoji' => '🎨', 'title' => 'Harrastus on paras investointi hyvinvointiin', 'category' => 'hyvinvointi', 'subcategory' => ['muut'], 'author' => 'Tiina K.', 'type' => 'post', 'desc' => 'Harrastus josta et saa rahaa on arvokkaampi kuin luuletkaan — tutkimukset ja oma kokemus puhuvat puolestaan.', 'body' => 'Olen pitänyt harrastukseni yllä kiireisinäkin aikoina, ja se on yksi parhaista päätöksistäni. Tässä miksi:
Kokeile kolme kuukautta jotain aivan uutta. Ei tarvitse olla hyvä heti. Ei tarvitse sitoutua ikuisesti. Pelkkä kokeileminen on jo askel. 🎨
', ], [ 'id' => 'wellness_social', 'emoji' => '🤝', 'title' => 'Ihmissuhteet ovat hyvinvoinnin perusta', 'category' => 'hyvinvointi', 'subcategory' => ['muut'], 'author' => 'Sinikka', 'type' => 'post', 'desc' => 'Tutkimukset ovat yksimielisiä: laadukkaat ihmissuhteet ennustavat hyvinvointia enemmän kuin mikään muu.', 'body' => 'Harvardissa seurattiin ihmisiä yli 80 vuotta selvittääkseen mikä tekee elämästä merkityksellisen. Vastaus ei ollut raha tai menestys — vaan ihmissuhteiden laatu.
Yksinäisyys on yleisempää kuin uskomme. Ei ole hävettävää. Mutta se ei muutu itsestään — ensimmäinen askel on aina oma. Yksikin ihminen riittää.
Pidä ihmiset lähellä. 🤝
', ], // ── LEMMIKIT - Koirat ──────────────────────────────────── [ 'id' => 'pet_dog_nutrition', 'emoji' => '🦴', 'title' => 'Koiran ruokinta — mitä kannattaa tietää', 'category' => 'lemmikit', 'subcategory' => ['koirat'], 'author' => 'Maija-Liisa', 'type' => 'post', 'desc' => 'Koiran ruokavaliolla on suuri vaikutus sen terveyteen ja elämänlaatuun. Perusasiat kuntoon ensin.', 'body' => 'Olen ruokkinut koiriani eri tavoilla ja oppinut kantapään kautta. Tässä tärkeimmät asiat:
Kuivaruoka on käytännöllinen, säilyy pitkään ja laadukas kuivaruoka kattaa ravitsemuksen hyvin. Valitse sellainen jossa liha on ensimmäisenä ainesosalistalla — ei viljoja tai lisäaineita ensin.
Raakaruoka voi sopia joillekin koirille, mutta vaatii enemmän tietoa oikean tasapainon saavuttamiseksi. Jos kiinnostaa, perehdy huolella.
Eläinlääkäri on paras neuvonantaja oman koirasi ruokinnan suhteen! 🐕
', ], // ── LEMMIKIT - Kissat ──────────────────────────────────── [ 'id' => 'pet_cat_diy_toys', 'emoji' => '🧶', 'title' => 'Tee itse kissanleluja — kissat rakastavat', 'category' => 'lemmikit', 'subcategory' => ['kissat'], 'author' => 'Tiina K.', 'type' => 'post', 'desc' => 'Kalliit lelupussit jäävät usein huomiotta — kotitekoiset lelut voittavat lähes aina. Tässä parhaat.', 'body' => 'Kissani on jättänyt kaupan lelut täysin huomiotta mutta leikkii kotitekoisilla tuntikausia. Tässä suosikkini:
Paras lelu on silti sinä itse — 15 minuuttia päivässä sulkasauvan kanssa tekee kissan onnelliseksi! 😸
', ], ]; } function getOrInitPosts(): array { if (!file_exists(DATA_DIR . 'posts.json')) { $posts = defaultPosts(); writeData('posts.json', $posts); return $posts; } $posts = readData('posts.json', []); $changed = false; $result = []; foreach ($posts as $post) { $cat = $post['category'] ?? ''; if (array_key_exists($cat, POST_CATEGORY_MAP)) { $newCat = POST_CATEGORY_MAP[$cat]; if ($newCat === null) { $changed = true; continue; } // poista postaus $post['category'] = $newCat; $changed = true; } $result[] = $post; } if ($changed) writeData('posts.json', $result); return $result; } function getOrInitCategories(): array { if (!file_exists(DATA_DIR . 'categories.json')) { $cats = defaultCategories(); writeData('categories.json', $cats); return $cats; } $cats = readData('categories.json', []); // Merge in subcategories (and new ones) from defaults if missing $defaults = defaultCategories(); $defaultMap = []; foreach ($defaults as $d) { $defaultMap[$d['id']] = $d; } $changed = false; foreach ($cats as &$cat) { $defSubs = $defaultMap[$cat['id']]['subcategories'] ?? []; if (!isset($cat['subcategories'])) { $cat['subcategories'] = $defSubs; $changed = true; } else { // Add any new subcategory ids from defaults that don't exist yet $existingIds = array_column($cat['subcategories'], 'id'); foreach ($defSubs as $ds) { if (!in_array($ds['id'], $existingIds)) { $cat['subcategories'][] = $ds; $changed = true; } } } } unset($cat); // Remove deprecated/merged categories $before = count($cats); $cats = array_values(array_filter($cats, fn($c) => !in_array($c['id'], REMOVED_CATEGORIES))); if (count($cats) !== $before) $changed = true; // Append brand-new default categories that don't exist in file yet $existingCatIds = array_column($cats, 'id'); foreach ($defaults as $def) { if (!in_array($def['id'], $existingCatIds)) { $cats[] = $def; $changed = true; } } if ($changed) writeData('categories.json', $cats); return $cats; } // ─── Routing ─────────────────────────────────────────────────── switch ($action) { case 'posts': ok(['posts' => getOrInitPosts()]); case 'categories': ok(['categories' => getOrInitCategories()]); case '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', []); $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 { $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); } writeData('likes.json', $likes); ok(['liked' => $liked, 'count' => $likes[$postId] ?? 0]); case 'comments': $postId = $_GET['postId'] ?? ''; $comments = readData('comments.json', []); ok(['comments' => $comments[$postId] ?? []]); case 'add_comment': $postId = $body['postId'] ?? ''; $name = trim($body['name'] ?? ''); $text = trim($body['text'] ?? ''); if (!$postId || !$name || !$text) err('Missing fields'); $now = time(); $win = 10 * 60; $times = array_values(array_filter($_SESSION['comment_times'] ?? [], fn($t) => $now - $t < $win)); if (count($times) >= 3) err('Liian monta kommenttia. Odota hetki.'); $times[] = $now; $_SESSION['comment_times'] = $times; $comments = readData('comments.json', []); if (!isset($comments[$postId])) $comments[$postId] = []; $comment = [ 'name' => htmlspecialchars($name, ENT_QUOTES | ENT_HTML5, 'UTF-8'), 'text' => htmlspecialchars($text, ENT_QUOTES | ENT_HTML5, 'UTF-8'), 'time' => date('d.m.Y'), ]; $comments[$postId][] = $comment; writeData('comments.json', $comments); ok(['comment' => $comment]); case 'add_post': $post = $body['post'] ?? []; if (empty($post['title'])) err('Missing title'); $post['id'] = preg_replace('/[^a-z0-9_]/', '', strtolower($post['id'] ?? '')) ?: 'post_' . time(); $posts = getOrInitPosts(); $posts[] = $post; writeData('posts.json', $posts); ok(); case 'update_post': if (!isAdmin()) err('Unauthorized', 403); $post = $body['post'] ?? []; if (empty($post['id'])) err('Missing id'); $posts = getOrInitPosts(); foreach ($posts as &$p) { if ($p['id'] === $post['id']) { $p = $post; break; } } unset($p); writeData('posts.json', $posts); ok(); case 'delete_post': if (!isAdmin()) err('Unauthorized', 403); $id = $body['id'] ?? ''; if (!$id) err('Missing id'); $posts = array_values(array_filter(getOrInitPosts(), fn($p) => ($p['id'] ?? '') !== $id)); writeData('posts.json', $posts); ok(); case 'save_categories': if (!isAdmin()) err('Unauthorized', 403); writeData('categories.json', $body['categories'] ?? []); ok(); case 'seed_posts': if (!isAdmin()) err('Unauthorized', 403); $posts = defaultPosts(); writeData('posts.json', $posts); ok(['count' => count($posts)]); case 'seed_categories': if (!isAdmin()) err('Unauthorized', 403); $cats = defaultCategories(); writeData('categories.json', $cats); ok(['count' => count($cats)]); case 'admin_login': if (($body['password'] ?? '') === ADMIN_PASSWORD) { $_SESSION['tykkaafi_admin'] = true; ok(); } err('Väärä salasana'); case 'admin_logout': $_SESSION['tykkaafi_admin'] = false; ok(); case 'admin_check': ok(['loggedIn' => isAdmin()]); case 'admin_users': if (!isAdmin()) err('Unauthorized', 403); $users = readData('users.json', []); $posts = getOrInitPosts(); $result = []; foreach ($users as $u) { $nick = mb_strtolower($u['nickname']); $postCount = 0; foreach ($posts as $p) { if (mb_strtolower($p['author'] ?? '') === $nick) $postCount++; } $result[] = [ 'id' => $u['id'], 'nickname' => $u['nickname'], 'email' => $u['email'] ?? '', 'created' => $u['created'] ?? '', 'likes' => count($u['likes'] ?? []), 'postCount' => $postCount, ]; } ok(['users' => $result]); // ─── 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]); // ─── Yhteydenotto ────────────────────────────────────────── case 'contact': $name = trim($body['name'] ?? ''); $email = trim($body['email'] ?? ''); $message = trim($body['message'] ?? ''); if (!$name || !$email || !$message) err('Täytä kaikki kentät.'); if (!filter_var($email, FILTER_VALIDATE_EMAIL)) err('Sähköpostiosoite ei kelpaa.'); if (mb_strlen($message) > 5000) err('Viesti on liian pitkä.'); // Tallenna varmuuskopio $messages = readData('messages.json', []); $messages[] = [ 'name' => htmlspecialchars($name, ENT_QUOTES | ENT_HTML5, 'UTF-8'), 'email' => htmlspecialchars($email, ENT_QUOTES | ENT_HTML5, 'UTF-8'), 'message' => htmlspecialchars($message, ENT_QUOTES | ENT_HTML5, 'UTF-8'), 'date' => date('Y-m-d H:i:s'), ]; writeData('messages.json', $messages); // Lähetä sähköposti $to = 'info@tykkaa.fi'; $subject = "tykkää.fi: viesti käyttäjältä $name"; $body = "Nimi: $name\nSähköposti: $email\n\nViesti:\n$message"; $headers = "From: noreply@tykkaa.fi\r\nReply-To: $email\r\nContent-Type: text/plain; charset=UTF-8"; @mail($to, $subject, $body, $headers); ok(['sent' => true]); default: err('Unknown action'); }