refactor: 重构项目目录结构

This commit is contained in:
RockYang 2023-05-06 11:35:57 +08:00
parent a897a755b6
commit 4dcae75488
28 changed files with 87 additions and 61 deletions

6
.gitignore vendored
View File

@ -8,7 +8,7 @@ pnpm-debug.log*
lerna-debug.log* lerna-debug.log*
node_modules node_modules
dist src/dist
dist-ssr dist-ssr
*.local *.local
@ -22,7 +22,7 @@ dist-ssr
*.njsproj *.njsproj
*.sln *.sln
*.sw? *.sw?
tmp src/tmp
bin bin
web/.env.development web/.env.development
data src/data

View File

@ -9,21 +9,42 @@
**本项目基于 MIT 协议,免费开放全部源代码,可以作为个人学习使用或者商用。如需商用建议联系作者登记,仅做统计使用,优秀项目我们将在项目首页为您展示。** **本项目基于 MIT 协议,免费开放全部源代码,可以作为个人学习使用或者商用。如需商用建议联系作者登记,仅做统计使用,优秀项目我们将在项目首页为您展示。**
## 项目功能介绍 ## 项目介绍
这一套完整的系统,包括两套前端聊天应用和一个后台管理系统。系统有用户鉴权,你可以自己使用,也可以部署直接给 C 端用户提供 ChatGPT 的服务。
项目的技术架构是
> Go + Vue3 + element-plus
后端采用的是 Go 语言开发的 Gin Web 框架。前端用的是 Vue3 + element-plus UI 框架
目前已经实现了以下功能:
1. 通用版的 ChatGPT 聊天界面和功能,聊天记录保存在客户端。
2. 口令机制:输入口令才可以访问,支持设置口令的对话次数,有效期。
3. 角色版的聊天界面和功能,角色设定,预设一些角色,比如程序员,客服,作家,老师,艺术家...
4. 保存聊天记录,支持聊天上下文。
5. OpenAI API 负载均衡,限制每个 API Key 每分钟之内调用次数不超过 15次防止被封。
6. 支持配置多个代理,保证高可用。
7. 实现 markdown 语法解析和代码高亮,支持复制回复内容功能。
8. 后台管理功能,实现系统的动态配置,用户和角色的动态管理。
## 功能截图
### 1.
## TODOLIST ## TODOLIST
* [x] 使用 level DB 保存用户,角色和聊天记录数据
* [x] 引入用户角色,添加用户聊天鉴权,设置口令模式
* [x] 定期清理不在线的会话 sessionID 和聊天上下文记录
* [x] OpenAI API 负载均衡,限制每个 API Key 每分钟之内调用次数不超过 15次防止被封
* [x] 角色设定,预设一些角色,比如程序员,客服,作家,老师,艺术家...
* [x] 实现 markdown 语法解析和代码高亮,支持复制回复内容功能
* [ ] 用户配置界面,配置用户的使用习惯,可以让用户配置自己的 API KEY调用自己的 API Key将不记 Token 的使用次数 * [ ] 用户配置界面,配置用户的使用习惯,可以让用户配置自己的 API KEY调用自己的 API Key将不记 Token 的使用次数
* [ ] **新增管理后台功能,实现用户,角色的动态管理**
* [ ] 嵌入 AI 绘画功能,支持根据描述词生成图片 * [ ] 嵌入 AI 绘画功能,支持根据描述词生成图片
* [ ] 接入自己训练的开源大语言模型 * [ ] 接入自己训练的开源大语言模型
* [ ] 接入 Google 语音 API支持语音聊天 * [ ] 接入 Google 语音 API支持语音聊天
## 本地部署
## 线上发布
## 注意事项
## 参与贡献 ## 参与贡献

View File

@ -1,22 +0,0 @@
Listen = "0.0.0.0:5678"
ProxyURL = "http://127.0.0.1:7890"
EnableAuth = false
Tokens = [""]
[Session]
SecretKey = "azyehq3ivunjhbntz78isj00i4hz2mt9xtddysfucxakadq4qbfrt0b7q3lnvg80"
Name = "CHAT_SESSION_ID"
Path = "/"
Domain = ""
MaxAge = 86400
Secure = true
HttpOnly = false
SameSite = 4
[Chat]
ApiURL = "https://api.openai.com/v1/chat/completions"
ApiKeys = ["xxx"]
Model = "gpt-3.5-turbo"
Temperature = 1.0
MaxTokens = 1024
EnableContext = true

31
src/config.sample.toml Normal file
View File

@ -0,0 +1,31 @@
Title = "Chat-Plus AI 助手"
ConsoleTitle = "Chat-Plus 控制台"
Listen = "0.0.0.0:5678"
ProxyURL = ["YOUR_PORYX_URL"]
AccessKey = "YOUR_ACCESS_KEY"
[Session]
SecretKey = "azyehq3ivunjhbntz78isj00i4hz2mt9xtddysfucxakadq4qbfrt0b7q3lnvg80"
Name = "CHAT_SESSION_ID"
Path = "/"
Domain = ""
MaxAge = 86400
Secure = false
HttpOnly = false
SameSite = 2
[ImgURL]
WechatCard = "https://img.r9it.com/chatgpt/WX20230505-162403.png"
WechatGroup = " https://img.r9it.com/chatgpt/WX20230505-162538.png"
[Manager]
Username = "admin"
Password = "admin123"
[Chat]
ApiURL = "https://api.openai.com/v1/chat/completions"
Model = "gpt-3.5-turbo"
Temperature = 1.0
MaxTokens = 1024
EnableContext = true
ChatContextExpireTime = 3600

