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/groups Группы: квота диска, лимиты папок и фото
/admin/banners Рекламные баннеры на сайте
/admin/photos Все фото на сервере

Права доступа:

  • Загрузка фото — только авторизованным пользователям
  • Удаление фото — владелец или администратор
  • Админка — только пользователи с is_admin=True

Релиз v1.4

Лимиты групп пользователей

  • В /admin/groups администратор задаёт для каждой группы:
    • Квота диска (МБ, 0 = без лимита)
    • Максимум папок на пользователя (0 = без лимита)
    • Максимум фото на пользователя (0 = без лимита)
  • Лимиты проверяются при создании папки и загрузке фото
  • В личном кабинете отображается использование квот

Переменные .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 на сервере:

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

Резервное копирование

База данных

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/ Админ-панель
GET /admin/banners Управление рекламными баннерами
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
S
Description
No description provided
Readme 361 KiB
Languages
Python 47.6%
HTML 36.8%
CSS 10.5%
JavaScript 4.6%
Dockerfile 0.3%
Other 0.2%