Co-authored-by: Cursor <cursoragent@cursor.com>
15 KiB
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. Подключение к серверу
ssh user@YOUR_SERVER_IP
Замените user на имя пользователя и YOUR_SERVER_IP на IP-адрес сервера.
2. Обновление системы
sudo apt update && sudo apt upgrade -y
3. Установка необходимых пакетов
sudo apt install -y ca-certificates curl gnupg git
4. Установка Docker
Docker официально поддерживается на Ubuntu 24.04.
# Добавить 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
Проверка установки:
sudo docker run hello-world
docker compose version
5. Добавить пользователя в группу docker (опционально)
Чтобы не использовать sudo перед каждой командой docker:
sudo usermod -aG docker $USER
newgrp docker
6. Копирование проекта на сервер
Вариант A — через Git:
cd ~
git clone https://git.evilfox.cc/test2/fotohost.git fotohost
cd fotohost
Вариант B — через SCP с локального компьютера:
# Выполнить на локальной машине (Windows PowerShell / Linux)
scp -r ./fotohost user@YOUR_SERVER_IP:~/
# На сервере
cd ~/fotohost
Вариант C — создать файлы вручную — скопируйте содержимое проекта в каталог ~/fotohost.
7. Настройка переменных окружения
cp .env.example .env
nano .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:
python3 -c "import secrets; print(secrets.token_hex(32))"
Первый администратор: при первом запуске, если в базе нет ни одного admin, создаётся пользователь из
ADMIN_USERNAME/ADMIN_EMAIL/ADMIN_PASSWORD. Обязательно смените пароль в.envдо деплоя.
Альтернатива — создать admin вручную через CLI:
docker compose exec web flask create-admin
8. Запуск приложения
docker compose up -d --build
Проверка статуса контейнеров:
docker compose ps
Ожидаемый результат — оба сервиса running:
| Сервис | Контейнер | Порт |
|---|---|---|
| web | photohost-web | 8080 → 8000 |
| db | photohost-db | 5432 (внутр.) |
Просмотр логов:
docker compose logs -f web
9. Проверка работы
Откройте в браузере:
http://YOUR_SERVER_IP:8080
Загрузите тестовое изображение — оно должно появиться в галерее.
Войдите как admin (/auth/login) → откройте Админку (/admin).
10. Открытие порта в файрволе (UFW)
Если включён UFW:
sudo ufw allow 8080/tcp
sudo ufw allow OpenSSH
sudo ufw enable
sudo ufw status
11. Автозапуск при перезагрузке сервера
Docker Compose с restart: unless-stopped уже перезапускает контейнеры. Убедитесь, что Docker включён:
sudo systemctl enable docker
sudo systemctl start docker
Обновление до новой версии на сервере
Когда выходит новая версия в Git, обновите проект на сервере без потери данных (БД и фото хранятся в Docker volumes, файл .env не перезаписывается).
Быстрое обновление (последняя версия из main)
cd ~/fotohost
git pull origin main
docker compose up -d --build
docker compose ps
docker compose logs --tail=50 web
Обновление до конкретного релиза (рекомендуется)
Список доступных версий:
cd ~/fotohost
git fetch --tags
git tag -l
Пример — установить релиз v1.0-beta:
cd ~/fotohost
git fetch --tags
git checkout v1.0-beta
docker compose up -d --build
docker compose ps
Вернуться на последнюю dev-версию из main:
cd ~/fotohost
git checkout main
git pull origin main
docker compose up -d --build
Перед обновлением (рекомендуется)
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 вручную.
После обновления — проверка
docker compose ps
curl -I http://127.0.0.1:8080
docker compose logs --tail=100 web
Откройте сайт в браузере и проверьте вход, загрузку фото и админку.
Если что-то пошло не так — откат на предыдущий тег
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/photos |
Все фото на сервере |
Права доступа:
- Загрузка фото — только авторизованным пользователям
- Удаление фото — владелец или администратор
- Админка — только пользователи с
is_admin=True
Полезные команды
| Действие | Команда |
|---|---|
| Остановить | 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 |
Резервное копирование
База данных
docker compose exec db pg_dump -U photohost photohost > backup_$(date +%Y%m%d).sql
Восстановление:
cat backup_20250606.sql | docker compose exec -T db psql -U photohost -d photohost
Загруженные фото
Фото хранятся в Docker volume uploads_data. Список volumes:
docker volume ls
Бэкап volume:
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
sudo apt install -y nginx certbot python3-certbot-nginx
Конфиг Nginx
sudo nano /etc/nginx/sites-available/photohost
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;
}
}
sudo ln -s /etc/nginx/sites-available/photohost /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
SSL-сертификат
sudo certbot --nginx -d photos.example.com
Откройте порты 80 и 443:
sudo ufw allow 'Nginx Full'
Локальная разработка (без Docker)
# Установить 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/ |
Админ-панель |
| POST | /upload |
Загрузка фото (auth) |
| GET | /uploads/<filename> |
Прямая ссылка на файл |
| GET | /api/photos |
JSON-список всех фото |
| POST | /delete/<id> |
Удаление фото |
Пример ответа /api/photos:
[
{
"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 не запускается
docker compose logs web
Частая причина — БД ещё не готова. Healthcheck в docker-compose.yml решает это; подождите 30 секунд и перезапустите:
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 и перезапустите:
docker compose down && docker compose up -d
Технологии
- Python 3.12, Flask 3, Flask-Login, Gunicorn
- PostgreSQL 16
- SQLAlchemy, Pillow
- Docker & Docker Compose