Backend-клон социальной сети ИТД (итд.com). Эндпоинты, модели ответов и
формат ошибок повторяют контракт, который ожидает itd-sdk
(и официальный веб-клиент), поэтому SDK работает с этим сервером без изменений — достаточно
указать ITDConfig(url='http://localhost:3000/api').
Стек: Bun · ElysiaJS (TypeScript) · TypeBox · PostgreSQL + Drizzle ORM · Redis (ioredis) ·
Swagger/OpenAPI (@elysiajs/swagger) · uuid (v7) · bcrypt.
JWT_SECRET=$(openssl rand -hex 32) ADMIN_EMAIL=admin@example.com ADMIN_PASSWORD=change-me-please docker compose up --buildAPI: http://localhost:3000/api, документация: http://localhost:3000/swagger.
Контейнер сам применяет миграции и сид (пины, версии приложений, ченджлог, приветственное
объявление, админ из ADMIN_EMAIL/ADMIN_PASSWORD).
Нужны Bun ≥ 1.3, PostgreSQL ≥ 14 (с расширением pg_trgm) и Redis ≥ 6.
bun install
cp .env.example .env # поправьте DATABASE_URL / REDIS_URL
bun run db:migrate
bun run db:seed
bun run dev # http://localhost:3000В разработке письма не отправляются: одноразовые коды пишутся в лог, а при DEV_EXPOSE_OTP=true
ещё и возвращаются в ответе (otp). Чтобы регистрироваться без кода, выставьте
EMAIL_VERIFICATION=false.
from itd import ITDConfig, init_client, Me, Post
from itd.core.auth import CredentialsAuth
client = init_client('local', config=ITDConfig(url='http://localhost:3000/api'),
auth=CredentialsAuth('me@example.com', 'my-password-123', 'captcha-not-required'))
print(Me().username)
Post.new('Привет из SDK #итд').like()| Область | Что реализовано |
|---|---|
| Аутентификация | регистрация с подтверждением email кодом, вход, refresh_token в HttpOnly-cookie с ротацией и grace-окном, JWT access на 15 минут (sid, sub, roles, iss: auth-service), выход, выход везде, смена/сброс пароля, вход по QR (подтверждение только с мобильной сессии), список и отзыв сессий с разбором устройства |
| Профили | создание профиля после регистрации, emoji-аватар = клан, баннер, био, приватность (закрытый аккаунт, кто пишет на стене, кто видит лайки, last seen), онлайн/последний визит, пины (значки), удаление аккаунта с восстановлением в течение 30 дней |
| Социальный граф | подписки (для закрытых аккаунтов — заявки с принятием/отклонением), блокировки, подписчики/подписки с пагинацией, статус подписки пачкой, топ кланов, рекомендации «кого читать» |
| Посты | текст со спанами (bold/italic/…/link), автоматические хэштеги и упоминания, вложения, опросы, посты на чужой стене, репосты с комментарием, закреп, редактирование 48 часов, удаление с восстановлением, лайки, «доминирующее эмодзи» (клан, который чаще лайкал пост) |
| Ленты | популярное (рейтинг + снапшоты в Redis для стабильной пагинации), подписки, клан; стена пользователя с закреплённым постом; лайкнутые посты (с учётом приватности); посты по хэштегу |
| Комментарии | комментарии и ответы (один уровень), превью ответов, сортировки, лайки, редактирование, удаление автором/владельцем поста с восстановлением |
| Уведомления | 12 типов SDK, дедупликация, настройки (старый и новый формат), прочтение, счётчик, SSE-стрим через Redis pub/sub (работает при нескольких инстансах) |
| Прочее | загрузка файлов (проверка типа по сигнатуре, размеры изображений, local/S3), поиск пользователей и хэштегов (pg_trgm), жалобы, верификация, подписка «НУКСТА» с тестовой оплатой и автопродлением, просмотры/взаимодействия (/v1/i, /v1/x, в т.ч. deflate), платформа (версии, ченджлог, объявления), сезонный ивент (портал, профили, магазин, кликухи, красные ручки/корректоры), админка |
- базовый путь
/api, те же методы и URL (posts/{id}/like,v1/auth/refresh,users/me/pins, …); - формат ошибок
{"error": {"code", "message"}}с теми же кодами и текстами, по которым SDK выбирает исключения (USERNAME_TAKEN,EDIT_WINDOW_EXPIRED,NOT_PINNED, «Вы уже отправляли жалобу на этот контент», …); ошибки токена — в «старом» формате{"error": "token expired"}; - ошибки валидации —
422+VALIDATION_ERROR(плюсerrors/violationsпо полям); - лимиты отдают
x-ratelimit-limit/x-ratelimit-remaining, превышение —{"error": "Too Many Requests"}илиRATE_LIMIT_EXCEEDEDсretryAfter; - в каждом посте есть
vs-токен для отчётов о просмотрах.
Проверено прогоном самого SDK (tests/sdk, 96 проверок: модели, списки, SSE, QR-вход, оплата,
все основные классы исключений):
EMAIL_VERIFICATION=false bun run dev &
ITD_API=http://localhost:3000/api bun run test:sdksrc/
index.ts запуск сервера, фоновые задачи, graceful shutdown
app.ts сборка Elysia-приложения (swagger, cors, лимиты, модули под /api)
config.ts конфигурация из переменных окружения
db/ схема Drizzle, клиент, миграции, сид
redis.ts клиенты Redis (команды + отдельное соединение для pub/sub)
plugins/ обработчик ошибок, контекст запроса, auth-макросы, лимиты по IP
services/ общая логика: пользователи и отношения, рендер постов и комментариев,
уведомления, realtime (pub/sub), кэш статистики, ленты, присутствие
modules/<домен>/ маршруты + сервис домена: auth, users, posts, comments, hashtags,
search, files, notifications, reports, platform, subscription,
verification, dwell, event, admin, uploads
schemas/ TypeBox-модели ответов (они же — схемы OpenAPI)
jobs/ периодические задачи с распределённой блокировкой
drizzle/ SQL-миграции
tests/ bun:test (интеграционные, на реальных PostgreSQL и Redis) + tests/sdk
Маршруты объявляют требования к авторизации макросами: account: true (валидный токен),
user: true (есть активный профиль), optionalUser: true, admin: true. Ответы описаны
TypeBox-моделями: они валидируют и нормализуют выдачу и формируют Swagger.
| Ключи | Назначение |
|---|---|
rl:ip:*, rl:act:* |
token bucket по IP для эндпоинтов и скользящие окна (минута/час) для действий: лайки 30/200, комментарии 5/75, подписки 5/20, репосты 5/25, посты 5/25 |
ps:{postId} |
кэш счётчиков постов для POST /posts/stats (клиенты опрашивают его каждые несколько секунд); записи обновляются атомарно через Lua |
feed:popular:* |
версионированные снапшоты популярной ленты (курсор <версия>.<смещение>) |
sess:revoked:*, sess:grace:* |
отозванные сессии (access-токены перестают работать сразу), grace-окно ротации refresh-токена |
flow:*, qr:* |
одноразовые коды (хранятся только хэши) и состояния QR-входа |
ch:user:*, ch:qr:* |
pub/sub каналы для SSE уведомлений и статусов QR |
lastseen:*, online:* |
присутствие: последний визит и открытые SSE-соединения |
cache:*, lock:* |
кэш трендов и кланов, блокировки фоновых задач |
Полный список с комментариями — в .env.example. Главное:
| Переменная | По умолчанию | Описание |
|---|---|---|
DATABASE_URL |
postgres://itd:itd@localhost:5432/itd |
PostgreSQL |
REDIS_URL |
redis://localhost:6379 |
Redis |
JWT_SECRET |
— | обязателен при NODE_ENV=production |
PUBLIC_URL |
http://localhost:3000 |
база для ссылок на файлы, QR и оплату |
EMAIL_VERIFICATION |
true |
регистрация с подтверждением кодом |
COOKIE_SECURE |
true в production |
флаг Secure для refresh_token |
CORS_ORIGINS |
— | разрешённые origin (в production без него CORS выключен) |
TRUST_PROXY |
false |
брать IP/страну из заголовков прокси |
STORAGE_DRIVER |
local |
local (раздача из /uploads) или s3 |
RATE_LIMIT_MULTIPLIER |
1 |
масштаб всех лимитов |
TURNSTILE_SECRET |
— | включает проверку Cloudflare Turnstile |
EVENT_ENABLED |
false |
сезонный ивент |
createdb itd_test # имя базы обязано заканчиваться на _test: тесты очищают таблицы
bun test # настройки берутся из .env.test71 интеграционный тест на настоящих PostgreSQL и Redis: OTP-регистрация, ротация и отзыв токенов, сессии, QR-вход, профили и приватность, подписки и заявки, блокировки, посты, ленты и курсоры, закрепы, лайки и доминирующее эмодзи, репосты, редактирование и восстановление со счётчиками, опросы, комментарии, уведомления и SSE, файлы, жалобы, оплата и автопродление, верификация, просмотры (в т.ч. сжатые deflate), лимиты, баны, очистка аккаунтов, ивент.
- письма (коды подтверждения) пишутся в лог — для продакшена подключите транспорт в
src/lib/mailer.ts; - оплата «НУКСТА» тестовая: ссылка
confirmationUrlведёт на страницу-заглушку, которая подтверждает платёж; - модерации изображений нет; запрещённые слова — из
BANNED_WORDSи таблицыbanned_words(админка); - геолокация по IP — только код страны из
CF-IPCountryприTRUST_PROXY=true; - механика сезонного ивента воспроизведена в упрощённом, но согласованном виде (баланс, магазин, стикеры, шторы, окна, кликухи, красные ручки и корректоры).