Files
tech-service-portal/docs/DEPLOY.md
T
darkorion e67c175f80 feat: документация из Nextcloud, график у сотрудника, автоочистка фото, мониторинг, итог мастера, фильтр по местам; доп. сервисы в docker-compose
- docker-compose.yml: профили docs (Nextcloud), office (ONLYOFFICE Document Server), ai (Ollama + скачивание модели)
- автонастройка: адреса Nextcloud, ONLYOFFICE, портала и модели подставляются из .env или по именам сервисов
  во внутренней сети Docker; значения из интерфейса важнее
- локальная модель в своей сети (имя сервиса, частный IP) подключается без ключа API
- .env.example, docs/DEPLOY.md (раздел 7а), README
2026-10-13 13:27:20 +03:00

180 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Установка на свой сервер (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`.
Установка: <https://docs.docker.com/engine/install/> (на 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://<IP сервера>: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`).
## 7а. Дополнительные сервисы: документация, просмотр Office, локальный ИИ
`docker-compose.yml` умеет поднять рядом с порталом ещё три сервиса. Они включаются **профилями** — строкой в `.env`:
```bash
COMPOSE_PROFILES=docs,office,ai
```
| Профиль | Что запускается | Зачем | Ресурсы сверх портала |
|---|---|---|---|
| `docs` | Nextcloud + своя PostgreSQL | хранилище документации оборудования: паспорта, схемы, руководства | 1 ГБ памяти, место под документы |
| `office` | ONLYOFFICE Document Server | Word, Excel, PowerPoint открываются прямо в портале (переводятся в PDF) | 2–4 ГБ памяти |
| `ai` | Ollama + скачивание модели | локальная модель для AI-помощника: вопросы и документы не уходят в интернет | 6–8 ГБ памяти для 7B-модели, лучше видеокарта |
Профили независимы: можно взять только `ai` или подключить свой Nextcloud и ONLYOFFICE, указав их адреса в `.env`
(`NC_URL`, `ONLYOFFICE_URL`, `PORTAL_INTERNAL_URL`).
**Настройки заполняются сами.** Портал ищет сервисы во внутренней сети Docker по именам `nextcloud`, `onlyoffice`, `ollama`
и подставляет адреса в «Настройки сервера»: Nextcloud — `http://nextcloud`, ONLYOFFICE — `http://onlyoffice` (документы
он забирает у портала по `http://app:8000`), модель — `http://ollama:11434/v1` с моделью из `OLLAMA_MODEL`. Учётная запись
Nextcloud и секрет ONLYOFFICE берутся из `.env`. Всё, что администратор поменяет в интерфейсе, важнее автоматических значений.
Что сделать после запуска:
1. **Nextcloud** откройте на порту `NEXTCLOUD_PORT` (по умолчанию 8081), войдите администратором из `.env`, создайте папку
документации (например, «Документация») с подпапками по машинам и укажите её в `NC_ROOT` или в настройках портала.
Для безопасности заведите отдельного пользователя с доступом только на чтение к этой папке и пароль приложения для
него — и впишите их в `NC_USER` / `NC_PASSWORD`.
2. **ONLYOFFICE** наружу не публикуется: портал общается с ним внутри сети Docker. Задайте свой `ONLYOFFICE_JWT`.
3. **ИИ**: дождитесь, пока `ollama-pull` скачает модель (`docker compose logs -f ollama-pull`), затем в «Настройках сервера →
AI-помощник» нажмите «Проверить» и включите помощника. Опция «Работа с документацией» включается там же.
## 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 — ключ в «Настройках сервера».