Sprucely title background

Интеграция

Интерактивные дашборды Sprucely.io можно встроить в собственный сайт или веб-приложение тремя способами: с помощью стандартного HTML-элемента iframe, путём нативной отрисовки через автономную среду выполнения JavaScript или путём монтирования автономных React-компонентов. iframe настраивается быстрее всего; автономные среды выполнения отрисовывают дашборды непосредственно на странице и позволяют добавлять новые данные во время выполнения. JSON-структура, которую считывают автономные среды выполнения — дашборды, диаграммы и наборы данных, — описана в справочнике формат данных. Об автоматизации дашбордов с помощью ИИ см. раздел MCP API.

Встраивание дашбордов в HTML

Самый простой способ интеграции — использовать стандартный HTML-элемент iframe; он работает на любом сайте и даже на локальных HTML-страницах. Чтобы дашборд загрузился, для него нужно включить общий доступ — в облаке (Cloud) или локально (On-premise). Обратите внимание: локальные (On-premise) дашборды загружаются только тогда, когда обращающийся к ним клиент находится в той же корпоративной сети.

Инструкция:

1) Скопируйте ссылку для встраивания дашборда - на странице Дашборды убедитесь, что для дашборда включён общий доступ, и нажмите значок рядом с этим дашбордом. Зелёный всплывающий баннер сообщит, что ссылка скопирована в буфер обмена. Используйте её, чтобы заменить содержимое атрибута src HTML-элемента iframe ниже.

2) Встройте дашборд с фиксированным размером

<html>
  <head>
    <title>Sprucely.io Dashboard</title>
  </head>
  <body>
    <h1>Sprucely.io Dashboard</h1>
    <iframe src="https://www.sprucely.io/service/dashboards/embed/[userId]/[dashboardId]/"
            allow="clipboard-write"
            style="height: 500px; width: 700px;"
            title="Sprucely.io Dashboard"></iframe>
  </body>
</html>

(или) Встройте дашборд с динамической шириной - Автоматически изменяет размер дашборда в зависимости от ширины родительского документа

Вы можете использовать CSS-параметр aspect-ratio, чтобы автоматически пересчитывать высоту на основе ширины.

<html>
  <head>
    <title>Sprucely.io Dashboard</title>
  </head>
  <body>
    <h1>Sprucely.io Dashboard</h1>
    <iframe src="https://www.sprucely.io/service/dashboards/embed/[userId]/[dashboardId]/"
            allow="clipboard-write"
            scrolling="no"
            style="width: 100%; aspect-ratio: 1.5; border: none;"
            title="Sprucely.io Dashboard"></iframe>
  </body>
</html>

3) Другие полезные параметры встраивания

Вы можете добавлять собственные стилевые изменения в блок style элемента iframe, чтобы настроить внешний вид фрейма. Эти параметры соответствуют стандартным рекомендациям HTML и CSS. В примере ниже вокруг дашборда добавлена серая рамка:

<html>
  <head>
    <title>Sprucely.io Dashboard</title>
  </head>
  <body>
    <h1>Sprucely.io Dashboard</h1>
    <iframe src="https://www.sprucely.io/service/dashboards/embed/[userId]/[dashboardId]/"
            allow="clipboard-write"
            style="height: 100%; width: 100%; border: 2px solid grey;"
            title="Sprucely.io Dashboard"></iframe>
  </body>
</html>

Встраивание с помощью автономного JavaScript

Автономная среда выполнения отрисовывает дашборды непосредственно на странице - без iframe. Ваши данные загружаются во внутрибраузерную базу данных и отображаются в виде полностью интерактивных диаграмм; среда выполнения обращается к Sprucely.io только для проверки вашего токена доступа.

Инструкция:

1) Создайте токен доступа - в разделе Токены доступа вашего профиля создайте токен доступа для источника, с которого обслуживаются ваши страницы (например, https://www.yourdomain.com). Перед отрисовкой среда выполнения проверяет, что источник встраивающей страницы совпадает с токеном.

2) Загрузите среду выполнения и отрисуйте дашборд - добавьте скрипт среды выполнения на свою страницу, один раз подключите внутрибраузерную базу данных с помощью sprucely_db, а затем отрисуйте каждый дашборд с помощью sprucely_create. Полная страница ниже также подключает кнопку, которая добавляет дополнительные строки:

