Przejdź do treści
megapromotingPorozmawiajmy

Ekspertyza · Utrzymanie

Utrzymanie oznacza wiedzieć, że formularz dostarcza dziś, a nie że serwer odpowiada 200.

Monitorowanie, które sprawdza pełną drogę żądania do człowieka, aktualizacje z bramką przed produkcją i kontrolę akceptacyjną uruchamianą po każdej publikacji. Wszystkie trzy działają na tej stronie i można je otworzyć z zewnątrz.

Już zbudowaneTrzy własne implementacje, wszystkie w repozytorium tej witryny i wszystkie weryfikowalne dziś: `src/app/api/health/contact/route.ts` (sonda dostarczania, odpowiada 200 na live teraz), `src/lib/lead-store.ts` (dziennik append-only z retencją stosowaną przy zapisie) oraz `scripts/verify-redesign.ts` (weryfikacja akceptacyjna uruchomiona po publikacji). Napisałem je, ponieważ 06.09.2026 własny formularz padł w ciszy: token bota został unieważniony, endpoint zwracał 500, a żądania znikały bez śladu. To nie jest opowieść sprzedażowa — to commit, który wygenerował powyższe pliki.

„Strona działa” to stwierdzenie o stronie głównej. Osoba, która wypełniła formularz i dostała błąd, nie została zatrzymana przez niedziałającą stronę, tylko przez kanał dostarczania, który cicho wygasł. Różnica między tymi dwiema sytuacjami to wszystko, co oznacza utrzymanie robione na serio: nie sprawdzamy, czy serwer odpowiada, lecz czy żądanie od osoby dociera do drugiej osoby.

6 września 2026 dokładnie to wydarzyło się na tej stronie. Token bota, który przekazywał zgłoszenia z formularza do zespołu, został unieważniony; interfejs Telegram odpowiadał `401 Unauthorized`, nasza trasa zwracała 500, a osoba odwiedzająca widziała „wiadomości nie udało się wysłać”. Zgłoszenie nie zapisywało się nigdzie. W dzienniku błędów procesu były trzy rzeczywiste niepowodzenia w około siedmiu godzinach, które minęły od poprzedniej publikacji. Nikt nie patrzył.

Wynikiem tego są trzy elementy, które teraz montujemy w każdym projekcie, który utrzymujemy. Dziennik append-only zapisywany *przed* próbą dostarczenia, aby uszkodzony kanał degradował do „musimy zajrzeć do pliku” zamiast „żądanie nigdy nie istniało”. Sonda zdrowia, która odpowiada na jedno żądanie, jeśli lead może teraz dotrzeć do osoby. I weryfikacja akceptacyjna uruchamiana po każdej publikacji, która pada, jeśli zniknęła kanoniczna adresacja, kotwica pytań albo jeśli w mapie witryny pojawiły się adresy, których nie da się otworzyć.

Reszta to nudna i sprawdzalna dyscyplina: aktualizacje z bramką audytu przed dotknięciem produkcji, automatyczny restart z progiem pamięci i limitem niestabilnych restartów, retencja stosowana przez kod, a nie deklarowana w polityce, oraz adresy IP obcięte do prefiksu sieciowego przed zapisaniem gdziekolwiek.

Co obejmuje

Praca, komponent po komponencie

Sonda, która sprawdza dostarczanie, a nie dostępność

`GET /api/health/contact` odpowiada 200, jeśli lead może teraz dotrzeć do osoby, i 503, jeśli nie. Sprawdza dwa rzeczy osobno: że token kanału powiadomień jest nadal ważny (rzeczywiste żądanie do dostawcy, z timeoutem 8 sekund) oraz że dziennik żądań jest zapisywalny. Odpowiedź składa się wyłącznie z wartości logicznych — nigdy z tokenu, identyfikatora kanału ani nazwy bota. Wynik jest przechowywany w pamięci przez 60 sekund, aby zewnętrzna sonda często naciskana sama nie stała się ruchem. Gdy dostawca jest niedostępny, pole staje się `null`, a nie `false`: „nie wiem” i „nieprawidłowe” to różne stany.

