# PhotoHost — Фото-хостинг Современный фото-хостинг на **Python (Flask)**, **PostgreSQL** и **Docker Compose**. - Красивая главная страница с drag-and-drop загрузкой - **Регистрация и авторизация** пользователей - **Личный кабинет** — загрузка и управление своими фото - **Админ-панель** — пользователи, фото, статистика - **Автоматическое создание первого администратора** через `.env` - Галерея загруженных фото - Копирование прямых ссылок на изображения - Хранение метаданных в PostgreSQL, файлов — в Docker volume --- ## Структура проекта ``` fotohost/ ├── app/ │ ├── __init__.py # Flask-приложение, Flask-Login │ ├── models.py # User, Photo │ ├── auth.py # Регистрация, вход, выход │ ├── routes.py # Главная, загрузка, личный кабинет │ ├── admin.py # Админ-панель │ ├── bootstrap.py # Миграция схемы, первый admin │ ├── auth_utils.py # Декораторы доступа │ ├── templates/ # HTML-шаблоны │ └── static/ # CSS и JavaScript ├── uploads/ # Локальная папка (в Docker — volume) ├── docker-compose.yml # Оркестрация web + PostgreSQL ├── Dockerfile ├── wsgi.py # Точка входа для Gunicorn ├── requirements.txt ├── .env.example └── README.md ``` --- ## Развёртывание на Ubuntu 24.04 Подробная пошаговая инструкция для чистого сервера Ubuntu 24.04 LTS. ### 1. Подключение к серверу ```bash ssh user@YOUR_SERVER_IP ``` Замените `user` на имя пользователя и `YOUR_SERVER_IP` на IP-адрес сервера. ### 2. Обновление системы ```bash sudo apt update && sudo apt upgrade -y ``` ### 3. Установка необходимых пакетов ```bash sudo apt install -y ca-certificates curl gnupg git ``` ### 4. Установка Docker Docker официально поддерживается на Ubuntu 24.04. ```bash # Добавить GPG-ключ Docker sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod a+r /etc/apt/keyrings/docker.gpg # Добавить репозиторий Docker echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null # Установить Docker Engine и Compose plugin sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin ``` Проверка установки: ```bash sudo docker run hello-world docker compose version ``` ### 5. Добавить пользователя в группу docker (опционально) Чтобы не использовать `sudo` перед каждой командой docker: ```bash sudo usermod -aG docker $USER newgrp docker ``` ### 6. Копирование проекта на сервер **Вариант A — через Git:** ```bash cd ~ git clone https://git.evilfox.cc/test2/fotohost.git fotohost cd fotohost ``` **Вариант B — через SCP с локального компьютера:** ```bash # Выполнить на локальной машине (Windows PowerShell / Linux) scp -r ./fotohost user@YOUR_SERVER_IP:~/ ``` ```bash # На сервере cd ~/fotohost ``` **Вариант C — создать файлы вручную** — скопируйте содержимое проекта в каталог `~/fotohost`. ### 7. Настройка переменных окружения ```bash cp .env.example .env nano .env ``` Измените значения в `.env`: ```env POSTGRES_USER=photohost POSTGRES_PASSWORD=YOUR_STRONG_DB_PASSWORD POSTGRES_DB=photohost DATABASE_URL=postgresql://photohost:YOUR_STRONG_DB_PASSWORD@db:5432/photohost SECRET_KEY=random_string_min_32_chars MAX_UPLOAD_MB=10 APP_PORT=8080 # Первый администратор (создаётся автоматически при первом запуске) ADMIN_USERNAME=admin ADMIN_EMAIL=admin@example.com ADMIN_PASSWORD=YOUR_STRONG_ADMIN_PASSWORD ``` Сгенерировать случайный `SECRET_KEY`: ```bash python3 -c "import secrets; print(secrets.token_hex(32))" ``` > **Первый администратор:** при первом запуске, если в базе нет ни одного admin, создаётся пользователь из `ADMIN_USERNAME` / `ADMIN_EMAIL` / `ADMIN_PASSWORD`. Обязательно смените пароль в `.env` до деплоя. Альтернатива — создать admin вручную через CLI: ```bash docker compose exec web flask create-admin ``` ### 8. Запуск приложения ```bash docker compose up -d --build ``` Проверка статуса контейнеров: ```bash docker compose ps ``` Ожидаемый результат — оба сервиса `running`: | Сервис | Контейнер | Порт | |--------|----------------|-------------| | web | photohost-web | 8080 → 8000 | | db | photohost-db | 5432 (внутр.) | Просмотр логов: ```bash docker compose logs -f web ``` ### 9. Проверка работы Откройте в браузере: ``` http://YOUR_SERVER_IP:8080 ``` Загрузите тестовое изображение — оно должно появиться в галерее. Войдите как admin (`/auth/login`) → откройте **Админку** (`/admin`). ### 10. Открытие порта в файрволе (UFW) Если включён UFW: ```bash sudo ufw allow 8080/tcp sudo ufw allow OpenSSH sudo ufw enable sudo ufw status ``` ### 11. Автозапуск при перезагрузке сервера Docker Compose с `restart: unless-stopped` уже перезапускает контейнеры. Убедитесь, что Docker включён: ```bash sudo systemctl enable docker sudo systemctl start docker ``` --- ## Обновление до новой версии на сервере Когда выходит новая версия в Git, обновите проект на сервере без потери данных (БД и фото хранятся в Docker volumes, файл `.env` не перезаписывается). ### Быстрое обновление (последняя версия из `main`) ```bash cd ~/fotohost git pull origin main docker compose up -d --build docker compose ps docker compose logs --tail=50 web ``` ### Обновление до конкретного релиза (рекомендуется) Список доступных версий: ```bash cd ~/fotohost git fetch --tags git tag -l ``` Пример — установить релиз **v1.0-beta**: ```bash cd ~/fotohost git fetch --tags git checkout v1.0-beta docker compose up -d --build docker compose ps ``` Вернуться на последнюю dev-версию из `main`: ```bash cd ~/fotohost git checkout main git pull origin main docker compose up -d --build ``` ### Перед обновлением (рекомендуется) ```bash cd ~/fotohost # Бэкап базы данных docker compose exec db pg_dump -U photohost photohost > backup_$(date +%Y%m%d_%H%M).sql # Проверить, не появились ли новые переменные в .env.example diff .env .env.example || true nano .env ``` Если в `.env.example` появились новые строки — добавьте их в свой `.env` вручную. ### После обновления — проверка ```bash docker compose ps curl -I http://127.0.0.1:8080 docker compose logs --tail=100 web ``` Откройте сайт в браузере и проверьте вход, загрузку фото и админку. ### Если что-то пошло не так — откат на предыдущий тег ```bash cd ~/fotohost git checkout v1.0-beta docker compose up -d --build ``` > **Важно:** команда `docker compose up -d --build` пересобирает контейнер `web`, но **не удаляет** volumes с PostgreSQL и загруженными фото. --- ## Регистрация, авторизация и роли | URL | Описание | |-----|----------| | `/auth/register` | Регистрация нового пользователя | | `/auth/login` | Вход (логин или email) | | `/auth/logout` | Выход | | `/cabinet/` | Личный кабинет — мои фото | | `/cabinet/profile` | Настройки профиля, смена пароля | | `/admin/` | Панель администратора (только admin) | | `/admin/users` | Управление пользователями | | `/admin/groups` | Группы: квота диска, лимиты папок и фото | | `/admin/banners` | Рекламные баннеры на сайте | | `/admin/photos` | Все фото на сервере | **Права доступа:** - Загрузка фото — только авторизованным пользователям - Удаление фото — владелец или администратор - Админка — только пользователи с `is_admin=True` --- ## Релиз v2.2 **Настройки авторизации в админке** - Включение/отключение регистрации, входа по паролю и Passkey - Passkey RP ID, RP Name и Origin — без правки `.env` - Captcha: Cloudflare Turnstile, Google reCAPTCHA v2 и v3 - Выбор страниц с captcha: вход, регистрация, сброс пароля **Captcha** - Один активный провайдер на сайт (настраивается в админке) - reCAPTCHA v3 — проверка score на сервере (порог 0–1) **Обновление до v2.2 на сервере:** ```bash cd ~/fotohost git fetch --tags git checkout v2.2 docker compose up -d --build ``` После деплоя откройте **Админка → Настройки** и задайте Passkey (RP ID / Origin) и captcha. --- ## Релиз v2.1 **GDPR и cookies** - Политика конфиденциальности: `/legal/privacy` - Политика cookies: `/legal/cookies` - GDPR-права: `/legal/gdpr` - Баннер согласия на cookies - Экспорт данных и удаление аккаунта в профиле **Passkey (WebAuthn)** - Регистрация passkey в профиле - Вход кнопкой «Войти с Passkey» на странице входа - Переменные `WEBAUTHN_RP_ID`, `WEBAUTHN_ORIGIN` в `.env` **Управление сессиями** - Список активных сессий в профиле (устройство, IP, время) - Завершение отдельной сессии или всех кроме текущей **Обновлённая админка** - Боковое меню, карточки статистики, улучшенная вёрстка **Обновление до v2.1 на сервере:** ```bash cd ~/fotohost git fetch --tags git checkout v2.1 # добавьте WEBAUTHN_RP_ID и WEBAUTHN_ORIGIN в .env docker compose up -d --build ``` --- ## Релиз v2.0 **Загрузка по прямым ссылкам** - В форме загрузки вкладка «Ссылки» - Вставьте одну или несколько прямых URL на изображения (HTTP/HTTPS) - Поддерживаются те же форматы и лимиты, что и при обычной загрузке **Поделиться: BBCode, HTML, QR** - На каждой фотографии кнопки **Ссылка**, **BBCode** и **QR** - BBCode для форумов: `[img]https://...[/img]` - HTML для сайтов: `` - QR-код открывается в модальном окне с быстрым копированием всех форматов **Обновление до v2.0 на сервере:** ```bash cd ~/fotohost git fetch --tags git checkout v2.0 docker compose up -d --build ``` --- ## Релиз v1.4 **Лимиты групп пользователей** - В `/admin/groups` администратор задаёт для каждой группы: - **Квота диска** (МБ, `0` = без лимита) - **Максимум папок** на пользователя (`0` = без лимита) - **Максимум фото** на пользователя (`0` = без лимита) - Лимиты проверяются при создании папки и загрузке фото - В личном кабинете отображается использование квот **Переменные `.env` для группы по умолчанию:** ```env DEFAULT_GROUP_QUOTA_MB=100 DEFAULT_GROUP_MAX_FOLDERS=10 DEFAULT_GROUP_MAX_PHOTOS=500 ``` **Рекламные баннеры** - Управление в `/admin/banners` - Позиции: главная (под hero), личный кабинет, подвал - URL изображения, опциональная ссылка при клике, порядок сортировки, вкл/выкл **Git deploy из админки** - Исправлена ошибка `could not lock config file .git/config: Permission denied` - Fetch/checkout без записи в `.git/config`, автоматическое восстановление прав **Обновление до v1.4 на сервере:** ```bash cd ~/fotohost git fetch --tags git checkout v1.4 docker compose up -d --build ``` --- ## Полезные команды | Действие | Команда | |-----------------------|----------------------------------| | Остановить | `docker compose down` | | Перезапустить | `docker compose restart` | | Пересобрать | `docker compose up -d --build` | | Логи web | `docker compose logs -f web` | | Логи БД | `docker compose logs -f db` | | Зайти в контейнер web | `docker compose exec web bash` | | Зайти в PostgreSQL | `docker compose exec db psql -U photohost -d photohost` | --- ## Резервное копирование ### База данных ```bash docker compose exec db pg_dump -U photohost photohost > backup_$(date +%Y%m%d).sql ``` Восстановление: ```bash cat backup_20250606.sql | docker compose exec -T db psql -U photohost -d photohost ``` ### Загруженные фото Фото хранятся в Docker volume `uploads_data`. Список volumes: ```bash docker volume ls ``` Бэкап volume: ```bash docker run --rm -v photohost_uploads_data:/data -v $(pwd):/backup alpine tar czf /backup/uploads_backup.tar.gz -C /data . ``` --- ## Настройка домена и HTTPS (Nginx + Let's Encrypt) ### Установка Nginx и Certbot ```bash sudo apt install -y nginx certbot python3-certbot-nginx ``` ### Конфиг Nginx ```bash sudo nano /etc/nginx/sites-available/photohost ``` ```nginx server { listen 80; server_name photos.example.com; client_max_body_size 15M; location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } ``` ```bash sudo ln -s /etc/nginx/sites-available/photohost /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx ``` ### SSL-сертификат ```bash sudo certbot --nginx -d photos.example.com ``` Откройте порты 80 и 443: ```bash sudo ufw allow 'Nginx Full' ``` --- ## Локальная разработка (без Docker) ```bash # Установить PostgreSQL локально или запустить только БД: docker compose up -d db python3 -m venv venv source venv/bin/activate # Windows: venv\Scripts\activate pip install -r requirements.txt cp .env.example .env # Измените DATABASE_URL на localhost: # DATABASE_URL=postgresql://photohost:photohost_secret@localhost:5432/photohost export FLASK_APP=wsgi.py python wsgi.py ``` Приложение будет доступно на `http://localhost:8000`. > Для локального запуска только БД добавьте в `docker-compose.yml` для сервиса `db` строку `ports: - "5432:5432"`. --- ## API | Метод | URL | Описание | |-------|---------------|-----------------------------| | GET | `/` | Главная страница | | POST | `/auth/register` | Регистрация | | POST | `/auth/login` | Вход | | GET | `/cabinet/` | Личный кабинет | | GET | `/admin/` | Админ-панель | | GET | `/admin/banners` | Управление рекламными баннерами | | GET | `/photo//qr` | QR-код для прямой ссылки на фото | | POST | `/upload` | Загрузка фото или по URL (auth) | | GET | `/uploads/` | Прямая ссылка на файл | | GET | `/api/photos` | JSON-список всех фото | | POST | `/delete/` | Удаление фото | Пример ответа `/api/photos`: ```json [ { "id": 1, "url": "/uploads/abc123.jpg", "original_name": "photo.jpg", "file_size": 245760, "size_human": "240.0 КБ", "created_at": "2025-06-06T12:00:00+00:00" } ] ``` --- ## Устранение неполадок **Контейнер web не запускается** ```bash docker compose logs web ``` Частая причина — БД ещё не готова. Healthcheck в `docker-compose.yml` решает это; подождите 30 секунд и перезапустите: ```bash docker compose restart web ``` **Ошибка подключения к PostgreSQL** Проверьте, что пароли в `.env` совпадают в `POSTGRES_PASSWORD` и `DATABASE_URL`. **Фото не загружаются (413 Request Entity Too Large)** Увеличьте `MAX_UPLOAD_MB` в `.env` и `client_max_body_size` в Nginx. **Порт 8080 занят** Измените `APP_PORT=9090` в `.env` и перезапустите: ```bash docker compose down && docker compose up -d ``` **502 Bad Gateway (Nginx)** Nginx не может достучаться до контейнера `web`. Проверьте: ```bash cd ~/fotohost docker compose ps docker compose logs --tail=100 web curl -I http://127.0.0.1:8080/health ``` Частые причины после обновления: 1. Контейнер `photohost-web` не запущен или перезапускается — смотрите логи `docker compose logs web` 2. В Nginx указан неверный порт — должен совпадать с `APP_PORT` из `.env` (по умолчанию `8080`): ```nginx proxy_pass http://127.0.0.1:8080; ``` 3. База данных ещё не готова — подождите 30–60 секунд и выполните `docker compose restart web` --- ## Технологии - Python 3.12, Flask 3, Flask-Login, Gunicorn - PostgreSQL 16 - SQLAlchemy, Pillow - Docker & Docker Compose