Рабочий клиент социальной сети итд.com на itd-api:
лента, профили, посты с комментариями, реакции, репосты, опросы, уведомления, поиск и
хэштеги. Рядом с интерфейсом — панель «под капотом» с журналом вызовов библиотеки.
Проект не является официальным и не аффилирован с итд.com.
npm install
npm run devОткроется http://localhost:3000. Токен не нужен: без него сайт работает на песочнице —
сервере API в памяти из @itd-api/testing.
Для продакшена задайте пароль сессии:
cp .env.example .env # и впишите NUXT_SESSION_PASSWORD (32+ символа)
npm run build && node .output/server/index.mjs| Режим | Данные | Когда |
|---|---|---|
| Песочница | createMockServer() в памяти, своя у каждого посетителя |
пока не введён токен |
| Живой | настоящий API итд.com | после ввода своего access token на /token |
Код серверных роутов у обоих режимов один и тот же — меняется только клиент, который
им отдаёт useItd(). Песочница живёт полчаса после последнего действия, ограничена
пятьюдесятью записями, двумястами комментариями, одним мегабайтом состояния и двумя
тысячами символов в тексте; вложений не поддерживает.
Свежую запись посетителя её жители встречают парой реакций и коротким комментарием — обычными вызовами SDK от своего имени. Это единственное место, где роут делает больше одного вызова, и в журнале видна вся цепочка.
итд.com отвечает на preflight без Access-Control-Allow-Origin, поэтому браузер запрос
отвергает. Вдобавок refresh-токен лежит в HttpOnly-cookie, выставить которую из JS
нельзя. Поэтому ItdClient работает только в Nitro (server/), в режиме
RuntimeMode.Server, — cookie ведёт встроенный jar библиотеки, а браузер ходит в свои
роуты /api/*.
accessToken, refreshToken, cookie API и deviceId не покидают сервер: они лежат в
хранилище Nitro (useStorage('itd-sessions')), а браузер получает лишь непрозрачный
идентификатор в запечатанной httpOnly-cookie itd_example. Запись живёт сутки с
последнего обращения; кнопка «Забыть токен» удаляет её сразу. Сессию на итд.com это не
закрывает — токен остаётся действительным.
Хранилище выбирается по окружению: upstash при заданных NUXT_REDIS_KV_REST_API_URL и
NUXT_REDIS_KV_REST_API_TOKEN, redis при REDIS_URL, иначе память процесса. На serverless
память не годится — инстансы поднимаются и гаснут, посетителя выбрасывало бы произвольно.
NUXT_SESSION_PASSWORD обязателен. Значение по умолчанию лежит в репозитории, поэтому
вне режима разработки приложение с ним падает: иначе cookie любого посетителя подделал бы
кто угодно.
Публичный сайт не должен принимать чужие учётные данные, а капча при входе требует поднять браузер на сервере — на serverless это невозможно. Поэтому в примере остался единственный способ: вставить собственный access token. Вход по email и паролю, код из письма и QR в самом SDK есть — см. руководство по авторизации.
Своего DTO-слоя в примере нет: роут возвращает ровно то, что вернул метод SDK, а страницы
типизируются моделями itd-api (Post, Profile, Page<T>). Снимается единственное
поле — raw с копией исходного ответа: с ней каждый список ехал бы в браузер дважды.
Расшифровку скрытого текста делает сам плагин @itd-api/crypto: он кладёт готовое
представление в decoded, а компоненты читают его через app/utils/decoded.ts. Тексты и
ссылки уведомлений тоже считает библиотека — formatNotificationText и
resolveNotificationUrl.
Каждый роут отдаёт конверт { response, meta }, где meta — журнал вызовов SDK: имя
операции, аргументы, длительность, число попыток, флаг «из кэша» и ошибка. Собирает его
плагин server/utils/inspector.ts на двух уровнях расширений — обёртке логической
операции и перехватчике сетевой попытки. На сервере журнал не хранится: он живёт ровно
столько, сколько выполняется запрос, а дальше — в памяти вкладки, последние 50 записей.
Заголовки для этого не годились: в них нельзя положить не-ASCII, а аргументы бывают русскими.
Файл уходит на сервер сырым телом запроса (имя и тип — заголовками), а оттуда сразу в
itd.files.upload() источником fromStream(): целиком в памяти он не собирается.
Предел размера сторожит сама библиотека через maxBytes, поэтому враньё в Content-Length
ничего не даёт.
Повторы для загрузки отключены ({ retry: false }) осознанно: потоковый источник
открывается заново на каждую попытку, а тело HTTP-запроса читается ровно один раз. При
обрыве или 429 ошибка сразу приходит в интерфейс, и файл выбирают заново.
На Vercel предел свой: тело запроса к функции там не больше 4.5 МБ, и файл крупнее отвергает платформа.
NUXT_REALTIME |
Что происходит |
|---|---|
poll (по умолчанию) |
браузер раз в 30 секунд спрашивает счётчик и подтягивает новое |
sse |
библиотека держит поток на сервере, браузер читает свой text/event-stream |
off |
блок уведомлений скрыт |
Опрос выбран по умолчанию не от бедности: на serverless открытое соединение оплачивается как выполнение и всё равно рвётся по лимиту длительности.
Лимиты итд.com считаются по IP, а все посетители демо приходят с одного адреса. Поэтому
клиенты собраны в ItdAccounts с rateLimitScope: 'shared' — библиотека разводит их
запросы одной очередью, — а ответы читающих операций кэшируются на 30 секунд
(@itd-api/cache). Поймав 429, библиотека сама выждет по лестнице пауз; после неё
ошибка придёт в интерфейс с понятным текстом.
| Файл | Что делает |
|---|---|
| server/utils/itd.ts | клиенты посетителей: ItdAccounts для живого режима, песочница для остальных |
| server/utils/session.ts | cookie с идентификатором и хранилище токенов |
| server/utils/sandbox.ts | сервер API в памяти на сессию: TTL, квоты, вытеснение |
| server/utils/inspector.ts | плагин, собирающий журнал вызовов |
| server/utils/handler.ts | defineItdHandler() — конверт { response, meta } и перевод ошибок |
| server/api/upload.post.ts | потоковая загрузка вложения через fromStream() |
| app/composables/useItdFetch.ts | разворачивает конверт и кормит панель |
| app/composables/useCursorList.ts | бесконечный список: курсор, смещение или номер страницы — одинаково |
| app/components/ItdInspector.vue | сама панель |
| Переменная | Назначение |
|---|---|
NUXT_SESSION_PASSWORD |
шифрование cookie сессии, 32+ символа. Обязательна вне dev |
NUXT_REDIS_KV_REST_API_URL, NUXT_REDIS_KV_REST_API_TOKEN |
хранилище токенов на serverless |
REDIS_URL |
локальный Redis вместо Upstash |
NUXT_REALTIME |
poll, sse или off |
NUXT_PUBLIC_SANDBOX |
пускать ли без токена |
NUXT_PUBLIC_INSPECTOR |
собирать ли журнал вызовов |
NUXT_ITD_BASE_URL |
свой базовый URL API |
NUXT_PROXY_URL |
прокси для локального запуска |
NUXT_STORAGE_DRIVER |
upstash, redis или memory — если не задана, выбирается по остальным переменным |
MIT.