Dziennik zapisany przed dostarczeniem, nie po

Każde żądanie dostaje identyfikator i jest zapisywane w dzienniku append-only, po jednej linii JSON na zdarzenie, *przed* próbą powiadomienia. Druga linia, z tym samym identyfikatorem, mówi, co rzeczywiście się wydarzyło: `delivered` albo `failed`, z powodem obciętym do 300 znaków. Katalog jest tworzony z prawami `0700`, pliki z `0600`, a ścieżka jest celowo umieszczona poza katalogiem wersji, aby historia przetrwała publikację.

Weryfikacja akceptacyjna uruchamiana po publikacji

Skrypt weryfikacyjny otwiera każdą stronę produktu, w grupach po trzy, i kończy działanie błędem, jeśli brakuje kodu 200, kotwicy pytań, kotwicy przykładu albo adresu kanonicznego. Następnie sprawdza mapę witryny — aby nie zawierała niewyświetlających się wariantów językowych i zawierała każdy produkt — oraz `robots.txt`, aby nie blokował zasobów potrzebnych do renderowania. Kończy się też błędem, jeśli na stronie ponownie pojawia się treść z dawnego klienta. To lista rzeczy, które już raz się zepsuły.

Aktualizacje z bramką, nie z nadzieją

Skrypt publikacji odmawia uruchomienia, jeśli serwer ma mniej niż 500 MB wolnej pamięci, uruchamia `npm audit --audit-level=high` i zatrzymuje się przy podatnościach wysokiego poziomu, jeśli nie potwierdzisz tego wyraźnie, a potem buduje od czysta — `.next` i `node_modules` usunięte, instalacja z pliku blokady. Jeśli proces nie pojawi się jako `online` po uruchomieniu, skrypt wyświetla ostatnie 50 wierszy dziennika i kończy się błędem, zamiast zgłaszać sukces.

Restart z progami, nie na chybił trafił

Proces uruchamia się ponownie automatycznie przy ponad 500 MB pamięci, z opóźnieniem 4 sekund między próbami, wykładniczym zwiększaniem opóźnienia i zatrzymaniem po 10 niestabilnych ponownych uruchomieniach — tak, aby pętla awarii stała się widoczna, zamiast po cichu obciążać serwer. Czas łaski przy zatrzymaniu: 5 sekund, potem wymuszone zakończenie. Dzienniki mają datę i strefę czasową, w jednym strumieniu.

Duplikaty obsługiwane, nie liczone podwójnie

Ta sama osoba, ta sama wiadomość, dwa razy — podwójne kliknięcie, odświeżenie strony — powodowały dwa identyczne powiadomienia dla jednego żądania. Teraz odcisk `sha256` pary adres + wiadomość jest przechowywany w pamięci przez 10 minut; druga wysyłka otrzymuje ten sam identyfikator żądania i znacznik `duplicate`, a powiadomienie nie jest powtarzane. Tłumione są tylko duplikaty udanego dostarczenia; nieudane może przejść ponownie.

Kody błędów, które mówią, co się zepsuło

400 dla nieprawidłowych danych, 405 przy złej metodzie, 500 dla uszkodzonego ciała żądania, 502 gdy kanał dostarczenia odpowiedział źle, 503 gdy brakuje poświadczeń. Różnica ma znaczenie o 3 rano: 502 znaczy „dostawca”, 503 znaczy „nasza konfiguracja”. Osobie odwiedzającej mówi się wyraźnie „otrzymaliśmy żądanie, ale nie mogliśmy powiadomić” — nie fałszywy sukces.

Retencja stosowana przez kod, nie obiecana w polityce

