Сервис коротких ссылок с аналитикой — это полноценный проект для тренировки архитектуры, с аутентификацией, доменной логикой, инфраструктурой и статистикой.
Пользователи регистрируются, создают короткие ссылки на свои URL, получают статистику по кликам. Админы видят глобальную аналитику и управляют блоклистом. roadmap Фокус на backend: clean/onion архитектура, SOLID, PostgreSQL + Alembic. pypi Фронтенд: только Swagger + 1 простая HTML-страница для создания ссылки и просмотра своей статистики (fetch + таблица).
- User: id, email, hashed_password, role (user/admin), created_at. roadmap
- Link: id, short_code (уникальный, 6–8 символов), original_url, owner_user_id, custom_slug (опционально), is_active, created_at, expires_at (опционально). roadmap
- Click: id, link_id, ip_address, user_agent, referrer (опционально), country (опционально, заглушка по IP), created_at (timestamp клика). roadmap
- BlockedDomain: domain (для админов), reason, is_active (чтобы блокировать подозрительные ссылки). roadmap
- Генерация short_code: случайная строка (base62), уникальная, проверка коллизий. roadmap
- Кастомный slug: пользователь может задать свой, но уникальный среди его ссылок. roadmap
- Валидация URL: must be valid http/https, не из блоклиста. roadmap
- Статистика: total clicks, unique IPs, топ-страны, график кликов по дням (group by date). roadmap
- Роли: user видит только свои ссылки/статистику; admin — все + блоклист. roadmap
Все защищены JWT кроме /auth и GET /{short_code}.
POST /auth/register— {email, password} → user_id, access_token. projectproPOST /auth/login— {email, password} → access_token. projectproGET /auth/me— текущий user info. projectpro
POST /links— {original_url, custom_slug?, expires_at?} → {short_url, link_id}. roadmapGET /links— ?page, ?limit → список своих ссылок (paginated). roadmapGET /links/{id}— детали ссылки. roadmapDELETE /links/{id}— удалить свою. roadmap
GET /links/{id}/stats— ?from_date, ?to_date → {total_clicks, unique_ips, top_countries: [...], clicks_per_day: [...]}. roadmapGET /links/{id}/clicks— ?page, ?limit → список кликов (paginated). roadmap
GET /{short_code}— redirect to original_url, лог клика (async). roadmap
GET /admin/links— все ссылки (paginated). roadmapGET /admin/stats/global— общая статистика (total_links, total_clicks и т.д.). roadmapPOST /admin/blocked-domains— {domain, reason}. roadmapGET /admin/blocked-domains— список. roadmap
- Entities: User, Link, Click, BlockedDomain (Pydantic BaseModel или dataclasses). github
- Value Objects: ShortCode (с генерацией/валидацией), Url (валидация). pypi
- Domain Services: LinkShortener (генерация кода, проверка уникальности). pypi
- CreateLinkUseCase(user_id, original_url, custom_slug?, expires_at?) → LinkId.
- GetLinkStatsUseCase(link_id, user_id?, from_date?, to_date?) → StatsDto.
- ResolveLinkUseCase(short_code) → original_url + log_click async.
- BlockDomainUseCase(domain, reason) — только admin. github Каждый use case — класс с call, принимает DTO, вызывает репозитории через протоколы. pypi
- Repositories (протоколы): UserRepository, LinkRepository, ClickRepository, BlockedDomainRepository. github
- Реализации: SQLAlchemyRepository (async session).
- External: JWTService, PasswordHasher (bcrypt/argon2), UrlValidator, IpToCountry (заглушка или free API). projectpro
- Кэш: Redis для short_code → link_id (чтобы редирект был быстрым). projectpro
- Dependencies: get_current_user (из JWT). projectpro
- Роутеры: auth_router, links_router, stats_router, admin_router. projectpro
- BackgroundTasks для логирования кликов (не блокировать редирект). roadmap
Таблицы (миграции Alembic):
users: id (uuid), email (unique), hashed_password, role (enum), created_at.
links: id (uuid), short_code (varchar unique), original_url (text), owner_id (fk users), custom_slug, is_active (bool), expires_at (timestamp?), created_at.
clicks: id (uuid), link_id (fk links), ip_address, user_agent (text), referrer (text), country (varchar), created_at (timestamp).
blocked_domains: id (uuid), domain (unique), reason (text), is_active (bool).Индексы: short_code (unique), links.owner_id, clicks.link_id + created_at (для stats), clicks.ip_address (partial для unique).
- index.html: форма логина/регистрации, поле для URL → кнопка “сократить”, таблица своих ссылок + кнопка “статистика”. realpython
- JS: fetch к API, localStorage для токена, простая таблица. Без React/Alpine.
- Всё остальное — Swagger на /docs для теста/админки.
project/
├── app/
│ ├── domain/ # entities, value objects, domain services
│ ├── application/ # use cases
│ ├── infrastructure/ # repositories impl, services (jwt, hasher)
│ ├── presentation/ # routers, dependencies, schemas pydantic
│ ├── core/ # config, exceptions
├── migrations/ # alembic
├── docker-compose.yml # postgres + redis
├── static/ # index.html + style.css
└── main.py
- Docker: postgres, redis, app.
- Env: DATABASE_URL, SECRET_KEY, REDIS_URL. projectpro
- Миграции: alembic upgrade head.
- Тесты: pytest для unit (use cases + mocks), integration (API). github