Sohbet widget'ı (gömme)
nivq sohbetini tek bir tag ile herhangi bir siteye gömün — framework-bağımsız <nivq-chat> Web Component'i, secret'ınız tarayıcıya hiç gitmesin diye küçük bir token broker, tema ve yapılandırma.
nivq sohbetini herhangi bir siteye ya da uygulamaya tek bir tag ile ekleyebilirsiniz. Widget, framework-bağımsız bir Web Component'tir (<nivq-chat>) — düz HTML, React, Angular ya da Vue'da aynı şekilde çalışır — ve bir Shadow DOM içinde render olur; yani stilleri sayfanıza sızmaz, sayfanızın CSS'i de onu bozmaz.
<script type="module" src="https://cdn.jsdelivr.net/npm/nivorbit-chat-widget@1/dist/nivq-chat.js"></script>
<nivq-chat
agent-id="AJAN_ID"
api-base-url="https://api.example.com"
token-endpoint="/nivq-token">
</nivq-chat>token-endpoint, kendi sunucunuzda barındırdığınız tek parçadır. Nedenini görelim.
Kimlik doğrulama nasıl işliyor — önce bunu okuyun
Bir nivq API client'ı, client_credentials akışında kullanılan bir OAuth2 kimlik çiftidir (clientId + clientSecret):
clientId + clientSecret → POST /oauth2/token → kısa ömürlü erişim token'ı (~1s)
erişim token'ı (Bearer) → POST /v1/agents/{agentId}/conversations (sohbet)clientSecret uzun ömürlü bir kök kimlik bilgisidir — onu okuyan herkes token üretip kullanımınızı şişirebilir. Bu yüzden asla tarayıcıya gitmemelidir. Widget yalnızca kısa ömürlü bir erişim token'ı tutar ve bunu sizin backend'inizdeki küçük bir uç noktadan ("token broker") alır. Secret sunucunuzda kalır.
Tarayıcı (widget) ──POST──► sizin /nivq-token ──client_credentials──► NivQ /oauth2/token
▲ │
└──── { access_token } ◄────┘ (secret tarayıcıya asla dönmez)Secret'ı tarayıcıya asla göndermeyin
clientSecret'ı widget'a, istemci tarafı JS'e ya da herkese açık bir config'e koymayın. Sunucunuzda tutun; widget'a yalnızca kısa ömürlü token'ı verin.
1. Bir API client oluşturun
nivq'da bir API client oluşturun (clientId + clientSecret veren ekran). Bu anahtarda, widget'ın gömüleceği her web origin'ini İzinli origin'ler (Allowed origins) altında listeleyin — örn. https://app.acme.com — çünkü widget sohbeti doğrudan tarayıcıdan API'nize akıtır (bkz. aşağıdaki Gömme origin'inizi izinleyin).
2. Bir token broker çalıştırın
Broker, backend'inizdeki tek bir uç noktadır. Sizin son kullanıcınızı kendi oturumunuzla doğrular, secret'ı bir token'la takas eder ve yalnızca { access_token, expires_in } döner. Widget token'ı cache'leyip süresi dolmadan yeniler.
// Node / Express
import express from "express";
const app = express();
app.post("/nivq-token", async (req, res) => {
// 1. KENDİ kullanıcınızı doğrulayın (session/cookie/JWT). Girişli değilse reddedin.
// if (!req.user) return res.sendStatus(401);
// 2. Secret'ı kısa ömürlü bir token'la takas edin.
const r = await fetch(`${process.env.NIVQ_API_BASE}/oauth2/token`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: process.env.NIVQ_CLIENT_ID,
client_secret: process.env.NIVQ_CLIENT_SECRET, // sunucuda kalır
}),
});
if (!r.ok) return res.sendStatus(502);
const { access_token, expires_in } = await r.json();
// 3. YALNIZCA token'ı dönün — secret'ı asla.
res.json({ access_token, expires_in });
});// Spring Boot
@RestController
class NivqTokenBroker {
private final RestClient client = RestClient.create();
@Value("${nivq.api-base}") String apiBase;
@Value("${nivq.client-id}") String clientId;
@Value("${nivq.client-secret}") String clientSecret; // sunucuda kalır
@PostMapping("/nivq-token")
Map<String, Object> token(/* kimliği doğrulanmış principal'ı buraya enjekte edin */) {
var form = new LinkedMultiValueMap<String, String>();
form.add("grant_type", "client_credentials");
form.add("client_id", clientId);
form.add("client_secret", clientSecret);
var resp = client.post()
.uri(apiBase + "/oauth2/token")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body(form)
.retrieve()
.body(Map.class);
// Yalnızca token + süre dönün — secret'ı asla.
return Map.of("access_token", resp.get("access_token"),
"expires_in", resp.get("expires_in"));
}
}3. Widget'ı gömün
Düz HTML / herhangi bir site
<script type="module" src="https://cdn.jsdelivr.net/npm/nivorbit-chat-widget@1/dist/nivq-chat.js"></script>
<nivq-chat agent-id="…" api-base-url="https://api.example.com" token-endpoint="/nivq-token"></nivq-chat>React
Paketlenmiş uygulamalar için npm'den kurun:
npm i nivorbit-chat-widgetimport "nivorbit-chat-widget"; // <nivq-chat>'i kaydeder; kendi tiplerini getirir
export function Support() {
return (
<nivq-chat
agent-id="…"
api-base-url="https://api.example.com"
token-endpoint="/nivq-token"
/>
);
}Paket tip tanımlarını içerir, yani tag hazırdan tipli gelir. React 19'un katı JSX'inde editörünüz tag'i işaretlerse tek satırlık bir modül augmentation ekleyin:
declare module "react" {
namespace JSX {
interface IntrinsicElements {
"nivq-chat": any;
}
}
}Angular
// app.module.ts — custom element'lere izin ver
import { CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
@NgModule({ schemas: [CUSTOM_ELEMENTS_SCHEMA] })
export class AppModule {}<nivq-chat agent-id="…" api-base-url="https://api.example.com" token-endpoint="/nivq-token"></nivq-chat>Yapılandırma
| Özellik (attribute) | Zorunlu | Varsayılan | Açıklama |
|---|---|---|---|
agent-id | ✅ | — | Sohbet edilecek nivq ajanı |
token-endpoint | ✅ | — | Token broker URL'iniz |
api-base-url | ✅ | — | nivq sohbet API tabanı (kendi kurulumunuz) |
mode | fab | fab (yüzen başlatıcı) veya inline | |
target | — | Yalnızca inline: gömüleceği kabın CSS seçicisi | |
position | bottom-right | bottom-right veya bottom-left (FAB) | |
theme | auto | light, dark veya auto (OS'i izler) | |
locale | auto | tr, en veya auto (tarayıcı dili) | |
primary-color | NivQ markası | Hex (#6d28d9) ya da HSL üçlüsü (265 70% 50%) | |
accent-color | NivQ markası | Hex ya da HSL üçlüsü | |
radius | 0.625rem | Köşe yarıçapı (herhangi bir CSS uzunluğu) | |
logo-url | NivQ logosu | Marka logosunu değiştirir | |
brand-name | NivQ | Başlık / logo alt metni | |
launcher-label | — | FAB ikonunun yanındaki metin | |
greeting | — | Özel boş-durum başlığı | |
placeholder | yerelleştirilmiş | Girdi placeholder metni | |
external-user-id | — | Son kullanıcınızı ayırt eder (analitik / izolasyon) | |
persist | false | Sohbeti localStorage'da saklar | |
start-open | false | Yalnızca FAB: açılışta paneli açar |
JavaScript'ten de yönetebilirsiniz:
const el = document.querySelector("nivq-chat");
el.configure({ primaryColor: "#6d28d9", brandName: "Acme Assistant", mode: "inline" });Tema
Varsayılanlar nivq markasıdır. Ana token'ları geçersiz kılın; gerisi onlardan türetilir:
<nivq-chat … primary-color="#6d28d9" accent-color="#f59e0b" radius="14px"
logo-url="https://acme.com/logo.svg" brand-name="Acme Assistant"></nivq-chat>Gömme origin'inizi izinleyin
Widget sohbeti doğrudan tarayıcıdan api-base-url'inize akıtır (broker yalnızca token üretir), bu yüzden API'nizin sitenizin origin'ine izin vermesi gerekir. Bunu API client'ta ayarlayın: İzinli origin'ler altında widget'ın gömülü olduğu her origin'i listeleyin — şema://host[:port], yol ya da joker olmadan, örn. https://app.acme.com. Liste boşsa o anahtar için tarayıcı gömme kapanır (yalnızca sunucu-sunucu kullanım). Değişiklikler bir dakika içinde etkin olur.
On-prem
api-base-url'i kendi nivq kurulumunuza yönlendirin; aynı anahtar-bazlı İzinli origin'ler geçerlidir. Gömme alan adlarını merkezî yönetmeyi tercih ederseniz deployment-geneli CORS_ALLOWED_ORIGIN_PATTERNS ayarı da aynı işi yapar — bkz. Yapılandırma.
Gizlilik
Bir API client, entegrasyonunuzun tüm son kullanıcıları arasında paylaşılır ve sohbet geçmişi kişiye değil, client'a göre kapsanır. Bu yüzden widget her oturumu yerelde tutar (bellekte ya da persist açıkken localStorage'da) ve paylaşılan geçmişi yüklemez. Son kullanıcılarınızı nivq tarafında ayırt etmek için external-user-id kullanın.