Opublikowana nota o poufności mówi, że zgłoszenia z formularza są przechowywane przez 24 miesiące. Kod stosuje dokładnie tę samą liczbę: domyślne `LEAD_RETENTION_DAYS` to 730, a pliki starsze niż próg są usuwane przy każdym zapisie, bez harmonogramu, o którym można zapomnieć. Adres IP jest obcinany do prefiksu sieci — `/24` dla IPv4, `/48` dla IPv6 — i odczytywany jest ostatni hop z nagłówka, nie pierwszy, ponieważ pierwszy jest wysyłany przez klienta.

Znalazłyśmy i to, czego nikt nie zgłosił

Ten sam przebieg przez kod ujawnił dwie rzeczy, których nie zgłosiła żadna osoba: limit 10 żądań na minutę dało się całkowicie obejść, ponieważ odczytywany był pierwszy element z nagłówka przekierowanych adresów — kontrolowany przez klienta — a jedna trasa inicjowania połączeń telefonicznych była otwarta anonimowo, na nasz koszt i z naszego numeru, mimo że komponent, który z niej korzystał, nie był już nigdzie zamontowany. Obie rzeczy naprawione i zweryfikowane na produkcji.

Jak to wygląda

Trasa, krok po kroku.

01

Inwentarz dróg, którymi żądanie trafia do człowieka

Pierwsza dostawa nie jest narzędziem, lecz listą: przez co przechodzi zgłoszenie od naciśnięcia przycisku do osoby, która je czyta, co psuje się na każdym kroku i co powinno się stać, gdy się psuje. Na tej stronie lista miała trzy ogniwa i jedno było niewidoczne.

02

Sonda i dziennik, zamontowane przed czymkolwiek innym

Dostarczamy adres zdrowia, który sprawdza pełną ścieżkę, nie proces, oraz dziennik, który zapisuje przed dostarczeniem. Od teraz awaria to pytanie z odpowiedzią, nie kopanie w dziennikach. Adres można odpytać z każdej zewnętrznej usługi monitoringu, ponieważ nie zwraca nic wrażliwego.

03

Sprawdzenie akceptacyjne, napisane z rzeczywistych usterek

Każda rzecz, która zepsuła się raz, trafia do skryptu weryfikacyjnego uruchamianego po publikacji. Nie piszemy testów dla hipotetycznych przypadków; piszemy dla tych, które już nas kosztowały. Dostarczamy skrypt, nie tylko jego wynik — można go uruchomić także samodzielnie.

04

Tempo aktualizacji i bramka przed produkcją

Ustalamy, co aktualizuje się automatycznie, co przechodzi przez weryfikację człowieka, a czego nie dotyka się bez zapowiedzianego okna. Bramka obejmuje audyt bezpieczeństwa zależności i sprawdzenie zasobów serwera, oba wykonywane przed dotknięciem produkcji.

05

Przekazanie, z listą braków

Na koniec przekazujemy procedurę publikacji, procedurę powrotu, adresy zdrowia i pisemną listę tego, co nie jest objęte. Na tej stronie, na przykład, gałąź wygaśnięcia żądania do dostawcy jest zaimplementowana i wpada do tego samego traktowania co błąd sieci, ale sam abort nie został wywołany w teście — tak jest zapisane w dzienniku wykonania, nie w notatce wewnętrznej.

1Sonda care verificălivrarea până la unom2jurnalul scrisînainte de încercare3verificarea deacceptare rulată dupăfiecare publicareTrei piese, toate în depozitul acestui site.
Trasa, w 3 krokach

Dane

Czego dotykamy, gdzie to leży i jak długo zostaje

Pytania, które zadaje każdy, kto ma inspektora ochrony danych — postawione tutaj, zanim on je zada.

