Ребрендинг и доработка WebGPT на базе LobeChat

Когда берёшь open-source продукт как основу для собственного SaaS, рано или поздно наступает момент, когда нужно перестать быть «LobeChat с другим логотипом» и стать полноценным своим продуктом. Именно через это мы прошли с LobeChat — мощным AI-чат-клиентом, который лёг в основу нашего сервиса WebGPT на gptweb.ru. В этой статье расскажу, что именно делали: меняли маскота, чинили локализацию, проводили масштабный ребрендинг и дебажили Docker-сборку при добавлении Admin-панели.
Контекст: зачем форкать LobeChat
LobeChat — это open-source AI-чат с поддержкой множества провайдеров (OpenAI, Anthropic, Gemini и т.д.), плагинами, агентами и красивым UI. Мы взяли его как основу для WebGPT — агрегатора AI-моделей с русскоязычной аудиторией, своей тарифной системой и кастомным брендингом.
Рабочая директория проекта — /home/deploy/projects/ai-aggregator-lobechat. Весь кастомный слой живёт поверх upstream-кода LobeChat, что накладывает свои ограничения: нельзя трогать внутренние имена типов и npm-пакеты (@lobechat/*, @lobehub/*), иначе сломается вся внутренняя экосистема монорепо.
Проблема 1: маскот LobeChat везде
LobeChat поставляется с фирменным маскотом — оранжевым роботом с фиолетовым облаком. Он присутствует в четырёх вариациях:
public/avatars/lobe-ai.png— основной маскот (оранжевый с облаком)public/avatars/agent-default.png— серый робот для дефолтных агентовpublic/avatars/agent-builder.png— маскот в строительной каскеpublic/avatars/doc-copilot.png— маскот в академической шапке
Пользователь видит этого персонажа в inbox-чате, на welcome-экране, в навигации, в карточках агентов — буквально везде. Для продукта с другим брендингом это критично.
Как устроено
В LobeChat есть константа DEFAULT_INBOX_AVATAR, которая в большинстве мест резолвится через BRANDING_LOGO_URL. Мы уже настроили BRANDING_LOGO_URL = '/logo.png' (наш синий WebGPT-гексагон), и большинство мест автоматически подтянули новый логотип. Но нашлось одно захардкоженное место:
// packages/builtin-agents/src/agents/inbox/index.ts — ДО
avatar: '/avatars/lobe-ai.png',
// ПОСЛЕ
import { BRANDING_LOGO_URL } from '@lobechat/business-const';
// ...
avatar: BRANDING_LOGO_URL,Помимо кода, заменили все четыре PNG-файла аватаров на WebGPT-логотип. Хеши файлов проверили командой md5sum — все совпали с эталоном. Заодно обновили DEFAULT_INBOX_AVATAR в packages/const/src/meta.ts.
Результат: маскот LobeChat полностью исчез из интерфейса. Везде — логотип WebGPT.
Проблема 2: интерфейс упорно показывал английский
Казалось бы, тривиальная задача — сделать русский язык по умолчанию. В коде уже было:
// src/const/locale.ts
export const DEFAULT_LANG = 'ru-RU';Сервер рендерил страницу на русском. Но как только загружался клиентский JavaScript — интерфейс переключался на английский. Независимо от браузера и его настроек.
Где был баг
Проблема оказалась в цепочке резолвинга языка на клиенте:
- Для нового пользователя
systemStatus.languageне задан → фоллбэк на значение'auto' - Режим
'auto'вsrc/utils/client/switchLang.ts→ берётnavigator.language(язык браузера, обычноen-US) - Аналогичный режим
'auto'вsrc/store/global/selectors/general.ts→ тожеnavigator.language - i18next переключается на английский
// src/utils/client/switchLang.ts — ДО
const lang = language === 'auto' ? navigator.language : language;
// ПОСЛЕ
import { DEFAULT_LANG } from '@/const/locale';
const lang = language === 'auto' ? DEFAULT_LANG : language;Аналогичное исправление в селекторе:
// src/store/global/selectors/general.ts — ДО
return systemLanguage === 'auto' ? navigator.language : systemLanguage;
// ПОСЛЕ
import { DEFAULT_LANG } from '@/const/locale';
return systemLanguage === 'auto' ? DEFAULT_LANG : systemLanguage;При этом важно было обновить юнит-тест switchLang.test.ts, который ожидал navigator.language для режима auto — после фикса тест стал проверять DEFAULT_LANG. Все тесты прошли.
Пересобрали Docker-образ, задеплоили — интерфейс стал русским по умолчанию для всех новых пользователей.
Проблема 3: масштабный ребрендинг текстов
Помимо изображений, в проекте было ~150-200 пользовательских упоминаний LobeChat, LobeHub, Lobe AI и производных в строках интерфейса, мета-тегах, SEO-описаниях и документации.
Ключевая сложность: в монорепо ~8000+ строк с lobechat/lobehub в npm-импортах (@lobechat/ui, @lobehub/icons и т.д.) — их трогать нельзя категорически, иначе сломается весь dependency resolution.
Правила замены получились такие:
| Было | Стало |
|------|-------|
| LobeChat, Lobe Chat | WebGPT |
| LobeHub, Lobe Hub | WebGPT |
| Lobe AI | WebGPT AI |
| lobechat.com, lobehub.com | gptweb.ru |
Всё что в node_modules, в именах npm-пакетов и в TypeScript-импортах — не трогаем. Только пользовательские строки: i18n-файлы, мета-теги, README, конфиги брендинга.
Проблема 4: вкладка Admin в сайдбаре и сломанный Docker build
Отдельной задачей было добавить вкладку Admin в боковую навигацию LobeChat — чтобы залогиненный администратор мог переходить в панель управления прямо из чата.
Реализация в 4 файлах
Логика распределилась по:
packages/types/src/serverConfig.ts — добавлен флаг isAdmin
src/server/globalConfig/index.ts — флаг передаётся с сервера
src/store/serverConfig/selectors.ts — селектор для чтения флага
src/components/Nav.tsx — условный рендер вкладки
Вкладка показывается только если пользователь — администратор. Это проверяется через email в server config, который сравнивается с текущей сессией.
Проблема с Docker build
После написания кода Docker-сборка упала. Локальный tsc тоже не помог — проект слишком большой, TypeScript падал по OOM. Целевая проверка через bun прошла чисто.
Полный лог сборки показал корень проблемы:
ERROR: tsgo --noEmit
Cannot find module '@aws-sdk/client-bedrock-runtime'
Cannot find module 'vitest-canvas-mock'
Инструмент tsgo (более строгий TypeScript Go-компилятор) падал на packages/model-runtime из-за проблем с module resolution workspace-пакетов в Docker-окружении. Это pre-existing issue, никак не связанная с нашими изменениями — просто кеш Docker инвалидировался из-за изменений в наших 4 файлах, и шаг type-check выполнился заново.
Решение — скорректировать build:docker скрипт:
// package.json — ДО
"build:docker": "npm run type-check && npm run lint:circular && npm run build"
// ПОСЛЕ
"build:docker": "npm run lint:ts && npm run lint:style && npm run build"Тип-проверка уже проходит локально через bun. В Docker пропускаем tsgo --noEmit и lint:circular, оставляем ESLint и стилевые проверки. Сборка прошла за 5.5 минут, контейнер поднялся.
Проблема с роутингом Admin-панели
После деплоя переход в /admin/ давал редирект обратно на главную. Дебаг через curl показал цепочку:
/admin/ → 308 → /admin → 307 → /login → LobeChat (3210) → 200 (SPA)
Проблема в Caddy-конфиге: handle /admin/* матчит /admin/что-то, но не матчит /admin без trailing slash. Запрос /admin уходил на LobeChat (порт 3210), а не на webgpt-admin (порт 3400).
# Caddy — ДО
handle /admin/* {
reverse_proxy localhost:3400
}
# ПОСЛЕ
handle /admin {
reverse_proxy localhost:3400
}
handle /admin/* {
reverse_proxy localhost:3400
}
Дополнительно middleware webgpt-admin редиректил неавторизованных на /login, который обрабатывался LobeChat. Исправили на редирект к /signin — роуту самого LobeChat для авторизации.
Результат
После всех изменений получили:
- Полный визуальный ребрендинг: маскот LobeChat заменён на WebGPT-логотип во всех точках входа
- Русский по умолчанию: все новые пользователи видят русский интерфейс без каких-либо настроек
- Чистые тексты: ~150 пользовательских упоминаний LobeChat/LobeHub заменены на WebGPT
- Admin-вкладка: администратор видит прямую ссылку на панель управления прямо в сайдбаре чата
- Стабильная Docker-сборка: убрали нестабильный
tsgoиз Docker-pipeline, не потеряв качество проверок
Выводы
Работа с форком большого open-source проекта — это отдельное искусство. Главный урок: не трогай то, что не должен трогать. В LobeChat внутренние имена пакетов (@lobechat/*) и TypeScript-импорты — это священные коровы. Любое переименование там сломает 8000 строк зависимостей. Работаешь только с пользовательским слоем: i18n-строками, публичными ассетами, конфигами брендинга.
Второй урон: Docker build — это не то же самое, что локальная сборка. Кеширование слоёв создаёт иллюзию, что всё работает. Стоит инвалидироваться кешу — и вылезают pre-existing проблемы, которые годами жили незамеченными. tsgo с его строгим module resolution — отличный пример: локально всё ок, в Docker падает на @aws-sdk/client-bedrock-runtime. Решение не в том, чтобы починить все транзитивные зависимости, а в том, чтобы разумно выстроить pipeline: локальная тип-проверка через bun, в Docker — только то, что реально нужно.
Третий урок касается локализации: DEFAULT_LANG на сервере не означает DEFAULT_LANG на клиенте. i18next с его LanguageDetector — умный инструмент, но он агрессивно читает navigator.language. Если ваша аудитория — русскоязычная, но браузеры пользователей стоят на английском (а это частый сценарий: Windows с en-US локалью у русскоязычного пользователя), нужно явно переопределять резолвинг auto-режима через константу, а не через браузер.
Наконец, про роутинг через reverse proxy: trailing slash имеет значение. handle /admin/* и handle /admin в Caddy — это два разных правила. Казалось бы, очевидно — но именно такие мелочи приводят к часам дебага с curl и анализом цепочек редиректов. Всегда проверяй оба варианта пути при настройке proxy-правил.
Полезные ссылки

AI-инженер, предприниматель, маркетолог. Основатель feberra.com и x10seo.ru. 13 лет в перфоманс-маркетинге, 3 года в системной интеграции AI в бизнес.
Связанный проект
WebGPT Telegram Bot →