Files
fotohost/README.md
T
2026-06-06 22:50:10 +03:00

551 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.
# 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`
---
## Релиз 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 изображения, опциональная ссылка при клике, порядок сортировки, вкл/выкл
**Обновление до 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` | Управление рекламными баннерами |
| POST | `/upload` | Загрузка фото (auth) |
| GET | `/uploads/<filename>` | Прямая ссылка на файл |
| GET | `/api/photos` | JSON-список всех фото |
| POST | `/delete/<id>` | Удаление фото |
Пример ответа `/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
```
---
## Технологии
- Python 3.12, Flask 3, Flask-Login, Gunicorn
- PostgreSQL 16
- SQLAlchemy, Pillow
- Docker & Docker Compose