Cервис преобразования длинных URL в короткие уникальные URL, c возможностью просмотра статистики 🔗.
Реализованы следующие эндпоинты:
- 🧭
GET /{short_id}/- перейти по сокращённой ссылке. Каждый переход учитывается в статистике - 🔗
POST /api/links/- создать короткую ссылку. Можно указать количество секунд, после которых ссылка станет недействительной. Требуется авторизация 🔒 - 📋
GET /api/links/- получить информацию о своих созданных ссылках. Можно отфильтровать неактивные ссылки и с истёкшим сроком действия. Доступна пагинация. Требуется авторизация 🔒 - 📊
GET /api/stats/- получить статистику по своим самым посещаемым ссылкам за последний час, последний день или за всё время. Можно настроить сортировку и количество отображаемых ссылок. Требуется авторизация 🔒 - 📈
GET /api/stats/{short_id}/- получить статистику по конкретной ссылке. Требуется авторизация 🔒 - 💡
GET /health/- проверка работоспособности сервиса
- Python 3.11
- REST API на FastAPI
- PostgreSQL 13 на продакшен-стеке и в Docker Compose
- In-memory SQLite в тестах
- SQLAlchemy для работы с базой
- Alembic для миграций
- Базовая аутентификация (Basic Auth)
- Хранение паролей с bcrypt
- Pytest, TestClient и Monkeypatch для тестирования
- Docker и Docker compose
- Валидация и схема данных
- Pydantic v2 + pydantic-settings (описание входных/выходных JSON-моделей)
- ORM и работа с бд
- SQLAlchemy (Declarative Base +
Mapped/mapped_column)
- SQLAlchemy (Declarative Base +
- Миграции схемы
- Alembic (предусмотрена возможность масштабирования бд без потери существующих данных)
- В Docker при старте контейнера всегда выполняется
alembic upgrade headдля поддержки данных в актуальном состоянии
- Сокращение ссылок
- Генерация случайных коротких идентификаторов (short_id) из букв и цифр
- Проверка уникальности short_id перед сохранением
- Настраиваемое время жизни ссылок (expire_seconds)
- Отслеживание статистики переходов по ссылкам
- Аутентификация
- Базовая аутентификация (Basic Auth)
- Безопасное хранение паролей с использованием bcrypt (через passlib)
- Скрипты для ручного создания пользователей (create_user.py)
- Автоматическое создание дефолтного пользователя при запуске сервиса
- Контейнеризация
- Docker Compose
- Сервис
db(Postgres 13 + volume для персистентности) - Сервис
web(Healthcheckpg_isready, зависимостьdepends_on: condition: service_healthy)
- Сервис
- Скрипт
entrypoint.shдля автоматического запуска миграций, создания пользователя и запуска приложения .envфайл (стандартные переменныеPOSTGRES_USER,POSTGRES_PASSWORD,POSTGRES_DBдля совместимости с Docker-образом Postgres иDEFAULT_USER_USERNAMEиDEFAULT_USER_PASSWORDдля автоматического создания пользователя на старте)
- Docker Compose
- Тестирование
- Покрытие тестами >98% кода
- TestClient (для end-to-end API-тестов без поднятия внешнего сервера)
- In-memory SQLite (лёгкая изолированная бд для ускорения тестов)
- Фикстуры с откатом транзакций (каждая
db_sessionоткатывает изменения после теста, сохраняя чистоту состояния)
- Обработка ошибок
- Явные проверки входных данных
- Глобальный
exception_handlerдля IntegrityError
- Разделение слоёв
routers/- HTTP + валидацияcrud/- операции с бдutils/- утилитарные функции
- User 👨💻 - модель пользователя с хешированными паролями
- Link 🔗 - модель для хранения оригинальных и коротких URL
- Click 👆 - модель для регистрации переходов по ссылкам и сбора статистики
-
Склонируйте репозиторий и перейдите в папку с проектом:
git clone https://github.com/id-andyyy/url-alias-api.git cd url-alias-api -
Создайте файл окружения на основе
.env.example:cp .env.example .env
-
Если необходимо, заполните переменные
DEFAULT_USER_USERNAMEиDEFAULT_USER_USERNAMEв файле.envчтобы при запуске сервера автоматически создавался пользователь. По умолчанию создается пользовательadminс паролемadmin. -
Запустите Docker Compose (не забудьте предварительно запустить Docker daemon):
docker-compose up --build
Дождитесь окончания процесса.
-
Проверьте работоспособность через терминал:
curl http://0.0.0.0:8080/api/health
Предполагаемый ответ:
{"status":"ok"}Или через Swagger UI в браузере:
http://127.0.0.1:8080/docs
-
Если хотите создать ещё одного пользователя:
-
Перейдите в консоль:
docker-compose exec web sh -
В консоли выполните команду (замените
new_userиsecret_passwordна желаемые значения):python3 create_user.py -u new_user -p secret_password
-
Если создание пользователя прошло успешно, после сообщения о несовместимости версий (его можно проигнорировать), вы получите сообщение:
New user created: username='new_user', id=2 -
Для выхода из консоли выполните:
exit
-
-
Создайте и активируйте виртуальное окружение:
python3 -m venv .venv source .venv/bin/activate # На macOS / Linux .\.venv\Scripts\Activate.ps1 # На Windows
-
Установите зависимости
pip install -r requirements.txt
-
Запустите тесты командой:
pytest
alembic/
│ ├── versions/ # Скрипты миграций
│ └── env.py # Конфигурация Alembic
app/
├── api/
│ ├── routes/ # Эндпоинты и их логика
│ └── deps.py # Зависимости FastAPI
├── core/
│ ├── config.py # Чтение .env
├── crud/ # Функции работы с бд
├── db/
│ ├── base.py # Базовый класс для DeclarativeBase
│ └── session.py # Настройка engine и SessionLocal
├── models/ # Декларативные модели
├── schemas/ # Модели запросов и ответов
├── utils/ # Утилитарные функции
└── main.py # Создание приложения
tests/
├── api/ # API-тесты через TestClient
├── crud/ # Unit-тесты CRUD-функций
├── fixtures/ # Дополнительные фикстуры
├── utils/ # Unit-тесты утилитарных функций
└── conftest.py # Общие фикстуры
.dockerignore # Файлы, игнорируемые Docker
.env # Локальные переменные окружения
.env.example # Пример содержимого .env
.gitignore # Файлы, игнорируемые Git
alembic.ini # Конфигурация Alembic
create_default_user.py # Скрипт для автоматического создания пользователя при старте
create_user.py # Скрипт для ручного создания пользователя
docker-compose.yaml # Описание сервисов Docker
Dockerfile # Инструкция сборки Docker-образа
entrypoint.sh # Скрипт запуска контейнера web
requirements.txt # Список зависимостей
Буду признателен, если вы поставите звезду ⭐. Если вы нашли баг или у вас есть предложения по улучшению, используйте раздел Issues.
Read in English 🇬🇧
