Перейти к содержимому

Эксплуатация

Как держать сервер TSMC в работе: где лежат данные, резервные копии и восстановление, обновление, HTTPS, остановка, мониторинг.

Всё состояние сервера — в одной папке (в Docker — том /data):

Что Где Переменная
база SQLite tsmc.db (и tsmc.db-wal, tsmc.db-shm, пока сервер работает) TSMC_DB
вложения files/ рядом с базой — файлы по SHA-256, один раз на одинаковое содержимое TSMC_FILES
ключ токенов трекеров secret.key рядом с базой TSMC_SECRET_KEY вместо файла
резервные копии backups/ рядом с базой (по умолчанию) --out
Окно терминала
# на сервере
pnpm tsmc-server backup # → data/backups/tsmc-backup-20261025-103000/
# в Docker
docker exec tsmc tsmc-server backup # → /data/backups/… (в томе)

Копия делается на ходу: сервер не останавливается и продолжает принимать запросы. База копируется командой SQLite VACUUM INTO — это снимок одной транзакции, целостный, даже если в этот момент идёт запись. Затем копируются файлы вложений, на которые ссылается снимок (они не меняются после записи), и ключ secret.key. В папке копии — manifest.json: время, версия, схема базы, размеры, контрольная сумма базы, сколько чего в ней. Недописанная копия называется ….partial и при ошибке удаляется.

Копию стоит увозить с сервера. Пример ежедневной копии с хранением 14 дней (cron, Docker):

30 3 * * * docker exec tsmc tsmc-server backup && docker run --rm -v tsmc-data:/data alpine sh -c 'cd /data/backups && ls -dt tsmc-backup-* | tail -n +15 | xargs -r rm -rf'

Если ключ задан переменной TSMC_SECRET_KEY, в копию он не попадает (так сказано и в manifest.json): храните его рядом с копией сами, иначе токены трекеров после восстановления придётся ввести заново.

Окно терминала
pnpm tsmc-server check # данные сервера
TSMC_DB=backups/tsmc-backup-…/tsmc.db TSMC_FILES=backups/tsmc-backup-…/files pnpm tsmc-server check # копия

Проверяет целостность SQLite (integrity_check), ссылочную целостность (foreign_key_check) и что файл каждого вложения на месте. Код выхода 2 — что-то не так. Неиспользуемые файлы (остаток прерванной загрузки) не ошибка: они удаляются при следующем удалении вложения.

  1. Остановите сервер (docker compose stop tsmc, systemctl stop tsmc): работающий сервер держит базу открытой, и восстановление поверх него потеряет данные.
  2. Восстановите:
    Окно терминала
    pnpm tsmc-server restore data/backups/tsmc-backup-20261025-103000 --force
    # в Docker, пока контейнер остановлен:
    docker run --rm -v tsmc-data:/data tsmc tsmc-server restore /data/backups/tsmc-backup-20261025-103000 --force
    Перед заменой команда сверяет контрольную сумму и целостность копии. Прежняя база сохраняется рядом как tsmc.db.before-restore-<время>, прежний ключ — так же. Без --force существующая база не заменяется.
  3. Запустите сервер. Если копия старше версии сервера, он сам применит недостающие миграции; копию более новой версии восстановить нельзя — нужен сервер той версии.
  1. Сделайте копию (tsmc-server backup).
  2. Обновите образ или код и перезапустите. Миграции схемы применяются при старте, каждая — одной транзакцией: прерванная миграция не оставляет базу наполовину изменённой. Большие миграции (например, перестройка таблицы результатов на этапе 20) на базе с сотней тысяч результатов идут секунды.
  3. Если что-то не так — остановите сервер и восстановите копию с прежней версией.

Сервер не занимается сертификатами: снаружи его закрывает обратный прокси. Готовые примеры:

За прокси серверу нужны TSMC_SECURE_COOKIES=1 (cookie только по HTTPS), TSMC_TRUST_PROXY=1 (адрес клиента из X-Forwarded-For — для ограничения попыток входа и журнала) и TSMC_PUBLIC_URL (адрес в ссылках из заведённых багов). Лимит тела запроса в прокси — не меньше TSMC_MAX_UPLOAD_MB (вложения) и с запасом для импорта CSV.

На SIGTERM (docker stop, systemctl stop) и SIGINT (Ctrl+C) сервер перестаёт принимать соединения, дожидается текущих запросов (до 10 с), переносит журнал SQLite в основной файл и выходит с кодом 0. После остановки данные — это tsmc.db без -wal, вложения и ключ. В Docker дайте на это время: stop_grace_period: 20s в compose (по умолчанию Docker ждёт 10 с).

  • GET /api/health — живость, версия сервера и API; на него смотрит HEALTHCHECK образа.

  • Метрики Prometheus (TSMC_METRICS=1): GET /metrics, вне /api и без входа; с TSMC_METRICS_TOKEN требуется Authorization: Bearer <токен>. Наружу их лучше не отдавать (в примерах прокси /metrics закрыт). Что есть:

    • tsmc_http_requests_total{method,route,status} и tsmc_http_request_duration_seconds (гистограмма) — по шаблонам маршрутов (/api/runs/:id), а не адресам;
    • tsmc_projects, tsmc_cases{status}, tsmc_runs{status}, tsmc_results, tsmc_users;
    • tsmc_attachments_bytes, tsmc_database_bytes{file="main|wal"}, tsmc_process_memory_bytes, tsmc_uptime_seconds.

    Пример для Prometheus:

    scrape_configs:
    - job_name: tsmc
    authorization: { credentials: change-me } # TSMC_METRICS_TOKEN
    static_configs: [{ targets: ['tsmc:3000'] }]
  • Журнал — JSON в stdout (Fastify/pino): docker logs tsmc, journalctl -u tsmc.