Dziennik żądań
Imię, adres e-mail, telefon, firma, wybrana usługa, wiadomość, strona, z której wysłano, host strony źródłowej (tylko host, nie pełny adres) oraz prefiks sieciowy odwiedzającego. Pliki JSON dziennie, na własnym serwerze, poza katalogiem wersji. Termin: 24 miesiące, stosowany przy zapisie.
Dzienniki procesu i serwera
Standardowe wyjście i błędy procesu, z datą i strefą czasową, plus dzienniki serwera WWW. Tu widać cichą awarię: trzy niepowodzenia dostarczenia z incydentu referencyjnego były w dzienniku błędów, a nie w żadnym alercie. Rotacja i termin są ustalane dla każdego serwera; to dane techniczne, nie treść konta.
Co nigdy nie wychodzi z serwera
Tokeny, identyfikatory kanałów i klucze dostawcy. Sonda zdrowia odpowiada wyłącznie wartościami logicznymi, właśnie po to, aby mogła być wywołana przez zewnętrzne narzędzie bez ujawniania czegokolwiek. Ta sama zasada w powiadomieniu: wiadomość zawiera identyfikator żądania i stronę, a nie poświadczenia.
Statystyki ruchu
Pomiar ruchu przechodzi przez instrument analityczny skonfigurowany w projekcie i aktywuje się po zgodzie. Rzeczywiste ustawienie retencji w koncie analitycznym to wartość, którą odczytujemy z konta, a nie taka, którą zakładamy — rejestr przetwarzania tej strony ma to wyraźnie oznaczone jako element do doprecyzowania, a nie jako ustalony fakt.
Rejestr przetwarzania
Każdy typ danych dotknięty przez stronę ma wpis ze celem, podstawą, kategoriami i terminem, w wersjonowanym dokumencie obok kodu. Gdy kod zmienia termin, dokument zmienia się w tym samym commicie — w przeciwnym razie polityka i program mówią różne rzeczy, a zwykle myli się dokument.

Przykład

Formularz, który dostarczył 500 zamiast leadów, siedem godzin

Sytuacja

Strona prezentacyjna z formularzem kontaktowym, niedawno opublikowana. Wszystkie strony odpowiadają 200, pulpit jest zielony, nikt nie zgłasza niczego. Jedynym kanałem, przez który zgłoszenie trafiało do zespołu, było powiadomienie w aplikacji do komunikacji.

Co zbudowaliśmy

Token bota powiadomień został cofnięty; interfejs dostawcy odpowiadał `401 Unauthorized`. Trasa formularza zwracała 500 i nic nie zapisywała. Pierwszą naprawą nie był token, tylko kolejność operacji: żądanie jest teraz zapisywane w dzienniku append-only, z własnym identyfikatorem, *przed* próbą dostarczenia, a wynik dostarczenia jest dopisywany jako druga linia. Na to nałożono publiczną sondę zdrowia sprawdzającą token i możliwość zapisu, odrębne kody błędów dla „dostawca odpowiedział źle” i „brakuje nam poświadczeń”, timeout 10 sekund na dostarczenie oraz tłumienie duplikatów w oknie 10 minut. Ten sam przegląd kodu usunął też limit żądań, który dało się obejść, oraz otwartą anonimowo trasę połączeń telefonicznych.

Co z tego wyszło

Sonda teraz odpowiada 200 z ważnym tokenem i zapisywalnym dziennikiem; można ją odpytać z dowolnej zewnętrznej usługi monitorującej, bez ujawniania czegokolwiek. Testy na produkcji objęły każdy kod odpowiedzi — 400, 405, 500, 502, 503 — a dwa identyczne wysłania zwróciły ten sam identyfikator żądania, drugie oznaczone jako duplikat, z dwiema liniami w dzienniku, nie czterema. Przyszła awaria kanału powiadomień nie usuwa już żądania: pozostaje w dzienniku, z zapisanym powodem.

Czego nie mówi ten przypadek

