Compare commits

...

10 Commits

Author SHA1 Message Date
Sakurasan
8257cd9bd9 update 2023-04-03 19:32:18 +08:00
Sakurasan
232712e27d update README 2023-04-03 19:05:59 +08:00
Sakurasan
60f4f58c6c fixed #2 适配zeabur
PORT写死的时候不能用
2023-04-03 17:18:11 +08:00
Sakurasan
610e56baac zeabur部署 2023-04-03 17:08:41 +08:00
Sakurasan
9e33f5a8dd Merge branch 'dev'
add reset_root && fix bug
2023-04-02 22:16:29 +08:00
Sakurasan
29abca9cf9 add reset_root 2023-04-02 22:06:27 +08:00
Sakurasan
9e5815a5e0 fix cache bug 2023-03-31 22:46:11 +08:00
Sakurasan
5023213596 Merge pull request #1 from mirrors2/dev
merge Dev to main
2023-03-31 22:06:38 +08:00
Sakurasan
a452469b51 up 2023-03-31 21:55:24 +08:00
Sakurasan
89f6e0df6a up 2023-03-31 21:08:07 +08:00
5 changed files with 91 additions and 27 deletions

View File

@@ -8,7 +8,7 @@ on:
tags:
- 'v*'
pull_request:
branches: [ "main","dev" ]
branches: [ "main" ]
#项目任务,任务之间可以并行调度

View File

