- docker-compose.yml: профили docs (Nextcloud), office (ONLYOFFICE Document Server), ai (Ollama + скачивание модели) - автонастройка: адреса Nextcloud, ONLYOFFICE, портала и модели подставляются из .env или по именам сервисов во внутренней сети Docker; значения из интерфейса важнее - локальная модель в своей сети (имя сервиса, частный IP) подключается без ключа API - .env.example, docs/DEPLOY.md (раздел 7а), README
180 lines
16 KiB
Markdown
180 lines
16 KiB
Markdown
# Установка на свой сервер (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 — ключ в «Настройках сервера».
|