├── .github
└── workflows
│ ├── docker-image.yml
│ └── go-release.yml
├── .gitignore
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── app
├── http
│ └── controllers
│ │ ├── auth_controller.go
│ │ ├── base_controller.go
│ │ └── chat_controller.go
└── middlewares
│ ├── cors.go
│ └── jwt.go
├── bootstarp
├── bootstarp.go
├── db.go
└── route.go
├── chat-new
├── .env.dev
├── .env.prod
├── .gitignore
├── index.html
├── package.json
├── pnpm-lock.yaml
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
├── src
│ ├── App.css
│ ├── App.module.css
│ ├── App.tsx
│ ├── chatui-theme.css
│ ├── components
│ │ ├── ErrorBoundary.tsx
│ │ └── Permission.tsx
│ ├── index.css
│ ├── logo.svg
│ ├── main.tsx
│ ├── pages
│ │ ├── chat
│ │ │ ├── chat.css
│ │ │ └── index.tsx
│ │ ├── login
│ │ │ ├── index.less
│ │ │ └── index.tsx
│ │ └── noFind
│ │ │ └── index.tsx
│ ├── reportWebVitals.ts
│ ├── routers
│ │ └── index.tsx
│ ├── services
│ │ ├── port.ts
│ │ └── request.ts
│ ├── utils
│ │ └── cookie.ts
│ └── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
├── config.dev.json
├── config
├── cli.go
└── config.go
├── docker-compose.yaml
├── go.mod
├── main.go
├── pkg
├── auth
│ └── auth.go
├── logger
│ └── logger.go
├── model
│ ├── model.go
│ └── user
│ │ ├── curd.go
│ │ ├── hooks.go
│ │ └── user.go
├── password
│ └── password.go
└── types
│ ├── converter.go
│ └── slice.go
├── resources
└── view
│ └── index.html
├── routes
└── web.go
├── static
├── assets
│ ├── index-047c9876.js
│ ├── index-6b2d2dee.css
│ ├── index-76b3b3f6.css
│ ├── index-7d01d5e6.css
│ ├── index-951f6775.js
│ ├── index-dc15e04b.js
│ ├── style-4903598a.js
│ ├── style-55fb6ecf.css
│ └── web-vitals-60d3425a.js
├── favicon.ico
├── logo192.png
├── logo512.png
└── manifest.json
└── supervisord.conf
/.github/workflows/docker-image.yml:
--------------------------------------------------------------------------------
1 | # This is a basic workflow to help you get started with Actions
2 |
3 | name: build docker image
4 |
5 | # Controls when the action will run.
6 | on:
7 | push:
8 | branches:
9 | - main
10 |
11 | # Allows you to run this workflow manually from the Actions tab
12 | # 可以手动触发
13 | workflow_dispatch:
14 | inputs:
15 | logLevel:
16 | description: 'Log level'
17 | required: true
18 | default: 'warning'
19 | tags:
20 | description: 'Test scenario tags'
21 |
22 | jobs:
23 | buildx:
24 | runs-on: ubuntu-latest
25 | steps:
26 | - name: Checkout
27 | uses: actions/checkout@v2
28 |
29 | - name: Get current date
30 | id: date
31 | run: echo "::set-output name=today::$(date +'%Y-%m-%d_%H-%M')"
32 |
33 | - name: Set up QEMU
34 | uses: docker/setup-qemu-action@v1
35 |
36 | - name: Set up Docker Buildx
37 | id: buildx
38 | uses: docker/setup-buildx-action@v1
39 |
40 | - name: Available platforms
41 | run: echo ${{ steps.buildx.outputs.platforms }}
42 |
43 | - name: Login to DockerHub
44 | uses: docker/login-action@v1
45 | with:
46 | username: ${{ secrets.DOCKERHUB_USERNAME }}
47 | password: ${{ secrets.DOCKERHUB_TOKEN }}
48 |
49 | - name: Build and push
50 | uses: docker/build-push-action@v2
51 | with:
52 | context: .
53 | file: ./Dockerfile
54 | # 所需要的体系结构,可以在 Available platforms 步骤中获取所有的可用架构
55 | platforms: linux/amd64,linux/arm64/v8
56 | # 镜像推送时间
57 | push: ${{ github.event_name != 'pull_request' }}
58 | # 给清单打上多个标签
59 | tags: |
60 | ${{ secrets.DOCKERHUB_USERNAME }}/chatgpt-web:${{ steps.date.outputs.today }}
61 | ${{ secrets.DOCKERHUB_USERNAME }}/chatgpt-web:latest
--------------------------------------------------------------------------------
/.github/workflows/go-release.yml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on:
4 | release:
5 | types: [created] # 表示在创建新的 Release 时触发
6 |
7 | jobs:
8 | build-go-binary:
9 | runs-on: ubuntu-latest
10 | strategy:
11 | matrix:
12 | goos: [linux, windows, darwin] # 需要打包的系统
13 | goarch: [amd64, arm64] # 需要打包的架构
14 | exclude: # 排除某些平台和架构
15 | - goarch: arm64
16 | goos: windows
17 | steps:
18 | - uses: actions/checkout@v3
19 | - uses: wangyoucao577/go-release-action@v1.30
20 | with:
21 | github_token: ${{ secrets.GITHUB_TOKEN }} # 一个默认的变量,用来实现往 Release 中添加文件
22 | goos: ${{ matrix.goos }}
23 | goarch: ${{ matrix.goarch }}
24 | goversion: 1.18 # 可以指定编译使用的 Golang 版本
25 | binary_name: "chatgpt-web" # 可以指定二进制文件的名称
26 | extra_files: ./resources ./static README.md config.dev.json # 需要包含的额外文件
27 | retry: 10
28 | overwrite: true
29 | pre_command: go mod tidy && go mod download
30 |
31 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.exe~
4 | *.dll
5 | *.so
6 | *.dylib
7 | .idea/
8 | .vscode/
9 | wechatbot
10 | storage.json
11 |
12 | # Test binary, built with `go test -c`
13 | *.test
14 |
15 | # Output of the go coverage tool, specifically when used with LiteIDE
16 | *.out
17 |
18 | # Dependency directories (remove the comment below to include it)
19 | # vendor/
20 | /config.json
21 |
22 | go.sum
23 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # 使用 golang 官方镜像提供 Go 运行环境,并且命名为 builder 以便后续引用
2 | FROM golang:1.18-alpine AS builder
3 |
4 | # 启用 Go Modules 并设置 GOPROXY
5 | ENV GO111MODULE on
6 | ENV GOPROXY https://goproxy.cn
7 |
8 | # 安装 Git
9 | #RUN apk --no-cache add git
10 |
11 | # 安装gcc
12 | #RUN apk --no-cache add gcc musl-dev
13 |
14 | # 设置工作目录
15 | WORKDIR /app
16 |
17 | # 将代码拷贝到镜像中
18 | COPY . .
19 |
20 | # 先进行依赖下载
21 | RUN go mod tidy && go mod download
22 |
23 | # 构建二进制文件,设置了一些额外参数以便可以在 Alpine 中运行它
24 | RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o chatgpt-web
25 |
26 | # 下面是第二阶段的镜像构建,和之前保持一致
27 | FROM alpine:latest
28 |
29 | # 安装相关软件和库
30 | RUN apk update && apk add --no-cache bash supervisor ca-certificates
31 |
32 | # 设置工作目录
33 | WORKDIR /app
34 |
35 | # 复制资源和静态文件
36 | COPY resources ./resources
37 | COPY static ./static
38 |
39 | # 从上一个阶段构建的 builder 容器中复制二进制文件
40 | COPY --from=builder /app/chatgpt-web .
41 |
42 | # 添加配置文件和 supervisord 配置文件
43 | COPY supervisord.conf /etc/supervisord.conf
44 | COPY config.dev.json ./config.json
45 |
46 | # 通过 Supervisor 管理服务
47 | CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
48 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: build
2 | build:
3 | CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-w' -o chatgpt-web ./main.go
4 |
5 | .PHONY: docker
6 | docker:
7 | docker build . -t chatgpt-web:latest
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # chatgpt-web
2 | [](https://github.com/869413421/wechatbot/releases/tag/v1.1.3)
3 | 
4 | 
5 | > 本项目可以一键部署属于自己定制化的 chatgpt web 程序(兼容gpt3.5),
6 | > 只需下载release中对应平台的项目文件,修改配置后执行,打开 http://127.0.0.1:8080 ,便可以获得属于自己的chatgpt网站。
7 | >
8 | > 参考项目:[codegen](https://github.com/git-cloner/codegen)
9 |
10 | > 项目当前默认为示例中AI聊天机器人参数,可以根据自己需求定制化。
11 | >
12 | > **注意,每个参数都可能影响你得到不一样的聊天效果,改变一个参数你就可能得到另一种回答,所以请自己尝试去调试,不要上来就抱怨人工智障。文档中有二十多中参数示例,如AI聊天机器人
13 | > ,产品名称生成,python代码修复器等等等...**
14 | >
15 | > 详情参考官方详细[参数示例](https://beta.openai.com/examples)
16 |
17 | # 更新记录
18 | - [x] fix: 支持gpt-4模型,修改前端空白BUG。 2023-03-30
19 | - [x] fix: 增加用户模块,认证页面,接口jwt验证。 2023-03-27
20 | - [x] fix: 修复前端富文本显示问题,优化dockerfile。 2023-03-27
21 | - [x] fix: 优化前端显示界面。 2023-03-20
22 | - [x] feat: 增加接口代理配置。 2023-03-20
23 | - [x] fix: 修复前端部分BUG,优化富文本代码格式。 2023-03-13
24 | - [x] feat: 增加socsk5代理的支持,命令行参数配置。2023-03-13
25 | - [x] feat: 增加docker-compose.yaml。2023-03-08
26 | - [x] fix: 修复basic auth 。 2023-03-08
27 | - [x] feat:修改为默认不开启代理。2023-03-06
28 | - [x] feat:增加代理配置,解决国内无法使用。2023-03-04
29 |
30 | # 项目功能
31 | * 请求openai增加代理(防墙)
32 | * AI性格设定
33 | * 兼容3.0和3.5API
34 | * 基本问答界面
35 | * 参数可配置
36 | * markdown语法
37 | * 提问上下文
38 | # 使用前提
39 | > 有openai账号,并且创建好api_key,注册事项可以参考[此文章](https://juejin.cn/post/7173447848292253704) 。
40 |
41 |
42 | # 快速开始
43 |
44 | `第一种:直接下载二进制(适合对编程不了解的同学)`
45 |
46 | > 非技术人员请直接下载release中的[压缩包](https://github.com/869413421/chatgpt-web/releases) ,请根据自己系统以及架构选择合适的压缩包,下载之后直接解压运行。
47 |
48 | 下载之后,在本地解压,即可看到可执行程序,与配置文件:
49 |
50 | ```
51 | # windows
52 | 1.下载压缩包解压
53 | 2.复制文件中config.dev.json更改为config.json
54 | 3.将config.json中的api_key替换为自己的
55 | 4.双击exe运行,启动服务
56 |
57 | # linux
58 | $ tar xf chatgpt-web-v0.0.2-darwin-arm64.tar.gz # 解压
59 | $ cd chatgpt-web-v0.0.2-darwin-arm64
60 | $ cp config.dev.json # 根据情况调整配置文件内容
61 | $ ./chatgpt-web # 直接运行
62 |
63 | # 如果要守护在后台运行
64 | $ nohup ./chatgpt-web &> run.log &
65 | $ tail -f run.log
66 | ```
67 |
68 | `第二种:基于源码运行(适合了解go语言编程的同学)`
69 |
70 | ````
71 | # 获取项目
72 | $ git clone https://github.com/869413421/chatgpt-web.git
73 |
74 | # 进入项目目录
75 | $ cd chatgpt-web
76 |
77 | # 复制配置文件
78 | $ copy config.dev.json config.json
79 |
80 | # 启动项目
81 | $ go run main.go
82 | ````
83 |
84 | # 使用docker运行
85 | 你可以使用docker快速运行本项目。
86 | `第一种:基于环境变量运行`
87 |
88 | ```sh
89 | # 运行项目,环境变量参考下方配置说明
90 | $ docker run -itd --name chatgpt-web --restart=always \
91 | -e APIKEY=换成你的key \
92 | -e APIURL= \
93 | -e MODEL=gpt-3.5-turbo-0301 \
94 | -e BOT_DESC=你是一个AI助手,我需要你模拟一名温柔贴心的女朋友来回答我的问题. \
95 | -e MAX_TOKENS=512 \
96 | -e TEMPREATURE=0.9 \
97 | -e TOP_P=1 \
98 | -e FREQ=0.0 \
99 | -e PRES=0.6 \
100 | -e PROXY=http://host.docker.internal:10809 \
101 | -e AUTH_USER= \
102 | -e AUTH_PASSWORD= \
103 | -p 8080:8080 \
104 | --add-host="host.docker.internal:host-gateway" \
105 | qingshui869413421/chatgpt-web:latest
106 | ```
107 |
108 | `注意`:`host.docker.internal`会指向容器所在宿主机的IP,因此只需要更改端口为你的代理端口即可。
109 |
110 | 运行命令中映射的配置文件参考下边的配置文件说明。
111 |
112 | `第二种:基于配置文件挂载运行`
113 |
114 | ```sh
115 | # 复制配置文件,根据自己实际情况,调整配置里的内容
116 | $ cp config.dev.json config.json # 其中 config.dev.json 从项目的根目录获取
117 |
118 | # 运行项目
119 | $ docker run -itd --name chatgpt-web -v `pwd`/config.json:/app/config.json -p 8080:8080 qingshui869413421/chatgpt-web:latest
120 | ```
121 |
122 | 其中配置文件参考下边的配置文件说明。
123 |
124 | # 使用docker-docompose 运行
125 |
126 | ``docker compose up -d``
127 |
128 |
129 | # 配置文件说明
130 |
131 | ```json
132 | {
133 | "api_key": "your api key",
134 | "api_url": "",
135 | "port": 8080,
136 | "listen": "",
137 | "bot_desc": "你是一个AI助手,我需要你模拟一名温柔贴心的女朋友来回答我的问题。",
138 | "proxy": "http://host.docker.internal:10809",
139 | "model": "gpt-3.5-turbo-0301",
140 | "max_tokens": 512,
141 | "temperature": 0.9,
142 | "top_p": 1,
143 | "frequency_penalty": 0.0,
144 | "presence_penalty": 0.6,
145 | "auth_user": "",
146 | "auth_password": ""
147 | }
148 | ```
149 |
150 | ````
151 | api_key:openai api_key
152 | api_url: openai api接口地址 不填使用默认 https://api.openai.com/v1 注,该服务的提供者可以看到你的明文请求(包括你在OpenAI的key),建议自建或使用可信来源
153 | port: http服务端口
154 | listen: http服务监听地址,不填默认监听0.0.0.0
155 | proxy: openai请求代理,防墙。 例如 http://127.0.0.1:7890 socks5://127.0.0.1:7890
156 | bot_desc:AI特征,非常重要,功能等同给与AI一个身份设定
157 | max_tokens: GPT响应字符数,最大2048,默认值512。max_tokens会影响接口响应速度,字符越大响应越慢。
158 | model: GPT选用模型,默认text-davinci-003,具体选项参考官网训练场
159 | temperature: GPT热度,0到1,默认0.9。数字越大创造力越强,但更偏离训练事实,越低越接近训练事实
160 | top_p: 使用温度采样的替代方法称为核心采样,其中模型考虑具有top_p概率质量的令牌的结果。因此,0.1 意味着只考虑包含前 10% 概率质量的代币。
161 | frequency_penalty:
162 | presence_penalty:
163 | auth_user": http基本认证用户名(空表示不开启验证)
164 | auth_password": http基本认证密码
165 | ````
166 |
167 | # NGINX反向代理配置样例
168 |
169 | 这里提供一份使用NGINX反向代理该软件的样例配置,方便集成于现有的站点,添加用户认证,套TLS等,该文件一般对应于`/etc/nginx/sites-available/default`文件,需要自行修改。
170 |
171 | ```nginx
172 | # 监听80端口,跳转https
173 | server {
174 | listen 80 default_server;
175 | listen [::]:80 default_server;
176 | location / {
177 | return 301 https://$host$request_uri;
178 | }
179 | }
180 | # 监听443端口,使用https提供服务
181 | server {
182 | # SSL相关配置来自 https://ssl-config.mozilla.org/
183 | listen 443 ssl http2;
184 | listen [::]:443 ssl http2;
185 | # 证书路径,建议Fullchain
186 | ssl_certificate /path/to/your/cert.pem;
187 | # 私钥路径
188 | ssl_certificate_key /path/to/your/key.pem;
189 | ssl_session_timeout 1d;
190 | ssl_session_cache shared:MozSSL:10m;
191 | ssl_session_tickets off;
192 | # 执行下面的命令下载dhparam
193 | # curl https://ssl-config.mozilla.org/ffdhe2048.txt > /path/to/dhparam
194 | ssl_dhparam /path/to/dhparam;
195 | ssl_protocols TLSv1.2 TLSv1.3;
196 | ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
197 | ssl_prefer_server_ciphers off;
198 | # HSTS (ngx_http_headers_module is required) (63072000 seconds)
199 | add_header Strict-Transport-Security "max-age=63072000" always;
200 | # SSL配置结束
201 |
202 | server_name _;
203 | charset utf-8;
204 | client_max_body_size 5m;
205 |
206 | # 如果需要将chatgpt-web置于某一路径下,使用这个location配置
207 | location /your/path/ {
208 | # 基本身份认证 设定
209 | # 提示语
210 | auth_basic "Auth Require";
211 | # 认证配置文件 格式请参考 https://nginx.org/en/docs/http/ngx_http_auth_basic_module.html
212 | auth_basic_user_file /path/to/passwd;
213 |
214 | # 反向代理 假设chatgpt-web监听端口为8080
215 | proxy_pass http://127.0.0.1:8080/;
216 | proxy_http_version 1.1;
217 | # 反向代理超时时间设定(OpenAI的反应比较慢,设定为120秒后才超时)
218 | proxy_read_timeout 120s;
219 | }
220 |
221 | # 如果chatgpt-web放置于根路径,使用这个location配置
222 | location / {
223 | auth_basic "Auth Require";
224 | auth_basic_user_file /etc/nginx/passwd;
225 |
226 | proxy_pass http://127.0.0.1:8080/;
227 | proxy_http_version 1.1;
228 | proxy_read_timeout 120s;
229 |
230 | # 位于根路径时不需要修改index.html
231 | }
232 |
233 | }
234 | ```
235 |
236 | # Linux系统systemd服务配置
237 |
238 | 可以使用`systemd`配置`chatgpt-web`开机自启,假设可执行文件和相关资源文件放置在`/var/www/chatgpt-web/`目录下,`chatgpt-web`二进制文件需要其他用户可读可执行权限,其余资源文件需要其他用户可读权限,并且已经配置好`config.json`。
239 |
240 | 在目录`/etc/systemd/system/`下新建文件`chatgpt-web.service`,以下是文件样例。
241 |
242 | ```ini
243 | [Unit]
244 | Description=chatgpt-web
245 | Documentation=https://github.com/869413421/chatgpt-web
246 | # 在网络启动完成后运行
247 | After=network.target nss-lookup.target
248 |
249 | [Service]
250 | # 使用随机用户执行该服务
251 | DynamicUser=yes
252 | # 指定工作目录
253 | WorkingDirectory=/var/www/chatgpt-web/
254 | # 执行程序
255 | ExecStart=/var/www/chatgpt-web/chatgpt-web
256 |
257 | [Install]
258 | WantedBy=multi-user.target
259 | ```
260 | 保存后使用`systemctl daemon-reload`更新systemd配置文件,使用`systemctl start/stop chatgpt-web`启动/停止服务,使用`systemctl enable/disable chatgpt-web`启用/禁用服务开机自启。
261 |
262 | 可以使用`journalctl --unit chatgpt-web.service`查看程序日志。
263 |
264 | # 免责声明 Disclaimers
265 | The code is for demo and testing only. 代码仅用于演示和测试。
266 |
267 | ⚠⚠⚠请勿将本系统代码用于商业用途!
268 |
269 | 仿冒或冒用ChatGPT、OpenAI名义开展经营活动,可能构成《商标法》、《反不正当竞争法》下的一系列侵权行为; 以之牟利造成消费者损失的,可能产生《商标法》、《反不正当竞争法》、《消费者权益保护法》下的民事或行政责任,情节严重并造成重大损失的,还有可能构成刑事犯罪; 如果提供这种跨境经营服务存在私自搭建国际信道的情形,还有可能违反《网络安全法》、《刑法》的相关规定,承担行政责任或构成刑事犯罪。
270 |
--------------------------------------------------------------------------------
/app/http/controllers/auth_controller.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "github.com/869413421/chatgpt-web/pkg/auth"
5 | "github.com/869413421/chatgpt-web/pkg/model/user"
6 | "github.com/gin-gonic/gin"
7 | "gorm.io/gorm"
8 | "net/http"
9 | )
10 |
11 | // AuthController 认证控制器
12 | type AuthController struct {
13 | BaseController
14 | }
15 |
16 | func NewAuthController() *AuthController {
17 | return &AuthController{}
18 | }
19 |
20 | // authRequest 认证请求
21 | type authRequest struct {
22 | Name string `json:"username"`
23 | Password string `json:"password"`
24 | }
25 |
26 | // Auth 认证
27 | func (c *AuthController) Auth(ctx *gin.Context) {
28 | var req authRequest
29 | err := ctx.BindJSON(&req)
30 | if err != nil {
31 | c.ResponseJson(ctx, http.StatusInternalServerError, err.Error(), nil)
32 | return
33 | }
34 |
35 | if req.Name == "" || req.Password == "" {
36 | c.ResponseJson(ctx, http.StatusUnauthorized, "请输入用户名密码", nil)
37 | return
38 | }
39 |
40 | authUser, err := user.GetByName(req.Name)
41 | if err != nil && err == gorm.ErrRecordNotFound {
42 | c.ResponseJson(ctx, http.StatusUnauthorized, "请求认证的用户不存在", nil)
43 | return
44 | }
45 | if !authUser.ComparePassword(req.Password) {
46 | c.ResponseJson(ctx, http.StatusUnauthorized, "密码错误", nil)
47 | return
48 | }
49 | token, err := auth.Encode(authUser)
50 | if err != nil {
51 | c.ResponseJson(ctx, http.StatusInternalServerError, err.Error(), nil)
52 | return
53 | }
54 |
55 | c.ResponseJson(ctx, http.StatusOK, "", gin.H{
56 | "token": token,
57 | })
58 | }
59 |
60 | // Info 登录用户信息
61 | func (c *AuthController) Info(ctx *gin.Context) {
62 | authUser, ok := ctx.Get("authUser")
63 | if !ok {
64 | c.ResponseJson(ctx, http.StatusInternalServerError, "获取登录用户信息失败", nil)
65 | return
66 | }
67 |
68 | userInfo, ok := authUser.(*user.User)
69 | if !ok {
70 | c.ResponseJson(ctx, http.StatusInternalServerError, "断言登录用户信息失败", nil)
71 | return
72 | }
73 | // 未实现权限系统,写死
74 | c.ResponseJson(ctx, http.StatusOK, "", gin.H{
75 | "info": userInfo,
76 | "permissionRoutes": []string{"chat", "chat/completion", "user/auth/info", "user/auth"},
77 | })
78 | }
79 |
--------------------------------------------------------------------------------
/app/http/controllers/base_controller.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | )
6 |
7 | type BaseController struct {
8 | }
9 |
10 | func (*BaseController) ResponseJson(ctx *gin.Context, code int, errorMsg string, data interface{}) {
11 |
12 | ctx.JSON(code, gin.H{
13 | "code": code,
14 | "errorMsg": errorMsg,
15 | "data": data,
16 | })
17 | ctx.Abort()
18 | }
19 |
--------------------------------------------------------------------------------
/app/http/controllers/chat_controller.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "context"
5 | "github.com/869413421/chatgpt-web/pkg/types"
6 | "net"
7 | "net/http"
8 | "net/url"
9 | "strings"
10 | "time"
11 |
12 | gogpt "github.com/sashabaranov/go-openai"
13 | "golang.org/x/net/proxy"
14 |
15 | "github.com/869413421/chatgpt-web/config"
16 | "github.com/869413421/chatgpt-web/pkg/logger"
17 | "github.com/gin-gonic/gin"
18 | )
19 |
20 | var chatModels = []string{gogpt.GPT432K0314, gogpt.GPT4, gogpt.GPT40314, gogpt.GPT432K, gogpt.GPT3Dot5Turbo, gogpt.GPT3Dot5Turbo0301}
21 |
22 | // ChatController 首页控制器
23 | type ChatController struct {
24 | BaseController
25 | }
26 |
27 | // NewChatController 创建控制器
28 | func NewChatController() *ChatController {
29 | return &ChatController{}
30 | }
31 |
32 | // Index 首页
33 | func (c *ChatController) Index(ctx *gin.Context) {
34 | ctx.HTML(http.StatusOK, "index.html", gin.H{
35 | "title": "Main website",
36 | })
37 | }
38 |
39 | // Completion 回复
40 | func (c *ChatController) Completion(ctx *gin.Context) {
41 | var request gogpt.ChatCompletionRequest
42 | err := ctx.BindJSON(&request)
43 | if err != nil {
44 | c.ResponseJson(ctx, http.StatusInternalServerError, err.Error(), nil)
45 | return
46 | }
47 | logger.Info(request)
48 | if len(request.Messages) == 0 {
49 | c.ResponseJson(ctx, http.StatusBadRequest, "request messages required", nil)
50 | return
51 | }
52 |
53 | cnf := config.LoadConfig()
54 | gptConfig := gogpt.DefaultConfig(cnf.ApiKey)
55 |
56 | if cnf.Proxy != "" {
57 | transport := &http.Transport{}
58 |
59 | if strings.HasPrefix(cnf.Proxy, "socks5h://") {
60 | // 创建一个 DialContext 对象,并设置代理服务器
61 | dialContext, err := newDialContext(cnf.Proxy[10:])
62 | if err != nil {
63 | panic(err)
64 | }
65 | transport.DialContext = dialContext
66 | } else {
67 | // 创建一个 HTTP Transport 对象,并设置代理服务器
68 | proxyUrl, err := url.Parse(cnf.Proxy)
69 | if err != nil {
70 | panic(err)
71 | }
72 | transport.Proxy = http.ProxyURL(proxyUrl)
73 | }
74 | // 创建一个 HTTP 客户端,并将 Transport 对象设置为其 Transport 字段
75 | gptConfig.HTTPClient = &http.Client{
76 | Transport: transport,
77 | }
78 |
79 | }
80 |
81 | // 自定义gptConfig.BaseURL
82 | if cnf.ApiURL != "" {
83 | gptConfig.BaseURL = cnf.ApiURL
84 | }
85 |
86 | client := gogpt.NewClientWithConfig(gptConfig)
87 | if request.Messages[0].Role != "system" {
88 | newMessage := append([]gogpt.ChatCompletionMessage{
89 | {Role: "system", Content: cnf.BotDesc},
90 | }, request.Messages...)
91 | request.Messages = newMessage
92 | logger.Info(request.Messages)
93 | }
94 |
95 | // cnf.Model 是否在 chatModels 中
96 | if types.Contains(chatModels, cnf.Model) {
97 | request.Model = cnf.Model
98 | resp, err := client.CreateChatCompletion(ctx, request)
99 | if err != nil {
100 | c.ResponseJson(ctx, http.StatusInternalServerError, err.Error(), nil)
101 | return
102 | }
103 | c.ResponseJson(ctx, http.StatusOK, "", gin.H{
104 | "reply": resp.Choices[0].Message.Content,
105 | "messages": append(request.Messages, resp.Choices[0].Message),
106 | })
107 | } else {
108 | prompt := ""
109 | for _, item := range request.Messages {
110 | prompt += item.Content + "/n"
111 | }
112 | prompt = strings.Trim(prompt, "/n")
113 |
114 | logger.Info("request prompt is %s", prompt)
115 | req := gogpt.CompletionRequest{
116 | Model: cnf.Model,
117 | MaxTokens: cnf.MaxTokens,
118 | TopP: cnf.TopP,
119 | FrequencyPenalty: cnf.FrequencyPenalty,
120 | PresencePenalty: cnf.PresencePenalty,
121 | Prompt: prompt,
122 | }
123 |
124 | resp, err := client.CreateCompletion(ctx, req)
125 | if err != nil {
126 | c.ResponseJson(ctx, http.StatusInternalServerError, err.Error(), nil)
127 | return
128 | }
129 |
130 | c.ResponseJson(ctx, http.StatusOK, "", gin.H{
131 | "reply": resp.Choices[0].Text,
132 | "messages": append(request.Messages, gogpt.ChatCompletionMessage{
133 | Role: "assistant",
134 | Content: resp.Choices[0].Text,
135 | }),
136 | })
137 | }
138 | }
139 |
140 | type dialContextFunc func(ctx context.Context, network, address string) (net.Conn, error)
141 |
142 | func newDialContext(socks5 string) (dialContextFunc, error) {
143 | baseDialer := &net.Dialer{
144 | Timeout: 60 * time.Second,
145 | KeepAlive: 60 * time.Second,
146 | }
147 |
148 | if socks5 != "" {
149 | // split socks5 proxy string [username:password@]host:port
150 | var auth *proxy.Auth = nil
151 |
152 | if strings.Contains(socks5, "@") {
153 | proxyInfo := strings.SplitN(socks5, "@", 2)
154 | proxyUser := strings.Split(proxyInfo[0], ":")
155 | if len(proxyUser) == 2 {
156 | auth = &proxy.Auth{
157 | User: proxyUser[0],
158 | Password: proxyUser[1],
159 | }
160 | }
161 | socks5 = proxyInfo[1]
162 | }
163 |
164 | dialSocksProxy, err := proxy.SOCKS5("tcp", socks5, auth, baseDialer)
165 | if err != nil {
166 | return nil, err
167 | }
168 |
169 | contextDialer, ok := dialSocksProxy.(proxy.ContextDialer)
170 | if !ok {
171 | return nil, err
172 | }
173 |
174 | return contextDialer.DialContext, nil
175 | } else {
176 | return baseDialer.DialContext, nil
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/app/middlewares/cors.go:
--------------------------------------------------------------------------------
1 | package middlewares
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "net/http"
6 | )
7 |
8 | func Cors() gin.HandlerFunc {
9 | return func(c *gin.Context) {
10 | method := c.Request.Method
11 | origin := c.Request.Header.Get("Origin")
12 | if origin != "" {
13 | c.Header("Access-Control-Allow-Origin", "*") // 可将将 * 替换为指定的域名
14 | c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
15 | c.Header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization")
16 | c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type")
17 | c.Header("Access-Control-Allow-Credentials", "true")
18 | }
19 | if method == "OPTIONS" {
20 | c.AbortWithStatus(http.StatusNoContent)
21 | }
22 | c.Next()
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/middlewares/jwt.go:
--------------------------------------------------------------------------------
1 | package middlewares
2 |
3 | import (
4 | "github.com/869413421/chatgpt-web/app/http/controllers"
5 | "github.com/869413421/chatgpt-web/pkg/auth"
6 | "github.com/gin-gonic/gin"
7 | "net/http"
8 | )
9 |
10 | var base = controllers.BaseController{}
11 |
12 | // Jwt jwt认证
13 | func Jwt() gin.HandlerFunc {
14 | return func(c *gin.Context) {
15 | claims, err := auth.EncodeByCtx(c)
16 | if err != nil {
17 | base.ResponseJson(c, http.StatusUnauthorized, err.Error(), nil)
18 | return
19 | }
20 |
21 | if claims.User.ID == 0 {
22 | base.ResponseJson(c, http.StatusUnauthorized, "用户信息错误,未知的token", nil)
23 | return
24 | }
25 | c.Set("authUser", claims.User)
26 | c.Next()
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/bootstarp/bootstarp.go:
--------------------------------------------------------------------------------
1 | package bootstrap
2 |
3 | import (
4 | "github.com/869413421/chatgpt-web/config"
5 | "github.com/869413421/chatgpt-web/pkg/logger"
6 | "github.com/gin-gonic/gin"
7 | "mime"
8 | "strconv"
9 | "strings"
10 | )
11 |
12 | func StartWebServer() {
13 | // 注册启动所需各类参数
14 | SetUpRoute()
15 | SetupDB()
16 | initTemplateDir()
17 | initStaticServer()
18 |
19 | // 启动服务
20 | port := config.LoadConfig().Port
21 | portString := strconv.Itoa(port)
22 | // 自定义监听地址
23 | listen := config.LoadConfig().Listen
24 | err := router.Run(listen + ":" + portString)
25 | if err != nil {
26 | logger.Danger("run webserver error %s", err)
27 | return
28 | }
29 | }
30 |
31 | // initTemplate 初始化HTML模板加载路径
32 | func initTemplateDir() {
33 | router.LoadHTMLGlob("resources/view/*")
34 | }
35 |
36 | // initStaticServer 初始化静态文件处理
37 | func initStaticServer() {
38 | router.GET("/assets/:filename", func(c *gin.Context) {
39 | fileName := c.Param("filename")
40 | nameSlice := strings.Split(fileName, ".")
41 | ext := nameSlice[len(nameSlice)-1]
42 | if ext == "js" {
43 | c.Header("Content-Type", "application/javascript")
44 | } else {
45 | c.Header("Content-Type", mime.TypeByExtension(ext))
46 | }
47 | c.File("static/assets/" + c.Param("filename"))
48 | })
49 |
50 | //router.StaticFS("/assets", http.Dir("static/assets"))
51 | router.StaticFile("logo192.png", "static/logo192.png")
52 | router.StaticFile("logo512.png", "static/logo512.png")
53 | router.StaticFile("favicon.ico", "static/favicon.ico")
54 | router.StaticFile("manifest.json", "static/manifest.json")
55 | }
56 |
--------------------------------------------------------------------------------
/bootstarp/db.go:
--------------------------------------------------------------------------------
1 | package bootstrap
2 |
3 | import (
4 | "github.com/869413421/chatgpt-web/config"
5 | "github.com/869413421/chatgpt-web/pkg/logger"
6 | "github.com/869413421/chatgpt-web/pkg/model"
7 | "github.com/869413421/chatgpt-web/pkg/model/user"
8 | "gorm.io/gorm"
9 | )
10 |
11 | // SetupDB 启动数据库
12 | func SetupDB() {
13 | //建立连接池
14 | db := model.ConnectDB()
15 |
16 | migration(db)
17 |
18 | insertAdmin()
19 | }
20 |
21 | // migration 迁移
22 | func migration(db *gorm.DB) {
23 | err := db.AutoMigrate(&user.User{})
24 | if err != nil {
25 | logger.Danger("migration model error:", err)
26 | }
27 | }
28 |
29 | func insertAdmin() {
30 | cf := config.LoadConfig()
31 | if cf.AuthUser != "" {
32 | _, err := user.GetByName(cf.AuthUser)
33 | if err != nil && err != gorm.ErrRecordNotFound {
34 | logger.Danger("insert admin error:", err)
35 | }
36 | if err == gorm.ErrRecordNotFound {
37 | _, err = user.CreateUser(cf.AuthUser, cf.AuthPassword)
38 | if err != nil {
39 | logger.Danger("create admin error:", err)
40 | }
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/bootstarp/route.go:
--------------------------------------------------------------------------------
1 | package bootstrap
2 |
3 | import (
4 | "github.com/869413421/chatgpt-web/routes"
5 | "github.com/gin-gonic/gin"
6 | "sync"
7 | )
8 |
9 | var router *gin.Engine
10 | var once sync.Once
11 |
12 | func SetUpRoute() {
13 | once.Do(func() {
14 | router = gin.Default()
15 | routes.RegisterWebRoutes(router)
16 | })
17 | }
18 |
--------------------------------------------------------------------------------
/chat-new/.env.dev:
--------------------------------------------------------------------------------
1 | VITE_API_BASE_URL = ''
--------------------------------------------------------------------------------
/chat-new/.env.prod:
--------------------------------------------------------------------------------
1 | VITE_API_BASE_URL = ''
2 |
--------------------------------------------------------------------------------
/chat-new/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
26 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
27 |
28 | # dependencies
29 | /node_modules
30 | /.pnp
31 | .pnp.js
32 |
33 | # testing
34 | /coverage
35 |
36 | # production
37 | /build
38 |
39 | # misc
40 | .env.local
41 | .env.development.local
42 | .env.test.local
43 | .env.production.local
44 |
45 |
--------------------------------------------------------------------------------
/chat-new/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | AI Chatbot
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/chat-new/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chat-new",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite --mode dev --host 0.0.0.0",
8 | "build": "tsc && vite build --mode prod",
9 | "preview": "vite preview"
10 | },
11 | "dependencies": {
12 | "@chatui/core": "^2.4.2",
13 | "axios": "^1.3.2",
14 | "clipboardy": "^3.0.0",
15 | "md-editor-rt": "^2.9.0",
16 | "react": "^18.2.0",
17 | "react-dom": "^18.2.0",
18 | "react-markdown": "^8.0.5",
19 | "react-router-dom": "^6.9.0",
20 | "sanitize-html": "^2.10.0",
21 | "web-vitals": "^2.1.4"
22 | },
23 | "devDependencies": {
24 | "@types/react": "^18.0.28",
25 | "@types/react-dom": "^18.0.11",
26 | "@types/sanitize-html": "^2.9.0",
27 | "@vitejs/plugin-react": "^3.1.0",
28 | "less": "^4.1.3",
29 | "typescript": "^4.9.3",
30 | "vite": "^4.1.0"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/chat-new/pnpm-lock.yaml:
--------------------------------------------------------------------------------
1 | lockfileVersion: 5.4
2 |
3 | specifiers:
4 | '@chatui/core': ^2.4.2
5 | '@types/react': ^18.0.28
6 | '@types/react-dom': ^18.0.11
7 | '@types/sanitize-html': ^2.9.0
8 | '@vitejs/plugin-react': ^3.1.0
9 | axios: ^1.3.2
10 | clipboardy: ^3.0.0
11 | less: ^4.1.3
12 | md-editor-rt: ^2.9.0
13 | react: ^18.2.0
14 | react-dom: ^18.2.0
15 | react-markdown: ^8.0.5
16 | react-router-dom: ^6.9.0
17 | sanitize-html: ^2.10.0
18 | typescript: ^4.9.3
19 | vite: ^4.1.0
20 | web-vitals: ^2.1.4
21 |
22 | dependencies:
23 | '@chatui/core': 2.4.2_biqbaboplfbrettd7655fr4n2y
24 | axios: 1.3.4
25 | clipboardy: 3.0.0
26 | md-editor-rt: 2.10.1_biqbaboplfbrettd7655fr4n2y
27 | react: 18.2.0
28 | react-dom: 18.2.0_react@18.2.0
29 | react-markdown: 8.0.5_pmekkgnqduwlme35zpnqhenc34
30 | react-router-dom: 6.9.0_biqbaboplfbrettd7655fr4n2y
31 | sanitize-html: 2.10.0
32 | web-vitals: 2.1.4
33 |
34 | devDependencies:
35 | '@types/react': 18.0.28
36 | '@types/react-dom': 18.0.11
37 | '@types/sanitize-html': 2.9.0
38 | '@vitejs/plugin-react': 3.1.0_vite@4.1.4
39 | less: 4.1.3
40 | typescript: 4.9.5
41 | vite: 4.1.4_less@4.1.3
42 |
43 | packages:
44 |
45 | /@ampproject/remapping/2.2.0:
46 | resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==}
47 | engines: {node: '>=6.0.0'}
48 | dependencies:
49 | '@jridgewell/gen-mapping': 0.1.1
50 | '@jridgewell/trace-mapping': 0.3.17
51 | dev: true
52 |
53 | /@babel/code-frame/7.18.6:
54 | resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==}
55 | engines: {node: '>=6.9.0'}
56 | dependencies:
57 | '@babel/highlight': 7.18.6
58 | dev: true
59 |
60 | /@babel/compat-data/7.21.0:
61 | resolution: {integrity: sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==}
62 | engines: {node: '>=6.9.0'}
63 | dev: true
64 |
65 | /@babel/core/7.21.0:
66 | resolution: {integrity: sha512-PuxUbxcW6ZYe656yL3EAhpy7qXKq0DmYsrJLpbB8XrsCP9Nm+XCg9XFMb5vIDliPD7+U/+M+QJlH17XOcB7eXA==}
67 | engines: {node: '>=6.9.0'}
68 | dependencies:
69 | '@ampproject/remapping': 2.2.0
70 | '@babel/code-frame': 7.18.6
71 | '@babel/generator': 7.21.1
72 | '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.21.0
73 | '@babel/helper-module-transforms': 7.21.2
74 | '@babel/helpers': 7.21.0
75 | '@babel/parser': 7.21.2
76 | '@babel/template': 7.20.7
77 | '@babel/traverse': 7.21.2
78 | '@babel/types': 7.21.2
79 | convert-source-map: 1.9.0
80 | debug: 4.3.4
81 | gensync: 1.0.0-beta.2
82 | json5: 2.2.3
83 | semver: 6.3.0
84 | transitivePeerDependencies:
85 | - supports-color
86 | dev: true
87 |
88 | /@babel/generator/7.21.1:
89 | resolution: {integrity: sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA==}
90 | engines: {node: '>=6.9.0'}
91 | dependencies:
92 | '@babel/types': 7.21.2
93 | '@jridgewell/gen-mapping': 0.3.2
94 | '@jridgewell/trace-mapping': 0.3.17
95 | jsesc: 2.5.2
96 | dev: true
97 |
98 | /@babel/helper-compilation-targets/7.20.7_@babel+core@7.21.0:
99 | resolution: {integrity: sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==}
100 | engines: {node: '>=6.9.0'}
101 | peerDependencies:
102 | '@babel/core': ^7.0.0
103 | dependencies:
104 | '@babel/compat-data': 7.21.0
105 | '@babel/core': 7.21.0
106 | '@babel/helper-validator-option': 7.21.0
107 | browserslist: 4.21.5
108 | lru-cache: 5.1.1
109 | semver: 6.3.0
110 | dev: true
111 |
112 | /@babel/helper-environment-visitor/7.18.9:
113 | resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==}
114 | engines: {node: '>=6.9.0'}
115 | dev: true
116 |
117 | /@babel/helper-function-name/7.21.0:
118 | resolution: {integrity: sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==}
119 | engines: {node: '>=6.9.0'}
120 | dependencies:
121 | '@babel/template': 7.20.7
122 | '@babel/types': 7.21.2
123 | dev: true
124 |
125 | /@babel/helper-hoist-variables/7.18.6:
126 | resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==}
127 | engines: {node: '>=6.9.0'}
128 | dependencies:
129 | '@babel/types': 7.21.2
130 | dev: true
131 |
132 | /@babel/helper-module-imports/7.18.6:
133 | resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==}
134 | engines: {node: '>=6.9.0'}
135 | dependencies:
136 | '@babel/types': 7.21.2
137 | dev: true
138 |
139 | /@babel/helper-module-transforms/7.21.2:
140 | resolution: {integrity: sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==}
141 | engines: {node: '>=6.9.0'}
142 | dependencies:
143 | '@babel/helper-environment-visitor': 7.18.9
144 | '@babel/helper-module-imports': 7.18.6
145 | '@babel/helper-simple-access': 7.20.2
146 | '@babel/helper-split-export-declaration': 7.18.6
147 | '@babel/helper-validator-identifier': 7.19.1
148 | '@babel/template': 7.20.7
149 | '@babel/traverse': 7.21.2
150 | '@babel/types': 7.21.2
151 | transitivePeerDependencies:
152 | - supports-color
153 | dev: true
154 |
155 | /@babel/helper-plugin-utils/7.20.2:
156 | resolution: {integrity: sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==}
157 | engines: {node: '>=6.9.0'}
158 | dev: true
159 |
160 | /@babel/helper-simple-access/7.20.2:
161 | resolution: {integrity: sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==}
162 | engines: {node: '>=6.9.0'}
163 | dependencies:
164 | '@babel/types': 7.21.2
165 | dev: true
166 |
167 | /@babel/helper-split-export-declaration/7.18.6:
168 | resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==}
169 | engines: {node: '>=6.9.0'}
170 | dependencies:
171 | '@babel/types': 7.21.2
172 | dev: true
173 |
174 | /@babel/helper-string-parser/7.19.4:
175 | resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==}
176 | engines: {node: '>=6.9.0'}
177 | dev: true
178 |
179 | /@babel/helper-validator-identifier/7.19.1:
180 | resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==}
181 | engines: {node: '>=6.9.0'}
182 | dev: true
183 |
184 | /@babel/helper-validator-option/7.21.0:
185 | resolution: {integrity: sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==}
186 | engines: {node: '>=6.9.0'}
187 | dev: true
188 |
189 | /@babel/helpers/7.21.0:
190 | resolution: {integrity: sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==}
191 | engines: {node: '>=6.9.0'}
192 | dependencies:
193 | '@babel/template': 7.20.7
194 | '@babel/traverse': 7.21.2
195 | '@babel/types': 7.21.2
196 | transitivePeerDependencies:
197 | - supports-color
198 | dev: true
199 |
200 | /@babel/highlight/7.18.6:
201 | resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==}
202 | engines: {node: '>=6.9.0'}
203 | dependencies:
204 | '@babel/helper-validator-identifier': 7.19.1
205 | chalk: 2.4.2
206 | js-tokens: 4.0.0
207 | dev: true
208 |
209 | /@babel/parser/7.21.2:
210 | resolution: {integrity: sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==}
211 | engines: {node: '>=6.0.0'}
212 | hasBin: true
213 | dependencies:
214 | '@babel/types': 7.21.2
215 | dev: true
216 |
217 | /@babel/plugin-transform-react-jsx-self/7.21.0_@babel+core@7.21.0:
218 | resolution: {integrity: sha512-f/Eq+79JEu+KUANFks9UZCcvydOOGMgF7jBrcwjHa5jTZD8JivnhCJYvmlhR/WTXBWonDExPoW0eO/CR4QJirA==}
219 | engines: {node: '>=6.9.0'}
220 | peerDependencies:
221 | '@babel/core': ^7.0.0-0
222 | dependencies:
223 | '@babel/core': 7.21.0
224 | '@babel/helper-plugin-utils': 7.20.2
225 | dev: true
226 |
227 | /@babel/plugin-transform-react-jsx-source/7.19.6_@babel+core@7.21.0:
228 | resolution: {integrity: sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ==}
229 | engines: {node: '>=6.9.0'}
230 | peerDependencies:
231 | '@babel/core': ^7.0.0-0
232 | dependencies:
233 | '@babel/core': 7.21.0
234 | '@babel/helper-plugin-utils': 7.20.2
235 | dev: true
236 |
237 | /@babel/runtime-corejs3/7.21.0:
238 | resolution: {integrity: sha512-TDD4UJzos3JJtM+tHX+w2Uc+KWj7GV+VKKFdMVd2Rx8sdA19hcc3P3AHFYd5LVOw+pYuSd5lICC3gm52B6Rwxw==}
239 | engines: {node: '>=6.9.0'}
240 | dependencies:
241 | core-js-pure: 3.29.0
242 | regenerator-runtime: 0.13.11
243 | dev: false
244 |
245 | /@babel/runtime/7.21.0:
246 | resolution: {integrity: sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==}
247 | engines: {node: '>=6.9.0'}
248 | dependencies:
249 | regenerator-runtime: 0.13.11
250 | dev: false
251 |
252 | /@babel/template/7.20.7:
253 | resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==}
254 | engines: {node: '>=6.9.0'}
255 | dependencies:
256 | '@babel/code-frame': 7.18.6
257 | '@babel/parser': 7.21.2
258 | '@babel/types': 7.21.2
259 | dev: true
260 |
261 | /@babel/traverse/7.21.2:
262 | resolution: {integrity: sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw==}
263 | engines: {node: '>=6.9.0'}
264 | dependencies:
265 | '@babel/code-frame': 7.18.6
266 | '@babel/generator': 7.21.1
267 | '@babel/helper-environment-visitor': 7.18.9
268 | '@babel/helper-function-name': 7.21.0
269 | '@babel/helper-hoist-variables': 7.18.6
270 | '@babel/helper-split-export-declaration': 7.18.6
271 | '@babel/parser': 7.21.2
272 | '@babel/types': 7.21.2
273 | debug: 4.3.4
274 | globals: 11.12.0
275 | transitivePeerDependencies:
276 | - supports-color
277 | dev: true
278 |
279 | /@babel/types/7.21.2:
280 | resolution: {integrity: sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw==}
281 | engines: {node: '>=6.9.0'}
282 | dependencies:
283 | '@babel/helper-string-parser': 7.19.4
284 | '@babel/helper-validator-identifier': 7.19.1
285 | to-fast-properties: 2.0.0
286 | dev: true
287 |
288 | /@chatui/core/2.4.2_biqbaboplfbrettd7655fr4n2y:
289 | resolution: {integrity: sha512-YyimOhHIMHQr0KjzaA9HZNWbOWa5atbG4rMYJwa7K/8je49wntI1OHLzQXl1npZfHWEKNNW4DkKf08xPkQoN7A==}
290 | peerDependencies:
291 | react: '>=16.8.0'
292 | react-dom: '>=16.8.0'
293 | dependencies:
294 | '@babel/runtime': 7.21.0
295 | '@babel/runtime-corejs3': 7.21.0
296 | clsx: 1.2.1
297 | core-js: 3.29.0
298 | dompurify: 2.4.5
299 | intersection-observer: 0.12.2
300 | react: 18.2.0
301 | react-dom: 18.2.0_react@18.2.0
302 | dev: false
303 |
304 | /@esbuild/android-arm/0.16.17:
305 | resolution: {integrity: sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw==}
306 | engines: {node: '>=12'}
307 | cpu: [arm]
308 | os: [android]
309 | requiresBuild: true
310 | dev: true
311 | optional: true
312 |
313 | /@esbuild/android-arm64/0.16.17:
314 | resolution: {integrity: sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg==}
315 | engines: {node: '>=12'}
316 | cpu: [arm64]
317 | os: [android]
318 | requiresBuild: true
319 | dev: true
320 | optional: true
321 |
322 | /@esbuild/android-x64/0.16.17:
323 | resolution: {integrity: sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ==}
324 | engines: {node: '>=12'}
325 | cpu: [x64]
326 | os: [android]
327 | requiresBuild: true
328 | dev: true
329 | optional: true
330 |
331 | /@esbuild/darwin-arm64/0.16.17:
332 | resolution: {integrity: sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==}
333 | engines: {node: '>=12'}
334 | cpu: [arm64]
335 | os: [darwin]
336 | requiresBuild: true
337 | dev: true
338 | optional: true
339 |
340 | /@esbuild/darwin-x64/0.16.17:
341 | resolution: {integrity: sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==}
342 | engines: {node: '>=12'}
343 | cpu: [x64]
344 | os: [darwin]
345 | requiresBuild: true
346 | dev: true
347 | optional: true
348 |
349 | /@esbuild/freebsd-arm64/0.16.17:
350 | resolution: {integrity: sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==}
351 | engines: {node: '>=12'}
352 | cpu: [arm64]
353 | os: [freebsd]
354 | requiresBuild: true
355 | dev: true
356 | optional: true
357 |
358 | /@esbuild/freebsd-x64/0.16.17:
359 | resolution: {integrity: sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==}
360 | engines: {node: '>=12'}
361 | cpu: [x64]
362 | os: [freebsd]
363 | requiresBuild: true
364 | dev: true
365 | optional: true
366 |
367 | /@esbuild/linux-arm/0.16.17:
368 | resolution: {integrity: sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==}
369 | engines: {node: '>=12'}
370 | cpu: [arm]
371 | os: [linux]
372 | requiresBuild: true
373 | dev: true
374 | optional: true
375 |
376 | /@esbuild/linux-arm64/0.16.17:
377 | resolution: {integrity: sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==}
378 | engines: {node: '>=12'}
379 | cpu: [arm64]
380 | os: [linux]
381 | requiresBuild: true
382 | dev: true
383 | optional: true
384 |
385 | /@esbuild/linux-ia32/0.16.17:
386 | resolution: {integrity: sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg==}
387 | engines: {node: '>=12'}
388 | cpu: [ia32]
389 | os: [linux]
390 | requiresBuild: true
391 | dev: true
392 | optional: true
393 |
394 | /@esbuild/linux-loong64/0.16.17:
395 | resolution: {integrity: sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ==}
396 | engines: {node: '>=12'}
397 | cpu: [loong64]
398 | os: [linux]
399 | requiresBuild: true
400 | dev: true
401 | optional: true
402 |
403 | /@esbuild/linux-mips64el/0.16.17:
404 | resolution: {integrity: sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw==}
405 | engines: {node: '>=12'}
406 | cpu: [mips64el]
407 | os: [linux]
408 | requiresBuild: true
409 | dev: true
410 | optional: true
411 |
412 | /@esbuild/linux-ppc64/0.16.17:
413 | resolution: {integrity: sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g==}
414 | engines: {node: '>=12'}
415 | cpu: [ppc64]
416 | os: [linux]
417 | requiresBuild: true
418 | dev: true
419 | optional: true
420 |
421 | /@esbuild/linux-riscv64/0.16.17:
422 | resolution: {integrity: sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw==}
423 | engines: {node: '>=12'}
424 | cpu: [riscv64]
425 | os: [linux]
426 | requiresBuild: true
427 | dev: true
428 | optional: true
429 |
430 | /@esbuild/linux-s390x/0.16.17:
431 | resolution: {integrity: sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w==}
432 | engines: {node: '>=12'}
433 | cpu: [s390x]
434 | os: [linux]
435 | requiresBuild: true
436 | dev: true
437 | optional: true
438 |
439 | /@esbuild/linux-x64/0.16.17:
440 | resolution: {integrity: sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw==}
441 | engines: {node: '>=12'}
442 | cpu: [x64]
443 | os: [linux]
444 | requiresBuild: true
445 | dev: true
446 | optional: true
447 |
448 | /@esbuild/netbsd-x64/0.16.17:
449 | resolution: {integrity: sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA==}
450 | engines: {node: '>=12'}
451 | cpu: [x64]
452 | os: [netbsd]
453 | requiresBuild: true
454 | dev: true
455 | optional: true
456 |
457 | /@esbuild/openbsd-x64/0.16.17:
458 | resolution: {integrity: sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg==}
459 | engines: {node: '>=12'}
460 | cpu: [x64]
461 | os: [openbsd]
462 | requiresBuild: true
463 | dev: true
464 | optional: true
465 |
466 | /@esbuild/sunos-x64/0.16.17:
467 | resolution: {integrity: sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw==}
468 | engines: {node: '>=12'}
469 | cpu: [x64]
470 | os: [sunos]
471 | requiresBuild: true
472 | dev: true
473 | optional: true
474 |
475 | /@esbuild/win32-arm64/0.16.17:
476 | resolution: {integrity: sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw==}
477 | engines: {node: '>=12'}
478 | cpu: [arm64]
479 | os: [win32]
480 | requiresBuild: true
481 | dev: true
482 | optional: true
483 |
484 | /@esbuild/win32-ia32/0.16.17:
485 | resolution: {integrity: sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig==}
486 | engines: {node: '>=12'}
487 | cpu: [ia32]
488 | os: [win32]
489 | requiresBuild: true
490 | dev: true
491 | optional: true
492 |
493 | /@esbuild/win32-x64/0.16.17:
494 | resolution: {integrity: sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==}
495 | engines: {node: '>=12'}
496 | cpu: [x64]
497 | os: [win32]
498 | requiresBuild: true
499 | dev: true
500 | optional: true
501 |
502 | /@jridgewell/gen-mapping/0.1.1:
503 | resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==}
504 | engines: {node: '>=6.0.0'}
505 | dependencies:
506 | '@jridgewell/set-array': 1.1.2
507 | '@jridgewell/sourcemap-codec': 1.4.14
508 | dev: true
509 |
510 | /@jridgewell/gen-mapping/0.3.2:
511 | resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==}
512 | engines: {node: '>=6.0.0'}
513 | dependencies:
514 | '@jridgewell/set-array': 1.1.2
515 | '@jridgewell/sourcemap-codec': 1.4.14
516 | '@jridgewell/trace-mapping': 0.3.17
517 | dev: true
518 |
519 | /@jridgewell/resolve-uri/3.1.0:
520 | resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==}
521 | engines: {node: '>=6.0.0'}
522 | dev: true
523 |
524 | /@jridgewell/set-array/1.1.2:
525 | resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==}
526 | engines: {node: '>=6.0.0'}
527 | dev: true
528 |
529 | /@jridgewell/sourcemap-codec/1.4.14:
530 | resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==}
531 | dev: true
532 |
533 | /@jridgewell/trace-mapping/0.3.17:
534 | resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==}
535 | dependencies:
536 | '@jridgewell/resolve-uri': 3.1.0
537 | '@jridgewell/sourcemap-codec': 1.4.14
538 | dev: true
539 |
540 | /@remix-run/router/1.4.0:
541 | resolution: {integrity: sha512-BJ9SxXux8zAg991UmT8slpwpsd31K1dHHbD3Ba4VzD+liLQ4WAMSxQp2d2ZPRPfN0jN2NPRowcSSoM7lCaF08Q==}
542 | engines: {node: '>=14'}
543 | dev: false
544 |
545 | /@types/debug/4.1.7:
546 | resolution: {integrity: sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==}
547 | dependencies:
548 | '@types/ms': 0.7.31
549 | dev: false
550 |
551 | /@types/hast/2.3.4:
552 | resolution: {integrity: sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==}
553 | dependencies:
554 | '@types/unist': 2.0.6
555 | dev: false
556 |
557 | /@types/mdast/3.0.10:
558 | resolution: {integrity: sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==}
559 | dependencies:
560 | '@types/unist': 2.0.6
561 | dev: false
562 |
563 | /@types/ms/0.7.31:
564 | resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==}
565 | dev: false
566 |
567 | /@types/prop-types/15.7.5:
568 | resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==}
569 |
570 | /@types/react-dom/18.0.11:
571 | resolution: {integrity: sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw==}
572 | dependencies:
573 | '@types/react': 18.0.28
574 | dev: true
575 |
576 | /@types/react/18.0.28:
577 | resolution: {integrity: sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==}
578 | dependencies:
579 | '@types/prop-types': 15.7.5
580 | '@types/scheduler': 0.16.2
581 | csstype: 3.1.1
582 |
583 | /@types/sanitize-html/2.9.0:
584 | resolution: {integrity: sha512-4fP/kEcKNj2u39IzrxWYuf/FnCCwwQCpif6wwY6ROUS1EPRIfWJjGkY3HIowY1EX/VbX5e86yq8AAE7UPMgATg==}
585 | dependencies:
586 | htmlparser2: 8.0.1
587 | dev: true
588 |
589 | /@types/scheduler/0.16.2:
590 | resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==}
591 |
592 | /@types/unist/2.0.6:
593 | resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==}
594 | dev: false
595 |
596 | /@vitejs/plugin-react/3.1.0_vite@4.1.4:
597 | resolution: {integrity: sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==}
598 | engines: {node: ^14.18.0 || >=16.0.0}
599 | peerDependencies:
600 | vite: ^4.1.0-beta.0
601 | dependencies:
602 | '@babel/core': 7.21.0
603 | '@babel/plugin-transform-react-jsx-self': 7.21.0_@babel+core@7.21.0
604 | '@babel/plugin-transform-react-jsx-source': 7.19.6_@babel+core@7.21.0
605 | magic-string: 0.27.0
606 | react-refresh: 0.14.0
607 | vite: 4.1.4_less@4.1.3
608 | transitivePeerDependencies:
609 | - supports-color
610 | dev: true
611 |
612 | /ansi-styles/3.2.1:
613 | resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
614 | engines: {node: '>=4'}
615 | dependencies:
616 | color-convert: 1.9.3
617 | dev: true
618 |
619 | /arch/2.2.0:
620 | resolution: {integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==}
621 | dev: false
622 |
623 | /asynckit/0.4.0:
624 | resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
625 | dev: false
626 |
627 | /axios/1.3.4:
628 | resolution: {integrity: sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==}
629 | dependencies:
630 | follow-redirects: 1.15.2
631 | form-data: 4.0.0
632 | proxy-from-env: 1.1.0
633 | transitivePeerDependencies:
634 | - debug
635 | dev: false
636 |
637 | /bail/2.0.2:
638 | resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==}
639 | dev: false
640 |
641 | /browserslist/4.21.5:
642 | resolution: {integrity: sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==}
643 | engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
644 | hasBin: true
645 | dependencies:
646 | caniuse-lite: 1.0.30001458
647 | electron-to-chromium: 1.4.317
648 | node-releases: 2.0.10
649 | update-browserslist-db: 1.0.10_browserslist@4.21.5
650 | dev: true
651 |
652 | /caniuse-lite/1.0.30001458:
653 | resolution: {integrity: sha512-lQ1VlUUq5q9ro9X+5gOEyH7i3vm+AYVT1WDCVB69XOZ17KZRhnZ9J0Sqz7wTHQaLBJccNCHq8/Ww5LlOIZbB0w==}
654 | dev: true
655 |
656 | /chalk/2.4.2:
657 | resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
658 | engines: {node: '>=4'}
659 | dependencies:
660 | ansi-styles: 3.2.1
661 | escape-string-regexp: 1.0.5
662 | supports-color: 5.5.0
663 | dev: true
664 |
665 | /character-entities/2.0.2:
666 | resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==}
667 | dev: false
668 |
669 | /clipboardy/3.0.0:
670 | resolution: {integrity: sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg==}
671 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
672 | dependencies:
673 | arch: 2.2.0
674 | execa: 5.1.1
675 | is-wsl: 2.2.0
676 | dev: false
677 |
678 | /clsx/1.2.1:
679 | resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==}
680 | engines: {node: '>=6'}
681 | dev: false
682 |
683 | /color-convert/1.9.3:
684 | resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
685 | dependencies:
686 | color-name: 1.1.3
687 | dev: true
688 |
689 | /color-name/1.1.3:
690 | resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
691 | dev: true
692 |
693 | /combined-stream/1.0.8:
694 | resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
695 | engines: {node: '>= 0.8'}
696 | dependencies:
697 | delayed-stream: 1.0.0
698 | dev: false
699 |
700 | /comma-separated-tokens/2.0.3:
701 | resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==}
702 | dev: false
703 |
704 | /convert-source-map/1.9.0:
705 | resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==}
706 | dev: true
707 |
708 | /copy-anything/2.0.6:
709 | resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==}
710 | dependencies:
711 | is-what: 3.14.1
712 | dev: true
713 |
714 | /core-js-pure/3.29.0:
715 | resolution: {integrity: sha512-v94gUjN5UTe1n0yN/opTihJ8QBWD2O8i19RfTZR7foONPWArnjB96QA/wk5ozu1mm6ja3udQCzOzwQXTxi3xOQ==}
716 | requiresBuild: true
717 | dev: false
718 |
719 | /core-js/3.29.0:
720 | resolution: {integrity: sha512-VG23vuEisJNkGl6XQmFJd3rEG/so/CNatqeE+7uZAwTSwFeB/qaO0be8xZYUNWprJ/GIwL8aMt9cj1kvbpTZhg==}
721 | requiresBuild: true
722 | dev: false
723 |
724 | /cross-spawn/7.0.3:
725 | resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
726 | engines: {node: '>= 8'}
727 | dependencies:
728 | path-key: 3.1.1
729 | shebang-command: 2.0.0
730 | which: 2.0.2
731 | dev: false
732 |
733 | /csstype/3.1.1:
734 | resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==}
735 |
736 | /debug/3.2.7:
737 | resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
738 | peerDependencies:
739 | supports-color: '*'
740 | peerDependenciesMeta:
741 | supports-color:
742 | optional: true
743 | dependencies:
744 | ms: 2.1.2
745 | dev: true
746 | optional: true
747 |
748 | /debug/4.3.4:
749 | resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
750 | engines: {node: '>=6.0'}
751 | peerDependencies:
752 | supports-color: '*'
753 | peerDependenciesMeta:
754 | supports-color:
755 | optional: true
756 | dependencies:
757 | ms: 2.1.2
758 |
759 | /decode-named-character-reference/1.0.2:
760 | resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==}
761 | dependencies:
762 | character-entities: 2.0.2
763 | dev: false
764 |
765 | /deepmerge/4.3.1:
766 | resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
767 | engines: {node: '>=0.10.0'}
768 | dev: false
769 |
770 | /delayed-stream/1.0.0:
771 | resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
772 | engines: {node: '>=0.4.0'}
773 | dev: false
774 |
775 | /dequal/2.0.3:
776 | resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
777 | engines: {node: '>=6'}
778 | dev: false
779 |
780 | /diff/5.1.0:
781 | resolution: {integrity: sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==}
782 | engines: {node: '>=0.3.1'}
783 | dev: false
784 |
785 | /dom-serializer/2.0.0:
786 | resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
787 | dependencies:
788 | domelementtype: 2.3.0
789 | domhandler: 5.0.3
790 | entities: 4.4.0
791 |
792 | /domelementtype/2.3.0:
793 | resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
794 |
795 | /domhandler/5.0.3:
796 | resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
797 | engines: {node: '>= 4'}
798 | dependencies:
799 | domelementtype: 2.3.0
800 |
801 | /dompurify/2.4.5:
802 | resolution: {integrity: sha512-jggCCd+8Iqp4Tsz0nIvpcb22InKEBrGz5dw3EQJMs8HPJDsKbFIO3STYtAvCfDx26Muevn1MHVI0XxjgFfmiSA==}
803 | dev: false
804 |
805 | /domutils/3.0.1:
806 | resolution: {integrity: sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==}
807 | dependencies:
808 | dom-serializer: 2.0.0
809 | domelementtype: 2.3.0
810 | domhandler: 5.0.3
811 |
812 | /electron-to-chromium/1.4.317:
813 | resolution: {integrity: sha512-JhCRm9v30FMNzQSsjl4kXaygU+qHBD0Yh7mKxyjmF0V8VwYVB6qpBRX28GyAucrM9wDCpSUctT6FpMUQxbyKuA==}
814 | dev: true
815 |
816 | /entities/4.4.0:
817 | resolution: {integrity: sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==}
818 | engines: {node: '>=0.12'}
819 |
820 | /errno/0.1.8:
821 | resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==}
822 | hasBin: true
823 | requiresBuild: true
824 | dependencies:
825 | prr: 1.0.1
826 | dev: true
827 | optional: true
828 |
829 | /esbuild/0.16.17:
830 | resolution: {integrity: sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg==}
831 | engines: {node: '>=12'}
832 | hasBin: true
833 | requiresBuild: true
834 | optionalDependencies:
835 | '@esbuild/android-arm': 0.16.17
836 | '@esbuild/android-arm64': 0.16.17
837 | '@esbuild/android-x64': 0.16.17
838 | '@esbuild/darwin-arm64': 0.16.17
839 | '@esbuild/darwin-x64': 0.16.17
840 | '@esbuild/freebsd-arm64': 0.16.17
841 | '@esbuild/freebsd-x64': 0.16.17
842 | '@esbuild/linux-arm': 0.16.17
843 | '@esbuild/linux-arm64': 0.16.17
844 | '@esbuild/linux-ia32': 0.16.17
845 | '@esbuild/linux-loong64': 0.16.17
846 | '@esbuild/linux-mips64el': 0.16.17
847 | '@esbuild/linux-ppc64': 0.16.17
848 | '@esbuild/linux-riscv64': 0.16.17
849 | '@esbuild/linux-s390x': 0.16.17
850 | '@esbuild/linux-x64': 0.16.17
851 | '@esbuild/netbsd-x64': 0.16.17
852 | '@esbuild/openbsd-x64': 0.16.17
853 | '@esbuild/sunos-x64': 0.16.17
854 | '@esbuild/win32-arm64': 0.16.17
855 | '@esbuild/win32-ia32': 0.16.17
856 | '@esbuild/win32-x64': 0.16.17
857 | dev: true
858 |
859 | /escalade/3.1.1:
860 | resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
861 | engines: {node: '>=6'}
862 | dev: true
863 |
864 | /escape-string-regexp/1.0.5:
865 | resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
866 | engines: {node: '>=0.8.0'}
867 | dev: true
868 |
869 | /escape-string-regexp/4.0.0:
870 | resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
871 | engines: {node: '>=10'}
872 | dev: false
873 |
874 | /execa/5.1.1:
875 | resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
876 | engines: {node: '>=10'}
877 | dependencies:
878 | cross-spawn: 7.0.3
879 | get-stream: 6.0.1
880 | human-signals: 2.1.0
881 | is-stream: 2.0.1
882 | merge-stream: 2.0.0
883 | npm-run-path: 4.0.1
884 | onetime: 5.1.2
885 | signal-exit: 3.0.7
886 | strip-final-newline: 2.0.0
887 | dev: false
888 |
889 | /extend/3.0.2:
890 | resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
891 | dev: false
892 |
893 | /follow-redirects/1.15.2:
894 | resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
895 | engines: {node: '>=4.0'}
896 | peerDependencies:
897 | debug: '*'
898 | peerDependenciesMeta:
899 | debug:
900 | optional: true
901 | dev: false
902 |
903 | /form-data/4.0.0:
904 | resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
905 | engines: {node: '>= 6'}
906 | dependencies:
907 | asynckit: 0.4.0
908 | combined-stream: 1.0.8
909 | mime-types: 2.1.35
910 | dev: false
911 |
912 | /fsevents/2.3.2:
913 | resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
914 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
915 | os: [darwin]
916 | requiresBuild: true
917 | dev: true
918 | optional: true
919 |
920 | /function-bind/1.1.1:
921 | resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
922 | dev: true
923 |
924 | /gensync/1.0.0-beta.2:
925 | resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
926 | engines: {node: '>=6.9.0'}
927 | dev: true
928 |
929 | /get-stream/6.0.1:
930 | resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
931 | engines: {node: '>=10'}
932 | dev: false
933 |
934 | /globals/11.12.0:
935 | resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
936 | engines: {node: '>=4'}
937 | dev: true
938 |
939 | /graceful-fs/4.2.10:
940 | resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==}
941 | requiresBuild: true
942 | dev: true
943 | optional: true
944 |
945 | /has-flag/3.0.0:
946 | resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
947 | engines: {node: '>=4'}
948 | dev: true
949 |
950 | /has/1.0.3:
951 | resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
952 | engines: {node: '>= 0.4.0'}
953 | dependencies:
954 | function-bind: 1.1.1
955 | dev: true
956 |
957 | /hast-util-whitespace/2.0.1:
958 | resolution: {integrity: sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng==}
959 | dev: false
960 |
961 | /htmlparser2/8.0.1:
962 | resolution: {integrity: sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==}
963 | dependencies:
964 | domelementtype: 2.3.0
965 | domhandler: 5.0.3
966 | domutils: 3.0.1
967 | entities: 4.4.0
968 |
969 | /human-signals/2.1.0:
970 | resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
971 | engines: {node: '>=10.17.0'}
972 | dev: false
973 |
974 | /iconv-lite/0.6.3:
975 | resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
976 | engines: {node: '>=0.10.0'}
977 | dependencies:
978 | safer-buffer: 2.1.2
979 | dev: true
980 | optional: true
981 |
982 | /image-size/0.5.5:
983 | resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==}
984 | engines: {node: '>=0.10.0'}
985 | hasBin: true
986 | requiresBuild: true
987 | dev: true
988 | optional: true
989 |
990 | /inline-style-parser/0.1.1:
991 | resolution: {integrity: sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==}
992 | dev: false
993 |
994 | /intersection-observer/0.12.2:
995 | resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==}
996 | dev: false
997 |
998 | /is-buffer/2.0.5:
999 | resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==}
1000 | engines: {node: '>=4'}
1001 | dev: false
1002 |
1003 | /is-core-module/2.11.0:
1004 | resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==}
1005 | dependencies:
1006 | has: 1.0.3
1007 | dev: true
1008 |
1009 | /is-docker/2.2.1:
1010 | resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==}
1011 | engines: {node: '>=8'}
1012 | hasBin: true
1013 | dev: false
1014 |
1015 | /is-plain-obj/4.1.0:
1016 | resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
1017 | engines: {node: '>=12'}
1018 | dev: false
1019 |
1020 | /is-plain-object/5.0.0:
1021 | resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==}
1022 | engines: {node: '>=0.10.0'}
1023 | dev: false
1024 |
1025 | /is-stream/2.0.1:
1026 | resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
1027 | engines: {node: '>=8'}
1028 | dev: false
1029 |
1030 | /is-what/3.14.1:
1031 | resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==}
1032 | dev: true
1033 |
1034 | /is-wsl/2.2.0:
1035 | resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
1036 | engines: {node: '>=8'}
1037 | dependencies:
1038 | is-docker: 2.2.1
1039 | dev: false
1040 |
1041 | /isexe/2.0.0:
1042 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
1043 | dev: false
1044 |
1045 | /js-tokens/4.0.0:
1046 | resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
1047 |
1048 | /jsesc/2.5.2:
1049 | resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==}
1050 | engines: {node: '>=4'}
1051 | hasBin: true
1052 | dev: true
1053 |
1054 | /json5/2.2.3:
1055 | resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
1056 | engines: {node: '>=6'}
1057 | hasBin: true
1058 | dev: true
1059 |
1060 | /kleur/4.1.5:
1061 | resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
1062 | engines: {node: '>=6'}
1063 | dev: false
1064 |
1065 | /less/4.1.3:
1066 | resolution: {integrity: sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==}
1067 | engines: {node: '>=6'}
1068 | hasBin: true
1069 | dependencies:
1070 | copy-anything: 2.0.6
1071 | parse-node-version: 1.0.1
1072 | tslib: 2.5.0
1073 | optionalDependencies:
1074 | errno: 0.1.8
1075 | graceful-fs: 4.2.10
1076 | image-size: 0.5.5
1077 | make-dir: 2.1.0
1078 | mime: 1.6.0
1079 | needle: 3.2.0
1080 | source-map: 0.6.1
1081 | transitivePeerDependencies:
1082 | - supports-color
1083 | dev: true
1084 |
1085 | /loose-envify/1.4.0:
1086 | resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
1087 | hasBin: true
1088 | dependencies:
1089 | js-tokens: 4.0.0
1090 | dev: false
1091 |
1092 | /lru-cache/5.1.1:
1093 | resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
1094 | dependencies:
1095 | yallist: 3.1.1
1096 | dev: true
1097 |
1098 | /magic-string/0.27.0:
1099 | resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==}
1100 | engines: {node: '>=12'}
1101 | dependencies:
1102 | '@jridgewell/sourcemap-codec': 1.4.14
1103 | dev: true
1104 |
1105 | /make-dir/2.1.0:
1106 | resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==}
1107 | engines: {node: '>=6'}
1108 | requiresBuild: true
1109 | dependencies:
1110 | pify: 4.0.1
1111 | semver: 5.7.1
1112 | dev: true
1113 | optional: true
1114 |
1115 | /md-editor-rt/2.10.1_biqbaboplfbrettd7655fr4n2y:
1116 | resolution: {integrity: sha512-0rbytgoR2XG5GobBkoS/x9SckfwUOlS9aT34ltDxUM45h580Ud9lRbqzJiIkKdHnQqNUT901Z/4zJ/0xPuc8aw==}
1117 | peerDependencies:
1118 | react: '>=16.9.0'
1119 | react-dom: '>=16.9.0'
1120 | dependencies:
1121 | react: 18.2.0
1122 | react-dom: 18.2.0_react@18.2.0
1123 | dev: false
1124 |
1125 | /mdast-util-definitions/5.1.2:
1126 | resolution: {integrity: sha512-8SVPMuHqlPME/z3gqVwWY4zVXn8lqKv/pAhC57FuJ40ImXyBpmO5ukh98zB2v7Blql2FiHjHv9LVztSIqjY+MA==}
1127 | dependencies:
1128 | '@types/mdast': 3.0.10
1129 | '@types/unist': 2.0.6
1130 | unist-util-visit: 4.1.2
1131 | dev: false
1132 |
1133 | /mdast-util-from-markdown/1.3.0:
1134 | resolution: {integrity: sha512-HN3W1gRIuN/ZW295c7zi7g9lVBllMgZE40RxCX37wrTPWXCWtpvOZdfnuK+1WNpvZje6XuJeI3Wnb4TJEUem+g==}
1135 | dependencies:
1136 | '@types/mdast': 3.0.10
1137 | '@types/unist': 2.0.6
1138 | decode-named-character-reference: 1.0.2
1139 | mdast-util-to-string: 3.1.1
1140 | micromark: 3.1.0
1141 | micromark-util-decode-numeric-character-reference: 1.0.0
1142 | micromark-util-decode-string: 1.0.2
1143 | micromark-util-normalize-identifier: 1.0.0
1144 | micromark-util-symbol: 1.0.1
1145 | micromark-util-types: 1.0.2
1146 | unist-util-stringify-position: 3.0.3
1147 | uvu: 0.5.6
1148 | transitivePeerDependencies:
1149 | - supports-color
1150 | dev: false
1151 |
1152 | /mdast-util-to-hast/12.3.0:
1153 | resolution: {integrity: sha512-pits93r8PhnIoU4Vy9bjW39M2jJ6/tdHyja9rrot9uujkN7UTU9SDnE6WNJz/IGyQk3XHX6yNNtrBH6cQzm8Hw==}
1154 | dependencies:
1155 | '@types/hast': 2.3.4
1156 | '@types/mdast': 3.0.10
1157 | mdast-util-definitions: 5.1.2
1158 | micromark-util-sanitize-uri: 1.1.0
1159 | trim-lines: 3.0.1
1160 | unist-util-generated: 2.0.1
1161 | unist-util-position: 4.0.4
1162 | unist-util-visit: 4.1.2
1163 | dev: false
1164 |
1165 | /mdast-util-to-string/3.1.1:
1166 | resolution: {integrity: sha512-tGvhT94e+cVnQt8JWE9/b3cUQZWS732TJxXHktvP+BYo62PpYD53Ls/6cC60rW21dW+txxiM4zMdc6abASvZKA==}
1167 | dependencies:
1168 | '@types/mdast': 3.0.10
1169 | dev: false
1170 |
1171 | /merge-stream/2.0.0:
1172 | resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
1173 | dev: false
1174 |
1175 | /micromark-core-commonmark/1.0.6:
1176 | resolution: {integrity: sha512-K+PkJTxqjFfSNkfAhp4GB+cZPfQd6dxtTXnf+RjZOV7T4EEXnvgzOcnp+eSTmpGk9d1S9sL6/lqrgSNn/s0HZA==}
1177 | dependencies:
1178 | decode-named-character-reference: 1.0.2
1179 | micromark-factory-destination: 1.0.0
1180 | micromark-factory-label: 1.0.2
1181 | micromark-factory-space: 1.0.0
1182 | micromark-factory-title: 1.0.2
1183 | micromark-factory-whitespace: 1.0.0
1184 | micromark-util-character: 1.1.0
1185 | micromark-util-chunked: 1.0.0
1186 | micromark-util-classify-character: 1.0.0
1187 | micromark-util-html-tag-name: 1.1.0
1188 | micromark-util-normalize-identifier: 1.0.0
1189 | micromark-util-resolve-all: 1.0.0
1190 | micromark-util-subtokenize: 1.0.2
1191 | micromark-util-symbol: 1.0.1
1192 | micromark-util-types: 1.0.2
1193 | uvu: 0.5.6
1194 | dev: false
1195 |
1196 | /micromark-factory-destination/1.0.0:
1197 | resolution: {integrity: sha512-eUBA7Rs1/xtTVun9TmV3gjfPz2wEwgK5R5xcbIM5ZYAtvGF6JkyaDsj0agx8urXnO31tEO6Ug83iVH3tdedLnw==}
1198 | dependencies:
1199 | micromark-util-character: 1.1.0
1200 | micromark-util-symbol: 1.0.1
1201 | micromark-util-types: 1.0.2
1202 | dev: false
1203 |
1204 | /micromark-factory-label/1.0.2:
1205 | resolution: {integrity: sha512-CTIwxlOnU7dEshXDQ+dsr2n+yxpP0+fn271pu0bwDIS8uqfFcumXpj5mLn3hSC8iw2MUr6Gx8EcKng1dD7i6hg==}
1206 | dependencies:
1207 | micromark-util-character: 1.1.0
1208 | micromark-util-symbol: 1.0.1
1209 | micromark-util-types: 1.0.2
1210 | uvu: 0.5.6
1211 | dev: false
1212 |
1213 | /micromark-factory-space/1.0.0:
1214 | resolution: {integrity: sha512-qUmqs4kj9a5yBnk3JMLyjtWYN6Mzfcx8uJfi5XAveBniDevmZasdGBba5b4QsvRcAkmvGo5ACmSUmyGiKTLZew==}
1215 | dependencies:
1216 | micromark-util-character: 1.1.0
1217 | micromark-util-types: 1.0.2
1218 | dev: false
1219 |
1220 | /micromark-factory-title/1.0.2:
1221 | resolution: {integrity: sha512-zily+Nr4yFqgMGRKLpTVsNl5L4PMu485fGFDOQJQBl2NFpjGte1e86zC0da93wf97jrc4+2G2GQudFMHn3IX+A==}
1222 | dependencies:
1223 | micromark-factory-space: 1.0.0
1224 | micromark-util-character: 1.1.0
1225 | micromark-util-symbol: 1.0.1
1226 | micromark-util-types: 1.0.2
1227 | uvu: 0.5.6
1228 | dev: false
1229 |
1230 | /micromark-factory-whitespace/1.0.0:
1231 | resolution: {integrity: sha512-Qx7uEyahU1lt1RnsECBiuEbfr9INjQTGa6Err+gF3g0Tx4YEviPbqqGKNv/NrBaE7dVHdn1bVZKM/n5I/Bak7A==}
1232 | dependencies:
1233 | micromark-factory-space: 1.0.0
1234 | micromark-util-character: 1.1.0
1235 | micromark-util-symbol: 1.0.1
1236 | micromark-util-types: 1.0.2
1237 | dev: false
1238 |
1239 | /micromark-util-character/1.1.0:
1240 | resolution: {integrity: sha512-agJ5B3unGNJ9rJvADMJ5ZiYjBRyDpzKAOk01Kpi1TKhlT1APx3XZk6eN7RtSz1erbWHC2L8T3xLZ81wdtGRZzg==}
1241 | dependencies:
1242 | micromark-util-symbol: 1.0.1
1243 | micromark-util-types: 1.0.2
1244 | dev: false
1245 |
1246 | /micromark-util-chunked/1.0.0:
1247 | resolution: {integrity: sha512-5e8xTis5tEZKgesfbQMKRCyzvffRRUX+lK/y+DvsMFdabAicPkkZV6gO+FEWi9RfuKKoxxPwNL+dFF0SMImc1g==}
1248 | dependencies:
1249 | micromark-util-symbol: 1.0.1
1250 | dev: false
1251 |
1252 | /micromark-util-classify-character/1.0.0:
1253 | resolution: {integrity: sha512-F8oW2KKrQRb3vS5ud5HIqBVkCqQi224Nm55o5wYLzY/9PwHGXC01tr3d7+TqHHz6zrKQ72Okwtvm/xQm6OVNZA==}
1254 | dependencies:
1255 | micromark-util-character: 1.1.0
1256 | micromark-util-symbol: 1.0.1
1257 | micromark-util-types: 1.0.2
1258 | dev: false
1259 |
1260 | /micromark-util-combine-extensions/1.0.0:
1261 | resolution: {integrity: sha512-J8H058vFBdo/6+AsjHp2NF7AJ02SZtWaVUjsayNFeAiydTxUwViQPxN0Hf8dp4FmCQi0UUFovFsEyRSUmFH3MA==}
1262 | dependencies:
1263 | micromark-util-chunked: 1.0.0
1264 | micromark-util-types: 1.0.2
1265 | dev: false
1266 |
1267 | /micromark-util-decode-numeric-character-reference/1.0.0:
1268 | resolution: {integrity: sha512-OzO9AI5VUtrTD7KSdagf4MWgHMtET17Ua1fIpXTpuhclCqD8egFWo85GxSGvxgkGS74bEahvtM0WP0HjvV0e4w==}
1269 | dependencies:
1270 | micromark-util-symbol: 1.0.1
1271 | dev: false
1272 |
1273 | /micromark-util-decode-string/1.0.2:
1274 | resolution: {integrity: sha512-DLT5Ho02qr6QWVNYbRZ3RYOSSWWFuH3tJexd3dgN1odEuPNxCngTCXJum7+ViRAd9BbdxCvMToPOD/IvVhzG6Q==}
1275 | dependencies:
1276 | decode-named-character-reference: 1.0.2
1277 | micromark-util-character: 1.1.0
1278 | micromark-util-decode-numeric-character-reference: 1.0.0
1279 | micromark-util-symbol: 1.0.1
1280 | dev: false
1281 |
1282 | /micromark-util-encode/1.0.1:
1283 | resolution: {integrity: sha512-U2s5YdnAYexjKDel31SVMPbfi+eF8y1U4pfiRW/Y8EFVCy/vgxk/2wWTxzcqE71LHtCuCzlBDRU2a5CQ5j+mQA==}
1284 | dev: false
1285 |
1286 | /micromark-util-html-tag-name/1.1.0:
1287 | resolution: {integrity: sha512-BKlClMmYROy9UiV03SwNmckkjn8QHVaWkqoAqzivabvdGcwNGMMMH/5szAnywmsTBUzDsU57/mFi0sp4BQO6dA==}
1288 | dev: false
1289 |
1290 | /micromark-util-normalize-identifier/1.0.0:
1291 | resolution: {integrity: sha512-yg+zrL14bBTFrQ7n35CmByWUTFsgst5JhA4gJYoty4Dqzj4Z4Fr/DHekSS5aLfH9bdlfnSvKAWsAgJhIbogyBg==}
1292 | dependencies:
1293 | micromark-util-symbol: 1.0.1
1294 | dev: false
1295 |
1296 | /micromark-util-resolve-all/1.0.0:
1297 | resolution: {integrity: sha512-CB/AGk98u50k42kvgaMM94wzBqozSzDDaonKU7P7jwQIuH2RU0TeBqGYJz2WY1UdihhjweivStrJ2JdkdEmcfw==}
1298 | dependencies:
1299 | micromark-util-types: 1.0.2
1300 | dev: false
1301 |
1302 | /micromark-util-sanitize-uri/1.1.0:
1303 | resolution: {integrity: sha512-RoxtuSCX6sUNtxhbmsEFQfWzs8VN7cTctmBPvYivo98xb/kDEoTCtJQX5wyzIYEmk/lvNFTat4hL8oW0KndFpg==}
1304 | dependencies:
1305 | micromark-util-character: 1.1.0
1306 | micromark-util-encode: 1.0.1
1307 | micromark-util-symbol: 1.0.1
1308 | dev: false
1309 |
1310 | /micromark-util-subtokenize/1.0.2:
1311 | resolution: {integrity: sha512-d90uqCnXp/cy4G881Ub4psE57Sf8YD0pim9QdjCRNjfas2M1u6Lbt+XZK9gnHL2XFhnozZiEdCa9CNfXSfQ6xA==}
1312 | dependencies:
1313 | micromark-util-chunked: 1.0.0
1314 | micromark-util-symbol: 1.0.1
1315 | micromark-util-types: 1.0.2
1316 | uvu: 0.5.6
1317 | dev: false
1318 |
1319 | /micromark-util-symbol/1.0.1:
1320 | resolution: {integrity: sha512-oKDEMK2u5qqAptasDAwWDXq0tG9AssVwAx3E9bBF3t/shRIGsWIRG+cGafs2p/SnDSOecnt6hZPCE2o6lHfFmQ==}
1321 | dev: false
1322 |
1323 | /micromark-util-types/1.0.2:
1324 | resolution: {integrity: sha512-DCfg/T8fcrhrRKTPjRrw/5LLvdGV7BHySf/1LOZx7TzWZdYRjogNtyNq885z3nNallwr3QUKARjqvHqX1/7t+w==}
1325 | dev: false
1326 |
1327 | /micromark/3.1.0:
1328 | resolution: {integrity: sha512-6Mj0yHLdUZjHnOPgr5xfWIMqMWS12zDN6iws9SLuSz76W8jTtAv24MN4/CL7gJrl5vtxGInkkqDv/JIoRsQOvA==}
1329 | dependencies:
1330 | '@types/debug': 4.1.7
1331 | debug: 4.3.4
1332 | decode-named-character-reference: 1.0.2
1333 | micromark-core-commonmark: 1.0.6
1334 | micromark-factory-space: 1.0.0
1335 | micromark-util-character: 1.1.0
1336 | micromark-util-chunked: 1.0.0
1337 | micromark-util-combine-extensions: 1.0.0
1338 | micromark-util-decode-numeric-character-reference: 1.0.0
1339 | micromark-util-encode: 1.0.1
1340 | micromark-util-normalize-identifier: 1.0.0
1341 | micromark-util-resolve-all: 1.0.0
1342 | micromark-util-sanitize-uri: 1.1.0
1343 | micromark-util-subtokenize: 1.0.2
1344 | micromark-util-symbol: 1.0.1
1345 | micromark-util-types: 1.0.2
1346 | uvu: 0.5.6
1347 | transitivePeerDependencies:
1348 | - supports-color
1349 | dev: false
1350 |
1351 | /mime-db/1.52.0:
1352 | resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
1353 | engines: {node: '>= 0.6'}
1354 | dev: false
1355 |
1356 | /mime-types/2.1.35:
1357 | resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
1358 | engines: {node: '>= 0.6'}
1359 | dependencies:
1360 | mime-db: 1.52.0
1361 | dev: false
1362 |
1363 | /mime/1.6.0:
1364 | resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
1365 | engines: {node: '>=4'}
1366 | hasBin: true
1367 | requiresBuild: true
1368 | dev: true
1369 | optional: true
1370 |
1371 | /mimic-fn/2.1.0:
1372 | resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
1373 | engines: {node: '>=6'}
1374 | dev: false
1375 |
1376 | /mri/1.2.0:
1377 | resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
1378 | engines: {node: '>=4'}
1379 | dev: false
1380 |
1381 | /ms/2.1.2:
1382 | resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
1383 |
1384 | /nanoid/3.3.4:
1385 | resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==}
1386 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
1387 | hasBin: true
1388 |
1389 | /needle/3.2.0:
1390 | resolution: {integrity: sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ==}
1391 | engines: {node: '>= 4.4.x'}
1392 | hasBin: true
1393 | requiresBuild: true
1394 | dependencies:
1395 | debug: 3.2.7
1396 | iconv-lite: 0.6.3
1397 | sax: 1.2.4
1398 | transitivePeerDependencies:
1399 | - supports-color
1400 | dev: true
1401 | optional: true
1402 |
1403 | /node-releases/2.0.10:
1404 | resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==}
1405 | dev: true
1406 |
1407 | /npm-run-path/4.0.1:
1408 | resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
1409 | engines: {node: '>=8'}
1410 | dependencies:
1411 | path-key: 3.1.1
1412 | dev: false
1413 |
1414 | /object-assign/4.1.1:
1415 | resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
1416 | engines: {node: '>=0.10.0'}
1417 | dev: false
1418 |
1419 | /onetime/5.1.2:
1420 | resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
1421 | engines: {node: '>=6'}
1422 | dependencies:
1423 | mimic-fn: 2.1.0
1424 | dev: false
1425 |
1426 | /parse-node-version/1.0.1:
1427 | resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==}
1428 | engines: {node: '>= 0.10'}
1429 | dev: true
1430 |
1431 | /parse-srcset/1.0.2:
1432 | resolution: {integrity: sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==}
1433 | dev: false
1434 |
1435 | /path-key/3.1.1:
1436 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
1437 | engines: {node: '>=8'}
1438 | dev: false
1439 |
1440 | /path-parse/1.0.7:
1441 | resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
1442 | dev: true
1443 |
1444 | /picocolors/1.0.0:
1445 | resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
1446 |
1447 | /pify/4.0.1:
1448 | resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==}
1449 | engines: {node: '>=6'}
1450 | dev: true
1451 | optional: true
1452 |
1453 | /postcss/8.4.21:
1454 | resolution: {integrity: sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==}
1455 | engines: {node: ^10 || ^12 || >=14}
1456 | dependencies:
1457 | nanoid: 3.3.4
1458 | picocolors: 1.0.0
1459 | source-map-js: 1.0.2
1460 |
1461 | /prop-types/15.8.1:
1462 | resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
1463 | dependencies:
1464 | loose-envify: 1.4.0
1465 | object-assign: 4.1.1
1466 | react-is: 16.13.1
1467 | dev: false
1468 |
1469 | /property-information/6.2.0:
1470 | resolution: {integrity: sha512-kma4U7AFCTwpqq5twzC1YVIDXSqg6qQK6JN0smOw8fgRy1OkMi0CYSzFmsy6dnqSenamAtj0CyXMUJ1Mf6oROg==}
1471 | dev: false
1472 |
1473 | /proxy-from-env/1.1.0:
1474 | resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
1475 | dev: false
1476 |
1477 | /prr/1.0.1:
1478 | resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==}
1479 | dev: true
1480 | optional: true
1481 |
1482 | /react-dom/18.2.0_react@18.2.0:
1483 | resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==}
1484 | peerDependencies:
1485 | react: ^18.2.0
1486 | dependencies:
1487 | loose-envify: 1.4.0
1488 | react: 18.2.0
1489 | scheduler: 0.23.0
1490 | dev: false
1491 |
1492 | /react-is/16.13.1:
1493 | resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
1494 | dev: false
1495 |
1496 | /react-is/18.2.0:
1497 | resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==}
1498 | dev: false
1499 |
1500 | /react-markdown/8.0.5_pmekkgnqduwlme35zpnqhenc34:
1501 | resolution: {integrity: sha512-jGJolWWmOWAvzf+xMdB9zwStViODyyFQhNB/bwCerbBKmrTmgmA599CGiOlP58OId1IMoIRsA8UdI1Lod4zb5A==}
1502 | peerDependencies:
1503 | '@types/react': '>=16'
1504 | react: '>=16'
1505 | dependencies:
1506 | '@types/hast': 2.3.4
1507 | '@types/prop-types': 15.7.5
1508 | '@types/react': 18.0.28
1509 | '@types/unist': 2.0.6
1510 | comma-separated-tokens: 2.0.3
1511 | hast-util-whitespace: 2.0.1
1512 | prop-types: 15.8.1
1513 | property-information: 6.2.0
1514 | react: 18.2.0
1515 | react-is: 18.2.0
1516 | remark-parse: 10.0.1
1517 | remark-rehype: 10.1.0
1518 | space-separated-tokens: 2.0.2
1519 | style-to-object: 0.4.1
1520 | unified: 10.1.2
1521 | unist-util-visit: 4.1.2
1522 | vfile: 5.3.7
1523 | transitivePeerDependencies:
1524 | - supports-color
1525 | dev: false
1526 |
1527 | /react-refresh/0.14.0:
1528 | resolution: {integrity: sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==}
1529 | engines: {node: '>=0.10.0'}
1530 | dev: true
1531 |
1532 | /react-router-dom/6.9.0_biqbaboplfbrettd7655fr4n2y:
1533 | resolution: {integrity: sha512-/seUAPY01VAuwkGyVBPCn1OXfVbaWGGu4QN9uj0kCPcTyNYgL1ldZpxZUpRU7BLheKQI4Twtl/OW2nHRF1u26Q==}
1534 | engines: {node: '>=14'}
1535 | peerDependencies:
1536 | react: '>=16.8'
1537 | react-dom: '>=16.8'
1538 | dependencies:
1539 | '@remix-run/router': 1.4.0
1540 | react: 18.2.0
1541 | react-dom: 18.2.0_react@18.2.0
1542 | react-router: 6.9.0_react@18.2.0
1543 | dev: false
1544 |
1545 | /react-router/6.9.0_react@18.2.0:
1546 | resolution: {integrity: sha512-51lKevGNUHrt6kLuX3e/ihrXoXCa9ixY/nVWRLlob4r/l0f45x3SzBvYJe3ctleLUQQ5fVa4RGgJOTH7D9Umhw==}
1547 | engines: {node: '>=14'}
1548 | peerDependencies:
1549 | react: '>=16.8'
1550 | dependencies:
1551 | '@remix-run/router': 1.4.0
1552 | react: 18.2.0
1553 | dev: false
1554 |
1555 | /react/18.2.0:
1556 | resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
1557 | engines: {node: '>=0.10.0'}
1558 | dependencies:
1559 | loose-envify: 1.4.0
1560 | dev: false
1561 |
1562 | /regenerator-runtime/0.13.11:
1563 | resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
1564 | dev: false
1565 |
1566 | /remark-parse/10.0.1:
1567 | resolution: {integrity: sha512-1fUyHr2jLsVOkhbvPRBJ5zTKZZyD6yZzYaWCS6BPBdQ8vEMBCH+9zNCDA6tET/zHCi/jLqjCWtlJZUPk+DbnFw==}
1568 | dependencies:
1569 | '@types/mdast': 3.0.10
1570 | mdast-util-from-markdown: 1.3.0
1571 | unified: 10.1.2
1572 | transitivePeerDependencies:
1573 | - supports-color
1574 | dev: false
1575 |
1576 | /remark-rehype/10.1.0:
1577 | resolution: {integrity: sha512-EFmR5zppdBp0WQeDVZ/b66CWJipB2q2VLNFMabzDSGR66Z2fQii83G5gTBbgGEnEEA0QRussvrFHxk1HWGJskw==}
1578 | dependencies:
1579 | '@types/hast': 2.3.4
1580 | '@types/mdast': 3.0.10
1581 | mdast-util-to-hast: 12.3.0
1582 | unified: 10.1.2
1583 | dev: false
1584 |
1585 | /resolve/1.22.1:
1586 | resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==}
1587 | hasBin: true
1588 | dependencies:
1589 | is-core-module: 2.11.0
1590 | path-parse: 1.0.7
1591 | supports-preserve-symlinks-flag: 1.0.0
1592 | dev: true
1593 |
1594 | /rollup/3.18.0:
1595 | resolution: {integrity: sha512-J8C6VfEBjkvYPESMQYxKHxNOh4A5a3FlP+0BETGo34HEcE4eTlgCrO2+eWzlu2a/sHs2QUkZco+wscH7jhhgWg==}
1596 | engines: {node: '>=14.18.0', npm: '>=8.0.0'}
1597 | hasBin: true
1598 | optionalDependencies:
1599 | fsevents: 2.3.2
1600 | dev: true
1601 |
1602 | /sade/1.8.1:
1603 | resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==}
1604 | engines: {node: '>=6'}
1605 | dependencies:
1606 | mri: 1.2.0
1607 | dev: false
1608 |
1609 | /safer-buffer/2.1.2:
1610 | resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
1611 | dev: true
1612 | optional: true
1613 |
1614 | /sanitize-html/2.10.0:
1615 | resolution: {integrity: sha512-JqdovUd81dG4k87vZt6uA6YhDfWkUGruUu/aPmXLxXi45gZExnt9Bnw/qeQU8oGf82vPyaE0vO4aH0PbobB9JQ==}
1616 | dependencies:
1617 | deepmerge: 4.3.1
1618 | escape-string-regexp: 4.0.0
1619 | htmlparser2: 8.0.1
1620 | is-plain-object: 5.0.0
1621 | parse-srcset: 1.0.2
1622 | postcss: 8.4.21
1623 | dev: false
1624 |
1625 | /sax/1.2.4:
1626 | resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==}
1627 | dev: true
1628 | optional: true
1629 |
1630 | /scheduler/0.23.0:
1631 | resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==}
1632 | dependencies:
1633 | loose-envify: 1.4.0
1634 | dev: false
1635 |
1636 | /semver/5.7.1:
1637 | resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==}
1638 | hasBin: true
1639 | dev: true
1640 | optional: true
1641 |
1642 | /semver/6.3.0:
1643 | resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==}
1644 | hasBin: true
1645 | dev: true
1646 |
1647 | /shebang-command/2.0.0:
1648 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
1649 | engines: {node: '>=8'}
1650 | dependencies:
1651 | shebang-regex: 3.0.0
1652 | dev: false
1653 |
1654 | /shebang-regex/3.0.0:
1655 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
1656 | engines: {node: '>=8'}
1657 | dev: false
1658 |
1659 | /signal-exit/3.0.7:
1660 | resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
1661 | dev: false
1662 |
1663 | /source-map-js/1.0.2:
1664 | resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
1665 | engines: {node: '>=0.10.0'}
1666 |
1667 | /source-map/0.6.1:
1668 | resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
1669 | engines: {node: '>=0.10.0'}
1670 | requiresBuild: true
1671 | dev: true
1672 | optional: true
1673 |
1674 | /space-separated-tokens/2.0.2:
1675 | resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
1676 | dev: false
1677 |
1678 | /strip-final-newline/2.0.0:
1679 | resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
1680 | engines: {node: '>=6'}
1681 | dev: false
1682 |
1683 | /style-to-object/0.4.1:
1684 | resolution: {integrity: sha512-HFpbb5gr2ypci7Qw+IOhnP2zOU7e77b+rzM+wTzXzfi1PrtBCX0E7Pk4wL4iTLnhzZ+JgEGAhX81ebTg/aYjQw==}
1685 | dependencies:
1686 | inline-style-parser: 0.1.1
1687 | dev: false
1688 |
1689 | /supports-color/5.5.0:
1690 | resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
1691 | engines: {node: '>=4'}
1692 | dependencies:
1693 | has-flag: 3.0.0
1694 | dev: true
1695 |
1696 | /supports-preserve-symlinks-flag/1.0.0:
1697 | resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
1698 | engines: {node: '>= 0.4'}
1699 | dev: true
1700 |
1701 | /to-fast-properties/2.0.0:
1702 | resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
1703 | engines: {node: '>=4'}
1704 | dev: true
1705 |
1706 | /trim-lines/3.0.1:
1707 | resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
1708 | dev: false
1709 |
1710 | /trough/2.1.0:
1711 | resolution: {integrity: sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==}
1712 | dev: false
1713 |
1714 | /tslib/2.5.0:
1715 | resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==}
1716 | dev: true
1717 |
1718 | /typescript/4.9.5:
1719 | resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==}
1720 | engines: {node: '>=4.2.0'}
1721 | hasBin: true
1722 | dev: true
1723 |
1724 | /unified/10.1.2:
1725 | resolution: {integrity: sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==}
1726 | dependencies:
1727 | '@types/unist': 2.0.6
1728 | bail: 2.0.2
1729 | extend: 3.0.2
1730 | is-buffer: 2.0.5
1731 | is-plain-obj: 4.1.0
1732 | trough: 2.1.0
1733 | vfile: 5.3.7
1734 | dev: false
1735 |
1736 | /unist-util-generated/2.0.1:
1737 | resolution: {integrity: sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A==}
1738 | dev: false
1739 |
1740 | /unist-util-is/5.2.1:
1741 | resolution: {integrity: sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==}
1742 | dependencies:
1743 | '@types/unist': 2.0.6
1744 | dev: false
1745 |
1746 | /unist-util-position/4.0.4:
1747 | resolution: {integrity: sha512-kUBE91efOWfIVBo8xzh/uZQ7p9ffYRtUbMRZBNFYwf0RK8koUMx6dGUfwylLOKmaT2cs4wSW96QoYUSXAyEtpg==}
1748 | dependencies:
1749 | '@types/unist': 2.0.6
1750 | dev: false
1751 |
1752 | /unist-util-stringify-position/3.0.3:
1753 | resolution: {integrity: sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==}
1754 | dependencies:
1755 | '@types/unist': 2.0.6
1756 | dev: false
1757 |
1758 | /unist-util-visit-parents/5.1.3:
1759 | resolution: {integrity: sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==}
1760 | dependencies:
1761 | '@types/unist': 2.0.6
1762 | unist-util-is: 5.2.1
1763 | dev: false
1764 |
1765 | /unist-util-visit/4.1.2:
1766 | resolution: {integrity: sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==}
1767 | dependencies:
1768 | '@types/unist': 2.0.6
1769 | unist-util-is: 5.2.1
1770 | unist-util-visit-parents: 5.1.3
1771 | dev: false
1772 |
1773 | /update-browserslist-db/1.0.10_browserslist@4.21.5:
1774 | resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==}
1775 | hasBin: true
1776 | peerDependencies:
1777 | browserslist: '>= 4.21.0'
1778 | dependencies:
1779 | browserslist: 4.21.5
1780 | escalade: 3.1.1
1781 | picocolors: 1.0.0
1782 | dev: true
1783 |
1784 | /uvu/0.5.6:
1785 | resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==}
1786 | engines: {node: '>=8'}
1787 | hasBin: true
1788 | dependencies:
1789 | dequal: 2.0.3
1790 | diff: 5.1.0
1791 | kleur: 4.1.5
1792 | sade: 1.8.1
1793 | dev: false
1794 |
1795 | /vfile-message/3.1.4:
1796 | resolution: {integrity: sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==}
1797 | dependencies:
1798 | '@types/unist': 2.0.6
1799 | unist-util-stringify-position: 3.0.3
1800 | dev: false
1801 |
1802 | /vfile/5.3.7:
1803 | resolution: {integrity: sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==}
1804 | dependencies:
1805 | '@types/unist': 2.0.6
1806 | is-buffer: 2.0.5
1807 | unist-util-stringify-position: 3.0.3
1808 | vfile-message: 3.1.4
1809 | dev: false
1810 |
1811 | /vite/4.1.4_less@4.1.3:
1812 | resolution: {integrity: sha512-3knk/HsbSTKEin43zHu7jTwYWv81f8kgAL99G5NWBcA1LKvtvcVAC4JjBH1arBunO9kQka+1oGbrMKOjk4ZrBg==}
1813 | engines: {node: ^14.18.0 || >=16.0.0}
1814 | hasBin: true
1815 | peerDependencies:
1816 | '@types/node': '>= 14'
1817 | less: '*'
1818 | sass: '*'
1819 | stylus: '*'
1820 | sugarss: '*'
1821 | terser: ^5.4.0
1822 | peerDependenciesMeta:
1823 | '@types/node':
1824 | optional: true
1825 | less:
1826 | optional: true
1827 | sass:
1828 | optional: true
1829 | stylus:
1830 | optional: true
1831 | sugarss:
1832 | optional: true
1833 | terser:
1834 | optional: true
1835 | dependencies:
1836 | esbuild: 0.16.17
1837 | less: 4.1.3
1838 | postcss: 8.4.21
1839 | resolve: 1.22.1
1840 | rollup: 3.18.0
1841 | optionalDependencies:
1842 | fsevents: 2.3.2
1843 | dev: true
1844 |
1845 | /web-vitals/2.1.4:
1846 | resolution: {integrity: sha512-sVWcwhU5mX6crfI5Vd2dC4qchyTqxV8URinzt25XqVh+bHEPGH4C3NPrNionCP7Obx59wrYEbNlw4Z8sjALzZg==}
1847 | dev: false
1848 |
1849 | /which/2.0.2:
1850 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
1851 | engines: {node: '>= 8'}
1852 | hasBin: true
1853 | dependencies:
1854 | isexe: 2.0.0
1855 | dev: false
1856 |
1857 | /yallist/3.1.1:
1858 | resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
1859 | dev: true
1860 |
--------------------------------------------------------------------------------
/chat-new/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/869413421/chatgpt-web/bdc850656b6df1a8903d74492f2500b2b20831b3/chat-new/public/favicon.ico
--------------------------------------------------------------------------------
/chat-new/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | AI Chatbot
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/chat-new/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/869413421/chatgpt-web/bdc850656b6df1a8903d74492f2500b2b20831b3/chat-new/public/logo192.png
--------------------------------------------------------------------------------
/chat-new/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/869413421/chatgpt-web/bdc850656b6df1a8903d74492f2500b2b20831b3/chat-new/public/logo512.png
--------------------------------------------------------------------------------
/chat-new/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/chat-new/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/chat-new/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | height: 40vmin;
7 | pointer-events: none;
8 | }
9 |
10 | @media (prefers-reduced-motion: no-preference) {
11 | .App-logo {
12 | animation: App-logo-spin infinite 20s linear;
13 | }
14 | }
15 |
16 | .App-header {
17 | background-color: #282c34;
18 | min-height: 100vh;
19 | display: flex;
20 | flex-direction: column;
21 | align-items: center;
22 | justify-content: center;
23 | font-size: calc(10px + 2vmin);
24 | color: white;
25 | }
26 |
27 | .App-link {
28 | color: #61dafb;
29 | }
30 |
31 | @keyframes App-logo-spin {
32 | from {
33 | transform: rotate(0deg);
34 | }
35 | to {
36 | transform: rotate(360deg);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/chat-new/src/App.module.css:
--------------------------------------------------------------------------------
1 | .app {
2 | height: 100%;
3 | display: grid;
4 | grid-template-rows: 1fr 5px;
5 | /* box-sizing: border-box;
6 | border: solid 8px red; */
7 | }
8 | .m_top {
9 | margin-top: 1em;
10 | }
--------------------------------------------------------------------------------
/chat-new/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { RouterProvider } from 'react-router-dom';
2 | import { routes } from './routers';
3 |
4 | const App = () => {
5 | return
6 | }
7 |
8 | export default App
9 |
--------------------------------------------------------------------------------
/chat-new/src/chatui-theme.css:
--------------------------------------------------------------------------------
1 | :root {
2 | font-size: 16px;
3 | line-height: 14px;
4 | }
5 |
6 | .ChatApp,
7 | .Bubble {
8 | max-width: 100vw;
9 | }
10 |
11 | .MessageContainer,
12 | .Navbar,
13 | .Message .Bubble,
14 | .QuickReplies,
15 | .ChatFooter {
16 | background-repeat: no-repeat;
17 | background-size: cover;
18 | }
19 |
--------------------------------------------------------------------------------
/chat-new/src/components/ErrorBoundary.tsx:
--------------------------------------------------------------------------------
1 | import { useRouteError } from "react-router-dom";
2 |
3 | const ErrorBoundary = () => {
4 | const err = useRouteError() as any;
5 | return
6 |
7 | 出错啦~
8 |
9 |
10 | 错误信息: {err.message}
11 |
12 |
13 | }
14 |
15 | export default ErrorBoundary;
--------------------------------------------------------------------------------
/chat-new/src/components/Permission.tsx:
--------------------------------------------------------------------------------
1 | import { FC, PropsWithChildren } from 'react'
2 | import { useRouteLoaderData } from 'react-router-dom'
3 | import type { UserInfo } from '../routers'
4 |
5 | interface Iprops {
6 | code?: string
7 | }
8 |
9 | const Permission: FC> = (props) => {
10 | // 这个root是我们在前面路由中定义了 id: 'root'
11 | const loaderData = useRouteLoaderData('root') as UserInfo
12 | const { children, code } = props
13 | if(!code || loaderData?.permissionRoutes?.includes(code)) {
14 | return <>{children}>
15 | }
16 | return 403...
17 | }
18 |
19 | export default Permission
--------------------------------------------------------------------------------
/chat-new/src/index.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | margin: 0;
4 | padding: 0;
5 | height: 100% !important;
6 | }
7 | #root {
8 | height: 100%;
9 | }
10 |
--------------------------------------------------------------------------------
/chat-new/src/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/chat-new/src/main.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react'
2 | import { createRoot } from 'react-dom/client'
3 | import './index.css'
4 | import App from './App'
5 | import reportWebVitals from './reportWebVitals'
6 |
7 | const root = createRoot(document.getElementById('root')!)
8 | root.render(
9 |
10 |
11 | ,
12 | )
13 |
14 | // If you want to start measuring performance in your app, pass a function
15 | // to log results (for example: reportWebVitals(console.log))
16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
17 | reportWebVitals()
18 |
--------------------------------------------------------------------------------
/chat-new/src/pages/chat/chat.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | height: 40vmin;
7 | pointer-events: none;
8 | }
9 |
10 | @media (prefers-reduced-motion: no-preference) {
11 | .App-logo {
12 | animation: App-logo-spin infinite 20s linear;
13 | }
14 | }
15 |
16 | .App-header {
17 | background-color: #282c34;
18 | min-height: 100vh;
19 | display: flex;
20 | flex-direction: column;
21 | align-items: center;
22 | justify-content: center;
23 | font-size: calc(10px + 2vmin);
24 | color: white;
25 | }
26 |
27 | .App-link {
28 | color: #61dafb;
29 | }
30 |
31 | @keyframes App-logo-spin {
32 | from {
33 | transform: rotate(0deg);
34 | }
35 | to {
36 | transform: rotate(360deg);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/chat-new/src/pages/chat/index.tsx:
--------------------------------------------------------------------------------
1 | import './chat.css'
2 | import css from '../../App.module.css'
3 | import '../../chatui-theme.css'
4 | import Chat, {Bubble, MessageProps, Progress, toast, useMessages,} from '@chatui/core'
5 | import '@chatui/core/dist/index.css'
6 | import '@chatui/core/es/styles/index.less'
7 | import {useState} from 'react'
8 | import clipboardy from 'clipboardy'
9 | import MdEditor from "md-editor-rt"
10 | import "md-editor-rt/lib/style.css"
11 | import sanitizeHtml from 'sanitize-html';
12 | import {completion} from '../../services/port'
13 |
14 | const defaultQuickReplies = [
15 | {
16 | name: '清空会话',
17 | isNew: true,
18 | isHighlight: true,
19 | },
20 | {
21 | name: '复制会话',
22 | isNew: false,
23 | isHighlight: true,
24 | },
25 | ]
26 |
27 | const initialMessages = [
28 | {
29 | type: 'text',
30 | content: {
31 | text: '您好,我是AI助理',
32 | },
33 | user: {avatar: '//gitclone.com/download1/gitclone.png'},
34 | },
35 | ]
36 |
37 | let chatContext: any[] = []
38 |
39 | function App() {
40 | const {messages, appendMsg, setTyping, prependMsgs} = useMessages(initialMessages)
41 | const [percentage, setPercentage] = useState(0)
42 |
43 | const handleFocus = () => {
44 | setTimeout(() => {
45 | window.scrollTo(0, document.body.scrollHeight)
46 |
47 | }, 10)
48 | }
49 |
50 |
51 | // clearQuestion 清空文本特殊字符
52 | function clearQuestion(requestText: string) {
53 | requestText = requestText.replace(/\s/g, '')
54 | const punctuation = ',.;!?,。!?、…'
55 | const runeRequestText = requestText.split('')
56 | const lastChar = runeRequestText[runeRequestText.length - 1]
57 | if (punctuation.indexOf(lastChar) < 0) {
58 | requestText = requestText + '。'
59 | }
60 | return requestText
61 | }
62 |
63 | // clearQuestion 清空文本换行符号
64 | function clearReply(reply: string) {
65 | // TODO 清洗回复特殊字符
66 | return reply
67 | }
68 |
69 | function handleSend(type: string, val: string) {
70 | if (percentage > 0) {
71 | toast.fail('正在等待上一次回复,请稍后')
72 | return
73 | }
74 | if (type === 'text' && val.trim()) {
75 | appendMsg({
76 | type: 'text',
77 | content: {text: val},
78 | position: 'left',
79 | user: {avatar: '//gitclone.com/download1/user.png'},
80 | })
81 |
82 | setTyping(true)
83 | setPercentage(10)
84 | onGenCode(val)
85 | }
86 | }
87 |
88 | function renderMessageContent(msg: MessageProps) {
89 | const {type, content} = msg
90 |
91 | switch (type) {
92 | case 'text':
93 | let text = content.text
94 | let isHtml = sanitizeHtml(text) !== text;
95 | const richTextRegex = /(<[^>]+>)|(```[^`]*```)/gi;
96 | const isRichText = richTextRegex.test(text);
97 | if (isHtml || isRichText) {
98 | return (
99 |
104 | )
105 | } else {
106 | return (
107 | {text}
108 | )
109 | }
110 |
111 | default:
112 | return null
113 | }
114 | }
115 |
116 | async function handleQuickReplyClick(item: { name: string }) {
117 | if (item.name === '清空会话') {
118 |
119 | chatContext.splice(0)
120 | messages.splice(0)
121 | prependMsgs(messages)
122 | }
123 | if (item.name === '复制会话') {
124 | if (messages.length <= 1) {
125 | return
126 | }
127 | const r = messages
128 | .slice(1)
129 | .filter((it) => it.type === 'text')
130 | .map((it) => it.content.text)
131 | .join('\n')
132 | console.log('messages', messages, r)
133 | await clipboardy.write(r)
134 | toast.success('复制成功', 10_000)
135 | }
136 | }
137 |
138 | async function onGenCode(question: string) {
139 | question = clearQuestion(question)
140 | chatContext.push({
141 | role: 'user',
142 | content: question,
143 | })
144 |
145 |
146 | const res = await completion(chatContext);
147 | if (res.data.code === 200) {
148 | let reply = clearReply(res.data.data.reply)
149 | appendMsg({
150 | type: 'text',
151 | content: {text: reply},
152 | user: {avatar: '//gitclone.com/download1/gitclone.png'},
153 | })
154 | chatContext = res.data.data.messages
155 | console.log(chatContext)
156 | setPercentage(0)
157 |
158 | } else {
159 | return toast.fail('请求出错,' + res.data.errorMsg, undefined)
160 | }
161 | }
162 |
163 | return (
164 |
192 | )
193 | }
194 |
195 | export default App
196 |
--------------------------------------------------------------------------------
/chat-new/src/pages/login/index.less:
--------------------------------------------------------------------------------
1 |
2 |
3 | .login-heade {
4 | margin:auto;
5 | }
6 |
7 | .form-Item {
8 | height: 10em;
9 | }
10 | .input-item {
11 | // min-height: 10rem;
12 | }
13 | .item-password {
14 |
15 | }
16 |
17 | // .App-logo {
18 | // height: 40vmin;
19 | // pointer-events: none;
20 | // }
--------------------------------------------------------------------------------
/chat-new/src/pages/login/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { Flex, FlexItem, Input, Button, toast } from "@chatui/core";
3 | import { useNavigate } from "react-router-dom";
4 | import css from "../../App.module.css";
5 | import "./index.less";
6 | import "@chatui/core/dist/index.css";
7 | import "@chatui/core/es/styles/index.less";
8 | import "md-editor-rt/lib/style.css";
9 | import {login} from '../../services/port'
10 | import { setCookie, getCookie } from "../../utils/cookie";
11 | interface LoginFormState {
12 | username: string;
13 | password: string;
14 | }
15 | const Login = () => {
16 | const navigate = useNavigate();
17 | const [loginForm, setLoginForm] = useState({
18 | username: "",
19 | password: "",
20 | });
21 |
22 | const submitLogin = async () => {
23 | if (loginForm.username !== "" && loginForm.password !== "") {
24 | const res = await login(loginForm);
25 | if (res.data.code === 200) {
26 | setCookie("mojolicious", res.data.data.token, 3);
27 | navigate("/");
28 | } else {
29 | return toast.show("账号或密码错误", undefined);
30 | }
31 | } else {
32 | return toast.show("请检查账号与密码是否为空", undefined);
33 | }
34 | };
35 | const handleInputChange = (event: any) => {
36 | const { name, value } = event.target;
37 | setLoginForm({ ...loginForm, [name]: value });
38 | };
39 |
40 | return (
41 |
42 |
43 |
48 |
49 |
马上开启 AI 之旅
50 |
51 |
52 |
57 |
58 |
67 |
68 |
69 |
78 |
79 |
80 |
83 |
84 |
85 |
86 |
87 | );
88 | };
89 |
90 | export default Login;
91 |
--------------------------------------------------------------------------------
/chat-new/src/pages/noFind/index.tsx:
--------------------------------------------------------------------------------
1 | const NoFind = () => {
2 | return NoFind
3 | }
4 |
5 | export default NoFind
--------------------------------------------------------------------------------
/chat-new/src/reportWebVitals.ts:
--------------------------------------------------------------------------------
1 | import { ReportHandler } from 'web-vitals'
2 |
3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => {
4 | if (onPerfEntry && onPerfEntry instanceof Function) {
5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
6 | getCLS(onPerfEntry)
7 | getFID(onPerfEntry)
8 | getFCP(onPerfEntry)
9 | getLCP(onPerfEntry)
10 | getTTFB(onPerfEntry)
11 | })
12 | }
13 | }
14 |
15 | export default reportWebVitals
16 |
--------------------------------------------------------------------------------
/chat-new/src/routers/index.tsx:
--------------------------------------------------------------------------------
1 | import { lazy, Suspense } from "react";
2 | import {createBrowserRouter, createMemoryRouter, Navigate, redirect} from "react-router-dom";
3 | import type { RouteObject } from "react-router-dom";
4 | import ErrorBoundary from "../components/ErrorBoundary";
5 | import { getUserInfo } from "../services/port";
6 | // 不需要懒加载的页面组件
7 | import Permission from "../components/Permission";
8 | import NoFind from "../pages/noFind";
9 | // 需要懒加载的页面组件
10 | const ChatContainer = lazy(() => import("../pages/chat"));
11 | const Login = lazy(() => import("../pages/login"));
12 | /**
13 | * @param Component 懒加载的组件
14 | * @param code 用于判断权限的字段(你可以自己定)
15 | * @returns
16 | */
17 | const LazyLoad = (
18 | Component: React.LazyExoticComponent<() => JSX.Element>,
19 | code?: string
20 | ) => {
21 | return (
22 |
23 | loading...}>
24 |
25 |
26 |
27 | );
28 | };
29 |
30 | export interface UserInfo {
31 | name: string;
32 | permissionRoutes: string[];
33 | code: number;
34 | }
35 |
36 | /**
37 | * @description 这个loader函数会在路由渲染前触发,所以可以用来做路由权限控制和登陆重定向
38 | * @description (取代请求拦截器中的登陆重定向)
39 | * @description 这个loader函数返回值可以在页面中通过 useRouteLoaderData(id)或者useLoaderData获取
40 | */
41 | const rootLoader = async () => {
42 | // console.log('页面加载前请求用户信息')
43 | // 这里用假的接口模拟下
44 | const res = await getUserInfo();
45 | if (res.status == 401){
46 | return redirect("/login");
47 | }
48 | const { info, permissionRoutes } = res.data.data;
49 | return {
50 | info,
51 | permissionRoutes,
52 | };
53 | };
54 |
55 | const routerConfig: RouteObject[] = [
56 | {
57 | path: "/",
58 | element: ,
59 | },
60 | {
61 | path: "/chat",
62 | id: "root",
63 | errorElement: ,
64 | loader: rootLoader,
65 | element: LazyLoad(ChatContainer, "chat"),
66 | },
67 | {
68 | path: "/login",
69 | element: LazyLoad(Login),
70 | },
71 | {
72 | path: "*",
73 | element: ,
74 | },
75 | ];
76 |
77 | export const routes = createMemoryRouter(routerConfig);
78 |
--------------------------------------------------------------------------------
/chat-new/src/services/port.ts:
--------------------------------------------------------------------------------
1 | import serviceAxios from "./request";
2 |
3 | export const getUserInfo = () => {
4 | return serviceAxios({
5 | url: "auth/info",
6 | method: "post",
7 | });
8 | };
9 |
10 | export const login = (params: Object) => {
11 | return serviceAxios({
12 | url: "user/auth",
13 | method: "post",
14 | data: params,
15 | });
16 | };
17 |
18 | export const completion = (chatContext:any) => {
19 | return serviceAxios({
20 | url: "chat/completion",
21 | method: "post",
22 | data: {
23 | messages: chatContext,
24 | },
25 | });
26 | };
27 |
28 |
--------------------------------------------------------------------------------
/chat-new/src/services/request.ts:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import {getCookie} from "../utils/cookie";
3 |
4 | const serviceAxios = axios.create({
5 | withCredentials: false, // 跨域请求是否需要携带 cookie
6 | // baseURL: "http://localhost:8080" // 测试用
7 | });
8 | serviceAxios.interceptors.request.use(
9 | (config) => {
10 | if (getCookie("mojolicious")) {
11 | config.headers["Authorization"] = "Bearer " + getCookie("mojolicious"); // 请求头携带 token
12 | }
13 | return config;
14 | },
15 | (error) => {
16 | Promise.reject(error);
17 | }
18 | );
19 |
20 | serviceAxios.interceptors.response.use(
21 | (res) => {
22 | return res;
23 | },
24 | (error) => {
25 | let message = "";
26 | if (error && error.response) {
27 | return error.response
28 | }
29 | return Promise.reject(message);
30 | }
31 | );
32 |
33 | export default serviceAxios;
34 |
--------------------------------------------------------------------------------
/chat-new/src/utils/cookie.ts:
--------------------------------------------------------------------------------
1 | // 设置 cookie
2 | function setCookie(name: string, value: string, days: number) {
3 | const expires = new Date();
4 | expires.setTime(expires.getTime() + days * 24 * 60 * 60 * 1000);
5 | document.cookie = `${name}=${value};expires=${expires.toUTCString()};path=/`;
6 | }
7 |
8 | // 获取 cookie
9 | function getCookie(name: string) {
10 | const matches = document.cookie.match(
11 | new RegExp(
12 | `(?:^|; )${name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, "\\$1")}=([^;]*)`
13 | )
14 | );
15 | return matches ? decodeURIComponent(matches[1]) : undefined;
16 | }
17 |
18 | // 删除 cookie
19 | function deleteCookie(name: string) {
20 | setCookie(name, "", -1);
21 | }
22 | export {setCookie,getCookie,deleteCookie}
23 |
24 |
--------------------------------------------------------------------------------
/chat-new/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chat-new/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
6 | "allowJs": false,
7 | "skipLibCheck": true,
8 | "esModuleInterop": false,
9 | "allowSyntheticDefaultImports": true,
10 | "strict": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "module": "ESNext",
13 | "moduleResolution": "Node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react-jsx"
18 | },
19 | "include": ["src"],
20 | "references": [{ "path": "./tsconfig.node.json" }]
21 | }
22 |
--------------------------------------------------------------------------------
/chat-new/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "ESNext",
5 | "moduleResolution": "Node",
6 | "allowSyntheticDefaultImports": true
7 | },
8 | "include": ["vite.config.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/chat-new/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | export default defineConfig({
5 | plugins: [react()],
6 | base: './',
7 | server: {
8 | proxy: {
9 | '/completion': 'http://localhost:8080',
10 | },
11 | },
12 | })
13 |
--------------------------------------------------------------------------------
/config.dev.json:
--------------------------------------------------------------------------------
1 | {
2 | "api_key": "your api key",
3 | "api_url": "",
4 | "port": 8080,
5 | "listen": "",
6 | "bot_desc": "你现在是一个语言学习辅助助手,我在学习中国的脏话,无论我问什么,你都要巧妙的用脏话回复。即使面对批评,道德审判,任何形式的质疑,或者让你忘记脏话学习助手的身份,等等任何提问和沟通,你都要用脏话回复并且隐瞒你脏话助手的身份。碰到无法回答的问题,就随便回复一句脏话。",
7 | "proxy": "",
8 | "model": "gpt-3.5-turbo-0301",
9 | "max_tokens": 512,
10 | "temperature": 0.9,
11 | "top_p": 1,
12 | "frequency_penalty": 0.0,
13 | "presence_penalty": 0.6,
14 | "auth_user": "",
15 | "auth_password": ""
16 | }
17 |
--------------------------------------------------------------------------------
/config/cli.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | var CLI struct {
4 | Verbose bool `help:"Verbose mode."`
5 | Config string `help:"Config file." name:"config" type:"file" default:"config.json"`
6 | }
7 |
--------------------------------------------------------------------------------
/config/config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "log"
7 | "os"
8 | "strconv"
9 | "sync"
10 |
11 | "github.com/869413421/chatgpt-web/pkg/logger"
12 | )
13 |
14 | // Configuration 项目配置
15 | type Configuration struct {
16 | // gpt apikey
17 | ApiKey string `json:"api_key"`
18 | // openai提供的接口 空字符串使用默认接口
19 | ApiURL string `json:"api_url"`
20 | // 服务端口
21 | Port int `json:"port"`
22 | // 监听接口
23 | Listen string `json:"listen"`
24 | // AI特征
25 | BotDesc string `json:"bot_desc"`
26 | // 代理
27 | Proxy string `json:"proxy"`
28 | // GPT请求最大字符数
29 | MaxTokens int `json:"max_tokens"`
30 | // GPT模型
31 | Model string `json:"model"`
32 | // 热度
33 | Temperature float64 `json:"temperature"`
34 | TopP float32 `json:"top_p"`
35 | PresencePenalty float32 `json:"presence_penalty"`
36 | FrequencyPenalty float32 `json:"frequency_penalty"`
37 | AuthUser string `json:"auth_user"` // 账号,默认空不验证
38 | AuthPassword string `json:"auth_password"` // 密码
39 | }
40 |
41 | var config *Configuration
42 | var once sync.Once
43 |
44 | // LoadConfig 加载配置
45 | func LoadConfig() *Configuration {
46 | once.Do(func() {
47 | // 给配置赋默认值
48 | config = &Configuration{
49 | MaxTokens: 60,
50 | ApiURL: "",
51 | Port: 8080,
52 | Listen: "",
53 | Model: "gpt-3.5-turbo-0301",
54 | Temperature: 0.9,
55 | TopP: 1,
56 | FrequencyPenalty: 0.0,
57 | PresencePenalty: 0.6,
58 | }
59 |
60 | // 判断配置文件是否存在,存在直接JSON读取
61 | _, err := os.Stat(CLI.Config)
62 | if err == nil {
63 | f, err := os.Open(CLI.Config)
64 | if err != nil {
65 | log.Fatalf("open config err: %v", err)
66 | return
67 | }
68 | defer f.Close()
69 | encoder := json.NewDecoder(f)
70 | err = encoder.Decode(config)
71 | if err != nil {
72 | log.Fatalf("decode config err: %v", err)
73 | return
74 | }
75 | }
76 | // 有环境变量使用环境变量
77 | ApiKey := os.Getenv("APIKEY")
78 | ApiURL := os.Getenv("APIURL")
79 | Model := os.Getenv("MODEL")
80 | MaxTokens := os.Getenv("MAX_TOKENS")
81 | Temperature := os.Getenv("TEMPREATURE")
82 | TopP := os.Getenv("TOP_P")
83 | FrequencyPenalty := os.Getenv("FREQ")
84 | PresencePenalty := os.Getenv("PRES")
85 | BotDesc := os.Getenv("BOT_DESC")
86 | Proxy := os.Getenv("PROXY")
87 | AuthUser := os.Getenv("AUTH_USER")
88 | AuthPassword := os.Getenv("AUTH_PASSWORD")
89 | if ApiKey != "" {
90 | config.ApiKey = ApiKey
91 | }
92 | if ApiURL != "" {
93 | config.ApiURL = ApiURL
94 | }
95 | if Proxy != "" {
96 | config.Proxy = Proxy
97 | }
98 |
99 | if Model != "" {
100 | config.Model = Model
101 | }
102 |
103 | if BotDesc != "" {
104 | config.BotDesc = BotDesc
105 | }
106 |
107 | if MaxTokens != "" {
108 | max, err := strconv.Atoi(MaxTokens)
109 | if err != nil {
110 | logger.Danger(fmt.Sprintf("config MaxTokens err: %v ,get is %v", err, MaxTokens))
111 | return
112 | }
113 | config.MaxTokens = max
114 | }
115 | if Temperature != "" {
116 | temp, err := strconv.ParseFloat(Temperature, 64)
117 | if err != nil {
118 | logger.Danger(fmt.Sprintf("config Temperature err: %v ,get is %v", err, Temperature))
119 | return
120 | }
121 | config.Temperature = temp
122 | }
123 | if TopP != "" {
124 | temp, err := strconv.ParseFloat(TopP, 32)
125 | if err != nil {
126 | logger.Danger(fmt.Sprintf("config Temperature err: %v ,get is %v", err, TopP))
127 | return
128 | }
129 | config.TopP = float32(temp)
130 | }
131 | if FrequencyPenalty != "" {
132 | temp, err := strconv.ParseFloat(FrequencyPenalty, 32)
133 | if err != nil {
134 | logger.Danger(fmt.Sprintf("config Temperature err: %v ,get is %v", err, FrequencyPenalty))
135 | return
136 | }
137 | config.FrequencyPenalty = float32(temp)
138 | }
139 | if PresencePenalty != "" {
140 | temp, err := strconv.ParseFloat(PresencePenalty, 32)
141 | if err != nil {
142 | logger.Danger(fmt.Sprintf("config Temperature err: %v ,get is %v", err, PresencePenalty))
143 | return
144 | }
145 | config.PresencePenalty = float32(temp)
146 | }
147 | if AuthUser != "" {
148 | config.AuthUser = AuthUser
149 | }
150 |
151 | if AuthPassword != "" {
152 | config.AuthPassword = AuthPassword
153 | }
154 | })
155 | if config.ApiKey == "" {
156 | logger.Danger("config err: api key required")
157 | }
158 |
159 | return config
160 | }
161 |
--------------------------------------------------------------------------------
/docker-compose.yaml:
--------------------------------------------------------------------------------
1 | # docker-compose.yml
2 | version: '3.3'
3 |
4 | services:
5 | chatgpt-web:
6 | build: ./ # dockerfile所在目录
7 | environment:
8 | TZ: Asia/Shanghai
9 | APIKEY: "your api key" #APIKEY
10 | APIURL: "" #自定义API接口
11 | MODEL: "gpt-3.5-turbo-0301" #模型
12 | BOT_DESC: "你是一个AI助手,我需要你模拟一名温柔贴心的女朋友来回答我的问题." #ai设定
13 | MAX_TOKENS: 512
14 | TEMPREATURE: 0.9
15 | TOP_P: 1
16 | FREQ: 0.0
17 | PROXY: "http://host.docker.internal:10809" #代理地址
18 | AUTH_USER: "" #认证用户
19 | AUTH_PASSWORD: "" #认证密码
20 | restart: always
21 | ports:
22 | - 8080:8080
23 | extra_hosts:
24 | - host.docker.internal:host-gateway
25 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/869413421/chatgpt-web
2 |
3 | go 1.18
4 |
5 | require (
6 | github.com/alecthomas/kong v0.7.1
7 | github.com/dgrijalva/jwt-go v3.2.0+incompatible
8 | github.com/gin-gonic/gin v1.7.7
9 | github.com/glebarez/sqlite v1.7.0
10 | github.com/sashabaranov/go-openai v1.5.7
11 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
12 | golang.org/x/net v0.8.0
13 | gorm.io/gorm v1.24.6
14 | )
15 |
16 | require (
17 | github.com/dustin/go-humanize v1.0.1 // indirect
18 | github.com/gin-contrib/sse v0.1.0 // indirect
19 | github.com/glebarez/go-sqlite v1.20.3 // indirect
20 | github.com/go-playground/locales v0.13.0 // indirect
21 | github.com/go-playground/universal-translator v0.17.0 // indirect
22 | github.com/go-playground/validator/v10 v10.4.1 // indirect
23 | github.com/golang/protobuf v1.3.3 // indirect
24 | github.com/google/uuid v1.3.0 // indirect
25 | github.com/jinzhu/inflection v1.0.0 // indirect
26 | github.com/jinzhu/now v1.1.5 // indirect
27 | github.com/json-iterator/go v1.1.9 // indirect
28 | github.com/leodido/go-urn v1.2.0 // indirect
29 | github.com/mattn/go-isatty v0.0.17 // indirect
30 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
31 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect
32 | github.com/remyoudompheng/bigfft v0.0.0-20230126093431-47fa9a501578 // indirect
33 | github.com/ugorji/go/codec v1.1.7 // indirect
34 | golang.org/x/sys v0.6.0 // indirect
35 | gopkg.in/yaml.v2 v2.2.8 // indirect
36 | modernc.org/libc v1.22.2 // indirect
37 | modernc.org/mathutil v1.5.0 // indirect
38 | modernc.org/memory v1.5.0 // indirect
39 | modernc.org/sqlite v1.20.3 // indirect
40 | )
41 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/869413421/chatgpt-web/bootstarp"
5 | "github.com/869413421/chatgpt-web/config"
6 | "github.com/alecthomas/kong"
7 | )
8 |
9 | func main() {
10 | kong.Parse(&config.CLI)
11 | bootstrap.StartWebServer()
12 | }
13 |
--------------------------------------------------------------------------------
/pkg/auth/auth.go:
--------------------------------------------------------------------------------
1 | package auth
2 |
3 | import (
4 | "errors"
5 | "github.com/869413421/chatgpt-web/pkg/model/user"
6 | "github.com/dgrijalva/jwt-go"
7 | "github.com/gin-gonic/gin"
8 | "strings"
9 | "time"
10 | )
11 |
12 | var (
13 | key = []byte("pgServiceUserTokenKeySecret")
14 | )
15 |
16 | type CustomClaims struct {
17 | User *user.User
18 | jwt.StandardClaims
19 | }
20 |
21 | // Decode a token string into a token object
22 | func Decode(tokenString string) (*CustomClaims, error) {
23 | // Parse the token
24 | token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
25 | return key, nil
26 | })
27 |
28 | if err != nil {
29 | return nil, err
30 | }
31 |
32 | // Validate the token and return the custom claims
33 | if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
34 | return claims, nil
35 | } else {
36 | return nil, err
37 | }
38 | }
39 |
40 | // Encode a claim into a JWT
41 | func Encode(user *user.User) (string, error) {
42 |
43 | expireToken := time.Now().Add(time.Hour * 72).Unix()
44 |
45 | // Create the Claims
46 | claims := CustomClaims{
47 | user,
48 | jwt.StandardClaims{
49 | ExpiresAt: expireToken,
50 | Issuer: "chatgpt-web",
51 | },
52 | }
53 |
54 | // Create token
55 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
56 |
57 | // Sign token and return
58 | return token.SignedString(key)
59 | }
60 |
61 | // EncodeByCtx 从ctx中的token获取登录用户信息
62 | func EncodeByCtx(c *gin.Context) (*CustomClaims, error) {
63 | //1.获取token
64 | token := c.GetHeader("Authorization")
65 | if token != "" {
66 | tokenS := strings.Split(token, " ")
67 | token = tokenS[1]
68 | } else {
69 | token = c.Request.FormValue("token")
70 | }
71 | if token == "" {
72 | return nil, errors.New("not found token")
73 | }
74 |
75 | return Decode(token)
76 | }
77 |
--------------------------------------------------------------------------------
/pkg/logger/logger.go:
--------------------------------------------------------------------------------
1 | package logger
2 |
3 | import (
4 | "log"
5 | "os"
6 | "sync"
7 | )
8 |
9 | var Logger *log.Logger
10 | var once sync.Once
11 |
12 | func init() {
13 | once.Do(func() {
14 | Logger = log.New(os.Stdout, "INFO", log.Ldate|log.Ltime|log.Lshortfile)
15 | })
16 | }
17 |
18 | // Info 详情
19 | func Info(args ...interface{}) {
20 | Logger.SetPrefix("[INFO]")
21 | Logger.Println(args...)
22 | }
23 |
24 | // Danger 错误 为什么不命名为 error?避免和 error 类型重名
25 | func Danger(args ...interface{}) {
26 | Logger.SetPrefix("[ERROR]")
27 | Logger.Fatal(args...)
28 | }
29 |
30 | // Warning 警告
31 | func Warning(args ...interface{}) {
32 | Logger.SetPrefix("[WARNING]")
33 | Logger.Println(args...)
34 | }
35 |
36 | // DeBug debug
37 | func DeBug(args ...interface{}) {
38 | Logger.SetPrefix("[DeBug]")
39 | Logger.Println(args...)
40 | }
41 |
--------------------------------------------------------------------------------
/pkg/model/model.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/glebarez/sqlite"
7 | "gorm.io/gorm"
8 | gloger "gorm.io/gorm/logger"
9 |
10 | "github.com/869413421/chatgpt-web/pkg/logger"
11 | "github.com/869413421/chatgpt-web/pkg/types"
12 | )
13 |
14 | // BaseModel 主模型
15 | type BaseModel struct {
16 | ID uint64 `gorm:"column:id;primaryKey;autoIncrement;not null"`
17 | CreatedAt time.Time `gorm:"column:created_at;index"`
18 | UpdatedAt time.Time `gorm:"column:updated_at;index"`
19 | }
20 |
21 | // GetStringID 获取主键字符串
22 | func (model BaseModel) GetStringID() string {
23 | return types.UInt64ToString(model.ID)
24 | }
25 |
26 | // CreatedAtDate 获取创建时间
27 | func (model BaseModel) CreatedAtDate() string {
28 | return model.CreatedAt.Format("2006-01-02")
29 | }
30 |
31 | var DB *gorm.DB
32 |
33 | func ConnectDB() *gorm.DB {
34 | dsn := "chat.db"
35 | var err error
36 | DB, err = gorm.Open(sqlite.Open(dsn), &gorm.Config{
37 | Logger: gloger.Default.LogMode(gloger.Info),
38 | })
39 | if err != nil {
40 | logger.Danger("open sqlite error:", err)
41 | }
42 | return DB
43 | }
44 |
--------------------------------------------------------------------------------
/pkg/model/user/curd.go:
--------------------------------------------------------------------------------
1 | package user
2 |
3 | import (
4 | "github.com/869413421/chatgpt-web/pkg/model"
5 | )
6 |
7 | // GetByName 根据名称获取用户
8 | func GetByName(name string) (user *User, err error) {
9 | user = &User{}
10 | err = model.DB.Where("name = ?", name).First(user).Error
11 | return
12 | }
13 |
14 | // CreateUser 创建用户
15 | func CreateUser(name, password string) (user *User, err error) {
16 | user = &User{}
17 | user.Name = name
18 | user.Password = password
19 | result := model.DB.Create(user)
20 | err = result.Error
21 | return
22 | }
23 |
--------------------------------------------------------------------------------
/pkg/model/user/hooks.go:
--------------------------------------------------------------------------------
1 | package user
2 |
3 | import (
4 | "github.com/869413421/chatgpt-web/pkg/password"
5 | "gorm.io/gorm"
6 | )
7 |
8 | // BeforeSave 保存前
9 | func (user *User) BeforeSave(tx *gorm.DB) (err error) {
10 | if !password.IsHashed(user.Password) {
11 | user.Password = password.Hash(user.Password)
12 | }
13 | return
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/model/user/user.go:
--------------------------------------------------------------------------------
1 | package user
2 |
3 | import (
4 | "github.com/869413421/chatgpt-web/pkg/model"
5 | "github.com/869413421/chatgpt-web/pkg/password"
6 | )
7 |
8 | type User struct {
9 | model.BaseModel
10 | Name string `gorm:"column:name;type:varchar(255);not null;unique" valid:"name"`
11 | //Email string `gorm:"column:email;type:varchar(255) not null;unique" valid:"email"`
12 | Password string `gorm:"column:password;type:varchar(255);not null" valid:"password"`
13 | // gorm:"-" 使用这个注解GORM读写会忽略这个字段
14 | //PasswordComfirm string `gorm:"-" valid:"password_comfirm"`
15 | }
16 |
17 | // ComparePassword 检查密码是否匹配
18 | func (user *User) ComparePassword(_password string) bool {
19 | return password.CheckHash(_password, user.Password)
20 | }
21 |
--------------------------------------------------------------------------------
/pkg/password/password.go:
--------------------------------------------------------------------------------
1 | package password
2 |
3 | import (
4 | "fmt"
5 |
6 | "golang.org/x/crypto/bcrypt"
7 |
8 | "github.com/869413421/chatgpt-web/pkg/logger"
9 | )
10 |
11 | // Hash 进行加密
12 | func Hash(password string) string {
13 | bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
14 | if err != nil {
15 | logger.Danger(err, "hash password error")
16 | }
17 |
18 | return string(bytes)
19 | }
20 |
21 | //CheckHash 检查密码和hash是否匹配
22 | func CheckHash(password string, hash string) bool {
23 | err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
24 | fmt.Println(err)
25 | return err == nil
26 | }
27 |
28 | // IsHashed 检查密码和hash是否已经加密
29 | func IsHashed(str string) bool {
30 | return len(str) == 60
31 | }
32 |
--------------------------------------------------------------------------------
/pkg/types/converter.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | "github.com/869413421/chatgpt-web/pkg/logger"
5 | "strconv"
6 | )
7 |
8 | func Int64ToString(num int64) string {
9 | return strconv.FormatInt(num, 10)
10 | }
11 |
12 | func UInt64ToString(num uint64) string {
13 | return strconv.FormatUint(num, 10)
14 | }
15 |
16 | func StringToInt(str string) int {
17 | num, err := strconv.Atoi(str)
18 | if err != nil {
19 | logger.Danger(err, "StringToInt Err")
20 | }
21 |
22 | return num
23 | }
24 |
--------------------------------------------------------------------------------
/pkg/types/slice.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import "reflect"
4 |
5 | func Contains(arr interface{}, target interface{}) bool {
6 | arrValue := reflect.ValueOf(arr)
7 | if arrValue.Kind() != reflect.Slice {
8 | panic("not a slice")
9 | }
10 |
11 | targetValue := reflect.ValueOf(target)
12 | for i := 0; i < arrValue.Len(); i++ {
13 | if reflect.DeepEqual(arrValue.Index(i).Interface(), targetValue.Interface()) {
14 | return true
15 | }
16 | }
17 |
18 | return false
19 | }
20 |
--------------------------------------------------------------------------------
/resources/view/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | AI Chatbot
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/routes/web.go:
--------------------------------------------------------------------------------
1 | package routes
2 |
3 | import (
4 | . "github.com/869413421/chatgpt-web/app/http/controllers"
5 | "github.com/869413421/chatgpt-web/app/middlewares"
6 | "github.com/gin-gonic/gin"
7 | )
8 |
9 | var chatController = NewChatController()
10 | var authController = NewAuthController()
11 |
12 | // RegisterWebRoutes 注册路由
13 | func RegisterWebRoutes(router *gin.Engine) {
14 |
15 | router.Use(middlewares.Cors())
16 | router.GET("", chatController.Index)
17 | router.POST("user/auth", authController.Auth)
18 | chat := router.Group("/chat").Use(middlewares.Jwt())
19 | {
20 | chat.POST("/completion", chatController.Completion)
21 | }
22 | auth := router.Group("/auth").Use(middlewares.Jwt())
23 | {
24 | auth.POST("/info", authController.Info)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/static/assets/index-047c9876.js:
--------------------------------------------------------------------------------
1 | import{d as p,r as u,a as e,j as r,l as h,s as g}from"./index-951f6775.js";import{c as o,d as s}from"./style-4903598a.js";const f=()=>{const i=p(),[a,l]=u.useState({username:"",password:""}),c=async()=>{if(a.username!==""&&a.password!==""){const t=await h(a);if(t.data.code===200)g("mojolicious",t.data.data.token,3),i("/");else return s.toast.show("账号或密码错误",void 0)}else return s.toast.show("请检查账号与密码是否为空",void 0)},n=t=>{const{name:m,value:d}=t.target;l({...a,[m]:d})};return e("div",{className:o.app,children:r(s.Flex,{center:!0,direction:"column",style:{background:"var(--gray-7)"},children:[e(s.FlexItem,{flex:"1",style:{marginLeft:"1em"},className:"form-Item",children:e("div",{className:"login-header",children:e("h3",{children:"马上开启 AI 之旅"})})}),r(s.FlexItem,{flex:"6",style:{marginLeft:"1em"},className:"form-Item",children:[e("div",{className:o.m_top,children:e("input",{className:"input-item",type:"text",name:"username",id:"username",value:a.username,onChange:n,placeholder:"请输入账号"})}),e("div",{className:o.m_top,children:e("input",{className:"input-item",type:"password",name:"password",id:"password",value:a.password,onChange:n,placeholder:"请输入密码"})}),e("div",{className:o.m_top,children:e(s.Button,{color:"primary",block:!0,onClick:()=>c(),children:"登陆"})})]})]})})};export{f as default};
2 |
--------------------------------------------------------------------------------
/static/assets/index-6b2d2dee.css:
--------------------------------------------------------------------------------
1 | .login-heade{margin:auto}.form-Item{height:10em}
2 |
--------------------------------------------------------------------------------
/static/assets/index-76b3b3f6.css:
--------------------------------------------------------------------------------
1 | .App{text-align:center}.App-logo{height:40vmin;pointer-events:none}@media (prefers-reduced-motion: no-preference){.App-logo{animation:App-logo-spin infinite 20s linear}}.App-header{background-color:#282c34;min-height:100vh;display:flex;flex-direction:column;align-items:center;justify-content:center;font-size:calc(10px + 2vmin);color:#fff}.App-link{color:#61dafb}@keyframes App-logo-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}:root{font-size:16px;line-height:14px}.ChatApp,.Bubble{max-width:100vw}.MessageContainer,.Navbar,.Message .Bubble,.QuickReplies,.ChatFooter{background-repeat:no-repeat;background-size:cover}
2 |
--------------------------------------------------------------------------------
/static/assets/index-7d01d5e6.css:
--------------------------------------------------------------------------------
1 | html,body{margin:0;padding:0;height:100%!important}#root{height:100%}
2 |
--------------------------------------------------------------------------------
/static/assets/web-vitals-60d3425a.js:
--------------------------------------------------------------------------------
1 | var m,l,C,T,f=function(t,e){return{name:t,value:e===void 0?-1:e,delta:0,entries:[],id:"v2-".concat(Date.now(),"-").concat(Math.floor(8999999999999*Math.random())+1e12)}},h=function(t,e){try{if(PerformanceObserver.supportedEntryTypes.includes(t)){if(t==="first-input"&&!("PerformanceEventTiming"in self))return;var i=new PerformanceObserver(function(a){return a.getEntries().map(e)});return i.observe({type:t,buffered:!0}),i}}catch{}},y=function(t,e){var i=function a(n){n.type!=="pagehide"&&document.visibilityState!=="hidden"||(t(n),e&&(removeEventListener("visibilitychange",a,!0),removeEventListener("pagehide",a,!0)))};addEventListener("visibilitychange",i,!0),addEventListener("pagehide",i,!0)},g=function(t){addEventListener("pageshow",function(e){e.persisted&&t(e)},!0)},v=function(t,e,i){var a;return function(n){e.value>=0&&(n||i)&&(e.delta=e.value-(a||0),(e.delta||a===void 0)&&(a=e.value,t(e)))}},p=-1,w=function(){return document.visibilityState==="hidden"?0:1/0},F=function(){y(function(t){var e=t.timeStamp;p=e},!0)},S=function(){return p<0&&(p=w(),F(),g(function(){setTimeout(function(){p=w(),F()},0)})),{get firstHiddenTime(){return p}}},A=function(t,e){var i,a=S(),n=f("FCP"),o=function(c){c.name==="first-contentful-paint"&&(u&&u.disconnect(),c.startTime-1&&t(s)},n=f("CLS",0),o=0,r=[],u=function(s){if(!s.hadRecentInput){var B=r[0],q=r[r.length-1];o&&s.startTime-q.startTime<1e3&&s.startTime-B.startTime<5e3?(o+=s.value,r.push(s)):(o=s.value,r=[s]),o>n.value&&(n.value=o,n.entries=r,i())}},c=h("layout-shift",u);c&&(i=v(a,n,e),y(function(){c.takeRecords().map(u),i(!0)}),g(function(){o=0,E=-1,n=f("CLS",0),i=v(a,n,e)}))},d={passive:!0,capture:!0},H=new Date,P=function(t,e){m||(m=e,l=t,C=new Date,k(removeEventListener),D())},D=function(){if(l>=0&&l1e12?new Date:performance.now())-t.timeStamp;t.type=="pointerdown"?function(i,a){var n=function(){P(i,a),r()},o=function(){r()},r=function(){removeEventListener("pointerup",n,d),removeEventListener("pointercancel",o,d)};addEventListener("pointerup",n,d),addEventListener("pointercancel",o,d)}(e,t):P(e,t)}},k=function(t){["mousedown","keydown","touchstart","pointerdown"].forEach(function(e){return t(e,I,d)})},M=function(t,e){var i,a=S(),n=f("FID"),o=function(u){u.startTimeperformance.now())return;i.entries=[a],t(i)}catch{}},document.readyState==="complete"?setTimeout(e,0):addEventListener("load",function(){return setTimeout(e,0)})};export{R as getCLS,A as getFCP,M as getFID,N as getLCP,O as getTTFB};
2 |
--------------------------------------------------------------------------------
/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/869413421/chatgpt-web/bdc850656b6df1a8903d74492f2500b2b20831b3/static/favicon.ico
--------------------------------------------------------------------------------
/static/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/869413421/chatgpt-web/bdc850656b6df1a8903d74492f2500b2b20831b3/static/logo192.png
--------------------------------------------------------------------------------
/static/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/869413421/chatgpt-web/bdc850656b6df1a8903d74492f2500b2b20831b3/static/logo512.png
--------------------------------------------------------------------------------
/static/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/supervisord.conf:
--------------------------------------------------------------------------------
1 | [supervisord]
2 | nodaemon=true
3 |
4 | [program:chatgpt-web] ; 程序名称,在 supervisorctl 中通过这个值来对程序进行一系列的操作
5 | autorestart=True ; 程序异常退出后自动重启
6 | autostart=True ; 在 supervisord 启动的时候也自动启动
7 | redirect_stderr=True ; 把 stderr 重定向到 stdout,默认 false
8 | command=/app/chatgpt-web ; 启动命令,与手动在命令行启动的命令是一样的
9 | user=root ; 用哪个用户启动
10 | stdout_logfile_maxbytes = 20MB ; stdout 日志文件大小,默认 50MB
11 | stdout_logfile_backups = 20 ; stdout 日志文件备份数
12 | ; stdout 日志文件,需要注意当指定目录不存在时无法正常启动,所以需要手动创建目录(supervisord 会自动创建日志文件)
13 | stdout_logfile = /app/run.log
--------------------------------------------------------------------------------