Skip to main content
Glama

GlassCloud

Облачный сервер для Rokid Bridge и GlassBridge — управляет привязкой устройств, ретрансляцией инструментов MCP, хранением изображений с галереей и описанием сцен с помощью LLM на стороне сервера.

Что он делает

GlassCloud — это бэкенд для приложений умных очков Rokid. Он предоставляет:

  1. Привязка устройств — сопряжение между веб-консолью и Android-приложением с помощью QR-кода или токена.

  2. Ретрансляция инструментов MCP — проксирование вызовов инструментов Gmail и Календаря с очков к API Google через WebSocket.

  3. Галерея изображений — получение изображений сцен, загруженных с очков, их хранение с метаданными и отображение в веб-галерее.

  4. LLM на стороне сервера — если изображение поступает без описания, автоматически вызывает Gemini или ChatGPT для его генерации, а затем возвращает его в приложение для отображения на очках.

  5. Публичные ссылки на галерею — персональные токены для публичного доступа, позволяющие просматривать галереи без входа в систему.

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│  Rokid Glasses  │────▶│   GlassCloud    │────▶│  Google APIs    │
│  + Android App  │ WS  │  (This Server)  │     │  Gmail/Calendar │
└─────────────────┘     └─────────────────┘     └─────────────────┘
                               │  │
                    REST API   │  │  Server-side LLM
                               ▼  ▼
                        ┌─────────────────┐
                        │  Web Console    │
                        │  Gallery · LLM  │
                        │  Settings · QR  │
                        └─────────────────┘

Быстрый старт

# Install dependencies
npm install

# Copy and fill in environment variables
cp .env.example .env
# Edit .env — see Configuration section below

# Development (auto-reload)
npm run dev

# Production build + run
npm run build
npm start

Откройте http://localhost:3000/console для доступа к веб-консоли.


Конфигурация

Все переменные окружения проверяются при запуске с помощью Zod. Сервер выведет список отсутствующих полей и завершит работу, если требуемые переменные не заданы.

Генерация секретов

node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"

Запустите это дважды — один раз для JWT_SECRET, один раз для ENCRYPTION_KEY.

Обязательные переменные

Переменная

Описание

JWT_SECRET

Подписывает JWT устройств. Мин. 32 символа. Должен быть случайным и секретным.

ENCRYPTION_KEY

Шифрует токены OAuth и ключи API LLM в состоянии покоя (AES-256-GCM). Мин. 32 символа.

GOOGLE_CLIENT_ID

Идентификатор клиента Google OAuth 2.0

GOOGLE_CLIENT_SECRET

Секрет клиента Google OAuth 2.0

GOOGLE_CALLBACK_URL

