Внешний доступ к изображениям и файлам
Изображения и файлы в разных каналах используют разные способы доступа. Веб-консоль и виджет встраивания могут обращаться к прокси, неся с собой учётные данные; платформы IM требуют публично загружаемых ссылок с ограниченным сроком; клиенты API могут выбирать внутреннюю ссылку или внешнюю ссылку по назначению.
При подключении нужно одновременно убедиться в форме ссылки, правах вызывающего и достижимости адреса. Если изображение не отображается, проверьте по таблице диагностики в конце документа.
Формы ссылок на файлы и способы доступа
Изображения и вложения баз знаний хранятся в файловом хранилище. В тексте используется стабильная внутренняя ссылка, которая при ответе или отрисовке преобразуется в адрес доступа в зависимости от канала:
| Форма | Пример адреса | Условие доступа | Срок действия |
|---|---|---|---|
| Внутренняя ссылка | resource://<handle> | Стабильный дескриптор, разрешаемый на сервере; нельзя обращаться напрямую как к URL | — |
| Аутентифицирующий прокси | /files, /api/v1/knowledge-bases/:id/files, /api/v1/embed/:channel_id/files, на уровне сообщения /api/v1/sessions/:id/messages/:message_id/files | Клиент с соответствующими учётными данными (сессия входа / право доступа к KB / Embed token) | Как у учётных данных |
| Короткая ссылка-возможность | /r/<token> | Любой, кто получил ссылку (анонимное чтение) | grant, выданный aihelpme.ru, 2 часа |
| Предподписанная ссылка хранилища | http(s)-ссылка, выдаваемая напрямую бэкендом хранения | Любой, кто получил ссылку (анонимное чтение) | Определяется хранилищем; для MinIO по умолчанию 24 часа |
Короткие ссылки-возможности и предподписанные ссылки хранилища в течение срока действия позволяют держателю анонимно читать файл. При передаче или фиксации таких ссылок следует обращаться с ними согласно области доступа самого файла. Отзыв общего доступа организации или выход из организации не делают уже выданные ссылки недействительными досрочно — они остаются работоспособными до истечения срока (короткие ссылки — 2 часа, предподписанные ссылки хранилища — максимум 24 часа).
Способ доступа по умолчанию
По умолчанию система возвращает внутреннюю ссылку, которую клиент с учётными данными читает через аутентифицирующий прокси. Внешняя ссылка требует доступного из публичной сети эндпоинта хранилища либо выдачи ссылки доступа со стороны aihelpme.ru после настройки APP_EXTERNAL_URL. Адрес MinIO по умолчанию minio:9000 достижим только внутри сети контейнеров.
Доступ к файлам по каналам
Веб-консоль
Веб-фронтенд преобразует ссылки resource:// и provider:// в адреса аутентифицирующего прокси. Для обычных ресурсов используется /files, запрос несёт Bearer token и X-Tenant-ID; для межпространственно разделяемых баз знаний используется /api/v1/knowledge-bases/:id/files, который читает файлы исходного пространства по праву доступа к базе знаний без дополнительной настройки.
Изображения в ответах общего агента или баз знаний, разделяемых в организации, в первую очередь идут через /api/v1/sessions/:id/messages/:message_id/files?file_path=.... Сервер проверяет, что вызывающий может прочитать это сообщение и что запрошенный ресурс действительно процитирован сообщением, и заново проверяет авторизацию для базы знаний или общего агента, к которым относится ресурс. После отзыва общего доступа исторические сообщения больше не дают доступ к ресурсу. Для обычного просмотра баз знаний по-прежнему можно использовать прокси уровня KB; оба варианта используют свой контекст.
IM-бот
Платформы IM не могут нести учётные данные aihelpme.ru, им нужен публично доступный HTTP(S) URL. Перед отправкой сообщения система генерирует внешнюю ссылку с учётом конфигурации хранилища и развёртывания:
- Сам бэкенд хранения доступен из публичной сети — объектное хранилище использует публичный endpoint, или
MINIO_ENDPOINTзадан как публичный хост. В этом случае происходит откат к предподписанному URL хранилища без дополнительной настройки; - Настроить
APP_EXTERNAL_URL— ссылка переписывается в<APP_EXTERNAL_URL>/r/<token>, а запрос черезlocation ^~ /r/в nginx обратно проксируется на app. В официальном образе фронтенда эта location уже встроена; в самостоятельно собранном обратном прокси её нужно добавить, иначе запрос попадёт в SPA fallback и вернёт пустую страницу.
Для развёрнутого во внутренней сети MinIO по умолчанию и хранилища local требуется настроить APP_EXTERNAL_URL. Если условия для внешней ссылки не выполнены, система сохраняет исходную ссылку и записывает предупреждение, а платформа IM не может напрямую показать это изображение. Если канал IM включён, но APP_EXTERNAL_URL пуст, предупреждение выводится также на этапе запуска.
Виджет встраивания
Виджет встраивания использует прокси файлов канала /api/v1/embed/:channel_id/files. Embed token определяет пространство, которому принадлежит канал, а сервер проверяет принадлежность пути ресурса. Канал встраивания всегда возвращает внутреннюю ссылку; ни RESOURCE_URL_MODE=public, ни ?resource_urls=public не меняют это поведение, чтобы чтение проходило аутентификацию канала.
REST API и SDK
По умолчанию API возвращает resource://, и клиент получает файл через аутентифицирующий прокси. Если нужно напрямую отрисовывать внешнюю ссылку, можно задать:
- Отдельный запрос:
?resource_urls=public - Всё развёртывание:
RESOURCE_URL_MODE=public
Параметр отдельного запроса имеет приоритет над переменной окружения, поэтому даже после задания значений по умолчанию для развёртывания как public можно отдельно вернуться назад через ?resource_urls=handle. Интерфейсы, поддерживающие этот параметр, охват и границы безопасности см. в разделе «Формы ссылок на файлы» в Обзоре API.
API Key, ограниченный областью базы знаний, при использовании public вернёт 403, и такой Key также не может обращаться к общему прокси /files. Если условия для внешней ссылки не выполнены, ответ сохраняет resource://, а клиент с соответствующими правами может переключиться на аутентифицирующий прокси.
Диагностика проблем доступа
| Симптом | Возможная причина | Способ решения |
|---|---|---|
| В IM изображение не отображается | Не настроен APP_EXTERNAL_URL и хранилище недоступно из публичной сети | Настроить APP_EXTERNAL_URL, убедиться, что nginx проксирует /r/; проверить WARN rewriteStorageURLs no-op в логах app |
| Ссылка на изображение в IM открывается, но возвращается пустая страница | В nginx отсутствует location ^~ /r/, запрос попадает в SPA fallback | Добавить эту location (в официальном образе фронтенда она уже есть), см. Веб-фронтенд |
APP_EXTERNAL_URL задан как адрес внутренней сети или localhost | Платформа IM находится в публичной сети и не может достучаться | Заменить на адрес, достижимый для платформы IM; для локальной разработки использовать ngrok / cloudflared / frp |
Адрес изображения, возвращённый API, — resource:// | По умолчанию это и есть внутренняя ссылка | Добавить ?resource_urls=public или вызвать прокси /files |
С resource_urls=public всё равно возвращается resource:// | Развёртывание не способно выдавать внешние ссылки (например, хранилище local без настроенного APP_EXTERNAL_URL) | Обеспечить условия для внешней ссылки или перейти на прокси /files |
С resource_urls=public возвращается 403 | Используется API Key, ограниченный базой знаний | Перейти на режим handle или использовать авторизованный full-access Key |
| В виджете встраивания изображение не отображается, а на веб-странице всё в порядке | Виджет использует прокси канала и иные учётные данные, нежели основной сайт | Убедиться, что страница виджета несёт действительный Embed token; resource_urls=public для канала встраивания не действует |
| Изображения в общем ответе дают 403/404 | Отсутствует контекст сообщения, ресурс не привязан или общий доступ отозван | Использовать прокси уровня сообщения и проверить текущие права общего доступа; не собирать вручную /files-адрес tenant-владельца |
| После обновления или смены ключа уже выданные ссылки перестают работать | После изменения ключа подписи (SYSTEM_SIGNING_KEY или используемого как запасной SYSTEM_AES_KEY) старая подпись не проверяется | Получить ссылку заново; при развёртывании с несколькими репликами убедиться, что все экземпляры используют один и тот же ключ |
| Внешняя ссылка через некоторое время перестаёт работать | Внешние ссылки имеют ограниченный срок (grant — 2 часа / предподписанная MinIO — 24 часа) | Не кешировать саму внешнюю ссылку, а получать её заново при необходимости; для одного и того же файла в течение срока действия переиспользуется одна и та же ссылка |
| На веб-странице изображение даёт 404, а в логах — несовпадение tenant | Изображения межтенантной общей базы хранятся под tenant-владельцем | В этом сценарии следует идти через /api/v1/knowledge-bases/:id/files и убедиться, что фронтенд получает адрес прокси уровня KB |
Справочник по конфигурации
| Конфигурация | Назначение |
|---|---|
APP_EXTERNAL_URL | Внешне достижимый адрес для внешних ссылок на изображения канала IM; условие для переписывания resource:// в <APP_EXTERNAL_URL>/r/<token> |
RESOURCE_URL_MODE | Форма ссылки на файл по умолчанию в ответах API (handle / public) |
endpoint хранилища, например MINIO_ENDPOINT | При задании публичного адреса внешние ссылки могут выдаваться предподписанными ссылками хранилища, без зависимости от APP_EXTERNAL_URL |
SYSTEM_SIGNING_KEY (при отсутствии используется запасной SYSTEM_AES_KEY) | Ключ подписи. От него зависят предподписанные ссылки /api/v1/files/presigned; он же используется для переиспользования grant /r/<token> в течение срока действия, стабилизации прямых URL и снижения нагрузки на запись в интерфейсах чтения. Если он не настроен, короче 16 символов или является примерным значением, предподписанные ссылки выдать нельзя; после замены уже выданные ссылки перестают работать |
Связанные документы
- Интеграция с IM: логика переписывания и предупреждения при запуске
- Веб-встраивание: аутентификация канала и анонимные сессии
- Обзор API: полная семантика
resource_urls - Подробное описание конфигурации: вышеуказанные переменные окружения
- Веб-фронтенд: прокси
/filesи/r/в nginx
Справочник по реализации
frontend/src/utils/protectedFileAccess.ts: ссылки на веб-файлы и пути прокси.- Перед отправкой сообщения IM внешние ссылки преобразуются через
rewriteStorageURLs().
