T ToutadminДокументация

Установка редакции Node

Редакция Node работает как постоянная служба: один процесс, база SQLite на диске, больше ставить нечего — ни внешней базы данных, ни кеша, ни очереди. Это руководство ведёт от пустого каталога до рабочего экземпляра, включая то, что большинство руководств обходит стороной: системную службу, обратный прокси, TLS, резервные копии и обновление.

Что понадобится#

СоставляющаяВерсияЗачем
Node.js22 или новееПродукт опирается на встроенный запуск тестов и на свежие интерфейсы.
Компилятор Cbuild-essentialbetter-sqlite3 собирается при установке, если для вашей платформы нет готового двоичного пакета.
Локальный дискSQLite и загруженные файлы. Никогда сетевая шара: см. ниже.

Машины с одним ядром и 512 МБ памяти хватит на несколько десятков человек. Важна не мощность, а диск: он должен быть локальным и резервируемым.

1. Получить и установить#

git clone <ваш-репозиторий> /var/www/toutadmin
cd /var/www/toutadmin
npm ci --omit=dev

npm ci, а не npm install: он ставит ровно то, что описано в файле блокировки, и никогда его не переписывает. На сервере уехавшая версия — это сбой, которого никто не понимает.

Если сборка не удалась

better-sqlite3 — единственная зависимость, собирающая код на C. sudo apt install -y build-essential python3 решает почти все случаи.

2. Настроить#

cp .env.example .env

Всё задаётся переменными окружения — читаемыми из .env или выставляемыми вашим диспетчером служб. Ни одна не обязательна: без .env экземпляр поднимается на порту 3000 и отправляет вас в мастер установки.

ПеременнаяПо умолчаниюЧто делает
PORT3000Порт, на котором слушают.
NODE_ENVproduction в рабочем режиме: строгие куки, без подробных следов.
SESSION_SECRETсоздаётсяСкрепляет сессии. Если оставить пустым, создаётся в data/session.key.
INSTALL_TOKENТребуется мастером до установки. Рекомендуется на открытом сервере.
TRUST_PROXY1 за доверенным прокси — и только там.
DB_PATHdata/app.sqliteБаза данных.
UPLOAD_DIR, CV_DIR, VAULT_DIR, SIGN_DIR, DOCS_DIR, BACKUP_DIRвнутри data/Папки файлов: фотографии, резюме, сейф, папка на подпись, поступившие документы, архивы.
LOGIN_RATE_LIMIT10Попыток входа за четверть часа с одного адреса.
GLOBAL_RATE_LIMIT300Запросов в минуту с одного адреса.
API_RATE_LIMITВызовов API в минуту на один токен.
SESSION_IDLE_MINUTES60Бездействие, после которого сессия обрывается.
SESSION_MAX_HOURS12Предельный срок, который не продлевает никакаяактивность.
ADMIN_EMAIL, ADMIN_PASSWORDУстановка без интерфейса: создаёт администратора при запуске.
Секрет сессий

Смена SESSION_SECRET разом выкидывает всех. Задайте его один раз, создайте случайным (node -e "console.log(require('crypto').randomBytes(32).toString('hex'))") и сохраните вместе с остальным. Никогда не оставляйте его в репозитории git.

Поставить установочный токен#

Между первым запуском и вашим приходом в мастер экземпляр принадлежит тому, кто его нашёл: кто пришёл первым, тот и создаёт учётную запись администратора. Поэтому задайте INSTALL_TOKEN до того, как откроете порт:

node -e "console.log(require('crypto').randomBytes(16).toString('hex'))"
# затем в .env:
INSTALL_TOKEN=c3f1…

Мастер сравнивает его за постоянное время — обычное сравнение позволило бы угадывать токен посимвольно — и записывает каждый отказ. После установки мастер закрывается сам.

Установка без интерфейса#

Для автоматического развёртывания ADMIN_EMAIL и ADMIN_PASSWORD создают администратора при запуске, минуя мастер. Затем уберите их: пароль в окружении службы читает всякий, кто читает эту службу.

3. Права и где лежат данные#

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

Код может остаться только для чтения; писать нужно лишь в data/. SQLite создаёт соседние файлы (-wal, -shm): важна папка, а не только сама база.

Никогда на сетевой шаре

NFS и SMB лгут о блокировке файлов. SQLite полагается на эту блокировку, чтобы не допустить двух одновременных записей: на сетевой шаре база в конце концов портится без предупреждения. Локальный диск, всегда, — и уезжает наружу резервная копия, а не база.

4. Служба#

Запущенный вручную продукт останавливается, как только вы закроете терминал. Отдайте его 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

# Службе нужно писать только в 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

Пять строк ужесточения — не украшение: ProtectSystem=strict делает всю файловую систему недоступной для записи, а ReadWritePaths открывает единственную папку, которой это нужно. Уязвимость произвольной записи тогда достаёт лишь до data/.

5. Обратный прокси#

Никогда не выставляйте порт 3000 прямо в интернет: он не умеет TLS, и учиться ему незачем.

