Ślad audytowy: kto wykonał akcję
Każda akcja pracownika zapisuje w bazie nie tylko e-mail konta, ale też sesję, adres IP i przeglądarkę. Dzięki temu przy koncie używanym na kilku urządzeniach da się ustalić, z którego z nich poszła zmiana.
👤 Instrukcja dla pracownika (Administracja)
Po co to jest
Gdy w grafiku brakuje zajęć albo ktoś kwestionuje zmianę („to nie ja"), historia zdarzeń pokazuje konto, które wykonało akcję. Samo konto to za mało, jeśli korzysta z niego kilka osób albo jest zalogowane na komputerze recepcji, telefonie i laptopie jednocześnie. Zapis sesji i adresu IP pozwala rozróżnić te urządzenia.
Gdzie to zobaczysz
W historii zdarzeń zajęć obok e-maila pojawia się identyfikator sesji i adres, z którego wykonano akcję. Ta sama informacja trafia do dziennika systemowego.
Czego to nie rozstrzyga
Zapis wskazuje konto i urządzenie, nie osobę. Jeśli hasło jest współdzielone, a przeglądarka pozostaje zalogowana, akcję mógł wykonać każdy, kto miał dostęp do tego urządzenia. Sesje wygasają po 7 dniach od ostatniego użycia i przedłużają się przy każdej wizycie w panelu.
🛠️ Dokumentacja techniczna
Autor żądania
lib/request-actor.ts trzyma dane autora w obiekcie zakresowanym na żądanie
(React cache()). Zapis następuje raz, przy odczycie sesji w
getServerSession (lib/session.ts), a odczyt jest synchroniczny i nie
wykonuje dodatkowych zapytań.
| Funkcja | Rola |
|---|---|
rememberRequestActor | zapamiętuje userId, email, sessionId, ipAddress, userAgent |
getRequestActor | zwraca zapamiętane dane; poza żądaniem zwraca pusty obiekt |
resolveClientIp | cf-connecting-ip → true-client-ip → x-real-ip → x-forwarded-for |
Poza kontekstem żądania (crony, worker-crons.ts) cache() nie ma czego
zwrócić, więc kolumny audytowe zostają puste — to celowe, cron nie ma autora.
Dziennik systemowy
lib/logger.ts uzupełnia każdy wpis o user_id, ip_address i user_agent
z autora żądania. Kolumny istniały w tabeli logs od początku, ale żaden
wywołujący ich nie przekazywał i były puste w całej historii systemu.
Adres IP w sesjach
Za Cloudflare adres gniazda TCP to adres krawędzi, nie klienta, więc Better Auth
zapisywał w tabeli session adres nierozpoznany (0000:…:0000). Konfiguracja w
lib/auth.ts wskazuje nagłówki, z których należy czytać adres:
const advanced = {
crossSubDomainCookies: getCrossSubDomainCookies(),
ipAddress: {
ipAddressHeaders: ['cf-connecting-ip', 'true-client-ip', 'x-real-ip']
}
};
Zmiana działa od wdrożenia w przód — sesje utworzone wcześniej zachowują stary zapis do czasu wygaśnięcia.
Log zdarzeń zajęć
createEventLogEntry (lib/actions/game.ts) dokłada do wpisu session_id i
ip_address, gdy autor żądania jest znany. Pola są opcjonalne w typie
GameEvent (types/index.ts), więc starsze wpisy pozostają poprawne.
Przykładowy wpis:
{
"event_type": "game_cancelled",
"session_id": "8f3c…",
"ip_address": "203.0.113.7",
"timestamp": "2026-08-29T09:14:02.118Z",
"text": "events.gameCancelled",
"params": { "gameId": "6681", "attendeeCount": "0" }
}
Diagnostyka luk w grafiku
Gdy w kalendarzu brakuje pojedynczych zajęć, kolejność sprawdzania jest taka:
Grafik pracownika filtruje cancelled_at IS NULL i archived = FALSE
(lib/actions/game.ts), więc odwołane zajęcia znikają z kalendarza bez śladu —
historię zdarzeń trzeba otworzyć na samych zajęciach.