T ToutadminDokumentacja

Instalacja edycji Node

Edycja Node działa jako usługa stała: jeden proces, baza SQLite na dysku, nic więcej do instalowania — bez zewnętrznej bazy danych, bez pamięci podręcznej, bez kolejki. Ten przewodnik prowadzi od pustego katalogu do instancji produkcyjnej, łącznie z tym, co większość poradników pomija: usługa systemowa, serwer pośredniczący, TLS, kopie zapasowe i aktualizacja.

Czego potrzeba#

ElementWersjaDlaczego
Node.js22 lub nowszyProdukt opiera się na wbudowanym uruchamiaczu testów i na nowszych interfejsach.
Kompilator Cbuild-essentialbetter-sqlite3 kompiluje się przy instalacji, chyba że istnieje gotowy plik binarny dla Twojej platformy.
Dysk lokalnySQLite i wgrane pliki. Nigdy udział sieciowy: patrz niżej.

Maszyna z jednym rdzeniem i 512 MB pamięci wystarczy dla kilkudziesięciu osób. Liczy się nie moc, lecz dysk: musi być lokalny i objęty kopiami zapasowymi.

1. Pobrać i zainstalować#

git clone <twoje-repozytorium> /var/www/toutadmin
cd /var/www/toutadmin
npm ci --omit=dev

npm ci, a nie npm install: instaluje dokładnie to, co opisuje plik blokady, i nigdy go nie przepisuje. Na serwerze wersja, która się przesunie, to awaria, której nikt nie rozumie.

Jeśli kompilacja się nie uda

better-sqlite3 to jedyna zależność kompilująca kod C. sudo apt install -y build-essential python3 rozwiązuje niemal wszystkie przypadki.

2. Skonfigurować#

cp .env.example .env

Wszystko ustawia się zmiennymi środowiskowymi — czytanymi z .env albo ustawianymi przez menedżera usług. Żadna nie jest obowiązkowa: bez .env instancja startuje na porcie 3000 i odsyła do kreatora.

ZmiennaDomyślnieCo robi
PORT3000Port nasłuchu.
NODE_ENVproduction na produkcji: ciasteczka rygorystyczne, bez szczegółowych śladów.
SESSION_SECRETgenerowanyPieczętuje sesje. Pozostawiony pusty, powstaje w data/session.key.
INSTALL_TOKENŻądany przez kreatora przed instalacją. Zalecany na serwerze wystawionym na świat.
TRUST_PROXY1 za zaufanym serwerem pośredniczącym — i tylko tam.
DB_PATHdata/app.sqliteBaza danych.
UPLOAD_DIR, CV_DIR, VAULT_DIR, SIGN_DIR, DOCS_DIR, BACKUP_DIRw data/Katalogi plików: zdjęcia, życiorysy, sejf, teczka podpisów, otrzymane dokumenty, archiwa.
LOGIN_RATE_LIMIT10Próby logowania na kwadrans i na adres.
GLOBAL_RATE_LIMIT300Żądania na minutę i na adres.
API_RATE_LIMITWywołania API na minutę i na token.
SESSION_IDLE_MINUTES60Bezczynność, po której sesja wygasa.
SESSION_MAX_HOURS12Czas bezwzględny, którego żadna aktywność nie przedłuża.
ADMIN_EMAIL, ADMIN_PASSWORDInstalacja bez interfejsu: zakłada administratora przy starcie.
Sekret sesji

Zmiana SESSION_SECRET wylogowuje wszystkich naraz. Ustaw go raz, wygeneruj losowo (node -e "console.log(require('crypto').randomBytes(32).toString('hex'))") i zachowaj razem z resztą. Nigdy nie zostawiaj go w repozytorium git.

Ustawić token instalacyjny#

Między pierwszym uruchomieniem a Twoim wejściem do kreatora instancja należy do tego, kto ją znajdzie: pierwszy przybysz zakłada konto administratora. Ustaw więc INSTALL_TOKEN, zanim otworzysz port:

node -e "console.log(require('crypto').randomBytes(16).toString('hex'))"
# a następnie w .env:
INSTALL_TOKEN=c3f1…

Kreator porównuje go w stałym czasie — zwykłe porównanie pozwoliłoby zgadnąć token znak po znaku — i zapisuje każdą odmowę. Po zainstalowaniu instancji kreator zamyka się sam.

