Osadzanie Saiku
Komponent Web Component <saiku-embed/> pozwala osadzić zapisane
zapytanie lub dashboard Saiku w dowolnej stronie HTML — na stronie
marketingowej, w artykule blogowym, w portalu klienta, na wiki albo
w dashboardzie React / Vue. Ten sam element niestandardowy działa
w każdym hoście: React JSX, szablonach Vue, Svelte, czystym HTML.
To prawdziwy Web Component, a nie opakowanie frameworkowe, więc strona hostująca nie musi nic wiedzieć o wnętrznościach Saiku.
Instalacja
Dwa równoważne sposoby załadowania bundla. Wybierz ten, który pasuje do strony hostującej.
Bez kroku budowania — wskaż tagiem <script> bundle, który już
serwuje Twoje Saiku:
<script src="https://YOUR-WORKSPACE.saiku.bi/ui/saiku-embed.js"></script>Pobranie to około 213 KB po gzipie. Przeglądarki buforują go, więc druga strona na tym samym origin dostaje go natychmiast.
Dla projektów React / Vue / SPA, które bundlują własny JS:
npm install @concepttocloud/saiku-embedimport "@concepttocloud/saiku-embed";Import ma efekt uboczny polegający na zarejestrowaniu globalnie
tagu saiku-embed — bez dalszej konfiguracji. Wydanie npm śledzi
wydanie Saiku jeden do jeden.
Działający przykład
Minimalny żywotny embed — zapisane zapytanie wyrenderowane jako tabela:
<saiku-embed server="https://YOUR-WORKSPACE.saiku.bi" token="tx-..." path="homes/admin/Examples/Trend.saiku" height="400px"></saiku-embed>Trzy nośne atrybuty to server (origin Twojego Saiku), path (ścieżka
zasobu zapisanego zapytania w repozytorium) i token (token osadzania
— zobacz Generowanie tokenu poniżej). Reszta ma
sensowne wartości domyślne.
Generowanie tokenu
Tokeny generuje uwierzytelniony użytkownik, który ma prawo odczytu zasobu. Token jest nieprzezroczysty i wąsko zakresowany: daje dostęp tylko do odczytu jednego zapisanego zapytania lub dashboarda, wygasa po określonym przez Ciebie TTL i może zostać unieważniony w dowolnej chwili.
curl -X POST 'https://YOUR-WORKSPACE.saiku.bi/rest/saiku/api/embed/tokens' \ -u admin:admin \ -H 'Content-Type: application/json' \ -d '{ "resourceKind": "query", "resourcePath": "homes/admin/Examples/Trend.saiku", "ttlHours": 72, "label": "Marketing landing page" }'
# Odpowiedź:# {# "status": "OK",# "token": "tx-...",# "resourceKind": "query",# "resourcePath": "homes/admin/Examples/Trend.saiku",# "expiresAt": 1739102400000# }Wklej token do <saiku-embed token="..."> na stronie hostującej.
Granice TTL. Wartość domyślna to 72 godziny. Maksimum to 30 dni (720 godzin). Krótki TTL to najbezpieczniejsza wartość domyślna dla tokenu, który podróżuje przez arbitralne strony hostujące.
Limit na użytkownika. Każdy użytkownik może trzymać do 200 aktywnych tokenów jednocześnie. Unieważnij nieużywane tokeny, by zwolnić budżet.
Lista Twoich tokenów
curl 'https://YOUR-WORKSPACE.saiku.bi/rest/saiku/api/embed/tokens' \ -u admin:adminAdmini mogą wylistować wszystkie tokeny w systemie z ?all=true.
Unieważnienie tokenu
curl -X DELETE 'https://YOUR-WORKSPACE.saiku.bi/rest/saiku/api/embed/tokens/<token>' \ -u admin:adminUnieważnienie wchodzi w życie przy najbliższym żądaniu — nie ma sesji gościa, którą można by porwać.
Publiczne (anonimowe) embedy
Jeśli zasób powinien być czytelny bez tokenu — publiczny wykres na stronie głównej, otwarty dashboard na blogu — przełącz go w tryb publicznego osadzania:
curl -X POST 'https://YOUR-WORKSPACE.saiku.bi/rest/saiku/api/embed/public' \ -u admin:admin \ -H 'Content-Type: application/json' \ -d '{ "resourceKind": "query", "resourcePath": "shared/public-chart.saiku", "label": "Homepage chart" }'Wtedy strona hostująca pomija token całkowicie:
<saiku-embed server="https://YOUR-WORKSPACE.saiku.bi" path="shared/public-chart.saiku" render="chart"></saiku-embed>Lista i unieważnianie publicznych grantów
# Lista Twoich publicznych grantówcurl 'https://YOUR-WORKSPACE.saiku.bi/rest/saiku/api/embed/public' \ -u admin:admin
# Unieważnieniecurl -X DELETE 'https://YOUR-WORKSPACE.saiku.bi/rest/saiku/api/embed/public?kind=query&path=shared/public-chart.saiku' \ -u admin:adminReferencja atrybutów
| Atrybut | Domyślnie | Uwagi |
|---|---|---|
server | wymagane | Origin Twojego launchera Saiku, np. https://YOUR-WORKSPACE.saiku.bi |
path | wymagane | Ścieżka w repozytorium. Kończy się .saiku dla zapytań, .saikudash dla dashboardów |
kind | query | query lub dashboard |
token | (brak) | Token osadzania z POST /saiku/api/embed/tokens. Pomiń dla anonimowych odczytów publicznych |
render | table | Dla kind="query": table lub chart |
mode | bar | Dla render="chart": bar, line lub pie |
height | 400px | Wysokość CSS (min-height na renderowanej powierzchni) |
Atrybuty są reaktywne — komponent ponownie się renderuje, kiedy którykolwiek z nich się zmieni. W React, Vue lub Svelte powiązanie stanu z tymi propsami działa bez żadnych zabiegów.
Przykłady
Zapisane zapytanie jako tabela
<saiku-embed server="https://YOUR-WORKSPACE.saiku.bi" token="tx-..." path="homes/admin/Examples/Trend.saiku"></saiku-embed>Zapisane zapytanie jako wykres
<saiku-embed server="https://YOUR-WORKSPACE.saiku.bi" token="tx-..." path="homes/admin/Examples/Sales.saiku" render="chart" mode="bar" height="500px"></saiku-embed>Tryby wykresu:
bar(domyślny) — słupki kategorialne; pierwsza nie-liczbowa kolumna to kategoria, każda liczbowa kolumna staje się własną seriąline— ten sam układ, serie liniowepie— tylko pierwsza liczbowa kolumna; spada do „Brak serii liczbowych”, jeśli wynik jest wyłącznie tekstowy
Tooltipy pokazują sformatowane przez serwer napisy komórek ($1,234,
12.3% itd.), więc strona hostująca widzi te same podpisy, które
pokazałby workbench Saiku.
Zapisany dashboard
<saiku-embed server="https://YOUR-WORKSPACE.saiku.bi" token="tx-..." kind="dashboard" path="homes/admin/exec.saikudash" height="700px"></saiku-embed>Komponent pobiera układ dashboarda raz, a potem uruchamia jedno zapytanie na kafelek wykresu / KPI równolegle pod zakresem danych właściciela dashboarda. Typy kafelków w v1:
| Typ kafelka | Co się renderuje |
|---|---|
text | Tekst kafelka, jako akapit |
chart | Wykres ECharts (używa tile.chartType jako trybu) |
kpi | Jedna duża liczba z miary kafelka |
filter | Pomijany — widżety filtrów są tylko w workbenchu w v1 |
| inne | Placeholder „Niewspierany kafelek” |
Anonimowy publiczny embed
Gdy zasób ma publiczny grant, pomiń token:
<saiku-embed server="https://YOUR-WORKSPACE.saiku.bi" path="shared/public.saiku" render="chart" mode="pie"></saiku-embed>Wewnątrz React
import "@concepttocloud/saiku-embed";
export function SalesEmbed({ token }: { token: string }) { return ( <saiku-embed server="https://YOUR-WORKSPACE.saiku.bi" token={token} path="homes/admin/Examples/Sales.saiku" render="chart" mode="line" height="500px" /> );}Wewnątrz Vue
<template> <saiku-embed server="https://YOUR-WORKSPACE.saiku.bi" :token="token" path="homes/admin/Examples/Sales.saiku" render="chart" /></template>
<script setup lang="ts">import "@concepttocloud/saiku-embed";defineProps<{ token: string }>();</script>Motywowanie
Embed renderuje się wewnątrz
otwartego shadow root,
więc CSS strony hostującej nie może wyciec do środka, a CSS embeda
nie może wyciec na zewnątrz. Aby zmienić kolory, ustaw zmienne CSS
na selektorze saiku-embed strony hostującej:
saiku-embed { --saiku-embed-fg: #0f172a; --saiku-embed-bg: transparent; --saiku-embed-border: #cbd5e1; --saiku-embed-header-bg: #f1f5f9; --saiku-embed-tile-bg: #ffffff; --saiku-embed-row-hover: #e2e8f0; --saiku-embed-negative: #b91c1c; --saiku-embed-error: #b91c1c; --saiku-embed-muted: #64748b;}Tryb ciemny to po prostu media query, które przełącza te same zmienne:
@media (prefers-color-scheme: dark) { saiku-embed { --saiku-embed-fg: #f1f5f9; --saiku-embed-bg: #0f172a; --saiku-embed-border: #334155; --saiku-embed-header-bg: #1e293b; --saiku-embed-tile-bg: #1e293b; --saiku-embed-row-hover: #334155; }}Model bezpieczeństwa
Powierzchnia osadzania jest zaprojektowana z myślą o wrogich stronach hostujących — Twoje zapisane zapytanie może wylądować na stronie trzeciej, której nie kontrolujesz. Wartości domyślne to odzwierciedlają.
Tokeny są nieprzezroczyste, autorytatywne po stronie serwera, unieważnialne
- Tokeny to 256-bitowe losowe identyfikatory, kodowane Base64-URL — nie do odgadnięcia.
- Nie niosą żadnych osadzonych claimów: serwer trzyma autorytatywny rekord (rodzaj i ścieżka zasobu, migawka właściciela, wygaśnięcie, flaga unieważnienia) i sprawdza go przy każdym żądaniu. Unieważnienie wchodzi w życie przy najbliższym żądaniu — bez czekania na wygaśnięcie podpisu w stylu JWT.
- Każdy token przypina dokładnie jeden zasób. Odtworzenie tokenu
na dowolnym innym zasobie (zła ścieżka, zły rodzaj, wygasły,
unieważniony) zwraca ten sam nieprzezroczysty 401
EMBED_INVALID— sondy nie mogą się enumerować.
Transport tylko przez nagłówek
Token podróżuje jako nagłówek HTTP X-Saiku-Embed-Token. Celowo
nie akceptujemy parametrów zapytania ?token=…, ponieważ parametry
URL wyciekają do:
- logów dostępu serwleta
- logów proxy przed serwerem
- historii przeglądarki
- nagłówka
Refererwysyłanego na każdy wychodzący asset na stronie hostującej
Bundlowany JS odczytuje token z atrybutu <saiku-embed token="…">
i wysyła go jako nagłówek na każdym fetchu.
Izolacja ciasteczek cross-origin
Embed pobiera z credentials: "omit". Jeśli użytkownik strony
hostującej akurat jest zalogowany do Saiku w innej karcie, to
ciasteczko sesyjne nie płynie z odczytami embeda. Tokenem JEST
jedyny nośnik uwierzytelnienia na tej powierzchni.
Zakres danych właściciela
Zarówno tokeny, jak i publiczne granty zapisują migawkę listy ról
właściciela w momencie wystawienia tokenu/grantu i uruchamiają
zapytanie embeda pod tą tożsamością przez sessionService.runAs.
Publicznie osadzone zapytanie używające filtrów wstrzykiwanych z sesji
renderuje się z perspektywy udzielającego, a nie (pustego) anonimowego
domyślnego — to, co autoryzowałeś w momencie grantu, to dokładnie to,
co widzi publika.
Nagłówki odpowiedzi typu obrona w głąb
Każda odpowiedź embeda niesie:
X-Content-Type-Options: nosniff— przeglądarki nie mogą MIME-sniffować ciała JSON (które może nieść HTML kafelka tekstowego lub podpisy członków) i wykonywać go jako HTMLReferrer-Policy: no-referrer— nigdy nie ujawniaj tokenu przezRefererna wychodzących assetachCache-Control: no-store, max-age=0— osadzone dane biznesowe nie są nigdy buforowane przez proxy ani historię przeglądarki
Przyjazne błędy
Gdy fetch zawiedzie (wygasły token, unieważniony token, zła ścieżka,
brakujący publiczny grant), strona hostująca widzi ogólne „Ten embed
jest niedostępny.” — a nie surowe ciało EMBED_INVALID. Strona
hostująca to strona trzecia i nie musi wiedzieć, czy awaria to wygasły
token czy unieważnienie.
Rozmiar bundla
Około 213 KB po gzipie w chwili pisania — runtime custom element Svelte 5 + ECharts (rdzeń + bar / line / pie + cztery typowe komponenty, modularnie tree-shaken) + renderery embeda. Bundle jest agresywnie buforowany przez przeglądarkę; drugi embed na tym samym origin dostaje go natychmiast.
Ograniczenia
- Tylko format records. Format cellset matrix nie jest renderowany w v1.
- Kafelki filtrów są pomijane na dashboardach. Embed renderuje autorskie dane jak są, bez interaktywnego paska filtrów.
- Markdown w kafelkach tekstowych renderuje się jako zwykły tekst.
Nie bundlujemy
marked, by ograniczyć rozmiar. - Wyniki AI Query (
/ai/query) nie są jeszcze podłączone jako źródło embeda. Śledź wsaiku— kolejny PR dodakind="ai".
Zobacz też
- Uwierzytelnianie — generowanie kluczy API do programistycznego dostępu Saiku Cloud (inne niż tokeny osadzania)
- MCP — podłączanie LLM-ów do Twoich kostek