79b37d1891
Co-authored-by: Cursor <cursoragent@cursor.com>
384 lines
10 KiB
Markdown
384 lines
10 KiB
Markdown
# PhotoHost — Фото-хостинг
|
||
|
||
Современный фото-хостинг на **Python (Flask)**, **PostgreSQL** и **Docker Compose**.
|
||
|
||
- Красивая главная страница с drag-and-drop загрузкой
|
||
- Галерея загруженных фото
|
||
- Копирование прямых ссылок на изображения
|
||
- Хранение метаданных в PostgreSQL, файлов — в Docker volume
|
||
|
||
---
|
||
|
||
## Структура проекта
|
||
|
||
```
|
||
фотохостинг/
|
||
├── app/
|
||
│ ├── __init__.py # Flask-приложение и модель Photo
|
||
│ ├── routes.py # Маршруты (загрузка, галерея, API)
|
||
│ ├── 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://github.com/YOUR_USER/photohost.git фотохостинг
|
||
cd фотохостинг
|
||
```
|
||
|
||
**Вариант B — через SCP с локального компьютера:**
|
||
|
||
```bash
|
||
# Выполнить на локальной машине (Windows PowerShell / Linux)
|
||
scp -r ./фотохостинг user@YOUR_SERVER_IP:~/
|
||
```
|
||
|
||
```bash
|
||
# На сервере
|
||
cd ~/фотохостинг
|
||
```
|
||
|
||
**Вариант C — создать файлы вручную** — скопируйте содержимое проекта в каталог `~/фотохостинг`.
|
||
|
||
### 7. Настройка переменных окружения
|
||
|
||
```bash
|
||
cp .env.example .env
|
||
nano .env
|
||
```
|
||
|
||
Измените значения в `.env`:
|
||
|
||
```env
|
||
POSTGRES_USER=photohost
|
||
POSTGRES_PASSWORD=ВАШ_НАДЁЖНЫЙ_ПАРОЛЬ_БД
|
||
POSTGRES_DB=photohost
|
||
DATABASE_URL=postgresql://photohost:ВАШ_НАДЁЖНЫЙ_ПАРОЛЬ_БД@db:5432/photohost
|
||
|
||
SECRET_KEY=случайная_строка_минимум_32_символа
|
||
MAX_UPLOAD_MB=10
|
||
APP_PORT=8080
|
||
```
|
||
|
||
Сгенерировать случайный `SECRET_KEY`:
|
||
|
||
```bash
|
||
python3 -c "import secrets; print(secrets.token_hex(32))"
|
||
```
|
||
|
||
### 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
|
||
```
|
||
|
||
Загрузите тестовое изображение — оно должно появиться в галерее.
|
||
|
||
### 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
|
||
```
|
||
|
||
---
|
||
|
||
## Полезные команды
|
||
|
||
| Действие | Команда |
|
||
|-----------------------|----------------------------------|
|
||
| Остановить | `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 | `/upload` | Загрузка фото (form-data) |
|
||
| 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, Gunicorn
|
||
- PostgreSQL 16
|
||
- SQLAlchemy, Pillow
|
||
- Docker & Docker Compose
|