<html>
  <head>
    <title>Orders Dashboard</title>
  </head>
  <body>
    <div id="database"></div>
    <div id="orders_dashboard"></div>
    <button onclick="addRows()">Add rows</button>

    <script src="https://www.sprucely.io/cross-origin/sprucely-runtime.min.js"></script>
    <script>
      const data = {
        name:    "Orders",
        headers: ["region", "amount", "items"],
        types:   ["VARCHAR", "FLOAT", "INTEGER"],
        entries: [
          ["North", 120.50, 2],
          ["South",  89.95, 1],
          ["North", 432.00, 5],
          ["South",  74.50, 1]
        ]
      };

      const dashboard = {
        config: {
          type: "dashboard",
          style: {
            widget: {
              color: "#667FFF",
              backgroundColor: "#0C0B29",
              primaryColor: "#01EEAE",
              primaryColorSubtle: "#C70584",
              secondaryColor: "#2C2B49"
            }
          }
        },
        data: {
          type: "dash",
          children: [
            {
              type: "dash_stacker_hor",
              children: [
                { type: "dash_chart", datasetId: "Orders", x: "region", chartType: "bar", dataFunction: "count" },
                { type: "dash_chart", datasetId: "Orders", x: "amount", y: "items", chartType: "hexbin", dataFunction: "count" }
              ]
            }
          ]
        }
      };

      sprucely_db({
        id: "database",
        host: "https://www.yourdomain.com",
        accessToken: "YOUR_ACCESS_TOKEN"
      });
      sprucely_create({ id: "orders_dashboard", dashboard, data });

      function addRows() {
        sprucely_add({
          name:    "Orders",
          headers: ["region", "amount", "items"],
          types:   ["VARCHAR", "FLOAT", "INTEGER"],
          entries: [
            ["East", 310.40, 3],
            ["West", 129.95, 1]
          ]
        });
      }
    </script>
  </body>
</html>

3) Добавляйте данные во время выполнения - в любой момент вызывайте sprucely_add с дополнительными строками. Каждый дашборд, использующий этот набор данных, обновится автоматически:

sprucely_add({
  name:    "Orders",
  headers: ["region", "amount", "items"],
  types:   ["VARCHAR", "FLOAT", "INTEGER"],
  entries: [
    ["East", 310.40, 3],
    ["West", 129.95, 1]
  ]
});

Справочник функций

  • sprucely_db({ id, host, accessToken }) - подключает внутрибраузерную базу данных и проверяет ваш токен доступа по источнику страницы. Отрисовывает баннер статуса в элементе с указанным id. Вызывайте один раз на странице, перед созданием дашбордов.
  • sprucely_create({ id, dashboard, data }) - отрисовывает один дашборд в элементе с указанным id. Параметр dashboard задаёт макет и оформление; data содержит набор данных с его именем, заголовками, типами столбцов (VARCHAR, INTEGER, FLOAT или TIMESTAMP) и строками - см. справочник формат данных.
  • sprucely_add(data) - добавляет строки к набору данных, имя которого совпадает с уже загруженным набором данных, а затем обновляет все дашборды, которые его используют.

Встраивание с помощью автономного React

Если ваш сайт построен на React, вы можете отрисовывать дашборды в виде компонентов вместо ручной загрузки скрипта. Компоненты используют те же токены доступа, определения дашбордов и формат наборов данных, что и автономная среда выполнения JavaScript.

Инструкция:

1) Создайте токен доступа - как и для автономного JavaScript, создайте токен доступа для источника вашего сайта в разделе Токены доступа вашего профиля.

2) Отрисуйте компоненты дашборда - получите среду выполнения по адресу https://www.sprucely.io/cross-origin/sprucely-runtime.min.cjs.js и разместите её в своём проекте. Монтируйте один компонент Sprucely.Database на страницу и один компонент Sprucely.Dashboard на каждый дашборд. Дашборды показывают индикатор загрузки, пока база данных не проверит токен доступа, а затем отрисовываются. Полное приложение ниже отрисовывает два дашборда из отдельных наборов данных и через пять секунд добавляет новые строки к первому:

// Get https://www.sprucely.io/cross-origin/sprucely-runtime.min.cjs.js
// and place it in your project, next to this file.

import React, { useEffect } from "react";
import { createRoot } from "react-dom/client";
import Sprucely, { sprucely_add } from "./sprucely-runtime.min.cjs.js";

const style = {
  widget: {
    color: "#667FFF",
    backgroundColor: "#0C0B29",
    primaryColor: "#01EEAE",
    primaryColorSubtle: "#C70584",
    secondaryColor: "#2C2B49"
  }
};

const orders = {
  name:    "Orders",
  headers: ["region", "amount", "items"],
  types:   ["VARCHAR", "FLOAT", "INTEGER"],
  entries: [
    ["North", 120.50, 2],
    ["South",  89.95, 1],
    ["North", 432.00, 5],
    ["South",  74.50, 1]
  ]
};

const returns = {
  name:    "Returns",
  headers: ["reason", "amount", "days"],
  types:   ["VARCHAR", "FLOAT", "INTEGER"],
  entries: [
    ["Damaged", 45.00, 3],
    ["Late",    12.50, 8],
    ["Changed", 99.90, 2]
  ]
};

const moreOrders = {
  name:    "Orders",
  headers: ["region", "amount", "items"],
  types:   ["VARCHAR", "FLOAT", "INTEGER"],
  entries: [
    ["East", 310.40, 3],
    ["West", 129.95, 1]
  ]
};

const ordersDashboard = {
  config: { type: "dashboard", style },
  data: {
    type: "dash",
    children: [
      {
        type: "dash_stacker_hor",
        children: [
          { type: "dash_chart", datasetId: "Orders", x: "region", chartType: "bar", dataFunction: "count" },
          { type: "dash_chart", datasetId: "Orders", x: "amount", y: "items", chartType: "hexbin", dataFunction: "count" }
        ]
      }
    ]
  }
};