URI перенаправления OAuth — должен совпадать с тем, что зарегистрирован в Google Cloud Console (например, https://yourserver.com/auth/google/callback)

Дополнительные переменные

Переменная

По умолчанию

Описание

PORT

3000

Порт HTTP-сервера

NODE_ENV

development

development

production

test

LOG_LEVEL

info

trace

debug

info

warn

error

fatal

DATABASE_PATH

./data/glasscloud.db

Путь к файлу базы данных SQLite

CORS_ORIGINS

http://localhost:3000

Список разрешенных источников CORS через запятую

RATE_LIMIT_WINDOW_MS

60000

Окно ограничения частоты запросов в миллисекундах

RATE_LIMIT_MAX_REQUESTS

60

Макс. количество запросов за окно на IP (применяется к /api/*)

WS_PING_INTERVAL_MS

30000

Интервал пинга для поддержания соединения WebSocket

WS_PONG_TIMEOUT_MS

10000

Время ожидания ответа pong перед закрытием соединения

Пример .env

PORT=3000
NODE_ENV=production
LOG_LEVEL=info
DATABASE_PATH=./data/glasscloud.db

JWT_SECRET=<generate with command above>
ENCRYPTION_KEY=<generate with command above>

CORS_ORIGINS=https://yourserver.com

GOOGLE_CLIENT_ID=123456789.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-xxxxx
GOOGLE_CALLBACK_URL=https://yourserver.com/auth/google/callback

Настройка Google OAuth

  1. Перейдите в Google Cloud Console → APIs & Credentials

  2. Создайте OAuth 2.0 Client ID (Веб-приложение)

  3. Добавьте ваш URL обратного вызова в Authorized redirect URIs: https://yourserver.com/auth/google/callback

  4. Включите Gmail API и Google Calendar API в проекте

  5. Добавьте тестовых пользователей в разделе OAuth consent screen → Test users (требуется, пока приложение находится в режиме тестирования)

Примечание о областях доступа Google: Это приложение запрашивает gmail.readonly и calendar.events. Для публичного развертывания с более чем 100 пользователями Google требует прохождения оценки безопасности CASA. Для частного или тестового использования оставьте приложение в режиме тестирования.


Настройки LLM (Описание сцены на стороне сервера)

GlassCloud может автоматически генерировать описания сцен для изображений, загруженных без них (например, когда приложение Rokid находится в режиме "Bypass LLM"). Сгенерированное описание:

  • Сохраняется в базу данных и отображается в галерее

  • Возвращается в ответе API загрузки, чтобы приложение могло немедленно отобразить его на очках

Конфигурация

  1. Войдите в /console и нажмите LLM Settings → в заголовке

  2. Выберите провайдера (Gemini или ChatGPT) и вставьте свой API-ключ

  3. При необходимости добавьте пользовательские промпты и выберите один в качестве активного

Промпт по умолчанию: "Describe the scene in this image concisely."

API-ключи

Провайдер

Где получить

Gemini

Google AI Studio → API keys

ChatGPT

OpenAI Platform → API keys

API-ключи шифруются в состоянии покоя с помощью ENCRYPTION_KEY (AES-256-GCM). Если API-ключ не настроен, LLM на стороне сервера молча пропускается, и изображения с пустыми описаниями сохраняются как есть.


Страницы веб-консоли

URL

Описание

/console

Панель управления: статистика, привязка устройств, управление сервисами MCP

/console/link

Генерация QR-кода / токена связи для сопряжения устройства

/console/gallery

Галерея изображений (табличный вид с миниатюрами, деталями, удалением)

/console/gallery/photo/:id

Полноразмерное изображение с метаданными

/console/settings/llm

Управление провайдером LLM, API-ключом и промптами

/public/gallery/:token

Публичная галерея (вход не требуется)

/public/gallery/:token/photo/:id

Детали публичного фото


Конечные точки REST API

Устройство и аутентификация

Конечная точка

Метод

Авторизация

Описание

/health

GET

Нет

Проверка работоспособности со статистикой соединений

/auth/google

GET

Нет

Инициация потока Google OAuth

/auth/google/callback

GET

Нет

Обратный вызов OAuth

/api/link/exchange

POST

Нет

Обмен токена связи на JWT устройства

/api/devices

GET

JWT устройства

Список привязанных устройств

/api/devices/:id

DELETE

Сессия

Отвязка устройства

Изображения

Конечная точка

Метод

Авторизация

Описание

/api/images/upload

POST

JWT устройства

Загрузка изображения + метаданные (multipart/form-data)

/api/images/:id

GET

JWT устройства

Получение метаданных изображения по ID

Поля загрузки

Поле

Тип

Обязательно

Описание

image

файл

Да

JPEG, PNG или WebP — макс. 20 МБ

scene_description

строка

Да

Описание сцены (может быть пустой строкой)

device_model

строка

Да

Название модели устройства

device_instance_id

строка

Да

Уникальный идентификатор устройства

captured_at

строка

Нет

Временная метка ISO 8601, когда было сделано изображение

image_width

число

Нет

Ширина изображения в пикселях

image_height

число

Нет

Высота изображения в пикселях

llm_provider

строка

Нет

LLM, используемая на устройстве (например, Gemini, ChatGPT, None)

Если scene_description пуст и настроена LLM на стороне сервера, сервер вызовет LLM и включит llmDescription в ответ:

{
  "imageId": "uuid",
  "originalFilename": "uuid.jpg",
  "thumbnailFilename": "uuid_thumb.jpg",
  "receivedAt": 1714000000000,
  "capturedAt": 1714000000000,
  "llmDescription": "A person walking through a crowded market..."
}

MCP

Конечная точка

Метод

Авторизация

Описание

/api/mcp/services

GET

JWT устройства

Список доступных сервисов и инструментов MCP


Протокол WebSocket

Подключение по адресу: ws://localhost:3000/ws?deviceId=YOUR_DEVICE_ID

Клиент → Сервер

// Execute a tool
{ "type": "tool_execute", "requestId": "uuid", "serverId": "gmail",
  "toolName": "gmail.get_unread", "arguments": { "maxResults": 10 } }

// Link device to user account
{ "type": "link_device", "requestId": "uuid",
  "linkToken": "from-qr-code", "deviceId": "..." }

// Get user account info
{ "type": "get_user_account", "requestId": "uuid", "deviceId": "..." }

Сервер → Клиент

// Immediate progress (for voice feedback while tool runs)
{ "type": "tool_progress", "requestId": "uuid",
  "status": "executing", "message": "Checking your emails..." }

// Tool result
{ "type": "tool_result", "requestId": "uuid",
  "result": { "success": true, "content": "You have 3 unread emails..." } }

// Error
{ "type": "error", "requestId": "uuid", "error": "Token expired" }

Доступные инструменты MCP

Gmail

Инструмент

Описание

gmail.get_unread

Получить количество непрочитанных писем и сводки

gmail.search

Поиск писем по запросу

gmail.get_message

Получить полное содержимое письма по ID

Календарь

Инструмент

Описание

calendar.get_today

Получить события на сегодня

calendar.get_events

Получить события на N дней

calendar.create_event

Создать новое событие


Схема базы данных

База данных — SQLite в режиме WAL. Схема создается при запуске; новые столбцы добавляются через идемпотентные миграции.

-- Users (from Google OAuth)
users (
  id, google_id, email, display_name, profile_picture_url,
  public_gallery_token,          -- UUID for public gallery share link
  llm_provider,                  -- 'Gemini' or 'ChatGPT' (default: 'Gemini')
  openai_api_key_encrypted,      -- AES-256-GCM encrypted
  gemini_api_key_encrypted,      -- AES-256-GCM encrypted
  active_prompt_id,              -- FK to llm_prompts.id
  created_at, updated_at
)

-- Linked devices
devices (id, user_id, device_name, device_model, last_seen_at, last_heartbeat_at, linked_at, created_at)

-- QR code link tokens (single-use, 5 min expiry)
link_tokens (id, user_id, expires_at, used_at, used_by_device_id, created_at)

-- Encrypted Google OAuth tokens
oauth_tokens (id, user_id, provider, access_token_encrypted, refresh_token_encrypted, ...)

-- Scene images from glasses
glass_images (
  id, device_model, device_instance_id,
  scene_description,             -- May be empty; server-side LLM fills it if blank
  original_filename, thumbnail_filename,
  captured_at, received_at, created_at,
  image_width, image_height,     -- Pixels (nullable)
  user_id,                       -- FK to users.id (nullable for legacy rows)
  llm_provider                   -- Which LLM generated the description
)

-- User-defined LLM prompts
llm_prompts (id, user_id, name, text, created_at)

-- Third-party MCP servers registered via console
third_party_mcp_services (id, name, description, owner_user_id, endpoint_url, auth_type, auth_config_encrypted, tools_json, ...)

Структура каталогов

src/
├── index.ts                    # Entry point — starts HTTP + WebSocket servers
├── config/
│   ├── env.ts                  # Zod environment validation (exits on invalid config)
│   └── mcp-services.ts         # Built-in MCP service definitions
├── server/
│   ├── express.ts              # Express app — middleware, route mounting, error handling
│   ├── sessions.ts             # In-memory session store for console auth
│   └── websocket.ts            # WebSocket server with zombie connection cleanup
├── routes/
│   ├── auth.ts                 # Google OAuth flow
│   ├── console.ts              # Web console dashboard
│   ├── devices.ts              # Device management API
│   ├── gallery.ts              # Image gallery (authenticated + public)
│   ├── health.ts               # Health check
│   ├── images.ts               # Image upload + retrieval API
│   ├── link.ts                 # QR code token generation
│   ├── mcp.ts                  # MCP services API
│   └── settings.ts             # LLM settings page
├── services/
│   ├── auth.service.ts         # OAuth + token refresh mutex
│   ├── device.service.ts       # Device CRUD
│   ├── image.service.ts        # Image save/list/delete + thumbnail generation
│   ├── link.service.ts         # QR code token handling
│   ├── llm.service.ts          # LLM settings CRUD + callLlm() (Gemini / ChatGPT)
│   ├── mcp-proxy.service.ts    # MCP tool execution with input coercion
│   └── mcp-registry.service.ts # Third-party MCP server registry
├── websocket/
│   ├── handler.ts              # Message routing with progress feedback
│   ├── protocol.ts             # Message type definitions
│   └── connection.ts           # Connection tracking
├── db/
│   ├── index.ts                # SQLite connection + WAL mode
│   └── schema.ts               # CREATE TABLE + ALTER TABLE migrations
├── utils/
│   ├── crypto.ts               # AES-256-GCM encrypt/decrypt + token generation
│   ├── logger.ts               # Pino structured logging
│   └── cache.ts                # LRU cache for MCP tool results
└── types/
    ├── api.ts                  # REST API types
    ├── mcp.ts                  # MCP types
    └── websocket.ts            # WebSocket message types

Файлы данных записываются в ./data/:

  • ./data/glasscloud.db — база данных SQLite

  • ./data/images/ — оригинальные загруженные изображения

  • ./data/thumbnails/ — миниатюры JPEG 400×400 (создаются с помощью Sharp)


Безопасность

  • Токены OAuth зашифрованы в состоянии покоя с помощью AES-256-GCM с использованием ENCRYPTION_KEY

  • API-ключи LLM зашифрованы в состоянии покоя тем же ключом

  • JWT устройств подписаны с помощью JWT_SECRET (HS256); проверяются при каждом запросе API

  • Токены связи хранятся только в виде хеша SHA-256; одноразовые; срок действия 5 минут

  • Ограничение частоты запросов на всех маршрутах /api/* (настраивается через переменные окружения)

  • Helmet — заголовки безопасности во всех ответах

  • Токены публичной галереи — случайные UUID; повторная генерация мгновенно аннулирует предыдущую ссылку


Лицензия

MIT

Related MCP Connectors