server {
    listen 443 ssl http2;
    server_name intranet.primer.ru;

    ssl_certificate     /etc/letsencrypt/live/intranet.primer.ru/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/intranet.primer.ru/privkey.pem;

    client_max_body_size 20M;          # загрузки в сейф, поступившие документы

    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.primer.ru;
    return 301 https://$host$request_uri;
}

Заодно заставьте службу слушать только петлевой интерфейс, чтобы порт 3000 был доступен лишь прокси.

TLS#

sudo certbot --nginx -d intranet.primer.ru

Продукт выставляет заголовок Strict-Transport-Security, как только соединение зашифровано, и никогда открытым текстом: объявленный с незашифрованной страницы он не был бы прочитан, а локальную пробу запер бы в https на полгода.

TRUST_PROXY идёт вместе с прокси — и только с ним

Эта переменная велит серверу верить адресу источника, объявленному в заголовке. За nginx это то, что нужно: иначе все запросы кажутся идущими с 127.0.0.1, и ограничения по адресу ничего не защищают. Без прокси впереди всё наоборот: любой объявит адрес по своему выбору и обойдёт ограничения.

6. Мастер установки#

Откройте свой домен. Пока нет ни одной учётной записи, любой адрес перенаправляет на /installation. Пять шагов:

  1. Язык экземпляра, из 16.
  2. Требования, проверенные и показанные.
  3. Организация: название, которое видно всюду.
  4. Годовой отпуск, назначаемый каждому новому штатному сотруднику.
  5. Учётная запись администратора: адрес и пароль не короче двенадцати знаков.

Как только появляется учётная запись, /installation перенаправляет на страницу входа: мастер закрылся сам, вручную удалять нечего.

7. Периодический обход#

В отличие от редакции PHP, планировать нечего: сервер несёт собственный планировщик, который просыпается каждый час и делает восемь вещей.

Что делает обходГарантия
Закрывает учётные записи с истёкшим договоромПроверяется также при входе и при открытии панели
Выставляет счета по подписке, срок которых насталУникальный индекс не даёт выставить дважды
Превращает сроки в уведомленияКлюч устранения повторов: один срок напоминает один раз
Чистит прочитанные уведомления и журнал аудитаСогласно выбранному сроку хранения
Забирает бухгалтерский почтовый ящик (IMAP)Если приём настроен
Опустошает очередь веб-перехватчиков с их повторамиПять попыток, затем отказ
Создаёт автоматическую копию и выносит её наружуКогда истёк заданный промежуток

Каждая операция идемпотентна: повторный обход не выставит счёт дважды и не уведомит дважды. Именно это позволяет перезапускать службу в любой момент не задумываясь.

8. Проверить, что всё работает#

# тесты (в вашу базу они не пишут)
npm test

# демонстрационный экземпляр, чтобы походить
node scripts/seed-demo.js

Затем в интерфейсе: заведите сотрудника, войдите под ним, загрузите вложение, запустите резервную копию вручную и проверьте её на экране Резервные копии. Эти четыре действия затрагивают базу, файлы, права и архив.

9. Обновление#

# 1. сначала резервная копия, всегда — с экрана «Резервные копии»

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

# 3. перезапуск; база обновляется сама при старте
sudo systemctl restart toutadmin
journalctl -u toutadmin -n 30 --no-pager

Схема развивается идемпотентными миграциями: повторить обновление ничего не ломает. data/ и .env не трогают никогда.

Откат#

Верните код на прежнюю версию и перезапустите. Миграции не удаляют столбцов: перенесённая база остаётся читаемой прежней версией, если в журнале версий не сказано иного. Если сомневаетесь — восстановите архив, снятый на шаге 1.

10. Резервировать по-настоящему#

Архив tar.gz несёт базу и пять папок с файлами. База копируется встроенным в SQLite онлайновым резервированием, дающим согласованную копию даже во время записи — редакция PHP добивается того же через VACUUM INTO. Каждый файл несёт свою контрольную сумму SHA-256, проверяемую при восстановлении.

Архив, оставшийся на сервере, который он защищает, не защищает ничего: настройте внешнее хранилище (FTPS или Google Drive) на экране Резервные копии и подпишите веб-перехватчик на sauvegarde.echec, чтобы узнавать о неудачном выносе. Подробности — на странице Резервное копирование и восстановление.

Сначала попробовать локально#

npm install
cp .env.example .env
npm run dev        # перезапускается при каждом изменении

Откройте http://localhost:3000. Локально оставьте NODE_ENV пустым: в режиме production куки помечены «защищёнными» и не сохранятся на открытом соединении — вы будете ходить по кругу на странице входа.

Перейти на редакцию PHP или прийти с неё#

Обе редакции делят одну схему — 142 таблиц, 1311 столбцов — и один формат паролей. Остановите одну, скопируйте app.sqlite и папки файлов, запустите другую: преобразования нет. См. Две редакции.

Документация Toutadmin — собрана 2026-09-13. Самостоятельный сайт, независимый от программы.