const returnsDashboard = {
  config: { type: "dashboard", style },
  data: {
    type: "dash",
    children: [
      {
        type: "dash_stacker_hor",
        children: [
          { type: "dash_chart", datasetId: "Returns", x: "amount", y: "days", d: "amount", r: "days", chartType: "dot", dataFunction: "average" }
        ]
      }
    ]
  }
};

const App = () => {
  useEffect(() => {
    const timer = setTimeout(() => { sprucely_add(moreOrders); }, 5000);
    return () => clearTimeout(timer);
  }, []);

  return (
    <>
      <Sprucely.Database host="https://www.yourdomain.com" accessToken="YOUR_ACCESS_TOKEN" />
      <Sprucely.Dashboard id="orders_dashboard" dashboard={ordersDashboard} data={orders} />
      <Sprucely.Dashboard id="returns_dashboard" dashboard={returnsDashboard} data={returns} />
    </>
  );
};

createRoot(document.getElementById("root")).render(<App />);

Обслуживание с помощью краткосрочных токенов

Токен доступа, который вы создали выше, — долгоживущий, и он должен оставаться секретом на стороне сервера. Если встраивающие страницы обслуживает ваш собственный бэкенд — а не страница вовсе без бэкенда, — вам не нужно вообще размещать этот долгоживущий токен на странице: ваш бэкенд может обменять его напрямую, по схеме «сервер—сервер», на краткосрочный (15-минутный) токен отрисовки непосредственно перед отдачей каждой страницы, и до браузера доходит только этот токен отрисовки. Токен отрисовки, который посетитель извлечёт со страницы, полезен лишь считанные минуты, а не бессрочно.

Инструкция:

1) Обменяйте токен доступа на токен отрисовки - вызовите со своего бэкенда POST https://www.sprucely.io/api/auth/render_token, передав токен доступа как bearer-учётные данные. В ответе приходит новый токен, привязанный к нему хост и время жизни в секундах:

// On your server, immediately before serving each embedding page:
const response = await fetch("https://www.sprucely.io/api/auth/render_token", {
  method:  "POST",
  headers: { Authorization: "Bearer " + process.env.SPRUCELY_ACCESS_TOKEN }
});
const { token, expires_in } = await response.json(); // expires_in: 900 (15 minutes)

// Send only the render token to the browser - never the long-lived access
// token itself:
res.send(`
  <script src="https://www.sprucely.io/cross-origin/sprucely-runtime.min.js"></script>
  <script>
    sprucely_db({
      id: "database",
      host: "https://www.yourdomain.com",
      accessToken: "${token}"
    });
  </script>
`);

2) Передайте токен отрисовки в браузер - используйте его точно так же, как токен доступа, при вызове sprucely_db или монтировании Sprucely.Database. Повторяйте обмен перед обслуживанием каждой страницы (или по таймеру, если вы кешируете отрисованную страницу), чтобы в браузер никогда не попадал токен, действительный дольше 15 минут.

Справочник функций

  • POST /api/auth/render_token - обменивает действительный токен доступа, переданный как Authorization: Bearer <token>, на токен отрисовки. Возвращает { token, host, expires_in }, где expires_in всегда равно 900 секундам. Токен отрисовки нельзя обменять на другой токен отрисовки - запросить его может только долгоживущий токен доступа.

Настройки безопасности

Если на вашем сайте применяется политика безопасности контента (CSP), для описанных выше интеграций автономного JavaScript и React потребуется разрешить несколько источников, прежде чем дашборды смогут загрузиться - среда выполнения загружает свой скрипт с Sprucely.io, открывает внутрибраузерную базу данных на основе WebAssembly и проверяет ваш токен доступа через наш API, и каждому из этих действий требуется явное разрешение CSP. Если ваш сайт не использует CSP, этот раздел можно пропустить.

Добавьте следующие источники в существующую политику - это директивы для объединения с уже имеющимися, а не полная политика, заменяющая то, что у вас уже есть:

script-src   'self' https://www.sprucely.io 'wasm-unsafe-eval';
connect-src  'self' https://www.sprucely.io;
worker-src   'self' blob: https://www.sprucely.io;

Назначение каждой директивы:

  • script-src - https://www.sprucely.io загружает скрипт среды выполнения. 'wasm-unsafe-eval' необходим для компиляции и запуска внутрибраузерной базы данных, построенной на WebAssembly.
  • connect-src - к https://www.sprucely.io выполняется обращение для проверки токена доступа и загрузки файлов WebAssembly и воркера базы данных.
  • worker-src - внутрибраузерная база данных выполняет запросы в фоновом потоке, созданном из URL вида blob:.

Ничего из перечисленного не требует 'unsafe-inline' - самой среде выполнения Sprucely он никогда не нужен. Если вы храните собственные определения дашборда и набора данных во встроенном теге <script>, как в примере автономного JavaScript выше, или ваша страница использует встроенные стили, добавьте 'unsafe-inline' в script-src или style-src, либо рассмотрите использование CSP-нонсов.