gpthouse-embed 3.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +34 -0
- package/.eslintrc.cjs +14 -0
- package/.husky/pre-commit +18 -0
- package/.prettierignore +3 -0
- package/.prettierrc +8 -0
- package/README.md +357 -0
- package/base.json +21 -0
- package/dist/components/Badge.d.ts +10 -0
- package/dist/components/Badge.d.ts.map +1 -0
- package/dist/components/Bot.d.ts +134 -0
- package/dist/components/Bot.d.ts.map +1 -0
- package/dist/components/FeedbackContentDialog.d.ts +10 -0
- package/dist/components/FeedbackContentDialog.d.ts.map +1 -0
- package/dist/components/ImageUploadButton.d.ts +11 -0
- package/dist/components/ImageUploadButton.d.ts.map +1 -0
- package/dist/components/RecordAudioButton.d.ts +11 -0
- package/dist/components/RecordAudioButton.d.ts.map +1 -0
- package/dist/components/RichTreeView.d.ts +24 -0
- package/dist/components/RichTreeView.d.ts.map +1 -0
- package/dist/components/SendButton.d.ts +12 -0
- package/dist/components/SendButton.d.ts.map +1 -0
- package/dist/components/TreeViewDemo.d.ts +6 -0
- package/dist/components/TreeViewDemo.d.ts.map +1 -0
- package/dist/components/TypingBubble.d.ts +2 -0
- package/dist/components/TypingBubble.d.ts.map +1 -0
- package/dist/components/avatars/Avatar.d.ts +4 -0
- package/dist/components/avatars/Avatar.d.ts.map +1 -0
- package/dist/components/avatars/DefaultAvatar.d.ts +2 -0
- package/dist/components/avatars/DefaultAvatar.d.ts.map +1 -0
- package/dist/components/bubbles/AgentReasoningBubble.d.ts +16 -0
- package/dist/components/bubbles/AgentReasoningBubble.d.ts.map +1 -0
- package/dist/components/bubbles/BotBubble.d.ts +32 -0
- package/dist/components/bubbles/BotBubble.d.ts.map +1 -0
- package/dist/components/bubbles/FollowUpPromptBubble.d.ts +8 -0
- package/dist/components/bubbles/FollowUpPromptBubble.d.ts.map +1 -0
- package/dist/components/bubbles/GuestBubble.d.ts +16 -0
- package/dist/components/bubbles/GuestBubble.d.ts.map +1 -0
- package/dist/components/bubbles/LeadCaptureBubble.d.ts +20 -0
- package/dist/components/bubbles/LeadCaptureBubble.d.ts.map +1 -0
- package/dist/components/bubbles/LoadingBubble.d.ts +2 -0
- package/dist/components/bubbles/LoadingBubble.d.ts.map +1 -0
- package/dist/components/bubbles/SourceBubble.d.ts +8 -0
- package/dist/components/bubbles/SourceBubble.d.ts.map +1 -0
- package/dist/components/bubbles/StarterPromptBubble.d.ts +8 -0
- package/dist/components/bubbles/StarterPromptBubble.d.ts.map +1 -0
- package/dist/components/buttons/AttachmentUploadButton.d.ts +11 -0
- package/dist/components/buttons/AttachmentUploadButton.d.ts.map +1 -0
- package/dist/components/buttons/CancelButton.d.ts +10 -0
- package/dist/components/buttons/CancelButton.d.ts.map +1 -0
- package/dist/components/buttons/FeedbackButtons.d.ts +13 -0
- package/dist/components/buttons/FeedbackButtons.d.ts.map +1 -0
- package/dist/components/buttons/ImageUploadButton.d.ts +11 -0
- package/dist/components/buttons/ImageUploadButton.d.ts.map +1 -0
- package/dist/components/buttons/LeadCaptureButtons.d.ts +11 -0
- package/dist/components/buttons/LeadCaptureButtons.d.ts.map +1 -0
- package/dist/components/buttons/RecordAudioButton.d.ts +11 -0
- package/dist/components/buttons/RecordAudioButton.d.ts.map +1 -0
- package/dist/components/buttons/SendButton.d.ts +12 -0
- package/dist/components/buttons/SendButton.d.ts.map +1 -0
- package/dist/components/buttons/TTSButton.d.ts +10 -0
- package/dist/components/buttons/TTSButton.d.ts.map +1 -0
- package/dist/components/examples/TreeViewExample.d.ts +2 -0
- package/dist/components/examples/TreeViewExample.d.ts.map +1 -0
- package/dist/components/examples/index.d.ts +6 -0
- package/dist/components/examples/index.d.ts.map +1 -0
- package/dist/components/icons/AddImageIcon.d.ts +3 -0
- package/dist/components/icons/AddImageIcon.d.ts.map +1 -0
- package/dist/components/icons/AttachmentIcon.d.ts +3 -0
- package/dist/components/icons/AttachmentIcon.d.ts.map +1 -0
- package/dist/components/icons/CircleDotIcon.d.ts +3 -0
- package/dist/components/icons/CircleDotIcon.d.ts.map +1 -0
- package/dist/components/icons/ClipboardIcon.d.ts +3 -0
- package/dist/components/icons/ClipboardIcon.d.ts.map +1 -0
- package/dist/components/icons/DeleteIcon.d.ts +3 -0
- package/dist/components/icons/DeleteIcon.d.ts.map +1 -0
- package/dist/components/icons/RecordIcon.d.ts +3 -0
- package/dist/components/icons/RecordIcon.d.ts.map +1 -0
- package/dist/components/icons/SendIcon.d.ts +3 -0
- package/dist/components/icons/SendIcon.d.ts.map +1 -0
- package/dist/components/icons/SparklesIcon.d.ts +3 -0
- package/dist/components/icons/SparklesIcon.d.ts.map +1 -0
- package/dist/components/icons/SquareStopIcon.d.ts +3 -0
- package/dist/components/icons/SquareStopIcon.d.ts.map +1 -0
- package/dist/components/icons/ThumbsDownIcon.d.ts +3 -0
- package/dist/components/icons/ThumbsDownIcon.d.ts.map +1 -0
- package/dist/components/icons/ThumbsUpIcon.d.ts +3 -0
- package/dist/components/icons/ThumbsUpIcon.d.ts.map +1 -0
- package/dist/components/icons/TickIcon.d.ts +2 -0
- package/dist/components/icons/TickIcon.d.ts.map +1 -0
- package/dist/components/icons/TrashIcon.d.ts +3 -0
- package/dist/components/icons/TrashIcon.d.ts.map +1 -0
- package/dist/components/icons/VolumeIcon.d.ts +3 -0
- package/dist/components/icons/VolumeIcon.d.ts.map +1 -0
- package/dist/components/icons/XIcon.d.ts +5 -0
- package/dist/components/icons/XIcon.d.ts.map +1 -0
- package/dist/components/icons/index.d.ts +16 -0
- package/dist/components/icons/index.d.ts.map +1 -0
- package/dist/components/index.d.ts +3 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/inputs/textInput/components/FilePreview.d.ts +12 -0
- package/dist/components/inputs/textInput/components/FilePreview.d.ts.map +1 -0
- package/dist/components/inputs/textInput/components/ShortTextInput.d.ts +10 -0
- package/dist/components/inputs/textInput/components/ShortTextInput.d.ts.map +1 -0
- package/dist/components/inputs/textInput/components/TextInput.d.ts +29 -0
- package/dist/components/inputs/textInput/components/TextInput.d.ts.map +1 -0
- package/dist/components/inputs/textInput/index.d.ts +3 -0
- package/dist/components/inputs/textInput/index.d.ts.map +1 -0
- package/dist/components/treeview/DataTransformer.d.ts +7 -0
- package/dist/components/treeview/DataTransformer.d.ts.map +1 -0
- package/dist/components/treeview/RichTreeView.d.ts +24 -0
- package/dist/components/treeview/RichTreeView.d.ts.map +1 -0
- package/dist/components/treeview/TreeView.d.ts +22 -0
- package/dist/components/treeview/TreeView.d.ts.map +1 -0
- package/dist/components/treeview/WorkflowTreeView.d.ts +20 -0
- package/dist/components/treeview/WorkflowTreeView.d.ts.map +1 -0
- package/dist/constants.d.ts +3 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/features/bubble/components/Bubble.d.ts +5 -0
- package/dist/features/bubble/components/Bubble.d.ts.map +1 -0
- package/dist/features/bubble/components/BubbleButton.d.ts +16 -0
- package/dist/features/bubble/components/BubbleButton.d.ts.map +1 -0
- package/dist/features/bubble/components/Tooltip.d.ts +15 -0
- package/dist/features/bubble/components/Tooltip.d.ts.map +1 -0
- package/dist/features/bubble/components/index.d.ts +2 -0
- package/dist/features/bubble/components/index.d.ts.map +1 -0
- package/dist/features/bubble/index.d.ts +2 -0
- package/dist/features/bubble/index.d.ts.map +1 -0
- package/dist/features/bubble/types.d.ts +116 -0
- package/dist/features/bubble/types.d.ts.map +1 -0
- package/dist/features/full/components/Full.d.ts +7 -0
- package/dist/features/full/components/Full.d.ts.map +1 -0
- package/dist/features/full/components/index.d.ts +2 -0
- package/dist/features/full/components/index.d.ts.map +1 -0
- package/dist/features/full/index.d.ts +2 -0
- package/dist/features/full/index.d.ts.map +1 -0
- package/dist/features/popup/components/DisclaimerPopup.d.ts +18 -0
- package/dist/features/popup/components/DisclaimerPopup.d.ts.map +1 -0
- package/dist/features/popup/components/Popup.d.ts +8 -0
- package/dist/features/popup/components/Popup.d.ts.map +1 -0
- package/dist/features/popup/components/index.d.ts +3 -0
- package/dist/features/popup/components/index.d.ts.map +1 -0
- package/dist/features/popup/index.d.ts +2 -0
- package/dist/features/popup/index.d.ts.map +1 -0
- package/dist/features/popup/types.d.ts +8 -0
- package/dist/features/popup/types.d.ts.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/queries/sendMessageQuery.d.ts +110 -0
- package/dist/queries/sendMessageQuery.d.ts.map +1 -0
- package/dist/register.d.ts +2 -0
- package/dist/register.d.ts.map +1 -0
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils/audioRecording.d.ts +21 -0
- package/dist/utils/audioRecording.d.ts.map +1 -0
- package/dist/utils/chatInputHistory.d.ts +22 -0
- package/dist/utils/chatInputHistory.d.ts.map +1 -0
- package/dist/utils/index.d.ts +24 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/isMobileSignal.d.ts +2 -0
- package/dist/utils/isMobileSignal.d.ts.map +1 -0
- package/dist/web.d.ts +23 -0
- package/dist/web.d.ts.map +1 -0
- package/dist/web.js +1 -0
- package/dist/web.umd.js +1 -0
- package/dist/window.d.ts +30 -0
- package/dist/window.d.ts.map +1 -0
- package/images/ChatEmbed.gif +0 -0
- package/images/proxyserver.png +0 -0
- package/package.json +74 -0
- package/public/index.html +37 -0
- package/server.js +394 -0
package/dist/window.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { observersConfigType } from './components/Bot';
|
|
2
|
+
import { BubbleTheme } from './features/bubble/types';
|
|
3
|
+
type BotProps = {
|
|
4
|
+
chatflowid: string;
|
|
5
|
+
apiHost?: string;
|
|
6
|
+
onRequest?: (request: RequestInit) => Promise<void>;
|
|
7
|
+
chatflowConfig?: Record<string, unknown>;
|
|
8
|
+
observersConfig?: observersConfigType;
|
|
9
|
+
theme?: BubbleTheme;
|
|
10
|
+
};
|
|
11
|
+
export declare const initFull: (props: BotProps & {
|
|
12
|
+
id?: string;
|
|
13
|
+
}) => void;
|
|
14
|
+
export declare const init: (props: BotProps) => void;
|
|
15
|
+
export declare const destroy: () => void;
|
|
16
|
+
type Chatbot = {
|
|
17
|
+
initFull: typeof initFull;
|
|
18
|
+
init: typeof init;
|
|
19
|
+
destroy: typeof destroy;
|
|
20
|
+
};
|
|
21
|
+
export declare const parseChatbot: () => {
|
|
22
|
+
initFull: (props: BotProps & {
|
|
23
|
+
id?: string;
|
|
24
|
+
}) => void;
|
|
25
|
+
init: (props: BotProps) => void;
|
|
26
|
+
destroy: () => void;
|
|
27
|
+
};
|
|
28
|
+
export declare const injectChatbotInWindow: (bot: Chatbot) => void;
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=window.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"window.d.ts","sourceRoot":"","sources":["../src/window.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAGtD,KAAK,QAAQ,GAAG;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,eAAe,CAAC,EAAE,mBAAmB,CAAC;IACtC,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB,CAAC;AAIF,eAAO,MAAM,QAAQ,UAAW,QAAQ,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,SAWzD,CAAC;AAEF,eAAO,MAAM,IAAI,UAAW,QAAQ,SAMnC,CAAC;AAEF,eAAO,MAAM,OAAO,YAEnB,CAAC;AAEF,KAAK,OAAO,GAAG;IACb,QAAQ,EAAE,OAAO,QAAQ,CAAC;IAC1B,IAAI,EAAE,OAAO,IAAI,CAAC;IAClB,OAAO,EAAE,OAAO,OAAO,CAAC;CACzB,CAAC;AAQF,eAAO,MAAM,YAAY;sBArCO,QAAQ,GAAG;QAAE,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE;kBAa9B,QAAQ;;CA4BlC,CAAC;AAEH,eAAO,MAAM,qBAAqB,QAAS,OAAO,SAGjD,CAAC"}
|
|
Binary file
|
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "gpthouse-embed",
|
|
3
|
+
"version": "3.0.5",
|
|
4
|
+
"description": "Javascript library to display gpthouse chatbot on your website",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"dev": "cross-env NODE_ENV=development rollup --watch --config rollup.config.js",
|
|
10
|
+
"build": "cross-env NODE_ENV=production rollup --config rollup.config.js",
|
|
11
|
+
"lint": "eslint \"src/**/*.ts*\"",
|
|
12
|
+
"lint-fix": "eslint --fix \"src/**/*.ts*\"",
|
|
13
|
+
"format": "prettier --write \"**/*.{js,jsx,ts,tsx,md,mdx}\"",
|
|
14
|
+
"format:check": "prettier --check \"**/*.{js,jsx,ts,tsx,md,mdx}\"",
|
|
15
|
+
"prepare": "husky install",
|
|
16
|
+
"start": "node server.js"
|
|
17
|
+
},
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@babel/core": "^7.22.1",
|
|
21
|
+
"@microsoft/fetch-event-source": "^2.0.1",
|
|
22
|
+
"@ts-stack/markdown": "^1.4.0",
|
|
23
|
+
"axios": "^1.7.7",
|
|
24
|
+
"cors": "^2.8.5",
|
|
25
|
+
"cross-env": "^7.0.3",
|
|
26
|
+
"device-detector-js": "^3.0.3",
|
|
27
|
+
"dotenv": "^16.4.5",
|
|
28
|
+
"express": "^4.21.1",
|
|
29
|
+
"form-data": "^4.0.1",
|
|
30
|
+
"lodash": "^4.17.21",
|
|
31
|
+
"multer": "^1.4.5-lts.1",
|
|
32
|
+
"node-fetch": "^3.3.2",
|
|
33
|
+
"prettier": "^3.1.0",
|
|
34
|
+
"solid-element": "1.7.0",
|
|
35
|
+
"solid-js": "1.7.1",
|
|
36
|
+
"zod": "^3.22.4"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@babel/preset-typescript": "7.21.4",
|
|
40
|
+
"@rollup/plugin-babel": "6.0.3",
|
|
41
|
+
"@rollup/plugin-commonjs": "^25.0.0",
|
|
42
|
+
"@rollup/plugin-json": "^6.1.0",
|
|
43
|
+
"@rollup/plugin-node-resolve": "15.0.1",
|
|
44
|
+
"@rollup/plugin-terser": "0.4.0",
|
|
45
|
+
"@rollup/plugin-typescript": "11.0.0",
|
|
46
|
+
"@tailwindcss/typography": "^0.5.10",
|
|
47
|
+
"@types/lodash": "^4.14.195",
|
|
48
|
+
"@types/node": "18.15.11",
|
|
49
|
+
"@types/uuid": "^8.3.4",
|
|
50
|
+
"@typescript-eslint/eslint-plugin": "^5.57.0",
|
|
51
|
+
"@typescript-eslint/parser": "^5.57.0",
|
|
52
|
+
"autoprefixer": "10.4.14",
|
|
53
|
+
"babel-plugin-lodash": "^3.3.4",
|
|
54
|
+
"babel-preset-solid": "1.7.1",
|
|
55
|
+
"eslint": "^8.24.0",
|
|
56
|
+
"eslint-config-next": "13.2.4",
|
|
57
|
+
"eslint-config-prettier": "^9.0.0",
|
|
58
|
+
"eslint-plugin-prettier": "^5.0.1",
|
|
59
|
+
"eslint-plugin-react": "^7.26.1",
|
|
60
|
+
"eslint-plugin-solid": "0.12.0",
|
|
61
|
+
"husky": "^8.0.0",
|
|
62
|
+
"postcss": "8.4.21",
|
|
63
|
+
"react": "18.2.0",
|
|
64
|
+
"rollup": "3.23.0",
|
|
65
|
+
"rollup-plugin-livereload": "2.0.5",
|
|
66
|
+
"rollup-plugin-postcss": "4.0.2",
|
|
67
|
+
"rollup-plugin-serve": "2.0.2",
|
|
68
|
+
"rollup-plugin-typescript-paths": "1.4.0",
|
|
69
|
+
"rollup-plugin-uglify": "^6.0.4",
|
|
70
|
+
"tailwindcss": "3.3.1",
|
|
71
|
+
"typescript": "5.0.3",
|
|
72
|
+
"uuid": "^9.0.1"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
NOTE: This .html file (and public folder) is optional and can be safely deleted.
|
|
3
|
+
It serves only as a demo/test page.
|
|
4
|
+
|
|
5
|
+
The chatbot can be embedded directly on any allowed domain using the embed script that
|
|
6
|
+
is generated when starting the server.
|
|
7
|
+
-->
|
|
8
|
+
|
|
9
|
+
<!DOCTYPE html>
|
|
10
|
+
<html lang="en">
|
|
11
|
+
|
|
12
|
+
<head>
|
|
13
|
+
<meta charset="UTF-8">
|
|
14
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
15
|
+
<title>GptHouse Chatbot Widget</title>
|
|
16
|
+
</head>
|
|
17
|
+
|
|
18
|
+
<body>
|
|
19
|
+
<div id="root"></div>
|
|
20
|
+
<script type="module">
|
|
21
|
+
import Chatbot from './web.js'
|
|
22
|
+
|
|
23
|
+
// Example initialization:
|
|
24
|
+
// If your .env contains:
|
|
25
|
+
// agent1=xyz789-uvw456,https://example.com
|
|
26
|
+
// support=abc123-def456,https://example.com
|
|
27
|
+
// salesbot=ghi123-jkl456,https://example.com
|
|
28
|
+
//
|
|
29
|
+
// Then use the environment variable name as chatflowid:
|
|
30
|
+
Chatbot.init({
|
|
31
|
+
chatflowid: 'agent1', // or 'support', 'salesbot', etc.
|
|
32
|
+
apiHost: window.location.origin
|
|
33
|
+
})
|
|
34
|
+
</script>
|
|
35
|
+
</body>
|
|
36
|
+
|
|
37
|
+
</html>
|
package/server.js
ADDED
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
Error.stackTraceLimit = 0;
|
|
2
|
+
|
|
3
|
+
import express from 'express';
|
|
4
|
+
import cors from 'cors';
|
|
5
|
+
import fetch from 'node-fetch';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { fileURLToPath } from 'url';
|
|
8
|
+
import dotenv from 'dotenv';
|
|
9
|
+
import axios from 'axios';
|
|
10
|
+
import multer from 'multer';
|
|
11
|
+
import FormData from 'form-data';
|
|
12
|
+
import { generateEmbedScript } from './src/utils/embedScript.js';
|
|
13
|
+
|
|
14
|
+
dotenv.config();
|
|
15
|
+
|
|
16
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
17
|
+
const __dirname = path.dirname(__filename);
|
|
18
|
+
|
|
19
|
+
const API_HOST = process.env.API_HOST;
|
|
20
|
+
const GPTHOUSE_API_KEY = process.env.GPTHOUSE_API_KEY;
|
|
21
|
+
|
|
22
|
+
if (!API_HOST) {
|
|
23
|
+
console.error('API_HOST is not set in environment variables');
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (!GPTHOUSE_API_KEY) {
|
|
28
|
+
console.error('GPTHOUSE_API_KEY is not set in environment variables');
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const parseChatflows = () => {
|
|
33
|
+
try {
|
|
34
|
+
const chatflows = new Map();
|
|
35
|
+
|
|
36
|
+
// Get all environment variables that don't start with special prefixes
|
|
37
|
+
const chatflowVars = Object.entries(process.env).filter(([key]) => {
|
|
38
|
+
return (
|
|
39
|
+
!key.startsWith('_') &&
|
|
40
|
+
!key.startsWith('npm_') &&
|
|
41
|
+
!key.startsWith('yarn_') &&
|
|
42
|
+
!key.startsWith('VSCODE_') &&
|
|
43
|
+
key !== 'API_HOST' &&
|
|
44
|
+
key !== 'GPTHOUSE_API_KEY' &&
|
|
45
|
+
key !== 'PORT' &&
|
|
46
|
+
key !== 'HOST' &&
|
|
47
|
+
key !== 'BASE_URL' &&
|
|
48
|
+
key !== 'NODE_ENV'
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
if (chatflowVars.length === 0) {
|
|
53
|
+
console.error('No chatflow configurations found in environment variables');
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const defaultDomains = process.env.NODE_ENV === 'production' ? [] : ['http://localhost:5678'];
|
|
58
|
+
|
|
59
|
+
for (const [identifier, value] of chatflowVars) {
|
|
60
|
+
const parts = value.split(',').map((s) => s.trim());
|
|
61
|
+
const chatflowId = parts[0];
|
|
62
|
+
const configuredDomains = parts.length > 1 ? parts.slice(1) : [];
|
|
63
|
+
|
|
64
|
+
const domains = [...new Set([...defaultDomains, ...configuredDomains])];
|
|
65
|
+
|
|
66
|
+
if (!chatflowId) {
|
|
67
|
+
console.error(`Missing chatflow ID for ${identifier}`);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (domains.includes('*')) {
|
|
72
|
+
console.error(`\x1b[31mError: Wildcard (*) domains are not allowed in ${identifier}. This flow will not be accessible.\x1b[0m`);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
chatflows.set(identifier, { chatflowId, domains });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (chatflows.size === 0) {
|
|
80
|
+
console.error('No valid chatflow configurations found');
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return chatflows;
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.error('Failed to parse chatflow configurations:', error);
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const chatflows = parseChatflows();
|
|
92
|
+
|
|
93
|
+
const getChatflowDetails = (identifier) => {
|
|
94
|
+
let chatflow = chatflows.get(identifier);
|
|
95
|
+
|
|
96
|
+
if (!chatflow) {
|
|
97
|
+
const lowerIdentifier = identifier.toLowerCase();
|
|
98
|
+
for (const [key, value] of chatflows.entries()) {
|
|
99
|
+
if (key.toLowerCase() === lowerIdentifier) {
|
|
100
|
+
chatflow = value;
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (!chatflow) {
|
|
107
|
+
throw new Error(`Chatflow not found: ${identifier}`);
|
|
108
|
+
}
|
|
109
|
+
return chatflow;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const isValidUUID = (str) => {
|
|
113
|
+
const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
114
|
+
return uuidPattern.test(str);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const isValidChatflowConfig = (value) => {
|
|
118
|
+
if (!value) return false;
|
|
119
|
+
const parts = value.split(',').map((s) => s.trim());
|
|
120
|
+
return isValidUUID(parts[0]);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
console.info('\x1b[36m%s\x1b[0m', 'Configured chatflows:');
|
|
124
|
+
chatflows.forEach((config, identifier) => {
|
|
125
|
+
if (isValidChatflowConfig(config.chatflowId)) {
|
|
126
|
+
console.info('\x1b[36m%s\x1b[0m', ` ${identifier}: ${config.chatflowId} (${config.domains.join(', ')})`);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const isValidDomain = (origin, domains) => {
|
|
131
|
+
if (!origin) return true;
|
|
132
|
+
return domains.includes(origin);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const app = express();
|
|
136
|
+
app.use(express.json({ limit: '50mb' }));
|
|
137
|
+
app.use(express.urlencoded({ limit: '50mb', extended: true }));
|
|
138
|
+
|
|
139
|
+
app.use(
|
|
140
|
+
cors({
|
|
141
|
+
origin: true,
|
|
142
|
+
credentials: true,
|
|
143
|
+
methods: ['GET', 'POST', 'PUT', 'OPTIONS'],
|
|
144
|
+
allowedHeaders: ['*'],
|
|
145
|
+
}),
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
app.get('/', (_, res) => {
|
|
149
|
+
res.sendFile(path.join(__dirname, 'public', 'index.html'));
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
app.get('/web.js', (req, res) => {
|
|
153
|
+
const origin = req.headers.origin;
|
|
154
|
+
|
|
155
|
+
const allAllowedDomains = Array.from(chatflows.values()).flatMap((config) => config.domains);
|
|
156
|
+
|
|
157
|
+
if (!isValidDomain(origin, allAllowedDomains)) {
|
|
158
|
+
return res.status(403).send('Access Denied');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
res.set({
|
|
162
|
+
'Content-Type': 'application/javascript',
|
|
163
|
+
'Cache-Control': 'no-store, no-cache, must-revalidate, proxy-revalidate',
|
|
164
|
+
Pragma: 'no-cache',
|
|
165
|
+
Expires: '0',
|
|
166
|
+
});
|
|
167
|
+
res.sendFile(path.join(__dirname, 'dist', 'web.js'));
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const validateApiKey = (req, res, next) => {
|
|
171
|
+
if (req.path === '/web.js' || req.path === '/' || req.method === 'OPTIONS') {
|
|
172
|
+
return next();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (req.path.includes('/get-upload-file')) {
|
|
176
|
+
return next();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
let identifier;
|
|
180
|
+
const pathParts = req.path.split('/').filter(Boolean);
|
|
181
|
+
|
|
182
|
+
if (pathParts.length >= 3) {
|
|
183
|
+
identifier = pathParts[3];
|
|
184
|
+
} else {
|
|
185
|
+
identifier = req.query.chatflowId?.split('/')[0];
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (!identifier) {
|
|
189
|
+
return res.status(400).json({ error: 'Bad Request' });
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
let chatflow;
|
|
193
|
+
try {
|
|
194
|
+
chatflow = getChatflowDetails(identifier);
|
|
195
|
+
req.chatflow = chatflow;
|
|
196
|
+
} catch (error) {
|
|
197
|
+
return res.status(404).json({ error: 'Not Found' });
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const origin = req.headers.origin;
|
|
201
|
+
const userAgent = req.headers['user-agent'];
|
|
202
|
+
const acceptLanguage = req.headers['accept-language'];
|
|
203
|
+
const accept = req.headers['accept'];
|
|
204
|
+
const secFetchMode = req.headers['sec-fetch-mode'];
|
|
205
|
+
const secFetchSite = req.headers['sec-fetch-site'];
|
|
206
|
+
|
|
207
|
+
if (
|
|
208
|
+
userAgent &&
|
|
209
|
+
acceptLanguage &&
|
|
210
|
+
accept &&
|
|
211
|
+
secFetchMode === 'cors' &&
|
|
212
|
+
secFetchSite &&
|
|
213
|
+
['same-origin', 'same-site', 'cross-site'].includes(secFetchSite)
|
|
214
|
+
) {
|
|
215
|
+
if (isValidDomain(origin, chatflow.domains)) {
|
|
216
|
+
return next();
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const authHeader = req.headers.authorization;
|
|
221
|
+
if (authHeader && authHeader.startsWith('Bearer ') && authHeader.split(' ')[1] === GPTHOUSE_API_KEY) {
|
|
222
|
+
return next();
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return res.status(401).json({ error: 'Unauthorized' });
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
app.use(validateApiKey);
|
|
229
|
+
|
|
230
|
+
const proxyEndpoints = {
|
|
231
|
+
prediction: {
|
|
232
|
+
method: 'POST',
|
|
233
|
+
path: '/api/v1/prediction/:identifier',
|
|
234
|
+
target: '/api/v1/prediction',
|
|
235
|
+
},
|
|
236
|
+
config: {
|
|
237
|
+
method: 'GET',
|
|
238
|
+
path: '/api/v1/public-chatbotConfig/:identifier',
|
|
239
|
+
target: '/api/v1/public-chatbotConfig',
|
|
240
|
+
},
|
|
241
|
+
streaming: {
|
|
242
|
+
method: 'GET',
|
|
243
|
+
path: '/api/v1/chatflows-streaming/:identifier',
|
|
244
|
+
target: '/api/v1/chatflows-streaming',
|
|
245
|
+
},
|
|
246
|
+
files: {
|
|
247
|
+
method: 'GET',
|
|
248
|
+
path: '/api/v1/get-upload-file',
|
|
249
|
+
target: '/api/v1/get-upload-file',
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
const handleProxy = async (req, res, targetPath) => {
|
|
254
|
+
try {
|
|
255
|
+
let identifier = req.query.chatflowId?.split('/')[0] || req.path.split('/').pop() || null;
|
|
256
|
+
|
|
257
|
+
if (!identifier) {
|
|
258
|
+
return res.status(400).json({ error: 'Bad Request' });
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const chatflow = getChatflowDetails(identifier);
|
|
262
|
+
if (!chatflow) {
|
|
263
|
+
return res.status(404).json({ error: 'Not Found' });
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (req.query.chatId && req.query.fileName) {
|
|
267
|
+
const url = `${API_HOST}${targetPath}?chatflowId=${chatflow.chatflowId}&chatId=${req.query.chatId}&fileName=${req.query.fileName}`;
|
|
268
|
+
|
|
269
|
+
const response = await fetch(url, {
|
|
270
|
+
method: req.method,
|
|
271
|
+
headers: {
|
|
272
|
+
Authorization: `Bearer ${GPTHOUSE_API_KEY}`,
|
|
273
|
+
},
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
if (!response.ok) {
|
|
277
|
+
console.error(`File proxy error: ${response.status} ${response.statusText}`);
|
|
278
|
+
return res.status(response.status).json({ error: `File proxy error: ${response.statusText}` });
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const contentType = response.headers.get('content-type');
|
|
282
|
+
if (contentType) {
|
|
283
|
+
res.setHeader('Content-Type', contentType);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return response.body.pipe(res);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
let finalPath = `${targetPath}/${chatflow.chatflowId}`;
|
|
290
|
+
const url = `${API_HOST}${finalPath}`;
|
|
291
|
+
|
|
292
|
+
const response = await fetch(url, {
|
|
293
|
+
method: req.method,
|
|
294
|
+
headers: {
|
|
295
|
+
...(req.method !== 'GET' && { 'Content-Type': 'application/json' }),
|
|
296
|
+
Authorization: `Bearer ${GPTHOUSE_API_KEY}`,
|
|
297
|
+
},
|
|
298
|
+
body: req.method !== 'GET' ? JSON.stringify(req.body) : undefined,
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
if (!response.ok) {
|
|
302
|
+
console.error(`Proxy error: ${response.status} ${response.statusText}`);
|
|
303
|
+
return res.status(response.status).json({ error: `Proxy error: ${response.statusText}` });
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const contentType = response.headers.get('content-type');
|
|
307
|
+
|
|
308
|
+
if (contentType?.includes('image/') || contentType?.includes('audio/') || contentType?.includes('application/octet-stream')) {
|
|
309
|
+
res.setHeader('Content-Type', contentType);
|
|
310
|
+
return response.body.pipe(res);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (contentType?.includes('text/event-stream')) {
|
|
314
|
+
res.setHeader('Content-Type', 'text/event-stream');
|
|
315
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
316
|
+
res.setHeader('Connection', 'keep-alive');
|
|
317
|
+
return response.body.pipe(res);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (contentType?.includes('application/json')) {
|
|
321
|
+
const data = await response.json();
|
|
322
|
+
return res.json(data);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return response.body.pipe(res);
|
|
326
|
+
} catch (error) {
|
|
327
|
+
console.error('Proxy error:', error);
|
|
328
|
+
res.status(500).json({ error: 'Internal Server Error' });
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
Object.values(proxyEndpoints).forEach(({ method, path, target }) => {
|
|
333
|
+
app[method.toLowerCase()](path, (req, res) => {
|
|
334
|
+
return handleProxy(req, res, target);
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
const storage = multer.memoryStorage();
|
|
339
|
+
const upload = multer({ storage: storage });
|
|
340
|
+
|
|
341
|
+
app.post('/api/v1/attachments/:identifier/:chatId', upload.array('files'), async (req, res) => {
|
|
342
|
+
try {
|
|
343
|
+
const chatId = req.params.chatId;
|
|
344
|
+
if (!chatId) {
|
|
345
|
+
return res.status(400).json({ error: 'Bad Request' });
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (!req.files || req.files.length === 0) {
|
|
349
|
+
return res.status(400).json({ error: 'Bad Request' });
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const form = new FormData();
|
|
353
|
+
req.files.forEach((file) => {
|
|
354
|
+
form.append('files', file.buffer, {
|
|
355
|
+
filename: file.originalname,
|
|
356
|
+
contentType: file.mimetype,
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
const chatflow = req.chatflow;
|
|
361
|
+
const targetUrl = `${API_HOST}/api/v1/attachments/${chatflow.chatflowId}/${chatId}`;
|
|
362
|
+
|
|
363
|
+
const response = await axios.post(targetUrl, form, {
|
|
364
|
+
headers: {
|
|
365
|
+
...form.getHeaders(),
|
|
366
|
+
Authorization: `Bearer ${GPTHOUSE_API_KEY}`,
|
|
367
|
+
},
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
res.json(response.data);
|
|
371
|
+
} catch (error) {
|
|
372
|
+
console.error('Attachment upload error:', error);
|
|
373
|
+
res.status(500).json({ error: 'Internal Server Error' });
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
app.use((_req, res) => {
|
|
378
|
+
res.status(404).json({ error: 'Not Found' });
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
const PORT = process.env.PORT || 3001;
|
|
382
|
+
const HOST = process.env.HOST || '0.0.0.0';
|
|
383
|
+
|
|
384
|
+
const server = app.listen(PORT, HOST, () => {
|
|
385
|
+
const addr = server.address();
|
|
386
|
+
if (!addr || typeof addr === 'string') return;
|
|
387
|
+
|
|
388
|
+
const baseUrl =
|
|
389
|
+
process.env.BASE_URL || process.env.NODE_ENV === 'production'
|
|
390
|
+
? `https://${process.env.HOST || 'localhost'}`
|
|
391
|
+
: `http://${HOST === '0.0.0.0' ? 'localhost' : HOST}:${addr.port}`;
|
|
392
|
+
|
|
393
|
+
generateEmbedScript(baseUrl);
|
|
394
|
+
});
|