Встраивание Grafana-дашбордов в веб-приложение

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.
Разработка и обслуживание любых видов сайтов:
Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Встраивание Grafana-дашбордов в веб-приложение
Средняя
от 1 рабочего дня до 3 рабочих дней
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1214
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    852
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    823
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    815

Встраивание Grafana-дашбордов в веб-приложение

Grafana умеет встраиваться в сторонние приложения через iframe. Это дешевле, чем строить собственный дашборд с нуля, и разумно когда у вас уже есть Grafana с настроенными datasource и дашбордами — нужно только показать их внутри своего интерфейса, возможно с авторизацией через SSO.

Два режима встраивания

Anonymous iframe — самый простой. Grafana настраивается на разрешение анонимного доступа к конкретным дашбордам. Подходит для публичных дашбордов (мониторинг сервисов, публичная статистика).

Grafana Embedded (Grafana 9.1+) — встраивание с токеном сервисного аккаунта. Iframe получает JWT-токен, Grafana проверяет его без пользовательского логина. Правильный выбор для приложений с аутентификацией.

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

# grafana.ini

[security]
allow_embedding = true

[auth.anonymous]
enabled = true
org_role = Viewer
hide_version = true

# Для production — ограничить CORS
[security]
cookie_secure = true
cookie_samesite = none

Grafana Service Account Token (рекомендуемый способ)

# Создаём сервисный аккаунт через API
curl -X POST http://grafana:3000/api/serviceaccounts \
  -H "Content-Type: application/json" \
  -u admin:admin \
  -d '{"name":"embed-reader","role":"Viewer","isDisabled":false}'

# Создаём токен для сервисного аккаунта
curl -X POST http://grafana:3000/api/serviceaccounts/1/tokens \
  -H "Content-Type: application/json" \
  -u admin:admin \
  -d '{"name":"embed-token","secondsToLive":0}'

Генерация signed URL на сервере

Чтобы конечный пользователь не видел токен сервисного аккаунта, URL генерируется на вашем сервере:

interface GrafanaEmbedOptions {
  dashboardUid: string;
  panelId?: number;
  from?: string;   // e.g. 'now-7d'
  to?: string;     // e.g. 'now'
  vars?: Record<string, string>;  // template variables
  theme?: 'light' | 'dark';
  kiosk?: boolean;  // скрыть заголовок и менюшки
}

class GrafanaEmbedService {
  constructor(
    private readonly baseUrl: string,
    private readonly serviceAccountToken: string
  ) {}

  buildEmbedUrl(options: GrafanaEmbedOptions): string {
    const {
      dashboardUid,
      panelId,
      from = 'now-24h',
      to = 'now',
      vars = {},
      theme = 'light',
      kiosk = true,
    } = options;

    const params = new URLSearchParams({
      from,
      to,
      theme,
      ...(kiosk ? { kiosk: 'tv' } : {}),
    });

    Object.entries(vars).forEach(([key, val]) => {
      params.append(`var-${key}`, val);
    });

    const path = panelId
      ? `/d-solo/${dashboardUid}?panelId=${panelId}&`
      : `/d/${dashboardUid}?`;

    return `${this.baseUrl}${path}${params.toString()}`;
  }

  // Для Grafana 10+ с auth token в URL (не рекомендуется для production)
  buildAuthenticatedUrl(options: GrafanaEmbedOptions, userEmail: string): string {
    const baseEmbed = this.buildEmbedUrl(options);
    // В production используйте OAuth/SSO вместо передачи токена в URL
    return baseEmbed;
  }
}

Компонент встраивания

import { useState, useEffect, useRef } from 'react';

interface GrafanaPanelProps {
  dashboardUid: string;
  panelId: number;
  vars?: Record<string, string>;
  from?: string;
  to?: string;
  height?: number;
  title?: string;
}

export function GrafanaPanel({
  dashboardUid,
  panelId,
  vars,
  from = 'now-24h',
  to = 'now',
  height = 300,
  title,
}: GrafanaPanelProps) {
  const [embedUrl, setEmbedUrl] = useState<string | null>(null);
  const [loading, setLoading] = useState(true);
  const iframeRef = useRef<HTMLIFrameElement>(null);

  useEffect(() => {
    // URL генерируется на нашем сервере, не в браузере
    fetch('/api/grafana/embed-url', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ dashboardUid, panelId, vars, from, to }),
    })
      .then(r => r.json())
      .then(({ url }) => setEmbedUrl(url));
  }, [dashboardUid, panelId, JSON.stringify(vars), from, to]);

  return (
    <div className="relative rounded-lg overflow-hidden border bg-white" style={{ height }}>
      {title && (
        <div className="px-4 py-2 border-b text-sm font-medium text-gray-700">{title}</div>
      )}
      {loading && (
        <div className="absolute inset-0 flex items-center justify-center bg-gray-50">
          <div className="w-6 h-6 border-2 border-blue-500 border-t-transparent rounded-full animate-spin" />
        </div>
      )}
      {embedUrl && (
        <iframe
          ref={iframeRef}
          src={embedUrl}
          width="100%"
          height={title ? height - 40 : height}
          frameBorder="0"
          onLoad={() => setLoading(false)}
          title={title ?? `Grafana panel ${panelId}`}
        />
      )}
    </div>
  );
}

API endpoint для генерации URL

// Express / Fastify
app.post('/api/grafana/embed-url', requireAuth, async (req, res) => {
  const { dashboardUid, panelId, vars, from, to } = req.body;

  // Проверяем права: пользователь имеет доступ к этому дашборду
  const hasAccess = await checkDashboardAccess(req.user.id, dashboardUid);
  if (!hasAccess) return res.status(403).json({ error: 'Forbidden' });

  const url = grafanaService.buildEmbedUrl({
    dashboardUid,
    panelId,
    vars,
    from,
    to,
    kiosk: true,
    theme: 'light',
  });

  res.json({ url });
});

Template Variables для multi-tenant

Если один дашборд используется для разных клиентов, Grafana template variables позволяют фильтровать данные:

// Каждый пользователь видит только свои данные
const embedUrl = grafanaService.buildEmbedUrl({
  dashboardUid: 'analytics-overview',
  vars: {
    tenant_id: req.user.tenantId,
    region: req.user.region,
  },
  from: 'now-30d',
  to: 'now',
});

В Grafana datasource query эта переменная используется как фильтр:

SELECT * FROM metrics WHERE tenant_id = '${tenant_id}' AND $__timeFilter(time)

Проблемы и решения

CSP блокирует iframe — добавьте домен Grafana в frame-src:

Content-Security-Policy: frame-src https://grafana.yourdomain.com

SameSite cookie — при встраивании в iframe Grafana-сессионные cookies могут блокироваться браузером. Решение: сервисные аккаунты с токеном вместо сессионных cookies.

Мобильные устройства — кнопки Grafana UI мелкие на телефоне. Если нужен полноценный мобильный опыт, встраивание не подходит — нужны собственные компоненты.

Сроки

Встраивание готовых дашбордов с генерацией URL на сервере и проверкой прав доступа — 2–4 дня. Multi-tenant конфигурация с template variables — ещё 1–2 дня.