Instalacja bez interfejsu#

Przy wdrożeniu zautomatyzowanym ADMIN_EMAIL i ADMIN_PASSWORD zakładają administratora przy starcie, z pominięciem kreatora. Potem je usuń: hasło w środowisku usługi czyta ten, kto czyta tę usługę.

3. Uprawnienia i miejsce danych#

sudo useradd --system --home /var/www/toutadmin --shell /usr/sbin/nologin toutadmin
sudo chown -R toutadmin:toutadmin /var/www/toutadmin/data
sudo chmod 750 /var/www/toutadmin/data
sudo chmod 640 /var/www/toutadmin/.env

Kod może zostać tylko do odczytu; zapisywalny musi być wyłącznie data/. SQLite zapisuje pliki sąsiadujące (-wal, -shm): liczy się katalog, a nie sama baza.

Nigdy na udziale sieciowym

NFS i SMB kłamią co do blokowania plików. SQLite polega na tym blokowaniu, by nie dopuścić do dwóch jednoczesnych zapisów: na udziale baza w końcu ulega uszkodzeniu bez ostrzeżenia. Dysk lokalny, zawsze — a to kopia zapasowa wędruje gdzie indziej, nie baza.

4. Usługa#

Uruchomiony ręcznie produkt kończy pracę, gdy zamkniesz terminal. Powierz go systemd:

# /etc/systemd/system/toutadmin.service
[Unit]
Description=Toutadmin
After=network.target

[Service]
Type=simple
User=toutadmin
WorkingDirectory=/var/www/toutadmin
EnvironmentFile=/var/www/toutadmin/.env
ExecStart=/usr/bin/node src/server.js
Restart=always
RestartSec=5

# Usługa musi pisać wyłącznie w data/.
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/www/toutadmin/data

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now toutadmin
sudo systemctl status toutadmin
journalctl -u toutadmin -f

Pięć linii utwardzających to nie ozdoba: ProtectSystem=strict czyni cały system plików niezapisywalnym, a ReadWritePaths otwiera z powrotem jedyny katalog, który zapisywalny być musi. Luka pozwalająca na dowolny zapis sięga wtedy tylko data/.

5. Serwer pośredniczący#

Nigdy nie wystawiaj portu 3000 wprost do internetu: nie obsługuje TLS i nie ma powodu się tego uczyć.

