Files
2026-07-17 21:48:37 +05:30

6.2 KiB

Wiring the demo to the real IIOS service

This app talks to a pluggable BFF backend. Today it defaults to the in-memory mock; an IIOS adapter is implemented and switches on with one env var. This doc explains what's built, how to turn it on, and what still needs IIOS-side endpoints.

Reference: D:\iios\docs\IIOS_API_AND_SDK_GUIDE.md (endpoints, auth, DTOs).

The seam

Every BFF route calls getBackend() — never the store directly. BFF_BACKEND picks the impl:

apps/web/lib/backend/
  Backend.ts     the interface every route uses (all async)
  mock.ts        MockBackend — wraps the in-memory store (default)
  iios.ts        IiosBackend — proxies the real IIOS service
  index.ts       getBackend()  → mock | iios  (from BFF_BACKEND)
apps/web/lib/iios/
  client.ts      server-only IIOS REST client (dev-token auth, lazy token mint)
  map.ts         IIOS → SDK type mappers (toMessage / toInboxItem / toTicket)

The SDK, hooks, and every UI component are unchanged between mock and IIOS — all translation lives in lib/iios/map.ts.

Turn it on

  1. Run IIOS (from D:\iios, needs Docker for Postgres):
    docker compose up -d                                  # Postgres on :5434
    pnpm install
    pnpm -F @insignia/iios-service prisma:generate
    pnpm -F @insignia/iios-service prisma:migrate
    cd packages/iios-service && IIOS_DEV_TOKENS=1 pnpm start   # → :3200
    
  2. Point this app at it (apps/web/.env.local):
    BFF_BACKEND="iios"
    IIOS_URL="http://localhost:3200"
    IIOS_APP_ID="portal-demo"
    IIOS_USER_ID="james"
    IIOS_ORG_ID="org_A"
    
  3. pnpm dev → the BFF now mints a dev token and proxies IIOS. IIOS_* are server-only (no NEXT_PUBLIC_), so the token never reaches the browser.

Verified live (no Docker required)

The IIOS path has been run end-to-end against a real IIOS service + real Postgres, and the round-trip through this app's BFF was confirmed (a real ingested message came back through GET /api/bff/channels/:threadId/messages, and /api/bff/support/tickets returned IIOS's real data instead of the mock set). Docker isn't needed — a native PostgreSQL works.

Exact steps that worked on Windows (PostgreSQL 18 already installed):

# 1. Create the iios role + database on the existing Postgres (port 5432)
#    (as the postgres superuser)
psql -U postgres -c "CREATE ROLE iios LOGIN PASSWORD 'iios' CREATEDB;"
psql -U postgres -c "CREATE DATABASE iios OWNER iios;"

# 2. Point IIOS at it — packages/iios-service/.env
#    DATABASE_URL="postgresql://iios:iios@localhost:5432/iios?schema=public"
#    (the repo default is :5434 via docker-compose; :5432 is fine too)
#    JWT_SECRET=... · APP_SECRETS={"portal-demo":"dev-secret"} · IIOS_DEV_TOKENS=1

# 3. From D:\iios — allow prisma/esbuild builds in pnpm-workspace.yaml, then:
pnpm install
pnpm -r build
node packages/iios-service/node_modules/prisma/build/index.js generate  --schema packages/iios-service/prisma/schema.prisma
node packages/iios-service/node_modules/prisma/build/index.js migrate deploy --schema packages/iios-service/prisma/schema.prisma
node packages/iios-service/dist/main.js          # → iios-service listening on :3200

# 4. Point THIS app at it — apps/web/.env.local
BFF_BACKEND=iios
IIOS_URL=http://localhost:3200
# then `pnpm dev` (restart) — the BFF now mints a dev token and proxies IIOS.

The mapper in map.ts was validated against the real IIOS message shape ({interactionId, actorId, occurredAt, parts:[{kind,bodyText}]}). A fresh IIOS DB is empty and IIOS has no channel-directory endpoint, so bootstrap/listChannels stay on mock (the demo keeps its rich sidebar) while messages/inbox/tickets read from IIOS. Seed IIOS (via POST /v1/interactions/ingest or its smoke scripts) to see live data flow through.

What's wired vs. delegated

BFF method IIOS backing Notes
channelMessages / thread GET /v1/threads/:id/messages maps parts→body, parent links
send POST /v1/threads/:id/messages (+ idempotency-key)
listInbox / patchInbox GET/PATCH /v1/inbox/items
listTickets / getTicket / patchTicket /v1/support/tickets getTicket lists+finds (no single-GET documented)
replyTicket POST to the ticket's linked thread
bootstrap / listChannels / createChannel delegated to mock IIOS has no portal/channel-directory endpoint yet; build one, or compose from /v1/threads
react / pin / save delegated to mock IIOS has no reaction/pin/save endpoints; keep local or add them
search / notifications / availability delegated to mock add IIOS endpoints then swap

Delegating keeps the UI fully functional while you fill the gaps — each is a small, isolated change in iios.ts.

Auth

lib/iios/client.ts#mintToken() calls POST /v1/dev/token and caches the JWT (~100 min). For production, replace mintToken() with a real Session-Broker exchange (PPT), keep it server-side, and attach Authorization: Bearer … exactly as now.

Shape mapping

Keep this app's types.ts as the UI contract; translate in map.ts (already defensive about field names). Example:

export function toMessage(m: any, channelId: string): Message {
  const text = m.content ?? m.body ?? m.parts?.find(p => (p.kind ?? p.partType) === "TEXT")?.bodyText ?? "";
  return { id: String(m.id ?? m.interactionId), channelId,
    authorId: String(m.senderActorId ?? m.actorId ?? "unknown"),
    ts: Date.parse(m.sentAt ?? m.occurredAt) || Date.now(),
    body: String(text), parentId: m.parentInteractionId ?? null, reactions: [], deliveryState: "sent" };
}

Tenancy & safety (carried by IIOS)

IIOS enforces tenant isolation (cross-tenant → 403), fail-closed policy/consent gates, and idempotency. The BFF just forwards the scoped token — don't reimplement those client-side. The UI's optimistic updates already tolerate a rejected write (surface an error, refetch).

Realtime (optional upgrade)

Swap the mock useTyping + refetch for the IIOS Socket.IO client (iios-kernel-client's MessageSocket) and feed message/receipt events into the same hook state. The component tree doesn't change.