@@ -30,13 +30,22 @@ or
```
wget https://github.com/mirrors2/opencatd-open/raw/main/docker/docker-compose.yml
```
## reset root token
```
docker exec -it opencatd-open ./opencatd reset_root
```
## Q&A
关于证书?
- docker部署会白白占用掉VPS的80443很不河里,建议用Nginx/Caddy/Traefik等反代并自动管理HTTPS证书.
没有服务器?
- 可以白嫖一些免费的容器托管服务:如:koyeb 或者其他
- 可以白嫖一些免费的容器托管服务:如:
- [![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/template/ppAoCV?referralCode=TW5RNa)
- [Zeabur](https://zeabur.com/zh-CN)
- [koyeb](https://koyeb.io/)
- [Fly.io](https://fly.io/)
- 或者其他
# License

View File

@@ -1,14 +1,29 @@
package main
import (
"log"
"net/http"
"opencatd-open/router"
_ "opencatd-open/store"
"opencatd-open/store"
"os"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
func main() {
args := os.Args[1:]
if len(args) > 0 && args[0] == "reset_root" {
log.Println("reset root token...")
ntoken := uuid.NewString()
if err := store.UpdateUser(uint(1), ntoken); err != nil {
log.Fatalln(err)
return
}
log.Println("new root token:", ntoken)
return
}
port := os.Getenv("PORT")
r := gin.Default()
group := r.Group("/1")
{
@@ -41,9 +56,17 @@ func main() {
// 初始化用户
r.POST("/1/users/init", router.Handleinit)
r.POST("/v1/chat/completions", router.HandleReverseProxy)
r.GET("/v1/models", router.HandleReverseProxy)
r.GET("/v1/dashboard/billing/credit_grants", router.HandleReverseProxy)
r.Run(":80")
r.POST("/v1/chat/completions", router.HandleProy)
r.GET("/v1/models", router.HandleProy)
r.GET("/v1/dashboard/billing/credit_grants", router.HandleProy)
r.GET("/", func(c *gin.Context) {
c.Writer.WriteHeader(http.StatusOK)
c.Writer.WriteString(`<h1><a href="https://github.com/mirrors2/opencatd-open" >opencatd-open</a> available</h1>Api-Keys:<a href=https://platform.openai.com/account/api-keys >https://platform.openai.com/account/api-keys</a>`)
})
if port == "" {
port = "80"
}
r.Run(":" + port)
}

View File

@@ -53,11 +53,27 @@ func AuthMiddleware() gin.HandlerFunc {
rootToken = u.Token
}
token := c.GetHeader("Authorization")
if token == "" || token[:7] != "Bearer " || token[7:] != rootToken {
if token == "" || token[:7] != "Bearer " {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
c.Abort()
return
}
if token[7:] != rootToken {
u, err := store.GetUserByID(uint(1))
if err != nil {
log.Println(err)
c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
c.Abort()
return
}
if token[:7] != u.Token {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
c.Abort()
return
}
rootToken = u.Token
store.LoadAuthCache()
}
// 可以在这里对 token 进行验证并检查权限
c.Next()
@@ -71,7 +87,7 @@ func Handleinit(c *gin.Context) {
u := store.User{Name: "root", Token: uuid.NewString()}
u.ID = 1
if err := store.CreateUser(&u); err != nil {
c.JSON(http.StatusOK, gin.H{
c.JSON(http.StatusForbidden, gin.H{
"error": err.Error(),
})
return
@@ -95,7 +111,7 @@ func Handleinit(c *gin.Context) {
return
}
if user.ID == uint(1) {
c.JSON(http.StatusOK, gin.H{
c.JSON(http.StatusForbidden, gin.H{
"error": "super user already exists, use cli to reset password",
})
}
@@ -214,14 +230,17 @@ func HandleResetUserToken(c *gin.Context) {
id := to.Int(c.Param("id"))
if err := store.UpdateUser(uint(id), uuid.NewString()); err != nil {
c.JSON(http.StatusOK, gin.H{"error": err.Error()})
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
return
}
u, err := store.GetUserByID(uint(id))
if err != nil {
c.JSON(http.StatusOK, gin.H{"error": err.Error()})
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
return
}
if u.ID == uint(1) {
rootToken = u.Token
}
c.JSON(http.StatusOK, u)
}
@@ -234,9 +253,7 @@ func HandleProy(c *gin.Context) {
var localuser bool
auth := c.Request.Header.Get("Authorization")
if len(auth) > 7 && auth[:7] == "Bearer " {
if store.IsExistAuthCache(auth[7:]) {
localuser = true
}
localuser = store.IsExistAuthCache(auth[7:])
}
client := http.DefaultClient
tr := &http.Transport{
@@ -324,30 +341,43 @@ func HandleReverseProxy(c *gin.Context) {
req.URL.Host = "api.openai.com"
// req.Header.Set("Authorization", "Bearer YOUR_API_KEY_HERE")
},
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
}
var localuser bool
auth := c.Request.Header.Get("Authorization")
if len(auth) > 7 && auth[:7] == "Bearer " {
if store.IsExistAuthCache(auth[7:]) {
localuser = true
}
log.Println(store.IsExistAuthCache(auth[7:]))
localuser = store.IsExistAuthCache(auth[7:])
}
// req, err := http.NewRequest(c.Request.Method, baseUrl+c.Request.URL.Path, c.Request.Body)
// if err != nil {
// c.JSON(http.StatusOK, gin.H{"error": err.Error()})
// return
// }
// req.Header = c.Request.Header
req, err := http.NewRequest(c.Request.Method, c.Request.URL.Path, c.Request.Body)
if err != nil {
c.JSON(http.StatusOK, gin.H{"error": err.Error()})
return
}
req.Header = c.Request.Header
if localuser {
if store.KeysCache.ItemCount() == 0 {
c.JSON(http.StatusOK, gin.H{"error": "No Api-Key Available"})
return
}
c.Request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", store.FromKeyCacheRandomItem()))
// c.Request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", store.FromKeyCacheRandomItem()))
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", store.FromKeyCacheRandomItem()))
}
proxy.ServeHTTP(c.Writer, c.Request)
proxy.ServeHTTP(c.Writer, req)
}

View File

@@ -19,6 +19,7 @@ func init() {
}
func LoadKeysCache() {
KeysCache = cache.New(cache.NoExpiration, cache.NoExpiration)
keys, err := GetAllKeys()
if err != nil {
log.Println(err)
@@ -40,6 +41,7 @@ func FromKeyCacheRandomItem() string {
}
func LoadAuthCache() {
AuthCache = cache.New(cache.NoExpiration, cache.NoExpiration)
users, err := GetAllUsers()
if err != nil {
log.Println(err)