Sonda mówi, że dostarczenie jest teraz możliwe, a nie że ktoś czyta powiadomienia. Kto je przegląda i w jakim czasie, to decyzja zespołu, nie funkcja kodu. A gałąź wygaśnięcia żądania do dostawcy, choć zaimplementowana, nie została wywołana w teście — wpada do tego samego traktowania co błąd sieci, który został przetestowany; zapisujemy to jako niewyćwiczone, a nie jako sprawdzone.

Pytania

O co pytają ludzie, zanim zadzwonią

Co dokładnie monitorujecie?

Drogę zgłoszenia do osoby, a nie dostępność serwera. `GET /api/health/contact` sprawdza w jednym żądaniu dwie rzeczy: czy token kanału powiadomień jest nadal ważny — przez rzeczywiste wywołanie do dostawcy, z timeoutem 8 sekund — oraz czy dziennik zgłoszeń da się zapisać. Zwraca 200, gdy oba warunki są spełnione, i 503, gdy nie. Możesz wywołać to już teraz na tej stronie: jest publiczne właśnie dlatego, że zwraca tylko wartości logiczne.

Dlaczego formularz miałby się zepsuć, a nikt by tego nie zauważył?

Bo widoczna część nadal działa. Strona się ładuje, przycisk odpowiada, serwer zwraca 200 na wszystkich stronach — psuje się tylko ostatnie ogniwo, które nie ma interfejsu. Na tej stronie token bota powiadomień został unieważniony: dostawca odpowiadał `401 Unauthorized`, trasa zwracała 500, a zgłoszenie nie zapisywało się nigdzie. Trzy rzeczywiste awarie w około siedem godzin, widoczne tylko w dzienniku błędów procesu. Dlatego dziennik zapisuje się teraz przed dostarczeniem: nawet jeśli powiadomienie padnie, zgłoszenie istnieje.

Co się dzieje, jeśli powiadomienie nie powiedzie się po naprawie?

Zgłoszenie jest już zapisane w dzienniku z własnym identyfikatorem, a druga linia w dzienniku mówi `failed` i podaje powód. Osobie odwiedzającej odpowiada się jednoznacznie — „otrzymaliśmy zgłoszenie, ale nie mogliśmy powiadomić” — a nie fałszywym sukcesem. Kod odpowiedzi rozróżnia przyczynę: 502 oznacza, że dostawca odpowiedział źle, 503 że brakuje u nas danych uwierzytelniających. O 3 rano różnica między tymi dwoma decyduje, czy zadzwonisz do kogoś, czy nie.

Czy aktualizujecie zależności automatycznie?

Nie bez bramki. Publikacja uruchamia `npm audit` na poziomie `high` i zatrzymuje się przy podatnościach tego poziomu, jeśli kontynuacja nie zostanie wyraźnie potwierdzona; najpierw sprawdza też dostępną pamięć serwera, z progiem 500 MB, bo budowanie uruchomione na ciasnym serwerze pozostawia aplikację w stanie zatrzymania. Budowanie odbywa się od czysta: katalog budowy i zależności są usuwane, instalacja odbywa się z pliku blokady. To, co aktualizuje się automatycznie, a co przechodzi przez weryfikację człowieka, ustala się w projekcie, nie domyślnie.

Skąd wiecie, że publikacja nie zepsuła czegoś innego?

Dzięki skryptowi weryfikacyjnemu uruchamianemu po publikacji, który otwiera każdą stronę produktu i kończy się błędem, jeśli brakuje kodu 200, kotwicy pytań, kotwicy przykładu albo adresu kanonicznego. Potem sprawdza mapę witryny — czy zawiera każdy produkt i nie zawiera wersji językowych, których nie da się otworzyć — oraz `robots.txt`, żeby nie blokował zasobów renderowania. Każda weryfikacja z listy odpowiada czemuś, co już kiedyś się zepsuło. Skrypt jest przekazywany razem z projektem.

Co robicie, gdy aplikacja się zawiesza albo zużywa pamięć?

