Compare commits

..

3 Commits

Author SHA1 Message Date
JustSong
2d39a135f2 feat: now slave server can sync options with master server (close #88) 2023-05-18 12:48:20 +08:00
JustSong
3c6834a79c feat: support redirecting frontend url now (close #89) 2023-05-18 12:26:18 +08:00
JustSong
6da3410823 fix: fix channel test error checking 2023-05-18 11:41:03 +08:00
6 changed files with 56 additions and 11 deletions

View File

@@ -55,19 +55,20 @@ _✨ All in one 的 OpenAI 接口,整合各种 API 访问方式,开箱即用
+ [x] 自定义渠道:例如使用自行搭建的 OpenAI 代理 + [x] 自定义渠道:例如使用自行搭建的 OpenAI 代理
2. 支持通过**负载均衡**的方式访问多个渠道。 2. 支持通过**负载均衡**的方式访问多个渠道。
3. 支持 **stream 模式**,可以通过流式传输实现打字机效果。 3. 支持 **stream 模式**,可以通过流式传输实现打字机效果。
4. 支持**令牌管理**,设置令牌的过期时间和使用次数 4. 支持**多机部署**[详见此处](#多机部署)
5. 支持**兑换码管理**支持批量生成和导出兑换码,可使用兑换码为令牌进行充值 5. 支持**令牌管理**设置令牌的过期时间和使用次数
6. 支持**通道管理**批量创建通道 6. 支持**兑换码管理**支持批量生成和导出兑换码,可使用兑换码为令牌进行充值
7. 支持发布公告,设置充值链接,设置新用户初始额度 7. 支持**通道管理**,批量创建通道
8. 支持丰富的**自定义**设置, 8. 支持发布公告,设置充值链接,设置新用户初始额度。
9. 支持丰富的**自定义**设置,
1. 支持自定义系统名称logo 以及页脚。 1. 支持自定义系统名称logo 以及页脚。
2. 支持自定义首页和关于页面,可以选择使用 HTML & Markdown 代码进行自定义,或者使用一个单独的网页通过 iframe 嵌入。 2. 支持自定义首页和关于页面,可以选择使用 HTML & Markdown 代码进行自定义,或者使用一个单独的网页通过 iframe 嵌入。
9. 支持通过系统访问令牌访问管理 API。 10. 支持通过系统访问令牌访问管理 API。
10. 支持用户管理,支持**多种用户登录注册方式** 11. 支持用户管理,支持**多种用户登录注册方式**
+ 邮箱登录注册以及通过邮箱进行密码重置。 + 邮箱登录注册以及通过邮箱进行密码重置。
+ [GitHub 开放授权](https://github.com/settings/applications/new)。 + [GitHub 开放授权](https://github.com/settings/applications/new)。
+ 微信公众号授权(需要额外部署 [WeChat Server](https://github.com/songquanpeng/wechat-server))。 + 微信公众号授权(需要额外部署 [WeChat Server](https://github.com/songquanpeng/wechat-server))。
11. 未来其他大模型开放 API 后,将第一时间支持,并将其封装成同样的 API 访问方式。 12. 未来其他大模型开放 API 后,将第一时间支持,并将其封装成同样的 API 访问方式。
## 部署 ## 部署
### 基于 Docker 进行部署 ### 基于 Docker 进行部署
@@ -133,6 +134,14 @@ sudo service nginx restart
更加详细的部署教程[参见此处](https://iamazing.cn/page/how-to-deploy-a-website)。 更加详细的部署教程[参见此处](https://iamazing.cn/page/how-to-deploy-a-website)。
### 多机部署
1. 所有服务器 `SESSION_SECRET` 设置一样的值。
2. 必须设置 `SQL_DSN`,使用 MySQL 数据库而非 SQLite请执行配置主备数据库同步。
3. 所有从服务器必须设置 `SYNC_FREQUENCY`,以定期从数据库同步配置。
4. 从服务器可以选择设置 `FRONTEND_BASE_URL`,以重定向页面请求到主服务器。‘
环境变量的具体使用方法详见[此处](#环境变量)。
## 配置 ## 配置
系统本身开箱即用。 系统本身开箱即用。
@@ -157,6 +166,10 @@ sudo service nginx restart
+ 例子:`SESSION_SECRET=random_string` + 例子:`SESSION_SECRET=random_string`
3. `SQL_DSN`:设置之后将使用指定数据库而非 SQLite。 3. `SQL_DSN`:设置之后将使用指定数据库而非 SQLite。
+ 例子:`SQL_DSN=root:123456@tcp(localhost:3306)/one-api` + 例子:`SQL_DSN=root:123456@tcp(localhost:3306)/one-api`
4. `FRONTEND_BASE_URL`:设置之后将使用指定的前端地址,而非后端地址。
+ 例子:`FRONTEND_BASE_URL=https://openai.justsong.cn`
5. `SYNC_FREQUENCY`:设置之后将定期与数据库同步配置,单位为秒,未设置则不进行同步。
+ 例子:`SYNC_FREQUENCY=60`
### 命令行参数 ### 命令行参数
1. `--port <port_number>`: 指定服务器监听的端口号,默认为 `3000`。 1. `--port <port_number>`: 指定服务器监听的端口号,默认为 `3000`。

View File

@@ -201,7 +201,7 @@ func testChannel(channel *model.Channel, request *ChatRequest) error {
if err != nil { if err != nil {
return err return err
} }
if response.Error.Type != "" { if response.Error.Message != "" {
return errors.New(fmt.Sprintf("type %s, code %s, message %s", response.Error.Type, response.Error.Code, response.Error.Message)) return errors.New(fmt.Sprintf("type %s, code %s, message %s", response.Error.Type, response.Error.Code, response.Error.Message))
} }
return nil return nil

View File

@@ -47,6 +47,13 @@ func main() {
// Initialize options // Initialize options
model.InitOptionMap() model.InitOptionMap()
if os.Getenv("SYNC_FREQUENCY") != "" {
frequency, err := strconv.Atoi(os.Getenv("SYNC_FREQUENCY"))
if err != nil {
common.FatalLog(err)
}
go model.SyncOptions(frequency)
}
// Initialize HTTP server // Initialize HTTP server
server := gin.Default() server := gin.Default()

View File

@@ -4,6 +4,7 @@ import (
"one-api/common" "one-api/common"
"strconv" "strconv"
"strings" "strings"
"time"
) )
type Option struct { type Option struct {
@@ -59,6 +60,10 @@ func InitOptionMap() {
common.OptionMap["ModelRatio"] = common.ModelRatio2JSONString() common.OptionMap["ModelRatio"] = common.ModelRatio2JSONString()
common.OptionMap["TopUpLink"] = common.TopUpLink common.OptionMap["TopUpLink"] = common.TopUpLink
common.OptionMapRWMutex.Unlock() common.OptionMapRWMutex.Unlock()
loadOptionsFromDatabase()
}
func loadOptionsFromDatabase() {
options, _ := AllOption() options, _ := AllOption()
for _, option := range options { for _, option := range options {
err := updateOptionMap(option.Key, option.Value) err := updateOptionMap(option.Key, option.Value)
@@ -68,6 +73,14 @@ func InitOptionMap() {
} }
} }
func SyncOptions(frequency int) {
for {
time.Sleep(time.Duration(frequency) * time.Second)
common.SysLog("Syncing options from database")
loadOptionsFromDatabase()
}
}
func UpdateOption(key string, value string) error { func UpdateOption(key string, value string) error {
// Save to database first // Save to database first
option := Option{ option := Option{

View File

@@ -2,12 +2,24 @@ package router
import ( import (
"embed" "embed"
"fmt"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"net/http"
"os"
"strings"
) )
func SetRouter(router *gin.Engine, buildFS embed.FS, indexPage []byte) { func SetRouter(router *gin.Engine, buildFS embed.FS, indexPage []byte) {
SetApiRouter(router) SetApiRouter(router)
SetDashboardRouter(router) SetDashboardRouter(router)
SetRelayRouter(router) SetRelayRouter(router)
setWebRouter(router, buildFS, indexPage) frontendBaseUrl := os.Getenv("FRONTEND_BASE_URL")
if frontendBaseUrl == "" {
SetWebRouter(router, buildFS, indexPage)
} else {
frontendBaseUrl = strings.TrimSuffix(frontendBaseUrl, "/")
router.NoRoute(func(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently, fmt.Sprintf("%s%s", frontendBaseUrl, c.Request.RequestURI))
})
}
} }

View File

@@ -10,7 +10,7 @@ import (
"one-api/middleware" "one-api/middleware"
) )
func setWebRouter(router *gin.Engine, buildFS embed.FS, indexPage []byte) { func SetWebRouter(router *gin.Engine, buildFS embed.FS, indexPage []byte) {
router.Use(gzip.Gzip(gzip.DefaultCompression)) router.Use(gzip.Gzip(gzip.DefaultCompression))
router.Use(middleware.GlobalWebRateLimit()) router.Use(middleware.GlobalWebRateLimit())
router.Use(middleware.Cache()) router.Use(middleware.Cache())