# Установка на свой сервер (Docker Compose, без Coolify) Портал поставляется как обычный проект Docker Compose: приложение (Python, FastAPI) + PostgreSQL 16. Никаких внешних сервисов не нужно — Coolify, Kubernetes и т. п. необязательны. Ниже — полная установка с нуля, обновление, резервные копии, откат и перенос на другой сервер. ## 1. Что нужно - Сервер Linux (Debian 12/13, Ubuntu 22.04/24.04 и т. п.), 2 ядра, 2 ГБ ОЗУ, от 10 ГБ диска (+ место под фото). - Docker Engine 24+ с плагином Compose v2: `docker --version`, `docker compose version`. Установка: (на Debian и Ubuntu — `curl -fsSL https://get.docker.com | sh`). - Git (или архив с исходниками). - Доменное имя и обратный прокси с HTTPS (nginx, Nginx Proxy Manager, Caddy, Traefik — любой). Без HTTPS не работают уведомления в браузере и установка мобильной версии на iPhone; приложение для Android в релизной сборке ходит только по HTTPS. ## 2. Получить проект ```bash sudo mkdir -p /opt/portal && sudo chown $USER /opt/portal git clone <адрес репозитория> /opt/portal cd /opt/portal git checkout main ``` ## 3. Настроить переменные ```bash cp .env.example .env nano .env ``` | Переменная | Что указать | |---|---| | `DB_PASSWORD` | Пароль базы (случайный: `openssl rand -hex 24`). После первого запуска **не меняйте** — он записан в базу. | | `SECRET_KEY` | Ключ подписи токенов, **не короче 32 символов**: `openssl rand -hex 32`. Смена завершит все сессии. | | `ADMIN_PASSWORD` | Пароль пользователя `admin`, создаётся при первом запуске, **не короче 10 символов**. | | `APP_TZ` | Часовой пояс предприятия (`Europe/Moscow`, `Asia/Yekaterinburg` …): смены, расписание нарядов, отчёты. | | `APP_PORT` | Порт на хосте, куда смотрит прокси (по умолчанию 8080). | | `FORWARDED_ALLOW_IPS` | Адрес обратного прокси, как его видит приложение (см. п. 6). | В боевом режиме (`APP_ENV=production` прописан в `docker-compose.yml`) приложение **не запустится** со слабыми `SECRET_KEY` или `ADMIN_PASSWORD` — это защита, а не ошибка: задайте значения по таблице. ## 4. Запустить ```bash docker compose up -d --build docker compose ps # app и db — running (healthy) curl -s http://127.0.0.1:8080/api/v1/health ``` Первая сборка занимает несколько минут. Логи: `docker compose logs -f app`. Данные хранятся в томах Docker: `pgdata` (база), `photos` (фото нарядов и задач), `appdata` (резервные копии, демо-база, загруженный APK). Они переживают пересборку и перезапуск контейнеров. Удаляет их только `docker compose down -v` — **не делайте так** на рабочем сервере. ## 5. Первый вход Откройте `http://<сервер>:8080` (до настройки прокси) или ваш домен, войдите как `admin` с паролем из `ADMIN_PASSWORD` и **сразу смените пароль**: «Администрирование → Пользователи» → admin → «Изменить» → новый пароль. Пустая система предложит **мастер первичной настройки**: графики, должности, отделы, места, оборудование, сотрудники, карта, склады, чек-листы, резервные копии. ## 6. Обратный прокси и HTTPS Приложение слушает обычный HTTP на `APP_PORT`; HTTPS даёт прокси. Пример для nginx на том же сервере (сертификат — certbot: `sudo certbot --nginx -d portal.example.ru`): ```nginx server { listen 443 ssl http2; server_name portal.example.ru; # ssl_certificate / ssl_certificate_key — пропишет certbot client_max_body_size 20m; # фото и картинки карт; иначе ошибка 413 location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 120s; # PDF-отчёты и AI-помощник могут отвечать долго } } server { listen 80; server_name portal.example.ru; return 301 https://$host$request_uri; } ``` - **Nginx Proxy Manager:** Proxy Host → Forward `http://:8080`, SSL (Let's Encrypt), Force SSL; во вкладке Advanced — `client_max_body_size 20m;`. - **Caddy:** `portal.example.ru { reverse_proxy 127.0.0.1:8080 }` и `request_body { max_size 20MB }`. **Адрес прокси (`FORWARDED_ALLOW_IPS`).** Приложение верит заголовку `X-Forwarded-For` только от этого адреса — иначе любой мог бы подделать свой IP и обойти ограничение попыток входа. Как настроить: зайдите на портал через прокси администратором → «Администрирование → Настройки сервера», блок «Состояние», строка «Адрес клиента, каким его видит сервер». Пока прокси не указан, там виден **адрес самого прокси** (если прокси на том же хосте — обычно шлюз сети Docker вида `172.18.0.1`, если на другой машине — её IP). Впишите его в `FORWARDED_ALLOW_IPS` и примените: `docker compose up -d`. После этого в той же строке должен появиться **ваш собственный** адрес — значит, всё настроено. **Файрвол.** Порт `APP_PORT` должен быть доступен только прокси. Docker публикует порты в обход `ufw`, поэтому либо привяжите порт к localhost (в `docker-compose.yml`: `"127.0.0.1:${APP_PORT:-8080}:8000"` — если прокси на этом же сервере), либо закройте его правилом в цепочке `DOCKER-USER` (iptables) или на внешнем файрволе. ## 7. Приложение для Android Готовый подписанный APK лежит в `release/` и входит в образ: ссылка «Скачать приложение для Android» на странице входа и адрес `/app.apk` работают сразу. Новую версию можно выложить без пересборки — положить файл в том `appdata`: ```bash docker compose cp checklists-1.7.0.apk app:/srv/data/apk/ ``` Версия берётся из имени файла (`checklists-<версия>.apk`); приложения сами предложат обновиться. В приложении на телефоне указывается адрес вашего сервера (`https://portal.example.ru`). ## 8. Обновление ```bash cd /opt/portal git pull docker compose up -d --build ``` Перед обновлением сделайте резервную копию (п. 9). Структура базы обновляется сама при запуске (только добавление новых полей), поэтому откат на предыдущую версию кода возможен: `git checkout <прежний коммит> && docker compose up -d --build`. ## 9. Резервные копии 1. **Встроенные (рекомендуется):** «Администрирование → Резервные копии». Копия — zip с базой в переносимом формате и настройками (по желанию — с фото); есть автокопии по расписанию (по умолчанию раз в сутки, хранится 14). Копии лежат в томе `appdata` (`/srv/data/backups`) — **регулярно забирайте их на другой носитель**: ```bash docker compose cp app:/srv/data/backups ./backups-$(date +%F) ``` 2. **Средствами PostgreSQL:** ```bash docker compose exec -T db pg_dump -U checklists -Fc checklists > portal-$(date +%F).dump docker run --rm -v portal_photos:/p -v "$PWD":/b alpine tar czf /b/photos-$(date +%F).tgz -C /p . ``` Имя тома — `<имя проекта>_photos` (имя проекта по умолчанию — имя каталога, здесь `portal`): `docker volume ls`. Восстановление дампа: `docker compose exec -T db pg_restore -U checklists -d checklists --clean --if-exists < portal-ДАТА.dump`. ## 10. Перенос на другой сервер 1. На старом сервере: «Резервные копии» → «Создать копию» с фото → скачать zip. 2. На новом: установка по пп. 1–6 (новые `DB_PASSWORD`/`SECRET_KEY` допустимы), вход `admin`. 3. «Резервные копии» → «Загрузить копию» → «Восстановить». Все данные, пользователи и пароли станут как на старом сервере; после восстановления все входят заново. ## 11. Обслуживание | Задача | Команда | |---|---| | Состояние | `docker compose ps` | | Логи приложения | `docker compose logs -f --tail 200 app` | | Перезапуск | `docker compose restart app` | | Остановить / запустить | `docker compose stop` / `docker compose start` | | Место на диске | `docker system df`, «Настройки сервера» → свободно на диске | | Забыт пароль admin | другой администратор задаёт новый в «Пользователях»; если администратор один — восстановите из копии или обратитесь к разработчику | Важно: приложение работает **одним процессом** (так настроено в образе) — планировщик нарядов и демо-режим живут внутри него. Не масштабируйте сервис `app` на несколько копий. ## 12. Если что-то не так - **Контейнер app перезапускается**, в логах `SECRET_KEY не задан или слишком короткий` / `ADMIN_PASSWORD слабый` — задайте значения по п. 3. - **Фото не загружаются (413)** — не задан `client_max_body_size 20m` в прокси. - **Все входы «блокируются на 5 минут»** — прокси не указан в `FORWARDED_ALLOW_IPS`, и все клиенты видны с одного адреса (п. 6). - **Неверное время нарядов и смен** — проверьте `APP_TZ` и время сервера («Настройки сервера» → время сервера). - **Нет уведомлений на телефоне** — нужен HTTPS; для Android-push через Firebase — ключ в «Настройках сервера».