├── .dockerignore
├── .env.template
├── .github
└── FUNDING.yml
├── .gitignore
├── Dockerfile
├── LICENSE
├── README.md
├── README_CN.md
├── app.js
├── docker-compose.yml
├── package.json
├── pictures
├── api.png
├── token.png
└── usage.png
├── pnpm-lock.yaml
└── vercel.json
/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
3 | .env
4 | .git
5 | .gitignore
6 |
--------------------------------------------------------------------------------
/.env.template:
--------------------------------------------------------------------------------
1 | PORT=3000
2 | BOT_ID=default_bot_id
3 | BOT_CONFIG={"model_name_1": "bot_id_1", "model_name_2": "bot_id_2", "model_name_3": "bot_id_3"}
4 | COZE_API_BASE=api.coze.com
5 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: fatwang2
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 | .pnpm-debug.log*
9 |
10 | # Diagnostic reports (https://nodejs.org/api/report.html)
11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
12 |
13 | # Runtime data
14 | pids
15 | *.pid
16 | *.seed
17 | *.pid.lock
18 |
19 | # Directory for instrumented libs generated by jscoverage/JSCover
20 | lib-cov
21 |
22 | # Coverage directory used by tools like istanbul
23 | coverage
24 | *.lcov
25 |
26 | # nyc test coverage
27 | .nyc_output
28 |
29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30 | .grunt
31 |
32 | # Bower dependency directory (https://bower.io/)
33 | bower_components
34 |
35 | # node-waf configuration
36 | .lock-wscript
37 |
38 | # Compiled binary addons (https://nodejs.org/api/addons.html)
39 | build/Release
40 |
41 | # Dependency directories
42 | node_modules/
43 | jspm_packages/
44 |
45 | # Snowpack dependency directory (https://snowpack.dev/)
46 | web_modules/
47 |
48 | # TypeScript cache
49 | *.tsbuildinfo
50 |
51 | # Optional npm cache directory
52 | .npm
53 |
54 | # Optional eslint cache
55 | .eslintcache
56 |
57 | # Optional stylelint cache
58 | .stylelintcache
59 |
60 | # Microbundle cache
61 | .rpt2_cache/
62 | .rts2_cache_cjs/
63 | .rts2_cache_es/
64 | .rts2_cache_umd/
65 |
66 | # Optional REPL history
67 | .node_repl_history
68 |
69 | # Output of 'npm pack'
70 | *.tgz
71 |
72 | # Yarn Integrity file
73 | .yarn-integrity
74 |
75 | # dotenv environment variable files
76 | .env
77 | .env.development.local
78 | .env.test.local
79 | .env.production.local
80 | .env.local
81 |
82 | # parcel-bundler cache (https://parceljs.org/)
83 | .cache
84 | .parcel-cache
85 |
86 | # Next.js build output
87 | .next
88 | out
89 |
90 | # Nuxt.js build / generate output
91 | .nuxt
92 | dist
93 |
94 | # Gatsby files
95 | .cache/
96 | # Comment in the public line in if your project uses Gatsby and not Next.js
97 | # https://nextjs.org/blog/next-9-1#public-directory-support
98 | # public
99 |
100 | # vuepress build output
101 | .vuepress/dist
102 |
103 | # vuepress v2.x temp and cache directory
104 | .temp
105 | .cache
106 |
107 | # Docusaurus cache and generated files
108 | .docusaurus
109 |
110 | # Serverless directories
111 | .serverless/
112 |
113 | # FuseBox cache
114 | .fusebox/
115 |
116 | # DynamoDB Local files
117 | .dynamodb/
118 |
119 | # TernJS port file
120 | .tern-port
121 |
122 | # Stores VSCode versions used for testing VSCode extensions
123 | .vscode-test
124 |
125 | # yarn v2
126 | .yarn/cache
127 | .yarn/unplugged
128 | .yarn/build-state.yml
129 | .yarn/install-state.gz
130 | .pnp.*
131 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # 使用Node.js官方镜像作为基础镜像
2 | FROM node:18-alpine
3 |
4 | # 设置环境变量配置镜像源
5 | ENV NPM_CONFIG_REGISTRY=https://registry.npmmirror.com/
6 | ENV PNPM_REGISTRY=https://registry.npmmirror.com/
7 |
8 | # 设置工作目录
9 | WORKDIR /app
10 |
11 | # 复制package.json和pnpm-lock.yaml(如果存在)
12 | COPY package.json pnpm-lock.yaml* ./
13 |
14 | # 安装pnpm
15 | RUN npm install -g pnpm
16 |
17 | # 安装依赖
18 | RUN pnpm install
19 |
20 | # 复制项目文件
21 | COPY . .
22 |
23 | # 暴露端口
24 | EXPOSE 3000
25 |
26 | # 启动应用
27 | CMD ["pnpm", "start"]
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) [2024] [fatwang2]
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## C2O
2 | **English** · [中文](README_CN.md)
3 |
4 | **Use Coze on your favorite OpenAI client.**
5 |
6 | This project converts the Coze API to the OpenAI API format, giving you access to [Coze](https://www.coze.com) LLMs, knowledge base, plugins, and workflows within your preferred OpenAI clients.
7 |
8 | ## Features
9 | - Convert Coze API into an OpenAI API
10 | - Support streaming and blocking
11 | - Supports multi-bot switching
12 |
13 | ## Preparation
14 | 1. Register with [coze.com](https://www.coze.com) or [coze.cn](https://www.coze.cn)and obtain your API token
15 | 
16 |
17 | 2. Create your bot and publish it to the API
18 | 
19 |
20 | 3. Obtain the bot's ID,the number after the bot parameter, and configure it as an environment variable
21 | ```bash
22 | https://www.coze.com/space/73428668341****/bot/73428668*****
23 | ```
24 |
25 | ## Deployment
26 | ### Zeabur
27 | [](https://zeabur.com/templates/BZ515Z?referralCode=fatwang2)
28 |
29 | ### Vercel
30 | [](https://vercel.com/new/clone?repository-url=https://github.com/fatwang2/coze2openai&env=BOT_ID&envDescription=COZE_BOT_ID)
31 |
32 | **Note:** Vercel's serverless functions have a 10-second timeout limit.
33 |
34 | ### Railway
35 | [](https://railway.app/template/yM5tQL?referralCode=mDim7U)
36 |
37 | ### Docker Deployment
38 |
39 | 1. Ensure Docker and Docker Compose are installed on your machine.
40 |
41 | 2. Clone the project repository:
42 | ```
43 | git clone https://github.com/your-username/coze2openai.git
44 | cd coze2openai
45 | ```
46 |
47 | 3. Create and configure the `.env` file:
48 | ```
49 | cp .env.template .env
50 | ```
51 | Edit the `.env` file and fill in your BOT_ID and other necessary configurations.
52 |
53 | 4. Build and start the Docker container:
54 | ```
55 | docker-compose up -d
56 | ```
57 |
58 | 5. Visit `http://localhost:3000` to confirm that the service is running correctly.
59 |
60 | To stop the service, run:
61 | ```
62 | docker-compose down
63 | ```
64 |
65 | Note: The Dockerfile uses Taobao NPM mirror, you can comment out or replace other different mirror:
66 | ```Dockerfile
67 | ENV NPM_CONFIG_REGISTRY=https://registry.npmmirror.com/
68 | ENV PNPM_REGISTRY=https://registry.npmmirror.com/
69 | ```
70 |
71 | ### Local Deployment
72 | 1. Set the environment variable on `.env` file
73 | ```bash
74 | BOT_ID=xxxx
75 | ```
76 |
77 | 2. Install dependencies
78 | ```bash
79 | pnpm install
80 | ```
81 |
82 | 3. Run the project
83 | ```bash
84 | pnpm start
85 | ```
86 |
87 | ## Usage
88 | 1. OpenAI Clients
89 |
90 | 
91 |
92 | 2. Code
93 |
94 | ```JavaScript
95 | const response = await fetch('http://localhost:3000/v1/chat/completions', {
96 | method: 'POST',
97 | headers: {
98 | 'Content-Type': 'application/json',
99 | 'Authorization': 'Bearer YOUR_COZE_API_KEY',
100 | },
101 | body: JSON.stringify({
102 | model: 'model_name',
103 | messages: [
104 | { role: 'system', content: 'You are a helpful assistant.' },
105 | { role: 'user', content: 'Hello, how are you?' },
106 | ],
107 | }),
108 | });
109 |
110 | const data = await response.json();
111 | console.log(data);
112 | ```
113 | ## Environment Variable
114 | This project provides some additional configuration items set with environment variables:
115 |
116 | | Environment Variable | Required | Description | Example |
117 | | -------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
118 | | `BOT_ID` | Yes | The ID of the bot. Obtain it from the Develop page URL of your bot in Coze. The number after the bot parameter is the bot ID.| `73428668*****`|
119 | | `BOT_CONFIG` | No | Configure different models to correspond to different bot ids to enable fast bot switching on the client side. Models that are not included will request the default BOT_ID | `{"model_name_1": "bot_id_1", "model_name_2": "bot_id_2", "model_name_3": "bot_id_3"}`|
120 | | `COZE_API_BASE` | No | Choose coze.com or coze.cn | `api.coze.com, api.coze.cn`|
121 |
122 | ## Roadmap
123 | **Coming Soon**
124 | * Image support
125 | * Audio-to-text
126 | * Text-to-audio
127 |
128 | **Available Now**
129 | * Coze.cn
130 | * Multi-bot switching
131 | * Workflow, Plugins, Knowledge base
132 | * Continuous dialogue with the history of chat
133 | * Zeabur & Vercel & Railway deployment
134 | * Streaming & Blocking
135 | * Docker deployment
136 |
137 | ## Contact
138 | Feel free to reach out for any questions or feedback
139 |
140 | [X](https://sum4all.site/twitter)\
141 | [telegram](https://sum4all.site/telegram)
142 |
143 |
144 |
145 | ## License
146 | This project is licensed under the MIT License.
147 |
--------------------------------------------------------------------------------
/README_CN.md:
--------------------------------------------------------------------------------
1 | # C2O
2 | [English](README.md) · **中文**
3 |
4 | **在您喜爱的 OpenAI 客户端上使用 Coze.**
5 |
6 | 该项目将 Coze API 转换为 OpenAI API 格式,使您可以在您喜爱的 OpenAI 客户端中访问 [Coze](https://www.coze.com) 的LLMs、知识库、插件和工作流程.
7 |
8 | # 功能
9 | - 支持 Coze API 转换为 OpenAI API 格式
10 | - 支持流式、非流式输出
11 | - 支持多机器人快速切换
12 |
13 | # 准备工作
14 | 1. 在 [coze.com](https://www.coze.com)或 [coze.cn](https://www.coze.cn)注册并获取您的 API 令牌
15 | 
16 |
17 | 2. 创建您的机器人并发布到 API
18 | 
19 |
20 | 3. 获取机器人的 ID,即机器人参数后面的数字,并将其配置为环境变量
21 | ```bash
22 | https://www.coze.com/space/73428668341****/bot/73428668*****
23 | ```
24 |
25 | # 部署
26 | ## Zeabur
27 | [](https://zeabur.com/templates/BZ515Z?referralCode=fatwang2)
28 |
29 | ## Vercel
30 | [](https://vercel.com/new/clone?repository-url=https://github.com/fatwang2/coze2openai&env=BOT_ID&envDescription=COZE_BOT_ID)
31 |
32 | **注意:** Vercel 的无服务器函数有 10 秒的超时限制
33 |
34 | ## Railway
35 | [](https://railway.app/template/yM5tQL?referralCode=mDim7U)
36 |
37 | ## Docker部署
38 |
39 | 1. 确保您的机器上安装了Docker和Docker Compose。
40 |
41 | 2. 克隆项目仓库:
42 | ```
43 | git clone https://github.com/your-username/coze2openai.git
44 | cd coze2openai
45 | ```
46 |
47 | 3. 创建并配置`.env`文件:
48 | ```
49 | cp .env.template .env
50 | ```
51 | 编辑`.env`文件,填入您的BOT_ID和其他必要的配置。
52 |
53 | 4. 构建并启动Docker容器:
54 | ```
55 | docker-compose up -d
56 | ```
57 |
58 | 5. 访问`http://localhost:3000`来确认服务是否正常运行。
59 |
60 | 要停止服务,运行:
61 | ```
62 | docker-compose down
63 | ```
64 |
65 | 注意:Dockerfile中使用了淘宝NPM镜像源,你可以注释掉或替换其他源:
66 | ```Dockerfile
67 | ENV NPM_CONFIG_REGISTRY=https://registry.npmmirror.com/
68 | ENV PNPM_REGISTRY=https://registry.npmmirror.com/
69 | ```
70 |
71 | # 本地部署
72 | 1. 首先把`.env.template`文件复制改名为`.env`
73 |
74 | 2. 在 .env 文件上设置环境变量
75 | ```bash
76 | BOT_ID=xxxx
77 | ```
78 |
79 | 3. 安装依赖项
80 | ```bash
81 | pnpm install
82 | ```
83 |
84 | 4.运行项目
85 | ```bash
86 | pnpm start
87 | ```
88 |
89 | # 用法
90 | 1. OpenAI 三方客户端
91 |
92 | 
93 |
94 | 2. 代码里直接调用
95 |
96 | ```JavaScript
97 | const response = await fetch('http://localhost:3000/v1/chat/completions', {
98 | method: 'POST',
99 | headers: {
100 | 'Content-Type': 'application/json',
101 | 'Authorization': 'Bearer YOUR_COZE_API_KEY',
102 | },
103 | body: JSON.stringify({
104 | model: 'model_name',
105 | messages: [
106 | { role: 'system', content: 'You are a helpful assistant.' },
107 | { role: 'user', content: 'Hello, how are you?' },
108 | ],
109 | }),
110 | });
111 |
112 | const data = await response.json();
113 | console.log(data);
114 | ```
115 | # 环境变量
116 | 该项目提供了一些额外的配置项,通过环境变量设置:
117 |
118 | | 环境变量 | 必须的 | 描述 | 例子 |
119 | | -------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
120 | | `BOT_ID` | Yes | 机器人的 ID。从 Coze 中机器人的开发页面 URL 获取它。 bot参数后面的数字是bot id.| `73428668*****`|
121 | | `BOT_CONFIG` | No | 配置模型和机器人ID的对应关系,实现在客户端切换模型来调用不同的机器人的效果。如果调用不在配置文件的模型,则走默认的BOT_ID| `{"model_name_1": "bot_id_1", "model_name_2": "bot_id_2", "model_name_3": "bot_id_3"}`|
122 | | `COZE_API_BASE` | No | 选择coze.com或者coze.cn| `api.coze.com, api.coze.cn`|
123 |
124 |
125 | # 路线图
126 | **即将推出**
127 | * 图像支持
128 | * 音频转文字
129 | * 文本转语音
130 |
131 | **现在可用**
132 | * 支持 coze.cn
133 | * 多机器人切换
134 | * 连续对话,有对话历史
135 | * Zeabur&Vercel&Railway 部署
136 | * 流式和非流式传输
137 | * Workflow、插件、知识库
138 | * Docker 部署
139 |
140 | # 联系
141 | 如有任何问题或反馈,请随时联系
142 |
143 | [X](https://sum4all.site/twitter)\
144 | [telegram](https://sum4all.site/telegram)
145 |
146 |
147 |
148 | # 许可证
149 | 该项目在 MIT 许可证下获得许可.
150 |
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | import express from "express";
2 | import bodyParser from "body-parser";
3 | import dotenv from "dotenv";
4 | import fetch from "node-fetch";
5 | dotenv.config();
6 |
7 | const app = express();
8 | app.use(bodyParser.json());
9 | const coze_api_base = process.env.COZE_API_BASE || "api.coze.com";
10 | const default_bot_id = process.env.BOT_ID || "";
11 | const botConfig = process.env.BOT_CONFIG ? JSON.parse(process.env.BOT_CONFIG) : {};
12 | var corsHeaders = {
13 | "Access-Control-Allow-Origin": "*",
14 | "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
15 | "Access-Control-Allow-Headers":
16 | "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization",
17 | "Access-Control-Max-Age": "86400",
18 | };
19 |
20 | app.use((req, res, next) => {
21 | res.set(corsHeaders);
22 | if (req.method === 'OPTIONS') {
23 | return res.status(204).end();
24 | }
25 | console.log('Request Method:', req.method);
26 | console.log('Request Path:', req.path);
27 | next();
28 | });
29 | app.get("/", (req, res) => {
30 | res.send(`
31 |
32 |
Congratulations! Your project has been successfully deployed.
38 | 39 | 40 | `); 41 | }); 42 | 43 | app.post("/v1/chat/completions", async (req, res) => { 44 | const authHeader = 45 | req.headers["authorization"] || req.headers["Authorization"]; 46 | if (!authHeader) { 47 | return res.status(401).json({ 48 | code: 401, 49 | errmsg: "Unauthorized.", 50 | }); 51 | } else { 52 | const token = authHeader.split(" ")[1]; 53 | if (!token) { 54 | return res.status(401).json({ 55 | code: 401, 56 | errmsg: "Unauthorized.", 57 | }); 58 | } 59 | } 60 | try { 61 | const data = req.body; 62 | const messages = data.messages; 63 | const model = data.model; 64 | const user = data.user !== undefined ? data.user : "apiuser"; 65 | const chatHistory = []; 66 | for (let i = 0; i < messages.length - 1; i++) { 67 | const message = messages[i]; 68 | const role = message.role; 69 | const content = message.content; 70 | 71 | chatHistory.push({ 72 | role: role, 73 | content: content, 74 | content_type: "text" 75 | }); 76 | } 77 | 78 | const lastMessage = messages[messages.length - 1]; 79 | const queryString = lastMessage.content; 80 | const stream = data.stream !== undefined ? data.stream : false; 81 | let requestBody; 82 | const bot_id = model && botConfig[model] ? botConfig[model] : default_bot_id; 83 | 84 | requestBody = { 85 | query: queryString, 86 | stream: stream, 87 | conversation_id: "", 88 | user: user, 89 | bot_id: bot_id, 90 | chat_history: chatHistory 91 | }; 92 | const coze_api_url = `https://${coze_api_base}/open_api/v2/chat`; 93 | const resp = await fetch(coze_api_url, { 94 | method: "POST", 95 | headers: { 96 | "Content-Type": "application/json", 97 | Authorization: `Bearer ${authHeader.split(" ")[1]}`, 98 | }, 99 | body: JSON.stringify(requestBody), 100 | }); 101 | if (stream) { 102 | res.setHeader("Content-Type", "text/event-stream"); 103 | const stream = resp.body; 104 | let buffer = ""; 105 | 106 | stream.on("data", (chunk) => { 107 | buffer += chunk.toString(); 108 | let lines = buffer.split("\n"); 109 | 110 | for (let i = 0; i < lines.length - 1; i++) { 111 | let line = lines[i].trim(); 112 | 113 | if (!line.startsWith("data:")) continue; 114 | line = line.slice(5).trim(); 115 | let chunkObj; 116 | try { 117 | if (line.startsWith("{")) { 118 | chunkObj = JSON.parse(line); 119 | } else { 120 | continue; 121 | } 122 | } catch (error) { 123 | console.error("Error parsing chunk:", error); 124 | continue; 125 | } 126 | if (chunkObj.event === "message") { 127 | if ( 128 | chunkObj.message.role === "assistant" && 129 | chunkObj.message.type === "answer" 130 | ) { 131 | let chunkContent = chunkObj.message.content; 132 | 133 | if (chunkContent !== "") { 134 | const chunkId = `chatcmpl-${Date.now()}`; 135 | const chunkCreated = Math.floor(Date.now() / 1000); 136 | res.write( 137 | "data: " + 138 | JSON.stringify({ 139 | id: chunkId, 140 | object: "chat.completion.chunk", 141 | created: chunkCreated, 142 | model: data.model, 143 | choices: [ 144 | { 145 | index: 0, 146 | delta: { 147 | content: chunkContent, 148 | }, 149 | finish_reason: null, 150 | }, 151 | ], 152 | }) + 153 | "\n\n" 154 | ); 155 | } 156 | } 157 | } else if (chunkObj.event === "done") { 158 | const chunkId = `chatcmpl-${Date.now()}`; 159 | const chunkCreated = Math.floor(Date.now() / 1000); 160 | res.write( 161 | "data: " + 162 | JSON.stringify({ 163 | id: chunkId, 164 | object: "chat.completion.chunk", 165 | created: chunkCreated, 166 | model: data.model, 167 | choices: [ 168 | { 169 | index: 0, 170 | delta: {}, 171 | finish_reason: "stop", 172 | }, 173 | ], 174 | }) + 175 | "\n\n" 176 | ); 177 | res.write("data: [DONE]\n\n"); 178 | res.end(); 179 | } else if (chunkObj.event === "ping") { 180 | } else if (chunkObj.event === "error") { 181 | let errorMsg = chunkObj.code + " " + chunkObj.message; 182 | 183 | if(chunkObj.error_information) { 184 | errorMsg = chunkObj.error_information.err_msg; 185 | } 186 | 187 | console.error('Error: ', errorMsg); 188 | 189 | res.write( 190 | `data: ${JSON.stringify({ error: { 191 | error: "Unexpected response from Coze API.", 192 | message: errorMsg 193 | } 194 | })}\n\n` 195 | ); 196 | res.write("data: [DONE]\n\n"); 197 | res.end(); 198 | } 199 | } 200 | 201 | buffer = lines[lines.length - 1]; 202 | }); 203 | } else { 204 | resp 205 | .json() 206 | .then((data) => { 207 | if (data.code === 0 && data.msg === "success") { 208 | const messages = data.messages; 209 | const answerMessage = messages.find( 210 | (message) => 211 | message.role === "assistant" && message.type === "answer" 212 | ); 213 | 214 | if (answerMessage) { 215 | const result = answerMessage.content.trim(); 216 | const usageData = { 217 | prompt_tokens: 100, 218 | completion_tokens: 10, 219 | total_tokens: 110, 220 | }; 221 | const chunkId = `chatcmpl-${Date.now()}`; 222 | const chunkCreated = Math.floor(Date.now() / 1000); 223 | 224 | const formattedResponse = { 225 | id: chunkId, 226 | object: "chat.completion", 227 | created: chunkCreated, 228 | model: req.body.model, 229 | choices: [ 230 | { 231 | index: 0, 232 | message: { 233 | role: "assistant", 234 | content: result, 235 | }, 236 | logprobs: null, 237 | finish_reason: "stop", 238 | }, 239 | ], 240 | usage: usageData, 241 | system_fingerprint: "fp_2f57f81c11", 242 | }; 243 | const jsonResponse = JSON.stringify(formattedResponse, null, 2); 244 | res.set("Content-Type", "application/json"); 245 | res.send(jsonResponse); 246 | } else { 247 | res.status(500).json({ error: "No answer message found." }); 248 | } 249 | } else { 250 | console.error("Error:", data.msg); 251 | res 252 | .status(500) 253 | .json({ error: { 254 | error: "Unexpected response from Coze API.", 255 | message: data.msg 256 | } 257 | }); 258 | } 259 | }) 260 | .catch((error) => { 261 | console.error("Error parsing JSON:", error); 262 | res.status(500).json({ error: "Error parsing JSON response." }); 263 | }); 264 | } 265 | } catch (error) { 266 | console.error("Error:", error); 267 | } 268 | }); 269 | 270 | const server = app.listen(process.env.PORT || 3000, function () { 271 | let port = server.address().port 272 | console.log('Ready! Listening all IP, port: %s. Example: at http://localhost:%s', port, port) 273 | }); 274 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | app: 3 | build: . 4 | ports: 5 | - "3000:3000" 6 | environment: 7 | - PORT=3000 8 | - BOT_ID=${BOT_ID} 9 | - BOT_CONFIG=${BOT_CONFIG} 10 | - COZE_API_BASE=${COZE_API_BASE} 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "coze2openai", 3 | "version": "1.0.0", 4 | "description": "turn coze api into openai", 5 | "main": "app.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1", 9 | "start": "node app.js" 10 | }, 11 | "keywords": [], 12 | "author": "fatwang2", 13 | "license": "MIT", 14 | "dependencies": { 15 | "body-parser": "^1.20.2", 16 | "dotenv": "^16.3.1", 17 | "express": "^4.18.2", 18 | "node-fetch": "^3.3.2" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /pictures/api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fatwang2/coze2openai/d0dab84e1d90edf8245cfe7b20bd31d4955f1350/pictures/api.png -------------------------------------------------------------------------------- /pictures/token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fatwang2/coze2openai/d0dab84e1d90edf8245cfe7b20bd31d4955f1350/pictures/token.png -------------------------------------------------------------------------------- /pictures/usage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fatwang2/coze2openai/d0dab84e1d90edf8245cfe7b20bd31d4955f1350/pictures/usage.png -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | body-parser: 12 | specifier: ^1.20.2 13 | version: 1.20.2 14 | dotenv: 15 | specifier: ^16.3.1 16 | version: 16.3.1 17 | express: 18 | specifier: ^4.18.2 19 | version: 4.18.2 20 | node-fetch: 21 | specifier: ^3.3.2 22 | version: 3.3.2 23 | 24 | packages: 25 | 26 | accepts@1.3.8: 27 | resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} 28 | engines: {node: '>= 0.6'} 29 | 30 | array-flatten@1.1.1: 31 | resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} 32 | 33 | body-parser@1.20.1: 34 | resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==} 35 | engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} 36 | 37 | body-parser@1.20.2: 38 | resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} 39 | engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} 40 | 41 | bytes@3.1.2: 42 | resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} 43 | engines: {node: '>= 0.8'} 44 | 45 | call-bind@1.0.2: 46 | resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} 47 | 48 | content-disposition@0.5.4: 49 | resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} 50 | engines: {node: '>= 0.6'} 51 | 52 | content-type@1.0.5: 53 | resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} 54 | engines: {node: '>= 0.6'} 55 | 56 | cookie-signature@1.0.6: 57 | resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} 58 | 59 | cookie@0.5.0: 60 | resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} 61 | engines: {node: '>= 0.6'} 62 | 63 | data-uri-to-buffer@4.0.1: 64 | resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} 65 | engines: {node: '>= 12'} 66 | 67 | debug@2.6.9: 68 | resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} 69 | peerDependencies: 70 | supports-color: '*' 71 | peerDependenciesMeta: 72 | supports-color: 73 | optional: true 74 | 75 | depd@2.0.0: 76 | resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} 77 | engines: {node: '>= 0.8'} 78 | 79 | destroy@1.2.0: 80 | resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} 81 | engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} 82 | 83 | dotenv@16.3.1: 84 | resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==} 85 | engines: {node: '>=12'} 86 | 87 | ee-first@1.1.1: 88 | resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} 89 | 90 | encodeurl@1.0.2: 91 | resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} 92 | engines: {node: '>= 0.8'} 93 | 94 | escape-html@1.0.3: 95 | resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} 96 | 97 | etag@1.8.1: 98 | resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} 99 | engines: {node: '>= 0.6'} 100 | 101 | express@4.18.2: 102 | resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} 103 | engines: {node: '>= 0.10.0'} 104 | 105 | fetch-blob@3.2.0: 106 | resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} 107 | engines: {node: ^12.20 || >= 14.13} 108 | 109 | finalhandler@1.2.0: 110 | resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} 111 | engines: {node: '>= 0.8'} 112 | 113 | formdata-polyfill@4.0.10: 114 | resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} 115 | engines: {node: '>=12.20.0'} 116 | 117 | forwarded@0.2.0: 118 | resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} 119 | engines: {node: '>= 0.6'} 120 | 121 | fresh@0.5.2: 122 | resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} 123 | engines: {node: '>= 0.6'} 124 | 125 | function-bind@1.1.1: 126 | resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} 127 | 128 | get-intrinsic@1.2.1: 129 | resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==} 130 | 131 | has-proto@1.0.1: 132 | resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} 133 | engines: {node: '>= 0.4'} 134 | 135 | has-symbols@1.0.3: 136 | resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} 137 | engines: {node: '>= 0.4'} 138 | 139 | has@1.0.3: 140 | resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} 141 | engines: {node: '>= 0.4.0'} 142 | 143 | http-errors@2.0.0: 144 | resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} 145 | engines: {node: '>= 0.8'} 146 | 147 | iconv-lite@0.4.24: 148 | resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} 149 | engines: {node: '>=0.10.0'} 150 | 151 | inherits@2.0.4: 152 | resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} 153 | 154 | ipaddr.js@1.9.1: 155 | resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} 156 | engines: {node: '>= 0.10'} 157 | 158 | media-typer@0.3.0: 159 | resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} 160 | engines: {node: '>= 0.6'} 161 | 162 | merge-descriptors@1.0.1: 163 | resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} 164 | 165 | methods@1.1.2: 166 | resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} 167 | engines: {node: '>= 0.6'} 168 | 169 | mime-db@1.52.0: 170 | resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} 171 | engines: {node: '>= 0.6'} 172 | 173 | mime-types@2.1.35: 174 | resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} 175 | engines: {node: '>= 0.6'} 176 | 177 | mime@1.6.0: 178 | resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} 179 | engines: {node: '>=4'} 180 | hasBin: true 181 | 182 | ms@2.0.0: 183 | resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} 184 | 185 | ms@2.1.3: 186 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 187 | 188 | negotiator@0.6.3: 189 | resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} 190 | engines: {node: '>= 0.6'} 191 | 192 | node-domexception@1.0.0: 193 | resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} 194 | engines: {node: '>=10.5.0'} 195 | 196 | node-fetch@3.3.2: 197 | resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} 198 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 199 | 200 | object-inspect@1.12.3: 201 | resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} 202 | 203 | on-finished@2.4.1: 204 | resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} 205 | engines: {node: '>= 0.8'} 206 | 207 | parseurl@1.3.3: 208 | resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} 209 | engines: {node: '>= 0.8'} 210 | 211 | path-to-regexp@0.1.7: 212 | resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} 213 | 214 | proxy-addr@2.0.7: 215 | resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} 216 | engines: {node: '>= 0.10'} 217 | 218 | qs@6.11.0: 219 | resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} 220 | engines: {node: '>=0.6'} 221 | 222 | range-parser@1.2.1: 223 | resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} 224 | engines: {node: '>= 0.6'} 225 | 226 | raw-body@2.5.1: 227 | resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==} 228 | engines: {node: '>= 0.8'} 229 | 230 | raw-body@2.5.2: 231 | resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} 232 | engines: {node: '>= 0.8'} 233 | 234 | safe-buffer@5.2.1: 235 | resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} 236 | 237 | safer-buffer@2.1.2: 238 | resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} 239 | 240 | send@0.18.0: 241 | resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} 242 | engines: {node: '>= 0.8.0'} 243 | 244 | serve-static@1.15.0: 245 | resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} 246 | engines: {node: '>= 0.8.0'} 247 | 248 | setprototypeof@1.2.0: 249 | resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} 250 | 251 | side-channel@1.0.4: 252 | resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} 253 | 254 | statuses@2.0.1: 255 | resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} 256 | engines: {node: '>= 0.8'} 257 | 258 | toidentifier@1.0.1: 259 | resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} 260 | engines: {node: '>=0.6'} 261 | 262 | type-is@1.6.18: 263 | resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} 264 | engines: {node: '>= 0.6'} 265 | 266 | unpipe@1.0.0: 267 | resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} 268 | engines: {node: '>= 0.8'} 269 | 270 | utils-merge@1.0.1: 271 | resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} 272 | engines: {node: '>= 0.4.0'} 273 | 274 | vary@1.1.2: 275 | resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} 276 | engines: {node: '>= 0.8'} 277 | 278 | web-streams-polyfill@3.2.1: 279 | resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==} 280 | engines: {node: '>= 8'} 281 | 282 | snapshots: 283 | 284 | accepts@1.3.8: 285 | dependencies: 286 | mime-types: 2.1.35 287 | negotiator: 0.6.3 288 | 289 | array-flatten@1.1.1: {} 290 | 291 | body-parser@1.20.1: 292 | dependencies: 293 | bytes: 3.1.2 294 | content-type: 1.0.5 295 | debug: 2.6.9 296 | depd: 2.0.0 297 | destroy: 1.2.0 298 | http-errors: 2.0.0 299 | iconv-lite: 0.4.24 300 | on-finished: 2.4.1 301 | qs: 6.11.0 302 | raw-body: 2.5.1 303 | type-is: 1.6.18 304 | unpipe: 1.0.0 305 | transitivePeerDependencies: 306 | - supports-color 307 | 308 | body-parser@1.20.2: 309 | dependencies: 310 | bytes: 3.1.2 311 | content-type: 1.0.5 312 | debug: 2.6.9 313 | depd: 2.0.0 314 | destroy: 1.2.0 315 | http-errors: 2.0.0 316 | iconv-lite: 0.4.24 317 | on-finished: 2.4.1 318 | qs: 6.11.0 319 | raw-body: 2.5.2 320 | type-is: 1.6.18 321 | unpipe: 1.0.0 322 | transitivePeerDependencies: 323 | - supports-color 324 | 325 | bytes@3.1.2: {} 326 | 327 | call-bind@1.0.2: 328 | dependencies: 329 | function-bind: 1.1.1 330 | get-intrinsic: 1.2.1 331 | 332 | content-disposition@0.5.4: 333 | dependencies: 334 | safe-buffer: 5.2.1 335 | 336 | content-type@1.0.5: {} 337 | 338 | cookie-signature@1.0.6: {} 339 | 340 | cookie@0.5.0: {} 341 | 342 | data-uri-to-buffer@4.0.1: {} 343 | 344 | debug@2.6.9: 345 | dependencies: 346 | ms: 2.0.0 347 | 348 | depd@2.0.0: {} 349 | 350 | destroy@1.2.0: {} 351 | 352 | dotenv@16.3.1: {} 353 | 354 | ee-first@1.1.1: {} 355 | 356 | encodeurl@1.0.2: {} 357 | 358 | escape-html@1.0.3: {} 359 | 360 | etag@1.8.1: {} 361 | 362 | express@4.18.2: 363 | dependencies: 364 | accepts: 1.3.8 365 | array-flatten: 1.1.1 366 | body-parser: 1.20.1 367 | content-disposition: 0.5.4 368 | content-type: 1.0.5 369 | cookie: 0.5.0 370 | cookie-signature: 1.0.6 371 | debug: 2.6.9 372 | depd: 2.0.0 373 | encodeurl: 1.0.2 374 | escape-html: 1.0.3 375 | etag: 1.8.1 376 | finalhandler: 1.2.0 377 | fresh: 0.5.2 378 | http-errors: 2.0.0 379 | merge-descriptors: 1.0.1 380 | methods: 1.1.2 381 | on-finished: 2.4.1 382 | parseurl: 1.3.3 383 | path-to-regexp: 0.1.7 384 | proxy-addr: 2.0.7 385 | qs: 6.11.0 386 | range-parser: 1.2.1 387 | safe-buffer: 5.2.1 388 | send: 0.18.0 389 | serve-static: 1.15.0 390 | setprototypeof: 1.2.0 391 | statuses: 2.0.1 392 | type-is: 1.6.18 393 | utils-merge: 1.0.1 394 | vary: 1.1.2 395 | transitivePeerDependencies: 396 | - supports-color 397 | 398 | fetch-blob@3.2.0: 399 | dependencies: 400 | node-domexception: 1.0.0 401 | web-streams-polyfill: 3.2.1 402 | 403 | finalhandler@1.2.0: 404 | dependencies: 405 | debug: 2.6.9 406 | encodeurl: 1.0.2 407 | escape-html: 1.0.3 408 | on-finished: 2.4.1 409 | parseurl: 1.3.3 410 | statuses: 2.0.1 411 | unpipe: 1.0.0 412 | transitivePeerDependencies: 413 | - supports-color 414 | 415 | formdata-polyfill@4.0.10: 416 | dependencies: 417 | fetch-blob: 3.2.0 418 | 419 | forwarded@0.2.0: {} 420 | 421 | fresh@0.5.2: {} 422 | 423 | function-bind@1.1.1: {} 424 | 425 | get-intrinsic@1.2.1: 426 | dependencies: 427 | function-bind: 1.1.1 428 | has: 1.0.3 429 | has-proto: 1.0.1 430 | has-symbols: 1.0.3 431 | 432 | has-proto@1.0.1: {} 433 | 434 | has-symbols@1.0.3: {} 435 | 436 | has@1.0.3: 437 | dependencies: 438 | function-bind: 1.1.1 439 | 440 | http-errors@2.0.0: 441 | dependencies: 442 | depd: 2.0.0 443 | inherits: 2.0.4 444 | setprototypeof: 1.2.0 445 | statuses: 2.0.1 446 | toidentifier: 1.0.1 447 | 448 | iconv-lite@0.4.24: 449 | dependencies: 450 | safer-buffer: 2.1.2 451 | 452 | inherits@2.0.4: {} 453 | 454 | ipaddr.js@1.9.1: {} 455 | 456 | media-typer@0.3.0: {} 457 | 458 | merge-descriptors@1.0.1: {} 459 | 460 | methods@1.1.2: {} 461 | 462 | mime-db@1.52.0: {} 463 | 464 | mime-types@2.1.35: 465 | dependencies: 466 | mime-db: 1.52.0 467 | 468 | mime@1.6.0: {} 469 | 470 | ms@2.0.0: {} 471 | 472 | ms@2.1.3: {} 473 | 474 | negotiator@0.6.3: {} 475 | 476 | node-domexception@1.0.0: {} 477 | 478 | node-fetch@3.3.2: 479 | dependencies: 480 | data-uri-to-buffer: 4.0.1 481 | fetch-blob: 3.2.0 482 | formdata-polyfill: 4.0.10 483 | 484 | object-inspect@1.12.3: {} 485 | 486 | on-finished@2.4.1: 487 | dependencies: 488 | ee-first: 1.1.1 489 | 490 | parseurl@1.3.3: {} 491 | 492 | path-to-regexp@0.1.7: {} 493 | 494 | proxy-addr@2.0.7: 495 | dependencies: 496 | forwarded: 0.2.0 497 | ipaddr.js: 1.9.1 498 | 499 | qs@6.11.0: 500 | dependencies: 501 | side-channel: 1.0.4 502 | 503 | range-parser@1.2.1: {} 504 | 505 | raw-body@2.5.1: 506 | dependencies: 507 | bytes: 3.1.2 508 | http-errors: 2.0.0 509 | iconv-lite: 0.4.24 510 | unpipe: 1.0.0 511 | 512 | raw-body@2.5.2: 513 | dependencies: 514 | bytes: 3.1.2 515 | http-errors: 2.0.0 516 | iconv-lite: 0.4.24 517 | unpipe: 1.0.0 518 | 519 | safe-buffer@5.2.1: {} 520 | 521 | safer-buffer@2.1.2: {} 522 | 523 | send@0.18.0: 524 | dependencies: 525 | debug: 2.6.9 526 | depd: 2.0.0 527 | destroy: 1.2.0 528 | encodeurl: 1.0.2 529 | escape-html: 1.0.3 530 | etag: 1.8.1 531 | fresh: 0.5.2 532 | http-errors: 2.0.0 533 | mime: 1.6.0 534 | ms: 2.1.3 535 | on-finished: 2.4.1 536 | range-parser: 1.2.1 537 | statuses: 2.0.1 538 | transitivePeerDependencies: 539 | - supports-color 540 | 541 | serve-static@1.15.0: 542 | dependencies: 543 | encodeurl: 1.0.2 544 | escape-html: 1.0.3 545 | parseurl: 1.3.3 546 | send: 0.18.0 547 | transitivePeerDependencies: 548 | - supports-color 549 | 550 | setprototypeof@1.2.0: {} 551 | 552 | side-channel@1.0.4: 553 | dependencies: 554 | call-bind: 1.0.2 555 | get-intrinsic: 1.2.1 556 | object-inspect: 1.12.3 557 | 558 | statuses@2.0.1: {} 559 | 560 | toidentifier@1.0.1: {} 561 | 562 | type-is@1.6.18: 563 | dependencies: 564 | media-typer: 0.3.0 565 | mime-types: 2.1.35 566 | 567 | unpipe@1.0.0: {} 568 | 569 | utils-merge@1.0.1: {} 570 | 571 | vary@1.1.2: {} 572 | 573 | web-streams-polyfill@3.2.1: {} 574 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "builds": [ 4 | { 5 | "src": "app.js", 6 | "use": "@vercel/node" 7 | } 8 | ], 9 | "routes": [ 10 | { 11 | "src": "/v1/(.*)", 12 | "dest": "/app.js", 13 | "methods": ["OPTIONS", "POST"] 14 | }, 15 | { 16 | "src": "/", 17 | "dest": "/app.js", 18 | "methods": ["GET"] 19 | } 20 | ] 21 | } 22 | --------------------------------------------------------------------------------