View File

@ -1,4 +1,4 @@
module openai module chatplus
go 1.19 go 1.19

View File

View File

@ -1,12 +1,12 @@
package main package main
import ( import (
logger2 "chatplus/logger"
"chatplus/server"
"embed" "embed"
"flag" "flag"
"fmt" "fmt"
"github.com/mitchellh/go-homedir" "github.com/mitchellh/go-homedir"
logger2 "openai/logger"
"openai/server"
"os" "os"
"path/filepath" "path/filepath"
) )

View File

@ -1,9 +1,9 @@
package server package server
import ( import (
"chatplus/types"
"chatplus/utils"
"encoding/json" "encoding/json"
"openai/types"
"openai/utils"
) )
const ( const (

View File

@ -1,11 +1,11 @@
package server package server
import ( import (
"chatplus/types"
"chatplus/utils"
"encoding/json" "encoding/json"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"net/http" "net/http"
"openai/types"
"openai/utils"
) )
// AddApiKeyHandle 添加一个 API key // AddApiKeyHandle 添加一个 API key
@ -20,7 +20,7 @@ func (s *Server) AddApiKeyHandle(c *gin.Context) {
return return
} }
// 过滤已存在的 Key // 过滤已存在的 Key
for _,key:=range s.Config.Chat.ApiKeys { for _, key := range s.Config.Chat.ApiKeys {
if key.Value == data.ApiKey { if key.Value == data.ApiKey {
c.JSON(http.StatusOK, types.BizVo{Code: types.Failed, Message: "API KEY 已存在"}) c.JSON(http.StatusOK, types.BizVo{Code: types.Failed, Message: "API KEY 已存在"})
return return

View File

@ -1,11 +1,11 @@
package server package server
import ( import (
"chatplus/types"
"chatplus/utils"
"encoding/json" "encoding/json"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"net/http" "net/http"
"openai/types"
"openai/utils"
"strings" "strings"
) )

View File

@ -1,9 +1,9 @@
package server package server
import ( import (
"chatplus/types"
"encoding/json" "encoding/json"
"net/http" "net/http"
"openai/types"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )

View File

@ -1,12 +1,12 @@
package server package server
import ( import (
"chatplus/types"
"chatplus/utils"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"net/http" "net/http"
"openai/types"
"openai/utils"
"strings" "strings"
) )

View File

@ -3,6 +3,8 @@ package server
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"chatplus/types"
"chatplus/utils"
"context" "context"
"encoding/json" "encoding/json"
"errors" "errors"
@ -12,8 +14,6 @@ import (
"math/rand" "math/rand"
"net/http" "net/http"
"net/url" "net/url"
"openai/types"
"openai/utils"
"strings" "strings"
"time" "time"
) )

View File

@ -1,12 +1,12 @@
package server package server
import ( import (
"chatplus/types"
"chatplus/utils"
"encoding/json" "encoding/json"
"github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"net/http" "net/http"
"openai/types"
"openai/utils"
"strings" "strings"
"time" "time"
) )

View File

@ -1,6 +1,9 @@
package server package server
import ( import (
logger2 "chatplus/logger"
"chatplus/types"
"chatplus/utils"
"context" "context"
"embed" "embed"
"github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions"
@ -9,9 +12,6 @@ import (
"io" "io"
"io/fs" "io/fs"
"net/http" "net/http"
logger2 "openai/logger"
"openai/types"
"openai/utils"
"os" "os"
"path/filepath" "path/filepath"
"runtime/debug" "runtime/debug"

View File

@ -2,10 +2,10 @@ package utils
import ( import (
"bytes" "bytes"
logger2 "chatplus/logger"
"chatplus/types"
"github.com/BurntSushi/toml" "github.com/BurntSushi/toml"
"net/http" "net/http"
logger2 "openai/logger"
"openai/types"
"os" "os"
) )

View File

@ -45,7 +45,7 @@
<el-dialog <el-dialog
v-model="showDialog" v-model="showDialog"
title="编辑用户" title="编辑角色"
width="50%" width="50%"
> >
<el-form :model="form1" label-width="120px" ref="formRef" :rules="rules"> <el-form :model="form1" label-width="120px" ref="formRef" :rules="rules">

View File

@ -1,9 +1,5 @@
const {defineConfig} = require('@vue/cli-service') const {defineConfig} = require('@vue/cli-service')
let webpack = require('webpack') let webpack = require('webpack')
// const fs = require('fs')
// const path = require('path')
// const os = require('os')
module.exports = defineConfig({ module.exports = defineConfig({
transpileDependencies: true, transpileDependencies: true,
configureWebpack: { configureWebpack: {
@ -17,7 +13,7 @@ module.exports = defineConfig({
}, },
publicPath: '/chat', publicPath: '/chat',
outputDir: '../dist', outputDir: '../src/dist',
crossorigin: "anonymous", crossorigin: "anonymous",
devServer: { devServer: {
allowedHosts: ['127.0.0.1:5678'], allowedHosts: ['127.0.0.1:5678'],