Skip to main content

Ś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ń.

FunkcjaRola
rememberRequestActorzapamiętuje userId, email, sessionId, ipAddress, userAgent
getRequestActorzwraca zapamiętane dane; poza żądaniem zwraca pusty obiekt
resolveClientIpcf-connecting-iptrue-client-ipx-real-ipx-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",
"user_email": "[email protected]",
"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.