Активность движка
🏡 KristinaHouse · batume.li#
Региональный портал Батуми — маркет, в котором семейное агентство недвижимости лишь один из
продавцов. Технологии — чтобы доверять прозрачно.
3 секунды — и сайт работает#
Один файл. Одна команда. Никаких npm install-ритуалов, контейнеров и DevOps.
$ bun run server.ts
✓ SQLite готов
✓ TLS: batume.li (let's encrypt)
✓ Telegram-боты слушают
✓ Курсы валют загружены
✓ Сервер слушает :3000
Стек, который удивляет: Bun (JavaScript/TypeScript runtime) — SQLite (WAL mode) — один исполняемый файл server.ts, в котором живёт всё: роутинг, бизнес-логика, API, авторизация, SSR-рендеринг, обработка изображений, аналитика и интеграции.
Архитектура — без единого фреймворка#
Почему это важно для вас:
- Никаких «упал сервер» — нет PostgreSQL, Redis, Docker. Одна сущность — одна точка отказа.
- Мгновенный старт — ~300 мс до первого запроса.
- Резервное копирование — скопировать один файл
kh.sqlite. Всё.
Один код — несколько сайтов (мульти-URN)#
Реестр адресов проекта живёт прямо в этом README: шапка задаёт основной urn, список внизу — все зеркала в порядке приоритета. Сервер при старте сам находит свои ключи let's encrypt и понимает, «кто он». Файл urn-config.json настраивает каждый urn один раз — canonical, акцент-город, разделы, синхронизацию и токены — локально, до деплоя.
3 языка · 3 страны · 0 компромиссов#
Система говорит с гостями на их языке — русский, английский, грузинский — с переключением в один клик. Переводы хранятся в едином словаре, менеджер может править их прямо в панели управления.
| Язык | Интерфейс | Контент | Уведомления |
|---|---|---|---|
| RU | ✅ | ✅ | ✅ |
| EN | ✅ | ✅ | ✅ |
| KA | ✅ | ✅ | ✅ |
Возможности — 25 модулей в одной системе#
| Модуль | Статус | Для кого |
|---|---|---|
| 🛒 Маркет — разделы, лоты, корзина | ☑ Активен | Продавцы + покупатели |
| 🎛 Конструктор — вид всего сайта за одной дверью | ☑ Активен | Хозяева разделов |
| 📃 Страницы из блоков — главная просто одна из них | ☑ Активен | Администратор |
| 📐 Схемы — залы и этажи в метрах, как в Visio | ☑ Активен | Организаторы + продавцы |
| 🗞 Лента — твиты, треды и ролики одним потоком | ☑ Активен | Все |
👤 Личные страницы /~логин с секретными версиями | ☑ Активен | Пользователи |
| 🔐 Документы со сквозным шифрованием | ◧ Модель и ручки | Участники доски |
| 🏠 Недвижимость — раздел, а не подсистема | ☑ Активен | Гости |
| 📅 Брони + календарь цен | ☑ Активен | Гости + продавцы |
| 🗺 Своя карта — тайлы OSM рисуем сами | ☑ Активен | Все |
| 📊 Кабинет продавца: отчёты, лоты, инструменты | ☑ Активен | Продавцы |
| 📝 Блог | ☑ Активен | Читатели |
| 💬 Форум | ☑ Активен | Все пользователи |
| 🎉 Мероприятия + приглашения | ☑ Активен | Организаторы + гости |
| 🤖 Telegram-боты (реестр, мультибот) | ☑ Активен | Гости + продавцы |
| 🧠 ИИ — пул ключей, мультивендор | ☑ Активен | Редакция + боты |
| 🔐 Авторизация | ☑ Активен | Пользователи |
| 💱 Курсы валют | ☑ Активен | Система (авто) |
| 📱 QR-коды лотов | ☑ Активен | Продвижение |
| 🌐 Синхронизация (донор → сателлиты) | ☑ Активен | Администратор |
| 📄 Документы | ☑ Активен | Гости |
| ⚙️ Панель управления | ☑ Активен | Менеджеры |
| 📈 Аналитика посещений | ☑ Активен | Менеджеры |
| ⚖️ Правовой профиль (GE · EU-GDPR · без ПД) | ☑ Активен | Посетители + админ |
| 🍪 Cookie-баннер | ☑ Активен | Посетители (GDPR) |
Каждый раздел выключается одним тумблером в панели — per-urn: донор может держать блог открытым, а сателлит — только читаемым (вход лишь для штата) или вовсе без блога.
Маркет-платформа, где недвижимость — просто раздел#
Сайт — это маркет + блог + форум, а KristinaHouse закреплён продавцом одного раздела в нём.
Недвижимость перестала быть подсистемой: квартира — обычный лот раздела «Недвижимость», а
всё, что раньше было зашито под неё, — календарь занятости, сезоны цен, заявки на бронь,
помещения, адрес — стало блоками конструктора интерфейсов, которые может объявить любой
раздел. Включите блок в «Коттеджах» — и у их продавцов появится тот же календарь; выключите —
и он исчезнет без единой строки кода.
Витрины /property и зеркальных объявлений больше нет — оба сняты сознательно. Адрес лота
теперь ссылка на дом в гео-справочнике, поэтому правка дома один раз исправляет все лоты в
нём. Основная навигация — Маркет · Статьи · Форум; главная собирается из конструктора
блоков, где каждый блок прячет себя сам, если под ним нет данных.
Главная — просто страница. Строка в таблице с флагом, собранная тем же конструктором, что и
любая другая: те же блоки, тот же предпросмотр, та же оболочка. У страницы ровно один
канонический адрес (/p/слаг или /слаг, если забрать верхний уровень; второй отвечает 301), а
состав можно форкнуть на язык — там, где грузинскому гостю нужен другой набор полос, а не
переведённая подпись. Каждая публичная подпись блока — заголовок, подзаголовок, надпись на
кнопке — живёт на трёх языках, поэтому перевод страницы редко означает её форк.
Блоки складываются в плоскости. Полоса идёт во всю ширину, режется на колонки, в колонке —
стопка блоков; ширины выбираются словами («шире слева», «узкая справа»), а не числами. До этого
страница умела только «одно под другим», и каждая новая раскладка стоила отдельного блока в
библиотеке — библиотека росла от вёрстки, а не от новых видов содержимого. Собирается всё в
одном месте: пункт «🎛 Конструктор» стои́т рядом с «Обзором» и перечисляет не подсистемы, а
живые страницы и разделы — главную, «о нас», /estate. Раздел маркета правится как раздел, а
не как шаблон: рядом с ним написано, общий у него вид с соседями или свой, и одной кнопкой вид
можно отделить — дальше правки достанутся только ему.
Отделы доски — тоже данные. /market, /bazaar, /gigs и /jobs — фильтрованные виды над
теми же разделами, и завести свой можно из панели. Закрытой осталась единица показа: отдел
выбирает одну из четырёх, которые умеет рендер (лот · лавка · исполнитель · вакансия), и именно
она решает, в каком блоке главной он появится. Открой обе оси — и конструктор начнёт обещать вид,
которого нет.
Любой продавец — не только магазин сайта — получает ту же глубину: **конструктор
витрины профиля (темы/цвета/блоки, без чужого CSS/JS), корзину и заказы**
(товарный режим с остатками), категории с атрибутами и точечным доступом и
кабинет продавца с мини-учётом уровня «отчёт владельцу»: выручка по периодам,
статистика просмотров, CSV-экспорт и ссылки-отчёты без логина.
Технологии, которые работают на вас#
⚡ Производительность#
| Метрика | Показатель |
|---|---|
| Старт сервера | ~300 мс |
| Загрузка каталога | <100 мс |
| Конвертация фото | ~50 мс/фото |
| Водяной знак | кэшируется |
| Сессия пользователя | 30 дней |
| Лимит POST-запросов | 10/мин на IP |
| Максимум загрузки | до 15 МБ |
| Размер базы | ~2 МБ |
🔒 Безопасность#
| Функция | Метод |
|---|---|
| Аутентификация | bcrypt + httpOnly cookies |
| Утечка данных | API всегда скрывает данные владельца |
| Доступ по ссылке | токен-отчёты (не требуют входа) |
| Защита от XSS | экранирование Unicode в JSON |
| Rate limiting | 10 запросов/мин на публичные POST |
| GEO-блокировка | опционально (по IP/стране) |
| Аналитика | только агрегаты, без хранения IP |
| Документы | сквозное шифрование: сервер видит только шифротекст |
| Секретные версии профиля | адрес после # до сервера не доходит — его нет ни в логах, ни в Referer, ни в индексе |
| Приватность резюме | по умолчанию резюме безымянно; имя открывает сам автор, и больше никто |
| Публичный каталог людей | только по согласию, и проверяется оно ВНУТРИ выборки, а не вокруг неё |
📈 Аналитика — без слежки#
Панель показывает посещения по дням: визиты, уникальные, mobile / desktop, семейства user-agent (iPhone, Android, Windows, macOS, Linux), боты — отдельной строкой. В базе — только суточные счётчики: ни IP-адресов, ни сырых логов.
🤖 Telegram — бронирования без сайта#
Гость пишет в Telegram-бот → видит свободные объекты → бронирует → получает контакты после подтверждения менеджером. Никакого сайта, никакой регистрации — просто чат.
🧠 AI — редактор и автор контента#
Редактор пересылает в Telegram-бот статью → AI проверяет качество, категоризирует (туризм/новости/законы), переписывает, генерирует заголовок, slug и предложения по связанным статьям. Порог качества настраивается (по умолч. 6/10).
✍️ BBCode и markdown — диаграммы, код и формулы#
Статьи, посты форума, объявления и документы используют один безопасный движок разметки (md.js — один и тот же файл на сервере и в браузере, поэтому превью в редакторе и опубликованная страница не могут разойтись). Всё экранируется до разметки: никакого сырого HTML, ссылок javascript: и XSS. Разметка — BBCode ∪ markdown: классика (заголовки, списки, GFM-таблицы, цитаты, картинки) плюс:
- Оформление BBCode —
[b] [i] [u] [s] [color=…] [size=…] [spoiler] [quote=кто] [h1…h4] - Блоки раскладки —
[row]/[col]для колонок и[block bg=… border=… align=…]для оформленного блока; каждое значение проходит whitelist, произвольный CSS на страницу не попадает - Ссылки с иконкой сайта —
[link="адрес" favicon]; иконки идут через собственный кеширующий прокси, браузер читателя не ходит к третьим сторонам - Диаграммы Mermaid — блок
`mermaidпревращается во flowchart, sequence или диаграмму Ганта (securityLevel: strict) - Подсветку кода — блок
`языкполучает highlight.js (тема GitHub) и кнопку «копировать» в один клик - Формулы LaTeX —
…в строке и…блоком, через KaTeX - Слайдеры картинок — короткий список фото становится свайп-галереей с подписями
Тяжёлые рендереры (mermaid, highlight.js, KaTeX) грузятся лениво с CDN и только если на странице реально есть такой блок — обычная текстовая статья не тянет ни одного лишнего байта, а у сервера по-прежнему одна зависимость.
💰 Прозрачная отчётность для собственников#
Каждый владелец квартиры получает ссылку-токен — без логина и пароля видит:
- Все бронирования (даты, суммы, статусы)
- Расходы (коммунальные, уборка, ремонт)
- Расчёт к выплате (схема: % от сделки или фиксированная)
- Каскадный калькулятор (если доход делится между несколькими участниками)
Никаких «потеряли бумажку». Всё онлайн, всё прозрачно.
📱 QR-код на каждом объекте#
У каждого объявления — уникальный QR-код (SVG, уровень коррекции H — 30%, переживёт даже наклейку на центр). Генерируется на сервере за микросекунды. Цвета — из тегов объекта.
💧 Водяной знак — картинкой или текстом#
Фото объектов защищаются на лету: PNG-логотип или текст (размер, жирность, курсив, цвет, шрифт — всё настраивается в панели), плюс опция grayscale — посетители видят чёрно-белые фото, оригиналы доступны только менеджерам объекта. Результат кэшируется.
🔎 SEO — которое видно поисковику, а не только вам#
Карта сайта собирается на каждый запрос и кладёт в индекс сами объекты: лоты, витрины продавцов и их страницы, жилые комплексы, гео-лендинги. Каждая ссылка идёт с hreflang-альтернативами по трём языкам, а зовётся ровно тем адресом, который страница объявляет каноническим, — обход не тратится на редиректы. Спрятали раздел настройкой — его товар уходит из карты вместе с ним; продавец переехал на свой домен — его страницы уходят в его собственную карту, а не дублируются в портальной.
Гео-лендинги — /market/in/грузия/батуми/cat/недвижимость: страница на пересечение места и типа, с перелинковкой между соседними городами. Пары строятся по фактическому наличию объектов: страницы «ни одного объявления» не заводятся вовсе.
🖼 Карточка репоста — рисуется сама#
Ссылка без картинки в ленте выглядит как битая. Если своего фото у страницы нет — у гео-лендинга его не бывает по построению, у нового бренда может не быть, — сервер рисует карточку 1200×630 сам: имя, место, цифра, подпись сайта, свой цвет на каждый вид страницы. Ноль внешних запросов и ноль дополнительных зависимостей: SVG собирается строкой, растрирует его тот же sharp, что делает водяные знаки. Переименовали организацию — картинка перерисовывается, ссылка остаётся прежней.
💱 Курсы валют — всегда актуальные#
4 раза в сутки (00/06/12/18) сервер обновляет курсы GEL/USD/RUB/EUR. Если API недоступен — продолжают работать старые курсы. Цены в объявлениях всегда корректны, даже при скачках валют.
Синхронизация — двухинстансная архитектура#
Данные разделены на два уровня: Donor (полные данные, включая персональные) и Satellite (только публичный контент, без данных владельцев и учётных записей гостей). Позволяет соблюдать требования к хранению данных в разных юрисдикциях. Сателлит делится ссылками на себя; канонический адрес живёт только в meta-тегах для поисковиков.
Стек — почему это high-tech#
| Компонент | Технология | Почему |
|---|---|---|
| Язык | TypeScript | Типобезопасность без оверхеда |
| Runtime | Bun | В 4× быстрее Node.js, встроенный SQLite, тестер, bundler |
| База данных | SQLite WAL | Ноль администрирования, ~2 МБ, 100% ACID |
| Фреймворк | ❌ нет | Собственный SSR — полный контроль, нулевой bloat |
| Зависимости | 1 (sharp) | Минимальная поверхность атаки |
| Обработка фото | sharp + mozjpeg | JPEG 85%, водяной знак (PNG/текст/ч-б), EXIF-ориентация |
| QR | Собственный GF(256) | Reed-Solomon level H, без внешних API |
| AI | OpenAI-совместимый | Любой провайдер (OpenAI, Anthropic, локальный Ollama) |
| Telegram | Long-polling (2 бота) | Без webhook, ничего не торчит наружу |
| TLS | Let's Encrypt | Автовыбор сертификата по реестру urn из README |
| Мульти-сайт | urn-config.json | Один код+БД → несколько доменов, настройка один раз |
| Резервное копирование | cp kh.sqlite backup/ | Одна команда |
Цифры, которые говорят сами за себя#
| Метрика | Показатель | |
|---|---|---|
| 📊 | Строк TypeScript | ~42 000 — 50 исходных файлов |
| 🧪 | Тестов | 54 файла — плюс 6 сквозных приёмок на живом сервере |
| 📦 | Зависимостей | 1 — sharp (остальное — своё) |
| 🗄️ | База данных | ~2 МБ — SQLite, без сервера |
| 🚀 | Старт | ~300 мс — от команды до запроса |
| 🌍 | Языков | 3 — RU, EN, KA |
| 🗺 | Тайлы карты | рисуем сами — 0 внешних запросов |
| 📸 | Лимит фото | 15 МБ на загрузку |
| 🔄 | Обновление курсов | 4 раза в сутки |
| 🤖 | Telegram-ботов | реестр — несколько на тип и на домен |
| 🧠 | Порог AI | 6/10 — настраиваемый |
| ⏱️ | Сессия | 30 дней — без перелогинов |
| 🛡️ | Коррекция QR | 30% — уровень H (Reed-Solomon) |
UX/CX — почему гости возвращаются#
| Сценарий | Впечатление |
|---|---|
| Нашёл квартиру за 5 сек | Фильтры: тип, цена, даты, комнаты |
| Листает фото на телефоне | Свайпы не срабатывают от скролла — только явный жест |
| Увидел реальные фото | Водяной знак — защита, не мешает |
| Забронировал в Telegram | Без регистрации на сайте |
| Получил контакт | После подтверждения менеджером |
| Собственник: отчёт | По ссылке, без входа |
| Читает статьи о Батуми | AI-качество, 3 языка |
| Покупает/продаёт | Маркетплейс с FTS5-поиском |
| Общается на форуме | Анонимно (с TTL), без страха |
| Переключает язык | 1 клик — RU/EN/KA |
Customer Experience (CX) — это когда система не мешает, а помогает. KristinaHouse спроектирована так, чтобы гость тратил время на выбор квартиры, а не на разбирательство с сайтом.
Для владельца бизнеса#
KristinaHouse — это не CRM, не WordPress, не конструктор. Это собственная высокопроизводительная платформа, написанная специально под модель семейного агентства в Батуми.
- Нет ежемесячных платежей — свой сервер Contabo, своя кодовая база.
- Нет лимитов SaaS — сколько угодно объектов, пользователей, бронирований.
- Полный контроль данных — база SQLite лежит у вас на сервере.
- Единая точка входа — сайт + Telegram + AI + отчётность в одной системе.
- GDPR-ready — двухинстансная архитектура, cookie-баннер, аналитика без хранения IP.
Источники данных#
Гео-справочник портала — страна, край, город, улица, дом — наполняется из OpenStreetMap
через Overpass API. Импортированные строки помечены происхождением: правка человеком выводит
строку из-под будущих прогонов, и её исправление живёт вечно.
Оттуда же приезжает геометрия для карты — и подложку сайт рисует сам, своим рендером из
собственной базы контуров. Ни одного обращения к чужому тайл-серверу при показе карты не
происходит: карта работает и без интернета.
Данные © OpenStreetMap contributors, лицензия ODbL.
Оформление карты — собственное.
KristinaHouse — Batumi, Georgia.
batume.li · batumi.su
«Мы сами здесь живём, лично проверяем каждую квартиру и ведём прозрачную отчётность для собственников.»
