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#
| Element | Wersja | Dlaczego |
|---|---|---|
| Node.js | 22 lub nowszy | Produkt opiera się na wbudowanym uruchamiaczu testów i na nowszych interfejsach. |
| Kompilator C | build-essential | better-sqlite3 kompiluje się przy instalacji, chyba że istnieje gotowy plik binarny dla Twojej platformy. |
| Dysk lokalny | — | SQLite 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.
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.
| Zmienna | Domyślnie | Co robi |
|---|---|---|
PORT | 3000 | Port nasłuchu. |
NODE_ENV | — | production na produkcji: ciasteczka rygorystyczne, bez szczegółowych śladów. |
SESSION_SECRET | generowany | Pieczętuje sesje. Pozostawiony pusty, powstaje w data/session.key. |
INSTALL_TOKEN | — | Żądany przez kreatora przed instalacją. Zalecany na serwerze wystawionym na świat. |
TRUST_PROXY | — | 1 za zaufanym serwerem pośredniczącym — i tylko tam. |
DB_PATH | data/app.sqlite | Baza danych. |
UPLOAD_DIR, CV_DIR, VAULT_DIR, SIGN_DIR, DOCS_DIR, BACKUP_DIR | w data/ | Katalogi plików: zdjęcia, życiorysy, sejf, teczka podpisów, otrzymane dokumenty, archiwa. |
LOGIN_RATE_LIMIT | 10 | Próby logowania na kwadrans i na adres. |
GLOBAL_RATE_LIMIT | 300 | Żądania na minutę i na adres. |
API_RATE_LIMIT | — | Wywołania API na minutę i na token. |
SESSION_IDLE_MINUTES | 60 | Bezczynność, po której sesja wygasa. |
SESSION_MAX_HOURS | 12 | Czas bezwzględny, którego żadna aktywność nie przedłuża. |
ADMIN_EMAIL, ADMIN_PASSWORD | — | Instalacja bez interfejsu: zakłada administratora przy starcie. |
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.
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:
- Język instancji, spośród 16.
- Wymagania, sprawdzone i pokazane.
- Firma: nazwa wyświetlana wszędzie.
- Urlop roczny przyznawany każdemu nowemu pracownikowi na etacie.
- 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 przebieg | Gwarancja |
|---|---|
| Zamyka konta, którym skończyła się umowa | Sprawdzane także przy logowaniu i przy otwarciu pulpitu |
| Wystawia faktury abonamentów, którym nadszedł termin | Unikalny indeks nie pozwala zafakturować dwa razy |
| Zamienia terminy w powiadomienia | Klucz odduplikowania: jeden termin przypomina raz |
| Czyści przeczytane powiadomienia i dziennik audytu | Zgodnie z wybranym okresem przechowywania |
| Pobiera księgową skrzynkę pocztową (IMAP) | Jeśli pobieranie jest skonfigurowane |
| Opróżnia kolejkę webhooków wraz z ponowieniami | Pięć prób, potem rezygnacja |
| Tworzy kopię automatyczną i wysyła ją na zewnątrz | Gdy 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.