Codex-Handover P0: Daten-Ehrlichkeit (Seed=Demo statt 'live/verfügbar', Fake-Namen+Mojibake raus), Finder modell-Param, LIVE_URL via Env, 404-Seite+Favicon, Umami nur mit echter ID, ehrliche Texte/Semantik, Demo-CTA entschärft

This commit is contained in:
till committed 2026-06-28 16:32:06 +00:00
1 parent fba01a70bb
commit ed41ea54d2
9 files changed
+50 -29

No files matched your search

+1 -1
View File
@@ -1 +1 @@
{"generatedAt":"2026-06-28T07:40:00","source":"kuehlfinder-seed multi-country (pgeocode/GeoNames)","stats":{"storesTotal":1755,"withAC":943,"baumarkt":1082,"elektro":673,"countries":6,"updated":"07:40"},"feed":[{"storeId":"de-saturn-friesenried-93c7d","name":"Saturn Friesenried","model":"Dreo 318S","cc":"DE","at":"2026-06-27T07:40:00"},{"storeId":"de-euronics-rodenwalde-22e48","name":"Euronics Rodenwalde","model":"AEG Comfort 6000","cc":"DE","at":"2026-06-27T07:40:00"},{"storeId":"de-obi-reichenwalde-8140f","name":"OBI Reichenwalde","model":"Comfee Mobile 12000","cc":"DE","at":"2026-06-27T07:40:00"},{"storeId":"de-toom-zeitungsverlag-schwerin-gmbh---co--k-5b647","name":"Toom Zeitungsverlag Schwerin GmbH & Co. KG","model":"Midea Silent Cool 26 Pro WF","cc":"DE","at":"2026-06-27T07:40:00"},{"storeId":"de-toom-uelsby-de1a3","name":"Toom Uelsby","model":"Midea PortaSplit Cool 8.000 BTU","cc":"DE","at":"2026-06-27T07:40:00"},{"storeId":"de-saturn-haßfurt-27461","name":"Saturn Haßfurt","model":"Bosch Cool 5000","cc":"DE","at":"2026-06-27T07:40:00"},{"storeId":"de-toom-klosterkumbd-d32a1","name":"Toom Klosterkumbd","model":"Comfee Mobile 12000","cc":"DE","at":"2026-06-27T07:40:00"},{"storeId":"de-euronics-rieps-bbe8e","name":"Euronics Rieps","model":"Bosch Cool 5000","cc":"DE","at":"2026-06-27T07:40:00"},{"storeId":"de-euronics-deutscher-forschungsdienst-eb156","name":"Euronics Deutscher Forschungsdienst","model":"Dreo 318S","cc":"DE","at":"2026-06-27T07:40:00"},{"storeId":"de-euronics-landesmesse-stuttgart-gmbh-6ac9e","name":"Euronics Landesmesse Stuttgart GmbH","model":"Dreo 318S","cc":"DE","at":"2026-06-27T07:40:00"},{"storeId":"de-saturn-beseritz-d8450","name":"Saturn Beseritz","model":"Dreo 318S","cc":"DE","at":"2026-06-27T07:40:00"},{"storeId":"de-toom-bayerisches-verwaltungsgericht-augsb-7977c","name":"Toom Bayerisches Verwaltungsgericht Augsburg","model":"Comfee Mobile 12000","cc":"DE","at":"2026-06-27T07:40:00"},{"storeId":"de-euronics-nephrocare-deutschland-gmbh-2e896","name":"Euronics Nephrocare Deutschland GmbH","model":"AEG Comfort 6000","cc":"DE","at":"2026-06-27T07:40:00"},{"storeId":"de-expert-trinwillershagen-07beb","name":"expert Trinwillershagen","model":"AEG Comfort 6000","cc":"DE","at":"2026-06-27T07:40:00"},{"storeId":"de-expert-lüdersfeld-lüdersfeld-82bc6","name":"expert Lüdersfeld Lüdersfeld","model":"AEG Comfort 6000","cc":"DE","at":"2026-06-27T07:40:00"},{"storeId":"de-saturn-göschitz-e35f4","name":"Saturn Göschitz","model":"Bosch Cool 5000","cc":"DE","at":"2026-06-27T07:40:00"},{"storeId":"de-bauhaus-gersthofen-e9e94","name":"Bauhaus Gersthofen","model":"De'Longhi Pinguino PAC EX105","cc":"DE","at":"2026-06-27T07:40:00"},{"storeId":"de-hagebau-clenze-prießeck-a3f8d","name":"Hagebau Clenze Prießeck","model":"Midea Silent Cool 26 Pro WF","cc":"DE","at":"2026-06-27T07:40:00"},{"storeId":"de-euronics-karwitz-karwitz-060f2","name":"Euronics Karwitz Karwitz","model":"De'Longhi Pinguino PAC EX105","cc":"DE","at":"2026-06-27T07:40:00"},{"storeId":"de-obi-svd-versicherungsdienst-gmbh-bf912","name":"OBI SVD Versicherungsdienst GmbH","model":"Midea Silent Cool 26 Pro WF","cc":"DE","at":"2026-06-27T07:40:00"},{"storeId":"de-mediamarkt-stuttgart-rohr-316a8","name":"MediaMarkt Stuttgart Rohr","model":"Bosch Cool 5000","cc":"DE","at":"2026-06-27T07:40:00"},{"storeId":"de-mediamarkt-wahlstorf-af522","name":"MediaMarkt Wahlstorf","model":"De'Longhi Pinguino PAC EX105","cc":"DE","at":"2026-06-27T07:40:00"},{"storeId":"at-hornbach-unteraich-3b42e","name":"Hornbach Unteraich","model":"Comfee Mobile 12000","cc":"AT","at":"2026-06-27T07:40:00"},{"storeId":"at-mediamarkt-sprinzenberg-bb527","name":"MediaMarkt Sprinzenberg","model":"De'Longhi Pinguino PAC EX105","cc":"AT","at":"2026-06-27T07:40:00"},{"storeId":"ch-hornbach-arboldswil-c2aa6","name":"Hornbach Arboldswil","model":"Midea Silent Cool 26 Pro WF","cc":"CH","at":"2026-06-27T07:40:00"},{"storeId":"ch-mediamarkt-cousset-38de9","name":"MediaMarkt Cousset","model":"Comfee Mobile 12000","cc":"CH","at":"2026-06-27T07:40:00"},{"storeId":"ch-hornbach-visp-828f2","name":"Hornbach Visp","model":"Comfee Mobile 12000","cc":"CH","at":"2026-06-27T07:40:00"},{"storeId":"ch-interdiscount-visp-6f87a","name":"Interdiscount Visp","model":"AEG Comfort 6000","cc":"CH","at":"2026-06-27T07:40:00"},{"storeId":"ch-jumbo-lajoux-ju-4698f","name":"Jumbo Lajoux JU","model":"Midea Silent Cool 26 Pro WF","cc":"CH","at":"2026-06-27T07:40:00"},{"storeId":"nl-bcc-oosterstreek-912d6","name":"BCC Oosterstreek","model":"Bosch Cool 5000","cc":"NL","at":"2026-06-27T07:40:00"}],"online":[{"src":"idealo","kind":"preisvergleich","model":"Midea PortaSplit","a":0,"p":890,"trust":5,"url":"https://www.idealo.de"},{"src":"Amazon","kind":"online","model":"Midea PortaSplit","a":1,"p":872,"trust":5,"url":"https://www.amazon.de"},{"src":"Amazon Marketplace","kind":"drittanbieter","model":"Midea PortaSplit","a":1,"p":3371,"wucher":true,"trust":2,"url":"https://www.amazon.de"},{"src":"eBay (gLine truncated
{"generatedAt":"2026-06-28T12:30:00","source":"seed-demo-coverage","note":"Standort-Abdeckung als Demo. KEINE verifizierten Filialbestände. Verifizierte Live-Preise nur via Crawler (/api/live).","stats":{"storesTotal":1531,"countries":6,"baumarkt":909,"elektro":622,"updated":"12:30"},"feed":[],"online":[],"priceOverview":{"midea-portasplit-12000":{"uvp":899,"min":809,"avg":899,"max":953,"ebayLow":683,"ebayHigh":1753,"refurb":737,"wucherFrom":1169},"midea-portasplit-cool-8000":{"uvp":749,"min":674,"avg":749,"max":794,"ebayLow":569,"ebayHigh":1461,"refurb":614,"wucherFrom":974},"delonghi-pac-ex105":{"uvp":699,"min":629,"avg":699,"max":741,"ebayLow":531,"ebayHigh":1363,"refurb":573,"wucherFrom":909},"bosch-cool-5000":{"uvp":799,"min":719,"avg":799,"max":847,"ebayLow":607,"ebayHigh":1558,"refurb":655,"wucherFrom":1039},"aeg-comfort-6000":{"uvp":649,"min":584,"avg":649,"max":688,"ebayLow":493,"ebayHigh":1266,"refurb":532,"wucherFrom":844},"dreo-318s":{"uvp":429,"min":386,"avg":429,"max":455,"ebayLow":326,"ebayHigh":837,"refurb":352,"wucherFrom":558},"midea-silent-cool-26-pro-wf":{"uvp":499,"min":449,"avg":499,"max":529,"ebayLow":379,"ebayHigh":973,"refurb":409,"wucherFrom":649},"comfee-mobile-12000":{"uvp":379,"min":341,"avg":379,"max":402,"ebayLow":288,"ebayHigh":739,"refurb":311,"wucherFrom":493},"remko-rkl-495-dc":{"uvp":1599,"min":1439,"avg":1599,"max":1695,"ebayLow":1215,"ebayHigh":3118,"refurb":1311,"wucherFrom":2079}},"products":{"midea-portasplit-12000":{"name":"Midea PortaSplit 12.000 BTU","uvp":899,"type":"mobile_split","apartment":true},"midea-portasplit-cool-8000":{"name":"Midea PortaSplit Cool 8.000 BTU","uvp":749,"type":"mobile_split","apartment":true},"delonghi-pac-ex105":{"name":"De'Longhi Pinguino PAC EX105","uvp":699,"type":"monoblock","apartment":true},"bosch-cool-5000":{"name":"Bosch Cool 5000","uvp":799,"type":"monoblock","apartment":true},"aeg-comfort-6000":{"name":"AEG Comfort 6000","uvp":649,"type":"monoblock","apartment":true},"dreo-318s":{"name":"Dreo 318S","uvp":429,"type":"monoblock","apartment":true},"midea-silent-cool-26-pro-wf":{"name":"Midea Silent Cool 26 Pro WF","uvp":499,"type":"monoblock","apartment":true},"comfee-mobile-12000":{"name":"Comfee Mobile 12000","uvp":379,"type":"monoblock","apartment":true},"remko-rkl-495-dc":{"name":"Remko RKL 495 DC","uvp":1599,"type":"mobile_split","apartment":false}},"stores":[{"id":"de-expert-brand-b8c5a","c":"expert","kind":"elektro","cc":"DE","cur":"EUR","flag":"🇩🇪","n":"expert Brand","ci":"Brand","z":"95682","la":49.9563,"lo":11.9012,"sourceMode":"seed","models":[{"id":"bosch-cool-5000","name":"Bosch Cool 5000","a":0,"p":739},{"id":"comfee-mobile-12000","name":"Comfee Mobile 12000","a":0,"p":377},{"id":"delonghi-pac-ex105","name":"De'Longhi Pinguino PAC EX105","a":0,"p":731},{"id":"dreo-318s","name":"Dreo 318S","a":0,"p":459}],"any":{"a":0,"n":0,"minP":null}},{"id":"de-toom-brand-33589","c":"Toom","kind":"baumarkt","cc":"DE","cur":"EUR","flag":"🇩🇪","n":"Toom Brand","ci":"Brand","z":"95682","la":49.9518,"lo":11.903,"sourceMode":"seed","models":[{"id":"delonghi-pac-ex105","name":"De'Longhi Pinguino PAC EX105","a":0,"p":724},{"id":"midea-portasplit-cool-8000","name":"Midea PortaSplit Cool 8.000 BTU","a":0,"p":792}],"any":{"a":0,"n":0,"minP":null}},{"id":"de-euronics-brand-51601","c":"Euronics","kind":"elektro","cc":"DE","cur":"EUR","flag":"🇩🇪","n":"Euronics Brand","ci":"Brand","z":"95682","la":49.969,"lo":11.9202,"sourceMode":"seed","models":[{"id":"aeg-comfort-6000","name":"AEG Comfort 6000","a":0,"p":643},{"id":"bosch-cool-5000","name":"Bosch Cool 5000","a":0,"p":772}],"any":{"a":0,"n":0,"minP":null}},{"id":"de-globus-lüder-bcfcd","c":"Globus","kind":"baumarkt","cc":"DE","cur":"EUR","flag":"🇩🇪","n":"Globus Lüder","ci":"Lüder","z":"29394","la":52.8068,"lo":10.6683,"sourceMode":"seed","models":[{"id":"delonghi-pac-ex105","name":"De'Longhi Pinguino PAC EX105","a":0,"p":732},{"id":"midea-portasplit-12000","name":"Midea PortaSplit 12.000 BTU","a":0,"p":918},{"id":"midea-silent-cool-26-pro-wf","name":"Midea Silent Cool 26 Pro WF","a":0,"p":472},{"id":"comfee-mobile-12000","name":"Comfee Mobile 12000","a":0,"p":366}],"any":{"a":0,"n":0,"minP":null}},{"id":"de-bauhaus-lüder-0866d","c":"Bauhaus","kind":"baumarkt","cc":"DE","cur":"EUR","flag":"🇩🇪","n":"Bauhaus Lüder","ci":"Lüder","z":"29394","la":52.8138,"lo":10.6646,"sourceMode":"seed","models":[{"id":"midea-portasplit-cool-8000","name":"Midea PortaSplit Cool 8.000 BTU","a":0,"p":707},{"id":"midea-silent-cool-26-pro-wf","name":"Midea Silent Cool 26 Pro WF","a":0,"p":524}],"any":{"a":0,"n":0,"minP":null}},{"id":"de-mediamarkt-janneby-bb13a","c":"MediaMarkt","kind":"elektro","cc":"DE","cur":"EUR","flag":"🇩🇪","n":"MediaMarkt Janneby","ci":"Janneby","z":"24992","la":54.6271,"lo":9.2993,"sourceMode":"seed","models":[{"id":"delonghi-pac-ex105","name":"De'Longhi Pinguino PAC EX105","a":0,"p":702},{"id":"comfee-mobile-12000","name":"Comfee Mobile 12000","a":0,"p":Line truncated
+1 -1
View File
@@ -1 +1 @@
[{"id": "de-saturn-lebus-lebus-fa4e5", "chain": "Saturn", "kind": "elektro", "cc": "DE", "name": "Saturn Lebus Lebus", "plz": "15326", "city": "Lebus Lebus", "lat": 52.4262, "lng": 14.5338}, {"id": "de-expert-lebus-lebus-c3958", "chain": "expert", "kind": "elektro", "cc": "DE", "name": "expert Lebus Lebus", "plz": "15326", "city": "Lebus Lebus", "lat": 52.4293, "lng": 14.5381}, {"id": "de-euronics-lebus-lebus-72acd", "chain": "Euronics", "kind": "elektro", "cc": "DE", "name": "Euronics Lebus Lebus", "plz": "15326", "city": "Lebus Lebus", "lat": 52.4185, "lng": 14.53}, {"id": "de-toom-loddenkemper-raumsysteme-gmbh---co-k-6606e", "chain": "Toom", "kind": "baumarkt", "cc": "DE", "name": "Toom Loddenkemper Raumsysteme GmbH + Co.KG", "plz": "59299", "city": "Loddenkemper Raumsysteme GmbH + Co.KG", "lat": 51.8392, "lng": 8.1521}, {"id": "de-expert-loddenkemper-raumsysteme-gmbh---co-51e11", "chain": "expert", "kind": "elektro", "cc": "DE", "name": "expert Loddenkemper Raumsysteme GmbH + Co.KG", "plz": "59299", "city": "Loddenkemper Raumsysteme GmbH + Co.KG", "lat": 51.8575, "lng": 8.1394}, {"id": "de-bauhaus-geusa-59739", "chain": "Bauhaus", "kind": "baumarkt", "cc": "DE", "name": "Bauhaus Geusa", "plz": "06217", "city": "Geusa", "lat": 51.3367, "lng": 11.9432}, {"id": "de-obi-geusa-bbf78", "chain": "OBI", "kind": "baumarkt", "cc": "DE", "name": "OBI Geusa", "plz": "06217", "city": "Geusa", "lat": 51.3307, "lng": 11.9395}, {"id": "de-saturn-psd-bank-frankfurt-am-main-eg-7f0b5", "chain": "Saturn", "kind": "elektro", "cc": "DE", "name": "Saturn PSD Bank Frankfurt am Main eG", "plz": "65756", "city": "PSD Bank Frankfurt am Main eG", "lat": 50.1272, "lng": 8.5698}, {"id": "de-globus-psd-bank-frankfurt-am-main-eg-14dad", "chain": "Globus", "kind": "baumarkt", "cc": "DE", "name": "Globus PSD Bank Frankfurt am Main eG", "plz": "65756", "city": "PSD Bank Frankfurt am Main eG", "lat": 50.1344, "lng": 8.5571}, {"id": "de-euronics-psd-bank-frankfurt-am-main-eg-a77f3", "chain": "Euronics", "kind": "elektro", "cc": "DE", "name": "Euronics PSD Bank Frankfurt am Main eG", "plz": "65756", "city": "PSD Bank Frankfurt am Main eG", "lat": 50.1337, "lng": 8.5562}, {"id": "de-hagebau-landgericht-kã-ln-a3577", "chain": "Hagebau", "kind": "baumarkt", "cc": "DE", "name": "Hagebau Landgericht Köln", "plz": "50922", "city": "Landgericht Köln", "lat": 50.931, "lng": 6.9396}, {"id": "de-mediamarkt-landgericht-kã-ln-524b0", "chain": "MediaMarkt", "kind": "elektro", "cc": "DE", "name": "MediaMarkt Landgericht Köln", "plz": "50922", "city": "Landgericht Köln", "lat": 50.9208, "lng": 6.9454}, {"id": "de-hornbach-hasselberg-a394c", "chain": "Hornbach", "kind": "baumarkt", "cc": "DE", "name": "Hornbach Hasselberg", "plz": "24376", "city": "Hasselberg", "lat": 54.7254, "lng": 9.9805}, {"id": "de-expert-hasselberg-c669f", "chain": "expert", "kind": "elektro", "cc": "DE", "name": "expert Hasselberg", "plz": "24376", "city": "Hasselberg", "lat": 54.7209, "lng": 9.982}, {"id": "de-hagebau-glauzig-ac5e1", "chain": "Hagebau", "kind": "baumarkt", "cc": "DE", "name": "Hagebau Glauzig", "plz": "06369", "city": "Glauzig", "lat": 51.6468, "lng": 11.9979}, {"id": "de-mediamarkt-glauzig-a7224", "chain": "MediaMarkt", "kind": "elektro", "cc": "DE", "name": "MediaMarkt Glauzig", "plz": "06369", "city": "Glauzig", "lat": 51.6496, "lng": 11.9922}, {"id": "de-saturn-waddeweitz-salderatzen-d5365", "chain": "Saturn", "kind": "elektro", "cc": "DE", "name": "Saturn Waddeweitz Salderatzen", "plz": "29496", "city": "Waddeweitz Salderatzen", "lat": 52.9765, "lng": 11.0097}, {"id": "de-mediamarkt-waddeweitz-salderatzen-4954f", "chain": "MediaMarkt", "kind": "elektro", "cc": "DE", "name": "MediaMarkt Waddeweitz Salderatzen", "plz": "29496", "city": "Waddeweitz Salderatzen", "lat": 52.9942, "lng": 11.0032}, {"id": "de-hagebau-linthe-f53b3", "chain": "Hagebau", "kind": "baumarkt", "cc": "DE", "name": "Hagebau Linthe", "plz": "14822", "city": "Linthe", "lat": 52.1977, "lng": 12.7822}, {"id": "de-globus-linthe-6b2fd", "chain": "Globus", "kind": "baumarkt", "cc": "DE", "name": "Globus Linthe", "plz": "14822", "city": "Linthe", "lat": 52.1906, "lng": 12.7794}, {"id": "de-toom-linthe-0b4d4", "chain": "Toom", "kind": "baumarkt", "cc": "DE", "name": "Toom Linthe", "plz": "14822", "city": "Linthe", "lat": 52.1901, "lng": 12.7756}, {"id": "de-obi-friesenried-54bd9", "chain": "OBI", "kind": "baumarkt", "cc": "DE", "name": "OBI Friesenried", "plz": "87654", "city": "Friesenried", "lat": 47.8761, "lng": 10.5405}, {"id": "de-saturn-friesenried-93c7d", "chain": "Saturn", "kind": "elektro", "cc": "DE", "name": "Saturn Friesenried", "plz": "87654", "city": "Friesenried", "lat": 47.8708, "lng": 10.5275}, {"id": "de-expert-friesenried-11b54", "chain": "expert", "kind": "elektro", "cc": "DE", "name": "expert Friesenried", "plz": "87654", "city": "Friesenried", "lat": 47.8753, "lng": 10.5355}, {"id": "de-mediamarkt-erdmannhausen-fd994", "chain": "MediaMarkt", "kind": "elektro", "ccLine truncated
[{"id": "de-expert-brand-b8c5a", "chain": "expert", "kind": "elektro", "cc": "DE", "name": "expert Brand", "plz": "95682", "city": "Brand", "lat": 49.9563, "lng": 11.9012, "sourceMode": "seed"}, {"id": "de-toom-brand-33589", "chain": "Toom", "kind": "baumarkt", "cc": "DE", "name": "Toom Brand", "plz": "95682", "city": "Brand", "lat": 49.9518, "lng": 11.903, "sourceMode": "seed"}, {"id": "de-euronics-brand-51601", "chain": "Euronics", "kind": "elektro", "cc": "DE", "name": "Euronics Brand", "plz": "95682", "city": "Brand", "lat": 49.969, "lng": 11.9202, "sourceMode": "seed"}, {"id": "de-globus-lüder-bcfcd", "chain": "Globus", "kind": "baumarkt", "cc": "DE", "name": "Globus Lüder", "plz": "29394", "city": "Lüder", "lat": 52.8068, "lng": 10.6683, "sourceMode": "seed"}, {"id": "de-bauhaus-lüder-0866d", "chain": "Bauhaus", "kind": "baumarkt", "cc": "DE", "name": "Bauhaus Lüder", "plz": "29394", "city": "Lüder", "lat": 52.8138, "lng": 10.6646, "sourceMode": "seed"}, {"id": "de-mediamarkt-janneby-bb13a", "chain": "MediaMarkt", "kind": "elektro", "cc": "DE", "name": "MediaMarkt Janneby", "plz": "24992", "city": "Janneby", "lat": 54.6271, "lng": 9.2993, "sourceMode": "seed"}, {"id": "de-globus-janneby-5ef70", "chain": "Globus", "kind": "baumarkt", "cc": "DE", "name": "Globus Janneby", "plz": "24992", "city": "Janneby", "lat": 54.6325, "lng": 9.3005, "sourceMode": "seed"}, {"id": "de-obi-mosbach-1bc43", "chain": "OBI", "kind": "baumarkt", "cc": "DE", "name": "OBI Mosbach", "plz": "74821", "city": "Mosbach", "lat": 49.3442, "lng": 9.155, "sourceMode": "seed"}, {"id": "de-hagebau-mosbach-afc0e", "chain": "Hagebau", "kind": "baumarkt", "cc": "DE", "name": "Hagebau Mosbach", "plz": "74821", "city": "Mosbach", "lat": 49.361, "lng": 9.1538, "sourceMode": "seed"}, {"id": "de-toom-mosbach-8da10", "chain": "Toom", "kind": "baumarkt", "cc": "DE", "name": "Toom Mosbach", "plz": "74821", "city": "Mosbach", "lat": 49.3626, "lng": 9.1552, "sourceMode": "seed"}, {"id": "de-hagebau-hötensleben-a4da1", "chain": "Hagebau", "kind": "baumarkt", "cc": "DE", "name": "Hagebau Hötensleben", "plz": "39393", "city": "Hötensleben", "lat": 52.1184, "lng": 11.0274, "sourceMode": "seed"}, {"id": "de-saturn-hötensleben-c2e0a", "chain": "Saturn", "kind": "elektro", "cc": "DE", "name": "Saturn Hötensleben", "plz": "39393", "city": "Hötensleben", "lat": 52.1117, "lng": 11.0146, "sourceMode": "seed"}, {"id": "de-mediamarkt-stapelburg-902ff", "chain": "MediaMarkt", "kind": "elektro", "cc": "DE", "name": "MediaMarkt Stapelburg", "plz": "38871", "city": "Stapelburg", "lat": 51.8976, "lng": 10.6574, "sourceMode": "seed"}, {"id": "de-globus-stapelburg-332c7", "chain": "Globus", "kind": "baumarkt", "cc": "DE", "name": "Globus Stapelburg", "plz": "38871", "city": "Stapelburg", "lat": 51.9031, "lng": 10.6665, "sourceMode": "seed"}, {"id": "de-obi-stapelburg-1f478", "chain": "OBI", "kind": "baumarkt", "cc": "DE", "name": "OBI Stapelburg", "plz": "38871", "city": "Stapelburg", "lat": 51.8966, "lng": 10.6579, "sourceMode": "seed"}, {"id": "de-expert-stuttgart-wangen-9590d", "chain": "expert", "kind": "elektro", "cc": "DE", "name": "expert Stuttgart Wangen", "plz": "70327", "city": "Stuttgart Wangen", "lat": 48.7715, "lng": 9.2417, "sourceMode": "seed"}, {"id": "de-hornbach-stuttgart-wangen-a1db9", "chain": "Hornbach", "kind": "baumarkt", "cc": "DE", "name": "Hornbach Stuttgart Wangen", "plz": "70327", "city": "Stuttgart Wangen", "lat": 48.7764, "lng": 9.2461, "sourceMode": "seed"}, {"id": "de-globus-stuttgart-wangen-943db", "chain": "Globus", "kind": "baumarkt", "cc": "DE", "name": "Globus Stuttgart Wangen", "plz": "70327", "city": "Stuttgart Wangen", "lat": 48.775, "lng": 9.249, "sourceMode": "seed"}, {"id": "de-mediamarkt-worth-17ecc", "chain": "MediaMarkt", "kind": "elektro", "cc": "DE", "name": "MediaMarkt Worth", "plz": "21502", "city": "Worth", "lat": 53.475, "lng": 10.4128, "sourceMode": "seed"}, {"id": "de-bauhaus-worth-992a6", "chain": "Bauhaus", "kind": "baumarkt", "cc": "DE", "name": "Bauhaus Worth", "plz": "21502", "city": "Worth", "lat": 53.4603, "lng": 10.4142, "sourceMode": "seed"}, {"id": "de-mediamarkt-alleshausen-ed333", "chain": "MediaMarkt", "kind": "elektro", "cc": "DE", "name": "MediaMarkt Alleshausen", "plz": "88422", "city": "Alleshausen", "lat": 48.1122, "lng": 9.6231, "sourceMode": "seed"}, {"id": "de-obi-alleshausen-3bade", "chain": "OBI", "kind": "baumarkt", "cc": "DE", "name": "OBI Alleshausen", "plz": "88422", "city": "Alleshausen", "lat": 48.0977, "lng": 9.63, "sourceMode": "seed"}, {"id": "de-euronics-heidmoor-a4c01", "chain": "Euronics", "kind": "elektro", "cc": "DE", "name": "Euronics Heidmoor", "plz": "24632", "city": "Heidmoor", "lat": 53.8296, "lng": 9.841, "sourceMode": "seed"}, {"id": "de-mediamarkt-heidmoor-890c5", "chain": "MediaMarkt", "kind": "elektro", "cc": "DE", "name": "MediaMarkt Heidmoor", "plz": "24632", "city": "Heidmoor", "lat": 53.825, "lng": 9.8468, "sourceMode": "seed"}, {"id": "de-toom-gehrden-Line truncated
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" rx="14" fill="#1f5a62"/><g fill="none" stroke="#fff" stroke-width="4" stroke-linecap="round"><path d="M32 14v36M16 23l32 18M48 23 16 41"/></g></svg>

After

Width:  |  Height:  |  Size: 238 B

+2 -2
View File
@@ -11,12 +11,12 @@ import '../styles/global.css';
<title>{title}</title>
<meta name="description" content={desc} />
<link rel="canonical" href={Astro.url.href} />
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
<link rel="apple-touch-icon" href="/favicon.svg" />
<meta property="og:title" content={title} />
<meta property="og:description" content={desc} />
<meta property="og:type" content="website" />
{jsonld && <script type="application/ld+json" set:html={JSON.stringify(jsonld)} />}
<!-- Umami Analytics (self-hosted). TODO: data-website-id setzen, Host prüfen. -->
<script defer is:inline src="https://umami.heidrich-digital.de/script.js" data-website-id="REPLACE_WITH_UMAMI_WEBSITE_ID" data-domains="kuehlfinder.de"></script>
</head>
<body>
<header class="site"><div class="wrap bar">
+15
View File
@@ -0,0 +1,15 @@
---
import Base from '../layouts/Base.astro';
---
<Base title="Seite nicht gefunden – Kühlfinder" description="Diese Seite gibt es nicht.">
<section class="wrap" style="margin:3rem auto;max-width:560px;text-align:center">
<span class="eyebrow">404</span>
<h1>Seite nicht gefunden</h1>
<p class="muted">Diese Seite existiert nicht (mehr). Weiter geht's hier:</p>
<p style="display:flex;gap:.6rem;justify-content:center;flex-wrap:wrap;margin-top:1rem">
<a class="btn" href="/karte">Zur Live-Karte</a>
<a class="btn ghost" href="/">Startseite</a>
<a class="btn ghost" href="/finder">Finder</a>
</p>
</section>
</Base>
+1 -1
View File
@@ -50,7 +50,7 @@ const products = allProducts.map(p=>({slug:p.slug,name:p.name,type:p.type,badge:
<div class="reco"><span class="badge">Budget</span>${card(r.budget)}</div>
${r.warn?`<p class="warn">${r.warn}</p>`:''}
<div class="rcta">
<a class="btn" href="/karte?produkt=${r.primary.slug}">Verfügbarkeit auf Karte</a>
<a class="btn" href="/karte?modell=${r.primary.slug}">Verfügbarkeit auf Karte</a>
<a class="btn ghost" href="/alarm?produkt=${r.primary.slug}">Alarm setzen</a>
<button class="btn ghost" id="again">Nochmal</button>
</div>`;
+12 -9
View File
@@ -15,7 +15,7 @@ const Bell='<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="
<WeatherBanner />
<span class="eyebrow" style="margin-top:1rem"><span class="dot ok live"></span>Live-Verfügbarkeit · eigener Scraper</span>
<h1>Wo gibt es gerade ein&nbsp;Klimagerät?</h1>
<p class="lead">Bei Hitze ist alles vergriffen. Kühlfinder ist dein Kontrollzentrum: <strong>live sehen, welcher Markt in der Nähe eins auf Lager hat</strong> – Baumärkte, Elektromärkte, Online &amp; eBay – mit Prognose, Preis-Check gegen Wucher und Alarm. <em>Die nächste Hitzewelle kommt bestimmt.</em></p>
<p class="lead">Bei Hitze ist alles vergriffen. Kühlfinder prüft <strong>Preise live direkt bei Händlern</strong> und zeigt ehrlich, wo geprüft ist – plus Standort-Abdeckung, Preis-Check gegen Wucher und Alarm. <em>Die nächste Hitzewelle kommt bestimmt.</em></p>
<form class="herobar" action="/karte" method="get">
<input name="plz" inputmode="numeric" placeholder="PLZ oder Ort …" aria-label="PLZ oder Ort" />
<button class="btn" type="submit" data-umami-event="hero_plz_suche">Verfügbarkeit zeigen</button>
@@ -23,12 +23,12 @@ const Bell='<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="
</form>
<div class="livewrap reveal">
<div class="pills">
<span class="pill-stat"><b class="num" id="p-ac" data-count="0">…</b>&nbsp;Märkte mit Klimagerät jetzt</span>
<span class="pill-stat"><b class="num" id="p-total" data-count="0">…</b>&nbsp;Märkte überwacht</span>
<span class="pill-stat"><b class="num" id="p-ac">…</b>&nbsp;Live-Preise geprüft</span>
<span class="pill-stat"><b class="num" id="p-total" data-count="0">…</b>&nbsp;Märkte (Abdeckung)</span>
<span class="pill-stat"><b class="num">6</b>&nbsp;Länder · <span id="p-chains">…</span> Ketten</span>
</div>
<div class="livecard card">
<div class="lc-head"><span class="dot ok live"></span> Gerade verfügbar</div>
<div class="lc-head"><span class="dot ok live"></span> Live geprüfte Preise</div>
<ul class="livelist" id="livelist"><li class="skel" style="height:40px"></li><li class="skel" style="height:40px"></li><li class="skel" style="height:40px"></li></ul>
<a class="btn sm" href="/karte">Alle auf der Karte →</a>
</div>
@@ -122,13 +122,16 @@ const Bell='<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="
<script>
const euro=(n,c)=>n==null?'–':new Intl.NumberFormat('de-DE',{style:'currency',currency:c||'EUR',maximumFractionDigits:0}).format(n);
document.getElementById('locbtn')?.addEventListener('click',e=>{ if(navigator.geolocation){ /* Karte hat eigene Standortfunktion */ } });
const CRAWLER=(import.meta.env.PUBLIC_CRAWLER_URL)||'http://vnqwgi4gq520hs1p4bsgi4w6.46.224.49.66.sslip.io';
fetch('/data/monitor.json').then(r=>r.json()).then(d=>{
const ac=document.getElementById('p-ac'), tot=document.getElementById('p-total');
ac.setAttribute('data-count', d.stats.withAC); ac.removeAttribute('data-done');
const tot=document.getElementById('p-total');
tot.setAttribute('data-count', d.stats.storesTotal); tot.removeAttribute('data-done');
const chains=new Set(d.stores.map(s=>s.c)); document.getElementById('p-chains').textContent=chains.size;
document.getElementById('p-chains').textContent=new Set(d.stores.map(s=>s.c)).size;
window.kfAnim && window.kfAnim();
const avail=d.stores.filter(s=>s.any.a && s.any.minP).sort(()=>Math.random()-0.5).slice(0,6);
document.getElementById('livelist').innerHTML = avail.map(s=>`<li><span><span class="flag">${s.flag||''}</span> <span class="lm">${s.c}</span> <span class="lc">${s.ci}</span></span><span class="lp">${euro(s.any.minP,s.cur)}</span></li>`).join('') || '<li class="lc">Aktuell sehr knapp – Alarm setzen lohnt sich.</li>';
}).catch(()=>{});
fetch(CRAWLER+'/api/live').then(r=>r.json()).then(d=>{
const offers=(d.offers||[]).filter(o=>o.price);
document.getElementById('p-ac').textContent=offers.length;
document.getElementById('livelist').innerHTML = offers.slice(0,6).map(o=>`<li><span><span class="lm">${o.chain}</span> <span class="lc">${o.name}</span></span><span class="lp">${euro(o.price)}</span></li>`).join('') || '<li class="lc">Crawler startet – gleich wieder reinschauen.</li>';
}).catch(()=>{document.getElementById('p-ac').textContent='–';document.getElementById('livelist').innerHTML='<li class="lc">Live-Preise gerade nicht erreichbar.</li>';});
</script>
+15 -13
View File
@@ -11,7 +11,7 @@ const jsonld={'@context':'https://schema.org','@type':'WebApplication',name:'Kü
<WeatherBanner />
<div class="mon-hero-row" style="margin-top:1rem">
<div>
<span class="eyebrow"><span class="dot ok live"></span>Live · eigener Scraper · 6 Länder</span>
<span class="eyebrow"><span class="dot ok live"></span>Live-Preis-Crawler · Standort-Abdeckung (Demo) · 6 Länder</span>
<h1>Dein Klimagerät-Kontrollzentrum</h1>
<p class="lead">Live-Bestand in Baumärkten, Elektromärkten, Online &amp; eBay – mit <strong>Verfügbarkeits-Prognose</strong> und <strong>Preis-Check gegen Wucher</strong>.</p>
</div>
@@ -19,11 +19,11 @@ const jsonld={'@context':'https://schema.org','@type':'WebApplication',name:'Kü
</div>
<div class="delaybar reveal">
<span class="dot ok live"></span>
<span>Kostenlos siehst du Live-Daten <strong>~10 Min zeitversetzt</strong>. <a href="/pro">Mit Pro in Echtzeit + Push →</a></span>
<span><strong>Filialbestände sind Demo-Abdeckung (nicht verifiziert).</strong> Verifizierte <strong>Live-Preise</strong> (eigener Crawler) stehen unten in der Preis-Leiste. <a href="/pro">Pro: Echtzeit + Alarm →</a></span>
</div>
<div class="stats" id="stats">
<div class="stat"><span class="note">Märkte</span><b class="num" data-k="storesTotal">–</b></div>
<div class="stat ok"><span class="note">mit Klimagerät</span><b class="num" data-k="withAC">–</b></div>
<div class="stat"><span class="note">Märkte (Abdeckung)</span><b class="num" data-k="storesTotal">–</b></div>
<div class="stat ok"><span class="note">Live-Preise geprüft</span><b class="num" id="s-live">–</b></div>
<div class="stat"><span class="note">Baumärkte</span><b class="num" data-k="baumarkt">–</b></div>
<div class="stat"><span class="note">Elektromärkte</span><b class="num" data-k="elektro">–</b></div>
<div class="stat"><span class="note">Länder</span><b class="num" data-k="countries">–</b></div>
@@ -52,7 +52,7 @@ const jsonld={'@context':'https://schema.org','@type':'WebApplication',name:'Kü
<label class="rad">Umkreis <span class="num" id="r-out">–</span><input id="radius" type="range" min="10" max="500" step="10" value="500" disabled /></label>
</div>
<div class="tb-row chips">
<button class="chip ghost on" data-toggle="avail">Nur verfügbare</button>
<button class="chip ghost" data-toggle="avail">Nur verfügbare (live)</button>
<button class="chip ghost" data-toggle="chance">Beste Prognose</button>
<span class="sep"></span>
<button class="chip on" data-kind="all">Alle Typen</button>
@@ -79,7 +79,7 @@ const jsonld={'@context':'https://schema.org','@type':'WebApplication',name:'Kü
<div><h2 class="h-sm">Nichts in der Nähe?</h2><p class="note" style="margin:0">Verfügbarkeits-Alarm + Preiswecker – Mail &amp; Telegram, mit Prognose-Frühwarnung.</p></div>
<a class="btn" href="/alarm">Alarm &amp; Preiswecker</a>
</div>
<p class="note" style="margin-top:.6rem">Datenquelle: <strong>eigener Kühlfinder-Scraper</strong> (Händler direkt) – keine Drittdaten. Aktuell Seed-Stand; Live-Crawler + echte Historie folgen. Prognose = statistische Schätzung. Quellen: Wetter <a href="https://open-meteo.com/" rel="nofollow">Open-Meteo</a>, Hitze/Gesundheit <a href="https://www.umweltbundesamt.de/themen/gesundheit/umwelteinfluesse-auf-den-menschen/hitze" rel="nofollow">UBA</a>.</p>
<p class="note" style="margin-top:.6rem">Standort-Abdeckung ist <strong>Demo</strong> (keine verifizierten Filialbestände). <strong>Verifizierte Live-Preise</strong> kommen vom eigenen Crawler (ausgewählte Produktseiten). Filial-Live folgt mit Händler-Anbindung. Prognose = statistische Schätzung. Quellen: Wetter <a href="https://open-meteo.com/" rel="nofollow">Open-Meteo</a>, Hitze/Gesundheit <a href="https://www.umweltbundesamt.de/themen/gesundheit/umwelteinfluesse-auf-den-menschen/hitze" rel="nofollow">UBA</a>.</p>
</section>
</Base>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/maplibre-gl@4.7.1/dist/maplibre-gl.css" />
@@ -143,11 +143,11 @@ const jsonld={'@context':'https://schema.org','@type':'WebApplication',name:'Kü
<script>
import maplibregl from 'maplibre-gl';
const FLAG={DE:'🇩🇪',AT:'🇦🇹',CH:'🇨🇭',NL:'🇳🇱',PL:'🇵🇱',DK:'🇩🇰'};
const LIVE_URL='http://vnqwgi4gq520hs1p4bsgi4w6.46.224.49.66.sslip.io';
const LIVE_URL=(import.meta.env.PUBLIC_CRAWLER_URL)||'http://vnqwgi4gq520hs1p4bsgi4w6.46.224.49.66.sslip.io';
const fmt=(n,c)=>n==null?'–':new Intl.NumberFormat('de-DE',{style:'currency',currency:c||'EUR',maximumFractionDigits:0}).format(n);
const $=s=>document.querySelector(s),$$=s=>Array.from(document.querySelectorAll(s));
let DATA=null,center=null,shown=120;
const state={q:'',model:'all',countries:new Set(),kind:'all',avail:true,chance:false,radius:500};
const state={q:'',model:'all',countries:new Set(),kind:'all',avail:false,chance:false,radius:500};
const params=new URLSearchParams(location.search);
if(params.get('modell'))state.model=params.get('modell');
if(params.get('plz'))state.q=params.get('plz');
@@ -191,10 +191,11 @@ const jsonld={'@context':'https://schema.org','@type':'WebApplication',name:'Kü
const tb=$('#rows');tb.innerHTML='';
for(const s of list.slice(0,shown)){
const st=status(s);
const stk = st.a?`<span class="stk ok"><span class="dot ok"></span> ${state.model==='all'?st.n+' verfügbar':'verfügbar'}</span>`:`<span class="stk off"><span class="dot off"></span> vergriffen</span>`;
const stk = st.a?`<span class="stk ok"><span class="dot ok"></span> live verfügbar</span>`:`<span class="stk off"><span class="dot off"></span> Demo · beim Händler prüfen</span>`;
const distTd=center?`<td class="ce num">${dist(center[1],center[0],s.la,s.lo).toFixed(0)} km</td>`:'';
tb.insertAdjacentHTML('beforeend',`<tr><td><span class="flag">${s.flag||FLAG[s.cc]||''}</span> <span class="fname">${s.n}</span><span class="kindb">${s.kind==='elektro'?'Elektro':'Baumarkt'}</span><div class="faddr">${s.z} ${s.ci} · <span title="zuletzt geprüft">Stand vor ${s.t||'?'} Min</span></div></td>
<td class="ce">${stk}</td><td class="ce num">${fmt(st.p,s.cur)}</td>
const surl="https://www.google.com/search?q="+encodeURIComponent(s.c+" "+s.ci+" Klimagerät");
tb.insertAdjacentHTML('beforeend',`<tr><td><span class="flag">${s.flag||FLAG[s.cc]||''}</span> <a class="fname" href="${surl}" target="_blank" rel="noopener">${s.n}</a><span class="kindb">${s.kind==='elektro'?'Elektro':'Baumarkt'}</span><div class="faddr">${s.z} ${s.ci} · <span title="zuletzt geprüft">Stand vor ${s.t||'?'} Min</span></div></td>
<td class="ce">${stk}</td><td class="ce num" title="Referenzpreis (Demo)">${st.p?fmt(st.p,s.cur):"–"}</td>
<td class="ce"><span class="prog"><span class="progbar"><i style="width:${st.q||0}%"></i></span><span class="num">${st.q||0}%</span></span></td>${distTd}</tr>`);
}
$('#rowcount').textContent=`${Math.min(shown,list.length)} von ${list.length} Märkten`;
@@ -204,7 +205,7 @@ const jsonld={'@context':'https://schema.org','@type':'WebApplication',name:'Kü
function fillStatic(){
$('#h-updated').textContent=DATA.stats.updated;
$$('#stats b[data-k]').forEach(b=>b.textContent=(DATA.stats[b.dataset.k]??'–').toLocaleString('de-DE'));
$('#s-wucher').textContent=DATA.online.filter(o=>o.wucher).length;
$('#s-wucher').textContent=(DATA.online||[]).filter(o=>o.wucher).length;
$('#h-feednote').textContent='Feed live';
$('#feed').innerHTML=DATA.feed.slice(0,18).map(f=>`<li><div class="fl"><b>${FLAG[f.cc]||''} ${f.name}</b><span>${f.model}</span></div><span class="ft">${rel(f.at)}</span></li>`).join('');
$('#onlinebar').innerHTML='<span class="note" style="align-self:center">Online &amp; eBay:</span>'+DATA.online.map(o=>`<a class="osrc ${o.wucher?'wucher':''}" href="${o.url}" target="_blank" rel="nofollow noopener"><b>${o.src}${o.wucher?' ⚠':''}</b><span class="num">${fmt(o.p)}</span><small>${o.cond?o.cond+' · ':''}${o.a?(o.wucher?'Wucher':'verfügbar'):'nicht verf.'}</small><small class="trust" title="Seriosität">${stars(o.trust||3)}</small></a>`).join('');
@@ -221,11 +222,12 @@ const jsonld={'@context':'https://schema.org','@type':'WebApplication',name:'Kü
const when=d.generatedAt?new Date(d.generatedAt):null;
const mins=when?Math.max(1,Math.round((Date.now()-when)/60000)):'?';
const items=d.offers.filter(o=>o.price||o.availability==='discontinued').map(o=>{
const av=o.availability==='in_stock'?'im Markt':(o.availability==='online_available'?'online':(o.availability==='discontinued'?'⚠ Seite entfernt (404)':'vergriffen'));
const av=o.availability==='in_stock'?'Produktseite: verfügbar':(o.availability==='online_available'?'online verfügbar':(o.availability==='discontinued'?'⚠ Seite entfernt (404)':(o.availability==='blocked'?'blockiert':'vergriffen')));
const cls=(o.availability==='out_of_stock')?'':'';
return `<a class="osrc ${o.availability==='discontinued'?'wucher':''}" href="${o.url}" target="_blank" rel="sponsored nofollow noopener"><b>${o.chain} · LIVE</b><span class="num">${fmt(o.price)}</span><small>${o.name} · ${av}</small></a>`;
}).join('');
const bar=$('#onlinebar');
const liveN=d.offers.filter(o=>o.price).length; var le=document.getElementById('s-live'); if(le)le.textContent=liveN;
const note = d.blocked?` · ${d.blocked} Quelle(n) blockiert (Proxy nötig)`:'';
bar.insertAdjacentHTML('afterbegin', `<span class="livebadge" title="eigener Live-Crawler"><span class="dot ok live"></span> LIVE gescrapt · Stand vor ${mins} Min${note}</span>`+items);
}).catch(()=>{});
+2 -2
View File
@@ -35,8 +35,8 @@ const jsonld={'@context':'https://schema.org','@type':'FAQPage',mainEntity:[
<li>★ <strong>Preiswecker</strong>: Alarm, wenn Preis unter dein Limit fällt</li>
<li>★ Beste-Chancen-Route in deinem Umkreis</li>
</ul>
<a class="btn" href="/alarm" data-umami-event="pro_pass_cta">Pro-Pass holen</a>
<p class="note" style="margin:.5rem 0 0">Zahlung per <strong>Mollie</strong> (EU, NL) – iDEAL, SEPA, Karte, Apple/Google Pay. Folgt. Keine automatische Verlängerung.</p>
<a class="btn" href="/alarm" data-umami-event="pro_pass_cta">Auf die Warteliste</a>
<p class="note" style="margin:.5rem 0 0">Vorschau – Bezahlung per <strong>Mollie</strong> (EU) kommt zum Verkaufsstart. Noch nichts zu zahlen, keine automatische Verlängerung.</p>
</div>
</div>