Портал технической службы: наряды и чек-листы, задачи, отказы и неисправности, анализ надёжности, карты, склады, приложение для Android
This commit is contained in:
+149
@@ -0,0 +1,149 @@
|
||||
# Установка на свой сервер (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`).
|
||||
|
||||
## 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 — ключ в «Настройках сервера».
|
||||
Reference in New Issue
Block a user