Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
d757df8a
Unverified
Commit
d757df8a
authored
Apr 05, 2026
by
Wesley Liddick
Committed by
GitHub
Apr 05, 2026
Browse files
Merge pull request #1463 from touwaeriol/feat/remove-sora
revert: completely remove Sora platform
parents
f585a15e
19655a15
Changes
163
Hide whitespace changes
Inline
Side-by-side
backend/ent/user/where.go
View file @
d757df8a
...
@@ -125,16 +125,6 @@ func TotpEnabledAt(v time.Time) predicate.User {
...
@@ -125,16 +125,6 @@ func TotpEnabledAt(v time.Time) predicate.User {
return
predicate
.
User
(
sql
.
FieldEQ
(
FieldTotpEnabledAt
,
v
))
return
predicate
.
User
(
sql
.
FieldEQ
(
FieldTotpEnabledAt
,
v
))
}
}
// SoraStorageQuotaBytes applies equality check predicate on the "sora_storage_quota_bytes" field. It's identical to SoraStorageQuotaBytesEQ.
func
SoraStorageQuotaBytes
(
v
int64
)
predicate
.
User
{
return
predicate
.
User
(
sql
.
FieldEQ
(
FieldSoraStorageQuotaBytes
,
v
))
}
// SoraStorageUsedBytes applies equality check predicate on the "sora_storage_used_bytes" field. It's identical to SoraStorageUsedBytesEQ.
func
SoraStorageUsedBytes
(
v
int64
)
predicate
.
User
{
return
predicate
.
User
(
sql
.
FieldEQ
(
FieldSoraStorageUsedBytes
,
v
))
}
// CreatedAtEQ applies the EQ predicate on the "created_at" field.
// CreatedAtEQ applies the EQ predicate on the "created_at" field.
func
CreatedAtEQ
(
v
time
.
Time
)
predicate
.
User
{
func
CreatedAtEQ
(
v
time
.
Time
)
predicate
.
User
{
return
predicate
.
User
(
sql
.
FieldEQ
(
FieldCreatedAt
,
v
))
return
predicate
.
User
(
sql
.
FieldEQ
(
FieldCreatedAt
,
v
))
...
@@ -870,86 +860,6 @@ func TotpEnabledAtNotNil() predicate.User {
...
@@ -870,86 +860,6 @@ func TotpEnabledAtNotNil() predicate.User {
return
predicate
.
User
(
sql
.
FieldNotNull
(
FieldTotpEnabledAt
))
return
predicate
.
User
(
sql
.
FieldNotNull
(
FieldTotpEnabledAt
))
}
}
// SoraStorageQuotaBytesEQ applies the EQ predicate on the "sora_storage_quota_bytes" field.
func
SoraStorageQuotaBytesEQ
(
v
int64
)
predicate
.
User
{
return
predicate
.
User
(
sql
.
FieldEQ
(
FieldSoraStorageQuotaBytes
,
v
))
}
// SoraStorageQuotaBytesNEQ applies the NEQ predicate on the "sora_storage_quota_bytes" field.
func
SoraStorageQuotaBytesNEQ
(
v
int64
)
predicate
.
User
{
return
predicate
.
User
(
sql
.
FieldNEQ
(
FieldSoraStorageQuotaBytes
,
v
))
}
// SoraStorageQuotaBytesIn applies the In predicate on the "sora_storage_quota_bytes" field.
func
SoraStorageQuotaBytesIn
(
vs
...
int64
)
predicate
.
User
{
return
predicate
.
User
(
sql
.
FieldIn
(
FieldSoraStorageQuotaBytes
,
vs
...
))
}
// SoraStorageQuotaBytesNotIn applies the NotIn predicate on the "sora_storage_quota_bytes" field.
func
SoraStorageQuotaBytesNotIn
(
vs
...
int64
)
predicate
.
User
{
return
predicate
.
User
(
sql
.
FieldNotIn
(
FieldSoraStorageQuotaBytes
,
vs
...
))
}
// SoraStorageQuotaBytesGT applies the GT predicate on the "sora_storage_quota_bytes" field.
func
SoraStorageQuotaBytesGT
(
v
int64
)
predicate
.
User
{
return
predicate
.
User
(
sql
.
FieldGT
(
FieldSoraStorageQuotaBytes
,
v
))
}
// SoraStorageQuotaBytesGTE applies the GTE predicate on the "sora_storage_quota_bytes" field.
func
SoraStorageQuotaBytesGTE
(
v
int64
)
predicate
.
User
{
return
predicate
.
User
(
sql
.
FieldGTE
(
FieldSoraStorageQuotaBytes
,
v
))
}
// SoraStorageQuotaBytesLT applies the LT predicate on the "sora_storage_quota_bytes" field.
func
SoraStorageQuotaBytesLT
(
v
int64
)
predicate
.
User
{
return
predicate
.
User
(
sql
.
FieldLT
(
FieldSoraStorageQuotaBytes
,
v
))
}
// SoraStorageQuotaBytesLTE applies the LTE predicate on the "sora_storage_quota_bytes" field.
func
SoraStorageQuotaBytesLTE
(
v
int64
)
predicate
.
User
{
return
predicate
.
User
(
sql
.
FieldLTE
(
FieldSoraStorageQuotaBytes
,
v
))
}
// SoraStorageUsedBytesEQ applies the EQ predicate on the "sora_storage_used_bytes" field.
func
SoraStorageUsedBytesEQ
(
v
int64
)
predicate
.
User
{
return
predicate
.
User
(
sql
.
FieldEQ
(
FieldSoraStorageUsedBytes
,
v
))
}
// SoraStorageUsedBytesNEQ applies the NEQ predicate on the "sora_storage_used_bytes" field.
func
SoraStorageUsedBytesNEQ
(
v
int64
)
predicate
.
User
{
return
predicate
.
User
(
sql
.
FieldNEQ
(
FieldSoraStorageUsedBytes
,
v
))
}
// SoraStorageUsedBytesIn applies the In predicate on the "sora_storage_used_bytes" field.
func
SoraStorageUsedBytesIn
(
vs
...
int64
)
predicate
.
User
{
return
predicate
.
User
(
sql
.
FieldIn
(
FieldSoraStorageUsedBytes
,
vs
...
))
}
// SoraStorageUsedBytesNotIn applies the NotIn predicate on the "sora_storage_used_bytes" field.
func
SoraStorageUsedBytesNotIn
(
vs
...
int64
)
predicate
.
User
{
return
predicate
.
User
(
sql
.
FieldNotIn
(
FieldSoraStorageUsedBytes
,
vs
...
))
}
// SoraStorageUsedBytesGT applies the GT predicate on the "sora_storage_used_bytes" field.
func
SoraStorageUsedBytesGT
(
v
int64
)
predicate
.
User
{
return
predicate
.
User
(
sql
.
FieldGT
(
FieldSoraStorageUsedBytes
,
v
))
}
// SoraStorageUsedBytesGTE applies the GTE predicate on the "sora_storage_used_bytes" field.
func
SoraStorageUsedBytesGTE
(
v
int64
)
predicate
.
User
{
return
predicate
.
User
(
sql
.
FieldGTE
(
FieldSoraStorageUsedBytes
,
v
))
}
// SoraStorageUsedBytesLT applies the LT predicate on the "sora_storage_used_bytes" field.
func
SoraStorageUsedBytesLT
(
v
int64
)
predicate
.
User
{
return
predicate
.
User
(
sql
.
FieldLT
(
FieldSoraStorageUsedBytes
,
v
))
}
// SoraStorageUsedBytesLTE applies the LTE predicate on the "sora_storage_used_bytes" field.
func
SoraStorageUsedBytesLTE
(
v
int64
)
predicate
.
User
{
return
predicate
.
User
(
sql
.
FieldLTE
(
FieldSoraStorageUsedBytes
,
v
))
}
// HasAPIKeys applies the HasEdge predicate on the "api_keys" edge.
// HasAPIKeys applies the HasEdge predicate on the "api_keys" edge.
func
HasAPIKeys
()
predicate
.
User
{
func
HasAPIKeys
()
predicate
.
User
{
return
predicate
.
User
(
func
(
s
*
sql
.
Selector
)
{
return
predicate
.
User
(
func
(
s
*
sql
.
Selector
)
{
...
...
backend/ent/user_create.go
View file @
d757df8a
...
@@ -210,34 +210,6 @@ func (_c *UserCreate) SetNillableTotpEnabledAt(v *time.Time) *UserCreate {
...
@@ -210,34 +210,6 @@ func (_c *UserCreate) SetNillableTotpEnabledAt(v *time.Time) *UserCreate {
return
_c
return
_c
}
}
// SetSoraStorageQuotaBytes sets the "sora_storage_quota_bytes" field.
func
(
_c
*
UserCreate
)
SetSoraStorageQuotaBytes
(
v
int64
)
*
UserCreate
{
_c
.
mutation
.
SetSoraStorageQuotaBytes
(
v
)
return
_c
}
// SetNillableSoraStorageQuotaBytes sets the "sora_storage_quota_bytes" field if the given value is not nil.
func
(
_c
*
UserCreate
)
SetNillableSoraStorageQuotaBytes
(
v
*
int64
)
*
UserCreate
{
if
v
!=
nil
{
_c
.
SetSoraStorageQuotaBytes
(
*
v
)
}
return
_c
}
// SetSoraStorageUsedBytes sets the "sora_storage_used_bytes" field.
func
(
_c
*
UserCreate
)
SetSoraStorageUsedBytes
(
v
int64
)
*
UserCreate
{
_c
.
mutation
.
SetSoraStorageUsedBytes
(
v
)
return
_c
}
// SetNillableSoraStorageUsedBytes sets the "sora_storage_used_bytes" field if the given value is not nil.
func
(
_c
*
UserCreate
)
SetNillableSoraStorageUsedBytes
(
v
*
int64
)
*
UserCreate
{
if
v
!=
nil
{
_c
.
SetSoraStorageUsedBytes
(
*
v
)
}
return
_c
}
// AddAPIKeyIDs adds the "api_keys" edge to the APIKey entity by IDs.
// AddAPIKeyIDs adds the "api_keys" edge to the APIKey entity by IDs.
func
(
_c
*
UserCreate
)
AddAPIKeyIDs
(
ids
...
int64
)
*
UserCreate
{
func
(
_c
*
UserCreate
)
AddAPIKeyIDs
(
ids
...
int64
)
*
UserCreate
{
_c
.
mutation
.
AddAPIKeyIDs
(
ids
...
)
_c
.
mutation
.
AddAPIKeyIDs
(
ids
...
)
...
@@ -452,14 +424,6 @@ func (_c *UserCreate) defaults() error {
...
@@ -452,14 +424,6 @@ func (_c *UserCreate) defaults() error {
v
:=
user
.
DefaultTotpEnabled
v
:=
user
.
DefaultTotpEnabled
_c
.
mutation
.
SetTotpEnabled
(
v
)
_c
.
mutation
.
SetTotpEnabled
(
v
)
}
}
if
_
,
ok
:=
_c
.
mutation
.
SoraStorageQuotaBytes
();
!
ok
{
v
:=
user
.
DefaultSoraStorageQuotaBytes
_c
.
mutation
.
SetSoraStorageQuotaBytes
(
v
)
}
if
_
,
ok
:=
_c
.
mutation
.
SoraStorageUsedBytes
();
!
ok
{
v
:=
user
.
DefaultSoraStorageUsedBytes
_c
.
mutation
.
SetSoraStorageUsedBytes
(
v
)
}
return
nil
return
nil
}
}
...
@@ -523,12 +487,6 @@ func (_c *UserCreate) check() error {
...
@@ -523,12 +487,6 @@ func (_c *UserCreate) check() error {
if
_
,
ok
:=
_c
.
mutation
.
TotpEnabled
();
!
ok
{
if
_
,
ok
:=
_c
.
mutation
.
TotpEnabled
();
!
ok
{
return
&
ValidationError
{
Name
:
"totp_enabled"
,
err
:
errors
.
New
(
`ent: missing required field "User.totp_enabled"`
)}
return
&
ValidationError
{
Name
:
"totp_enabled"
,
err
:
errors
.
New
(
`ent: missing required field "User.totp_enabled"`
)}
}
}
if
_
,
ok
:=
_c
.
mutation
.
SoraStorageQuotaBytes
();
!
ok
{
return
&
ValidationError
{
Name
:
"sora_storage_quota_bytes"
,
err
:
errors
.
New
(
`ent: missing required field "User.sora_storage_quota_bytes"`
)}
}
if
_
,
ok
:=
_c
.
mutation
.
SoraStorageUsedBytes
();
!
ok
{
return
&
ValidationError
{
Name
:
"sora_storage_used_bytes"
,
err
:
errors
.
New
(
`ent: missing required field "User.sora_storage_used_bytes"`
)}
}
return
nil
return
nil
}
}
...
@@ -612,14 +570,6 @@ func (_c *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) {
...
@@ -612,14 +570,6 @@ func (_c *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) {
_spec
.
SetField
(
user
.
FieldTotpEnabledAt
,
field
.
TypeTime
,
value
)
_spec
.
SetField
(
user
.
FieldTotpEnabledAt
,
field
.
TypeTime
,
value
)
_node
.
TotpEnabledAt
=
&
value
_node
.
TotpEnabledAt
=
&
value
}
}
if
value
,
ok
:=
_c
.
mutation
.
SoraStorageQuotaBytes
();
ok
{
_spec
.
SetField
(
user
.
FieldSoraStorageQuotaBytes
,
field
.
TypeInt64
,
value
)
_node
.
SoraStorageQuotaBytes
=
value
}
if
value
,
ok
:=
_c
.
mutation
.
SoraStorageUsedBytes
();
ok
{
_spec
.
SetField
(
user
.
FieldSoraStorageUsedBytes
,
field
.
TypeInt64
,
value
)
_node
.
SoraStorageUsedBytes
=
value
}
if
nodes
:=
_c
.
mutation
.
APIKeysIDs
();
len
(
nodes
)
>
0
{
if
nodes
:=
_c
.
mutation
.
APIKeysIDs
();
len
(
nodes
)
>
0
{
edge
:=
&
sqlgraph
.
EdgeSpec
{
edge
:=
&
sqlgraph
.
EdgeSpec
{
Rel
:
sqlgraph
.
O2M
,
Rel
:
sqlgraph
.
O2M
,
...
@@ -1006,42 +956,6 @@ func (u *UserUpsert) ClearTotpEnabledAt() *UserUpsert {
...
@@ -1006,42 +956,6 @@ func (u *UserUpsert) ClearTotpEnabledAt() *UserUpsert {
return
u
return
u
}
}
// SetSoraStorageQuotaBytes sets the "sora_storage_quota_bytes" field.
func
(
u
*
UserUpsert
)
SetSoraStorageQuotaBytes
(
v
int64
)
*
UserUpsert
{
u
.
Set
(
user
.
FieldSoraStorageQuotaBytes
,
v
)
return
u
}
// UpdateSoraStorageQuotaBytes sets the "sora_storage_quota_bytes" field to the value that was provided on create.
func
(
u
*
UserUpsert
)
UpdateSoraStorageQuotaBytes
()
*
UserUpsert
{
u
.
SetExcluded
(
user
.
FieldSoraStorageQuotaBytes
)
return
u
}
// AddSoraStorageQuotaBytes adds v to the "sora_storage_quota_bytes" field.
func
(
u
*
UserUpsert
)
AddSoraStorageQuotaBytes
(
v
int64
)
*
UserUpsert
{
u
.
Add
(
user
.
FieldSoraStorageQuotaBytes
,
v
)
return
u
}
// SetSoraStorageUsedBytes sets the "sora_storage_used_bytes" field.
func
(
u
*
UserUpsert
)
SetSoraStorageUsedBytes
(
v
int64
)
*
UserUpsert
{
u
.
Set
(
user
.
FieldSoraStorageUsedBytes
,
v
)
return
u
}
// UpdateSoraStorageUsedBytes sets the "sora_storage_used_bytes" field to the value that was provided on create.
func
(
u
*
UserUpsert
)
UpdateSoraStorageUsedBytes
()
*
UserUpsert
{
u
.
SetExcluded
(
user
.
FieldSoraStorageUsedBytes
)
return
u
}
// AddSoraStorageUsedBytes adds v to the "sora_storage_used_bytes" field.
func
(
u
*
UserUpsert
)
AddSoraStorageUsedBytes
(
v
int64
)
*
UserUpsert
{
u
.
Add
(
user
.
FieldSoraStorageUsedBytes
,
v
)
return
u
}
// UpdateNewValues updates the mutable fields using the new values that were set on create.
// UpdateNewValues updates the mutable fields using the new values that were set on create.
// Using this option is equivalent to using:
// Using this option is equivalent to using:
//
//
...
@@ -1304,48 +1218,6 @@ func (u *UserUpsertOne) ClearTotpEnabledAt() *UserUpsertOne {
...
@@ -1304,48 +1218,6 @@ func (u *UserUpsertOne) ClearTotpEnabledAt() *UserUpsertOne {
})
})
}
}
// SetSoraStorageQuotaBytes sets the "sora_storage_quota_bytes" field.
func
(
u
*
UserUpsertOne
)
SetSoraStorageQuotaBytes
(
v
int64
)
*
UserUpsertOne
{
return
u
.
Update
(
func
(
s
*
UserUpsert
)
{
s
.
SetSoraStorageQuotaBytes
(
v
)
})
}
// AddSoraStorageQuotaBytes adds v to the "sora_storage_quota_bytes" field.
func
(
u
*
UserUpsertOne
)
AddSoraStorageQuotaBytes
(
v
int64
)
*
UserUpsertOne
{
return
u
.
Update
(
func
(
s
*
UserUpsert
)
{
s
.
AddSoraStorageQuotaBytes
(
v
)
})
}
// UpdateSoraStorageQuotaBytes sets the "sora_storage_quota_bytes" field to the value that was provided on create.
func
(
u
*
UserUpsertOne
)
UpdateSoraStorageQuotaBytes
()
*
UserUpsertOne
{
return
u
.
Update
(
func
(
s
*
UserUpsert
)
{
s
.
UpdateSoraStorageQuotaBytes
()
})
}
// SetSoraStorageUsedBytes sets the "sora_storage_used_bytes" field.
func
(
u
*
UserUpsertOne
)
SetSoraStorageUsedBytes
(
v
int64
)
*
UserUpsertOne
{
return
u
.
Update
(
func
(
s
*
UserUpsert
)
{
s
.
SetSoraStorageUsedBytes
(
v
)
})
}
// AddSoraStorageUsedBytes adds v to the "sora_storage_used_bytes" field.
func
(
u
*
UserUpsertOne
)
AddSoraStorageUsedBytes
(
v
int64
)
*
UserUpsertOne
{
return
u
.
Update
(
func
(
s
*
UserUpsert
)
{
s
.
AddSoraStorageUsedBytes
(
v
)
})
}
// UpdateSoraStorageUsedBytes sets the "sora_storage_used_bytes" field to the value that was provided on create.
func
(
u
*
UserUpsertOne
)
UpdateSoraStorageUsedBytes
()
*
UserUpsertOne
{
return
u
.
Update
(
func
(
s
*
UserUpsert
)
{
s
.
UpdateSoraStorageUsedBytes
()
})
}
// Exec executes the query.
// Exec executes the query.
func
(
u
*
UserUpsertOne
)
Exec
(
ctx
context
.
Context
)
error
{
func
(
u
*
UserUpsertOne
)
Exec
(
ctx
context
.
Context
)
error
{
if
len
(
u
.
create
.
conflict
)
==
0
{
if
len
(
u
.
create
.
conflict
)
==
0
{
...
@@ -1774,48 +1646,6 @@ func (u *UserUpsertBulk) ClearTotpEnabledAt() *UserUpsertBulk {
...
@@ -1774,48 +1646,6 @@ func (u *UserUpsertBulk) ClearTotpEnabledAt() *UserUpsertBulk {
})
})
}
}
// SetSoraStorageQuotaBytes sets the "sora_storage_quota_bytes" field.
func
(
u
*
UserUpsertBulk
)
SetSoraStorageQuotaBytes
(
v
int64
)
*
UserUpsertBulk
{
return
u
.
Update
(
func
(
s
*
UserUpsert
)
{
s
.
SetSoraStorageQuotaBytes
(
v
)
})
}
// AddSoraStorageQuotaBytes adds v to the "sora_storage_quota_bytes" field.
func
(
u
*
UserUpsertBulk
)
AddSoraStorageQuotaBytes
(
v
int64
)
*
UserUpsertBulk
{
return
u
.
Update
(
func
(
s
*
UserUpsert
)
{
s
.
AddSoraStorageQuotaBytes
(
v
)
})
}
// UpdateSoraStorageQuotaBytes sets the "sora_storage_quota_bytes" field to the value that was provided on create.
func
(
u
*
UserUpsertBulk
)
UpdateSoraStorageQuotaBytes
()
*
UserUpsertBulk
{
return
u
.
Update
(
func
(
s
*
UserUpsert
)
{
s
.
UpdateSoraStorageQuotaBytes
()
})
}
// SetSoraStorageUsedBytes sets the "sora_storage_used_bytes" field.
func
(
u
*
UserUpsertBulk
)
SetSoraStorageUsedBytes
(
v
int64
)
*
UserUpsertBulk
{
return
u
.
Update
(
func
(
s
*
UserUpsert
)
{
s
.
SetSoraStorageUsedBytes
(
v
)
})
}
// AddSoraStorageUsedBytes adds v to the "sora_storage_used_bytes" field.
func
(
u
*
UserUpsertBulk
)
AddSoraStorageUsedBytes
(
v
int64
)
*
UserUpsertBulk
{
return
u
.
Update
(
func
(
s
*
UserUpsert
)
{
s
.
AddSoraStorageUsedBytes
(
v
)
})
}
// UpdateSoraStorageUsedBytes sets the "sora_storage_used_bytes" field to the value that was provided on create.
func
(
u
*
UserUpsertBulk
)
UpdateSoraStorageUsedBytes
()
*
UserUpsertBulk
{
return
u
.
Update
(
func
(
s
*
UserUpsert
)
{
s
.
UpdateSoraStorageUsedBytes
()
})
}
// Exec executes the query.
// Exec executes the query.
func
(
u
*
UserUpsertBulk
)
Exec
(
ctx
context
.
Context
)
error
{
func
(
u
*
UserUpsertBulk
)
Exec
(
ctx
context
.
Context
)
error
{
if
u
.
create
.
err
!=
nil
{
if
u
.
create
.
err
!=
nil
{
...
...
backend/ent/user_update.go
View file @
d757df8a
...
@@ -242,48 +242,6 @@ func (_u *UserUpdate) ClearTotpEnabledAt() *UserUpdate {
...
@@ -242,48 +242,6 @@ func (_u *UserUpdate) ClearTotpEnabledAt() *UserUpdate {
return
_u
return
_u
}
}
// SetSoraStorageQuotaBytes sets the "sora_storage_quota_bytes" field.
func
(
_u
*
UserUpdate
)
SetSoraStorageQuotaBytes
(
v
int64
)
*
UserUpdate
{
_u
.
mutation
.
ResetSoraStorageQuotaBytes
()
_u
.
mutation
.
SetSoraStorageQuotaBytes
(
v
)
return
_u
}
// SetNillableSoraStorageQuotaBytes sets the "sora_storage_quota_bytes" field if the given value is not nil.
func
(
_u
*
UserUpdate
)
SetNillableSoraStorageQuotaBytes
(
v
*
int64
)
*
UserUpdate
{
if
v
!=
nil
{
_u
.
SetSoraStorageQuotaBytes
(
*
v
)
}
return
_u
}
// AddSoraStorageQuotaBytes adds value to the "sora_storage_quota_bytes" field.
func
(
_u
*
UserUpdate
)
AddSoraStorageQuotaBytes
(
v
int64
)
*
UserUpdate
{
_u
.
mutation
.
AddSoraStorageQuotaBytes
(
v
)
return
_u
}
// SetSoraStorageUsedBytes sets the "sora_storage_used_bytes" field.
func
(
_u
*
UserUpdate
)
SetSoraStorageUsedBytes
(
v
int64
)
*
UserUpdate
{
_u
.
mutation
.
ResetSoraStorageUsedBytes
()
_u
.
mutation
.
SetSoraStorageUsedBytes
(
v
)
return
_u
}
// SetNillableSoraStorageUsedBytes sets the "sora_storage_used_bytes" field if the given value is not nil.
func
(
_u
*
UserUpdate
)
SetNillableSoraStorageUsedBytes
(
v
*
int64
)
*
UserUpdate
{
if
v
!=
nil
{
_u
.
SetSoraStorageUsedBytes
(
*
v
)
}
return
_u
}
// AddSoraStorageUsedBytes adds value to the "sora_storage_used_bytes" field.
func
(
_u
*
UserUpdate
)
AddSoraStorageUsedBytes
(
v
int64
)
*
UserUpdate
{
_u
.
mutation
.
AddSoraStorageUsedBytes
(
v
)
return
_u
}
// AddAPIKeyIDs adds the "api_keys" edge to the APIKey entity by IDs.
// AddAPIKeyIDs adds the "api_keys" edge to the APIKey entity by IDs.
func
(
_u
*
UserUpdate
)
AddAPIKeyIDs
(
ids
...
int64
)
*
UserUpdate
{
func
(
_u
*
UserUpdate
)
AddAPIKeyIDs
(
ids
...
int64
)
*
UserUpdate
{
_u
.
mutation
.
AddAPIKeyIDs
(
ids
...
)
_u
.
mutation
.
AddAPIKeyIDs
(
ids
...
)
...
@@ -751,18 +709,6 @@ func (_u *UserUpdate) sqlSave(ctx context.Context) (_node int, err error) {
...
@@ -751,18 +709,6 @@ func (_u *UserUpdate) sqlSave(ctx context.Context) (_node int, err error) {
if
_u
.
mutation
.
TotpEnabledAtCleared
()
{
if
_u
.
mutation
.
TotpEnabledAtCleared
()
{
_spec
.
ClearField
(
user
.
FieldTotpEnabledAt
,
field
.
TypeTime
)
_spec
.
ClearField
(
user
.
FieldTotpEnabledAt
,
field
.
TypeTime
)
}
}
if
value
,
ok
:=
_u
.
mutation
.
SoraStorageQuotaBytes
();
ok
{
_spec
.
SetField
(
user
.
FieldSoraStorageQuotaBytes
,
field
.
TypeInt64
,
value
)
}
if
value
,
ok
:=
_u
.
mutation
.
AddedSoraStorageQuotaBytes
();
ok
{
_spec
.
AddField
(
user
.
FieldSoraStorageQuotaBytes
,
field
.
TypeInt64
,
value
)
}
if
value
,
ok
:=
_u
.
mutation
.
SoraStorageUsedBytes
();
ok
{
_spec
.
SetField
(
user
.
FieldSoraStorageUsedBytes
,
field
.
TypeInt64
,
value
)
}
if
value
,
ok
:=
_u
.
mutation
.
AddedSoraStorageUsedBytes
();
ok
{
_spec
.
AddField
(
user
.
FieldSoraStorageUsedBytes
,
field
.
TypeInt64
,
value
)
}
if
_u
.
mutation
.
APIKeysCleared
()
{
if
_u
.
mutation
.
APIKeysCleared
()
{
edge
:=
&
sqlgraph
.
EdgeSpec
{
edge
:=
&
sqlgraph
.
EdgeSpec
{
Rel
:
sqlgraph
.
O2M
,
Rel
:
sqlgraph
.
O2M
,
...
@@ -1406,48 +1352,6 @@ func (_u *UserUpdateOne) ClearTotpEnabledAt() *UserUpdateOne {
...
@@ -1406,48 +1352,6 @@ func (_u *UserUpdateOne) ClearTotpEnabledAt() *UserUpdateOne {
return
_u
return
_u
}
}
// SetSoraStorageQuotaBytes sets the "sora_storage_quota_bytes" field.
func
(
_u
*
UserUpdateOne
)
SetSoraStorageQuotaBytes
(
v
int64
)
*
UserUpdateOne
{
_u
.
mutation
.
ResetSoraStorageQuotaBytes
()
_u
.
mutation
.
SetSoraStorageQuotaBytes
(
v
)
return
_u
}
// SetNillableSoraStorageQuotaBytes sets the "sora_storage_quota_bytes" field if the given value is not nil.
func
(
_u
*
UserUpdateOne
)
SetNillableSoraStorageQuotaBytes
(
v
*
int64
)
*
UserUpdateOne
{
if
v
!=
nil
{
_u
.
SetSoraStorageQuotaBytes
(
*
v
)
}
return
_u
}
// AddSoraStorageQuotaBytes adds value to the "sora_storage_quota_bytes" field.
func
(
_u
*
UserUpdateOne
)
AddSoraStorageQuotaBytes
(
v
int64
)
*
UserUpdateOne
{
_u
.
mutation
.
AddSoraStorageQuotaBytes
(
v
)
return
_u
}
// SetSoraStorageUsedBytes sets the "sora_storage_used_bytes" field.
func
(
_u
*
UserUpdateOne
)
SetSoraStorageUsedBytes
(
v
int64
)
*
UserUpdateOne
{
_u
.
mutation
.
ResetSoraStorageUsedBytes
()
_u
.
mutation
.
SetSoraStorageUsedBytes
(
v
)
return
_u
}
// SetNillableSoraStorageUsedBytes sets the "sora_storage_used_bytes" field if the given value is not nil.
func
(
_u
*
UserUpdateOne
)
SetNillableSoraStorageUsedBytes
(
v
*
int64
)
*
UserUpdateOne
{
if
v
!=
nil
{
_u
.
SetSoraStorageUsedBytes
(
*
v
)
}
return
_u
}
// AddSoraStorageUsedBytes adds value to the "sora_storage_used_bytes" field.
func
(
_u
*
UserUpdateOne
)
AddSoraStorageUsedBytes
(
v
int64
)
*
UserUpdateOne
{
_u
.
mutation
.
AddSoraStorageUsedBytes
(
v
)
return
_u
}
// AddAPIKeyIDs adds the "api_keys" edge to the APIKey entity by IDs.
// AddAPIKeyIDs adds the "api_keys" edge to the APIKey entity by IDs.
func
(
_u
*
UserUpdateOne
)
AddAPIKeyIDs
(
ids
...
int64
)
*
UserUpdateOne
{
func
(
_u
*
UserUpdateOne
)
AddAPIKeyIDs
(
ids
...
int64
)
*
UserUpdateOne
{
_u
.
mutation
.
AddAPIKeyIDs
(
ids
...
)
_u
.
mutation
.
AddAPIKeyIDs
(
ids
...
)
...
@@ -1945,18 +1849,6 @@ func (_u *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error) {
...
@@ -1945,18 +1849,6 @@ func (_u *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error) {
if
_u
.
mutation
.
TotpEnabledAtCleared
()
{
if
_u
.
mutation
.
TotpEnabledAtCleared
()
{
_spec
.
ClearField
(
user
.
FieldTotpEnabledAt
,
field
.
TypeTime
)
_spec
.
ClearField
(
user
.
FieldTotpEnabledAt
,
field
.
TypeTime
)
}
}
if
value
,
ok
:=
_u
.
mutation
.
SoraStorageQuotaBytes
();
ok
{
_spec
.
SetField
(
user
.
FieldSoraStorageQuotaBytes
,
field
.
TypeInt64
,
value
)
}
if
value
,
ok
:=
_u
.
mutation
.
AddedSoraStorageQuotaBytes
();
ok
{
_spec
.
AddField
(
user
.
FieldSoraStorageQuotaBytes
,
field
.
TypeInt64
,
value
)
}
if
value
,
ok
:=
_u
.
mutation
.
SoraStorageUsedBytes
();
ok
{
_spec
.
SetField
(
user
.
FieldSoraStorageUsedBytes
,
field
.
TypeInt64
,
value
)
}
if
value
,
ok
:=
_u
.
mutation
.
AddedSoraStorageUsedBytes
();
ok
{
_spec
.
AddField
(
user
.
FieldSoraStorageUsedBytes
,
field
.
TypeInt64
,
value
)
}
if
_u
.
mutation
.
APIKeysCleared
()
{
if
_u
.
mutation
.
APIKeysCleared
()
{
edge
:=
&
sqlgraph
.
EdgeSpec
{
edge
:=
&
sqlgraph
.
EdgeSpec
{
Rel
:
sqlgraph
.
O2M
,
Rel
:
sqlgraph
.
O2M
,
...
...
backend/internal/config/config.go
View file @
d757df8a
...
@@ -77,7 +77,6 @@ type Config struct {
...
@@ -77,7 +77,6 @@ type Config struct {
UsageCleanup
UsageCleanupConfig
`mapstructure:"usage_cleanup"`
UsageCleanup
UsageCleanupConfig
`mapstructure:"usage_cleanup"`
Concurrency
ConcurrencyConfig
`mapstructure:"concurrency"`
Concurrency
ConcurrencyConfig
`mapstructure:"concurrency"`
TokenRefresh
TokenRefreshConfig
`mapstructure:"token_refresh"`
TokenRefresh
TokenRefreshConfig
`mapstructure:"token_refresh"`
Sora
SoraConfig
`mapstructure:"sora"`
RunMode
string
`mapstructure:"run_mode" yaml:"run_mode"`
RunMode
string
`mapstructure:"run_mode" yaml:"run_mode"`
Timezone
string
`mapstructure:"timezone"`
// e.g. "Asia/Shanghai", "UTC"
Timezone
string
`mapstructure:"timezone"`
// e.g. "Asia/Shanghai", "UTC"
Gemini
GeminiConfig
`mapstructure:"gemini"`
Gemini
GeminiConfig
`mapstructure:"gemini"`
...
@@ -197,8 +196,6 @@ type TokenRefreshConfig struct {
...
@@ -197,8 +196,6 @@ type TokenRefreshConfig struct {
MaxRetries
int
`mapstructure:"max_retries"`
MaxRetries
int
`mapstructure:"max_retries"`
// 重试退避基础时间(秒)
// 重试退避基础时间(秒)
RetryBackoffSeconds
int
`mapstructure:"retry_backoff_seconds"`
RetryBackoffSeconds
int
`mapstructure:"retry_backoff_seconds"`
// 是否允许 OpenAI 刷新器同步覆盖关联的 Sora 账号 token(默认关闭)
SyncLinkedSoraAccounts
bool
`mapstructure:"sync_linked_sora_accounts"`
}
}
type
PricingConfig
struct
{
type
PricingConfig
struct
{
...
@@ -303,59 +300,6 @@ type ConcurrencyConfig struct {
...
@@ -303,59 +300,6 @@ type ConcurrencyConfig struct {
PingInterval
int
`mapstructure:"ping_interval"`
PingInterval
int
`mapstructure:"ping_interval"`
}
}
// SoraConfig 直连 Sora 配置
type
SoraConfig
struct
{
Client
SoraClientConfig
`mapstructure:"client"`
Storage
SoraStorageConfig
`mapstructure:"storage"`
}
// SoraClientConfig 直连 Sora 客户端配置
type
SoraClientConfig
struct
{
BaseURL
string
`mapstructure:"base_url"`
TimeoutSeconds
int
`mapstructure:"timeout_seconds"`
MaxRetries
int
`mapstructure:"max_retries"`
CloudflareChallengeCooldownSeconds
int
`mapstructure:"cloudflare_challenge_cooldown_seconds"`
PollIntervalSeconds
int
`mapstructure:"poll_interval_seconds"`
MaxPollAttempts
int
`mapstructure:"max_poll_attempts"`
RecentTaskLimit
int
`mapstructure:"recent_task_limit"`
RecentTaskLimitMax
int
`mapstructure:"recent_task_limit_max"`
Debug
bool
`mapstructure:"debug"`
UseOpenAITokenProvider
bool
`mapstructure:"use_openai_token_provider"`
Headers
map
[
string
]
string
`mapstructure:"headers"`
UserAgent
string
`mapstructure:"user_agent"`
DisableTLSFingerprint
bool
`mapstructure:"disable_tls_fingerprint"`
CurlCFFISidecar
SoraCurlCFFISidecarConfig
`mapstructure:"curl_cffi_sidecar"`
}
// SoraCurlCFFISidecarConfig Sora 专用 curl_cffi sidecar 配置
type
SoraCurlCFFISidecarConfig
struct
{
Enabled
bool
`mapstructure:"enabled"`
BaseURL
string
`mapstructure:"base_url"`
Impersonate
string
`mapstructure:"impersonate"`
TimeoutSeconds
int
`mapstructure:"timeout_seconds"`
SessionReuseEnabled
bool
`mapstructure:"session_reuse_enabled"`
SessionTTLSeconds
int
`mapstructure:"session_ttl_seconds"`
}
// SoraStorageConfig 媒体存储配置
type
SoraStorageConfig
struct
{
Type
string
`mapstructure:"type"`
LocalPath
string
`mapstructure:"local_path"`
FallbackToUpstream
bool
`mapstructure:"fallback_to_upstream"`
MaxConcurrentDownloads
int
`mapstructure:"max_concurrent_downloads"`
DownloadTimeoutSeconds
int
`mapstructure:"download_timeout_seconds"`
MaxDownloadBytes
int64
`mapstructure:"max_download_bytes"`
Debug
bool
`mapstructure:"debug"`
Cleanup
SoraStorageCleanupConfig
`mapstructure:"cleanup"`
}
// SoraStorageCleanupConfig 媒体清理配置
type
SoraStorageCleanupConfig
struct
{
Enabled
bool
`mapstructure:"enabled"`
Schedule
string
`mapstructure:"schedule"`
RetentionDays
int
`mapstructure:"retention_days"`
}
// GatewayConfig API网关相关配置
// GatewayConfig API网关相关配置
type
GatewayConfig
struct
{
type
GatewayConfig
struct
{
// 等待上游响应头的超时时间(秒),0表示无超时
// 等待上游响应头的超时时间(秒),0表示无超时
...
@@ -424,24 +368,6 @@ type GatewayConfig struct {
...
@@ -424,24 +368,6 @@ type GatewayConfig struct {
// 是否允许对部分 400 错误触发 failover(默认关闭以避免改变语义)
// 是否允许对部分 400 错误触发 failover(默认关闭以避免改变语义)
FailoverOn400
bool
`mapstructure:"failover_on_400"`
FailoverOn400
bool
`mapstructure:"failover_on_400"`
// Sora 专用配置
// SoraMaxBodySize: Sora 请求体最大字节数(0 表示使用 gateway.max_body_size)
SoraMaxBodySize
int64
`mapstructure:"sora_max_body_size"`
// SoraStreamTimeoutSeconds: Sora 流式请求总超时(秒,0 表示不限制)
SoraStreamTimeoutSeconds
int
`mapstructure:"sora_stream_timeout_seconds"`
// SoraRequestTimeoutSeconds: Sora 非流式请求超时(秒,0 表示不限制)
SoraRequestTimeoutSeconds
int
`mapstructure:"sora_request_timeout_seconds"`
// SoraStreamMode: stream 强制策略(force/error)
SoraStreamMode
string
`mapstructure:"sora_stream_mode"`
// SoraModelFilters: 模型列表过滤配置
SoraModelFilters
SoraModelFiltersConfig
`mapstructure:"sora_model_filters"`
// SoraMediaRequireAPIKey: 是否要求访问 /sora/media 携带 API Key
SoraMediaRequireAPIKey
bool
`mapstructure:"sora_media_require_api_key"`
// SoraMediaSigningKey: /sora/media 临时签名密钥(空表示禁用签名)
SoraMediaSigningKey
string
`mapstructure:"sora_media_signing_key"`
// SoraMediaSignedURLTTLSeconds: 临时签名 URL 有效期(秒,<=0 表示禁用)
SoraMediaSignedURLTTLSeconds
int
`mapstructure:"sora_media_signed_url_ttl_seconds"`
// 账户切换最大次数(遇到上游错误时切换到其他账户的次数上限)
// 账户切换最大次数(遇到上游错误时切换到其他账户的次数上限)
MaxAccountSwitches
int
`mapstructure:"max_account_switches"`
MaxAccountSwitches
int
`mapstructure:"max_account_switches"`
// Gemini 账户切换最大次数(Gemini 平台单独配置,因 API 限制更严格)
// Gemini 账户切换最大次数(Gemini 平台单独配置,因 API 限制更严格)
...
@@ -639,12 +565,6 @@ type GatewayUsageRecordConfig struct {
...
@@ -639,12 +565,6 @@ type GatewayUsageRecordConfig struct {
AutoScaleCooldownSeconds
int
`mapstructure:"auto_scale_cooldown_seconds"`
AutoScaleCooldownSeconds
int
`mapstructure:"auto_scale_cooldown_seconds"`
}
}
// SoraModelFiltersConfig Sora 模型过滤配置
type
SoraModelFiltersConfig
struct
{
// HidePromptEnhance 是否隐藏 prompt-enhance 模型
HidePromptEnhance
bool
`mapstructure:"hide_prompt_enhance"`
}
// TLSFingerprintConfig TLS指纹伪装配置
// TLSFingerprintConfig TLS指纹伪装配置
// 用于模拟 Claude CLI (Node.js) 的 TLS 握手特征,避免被识别为非官方客户端
// 用于模拟 Claude CLI (Node.js) 的 TLS 握手特征,避免被识别为非官方客户端
type
TLSFingerprintConfig
struct
{
type
TLSFingerprintConfig
struct
{
...
@@ -1402,13 +1322,6 @@ func setDefaults() {
...
@@ -1402,13 +1322,6 @@ func setDefaults() {
viper
.
SetDefault
(
"gateway.upstream_response_read_max_bytes"
,
int64
(
8
*
1024
*
1024
))
viper
.
SetDefault
(
"gateway.upstream_response_read_max_bytes"
,
int64
(
8
*
1024
*
1024
))
viper
.
SetDefault
(
"gateway.proxy_probe_response_read_max_bytes"
,
int64
(
1024
*
1024
))
viper
.
SetDefault
(
"gateway.proxy_probe_response_read_max_bytes"
,
int64
(
1024
*
1024
))
viper
.
SetDefault
(
"gateway.gemini_debug_response_headers"
,
false
)
viper
.
SetDefault
(
"gateway.gemini_debug_response_headers"
,
false
)
viper
.
SetDefault
(
"gateway.sora_max_body_size"
,
int64
(
256
*
1024
*
1024
))
viper
.
SetDefault
(
"gateway.sora_stream_timeout_seconds"
,
900
)
viper
.
SetDefault
(
"gateway.sora_request_timeout_seconds"
,
180
)
viper
.
SetDefault
(
"gateway.sora_stream_mode"
,
"force"
)
viper
.
SetDefault
(
"gateway.sora_model_filters.hide_prompt_enhance"
,
true
)
viper
.
SetDefault
(
"gateway.sora_media_require_api_key"
,
true
)
viper
.
SetDefault
(
"gateway.sora_media_signed_url_ttl_seconds"
,
900
)
viper
.
SetDefault
(
"gateway.connection_pool_isolation"
,
ConnectionPoolIsolationAccountProxy
)
viper
.
SetDefault
(
"gateway.connection_pool_isolation"
,
ConnectionPoolIsolationAccountProxy
)
// HTTP 上游连接池配置(针对 5000+ 并发用户优化)
// HTTP 上游连接池配置(针对 5000+ 并发用户优化)
viper
.
SetDefault
(
"gateway.max_idle_conns"
,
2560
)
// 最大空闲连接总数(高并发场景可调大)
viper
.
SetDefault
(
"gateway.max_idle_conns"
,
2560
)
// 最大空闲连接总数(高并发场景可调大)
...
@@ -1465,45 +1378,12 @@ func setDefaults() {
...
@@ -1465,45 +1378,12 @@ func setDefaults() {
viper
.
SetDefault
(
"gateway.tls_fingerprint.enabled"
,
true
)
viper
.
SetDefault
(
"gateway.tls_fingerprint.enabled"
,
true
)
viper
.
SetDefault
(
"concurrency.ping_interval"
,
10
)
viper
.
SetDefault
(
"concurrency.ping_interval"
,
10
)
// Sora 直连配置
viper
.
SetDefault
(
"sora.client.base_url"
,
"https://sora.chatgpt.com/backend"
)
viper
.
SetDefault
(
"sora.client.timeout_seconds"
,
120
)
viper
.
SetDefault
(
"sora.client.max_retries"
,
3
)
viper
.
SetDefault
(
"sora.client.cloudflare_challenge_cooldown_seconds"
,
900
)
viper
.
SetDefault
(
"sora.client.poll_interval_seconds"
,
2
)
viper
.
SetDefault
(
"sora.client.max_poll_attempts"
,
600
)
viper
.
SetDefault
(
"sora.client.recent_task_limit"
,
50
)
viper
.
SetDefault
(
"sora.client.recent_task_limit_max"
,
200
)
viper
.
SetDefault
(
"sora.client.debug"
,
false
)
viper
.
SetDefault
(
"sora.client.use_openai_token_provider"
,
false
)
viper
.
SetDefault
(
"sora.client.headers"
,
map
[
string
]
string
{})
viper
.
SetDefault
(
"sora.client.user_agent"
,
"Sora/1.2026.007 (Android 15; 24122RKC7C; build 2600700)"
)
viper
.
SetDefault
(
"sora.client.disable_tls_fingerprint"
,
false
)
viper
.
SetDefault
(
"sora.client.curl_cffi_sidecar.enabled"
,
true
)
viper
.
SetDefault
(
"sora.client.curl_cffi_sidecar.base_url"
,
"http://sora-curl-cffi-sidecar:8080"
)
viper
.
SetDefault
(
"sora.client.curl_cffi_sidecar.impersonate"
,
"chrome131"
)
viper
.
SetDefault
(
"sora.client.curl_cffi_sidecar.timeout_seconds"
,
60
)
viper
.
SetDefault
(
"sora.client.curl_cffi_sidecar.session_reuse_enabled"
,
true
)
viper
.
SetDefault
(
"sora.client.curl_cffi_sidecar.session_ttl_seconds"
,
3600
)
viper
.
SetDefault
(
"sora.storage.type"
,
"local"
)
viper
.
SetDefault
(
"sora.storage.local_path"
,
""
)
viper
.
SetDefault
(
"sora.storage.fallback_to_upstream"
,
true
)
viper
.
SetDefault
(
"sora.storage.max_concurrent_downloads"
,
4
)
viper
.
SetDefault
(
"sora.storage.download_timeout_seconds"
,
120
)
viper
.
SetDefault
(
"sora.storage.max_download_bytes"
,
int64
(
200
<<
20
))
viper
.
SetDefault
(
"sora.storage.debug"
,
false
)
viper
.
SetDefault
(
"sora.storage.cleanup.enabled"
,
true
)
viper
.
SetDefault
(
"sora.storage.cleanup.retention_days"
,
7
)
viper
.
SetDefault
(
"sora.storage.cleanup.schedule"
,
"0 3 * * *"
)
// TokenRefresh
// TokenRefresh
viper
.
SetDefault
(
"token_refresh.enabled"
,
true
)
viper
.
SetDefault
(
"token_refresh.enabled"
,
true
)
viper
.
SetDefault
(
"token_refresh.check_interval_minutes"
,
5
)
// 每5分钟检查一次
viper
.
SetDefault
(
"token_refresh.check_interval_minutes"
,
5
)
// 每5分钟检查一次
viper
.
SetDefault
(
"token_refresh.refresh_before_expiry_hours"
,
0.5
)
// 提前30分钟刷新(适配Google 1小时token)
viper
.
SetDefault
(
"token_refresh.refresh_before_expiry_hours"
,
0.5
)
// 提前30分钟刷新(适配Google 1小时token)
viper
.
SetDefault
(
"token_refresh.max_retries"
,
3
)
// 最多重试3次
viper
.
SetDefault
(
"token_refresh.max_retries"
,
3
)
// 最多重试3次
viper
.
SetDefault
(
"token_refresh.retry_backoff_seconds"
,
2
)
// 重试退避基础2秒
viper
.
SetDefault
(
"token_refresh.retry_backoff_seconds"
,
2
)
// 重试退避基础2秒
viper
.
SetDefault
(
"token_refresh.sync_linked_sora_accounts"
,
false
)
// 默认不跨平台覆盖 Sora token
// Gemini OAuth - configure via environment variables or config file
// Gemini OAuth - configure via environment variables or config file
// GEMINI_OAUTH_CLIENT_ID and GEMINI_OAUTH_CLIENT_SECRET
// GEMINI_OAUTH_CLIENT_ID and GEMINI_OAUTH_CLIENT_SECRET
...
@@ -1879,86 +1759,6 @@ func (c *Config) Validate() error {
...
@@ -1879,86 +1759,6 @@ func (c *Config) Validate() error {
if
c
.
Gateway
.
ProxyProbeResponseReadMaxBytes
<=
0
{
if
c
.
Gateway
.
ProxyProbeResponseReadMaxBytes
<=
0
{
return
fmt
.
Errorf
(
"gateway.proxy_probe_response_read_max_bytes must be positive"
)
return
fmt
.
Errorf
(
"gateway.proxy_probe_response_read_max_bytes must be positive"
)
}
}
if
c
.
Gateway
.
SoraMaxBodySize
<
0
{
return
fmt
.
Errorf
(
"gateway.sora_max_body_size must be non-negative"
)
}
if
c
.
Gateway
.
SoraStreamTimeoutSeconds
<
0
{
return
fmt
.
Errorf
(
"gateway.sora_stream_timeout_seconds must be non-negative"
)
}
if
c
.
Gateway
.
SoraRequestTimeoutSeconds
<
0
{
return
fmt
.
Errorf
(
"gateway.sora_request_timeout_seconds must be non-negative"
)
}
if
c
.
Gateway
.
SoraMediaSignedURLTTLSeconds
<
0
{
return
fmt
.
Errorf
(
"gateway.sora_media_signed_url_ttl_seconds must be non-negative"
)
}
if
mode
:=
strings
.
TrimSpace
(
strings
.
ToLower
(
c
.
Gateway
.
SoraStreamMode
));
mode
!=
""
{
switch
mode
{
case
"force"
,
"error"
:
default
:
return
fmt
.
Errorf
(
"gateway.sora_stream_mode must be one of: force/error"
)
}
}
if
c
.
Sora
.
Client
.
TimeoutSeconds
<
0
{
return
fmt
.
Errorf
(
"sora.client.timeout_seconds must be non-negative"
)
}
if
c
.
Sora
.
Client
.
MaxRetries
<
0
{
return
fmt
.
Errorf
(
"sora.client.max_retries must be non-negative"
)
}
if
c
.
Sora
.
Client
.
CloudflareChallengeCooldownSeconds
<
0
{
return
fmt
.
Errorf
(
"sora.client.cloudflare_challenge_cooldown_seconds must be non-negative"
)
}
if
c
.
Sora
.
Client
.
PollIntervalSeconds
<
0
{
return
fmt
.
Errorf
(
"sora.client.poll_interval_seconds must be non-negative"
)
}
if
c
.
Sora
.
Client
.
MaxPollAttempts
<
0
{
return
fmt
.
Errorf
(
"sora.client.max_poll_attempts must be non-negative"
)
}
if
c
.
Sora
.
Client
.
RecentTaskLimit
<
0
{
return
fmt
.
Errorf
(
"sora.client.recent_task_limit must be non-negative"
)
}
if
c
.
Sora
.
Client
.
RecentTaskLimitMax
<
0
{
return
fmt
.
Errorf
(
"sora.client.recent_task_limit_max must be non-negative"
)
}
if
c
.
Sora
.
Client
.
RecentTaskLimitMax
>
0
&&
c
.
Sora
.
Client
.
RecentTaskLimit
>
0
&&
c
.
Sora
.
Client
.
RecentTaskLimitMax
<
c
.
Sora
.
Client
.
RecentTaskLimit
{
c
.
Sora
.
Client
.
RecentTaskLimitMax
=
c
.
Sora
.
Client
.
RecentTaskLimit
}
if
c
.
Sora
.
Client
.
CurlCFFISidecar
.
TimeoutSeconds
<
0
{
return
fmt
.
Errorf
(
"sora.client.curl_cffi_sidecar.timeout_seconds must be non-negative"
)
}
if
c
.
Sora
.
Client
.
CurlCFFISidecar
.
SessionTTLSeconds
<
0
{
return
fmt
.
Errorf
(
"sora.client.curl_cffi_sidecar.session_ttl_seconds must be non-negative"
)
}
if
!
c
.
Sora
.
Client
.
CurlCFFISidecar
.
Enabled
{
return
fmt
.
Errorf
(
"sora.client.curl_cffi_sidecar.enabled must be true"
)
}
if
strings
.
TrimSpace
(
c
.
Sora
.
Client
.
CurlCFFISidecar
.
BaseURL
)
==
""
{
return
fmt
.
Errorf
(
"sora.client.curl_cffi_sidecar.base_url is required"
)
}
if
c
.
Sora
.
Storage
.
MaxConcurrentDownloads
<
0
{
return
fmt
.
Errorf
(
"sora.storage.max_concurrent_downloads must be non-negative"
)
}
if
c
.
Sora
.
Storage
.
DownloadTimeoutSeconds
<
0
{
return
fmt
.
Errorf
(
"sora.storage.download_timeout_seconds must be non-negative"
)
}
if
c
.
Sora
.
Storage
.
MaxDownloadBytes
<
0
{
return
fmt
.
Errorf
(
"sora.storage.max_download_bytes must be non-negative"
)
}
if
c
.
Sora
.
Storage
.
Cleanup
.
Enabled
{
if
c
.
Sora
.
Storage
.
Cleanup
.
RetentionDays
<=
0
{
return
fmt
.
Errorf
(
"sora.storage.cleanup.retention_days must be positive"
)
}
if
strings
.
TrimSpace
(
c
.
Sora
.
Storage
.
Cleanup
.
Schedule
)
==
""
{
return
fmt
.
Errorf
(
"sora.storage.cleanup.schedule is required when cleanup is enabled"
)
}
}
else
{
if
c
.
Sora
.
Storage
.
Cleanup
.
RetentionDays
<
0
{
return
fmt
.
Errorf
(
"sora.storage.cleanup.retention_days must be non-negative"
)
}
}
if
storageType
:=
strings
.
TrimSpace
(
strings
.
ToLower
(
c
.
Sora
.
Storage
.
Type
));
storageType
!=
""
&&
storageType
!=
"local"
{
return
fmt
.
Errorf
(
"sora.storage.type must be 'local'"
)
}
if
strings
.
TrimSpace
(
c
.
Gateway
.
ConnectionPoolIsolation
)
!=
""
{
if
strings
.
TrimSpace
(
c
.
Gateway
.
ConnectionPoolIsolation
)
!=
""
{
switch
c
.
Gateway
.
ConnectionPoolIsolation
{
switch
c
.
Gateway
.
ConnectionPoolIsolation
{
case
ConnectionPoolIsolationProxy
,
ConnectionPoolIsolationAccount
,
ConnectionPoolIsolationAccountProxy
:
case
ConnectionPoolIsolationProxy
,
ConnectionPoolIsolationAccount
,
ConnectionPoolIsolationAccountProxy
:
...
...
backend/internal/config/config_test.go
View file @
d757df8a
...
@@ -1554,94 +1554,6 @@ func TestValidateConfig_LogRequiredAndRotationBounds(t *testing.T) {
...
@@ -1554,94 +1554,6 @@ func TestValidateConfig_LogRequiredAndRotationBounds(t *testing.T) {
}
}
}
}
func
TestSoraCurlCFFISidecarDefaults
(
t
*
testing
.
T
)
{
resetViperWithJWTSecret
(
t
)
cfg
,
err
:=
Load
()
if
err
!=
nil
{
t
.
Fatalf
(
"Load() error: %v"
,
err
)
}
if
!
cfg
.
Sora
.
Client
.
CurlCFFISidecar
.
Enabled
{
t
.
Fatalf
(
"Sora curl_cffi sidecar should be enabled by default"
)
}
if
cfg
.
Sora
.
Client
.
CloudflareChallengeCooldownSeconds
<=
0
{
t
.
Fatalf
(
"Sora cloudflare challenge cooldown should be positive by default"
)
}
if
cfg
.
Sora
.
Client
.
CurlCFFISidecar
.
BaseURL
==
""
{
t
.
Fatalf
(
"Sora curl_cffi sidecar base_url should not be empty by default"
)
}
if
cfg
.
Sora
.
Client
.
CurlCFFISidecar
.
Impersonate
==
""
{
t
.
Fatalf
(
"Sora curl_cffi sidecar impersonate should not be empty by default"
)
}
if
!
cfg
.
Sora
.
Client
.
CurlCFFISidecar
.
SessionReuseEnabled
{
t
.
Fatalf
(
"Sora curl_cffi sidecar session reuse should be enabled by default"
)
}
if
cfg
.
Sora
.
Client
.
CurlCFFISidecar
.
SessionTTLSeconds
<=
0
{
t
.
Fatalf
(
"Sora curl_cffi sidecar session ttl should be positive by default"
)
}
}
func
TestValidateSoraCurlCFFISidecarRequired
(
t
*
testing
.
T
)
{
resetViperWithJWTSecret
(
t
)
cfg
,
err
:=
Load
()
if
err
!=
nil
{
t
.
Fatalf
(
"Load() error: %v"
,
err
)
}
cfg
.
Sora
.
Client
.
CurlCFFISidecar
.
Enabled
=
false
err
=
cfg
.
Validate
()
if
err
==
nil
||
!
strings
.
Contains
(
err
.
Error
(),
"sora.client.curl_cffi_sidecar.enabled must be true"
)
{
t
.
Fatalf
(
"Validate() error = %v, want sidecar enabled error"
,
err
)
}
}
func
TestValidateSoraCurlCFFISidecarBaseURLRequired
(
t
*
testing
.
T
)
{
resetViperWithJWTSecret
(
t
)
cfg
,
err
:=
Load
()
if
err
!=
nil
{
t
.
Fatalf
(
"Load() error: %v"
,
err
)
}
cfg
.
Sora
.
Client
.
CurlCFFISidecar
.
BaseURL
=
" "
err
=
cfg
.
Validate
()
if
err
==
nil
||
!
strings
.
Contains
(
err
.
Error
(),
"sora.client.curl_cffi_sidecar.base_url is required"
)
{
t
.
Fatalf
(
"Validate() error = %v, want sidecar base_url required error"
,
err
)
}
}
func
TestValidateSoraCurlCFFISidecarSessionTTLNonNegative
(
t
*
testing
.
T
)
{
resetViperWithJWTSecret
(
t
)
cfg
,
err
:=
Load
()
if
err
!=
nil
{
t
.
Fatalf
(
"Load() error: %v"
,
err
)
}
cfg
.
Sora
.
Client
.
CurlCFFISidecar
.
SessionTTLSeconds
=
-
1
err
=
cfg
.
Validate
()
if
err
==
nil
||
!
strings
.
Contains
(
err
.
Error
(),
"sora.client.curl_cffi_sidecar.session_ttl_seconds must be non-negative"
)
{
t
.
Fatalf
(
"Validate() error = %v, want sidecar session ttl error"
,
err
)
}
}
func
TestValidateSoraCloudflareChallengeCooldownNonNegative
(
t
*
testing
.
T
)
{
resetViperWithJWTSecret
(
t
)
cfg
,
err
:=
Load
()
if
err
!=
nil
{
t
.
Fatalf
(
"Load() error: %v"
,
err
)
}
cfg
.
Sora
.
Client
.
CloudflareChallengeCooldownSeconds
=
-
1
err
=
cfg
.
Validate
()
if
err
==
nil
||
!
strings
.
Contains
(
err
.
Error
(),
"sora.client.cloudflare_challenge_cooldown_seconds must be non-negative"
)
{
t
.
Fatalf
(
"Validate() error = %v, want cloudflare cooldown error"
,
err
)
}
}
func
TestLoad_DefaultGatewayUsageRecordConfig
(
t
*
testing
.
T
)
{
func
TestLoad_DefaultGatewayUsageRecordConfig
(
t
*
testing
.
T
)
{
resetViperWithJWTSecret
(
t
)
resetViperWithJWTSecret
(
t
)
cfg
,
err
:=
Load
()
cfg
,
err
:=
Load
()
...
...
backend/internal/domain/constants.go
View file @
d757df8a
...
@@ -22,7 +22,6 @@ const (
...
@@ -22,7 +22,6 @@ const (
PlatformOpenAI
=
"openai"
PlatformOpenAI
=
"openai"
PlatformGemini
=
"gemini"
PlatformGemini
=
"gemini"
PlatformAntigravity
=
"antigravity"
PlatformAntigravity
=
"antigravity"
PlatformSora
=
"sora"
)
)
// Account type constants
// Account type constants
...
...
backend/internal/handler/admin/account_data.go
View file @
d757df8a
...
@@ -567,15 +567,15 @@ func defaultProxyName(name string) string {
...
@@ -567,15 +567,15 @@ func defaultProxyName(name string) string {
// enrichCredentialsFromIDToken performs best-effort extraction of user info fields
// enrichCredentialsFromIDToken performs best-effort extraction of user info fields
// (email, plan_type, chatgpt_account_id, etc.) from id_token in credentials.
// (email, plan_type, chatgpt_account_id, etc.) from id_token in credentials.
// Only applies to OpenAI
/Sora
OAuth accounts. Skips expired token errors silently.
// Only applies to OpenAI OAuth accounts. Skips expired token errors silently.
// Existing credential values are never overwritten — only missing fields are filled.
// Existing credential values are never overwritten — only missing fields are filled.
func
enrichCredentialsFromIDToken
(
item
*
DataAccount
)
{
func
enrichCredentialsFromIDToken
(
item
*
DataAccount
)
{
if
item
.
Credentials
==
nil
{
if
item
.
Credentials
==
nil
{
return
return
}
}
// Only enrich OpenAI
/Sora
OAuth accounts
// Only enrich OpenAI OAuth accounts
platform
:=
strings
.
ToLower
(
strings
.
TrimSpace
(
item
.
Platform
))
platform
:=
strings
.
ToLower
(
strings
.
TrimSpace
(
item
.
Platform
))
if
platform
!=
service
.
PlatformOpenAI
&&
platform
!=
service
.
PlatformSora
{
if
platform
!=
service
.
PlatformOpenAI
{
return
return
}
}
if
strings
.
ToLower
(
strings
.
TrimSpace
(
item
.
Type
))
!=
service
.
AccountTypeOAuth
{
if
strings
.
ToLower
(
strings
.
TrimSpace
(
item
.
Type
))
!=
service
.
AccountTypeOAuth
{
...
...
backend/internal/handler/admin/account_handler.go
View file @
d757df8a
...
@@ -1875,12 +1875,6 @@ func (h *AccountHandler) GetAvailableModels(c *gin.Context) {
...
@@ -1875,12 +1875,6 @@ func (h *AccountHandler) GetAvailableModels(c *gin.Context) {
return
return
}
}
// Handle Sora accounts
if
account
.
Platform
==
service
.
PlatformSora
{
response
.
Success
(
c
,
service
.
DefaultSoraModels
(
nil
))
return
}
// Handle Claude/Anthropic accounts
// Handle Claude/Anthropic accounts
// For OAuth and Setup-Token accounts: return default models
// For OAuth and Setup-Token accounts: return default models
if
account
.
IsOAuth
()
{
if
account
.
IsOAuth
()
{
...
...
backend/internal/handler/admin/admin_service_stub_test.go
View file @
d757df8a
...
@@ -380,7 +380,6 @@ func (s *stubAdminService) CheckProxyQuality(ctx context.Context, id int64) (*se
...
@@ -380,7 +380,6 @@ func (s *stubAdminService) CheckProxyQuality(ctx context.Context, id int64) (*se
{
Target
:
"openai"
,
Status
:
"pass"
,
HTTPStatus
:
401
},
{
Target
:
"openai"
,
Status
:
"pass"
,
HTTPStatus
:
401
},
{
Target
:
"anthropic"
,
Status
:
"pass"
,
HTTPStatus
:
401
},
{
Target
:
"anthropic"
,
Status
:
"pass"
,
HTTPStatus
:
401
},
{
Target
:
"gemini"
,
Status
:
"pass"
,
HTTPStatus
:
200
},
{
Target
:
"gemini"
,
Status
:
"pass"
,
HTTPStatus
:
200
},
{
Target
:
"sora"
,
Status
:
"pass"
,
HTTPStatus
:
401
},
},
},
},
nil
},
nil
}
}
...
...
backend/internal/handler/admin/group_handler.go
View file @
d757df8a
...
@@ -84,7 +84,7 @@ func NewGroupHandler(adminService service.AdminService, dashboardService *servic
...
@@ -84,7 +84,7 @@ func NewGroupHandler(adminService service.AdminService, dashboardService *servic
type
CreateGroupRequest
struct
{
type
CreateGroupRequest
struct
{
Name
string
`json:"name" binding:"required"`
Name
string
`json:"name" binding:"required"`
Description
string
`json:"description"`
Description
string
`json:"description"`
Platform
string
`json:"platform" binding:"omitempty,oneof=anthropic openai gemini antigravity
sora
"`
Platform
string
`json:"platform" binding:"omitempty,oneof=anthropic openai gemini antigravity"`
RateMultiplier
float64
`json:"rate_multiplier"`
RateMultiplier
float64
`json:"rate_multiplier"`
IsExclusive
bool
`json:"is_exclusive"`
IsExclusive
bool
`json:"is_exclusive"`
SubscriptionType
string
`json:"subscription_type" binding:"omitempty,oneof=standard subscription"`
SubscriptionType
string
`json:"subscription_type" binding:"omitempty,oneof=standard subscription"`
...
@@ -95,10 +95,6 @@ type CreateGroupRequest struct {
...
@@ -95,10 +95,6 @@ type CreateGroupRequest struct {
ImagePrice1K
*
float64
`json:"image_price_1k"`
ImagePrice1K
*
float64
`json:"image_price_1k"`
ImagePrice2K
*
float64
`json:"image_price_2k"`
ImagePrice2K
*
float64
`json:"image_price_2k"`
ImagePrice4K
*
float64
`json:"image_price_4k"`
ImagePrice4K
*
float64
`json:"image_price_4k"`
SoraImagePrice360
*
float64
`json:"sora_image_price_360"`
SoraImagePrice540
*
float64
`json:"sora_image_price_540"`
SoraVideoPricePerRequest
*
float64
`json:"sora_video_price_per_request"`
SoraVideoPricePerRequestHD
*
float64
`json:"sora_video_price_per_request_hd"`
ClaudeCodeOnly
bool
`json:"claude_code_only"`
ClaudeCodeOnly
bool
`json:"claude_code_only"`
FallbackGroupID
*
int64
`json:"fallback_group_id"`
FallbackGroupID
*
int64
`json:"fallback_group_id"`
FallbackGroupIDOnInvalidRequest
*
int64
`json:"fallback_group_id_on_invalid_request"`
FallbackGroupIDOnInvalidRequest
*
int64
`json:"fallback_group_id_on_invalid_request"`
...
@@ -108,8 +104,6 @@ type CreateGroupRequest struct {
...
@@ -108,8 +104,6 @@ type CreateGroupRequest struct {
MCPXMLInject
*
bool
`json:"mcp_xml_inject"`
MCPXMLInject
*
bool
`json:"mcp_xml_inject"`
// 支持的模型系列(仅 antigravity 平台使用)
// 支持的模型系列(仅 antigravity 平台使用)
SupportedModelScopes
[]
string
`json:"supported_model_scopes"`
SupportedModelScopes
[]
string
`json:"supported_model_scopes"`
// Sora 存储配额
SoraStorageQuotaBytes
int64
`json:"sora_storage_quota_bytes"`
// OpenAI Messages 调度配置(仅 openai 平台使用)
// OpenAI Messages 调度配置(仅 openai 平台使用)
AllowMessagesDispatch
bool
`json:"allow_messages_dispatch"`
AllowMessagesDispatch
bool
`json:"allow_messages_dispatch"`
RequireOAuthOnly
bool
`json:"require_oauth_only"`
RequireOAuthOnly
bool
`json:"require_oauth_only"`
...
@@ -123,7 +117,7 @@ type CreateGroupRequest struct {
...
@@ -123,7 +117,7 @@ type CreateGroupRequest struct {
type
UpdateGroupRequest
struct
{
type
UpdateGroupRequest
struct
{
Name
string
`json:"name"`
Name
string
`json:"name"`
Description
string
`json:"description"`
Description
string
`json:"description"`
Platform
string
`json:"platform" binding:"omitempty,oneof=anthropic openai gemini antigravity
sora
"`
Platform
string
`json:"platform" binding:"omitempty,oneof=anthropic openai gemini antigravity"`
RateMultiplier
*
float64
`json:"rate_multiplier"`
RateMultiplier
*
float64
`json:"rate_multiplier"`
IsExclusive
*
bool
`json:"is_exclusive"`
IsExclusive
*
bool
`json:"is_exclusive"`
Status
string
`json:"status" binding:"omitempty,oneof=active inactive"`
Status
string
`json:"status" binding:"omitempty,oneof=active inactive"`
...
@@ -135,10 +129,6 @@ type UpdateGroupRequest struct {
...
@@ -135,10 +129,6 @@ type UpdateGroupRequest struct {
ImagePrice1K
*
float64
`json:"image_price_1k"`
ImagePrice1K
*
float64
`json:"image_price_1k"`
ImagePrice2K
*
float64
`json:"image_price_2k"`
ImagePrice2K
*
float64
`json:"image_price_2k"`
ImagePrice4K
*
float64
`json:"image_price_4k"`
ImagePrice4K
*
float64
`json:"image_price_4k"`
SoraImagePrice360
*
float64
`json:"sora_image_price_360"`
SoraImagePrice540
*
float64
`json:"sora_image_price_540"`
SoraVideoPricePerRequest
*
float64
`json:"sora_video_price_per_request"`
SoraVideoPricePerRequestHD
*
float64
`json:"sora_video_price_per_request_hd"`
ClaudeCodeOnly
*
bool
`json:"claude_code_only"`
ClaudeCodeOnly
*
bool
`json:"claude_code_only"`
FallbackGroupID
*
int64
`json:"fallback_group_id"`
FallbackGroupID
*
int64
`json:"fallback_group_id"`
FallbackGroupIDOnInvalidRequest
*
int64
`json:"fallback_group_id_on_invalid_request"`
FallbackGroupIDOnInvalidRequest
*
int64
`json:"fallback_group_id_on_invalid_request"`
...
@@ -148,8 +138,6 @@ type UpdateGroupRequest struct {
...
@@ -148,8 +138,6 @@ type UpdateGroupRequest struct {
MCPXMLInject
*
bool
`json:"mcp_xml_inject"`
MCPXMLInject
*
bool
`json:"mcp_xml_inject"`
// 支持的模型系列(仅 antigravity 平台使用)
// 支持的模型系列(仅 antigravity 平台使用)
SupportedModelScopes
*
[]
string
`json:"supported_model_scopes"`
SupportedModelScopes
*
[]
string
`json:"supported_model_scopes"`
// Sora 存储配额
SoraStorageQuotaBytes
*
int64
`json:"sora_storage_quota_bytes"`
// OpenAI Messages 调度配置(仅 openai 平台使用)
// OpenAI Messages 调度配置(仅 openai 平台使用)
AllowMessagesDispatch
*
bool
`json:"allow_messages_dispatch"`
AllowMessagesDispatch
*
bool
`json:"allow_messages_dispatch"`
RequireOAuthOnly
*
bool
`json:"require_oauth_only"`
RequireOAuthOnly
*
bool
`json:"require_oauth_only"`
...
@@ -258,10 +246,6 @@ func (h *GroupHandler) Create(c *gin.Context) {
...
@@ -258,10 +246,6 @@ func (h *GroupHandler) Create(c *gin.Context) {
ImagePrice1K
:
req
.
ImagePrice1K
,
ImagePrice1K
:
req
.
ImagePrice1K
,
ImagePrice2K
:
req
.
ImagePrice2K
,
ImagePrice2K
:
req
.
ImagePrice2K
,
ImagePrice4K
:
req
.
ImagePrice4K
,
ImagePrice4K
:
req
.
ImagePrice4K
,
SoraImagePrice360
:
req
.
SoraImagePrice360
,
SoraImagePrice540
:
req
.
SoraImagePrice540
,
SoraVideoPricePerRequest
:
req
.
SoraVideoPricePerRequest
,
SoraVideoPricePerRequestHD
:
req
.
SoraVideoPricePerRequestHD
,
ClaudeCodeOnly
:
req
.
ClaudeCodeOnly
,
ClaudeCodeOnly
:
req
.
ClaudeCodeOnly
,
FallbackGroupID
:
req
.
FallbackGroupID
,
FallbackGroupID
:
req
.
FallbackGroupID
,
FallbackGroupIDOnInvalidRequest
:
req
.
FallbackGroupIDOnInvalidRequest
,
FallbackGroupIDOnInvalidRequest
:
req
.
FallbackGroupIDOnInvalidRequest
,
...
@@ -269,7 +253,6 @@ func (h *GroupHandler) Create(c *gin.Context) {
...
@@ -269,7 +253,6 @@ func (h *GroupHandler) Create(c *gin.Context) {
ModelRoutingEnabled
:
req
.
ModelRoutingEnabled
,
ModelRoutingEnabled
:
req
.
ModelRoutingEnabled
,
MCPXMLInject
:
req
.
MCPXMLInject
,
MCPXMLInject
:
req
.
MCPXMLInject
,
SupportedModelScopes
:
req
.
SupportedModelScopes
,
SupportedModelScopes
:
req
.
SupportedModelScopes
,
SoraStorageQuotaBytes
:
req
.
SoraStorageQuotaBytes
,
AllowMessagesDispatch
:
req
.
AllowMessagesDispatch
,
AllowMessagesDispatch
:
req
.
AllowMessagesDispatch
,
RequireOAuthOnly
:
req
.
RequireOAuthOnly
,
RequireOAuthOnly
:
req
.
RequireOAuthOnly
,
RequirePrivacySet
:
req
.
RequirePrivacySet
,
RequirePrivacySet
:
req
.
RequirePrivacySet
,
...
@@ -313,10 +296,6 @@ func (h *GroupHandler) Update(c *gin.Context) {
...
@@ -313,10 +296,6 @@ func (h *GroupHandler) Update(c *gin.Context) {
ImagePrice1K
:
req
.
ImagePrice1K
,
ImagePrice1K
:
req
.
ImagePrice1K
,
ImagePrice2K
:
req
.
ImagePrice2K
,
ImagePrice2K
:
req
.
ImagePrice2K
,
ImagePrice4K
:
req
.
ImagePrice4K
,
ImagePrice4K
:
req
.
ImagePrice4K
,
SoraImagePrice360
:
req
.
SoraImagePrice360
,
SoraImagePrice540
:
req
.
SoraImagePrice540
,
SoraVideoPricePerRequest
:
req
.
SoraVideoPricePerRequest
,
SoraVideoPricePerRequestHD
:
req
.
SoraVideoPricePerRequestHD
,
ClaudeCodeOnly
:
req
.
ClaudeCodeOnly
,
ClaudeCodeOnly
:
req
.
ClaudeCodeOnly
,
FallbackGroupID
:
req
.
FallbackGroupID
,
FallbackGroupID
:
req
.
FallbackGroupID
,
FallbackGroupIDOnInvalidRequest
:
req
.
FallbackGroupIDOnInvalidRequest
,
FallbackGroupIDOnInvalidRequest
:
req
.
FallbackGroupIDOnInvalidRequest
,
...
@@ -324,7 +303,6 @@ func (h *GroupHandler) Update(c *gin.Context) {
...
@@ -324,7 +303,6 @@ func (h *GroupHandler) Update(c *gin.Context) {
ModelRoutingEnabled
:
req
.
ModelRoutingEnabled
,
ModelRoutingEnabled
:
req
.
ModelRoutingEnabled
,
MCPXMLInject
:
req
.
MCPXMLInject
,
MCPXMLInject
:
req
.
MCPXMLInject
,
SupportedModelScopes
:
req
.
SupportedModelScopes
,
SupportedModelScopes
:
req
.
SupportedModelScopes
,
SoraStorageQuotaBytes
:
req
.
SoraStorageQuotaBytes
,
AllowMessagesDispatch
:
req
.
AllowMessagesDispatch
,
AllowMessagesDispatch
:
req
.
AllowMessagesDispatch
,
RequireOAuthOnly
:
req
.
RequireOAuthOnly
,
RequireOAuthOnly
:
req
.
RequireOAuthOnly
,
RequirePrivacySet
:
req
.
RequirePrivacySet
,
RequirePrivacySet
:
req
.
RequirePrivacySet
,
...
...
backend/internal/handler/admin/openai_oauth_handler.go
View file @
d757df8a
...
@@ -19,9 +19,6 @@ type OpenAIOAuthHandler struct {
...
@@ -19,9 +19,6 @@ type OpenAIOAuthHandler struct {
}
}
func
oauthPlatformFromPath
(
c
*
gin
.
Context
)
string
{
func
oauthPlatformFromPath
(
c
*
gin
.
Context
)
string
{
if
strings
.
Contains
(
c
.
FullPath
(),
"/admin/sora/"
)
{
return
service
.
PlatformSora
}
return
service
.
PlatformOpenAI
return
service
.
PlatformOpenAI
}
}
...
@@ -105,7 +102,6 @@ type OpenAIRefreshTokenRequest struct {
...
@@ -105,7 +102,6 @@ type OpenAIRefreshTokenRequest struct {
// RefreshToken refreshes an OpenAI OAuth token
// RefreshToken refreshes an OpenAI OAuth token
// POST /api/v1/admin/openai/refresh-token
// POST /api/v1/admin/openai/refresh-token
// POST /api/v1/admin/sora/rt2at
func
(
h
*
OpenAIOAuthHandler
)
RefreshToken
(
c
*
gin
.
Context
)
{
func
(
h
*
OpenAIOAuthHandler
)
RefreshToken
(
c
*
gin
.
Context
)
{
var
req
OpenAIRefreshTokenRequest
var
req
OpenAIRefreshTokenRequest
if
err
:=
c
.
ShouldBindJSON
(
&
req
);
err
!=
nil
{
if
err
:=
c
.
ShouldBindJSON
(
&
req
);
err
!=
nil
{
...
@@ -145,39 +141,8 @@ func (h *OpenAIOAuthHandler) RefreshToken(c *gin.Context) {
...
@@ -145,39 +141,8 @@ func (h *OpenAIOAuthHandler) RefreshToken(c *gin.Context) {
response
.
Success
(
c
,
tokenInfo
)
response
.
Success
(
c
,
tokenInfo
)
}
}
// ExchangeSoraSessionToken exchanges Sora session token to access token
// RefreshAccountToken refreshes token for a specific OpenAI account
// POST /api/v1/admin/sora/st2at
func
(
h
*
OpenAIOAuthHandler
)
ExchangeSoraSessionToken
(
c
*
gin
.
Context
)
{
var
req
struct
{
SessionToken
string
`json:"session_token"`
ST
string
`json:"st"`
ProxyID
*
int64
`json:"proxy_id"`
}
if
err
:=
c
.
ShouldBindJSON
(
&
req
);
err
!=
nil
{
response
.
BadRequest
(
c
,
"Invalid request: "
+
err
.
Error
())
return
}
sessionToken
:=
strings
.
TrimSpace
(
req
.
SessionToken
)
if
sessionToken
==
""
{
sessionToken
=
strings
.
TrimSpace
(
req
.
ST
)
}
if
sessionToken
==
""
{
response
.
BadRequest
(
c
,
"session_token is required"
)
return
}
tokenInfo
,
err
:=
h
.
openaiOAuthService
.
ExchangeSoraSessionToken
(
c
.
Request
.
Context
(),
sessionToken
,
req
.
ProxyID
)
if
err
!=
nil
{
response
.
ErrorFrom
(
c
,
err
)
return
}
response
.
Success
(
c
,
tokenInfo
)
}
// RefreshAccountToken refreshes token for a specific OpenAI/Sora account
// POST /api/v1/admin/openai/accounts/:id/refresh
// POST /api/v1/admin/openai/accounts/:id/refresh
// POST /api/v1/admin/sora/accounts/:id/refresh
func
(
h
*
OpenAIOAuthHandler
)
RefreshAccountToken
(
c
*
gin
.
Context
)
{
func
(
h
*
OpenAIOAuthHandler
)
RefreshAccountToken
(
c
*
gin
.
Context
)
{
accountID
,
err
:=
strconv
.
ParseInt
(
c
.
Param
(
"id"
),
10
,
64
)
accountID
,
err
:=
strconv
.
ParseInt
(
c
.
Param
(
"id"
),
10
,
64
)
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -232,9 +197,8 @@ func (h *OpenAIOAuthHandler) RefreshAccountToken(c *gin.Context) {
...
@@ -232,9 +197,8 @@ func (h *OpenAIOAuthHandler) RefreshAccountToken(c *gin.Context) {
response
.
Success
(
c
,
dto
.
AccountFromService
(
updatedAccount
))
response
.
Success
(
c
,
dto
.
AccountFromService
(
updatedAccount
))
}
}
// CreateAccountFromOAuth creates a new OpenAI
/Sora
OAuth account from token info
// CreateAccountFromOAuth creates a new OpenAI OAuth account from token info
// POST /api/v1/admin/openai/create-from-oauth
// POST /api/v1/admin/openai/create-from-oauth
// POST /api/v1/admin/sora/create-from-oauth
func
(
h
*
OpenAIOAuthHandler
)
CreateAccountFromOAuth
(
c
*
gin
.
Context
)
{
func
(
h
*
OpenAIOAuthHandler
)
CreateAccountFromOAuth
(
c
*
gin
.
Context
)
{
var
req
struct
{
var
req
struct
{
SessionID
string
`json:"session_id" binding:"required"`
SessionID
string
`json:"session_id" binding:"required"`
...
@@ -276,11 +240,7 @@ func (h *OpenAIOAuthHandler) CreateAccountFromOAuth(c *gin.Context) {
...
@@ -276,11 +240,7 @@ func (h *OpenAIOAuthHandler) CreateAccountFromOAuth(c *gin.Context) {
name
=
tokenInfo
.
Email
name
=
tokenInfo
.
Email
}
}
if
name
==
""
{
if
name
==
""
{
if
platform
==
service
.
PlatformSora
{
name
=
"OpenAI OAuth Account"
name
=
"Sora OAuth Account"
}
else
{
name
=
"OpenAI OAuth Account"
}
}
}
// Create account
// Create account
...
...
backend/internal/handler/admin/setting_handler.go
View file @
d757df8a
...
@@ -41,17 +41,15 @@ type SettingHandler struct {
...
@@ -41,17 +41,15 @@ type SettingHandler struct {
emailService
*
service
.
EmailService
emailService
*
service
.
EmailService
turnstileService
*
service
.
TurnstileService
turnstileService
*
service
.
TurnstileService
opsService
*
service
.
OpsService
opsService
*
service
.
OpsService
soraS3Storage
*
service
.
SoraS3Storage
}
}
// NewSettingHandler 创建系统设置处理器
// NewSettingHandler 创建系统设置处理器
func
NewSettingHandler
(
settingService
*
service
.
SettingService
,
emailService
*
service
.
EmailService
,
turnstileService
*
service
.
TurnstileService
,
opsService
*
service
.
OpsService
,
soraS3Storage
*
service
.
SoraS3Storage
)
*
SettingHandler
{
func
NewSettingHandler
(
settingService
*
service
.
SettingService
,
emailService
*
service
.
EmailService
,
turnstileService
*
service
.
TurnstileService
,
opsService
*
service
.
OpsService
)
*
SettingHandler
{
return
&
SettingHandler
{
return
&
SettingHandler
{
settingService
:
settingService
,
settingService
:
settingService
,
emailService
:
emailService
,
emailService
:
emailService
,
turnstileService
:
turnstileService
,
turnstileService
:
turnstileService
,
opsService
:
opsService
,
opsService
:
opsService
,
soraS3Storage
:
soraS3Storage
,
}
}
}
}
...
@@ -108,7 +106,6 @@ func (h *SettingHandler) GetSettings(c *gin.Context) {
...
@@ -108,7 +106,6 @@ func (h *SettingHandler) GetSettings(c *gin.Context) {
HideCcsImportButton
:
settings
.
HideCcsImportButton
,
HideCcsImportButton
:
settings
.
HideCcsImportButton
,
PurchaseSubscriptionEnabled
:
settings
.
PurchaseSubscriptionEnabled
,
PurchaseSubscriptionEnabled
:
settings
.
PurchaseSubscriptionEnabled
,
PurchaseSubscriptionURL
:
settings
.
PurchaseSubscriptionURL
,
PurchaseSubscriptionURL
:
settings
.
PurchaseSubscriptionURL
,
SoraClientEnabled
:
settings
.
SoraClientEnabled
,
CustomMenuItems
:
dto
.
ParseCustomMenuItems
(
settings
.
CustomMenuItems
),
CustomMenuItems
:
dto
.
ParseCustomMenuItems
(
settings
.
CustomMenuItems
),
CustomEndpoints
:
dto
.
ParseCustomEndpoints
(
settings
.
CustomEndpoints
),
CustomEndpoints
:
dto
.
ParseCustomEndpoints
(
settings
.
CustomEndpoints
),
DefaultConcurrency
:
settings
.
DefaultConcurrency
,
DefaultConcurrency
:
settings
.
DefaultConcurrency
,
...
@@ -177,7 +174,6 @@ type UpdateSettingsRequest struct {
...
@@ -177,7 +174,6 @@ type UpdateSettingsRequest struct {
HideCcsImportButton
bool
`json:"hide_ccs_import_button"`
HideCcsImportButton
bool
`json:"hide_ccs_import_button"`
PurchaseSubscriptionEnabled
*
bool
`json:"purchase_subscription_enabled"`
PurchaseSubscriptionEnabled
*
bool
`json:"purchase_subscription_enabled"`
PurchaseSubscriptionURL
*
string
`json:"purchase_subscription_url"`
PurchaseSubscriptionURL
*
string
`json:"purchase_subscription_url"`
SoraClientEnabled
bool
`json:"sora_client_enabled"`
CustomMenuItems
*
[]
dto
.
CustomMenuItem
`json:"custom_menu_items"`
CustomMenuItems
*
[]
dto
.
CustomMenuItem
`json:"custom_menu_items"`
CustomEndpoints
*
[]
dto
.
CustomEndpoint
`json:"custom_endpoints"`
CustomEndpoints
*
[]
dto
.
CustomEndpoint
`json:"custom_endpoints"`
...
@@ -566,7 +562,6 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
...
@@ -566,7 +562,6 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
HideCcsImportButton
:
req
.
HideCcsImportButton
,
HideCcsImportButton
:
req
.
HideCcsImportButton
,
PurchaseSubscriptionEnabled
:
purchaseEnabled
,
PurchaseSubscriptionEnabled
:
purchaseEnabled
,
PurchaseSubscriptionURL
:
purchaseURL
,
PurchaseSubscriptionURL
:
purchaseURL
,
SoraClientEnabled
:
req
.
SoraClientEnabled
,
CustomMenuItems
:
customMenuJSON
,
CustomMenuItems
:
customMenuJSON
,
CustomEndpoints
:
customEndpointsJSON
,
CustomEndpoints
:
customEndpointsJSON
,
DefaultConcurrency
:
req
.
DefaultConcurrency
,
DefaultConcurrency
:
req
.
DefaultConcurrency
,
...
@@ -676,7 +671,6 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
...
@@ -676,7 +671,6 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
HideCcsImportButton
:
updatedSettings
.
HideCcsImportButton
,
HideCcsImportButton
:
updatedSettings
.
HideCcsImportButton
,
PurchaseSubscriptionEnabled
:
updatedSettings
.
PurchaseSubscriptionEnabled
,
PurchaseSubscriptionEnabled
:
updatedSettings
.
PurchaseSubscriptionEnabled
,
PurchaseSubscriptionURL
:
updatedSettings
.
PurchaseSubscriptionURL
,
PurchaseSubscriptionURL
:
updatedSettings
.
PurchaseSubscriptionURL
,
SoraClientEnabled
:
updatedSettings
.
SoraClientEnabled
,
CustomMenuItems
:
dto
.
ParseCustomMenuItems
(
updatedSettings
.
CustomMenuItems
),
CustomMenuItems
:
dto
.
ParseCustomMenuItems
(
updatedSettings
.
CustomMenuItems
),
CustomEndpoints
:
dto
.
ParseCustomEndpoints
(
updatedSettings
.
CustomEndpoints
),
CustomEndpoints
:
dto
.
ParseCustomEndpoints
(
updatedSettings
.
CustomEndpoints
),
DefaultConcurrency
:
updatedSettings
.
DefaultConcurrency
,
DefaultConcurrency
:
updatedSettings
.
DefaultConcurrency
,
...
@@ -1207,384 +1201,6 @@ func (h *SettingHandler) GetStreamTimeoutSettings(c *gin.Context) {
...
@@ -1207,384 +1201,6 @@ func (h *SettingHandler) GetStreamTimeoutSettings(c *gin.Context) {
})
})
}
}
func
toSoraS3SettingsDTO
(
settings
*
service
.
SoraS3Settings
)
dto
.
SoraS3Settings
{
if
settings
==
nil
{
return
dto
.
SoraS3Settings
{}
}
return
dto
.
SoraS3Settings
{
Enabled
:
settings
.
Enabled
,
Endpoint
:
settings
.
Endpoint
,
Region
:
settings
.
Region
,
Bucket
:
settings
.
Bucket
,
AccessKeyID
:
settings
.
AccessKeyID
,
SecretAccessKeyConfigured
:
settings
.
SecretAccessKeyConfigured
,
Prefix
:
settings
.
Prefix
,
ForcePathStyle
:
settings
.
ForcePathStyle
,
CDNURL
:
settings
.
CDNURL
,
DefaultStorageQuotaBytes
:
settings
.
DefaultStorageQuotaBytes
,
}
}
func
toSoraS3ProfileDTO
(
profile
service
.
SoraS3Profile
)
dto
.
SoraS3Profile
{
return
dto
.
SoraS3Profile
{
ProfileID
:
profile
.
ProfileID
,
Name
:
profile
.
Name
,
IsActive
:
profile
.
IsActive
,
Enabled
:
profile
.
Enabled
,
Endpoint
:
profile
.
Endpoint
,
Region
:
profile
.
Region
,
Bucket
:
profile
.
Bucket
,
AccessKeyID
:
profile
.
AccessKeyID
,
SecretAccessKeyConfigured
:
profile
.
SecretAccessKeyConfigured
,
Prefix
:
profile
.
Prefix
,
ForcePathStyle
:
profile
.
ForcePathStyle
,
CDNURL
:
profile
.
CDNURL
,
DefaultStorageQuotaBytes
:
profile
.
DefaultStorageQuotaBytes
,
UpdatedAt
:
profile
.
UpdatedAt
,
}
}
func
validateSoraS3RequiredWhenEnabled
(
enabled
bool
,
endpoint
,
bucket
,
accessKeyID
,
secretAccessKey
string
,
hasStoredSecret
bool
)
error
{
if
!
enabled
{
return
nil
}
if
strings
.
TrimSpace
(
endpoint
)
==
""
{
return
fmt
.
Errorf
(
"S3 Endpoint is required when enabled"
)
}
if
strings
.
TrimSpace
(
bucket
)
==
""
{
return
fmt
.
Errorf
(
"S3 Bucket is required when enabled"
)
}
if
strings
.
TrimSpace
(
accessKeyID
)
==
""
{
return
fmt
.
Errorf
(
"S3 Access Key ID is required when enabled"
)
}
if
strings
.
TrimSpace
(
secretAccessKey
)
!=
""
||
hasStoredSecret
{
return
nil
}
return
fmt
.
Errorf
(
"S3 Secret Access Key is required when enabled"
)
}
func
findSoraS3ProfileByID
(
items
[]
service
.
SoraS3Profile
,
profileID
string
)
*
service
.
SoraS3Profile
{
for
idx
:=
range
items
{
if
items
[
idx
]
.
ProfileID
==
profileID
{
return
&
items
[
idx
]
}
}
return
nil
}
// GetSoraS3Settings 获取 Sora S3 存储配置(兼容旧单配置接口)
// GET /api/v1/admin/settings/sora-s3
func
(
h
*
SettingHandler
)
GetSoraS3Settings
(
c
*
gin
.
Context
)
{
settings
,
err
:=
h
.
settingService
.
GetSoraS3Settings
(
c
.
Request
.
Context
())
if
err
!=
nil
{
response
.
ErrorFrom
(
c
,
err
)
return
}
response
.
Success
(
c
,
toSoraS3SettingsDTO
(
settings
))
}
// ListSoraS3Profiles 获取 Sora S3 多配置
// GET /api/v1/admin/settings/sora-s3/profiles
func
(
h
*
SettingHandler
)
ListSoraS3Profiles
(
c
*
gin
.
Context
)
{
result
,
err
:=
h
.
settingService
.
ListSoraS3Profiles
(
c
.
Request
.
Context
())
if
err
!=
nil
{
response
.
ErrorFrom
(
c
,
err
)
return
}
items
:=
make
([]
dto
.
SoraS3Profile
,
0
,
len
(
result
.
Items
))
for
idx
:=
range
result
.
Items
{
items
=
append
(
items
,
toSoraS3ProfileDTO
(
result
.
Items
[
idx
]))
}
response
.
Success
(
c
,
dto
.
ListSoraS3ProfilesResponse
{
ActiveProfileID
:
result
.
ActiveProfileID
,
Items
:
items
,
})
}
// UpdateSoraS3SettingsRequest 更新/测试 Sora S3 配置请求(兼容旧接口)
type
UpdateSoraS3SettingsRequest
struct
{
ProfileID
string
`json:"profile_id"`
Enabled
bool
`json:"enabled"`
Endpoint
string
`json:"endpoint"`
Region
string
`json:"region"`
Bucket
string
`json:"bucket"`
AccessKeyID
string
`json:"access_key_id"`
SecretAccessKey
string
`json:"secret_access_key"`
Prefix
string
`json:"prefix"`
ForcePathStyle
bool
`json:"force_path_style"`
CDNURL
string
`json:"cdn_url"`
DefaultStorageQuotaBytes
int64
`json:"default_storage_quota_bytes"`
}
type
CreateSoraS3ProfileRequest
struct
{
ProfileID
string
`json:"profile_id"`
Name
string
`json:"name"`
SetActive
bool
`json:"set_active"`
Enabled
bool
`json:"enabled"`
Endpoint
string
`json:"endpoint"`
Region
string
`json:"region"`
Bucket
string
`json:"bucket"`
AccessKeyID
string
`json:"access_key_id"`
SecretAccessKey
string
`json:"secret_access_key"`
Prefix
string
`json:"prefix"`
ForcePathStyle
bool
`json:"force_path_style"`
CDNURL
string
`json:"cdn_url"`
DefaultStorageQuotaBytes
int64
`json:"default_storage_quota_bytes"`
}
type
UpdateSoraS3ProfileRequest
struct
{
Name
string
`json:"name"`
Enabled
bool
`json:"enabled"`
Endpoint
string
`json:"endpoint"`
Region
string
`json:"region"`
Bucket
string
`json:"bucket"`
AccessKeyID
string
`json:"access_key_id"`
SecretAccessKey
string
`json:"secret_access_key"`
Prefix
string
`json:"prefix"`
ForcePathStyle
bool
`json:"force_path_style"`
CDNURL
string
`json:"cdn_url"`
DefaultStorageQuotaBytes
int64
`json:"default_storage_quota_bytes"`
}
// CreateSoraS3Profile 创建 Sora S3 配置
// POST /api/v1/admin/settings/sora-s3/profiles
func
(
h
*
SettingHandler
)
CreateSoraS3Profile
(
c
*
gin
.
Context
)
{
var
req
CreateSoraS3ProfileRequest
if
err
:=
c
.
ShouldBindJSON
(
&
req
);
err
!=
nil
{
response
.
BadRequest
(
c
,
"Invalid request: "
+
err
.
Error
())
return
}
if
req
.
DefaultStorageQuotaBytes
<
0
{
req
.
DefaultStorageQuotaBytes
=
0
}
if
strings
.
TrimSpace
(
req
.
Name
)
==
""
{
response
.
BadRequest
(
c
,
"Name is required"
)
return
}
if
strings
.
TrimSpace
(
req
.
ProfileID
)
==
""
{
response
.
BadRequest
(
c
,
"Profile ID is required"
)
return
}
if
err
:=
validateSoraS3RequiredWhenEnabled
(
req
.
Enabled
,
req
.
Endpoint
,
req
.
Bucket
,
req
.
AccessKeyID
,
req
.
SecretAccessKey
,
false
);
err
!=
nil
{
response
.
BadRequest
(
c
,
err
.
Error
())
return
}
created
,
err
:=
h
.
settingService
.
CreateSoraS3Profile
(
c
.
Request
.
Context
(),
&
service
.
SoraS3Profile
{
ProfileID
:
req
.
ProfileID
,
Name
:
req
.
Name
,
Enabled
:
req
.
Enabled
,
Endpoint
:
req
.
Endpoint
,
Region
:
req
.
Region
,
Bucket
:
req
.
Bucket
,
AccessKeyID
:
req
.
AccessKeyID
,
SecretAccessKey
:
req
.
SecretAccessKey
,
Prefix
:
req
.
Prefix
,
ForcePathStyle
:
req
.
ForcePathStyle
,
CDNURL
:
req
.
CDNURL
,
DefaultStorageQuotaBytes
:
req
.
DefaultStorageQuotaBytes
,
},
req
.
SetActive
)
if
err
!=
nil
{
response
.
ErrorFrom
(
c
,
err
)
return
}
response
.
Success
(
c
,
toSoraS3ProfileDTO
(
*
created
))
}
// UpdateSoraS3Profile 更新 Sora S3 配置
// PUT /api/v1/admin/settings/sora-s3/profiles/:profile_id
func
(
h
*
SettingHandler
)
UpdateSoraS3Profile
(
c
*
gin
.
Context
)
{
profileID
:=
strings
.
TrimSpace
(
c
.
Param
(
"profile_id"
))
if
profileID
==
""
{
response
.
BadRequest
(
c
,
"Profile ID is required"
)
return
}
var
req
UpdateSoraS3ProfileRequest
if
err
:=
c
.
ShouldBindJSON
(
&
req
);
err
!=
nil
{
response
.
BadRequest
(
c
,
"Invalid request: "
+
err
.
Error
())
return
}
if
req
.
DefaultStorageQuotaBytes
<
0
{
req
.
DefaultStorageQuotaBytes
=
0
}
if
strings
.
TrimSpace
(
req
.
Name
)
==
""
{
response
.
BadRequest
(
c
,
"Name is required"
)
return
}
existingList
,
err
:=
h
.
settingService
.
ListSoraS3Profiles
(
c
.
Request
.
Context
())
if
err
!=
nil
{
response
.
ErrorFrom
(
c
,
err
)
return
}
existing
:=
findSoraS3ProfileByID
(
existingList
.
Items
,
profileID
)
if
existing
==
nil
{
response
.
ErrorFrom
(
c
,
service
.
ErrSoraS3ProfileNotFound
)
return
}
if
err
:=
validateSoraS3RequiredWhenEnabled
(
req
.
Enabled
,
req
.
Endpoint
,
req
.
Bucket
,
req
.
AccessKeyID
,
req
.
SecretAccessKey
,
existing
.
SecretAccessKeyConfigured
);
err
!=
nil
{
response
.
BadRequest
(
c
,
err
.
Error
())
return
}
updated
,
updateErr
:=
h
.
settingService
.
UpdateSoraS3Profile
(
c
.
Request
.
Context
(),
profileID
,
&
service
.
SoraS3Profile
{
Name
:
req
.
Name
,
Enabled
:
req
.
Enabled
,
Endpoint
:
req
.
Endpoint
,
Region
:
req
.
Region
,
Bucket
:
req
.
Bucket
,
AccessKeyID
:
req
.
AccessKeyID
,
SecretAccessKey
:
req
.
SecretAccessKey
,
Prefix
:
req
.
Prefix
,
ForcePathStyle
:
req
.
ForcePathStyle
,
CDNURL
:
req
.
CDNURL
,
DefaultStorageQuotaBytes
:
req
.
DefaultStorageQuotaBytes
,
})
if
updateErr
!=
nil
{
response
.
ErrorFrom
(
c
,
updateErr
)
return
}
response
.
Success
(
c
,
toSoraS3ProfileDTO
(
*
updated
))
}
// DeleteSoraS3Profile 删除 Sora S3 配置
// DELETE /api/v1/admin/settings/sora-s3/profiles/:profile_id
func
(
h
*
SettingHandler
)
DeleteSoraS3Profile
(
c
*
gin
.
Context
)
{
profileID
:=
strings
.
TrimSpace
(
c
.
Param
(
"profile_id"
))
if
profileID
==
""
{
response
.
BadRequest
(
c
,
"Profile ID is required"
)
return
}
if
err
:=
h
.
settingService
.
DeleteSoraS3Profile
(
c
.
Request
.
Context
(),
profileID
);
err
!=
nil
{
response
.
ErrorFrom
(
c
,
err
)
return
}
response
.
Success
(
c
,
gin
.
H
{
"deleted"
:
true
})
}
// SetActiveSoraS3Profile 切换激活 Sora S3 配置
// POST /api/v1/admin/settings/sora-s3/profiles/:profile_id/activate
func
(
h
*
SettingHandler
)
SetActiveSoraS3Profile
(
c
*
gin
.
Context
)
{
profileID
:=
strings
.
TrimSpace
(
c
.
Param
(
"profile_id"
))
if
profileID
==
""
{
response
.
BadRequest
(
c
,
"Profile ID is required"
)
return
}
active
,
err
:=
h
.
settingService
.
SetActiveSoraS3Profile
(
c
.
Request
.
Context
(),
profileID
)
if
err
!=
nil
{
response
.
ErrorFrom
(
c
,
err
)
return
}
response
.
Success
(
c
,
toSoraS3ProfileDTO
(
*
active
))
}
// UpdateSoraS3Settings 更新 Sora S3 存储配置(兼容旧单配置接口)
// PUT /api/v1/admin/settings/sora-s3
func
(
h
*
SettingHandler
)
UpdateSoraS3Settings
(
c
*
gin
.
Context
)
{
var
req
UpdateSoraS3SettingsRequest
if
err
:=
c
.
ShouldBindJSON
(
&
req
);
err
!=
nil
{
response
.
BadRequest
(
c
,
"Invalid request: "
+
err
.
Error
())
return
}
existing
,
err
:=
h
.
settingService
.
GetSoraS3Settings
(
c
.
Request
.
Context
())
if
err
!=
nil
{
response
.
ErrorFrom
(
c
,
err
)
return
}
if
req
.
DefaultStorageQuotaBytes
<
0
{
req
.
DefaultStorageQuotaBytes
=
0
}
if
err
:=
validateSoraS3RequiredWhenEnabled
(
req
.
Enabled
,
req
.
Endpoint
,
req
.
Bucket
,
req
.
AccessKeyID
,
req
.
SecretAccessKey
,
existing
.
SecretAccessKeyConfigured
);
err
!=
nil
{
response
.
BadRequest
(
c
,
err
.
Error
())
return
}
settings
:=
&
service
.
SoraS3Settings
{
Enabled
:
req
.
Enabled
,
Endpoint
:
req
.
Endpoint
,
Region
:
req
.
Region
,
Bucket
:
req
.
Bucket
,
AccessKeyID
:
req
.
AccessKeyID
,
SecretAccessKey
:
req
.
SecretAccessKey
,
Prefix
:
req
.
Prefix
,
ForcePathStyle
:
req
.
ForcePathStyle
,
CDNURL
:
req
.
CDNURL
,
DefaultStorageQuotaBytes
:
req
.
DefaultStorageQuotaBytes
,
}
if
err
:=
h
.
settingService
.
SetSoraS3Settings
(
c
.
Request
.
Context
(),
settings
);
err
!=
nil
{
response
.
ErrorFrom
(
c
,
err
)
return
}
updatedSettings
,
err
:=
h
.
settingService
.
GetSoraS3Settings
(
c
.
Request
.
Context
())
if
err
!=
nil
{
response
.
ErrorFrom
(
c
,
err
)
return
}
response
.
Success
(
c
,
toSoraS3SettingsDTO
(
updatedSettings
))
}
// TestSoraS3Connection 测试 Sora S3 连接(HeadBucket)
// POST /api/v1/admin/settings/sora-s3/test
func
(
h
*
SettingHandler
)
TestSoraS3Connection
(
c
*
gin
.
Context
)
{
if
h
.
soraS3Storage
==
nil
{
response
.
Error
(
c
,
500
,
"S3 存储服务未初始化"
)
return
}
var
req
UpdateSoraS3SettingsRequest
if
err
:=
c
.
ShouldBindJSON
(
&
req
);
err
!=
nil
{
response
.
BadRequest
(
c
,
"Invalid request: "
+
err
.
Error
())
return
}
if
!
req
.
Enabled
{
response
.
BadRequest
(
c
,
"S3 未启用,无法测试连接"
)
return
}
if
req
.
SecretAccessKey
==
""
{
if
req
.
ProfileID
!=
""
{
profiles
,
err
:=
h
.
settingService
.
ListSoraS3Profiles
(
c
.
Request
.
Context
())
if
err
==
nil
{
profile
:=
findSoraS3ProfileByID
(
profiles
.
Items
,
req
.
ProfileID
)
if
profile
!=
nil
{
req
.
SecretAccessKey
=
profile
.
SecretAccessKey
}
}
}
if
req
.
SecretAccessKey
==
""
{
existing
,
err
:=
h
.
settingService
.
GetSoraS3Settings
(
c
.
Request
.
Context
())
if
err
==
nil
{
req
.
SecretAccessKey
=
existing
.
SecretAccessKey
}
}
}
testCfg
:=
&
service
.
SoraS3Settings
{
Enabled
:
true
,
Endpoint
:
req
.
Endpoint
,
Region
:
req
.
Region
,
Bucket
:
req
.
Bucket
,
AccessKeyID
:
req
.
AccessKeyID
,
SecretAccessKey
:
req
.
SecretAccessKey
,
Prefix
:
req
.
Prefix
,
ForcePathStyle
:
req
.
ForcePathStyle
,
CDNURL
:
req
.
CDNURL
,
}
if
err
:=
h
.
soraS3Storage
.
TestConnectionWithSettings
(
c
.
Request
.
Context
(),
testCfg
);
err
!=
nil
{
response
.
Error
(
c
,
400
,
"S3 连接测试失败: "
+
err
.
Error
())
return
}
response
.
Success
(
c
,
gin
.
H
{
"message"
:
"S3 连接成功"
})
}
// GetRectifierSettings 获取请求整流器配置
// GetRectifierSettings 获取请求整流器配置
// GET /api/v1/admin/settings/rectifier
// GET /api/v1/admin/settings/rectifier
func
(
h
*
SettingHandler
)
GetRectifierSettings
(
c
*
gin
.
Context
)
{
func
(
h
*
SettingHandler
)
GetRectifierSettings
(
c
*
gin
.
Context
)
{
...
...
backend/internal/handler/admin/user_handler.go
View file @
d757df8a
...
@@ -34,14 +34,13 @@ func NewUserHandler(adminService service.AdminService, concurrencyService *servi
...
@@ -34,14 +34,13 @@ func NewUserHandler(adminService service.AdminService, concurrencyService *servi
// CreateUserRequest represents admin create user request
// CreateUserRequest represents admin create user request
type
CreateUserRequest
struct
{
type
CreateUserRequest
struct
{
Email
string
`json:"email" binding:"required,email"`
Email
string
`json:"email" binding:"required,email"`
Password
string
`json:"password" binding:"required,min=6"`
Password
string
`json:"password" binding:"required,min=6"`
Username
string
`json:"username"`
Username
string
`json:"username"`
Notes
string
`json:"notes"`
Notes
string
`json:"notes"`
Balance
float64
`json:"balance"`
Balance
float64
`json:"balance"`
Concurrency
int
`json:"concurrency"`
Concurrency
int
`json:"concurrency"`
AllowedGroups
[]
int64
`json:"allowed_groups"`
AllowedGroups
[]
int64
`json:"allowed_groups"`
SoraStorageQuotaBytes
int64
`json:"sora_storage_quota_bytes"`
}
}
// UpdateUserRequest represents admin update user request
// UpdateUserRequest represents admin update user request
...
@@ -57,8 +56,7 @@ type UpdateUserRequest struct {
...
@@ -57,8 +56,7 @@ type UpdateUserRequest struct {
AllowedGroups
*
[]
int64
`json:"allowed_groups"`
AllowedGroups
*
[]
int64
`json:"allowed_groups"`
// GroupRates 用户专属分组倍率配置
// GroupRates 用户专属分组倍率配置
// map[groupID]*rate,nil 表示删除该分组的专属倍率
// map[groupID]*rate,nil 表示删除该分组的专属倍率
GroupRates
map
[
int64
]
*
float64
`json:"group_rates"`
GroupRates
map
[
int64
]
*
float64
`json:"group_rates"`
SoraStorageQuotaBytes
*
int64
`json:"sora_storage_quota_bytes"`
}
}
// UpdateBalanceRequest represents balance update request
// UpdateBalanceRequest represents balance update request
...
@@ -182,14 +180,13 @@ func (h *UserHandler) Create(c *gin.Context) {
...
@@ -182,14 +180,13 @@ func (h *UserHandler) Create(c *gin.Context) {
}
}
user
,
err
:=
h
.
adminService
.
CreateUser
(
c
.
Request
.
Context
(),
&
service
.
CreateUserInput
{
user
,
err
:=
h
.
adminService
.
CreateUser
(
c
.
Request
.
Context
(),
&
service
.
CreateUserInput
{
Email
:
req
.
Email
,
Email
:
req
.
Email
,
Password
:
req
.
Password
,
Password
:
req
.
Password
,
Username
:
req
.
Username
,
Username
:
req
.
Username
,
Notes
:
req
.
Notes
,
Notes
:
req
.
Notes
,
Balance
:
req
.
Balance
,
Balance
:
req
.
Balance
,
Concurrency
:
req
.
Concurrency
,
Concurrency
:
req
.
Concurrency
,
AllowedGroups
:
req
.
AllowedGroups
,
AllowedGroups
:
req
.
AllowedGroups
,
SoraStorageQuotaBytes
:
req
.
SoraStorageQuotaBytes
,
})
})
if
err
!=
nil
{
if
err
!=
nil
{
response
.
ErrorFrom
(
c
,
err
)
response
.
ErrorFrom
(
c
,
err
)
...
@@ -216,16 +213,15 @@ func (h *UserHandler) Update(c *gin.Context) {
...
@@ -216,16 +213,15 @@ func (h *UserHandler) Update(c *gin.Context) {
// 使用指针类型直接传递,nil 表示未提供该字段
// 使用指针类型直接传递,nil 表示未提供该字段
user
,
err
:=
h
.
adminService
.
UpdateUser
(
c
.
Request
.
Context
(),
userID
,
&
service
.
UpdateUserInput
{
user
,
err
:=
h
.
adminService
.
UpdateUser
(
c
.
Request
.
Context
(),
userID
,
&
service
.
UpdateUserInput
{
Email
:
req
.
Email
,
Email
:
req
.
Email
,
Password
:
req
.
Password
,
Password
:
req
.
Password
,
Username
:
req
.
Username
,
Username
:
req
.
Username
,
Notes
:
req
.
Notes
,
Notes
:
req
.
Notes
,
Balance
:
req
.
Balance
,
Balance
:
req
.
Balance
,
Concurrency
:
req
.
Concurrency
,
Concurrency
:
req
.
Concurrency
,
Status
:
req
.
Status
,
Status
:
req
.
Status
,
AllowedGroups
:
req
.
AllowedGroups
,
AllowedGroups
:
req
.
AllowedGroups
,
GroupRates
:
req
.
GroupRates
,
GroupRates
:
req
.
GroupRates
,
SoraStorageQuotaBytes
:
req
.
SoraStorageQuotaBytes
,
})
})
if
err
!=
nil
{
if
err
!=
nil
{
response
.
ErrorFrom
(
c
,
err
)
response
.
ErrorFrom
(
c
,
err
)
...
...
backend/internal/handler/dto/mappers.go
View file @
d757df8a
...
@@ -59,11 +59,9 @@ func UserFromServiceAdmin(u *service.User) *AdminUser {
...
@@ -59,11 +59,9 @@ func UserFromServiceAdmin(u *service.User) *AdminUser {
return
nil
return
nil
}
}
return
&
AdminUser
{
return
&
AdminUser
{
User
:
*
base
,
User
:
*
base
,
Notes
:
u
.
Notes
,
Notes
:
u
.
Notes
,
GroupRates
:
u
.
GroupRates
,
GroupRates
:
u
.
GroupRates
,
SoraStorageQuotaBytes
:
u
.
SoraStorageQuotaBytes
,
SoraStorageUsedBytes
:
u
.
SoraStorageUsedBytes
,
}
}
}
}
...
@@ -172,14 +170,9 @@ func groupFromServiceBase(g *service.Group) Group {
...
@@ -172,14 +170,9 @@ func groupFromServiceBase(g *service.Group) Group {
ImagePrice1K
:
g
.
ImagePrice1K
,
ImagePrice1K
:
g
.
ImagePrice1K
,
ImagePrice2K
:
g
.
ImagePrice2K
,
ImagePrice2K
:
g
.
ImagePrice2K
,
ImagePrice4K
:
g
.
ImagePrice4K
,
ImagePrice4K
:
g
.
ImagePrice4K
,
SoraImagePrice360
:
g
.
SoraImagePrice360
,
SoraImagePrice540
:
g
.
SoraImagePrice540
,
SoraVideoPricePerRequest
:
g
.
SoraVideoPricePerRequest
,
SoraVideoPricePerRequestHD
:
g
.
SoraVideoPricePerRequestHD
,
ClaudeCodeOnly
:
g
.
ClaudeCodeOnly
,
ClaudeCodeOnly
:
g
.
ClaudeCodeOnly
,
FallbackGroupID
:
g
.
FallbackGroupID
,
FallbackGroupID
:
g
.
FallbackGroupID
,
FallbackGroupIDOnInvalidRequest
:
g
.
FallbackGroupIDOnInvalidRequest
,
FallbackGroupIDOnInvalidRequest
:
g
.
FallbackGroupIDOnInvalidRequest
,
SoraStorageQuotaBytes
:
g
.
SoraStorageQuotaBytes
,
AllowMessagesDispatch
:
g
.
AllowMessagesDispatch
,
AllowMessagesDispatch
:
g
.
AllowMessagesDispatch
,
RequireOAuthOnly
:
g
.
RequireOAuthOnly
,
RequireOAuthOnly
:
g
.
RequireOAuthOnly
,
RequirePrivacySet
:
g
.
RequirePrivacySet
,
RequirePrivacySet
:
g
.
RequirePrivacySet
,
...
...
backend/internal/handler/dto/settings.go
View file @
d757df8a
...
@@ -61,7 +61,6 @@ type SystemSettings struct {
...
@@ -61,7 +61,6 @@ type SystemSettings struct {
HideCcsImportButton
bool
`json:"hide_ccs_import_button"`
HideCcsImportButton
bool
`json:"hide_ccs_import_button"`
PurchaseSubscriptionEnabled
bool
`json:"purchase_subscription_enabled"`
PurchaseSubscriptionEnabled
bool
`json:"purchase_subscription_enabled"`
PurchaseSubscriptionURL
string
`json:"purchase_subscription_url"`
PurchaseSubscriptionURL
string
`json:"purchase_subscription_url"`
SoraClientEnabled
bool
`json:"sora_client_enabled"`
CustomMenuItems
[]
CustomMenuItem
`json:"custom_menu_items"`
CustomMenuItems
[]
CustomMenuItem
`json:"custom_menu_items"`
CustomEndpoints
[]
CustomEndpoint
`json:"custom_endpoints"`
CustomEndpoints
[]
CustomEndpoint
`json:"custom_endpoints"`
...
@@ -128,49 +127,10 @@ type PublicSettings struct {
...
@@ -128,49 +127,10 @@ type PublicSettings struct {
CustomMenuItems
[]
CustomMenuItem
`json:"custom_menu_items"`
CustomMenuItems
[]
CustomMenuItem
`json:"custom_menu_items"`
CustomEndpoints
[]
CustomEndpoint
`json:"custom_endpoints"`
CustomEndpoints
[]
CustomEndpoint
`json:"custom_endpoints"`
LinuxDoOAuthEnabled
bool
`json:"linuxdo_oauth_enabled"`
LinuxDoOAuthEnabled
bool
`json:"linuxdo_oauth_enabled"`
SoraClientEnabled
bool
`json:"sora_client_enabled"`
BackendModeEnabled
bool
`json:"backend_mode_enabled"`
BackendModeEnabled
bool
`json:"backend_mode_enabled"`
Version
string
`json:"version"`
Version
string
`json:"version"`
}
}
// SoraS3Settings Sora S3 存储配置 DTO(响应用,不含敏感字段)
type
SoraS3Settings
struct
{
Enabled
bool
`json:"enabled"`
Endpoint
string
`json:"endpoint"`
Region
string
`json:"region"`
Bucket
string
`json:"bucket"`
AccessKeyID
string
`json:"access_key_id"`
SecretAccessKeyConfigured
bool
`json:"secret_access_key_configured"`
Prefix
string
`json:"prefix"`
ForcePathStyle
bool
`json:"force_path_style"`
CDNURL
string
`json:"cdn_url"`
DefaultStorageQuotaBytes
int64
`json:"default_storage_quota_bytes"`
}
// SoraS3Profile Sora S3 存储配置项 DTO(响应用,不含敏感字段)
type
SoraS3Profile
struct
{
ProfileID
string
`json:"profile_id"`
Name
string
`json:"name"`
IsActive
bool
`json:"is_active"`
Enabled
bool
`json:"enabled"`
Endpoint
string
`json:"endpoint"`
Region
string
`json:"region"`
Bucket
string
`json:"bucket"`
AccessKeyID
string
`json:"access_key_id"`
SecretAccessKeyConfigured
bool
`json:"secret_access_key_configured"`
Prefix
string
`json:"prefix"`
ForcePathStyle
bool
`json:"force_path_style"`
CDNURL
string
`json:"cdn_url"`
DefaultStorageQuotaBytes
int64
`json:"default_storage_quota_bytes"`
UpdatedAt
string
`json:"updated_at"`
}
// ListSoraS3ProfilesResponse Sora S3 配置列表响应
type
ListSoraS3ProfilesResponse
struct
{
ActiveProfileID
string
`json:"active_profile_id"`
Items
[]
SoraS3Profile
`json:"items"`
}
// OverloadCooldownSettings 529过载冷却配置 DTO
// OverloadCooldownSettings 529过载冷却配置 DTO
type
OverloadCooldownSettings
struct
{
type
OverloadCooldownSettings
struct
{
Enabled
bool
`json:"enabled"`
Enabled
bool
`json:"enabled"`
...
...
backend/internal/handler/dto/types.go
View file @
d757df8a
...
@@ -26,9 +26,7 @@ type AdminUser struct {
...
@@ -26,9 +26,7 @@ type AdminUser struct {
Notes
string
`json:"notes"`
Notes
string
`json:"notes"`
// GroupRates 用户专属分组倍率配置
// GroupRates 用户专属分组倍率配置
// map[groupID]rateMultiplier
// map[groupID]rateMultiplier
GroupRates
map
[
int64
]
float64
`json:"group_rates,omitempty"`
GroupRates
map
[
int64
]
float64
`json:"group_rates,omitempty"`
SoraStorageQuotaBytes
int64
`json:"sora_storage_quota_bytes"`
SoraStorageUsedBytes
int64
`json:"sora_storage_used_bytes"`
}
}
type
APIKey
struct
{
type
APIKey
struct
{
...
@@ -84,21 +82,12 @@ type Group struct {
...
@@ -84,21 +82,12 @@ type Group struct {
ImagePrice2K
*
float64
`json:"image_price_2k"`
ImagePrice2K
*
float64
`json:"image_price_2k"`
ImagePrice4K
*
float64
`json:"image_price_4k"`
ImagePrice4K
*
float64
`json:"image_price_4k"`
// Sora 按次计费配置
SoraImagePrice360
*
float64
`json:"sora_image_price_360"`
SoraImagePrice540
*
float64
`json:"sora_image_price_540"`
SoraVideoPricePerRequest
*
float64
`json:"sora_video_price_per_request"`
SoraVideoPricePerRequestHD
*
float64
`json:"sora_video_price_per_request_hd"`
// Claude Code 客户端限制
// Claude Code 客户端限制
ClaudeCodeOnly
bool
`json:"claude_code_only"`
ClaudeCodeOnly
bool
`json:"claude_code_only"`
FallbackGroupID
*
int64
`json:"fallback_group_id"`
FallbackGroupID
*
int64
`json:"fallback_group_id"`
// 无效请求兜底分组
// 无效请求兜底分组
FallbackGroupIDOnInvalidRequest
*
int64
`json:"fallback_group_id_on_invalid_request"`
FallbackGroupIDOnInvalidRequest
*
int64
`json:"fallback_group_id_on_invalid_request"`
// Sora 存储配额
SoraStorageQuotaBytes
int64
`json:"sora_storage_quota_bytes"`
// OpenAI Messages 调度开关(用户侧需要此字段判断是否展示 Claude Code 教程)
// OpenAI Messages 调度开关(用户侧需要此字段判断是否展示 Claude Code 教程)
AllowMessagesDispatch
bool
`json:"allow_messages_dispatch"`
AllowMessagesDispatch
bool
`json:"allow_messages_dispatch"`
...
...
backend/internal/handler/endpoint.go
View file @
d757df8a
...
@@ -31,7 +31,7 @@ const (
...
@@ -31,7 +31,7 @@ const (
// ──────────────────────────────────────────────────────────
// ──────────────────────────────────────────────────────────
// NormalizeInboundEndpoint maps a raw request path (which may carry
// NormalizeInboundEndpoint maps a raw request path (which may carry
// prefixes like /antigravity, /openai
, /sora
) to its canonical form.
// prefixes like /antigravity, /openai) to its canonical form.
//
//
// "/antigravity/v1/messages" → "/v1/messages"
// "/antigravity/v1/messages" → "/v1/messages"
// "/v1/chat/completions" → "/v1/chat/completions"
// "/v1/chat/completions" → "/v1/chat/completions"
...
@@ -61,7 +61,7 @@ func NormalizeInboundEndpoint(path string) string {
...
@@ -61,7 +61,7 @@ func NormalizeInboundEndpoint(path string) string {
// such as /v1/responses/compact preserved from the raw URL).
// such as /v1/responses/compact preserved from the raw URL).
// - Anthropic → /v1/messages
// - Anthropic → /v1/messages
// - Gemini → /v1beta/models
// - Gemini → /v1beta/models
// -
Sora → /v1/chat/completions
// -
Antigravity → /v1/messages (Claude) or gemini (Gemini)
// - Antigravity routes may target either Claude or Gemini, so the
// - Antigravity routes may target either Claude or Gemini, so the
// inbound endpoint is used to distinguish.
// inbound endpoint is used to distinguish.
func
DeriveUpstreamEndpoint
(
inbound
,
rawRequestPath
,
platform
string
)
string
{
func
DeriveUpstreamEndpoint
(
inbound
,
rawRequestPath
,
platform
string
)
string
{
...
@@ -82,9 +82,6 @@ func DeriveUpstreamEndpoint(inbound, rawRequestPath, platform string) string {
...
@@ -82,9 +82,6 @@ func DeriveUpstreamEndpoint(inbound, rawRequestPath, platform string) string {
case
service
.
PlatformGemini
:
case
service
.
PlatformGemini
:
return
EndpointGeminiModels
return
EndpointGeminiModels
case
service
.
PlatformSora
:
return
EndpointChatCompletions
case
service
.
PlatformAntigravity
:
case
service
.
PlatformAntigravity
:
// Antigravity accounts serve both Claude and Gemini.
// Antigravity accounts serve both Claude and Gemini.
if
inbound
==
EndpointGeminiModels
{
if
inbound
==
EndpointGeminiModels
{
...
...
backend/internal/handler/endpoint_test.go
View file @
d757df8a
...
@@ -27,11 +27,10 @@ func TestNormalizeInboundEndpoint(t *testing.T) {
...
@@ -27,11 +27,10 @@ func TestNormalizeInboundEndpoint(t *testing.T) {
{
"/v1/responses"
,
EndpointResponses
},
{
"/v1/responses"
,
EndpointResponses
},
{
"/v1beta/models"
,
EndpointGeminiModels
},
{
"/v1beta/models"
,
EndpointGeminiModels
},
// Prefixed paths (antigravity, openai
, sora
).
// Prefixed paths (antigravity, openai).
{
"/antigravity/v1/messages"
,
EndpointMessages
},
{
"/antigravity/v1/messages"
,
EndpointMessages
},
{
"/openai/v1/responses"
,
EndpointResponses
},
{
"/openai/v1/responses"
,
EndpointResponses
},
{
"/openai/v1/responses/compact"
,
EndpointResponses
},
{
"/openai/v1/responses/compact"
,
EndpointResponses
},
{
"/sora/v1/chat/completions"
,
EndpointChatCompletions
},
{
"/antigravity/v1beta/models/gemini:generateContent"
,
EndpointGeminiModels
},
{
"/antigravity/v1beta/models/gemini:generateContent"
,
EndpointGeminiModels
},
// Gin route patterns with wildcards.
// Gin route patterns with wildcards.
...
@@ -68,9 +67,6 @@ func TestDeriveUpstreamEndpoint(t *testing.T) {
...
@@ -68,9 +67,6 @@ func TestDeriveUpstreamEndpoint(t *testing.T) {
// Gemini.
// Gemini.
{
"gemini models"
,
EndpointGeminiModels
,
"/v1beta/models/gemini:gen"
,
service
.
PlatformGemini
,
EndpointGeminiModels
},
{
"gemini models"
,
EndpointGeminiModels
,
"/v1beta/models/gemini:gen"
,
service
.
PlatformGemini
,
EndpointGeminiModels
},
// Sora.
{
"sora completions"
,
EndpointChatCompletions
,
"/sora/v1/chat/completions"
,
service
.
PlatformSora
,
EndpointChatCompletions
},
// OpenAI — always /v1/responses.
// OpenAI — always /v1/responses.
{
"openai responses root"
,
EndpointResponses
,
"/v1/responses"
,
service
.
PlatformOpenAI
,
EndpointResponses
},
{
"openai responses root"
,
EndpointResponses
,
"/v1/responses"
,
service
.
PlatformOpenAI
,
EndpointResponses
},
{
"openai responses compact"
,
EndpointResponses
,
"/openai/v1/responses/compact"
,
service
.
PlatformOpenAI
,
"/v1/responses/compact"
},
{
"openai responses compact"
,
EndpointResponses
,
"/openai/v1/responses/compact"
,
service
.
PlatformOpenAI
,
"/v1/responses/compact"
},
...
...
backend/internal/handler/gateway_handler.go
View file @
d757df8a
...
@@ -859,14 +859,6 @@ func (h *GatewayHandler) Models(c *gin.Context) {
...
@@ -859,14 +859,6 @@ func (h *GatewayHandler) Models(c *gin.Context) {
platform
=
forcedPlatform
platform
=
forcedPlatform
}
}
if
platform
==
service
.
PlatformSora
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"object"
:
"list"
,
"data"
:
service
.
DefaultSoraModels
(
h
.
cfg
),
})
return
}
// Get available models from account configurations (without platform filter)
// Get available models from account configurations (without platform filter)
availableModels
:=
h
.
gatewayService
.
GetAvailableModels
(
c
.
Request
.
Context
(),
groupID
,
""
)
availableModels
:=
h
.
gatewayService
.
GetAvailableModels
(
c
.
Request
.
Context
(),
groupID
,
""
)
...
...
backend/internal/handler/handler.go
View file @
d757df8a
...
@@ -45,8 +45,6 @@ type Handlers struct {
...
@@ -45,8 +45,6 @@ type Handlers struct {
Admin
*
AdminHandlers
Admin
*
AdminHandlers
Gateway
*
GatewayHandler
Gateway
*
GatewayHandler
OpenAIGateway
*
OpenAIGatewayHandler
OpenAIGateway
*
OpenAIGatewayHandler
SoraGateway
*
SoraGatewayHandler
SoraClient
*
SoraClientHandler
Setting
*
SettingHandler
Setting
*
SettingHandler
Totp
*
TotpHandler
Totp
*
TotpHandler
}
}
...
...
Prev
1
2
3
4
5
6
…
9
Next
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