Proces uruchamia się ponownie automatycznie po przekroczeniu progu 500 MB, z 4-sekundowym opóźnieniem między próbami i wzrostem wykładniczym, ale zatrzymuje się po 10 niestabilnych restartach w krótkim czasie. To ważne: pętla awarii, która restartuje się w nieskończoność, wygląda zdrowo w panelu i po cichu zużywa serwer. Wolimy, żeby proces pozostał zatrzymany i widoczny.

Jak długo przechowujecie dane z formularza i kto może je odczytać?

24 miesiące, stosowane przez kod, a nie tylko deklarowane: próg to zmienna z domyślną wartością 730 dni, a starsze pliki są usuwane przy każdym nowym zapisie, bez harmonogramu, który można przeoczyć. Katalog ma uprawnienia `0700`, pliki `0600`, a ścieżka znajduje się poza katalogiem wersji, żeby historia przetrwała publikację. Adres IP nie jest przechowywany w całości: jest obcinany do `/24` dla IPv4 i `/48` dla IPv6, z odczytem ostatniego hopa z nagłówka, nie pierwszego — pierwszy jest wysyłany przez klienta i nic nie znaczy.

Czy znajdujecie też problemy, których nie zgłosiliśmy?

Tak się zdarza i zwykle to właśnie one są kosztowne. To samo przejście przez kod, które naprawiło formularz, wykryło dwie rzeczy, których nikt nie zgłosił: limit żądań można było całkowicie obejść, bo czytano pierwszy element z nagłówka przekierowanych adresów — dokładnie ten, który wysyła klient; oraz trasa inicjująca połączenia telefoniczne była otwarta anonimowo, na nasz koszt, choć komponent, który z niej korzystał, został zdemontowany. Obie rzeczy naprawiono i sprawdzono na produkcji. Nie możemy obiecać, że znajdziemy wszystko; możemy obiecać, że zgłosimy też to, o co nie prosiliście.

Czego nie obejmuje utrzymanie?

To nie jest usługa bezpieczeństwa z całodobowym monitoringiem ani centrum operacyjne. Nie gwarantujemy procentu dostępności bez pomiaru, który by go potwierdzał — i, żeby było jasno, w tym momencie nie publikujemy takiej liczby dla własnej strony. Nie przejmujemy odpowiedzialności za platformę zewnętrzną, do której nie mamy dostępu. I nie traktujemy strony, która odpowiada 200, jako dowodu, że wszystko działa: dokładnie to było problemem, od którego zaczęło się wszystko, co napisano wyżej.

Na czym opierają się powyższe stwierdzenia (13 źródeł)
  1. Sonda dostawy odpowiada 200 na produkcji: `{"ok":true,"telegram":{"configured":true,"credentialsValid":true},"leadJournal":{"writable":true}}`https://www.megapromoting.com/api/health/contact · 2026-09-06
  2. Aktywne nagłówki bezpieczeństwa na produkcji: HSTS `max-age=63072000; includeSubDomains; preload`, `X-Content-Type-Options: nosniff`, `X-Frame-Options: SAMEORIGIN`, `Referrer-Policy: strict-origin-when-cross-origin`, `Permissions-Policy` z `microphone=(self)`https://www.megapromoting.com/servicii · 2026-09-06

11 z nich to kod i pliki z naszych repozytoriów. Nie publikujemy ich nazw ani numerów linii: razem, na jednej stronie, opisałoby to zbyt dokładnie, jak zbudowane są systemy, które nie należą tylko do nas. Przechodzimy je razem z Tobą, w repozytorium, na życzenie — weryfikacja pozostaje możliwa, tylko odbywa się w rozmowie.

Co chcesz, żeby działało lepiej?

Opowiedz nam o swoim procesie. Wspólnie ustalimy, co warto zbudować, co da się połączyć i jak sprawdzimy wynik.

Porozmawiajmy