Sprucely title background

インテグレーション

インタラクティブなSprucely.ioダッシュボードは、自社のウェブサイトやウェブアプリケーションに3通りの方法で統合できます。標準的なHTMLのiframeで埋め込む方法、スタンドアロンのJavaScriptランタイムでネイティブに描画する方法、またはスタンドアロンのReactコンポーネントをマウントする方法です。iframeが最も手早く導入できますが、スタンドアロンランタイムはダッシュボードをページ内に直接描画し、実行時に新しいデータを送り込むことができます。スタンドアロンランタイムが読み込むJSONの構造(ダッシュボード、チャート、データセット)は、データ形式のリファレンスで解説しています。AIによるダッシュボード自動化については、MCP APIのセクションを参照してください。

HTML埋め込み

最もシンプルな統合方法は、標準的なHTMLのiframe要素を使う方法で、どのようなウェブサイトでも、ローカルのHTMLページでも動作します。正常に読み込まれるためには、ダッシュボードがクラウドまたはオンプレミスのいずれかで共有されている必要があります。なお、オンプレミスのダッシュボードは、アクセスするクライアントが同じ社内ネットワーク内にある場合にのみ読み込まれる点にご注意ください。

手順:

1) ダッシュボードの埋め込みリンクを取得する - ダッシュボードページで、対象のダッシュボードが共有されていることを確認し、そのダッシュボードのアイコンをクリックします。リンクがクリップボードにコピーされたことを知らせる緑色のポップアップバナーが表示されます。これを使って、以下のHTMLiframeのsrcの内容を置き換えます。

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>

(または)動的な幅でダッシュボードを埋め込む - 親ドキュメントの幅に応じて、ダッシュボードのサイズを自動的に調整します

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) その他の便利な埋め込みオプション

iframe要素のstyleブロックにカスタムのスタイル変更を追加すると、フレームの見た目を自由にカスタマイズできます。これらのパラメータは、標準的な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で指定した要素に1つのダッシュボードを描画します。dashboardパラメータはレイアウトとスタイルを定義し、dataはデータセットのname、headers、列タイプ(VARCHARINTEGERFLOATまたはTIMESTAMP)、rowsを提供します。詳細はデータ形式のリファレンスを参照してください。
  • sprucely_add(data) - nameが既に読み込まれているデータセットと一致する行を追加し、それを使用するすべてのダッシュボードを更新します。

スタンドアロンReact

サイトがReactで構築されている場合、スクリプトを手動で読み込む代わりに、ダッシュボードをコンポーネントとして描画できます。このコンポーネントは、スタンドアロンJavaScriptランタイムと同じアクセストークン、ダッシュボード定義、データセット形式を使用します。

手順:

1) アクセストークンを作成する - スタンドアロンJavaScriptの場合と同様に、プロフィールのアクセストークンセクションで、サイトのオリジンに対するアクセストークンを作成します。

2) ダッシュボードコンポーネントを描画する - https://www.sprucely.io/cross-origin/sprucely-runtime.min.cjs.jsからランタイムを取得し、プロジェクトに配置します。ページごとにSprucely.Databaseコンポーネントを1つ、ダッシュボードごとにSprucely.Dashboardコンポーネントを1つマウントします。ダッシュボードは、データベースがアクセストークンを検証するまでローディングインジケーターを表示し、その後描画されます。以下の完全なアプリケーション例では、別々のデータセットから2つのダッシュボードを描画し、5秒後に最初のダッシュボードに新しい行を追加しています。

// 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を呼び出します。レスポンスには、新しいトークンと、それが紐づくホスト、有効期間(秒単位)が含まれます:

// 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 - アクセストークンを検証し、データベースのWebAssemblyファイルとワーカーファイルを読み込むために、https://www.sprucely.ioと通信します。
  • worker-src - ブラウザ内データベースは、blob: URLから作成されたバックグラウンドスレッドでクエリを実行します。

上記のいずれも'unsafe-inline'を必要としません。Sprucelyランタイム自体がこれを必要とすることはありません。上記のスタンドアロンJavaScriptの例のように、独自のダッシュボードおよびデータセット定義をインラインの<script>タグ内に保持している場合や、独自のページでインラインスタイルを使用している場合は、'unsafe-inline'script-srcまたはstyle-srcに追加するか、CSPノンスの使用を検討してください。