diff --git a/.changeset/add-swiftcx-integration.md b/.changeset/add-swiftcx-integration.md new file mode 100644 index 000000000..8e2bbb690 --- /dev/null +++ b/.changeset/add-swiftcx-integration.md @@ -0,0 +1,5 @@ +--- +"@gitbook/integration-swiftcx": minor +--- + +Add SwiftCX integration: initial setup with chat widget and configuration files diff --git a/bun.lock b/bun.lock index f11ec9b18..19b48cf76 100644 --- a/bun.lock +++ b/bun.lock @@ -569,6 +569,18 @@ "@gitbook/tsconfig": "workspace:*", }, }, + "integrations/swiftcx": { + "name": "@gitbook/integration-swiftcx", + "version": "0.0.1", + "dependencies": { + "@gitbook/api": "*", + "@gitbook/runtime": "*", + }, + "devDependencies": { + "@gitbook/cli": "workspace:*", + "@gitbook/tsconfig": "workspace:*", + }, + }, "integrations/toucantoco": { "name": "@gitbook/integration-toucantoco", "version": "1.1.0", @@ -1088,6 +1100,8 @@ "@gitbook/integration-slack": ["@gitbook/integration-slack@workspace:integrations/slack"], + "@gitbook/integration-swiftcx": ["@gitbook/integration-swiftcx@workspace:integrations/swiftcx"], + "@gitbook/integration-toucantoco": ["@gitbook/integration-toucantoco@workspace:integrations/toucantoco"], "@gitbook/integration-unify": ["@gitbook/integration-unify@workspace:integrations/unify"], diff --git a/integrations/swiftcx/CHANGELOG.md b/integrations/swiftcx/CHANGELOG.md new file mode 100644 index 000000000..0470d4d39 --- /dev/null +++ b/integrations/swiftcx/CHANGELOG.md @@ -0,0 +1,5 @@ +# @gitbook/integration-swiftcx + +## 0.1.0 + +- Initial SwiftCX integration: injects SwiftCX chat widget into published content. diff --git a/integrations/swiftcx/assets/icon.png b/integrations/swiftcx/assets/icon.png new file mode 100644 index 000000000..ba67be3ad Binary files /dev/null and b/integrations/swiftcx/assets/icon.png differ diff --git a/integrations/swiftcx/assets/swiftcx-preview.png b/integrations/swiftcx/assets/swiftcx-preview.png new file mode 100644 index 000000000..49c608529 Binary files /dev/null and b/integrations/swiftcx/assets/swiftcx-preview.png differ diff --git a/integrations/swiftcx/gitbook-manifest.yaml b/integrations/swiftcx/gitbook-manifest.yaml new file mode 100644 index 000000000..4ba3b3ad2 --- /dev/null +++ b/integrations/swiftcx/gitbook-manifest.yaml @@ -0,0 +1,75 @@ +name: swiftcx +title: SwiftCX Chat Widget +icon: ./assets/icon.png +previewImages: + - ./assets/swiftcx-preview.png +description: Add the SwiftCX chat widget to your published GitBook content. +visibility: private +script: ./src/index.ts +# The following scope(s) are available only to GitBook Staff +# See https://developer.gitbook.com/integrations/configurations#scopes +scopes: + - site:script:inject +organization: gitbook +contentSecurityPolicy: + script-src: | + https://dev.chatwidget.swiftcx.com + https://chatwidget.swiftcx.com + https://swiftcx.com; + style-src: | + 'unsafe-inline'; + frame-src: | + https://chatwidget.swiftcx.com + https://dev.chatwidget.swiftcx.com; + child-src: | + https://chatwidget.swiftcx.com + https://dev.chatwidget.swiftcx.com; + connect-src: | + https://sse.api.swiftcx.com + https://dev-sse.api.swiftcx.com; +summary: | + # Overview + + The SwiftCX integration for GitBook allows you to display the SwiftCX chat widget on your public documentation to connect and interact with your readers. + + # How it works + + Automatic chat widget on your documentation: Each of your connected GitBook spaces will fetch the SwiftCX chat widget script and inject it in your published content. + + # Configure + + You can configure the integration on single or multiple public spaces by navigating to the integrations in sub-navigation or org settings. You will then have to provide configuration values to finish the setup. +categories: + - analytics +configurations: + site: + properties: + customer_id: + type: string + title: Customer ID + description: This is a unique identifier for your customer in SwiftCX + agent_id: + type: string + title: Agent ID + description: This is a unique identifier for your agent in SwiftCX + access_token: + type: string + title: Access Token + description: 64-character access token for the above agent obtained from SwiftCX + origin: + type: string + title: Origin + description: HTTPS origin where this widget will be loaded from + environment: + type: string + enum: + - production + - development + title: Environment + description: 'production: chatwidget.swiftcx.com; development: dev.chatwidget.swiftcx.com' + required: + - customer_id + - agent_id + - access_token + - origin +target: site diff --git a/integrations/swiftcx/package.json b/integrations/swiftcx/package.json new file mode 100644 index 000000000..433db88c7 --- /dev/null +++ b/integrations/swiftcx/package.json @@ -0,0 +1,19 @@ +{ + "name": "@gitbook/integration-swiftcx", + "version": "0.0.1", + "private": true, + "dependencies": { + "@gitbook/api": "*", + "@gitbook/runtime": "*" + }, + "devDependencies": { + "@gitbook/cli": "workspace:*", + "@gitbook/tsconfig": "workspace:*" + }, + "scripts": { + "typecheck": "tsc --noEmit", + "publish-integrations-staging": "gitbook publish .", + "check": "gitbook check", + "publish-integrations": "gitbook publish ." + } +} diff --git a/integrations/swiftcx/src/index.ts b/integrations/swiftcx/src/index.ts new file mode 100644 index 000000000..6d722d288 --- /dev/null +++ b/integrations/swiftcx/src/index.ts @@ -0,0 +1,74 @@ +import { + createIntegration, + FetchPublishScriptEventCallback, + RuntimeContext, + RuntimeEnvironment, +} from '@gitbook/runtime'; + +type SwiftCXRuntimeContext = RuntimeContext< + RuntimeEnvironment< + {}, + { + customer_id?: string; + agent_id?: string; + access_token?: string; + origin?: string; + environment?: 'production' | 'development'; + } + > +>; + +export const handleFetchEvent: FetchPublishScriptEventCallback = async ( + event, + { environment }: SwiftCXRuntimeContext, +) => { + const config = environment.siteInstallation?.configuration || {}; + const customerId = config.customer_id; + const agentId = config.agent_id; + const accessToken = config.access_token; + const origin = config.origin; + const env = config.environment || 'production'; + + if (!customerId || !agentId || !accessToken || !origin) { + throw new Error( + `SwiftCX configuration is incomplete (ID: ${ + 'spaceId' in event ? event.spaceId : event.siteId + }). Required: customer_id, agent_id, access_token, origin.`, + ); + } + + const scriptUrl = + env === 'production' + ? 'https://chatwidget.swiftcx.com/src/embed/index.js' + : 'https://dev.chatwidget.swiftcx.com/src/embed/index.js'; + + const script = ` + (function() { + if (document.getElementById('scxChatWidget')) return; + var s = document.createElement('script'); + s.id = 'scxChatWidget'; + s.type = 'module'; + s.async = true; + s.crossOrigin = 'anonymous'; + s.setAttribute('data-customer-id', ${JSON.stringify(customerId)}); + s.setAttribute('data-agent-id', ${JSON.stringify(agentId)}); + s.setAttribute('data-access-token', ${JSON.stringify(accessToken)}); + s.setAttribute('data-origin', ${JSON.stringify(origin)}); + s.src = ${JSON.stringify(scriptUrl)}; + s.onerror = function(){ console.error('Failed to load SwiftCX ChatWidget'); }; + s.onload = function(){ console.log('SwiftCX ChatWidget loaded'); }; + document.head.appendChild(s); + })(); + `; + + return new Response(script, { + headers: { + 'Content-Type': 'application/javascript', + 'Cache-Control': 'max-age=86400', + }, + }); +}; + +export default createIntegration({ + fetch_published_script: handleFetchEvent, +}); diff --git a/integrations/swiftcx/tsconfig.json b/integrations/swiftcx/tsconfig.json new file mode 100644 index 000000000..1a48f875b --- /dev/null +++ b/integrations/swiftcx/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "@gitbook/tsconfig/integration.json" +}