server {
    listen 443 ssl http2;
    server_name intranet.przyklad.pl;

    ssl_certificate     /etc/letsencrypt/live/intranet.przyklad.pl/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/intranet.przyklad.pl/privkey.pem;

    client_max_body_size 20M;          # wrzuty do sejfu, otrzymane dokumenty

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

server {
    listen 80;
    server_name intranet.przyklad.pl;
    return 301 https://$host$request_uri;
}

Spraw też, by usługa nasłuchiwała tylko na pętli zwrotnej, tak aby port 3000 był osiągalny wyłącznie dla serwera pośredniczącego.

TLS#

sudo certbot --nginx -d intranet.przyklad.pl

Produkt ustawia nagłówek Strict-Transport-Security, gdy tylko połączenie jest szyfrowane, i nigdy otwartym tekstem: ogłoszony ze strony nieszyfrowanej nie zostałby przeczytany, a lokalną próbę zamknąłby w https na pół roku.

TRUST_PROXY idzie w parze z serwerem pośredniczącym — i tylko z nim

Ta zmienna każe serwerowi wierzyć adresowi źródłowemu ogłoszonemu w nagłówku. Za nginxem to właśnie to, czego trzeba — inaczej wszystkie żądania wydają się pochodzić z 127.0.0.1, a limity na adres niczego już nie chronią. Bez serwera pośredniczącego jest odwrotnie: każdy ogłasza adres, jaki chce, i omija limity.

6. Kreator#

Otwórz swoją domenę. Dopóki nie istnieje żadne konto, każdy adres przekierowuje do /installation. Pięć kroków:

  1. Język instancji, spośród 16.
  2. Wymagania, sprawdzone i pokazane.
  3. Firma: nazwa wyświetlana wszędzie.
  4. Urlop roczny przyznawany każdemu nowemu pracownikowi na etacie.
  5. Konto administratora: adres i hasło o długości co najmniej dwunastu znaków.

Gdy tylko konto istnieje, /installation odsyła do strony logowania: kreator zamknął się sam, nie ma żadnego pliku do usunięcia ręcznie.

7. Przebieg okresowy#

Inaczej niż w edycji PHP nie ma tu nic do planowania: serwer niesie własny harmonogram, który budzi się co godzinę i robi osiem rzeczy.

Co robi przebiegGwarancja
Zamyka konta, którym skończyła się umowaSprawdzane także przy logowaniu i przy otwarciu pulpitu
Wystawia faktury abonamentów, którym nadszedł terminUnikalny indeks nie pozwala zafakturować dwa razy
Zamienia terminy w powiadomieniaKlucz odduplikowania: jeden termin przypomina raz
Czyści przeczytane powiadomienia i dziennik audytuZgodnie z wybranym okresem przechowywania
Pobiera księgową skrzynkę pocztową (IMAP)Jeśli pobieranie jest skonfigurowane
Opróżnia kolejkę webhooków wraz z ponowieniamiPięć prób, potem rezygnacja
Tworzy kopię automatyczną i wysyła ją na zewnątrzGdy minie ustalony odstęp

Każda operacja jest idempotentna: powtórzony przebieg nie zafakturuje dwa razy i nie powiadomi dwa razy. To właśnie pozwala zrestartować usługę w dowolnej chwili bez zastanawiania się.

8. Sprawdzić, że wszystko działa#

# testy (nie zapisują niczego w Twojej bazie)
npm test

# instancja pokazowa, żeby się rozejrzeć
node scripts/seed-demo.js

Potem, w interfejsie: utwórz pracownika, zaloguj się na niego, wgraj załącznik, uruchom kopię ręczną i sprawdź ją na ekranie Kopie zapasowe. Te cztery gesty dotykają bazy, plików, uprawnień i archiwum.

9. Aktualizacja#

# 1. najpierw kopia zapasowa, zawsze — z ekranu Kopie zapasowe

# 2. kod
cd /var/www/toutadmin
git pull
npm ci --omit=dev

# 3. restart; baza aktualizuje się sama przy starcie
sudo systemctl restart toutadmin
journalctl -u toutadmin -n 30 --no-pager

Schemat zmienia się przez migracje idempotentne: powtórzenie aktualizacji niczego nie psuje. data/ i .env nigdy nie są ruszane.

Cofnięcie#

Przywróć kod do poprzedniej wersji i zrestartuj. Migracje nie usuwają kolumn: baza po migracji pozostaje czytelna dla poprzedniej wersji, chyba że dziennik zmian mówi inaczej. W razie wątpliwości odtwórz archiwum zrobione w kroku 1.

10. Robić kopie naprawdę#

Archiwum tar.gz niesie bazę i pięć katalogów plików. Baza kopiowana jest przez kopię on-line SQLite, która daje spójną kopię nawet w trakcie zapisu — edycja PHP osiąga to samo przez VACUUM INTO. Każdy plik niesie swój skrót SHA-256, sprawdzany przy odtwarzaniu.

Archiwum, które zostaje na serwerze, który chroni, nie chroni niczego: skonfiguruj cel zdalny (FTPS albo Google Drive) na ekranie Kopie zapasowe i podłącz webhook do sauvegarde.echec, by dowiedzieć się, gdy wysyłka na zewnątrz zawiedzie. Szczegóły są na stronie Kopie zapasowe i odtwarzanie.

Najpierw spróbować lokalnie#

npm install
cp .env.example .env
npm run dev        # restartuje przy każdej zmianie

Otwórz http://localhost:3000. Lokalnie zostaw NODE_ENV puste: w trybie production ciasteczka są oznaczone jako „bezpieczne” i nie zostaną zachowane na połączeniu nieszyfrowanym — kręciłbyś się w kółko na stronie logowania.

Przejść na edycję PHP albo z niej przyjść#

Obie edycje mają ten sam schemat — 142 tabel, 1311 kolumn — i ten sam format haseł. Zatrzymaj jedną, skopiuj app.sqlite oraz katalogi plików, uruchom drugą: nie ma żadnej konwersji. Zobacz Dwie edycje.

Dokumentacja Toutadmin — zbudowana 2026-09-13. Witryna samodzielna, niezależna od oprogramowania.