Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
new-api
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
12aa6c35
authored
Apr 23, 2023
by
JustSong
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Relay done but not working
parent
28e8e89e
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
225 additions
and
70 deletions
+225
-70
common/constants.go
+18
-0
controller/channel.go
+1
-1
controller/relay.go
+52
-0
middleware/auth.go
+20
-49
middleware/distributor.go
+68
-0
model/channel.go
+20
-3
model/main.go
+1
-0
model/token.go
+26
-1
model/user.go
+0
-13
router/api-router.go
+3
-3
router/main.go
+1
-0
router/relay-router.go
+15
-0
No files found.
common/constants.go
View file @
12aa6c35
...
@@ -12,6 +12,8 @@ var SystemName = "One API"
...
@@ -12,6 +12,8 @@ var SystemName = "One API"
var
ServerAddress
=
"http://localhost:3000"
var
ServerAddress
=
"http://localhost:3000"
var
Footer
=
""
var
Footer
=
""
var
UsingSQLite
=
false
// Any options with "Secret", "Token" in its key won't be return by GetOptions
// Any options with "Secret", "Token" in its key won't be return by GetOptions
var
SessionSecret
=
uuid
.
New
()
.
String
()
var
SessionSecret
=
uuid
.
New
()
.
String
()
...
@@ -85,6 +87,11 @@ const (
...
@@ -85,6 +87,11 @@ const (
)
)
const
(
const
(
TokenStatusEnabled
=
1
// don't use 0, 0 is the default value!
TokenStatusDisabled
=
2
// also don't use 0
)
const
(
ChannelStatusUnknown
=
0
ChannelStatusUnknown
=
0
ChannelStatusEnabled
=
1
// don't use 0, 0 is the default value!
ChannelStatusEnabled
=
1
// don't use 0, 0 is the default value!
ChannelStatusDisabled
=
2
// also don't use 0
ChannelStatusDisabled
=
2
// also don't use 0
...
@@ -100,3 +107,14 @@ const (
...
@@ -100,3 +107,14 @@ const (
ChannelTypeOpenAIMax
=
6
ChannelTypeOpenAIMax
=
6
ChannelTypeOhMyGPT
=
7
ChannelTypeOhMyGPT
=
7
)
)
var
ChannelHosts
=
[]
string
{
""
,
// 0
"https://api.openai.com"
,
// 1
"https://openai.api2d.net"
,
// 2
""
,
// 3
"https://api.openai-asia.com"
,
// 4
"https://api.openai-sb.com"
,
// 5
"https://api.openaimax.com"
,
// 6
"https://api.ohmygpt.com"
,
// 7
}
controller/channel.go
View file @
12aa6c35
...
@@ -56,7 +56,7 @@ func GetChannel(c *gin.Context) {
...
@@ -56,7 +56,7 @@ func GetChannel(c *gin.Context) {
})
})
return
return
}
}
channel
,
err
:=
model
.
GetChannelById
(
id
)
channel
,
err
:=
model
.
GetChannelById
(
id
,
false
)
if
err
!=
nil
{
if
err
!=
nil
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"success"
:
false
,
...
...
controller/relay.go
0 → 100644
View file @
12aa6c35
package
controller
import
(
"fmt"
"github.com/gin-gonic/gin"
"io"
"net/http"
"one-api/common"
)
func
Relay
(
c
*
gin
.
Context
)
{
channelType
:=
c
.
GetInt
(
"channel"
)
host
:=
common
.
ChannelHosts
[
channelType
]
req
,
err
:=
http
.
NewRequest
(
c
.
Request
.
Method
,
fmt
.
Sprintf
(
"%s/%s"
,
host
,
c
.
Request
.
URL
.
String
()),
c
.
Request
.
Body
)
if
err
!=
nil
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"error"
:
gin
.
H
{
"message"
:
err
.
Error
(),
"type"
:
"one_api_error"
,
},
})
return
}
req
.
Header
=
c
.
Request
.
Header
.
Clone
()
client
:=
&
http
.
Client
{}
resp
,
err
:=
client
.
Do
(
req
)
if
err
!=
nil
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"error"
:
gin
.
H
{
"message"
:
err
.
Error
(),
"type"
:
"one_api_error"
,
},
})
return
}
for
k
,
v
:=
range
resp
.
Header
{
c
.
Writer
.
Header
()
.
Set
(
k
,
v
[
0
])
}
_
,
err
=
io
.
Copy
(
c
.
Writer
,
resp
.
Body
)
//body, err := io.ReadAll(resp.Body)
//_, err = c.Writer.Write(body)
if
err
!=
nil
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"error"
:
gin
.
H
{
"message"
:
err
.
Error
(),
"type"
:
"one_api_error"
,
},
})
return
}
}
middleware/auth.go
View file @
12aa6c35
...
@@ -6,6 +6,7 @@ import (
...
@@ -6,6 +6,7 @@ import (
"net/http"
"net/http"
"one-api/common"
"one-api/common"
"one-api/model"
"one-api/model"
"strings"
)
)
func
authHelper
(
c
*
gin
.
Context
,
minRole
int
)
{
func
authHelper
(
c
*
gin
.
Context
,
minRole
int
)
{
...
@@ -14,34 +15,13 @@ func authHelper(c *gin.Context, minRole int) {
...
@@ -14,34 +15,13 @@ func authHelper(c *gin.Context, minRole int) {
role
:=
session
.
Get
(
"role"
)
role
:=
session
.
Get
(
"role"
)
id
:=
session
.
Get
(
"id"
)
id
:=
session
.
Get
(
"id"
)
status
:=
session
.
Get
(
"status"
)
status
:=
session
.
Get
(
"status"
)
authByToken
:=
false
if
username
==
nil
{
if
username
==
nil
{
// Check token
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
token
:=
c
.
Request
.
Header
.
Get
(
"Authorization"
)
"success"
:
false
,
if
token
==
""
{
"message"
:
"无权进行此操作,未登录"
,
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
})
"success"
:
false
,
c
.
Abort
()
"message"
:
"无权进行此操作,未登录或 token 无效"
,
return
})
c
.
Abort
()
return
}
user
:=
model
.
ValidateUserToken
(
token
)
if
user
!=
nil
&&
user
.
Username
!=
""
{
// Token is valid
username
=
user
.
Username
role
=
user
.
Role
id
=
user
.
Id
status
=
user
.
Status
}
else
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"message"
:
"无权进行此操作,token 无效"
,
})
c
.
Abort
()
return
}
authByToken
=
true
}
}
if
status
.
(
int
)
==
common
.
UserStatusDisabled
{
if
status
.
(
int
)
==
common
.
UserStatusDisabled
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
...
@@ -62,7 +42,6 @@ func authHelper(c *gin.Context, minRole int) {
...
@@ -62,7 +42,6 @@ func authHelper(c *gin.Context, minRole int) {
c
.
Set
(
"username"
,
username
)
c
.
Set
(
"username"
,
username
)
c
.
Set
(
"role"
,
role
)
c
.
Set
(
"role"
,
role
)
c
.
Set
(
"id"
,
id
)
c
.
Set
(
"id"
,
id
)
c
.
Set
(
"authByToken"
,
authByToken
)
c
.
Next
()
c
.
Next
()
}
}
...
@@ -84,33 +63,25 @@ func RootAuth() func(c *gin.Context) {
...
@@ -84,33 +63,25 @@ func RootAuth() func(c *gin.Context) {
}
}
}
}
// NoTokenAuth You should always use this after normal auth middlewares.
func
TokenAuth
()
func
(
c
*
gin
.
Context
)
{
func
NoTokenAuth
()
func
(
c
*
gin
.
Context
)
{
return
func
(
c
*
gin
.
Context
)
{
return
func
(
c
*
gin
.
Context
)
{
authByToken
:=
c
.
GetBool
(
"authByToken"
)
key
:=
c
.
Request
.
Header
.
Get
(
"Authorization"
)
if
authByToken
{
parts
:=
strings
.
Split
(
key
,
"-"
)
key
=
parts
[
0
]
token
,
err
:=
model
.
ValidateUserToken
(
key
)
if
err
!=
nil
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"error"
:
gin
.
H
{
"message"
:
"本接口不支持使用 token 进行验证"
,
"message"
:
err
.
Error
(),
"type"
:
"one_api_error"
,
},
})
})
c
.
Abort
()
c
.
Abort
()
return
return
}
}
c
.
Next
()
c
.
Set
(
"id"
,
token
.
UserId
)
}
if
len
(
parts
)
>
1
{
}
c
.
Set
(
"channelId"
,
parts
[
1
])
// TokenOnlyAuth You should always use this after normal auth middlewares.
func
TokenOnlyAuth
()
func
(
c
*
gin
.
Context
)
{
return
func
(
c
*
gin
.
Context
)
{
authByToken
:=
c
.
GetBool
(
"authByToken"
)
if
!
authByToken
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"message"
:
"本接口仅支持使用 token 进行验证"
,
})
c
.
Abort
()
return
}
}
c
.
Next
()
c
.
Next
()
}
}
...
...
middleware/distributor.go
0 → 100644
View file @
12aa6c35
package
middleware
import
(
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"one-api/common"
"one-api/model"
"strconv"
)
func
Distribute
()
func
(
c
*
gin
.
Context
)
{
return
func
(
c
*
gin
.
Context
)
{
var
channel
*
model
.
Channel
channelId
,
ok
:=
c
.
Get
(
"channelId"
)
if
ok
{
id
,
err
:=
strconv
.
Atoi
(
channelId
.
(
string
))
if
err
!=
nil
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"error"
:
gin
.
H
{
"message"
:
"无效的渠道 ID"
,
"type"
:
"one_api_error"
,
},
})
c
.
Abort
()
return
}
channel
,
err
=
model
.
GetChannelById
(
id
,
true
)
if
err
!=
nil
{
c
.
JSON
(
200
,
gin
.
H
{
"error"
:
gin
.
H
{
"message"
:
"无效的渠道 ID"
,
"type"
:
"one_api_error"
,
},
})
c
.
Abort
()
return
}
if
channel
.
Status
!=
common
.
ChannelStatusEnabled
{
c
.
JSON
(
200
,
gin
.
H
{
"error"
:
gin
.
H
{
"message"
:
"该渠道已被禁用"
,
"type"
:
"one_api_error"
,
},
})
c
.
Abort
()
return
}
}
else
{
// Select a channel for the user
var
err
error
channel
,
err
=
model
.
GetRandomChannel
()
if
err
!=
nil
{
c
.
JSON
(
200
,
gin
.
H
{
"error"
:
gin
.
H
{
"message"
:
"无可用渠道"
,
"type"
:
"one_api_error"
,
},
})
c
.
Abort
()
return
}
}
c
.
Set
(
"channel"
,
channel
.
Type
)
c
.
Request
.
Header
.
Set
(
"Authorization"
,
fmt
.
Sprintf
(
"Bearer %s"
,
channel
.
Key
))
c
.
Next
()
}
}
model/channel.go
View file @
12aa6c35
...
@@ -2,12 +2,13 @@ package model
...
@@ -2,12 +2,13 @@ package model
import
(
import
(
_
"gorm.io/driver/sqlite"
_
"gorm.io/driver/sqlite"
"one-api/common"
)
)
type
Channel
struct
{
type
Channel
struct
{
Id
int
`json:"id"`
Id
int
`json:"id"`
Type
int
`json:"type" gorm:"default:0"`
Type
int
`json:"type" gorm:"default:0"`
Key
string
`json:"key"`
Key
string
`json:"key"
gorm:"not null"
`
Status
int
`json:"status" gorm:"default:1"`
Status
int
`json:"status" gorm:"default:1"`
Name
string
`json:"name" gorm:"index"`
Name
string
`json:"name" gorm:"index"`
Weight
int
`json:"weight"`
Weight
int
`json:"weight"`
...
@@ -27,10 +28,26 @@ func SearchChannels(keyword string) (channels []*Channel, err error) {
...
@@ -27,10 +28,26 @@ func SearchChannels(keyword string) (channels []*Channel, err error) {
return
channels
,
err
return
channels
,
err
}
}
func
GetChannelById
(
id
int
)
(
*
Channel
,
error
)
{
func
GetChannelById
(
id
int
,
selectAll
bool
)
(
*
Channel
,
error
)
{
channel
:=
Channel
{
Id
:
id
}
channel
:=
Channel
{
Id
:
id
}
var
err
error
=
nil
var
err
error
=
nil
err
=
DB
.
Omit
(
"key"
)
.
First
(
&
channel
,
"id = ?"
,
id
)
.
Error
if
selectAll
{
err
=
DB
.
First
(
&
channel
,
"id = ?"
,
id
)
.
Error
}
else
{
err
=
DB
.
Omit
(
"key"
)
.
First
(
&
channel
,
"id = ?"
,
id
)
.
Error
}
return
&
channel
,
err
}
func
GetRandomChannel
()
(
*
Channel
,
error
)
{
// TODO: consider weight
channel
:=
Channel
{}
var
err
error
=
nil
if
common
.
UsingSQLite
{
err
=
DB
.
Where
(
"status = ?"
,
common
.
ChannelStatusEnabled
)
.
Order
(
"RANDOM()"
)
.
Limit
(
1
)
.
First
(
&
channel
)
.
Error
}
else
{
err
=
DB
.
Where
(
"status = ?"
,
common
.
ChannelStatusEnabled
)
.
Order
(
"RAND()"
)
.
Limit
(
1
)
.
First
(
&
channel
)
.
Error
}
return
&
channel
,
err
return
&
channel
,
err
}
}
...
...
model/main.go
View file @
12aa6c35
...
@@ -45,6 +45,7 @@ func InitDB() (err error) {
...
@@ -45,6 +45,7 @@ func InitDB() (err error) {
})
})
}
else
{
}
else
{
// Use SQLite
// Use SQLite
common
.
UsingSQLite
=
true
db
,
err
=
gorm
.
Open
(
sqlite
.
Open
(
common
.
SQLitePath
),
&
gorm
.
Config
{
db
,
err
=
gorm
.
Open
(
sqlite
.
Open
(
common
.
SQLitePath
),
&
gorm
.
Config
{
PrepareStmt
:
true
,
// precompile SQL
PrepareStmt
:
true
,
// precompile SQL
})
})
...
...
model/token.go
View file @
12aa6c35
...
@@ -3,12 +3,14 @@ package model
...
@@ -3,12 +3,14 @@ package model
import
(
import
(
"errors"
"errors"
_
"gorm.io/driver/sqlite"
_
"gorm.io/driver/sqlite"
"one-api/common"
"strings"
)
)
type
Token
struct
{
type
Token
struct
{
Id
int
`json:"id"`
Id
int
`json:"id"`
UserId
int
`json:"user_id"`
UserId
int
`json:"user_id"`
Key
string
`json:"key"`
Key
string
`json:"key"
gorm:"uniqueIndex"
`
Status
int
`json:"status" gorm:"default:1"`
Status
int
`json:"status" gorm:"default:1"`
Name
string
`json:"name" gorm:"index" `
Name
string
`json:"name" gorm:"index" `
CreatedTime
int64
`json:"created_time" gorm:"bigint"`
CreatedTime
int64
`json:"created_time" gorm:"bigint"`
...
@@ -27,6 +29,29 @@ func SearchUserTokens(userId int, keyword string) (tokens []*Token, err error) {
...
@@ -27,6 +29,29 @@ func SearchUserTokens(userId int, keyword string) (tokens []*Token, err error) {
return
tokens
,
err
return
tokens
,
err
}
}
func
ValidateUserToken
(
key
string
)
(
token
*
Token
,
err
error
)
{
if
key
==
""
{
return
nil
,
errors
.
New
(
"未提供 token"
)
}
key
=
strings
.
Replace
(
key
,
"Bearer "
,
""
,
1
)
token
=
&
Token
{}
err
=
DB
.
Where
(
"key = ?"
,
key
)
.
First
(
token
)
.
Error
if
err
==
nil
{
if
token
.
Status
!=
common
.
TokenStatusEnabled
{
return
nil
,
errors
.
New
(
"该 token 已被禁用"
)
}
go
func
()
{
token
.
AccessedTime
=
common
.
GetTimestamp
()
err
:=
token
.
Update
()
if
err
!=
nil
{
common
.
SysError
(
"更新 token 访问时间失败:"
+
err
.
Error
())
}
}()
return
token
,
nil
}
return
nil
,
err
}
func
GetTokenByIds
(
id
int
,
userId
int
)
(
*
Token
,
error
)
{
func
GetTokenByIds
(
id
int
,
userId
int
)
(
*
Token
,
error
)
{
if
id
==
0
||
userId
==
0
{
if
id
==
0
||
userId
==
0
{
return
nil
,
errors
.
New
(
"id 或 userId 为空!"
)
return
nil
,
errors
.
New
(
"id 或 userId 为空!"
)
...
...
model/user.go
View file @
12aa6c35
...
@@ -3,7 +3,6 @@ package model
...
@@ -3,7 +3,6 @@ package model
import
(
import
(
"errors"
"errors"
"one-api/common"
"one-api/common"
"strings"
)
)
// User if you add sensitive fields, don't forget to clean them in setupLogin function.
// User if you add sensitive fields, don't forget to clean them in setupLogin function.
...
@@ -149,18 +148,6 @@ func (user *User) FillUserByUsername() error {
...
@@ -149,18 +148,6 @@ func (user *User) FillUserByUsername() error {
return
nil
return
nil
}
}
func
ValidateUserToken
(
token
string
)
(
user
*
User
)
{
if
token
==
""
{
return
nil
}
token
=
strings
.
Replace
(
token
,
"Bearer "
,
""
,
1
)
user
=
&
User
{}
if
DB
.
Where
(
"token = ?"
,
token
)
.
First
(
user
)
.
RowsAffected
==
1
{
return
user
}
return
nil
}
func
IsEmailAlreadyTaken
(
email
string
)
bool
{
func
IsEmailAlreadyTaken
(
email
string
)
bool
{
return
DB
.
Where
(
"email = ?"
,
email
)
.
Find
(
&
User
{})
.
RowsAffected
==
1
return
DB
.
Where
(
"email = ?"
,
email
)
.
Find
(
&
User
{})
.
RowsAffected
==
1
}
}
...
...
router/api-router.go
View file @
12aa6c35
...
@@ -28,7 +28,7 @@ func SetApiRouter(router *gin.Engine) {
...
@@ -28,7 +28,7 @@ func SetApiRouter(router *gin.Engine) {
userRoute
.
GET
(
"/logout"
,
controller
.
Logout
)
userRoute
.
GET
(
"/logout"
,
controller
.
Logout
)
selfRoute
:=
userRoute
.
Group
(
"/"
)
selfRoute
:=
userRoute
.
Group
(
"/"
)
selfRoute
.
Use
(
middleware
.
UserAuth
()
,
middleware
.
NoTokenAuth
()
)
selfRoute
.
Use
(
middleware
.
UserAuth
())
{
{
selfRoute
.
GET
(
"/self"
,
controller
.
GetSelf
)
selfRoute
.
GET
(
"/self"
,
controller
.
GetSelf
)
selfRoute
.
PUT
(
"/self"
,
controller
.
UpdateSelf
)
selfRoute
.
PUT
(
"/self"
,
controller
.
UpdateSelf
)
...
@@ -36,7 +36,7 @@ func SetApiRouter(router *gin.Engine) {
...
@@ -36,7 +36,7 @@ func SetApiRouter(router *gin.Engine) {
}
}
adminRoute
:=
userRoute
.
Group
(
"/"
)
adminRoute
:=
userRoute
.
Group
(
"/"
)
adminRoute
.
Use
(
middleware
.
AdminAuth
()
,
middleware
.
NoTokenAuth
()
)
adminRoute
.
Use
(
middleware
.
AdminAuth
())
{
{
adminRoute
.
GET
(
"/"
,
controller
.
GetAllUsers
)
adminRoute
.
GET
(
"/"
,
controller
.
GetAllUsers
)
adminRoute
.
GET
(
"/search"
,
controller
.
SearchUsers
)
adminRoute
.
GET
(
"/search"
,
controller
.
SearchUsers
)
...
@@ -48,7 +48,7 @@ func SetApiRouter(router *gin.Engine) {
...
@@ -48,7 +48,7 @@ func SetApiRouter(router *gin.Engine) {
}
}
}
}
optionRoute
:=
apiRouter
.
Group
(
"/option"
)
optionRoute
:=
apiRouter
.
Group
(
"/option"
)
optionRoute
.
Use
(
middleware
.
RootAuth
()
,
middleware
.
NoTokenAuth
()
)
optionRoute
.
Use
(
middleware
.
RootAuth
())
{
{
optionRoute
.
GET
(
"/"
,
controller
.
GetOptions
)
optionRoute
.
GET
(
"/"
,
controller
.
GetOptions
)
optionRoute
.
PUT
(
"/"
,
controller
.
UpdateOption
)
optionRoute
.
PUT
(
"/"
,
controller
.
UpdateOption
)
...
...
router/main.go
View file @
12aa6c35
...
@@ -7,5 +7,6 @@ import (
...
@@ -7,5 +7,6 @@ import (
func
SetRouter
(
router
*
gin
.
Engine
,
buildFS
embed
.
FS
,
indexPage
[]
byte
)
{
func
SetRouter
(
router
*
gin
.
Engine
,
buildFS
embed
.
FS
,
indexPage
[]
byte
)
{
SetApiRouter
(
router
)
SetApiRouter
(
router
)
SetRelayRouter
(
router
)
setWebRouter
(
router
,
buildFS
,
indexPage
)
setWebRouter
(
router
,
buildFS
,
indexPage
)
}
}
router/relay-router.go
0 → 100644
View file @
12aa6c35
package
router
import
(
"github.com/gin-gonic/gin"
"one-api/controller"
"one-api/middleware"
)
func
SetRelayRouter
(
router
*
gin
.
Engine
)
{
relayRouter
:=
router
.
Group
(
"/v1"
)
relayRouter
.
Use
(
middleware
.
GlobalAPIRateLimit
(),
middleware
.
TokenAuth
(),
middleware
.
Distribute
())
{
relayRouter
.
POST
(
"/chat/completions"
,
controller
.
Relay
)
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment