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
beceb45d
Unverified
Commit
beceb45d
authored
Feb 18, 2026
by
Wesley Liddick
Committed by
GitHub
Feb 18, 2026
Browse files
Merge pull request #591 from miraserver/main
feat: add Cache TTL Override per account
parents
9450edf4
3d1f03c2
Changes
25
Hide whitespace changes
Inline
Side-by-side
backend/cmd/server/VERSION
View file @
beceb45d
0.1.76
0.1.83
\ No newline at end of file
\ No newline at end of file
backend/ent/migrate/schema.go
View file @
beceb45d
...
@@ -650,6 +650,7 @@ var (
...
@@ -650,6 +650,7 @@ var (
{
Name
:
"ip_address"
,
Type
:
field
.
TypeString
,
Nullable
:
true
,
Size
:
45
},
{
Name
:
"ip_address"
,
Type
:
field
.
TypeString
,
Nullable
:
true
,
Size
:
45
},
{
Name
:
"image_count"
,
Type
:
field
.
TypeInt
,
Default
:
0
},
{
Name
:
"image_count"
,
Type
:
field
.
TypeInt
,
Default
:
0
},
{
Name
:
"image_size"
,
Type
:
field
.
TypeString
,
Nullable
:
true
,
Size
:
10
},
{
Name
:
"image_size"
,
Type
:
field
.
TypeString
,
Nullable
:
true
,
Size
:
10
},
{
Name
:
"cache_ttl_overridden"
,
Type
:
field
.
TypeBool
,
Default
:
false
},
{
Name
:
"created_at"
,
Type
:
field
.
TypeTime
,
SchemaType
:
map
[
string
]
string
{
"postgres"
:
"timestamptz"
}},
{
Name
:
"created_at"
,
Type
:
field
.
TypeTime
,
SchemaType
:
map
[
string
]
string
{
"postgres"
:
"timestamptz"
}},
{
Name
:
"api_key_id"
,
Type
:
field
.
TypeInt64
},
{
Name
:
"api_key_id"
,
Type
:
field
.
TypeInt64
},
{
Name
:
"account_id"
,
Type
:
field
.
TypeInt64
},
{
Name
:
"account_id"
,
Type
:
field
.
TypeInt64
},
...
@@ -665,31 +666,31 @@ var (
...
@@ -665,31 +666,31 @@ var (
ForeignKeys
:
[]
*
schema
.
ForeignKey
{
ForeignKeys
:
[]
*
schema
.
ForeignKey
{
{
{
Symbol
:
"usage_logs_api_keys_usage_logs"
,
Symbol
:
"usage_logs_api_keys_usage_logs"
,
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
2
6
]},
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
2
7
]},
RefColumns
:
[]
*
schema
.
Column
{
APIKeysColumns
[
0
]},
RefColumns
:
[]
*
schema
.
Column
{
APIKeysColumns
[
0
]},
OnDelete
:
schema
.
NoAction
,
OnDelete
:
schema
.
NoAction
,
},
},
{
{
Symbol
:
"usage_logs_accounts_usage_logs"
,
Symbol
:
"usage_logs_accounts_usage_logs"
,
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
2
7
]},
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
2
8
]},
RefColumns
:
[]
*
schema
.
Column
{
AccountsColumns
[
0
]},
RefColumns
:
[]
*
schema
.
Column
{
AccountsColumns
[
0
]},
OnDelete
:
schema
.
NoAction
,
OnDelete
:
schema
.
NoAction
,
},
},
{
{
Symbol
:
"usage_logs_groups_usage_logs"
,
Symbol
:
"usage_logs_groups_usage_logs"
,
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
2
8
]},
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
2
9
]},
RefColumns
:
[]
*
schema
.
Column
{
GroupsColumns
[
0
]},
RefColumns
:
[]
*
schema
.
Column
{
GroupsColumns
[
0
]},
OnDelete
:
schema
.
SetNull
,
OnDelete
:
schema
.
SetNull
,
},
},
{
{
Symbol
:
"usage_logs_users_usage_logs"
,
Symbol
:
"usage_logs_users_usage_logs"
,
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
29
]},
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
30
]},
RefColumns
:
[]
*
schema
.
Column
{
UsersColumns
[
0
]},
RefColumns
:
[]
*
schema
.
Column
{
UsersColumns
[
0
]},
OnDelete
:
schema
.
NoAction
,
OnDelete
:
schema
.
NoAction
,
},
},
{
{
Symbol
:
"usage_logs_user_subscriptions_usage_logs"
,
Symbol
:
"usage_logs_user_subscriptions_usage_logs"
,
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
3
0
]},
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
3
1
]},
RefColumns
:
[]
*
schema
.
Column
{
UserSubscriptionsColumns
[
0
]},
RefColumns
:
[]
*
schema
.
Column
{
UserSubscriptionsColumns
[
0
]},
OnDelete
:
schema
.
SetNull
,
OnDelete
:
schema
.
SetNull
,
},
},
...
@@ -698,32 +699,32 @@ var (
...
@@ -698,32 +699,32 @@ var (
{
{
Name
:
"usagelog_user_id"
,
Name
:
"usagelog_user_id"
,
Unique
:
false
,
Unique
:
false
,
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
29
]},
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
30
]},
},
},
{
{
Name
:
"usagelog_api_key_id"
,
Name
:
"usagelog_api_key_id"
,
Unique
:
false
,
Unique
:
false
,
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
2
6
]},
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
2
7
]},
},
},
{
{
Name
:
"usagelog_account_id"
,
Name
:
"usagelog_account_id"
,
Unique
:
false
,
Unique
:
false
,
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
2
7
]},
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
2
8
]},
},
},
{
{
Name
:
"usagelog_group_id"
,
Name
:
"usagelog_group_id"
,
Unique
:
false
,
Unique
:
false
,
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
2
8
]},
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
2
9
]},
},
},
{
{
Name
:
"usagelog_subscription_id"
,
Name
:
"usagelog_subscription_id"
,
Unique
:
false
,
Unique
:
false
,
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
3
0
]},
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
3
1
]},
},
},
{
{
Name
:
"usagelog_created_at"
,
Name
:
"usagelog_created_at"
,
Unique
:
false
,
Unique
:
false
,
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
2
5
]},
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
2
6
]},
},
},
{
{
Name
:
"usagelog_model"
,
Name
:
"usagelog_model"
,
...
@@ -738,12 +739,12 @@ var (
...
@@ -738,12 +739,12 @@ var (
{
{
Name
:
"usagelog_user_id_created_at"
,
Name
:
"usagelog_user_id_created_at"
,
Unique
:
false
,
Unique
:
false
,
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
29
],
UsageLogsColumns
[
2
5
]},
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
30
],
UsageLogsColumns
[
2
6
]},
},
},
{
{
Name
:
"usagelog_api_key_id_created_at"
,
Name
:
"usagelog_api_key_id_created_at"
,
Unique
:
false
,
Unique
:
false
,
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
2
6
],
UsageLogsColumns
[
2
5
]},
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
2
7
],
UsageLogsColumns
[
2
6
]},
},
},
},
},
}
}
...
...
backend/ent/mutation.go
View file @
beceb45d
...
@@ -15061,6 +15061,7 @@ type UsageLogMutation struct {
...
@@ -15061,6 +15061,7 @@ type UsageLogMutation struct {
image_count *int
image_count *int
addimage_count *int
addimage_count *int
image_size *string
image_size *string
cache_ttl_overridden *bool
created_at *time.Time
created_at *time.Time
clearedFields map[string]struct{}
clearedFields map[string]struct{}
user *int64
user *int64
...
@@ -16687,6 +16688,42 @@ func (m *UsageLogMutation) ResetImageSize() {
...
@@ -16687,6 +16688,42 @@ func (m *UsageLogMutation) ResetImageSize() {
delete(m.clearedFields, usagelog.FieldImageSize)
delete(m.clearedFields, usagelog.FieldImageSize)
}
}
// SetCacheTTLOverridden sets the "cache_ttl_overridden" field.
func (m *UsageLogMutation) SetCacheTTLOverridden(b bool) {
m.cache_ttl_overridden = &b
}
// CacheTTLOverridden returns the value of the "cache_ttl_overridden" field in the mutation.
func (m *UsageLogMutation) CacheTTLOverridden() (r bool, exists bool) {
v := m.cache_ttl_overridden
if v == nil {
return
}
return *v, true
}
// OldCacheTTLOverridden returns the old "cache_ttl_overridden" field's value of the UsageLog entity.
// If the UsageLog object wasn't provided to the builder, the object is fetched from the database.
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *UsageLogMutation) OldCacheTTLOverridden(ctx context.Context) (v bool, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldCacheTTLOverridden is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldCacheTTLOverridden requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldCacheTTLOverridden: %w", err)
}
return oldValue.CacheTTLOverridden, nil
}
// ResetCacheTTLOverridden resets all changes to the "cache_ttl_overridden" field.
func (m *UsageLogMutation) ResetCacheTTLOverridden() {
m.cache_ttl_overridden = nil
}
// SetCreatedAt sets the "created_at" field.
// SetCreatedAt sets the "created_at" field.
func (m *UsageLogMutation) SetCreatedAt(t time.Time) {
func (m *UsageLogMutation) SetCreatedAt(t time.Time) {
m.created_at = &t
m.created_at = &t
...
@@ -16892,7 +16929,7 @@ func (m *UsageLogMutation) Type() string {
...
@@ -16892,7 +16929,7 @@ func (m *UsageLogMutation) Type() string {
// order to get all numeric fields that were incremented/decremented, call
// order to get all numeric fields that were incremented/decremented, call
// AddedFields().
// AddedFields().
func (m *UsageLogMutation) Fields() []string {
func (m *UsageLogMutation) Fields() []string {
fields := make([]string, 0, 3
0
)
fields := make([]string, 0, 3
1
)
if m.user != nil {
if m.user != nil {
fields = append(fields, usagelog.FieldUserID)
fields = append(fields, usagelog.FieldUserID)
}
}
...
@@ -16980,6 +17017,9 @@ func (m *UsageLogMutation) Fields() []string {
...
@@ -16980,6 +17017,9 @@ func (m *UsageLogMutation) Fields() []string {
if m.image_size != nil {
if m.image_size != nil {
fields = append(fields, usagelog.FieldImageSize)
fields = append(fields, usagelog.FieldImageSize)
}
}
if m.cache_ttl_overridden != nil {
fields = append(fields, usagelog.FieldCacheTTLOverridden)
}
if m.created_at != nil {
if m.created_at != nil {
fields = append(fields, usagelog.FieldCreatedAt)
fields = append(fields, usagelog.FieldCreatedAt)
}
}
...
@@ -17049,6 +17089,8 @@ func (m *UsageLogMutation) Field(name string) (ent.Value, bool) {
...
@@ -17049,6 +17089,8 @@ func (m *UsageLogMutation) Field(name string) (ent.Value, bool) {
return m.ImageCount()
return m.ImageCount()
case usagelog.FieldImageSize:
case usagelog.FieldImageSize:
return m.ImageSize()
return m.ImageSize()
case usagelog.FieldCacheTTLOverridden:
return m.CacheTTLOverridden()
case usagelog.FieldCreatedAt:
case usagelog.FieldCreatedAt:
return m.CreatedAt()
return m.CreatedAt()
}
}
...
@@ -17118,6 +17160,8 @@ func (m *UsageLogMutation) OldField(ctx context.Context, name string) (ent.Value
...
@@ -17118,6 +17160,8 @@ func (m *UsageLogMutation) OldField(ctx context.Context, name string) (ent.Value
return m.OldImageCount(ctx)
return m.OldImageCount(ctx)
case usagelog.FieldImageSize:
case usagelog.FieldImageSize:
return m.OldImageSize(ctx)
return m.OldImageSize(ctx)
case usagelog.FieldCacheTTLOverridden:
return m.OldCacheTTLOverridden(ctx)
case usagelog.FieldCreatedAt:
case usagelog.FieldCreatedAt:
return m.OldCreatedAt(ctx)
return m.OldCreatedAt(ctx)
}
}
...
@@ -17332,6 +17376,13 @@ func (m *UsageLogMutation) SetField(name string, value ent.Value) error {
...
@@ -17332,6 +17376,13 @@ func (m *UsageLogMutation) SetField(name string, value ent.Value) error {
}
}
m.SetImageSize(v)
m.SetImageSize(v)
return nil
return nil
case usagelog.FieldCacheTTLOverridden:
v, ok := value.(bool)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetCacheTTLOverridden(v)
return nil
case usagelog.FieldCreatedAt:
case usagelog.FieldCreatedAt:
v, ok := value.(time.Time)
v, ok := value.(time.Time)
if !ok {
if !ok {
...
@@ -17745,6 +17796,9 @@ func (m *UsageLogMutation) ResetField(name string) error {
...
@@ -17745,6 +17796,9 @@ func (m *UsageLogMutation) ResetField(name string) error {
case usagelog.FieldImageSize:
case usagelog.FieldImageSize:
m.ResetImageSize()
m.ResetImageSize()
return nil
return nil
case usagelog.FieldCacheTTLOverridden:
m.ResetCacheTTLOverridden()
return nil
case usagelog.FieldCreatedAt:
case usagelog.FieldCreatedAt:
m.ResetCreatedAt()
m.ResetCreatedAt()
return nil
return nil
...
...
backend/ent/runtime/runtime.go
View file @
beceb45d
...
@@ -779,8 +779,12 @@ func init() {
...
@@ -779,8 +779,12 @@ func init() {
usagelogDescImageSize
:=
usagelogFields
[
28
]
.
Descriptor
()
usagelogDescImageSize
:=
usagelogFields
[
28
]
.
Descriptor
()
// usagelog.ImageSizeValidator is a validator for the "image_size" field. It is called by the builders before save.
// usagelog.ImageSizeValidator is a validator for the "image_size" field. It is called by the builders before save.
usagelog
.
ImageSizeValidator
=
usagelogDescImageSize
.
Validators
[
0
]
.
(
func
(
string
)
error
)
usagelog
.
ImageSizeValidator
=
usagelogDescImageSize
.
Validators
[
0
]
.
(
func
(
string
)
error
)
// usagelogDescCacheTTLOverridden is the schema descriptor for cache_ttl_overridden field.
usagelogDescCacheTTLOverridden
:=
usagelogFields
[
29
]
.
Descriptor
()
// usagelog.DefaultCacheTTLOverridden holds the default value on creation for the cache_ttl_overridden field.
usagelog
.
DefaultCacheTTLOverridden
=
usagelogDescCacheTTLOverridden
.
Default
.
(
bool
)
// usagelogDescCreatedAt is the schema descriptor for created_at field.
// usagelogDescCreatedAt is the schema descriptor for created_at field.
usagelogDescCreatedAt
:=
usagelogFields
[
29
]
.
Descriptor
()
usagelogDescCreatedAt
:=
usagelogFields
[
30
]
.
Descriptor
()
// usagelog.DefaultCreatedAt holds the default value on creation for the created_at field.
// usagelog.DefaultCreatedAt holds the default value on creation for the created_at field.
usagelog
.
DefaultCreatedAt
=
usagelogDescCreatedAt
.
Default
.
(
func
()
time
.
Time
)
usagelog
.
DefaultCreatedAt
=
usagelogDescCreatedAt
.
Default
.
(
func
()
time
.
Time
)
userMixin
:=
schema
.
User
{}
.
Mixin
()
userMixin
:=
schema
.
User
{}
.
Mixin
()
...
...
backend/ent/schema/usage_log.go
View file @
beceb45d
...
@@ -119,6 +119,10 @@ func (UsageLog) Fields() []ent.Field {
...
@@ -119,6 +119,10 @@ func (UsageLog) Fields() []ent.Field {
Optional
()
.
Optional
()
.
Nillable
(),
Nillable
(),
// Cache TTL Override 标记(管理员强制替换了缓存 TTL 计费)
field
.
Bool
(
"cache_ttl_overridden"
)
.
Default
(
false
),
// 时间戳(只有 created_at,日志不可修改)
// 时间戳(只有 created_at,日志不可修改)
field
.
Time
(
"created_at"
)
.
field
.
Time
(
"created_at"
)
.
Default
(
time
.
Now
)
.
Default
(
time
.
Now
)
.
...
...
backend/ent/usagelog.go
View file @
beceb45d
...
@@ -80,6 +80,8 @@ type UsageLog struct {
...
@@ -80,6 +80,8 @@ type UsageLog struct {
ImageCount
int
`json:"image_count,omitempty"`
ImageCount
int
`json:"image_count,omitempty"`
// ImageSize holds the value of the "image_size" field.
// ImageSize holds the value of the "image_size" field.
ImageSize
*
string
`json:"image_size,omitempty"`
ImageSize
*
string
`json:"image_size,omitempty"`
// CacheTTLOverridden holds the value of the "cache_ttl_overridden" field.
CacheTTLOverridden
bool
`json:"cache_ttl_overridden,omitempty"`
// CreatedAt holds the value of the "created_at" field.
// CreatedAt holds the value of the "created_at" field.
CreatedAt
time
.
Time
`json:"created_at,omitempty"`
CreatedAt
time
.
Time
`json:"created_at,omitempty"`
// Edges holds the relations/edges for other nodes in the graph.
// Edges holds the relations/edges for other nodes in the graph.
...
@@ -165,7 +167,7 @@ func (*UsageLog) scanValues(columns []string) ([]any, error) {
...
@@ -165,7 +167,7 @@ func (*UsageLog) scanValues(columns []string) ([]any, error) {
values
:=
make
([]
any
,
len
(
columns
))
values
:=
make
([]
any
,
len
(
columns
))
for
i
:=
range
columns
{
for
i
:=
range
columns
{
switch
columns
[
i
]
{
switch
columns
[
i
]
{
case
usagelog
.
FieldStream
:
case
usagelog
.
FieldStream
,
usagelog
.
FieldCacheTTLOverridden
:
values
[
i
]
=
new
(
sql
.
NullBool
)
values
[
i
]
=
new
(
sql
.
NullBool
)
case
usagelog
.
FieldInputCost
,
usagelog
.
FieldOutputCost
,
usagelog
.
FieldCacheCreationCost
,
usagelog
.
FieldCacheReadCost
,
usagelog
.
FieldTotalCost
,
usagelog
.
FieldActualCost
,
usagelog
.
FieldRateMultiplier
,
usagelog
.
FieldAccountRateMultiplier
:
case
usagelog
.
FieldInputCost
,
usagelog
.
FieldOutputCost
,
usagelog
.
FieldCacheCreationCost
,
usagelog
.
FieldCacheReadCost
,
usagelog
.
FieldTotalCost
,
usagelog
.
FieldActualCost
,
usagelog
.
FieldRateMultiplier
,
usagelog
.
FieldAccountRateMultiplier
:
values
[
i
]
=
new
(
sql
.
NullFloat64
)
values
[
i
]
=
new
(
sql
.
NullFloat64
)
...
@@ -378,6 +380,12 @@ func (_m *UsageLog) assignValues(columns []string, values []any) error {
...
@@ -378,6 +380,12 @@ func (_m *UsageLog) assignValues(columns []string, values []any) error {
_m
.
ImageSize
=
new
(
string
)
_m
.
ImageSize
=
new
(
string
)
*
_m
.
ImageSize
=
value
.
String
*
_m
.
ImageSize
=
value
.
String
}
}
case
usagelog
.
FieldCacheTTLOverridden
:
if
value
,
ok
:=
values
[
i
]
.
(
*
sql
.
NullBool
);
!
ok
{
return
fmt
.
Errorf
(
"unexpected type %T for field cache_ttl_overridden"
,
values
[
i
])
}
else
if
value
.
Valid
{
_m
.
CacheTTLOverridden
=
value
.
Bool
}
case
usagelog
.
FieldCreatedAt
:
case
usagelog
.
FieldCreatedAt
:
if
value
,
ok
:=
values
[
i
]
.
(
*
sql
.
NullTime
);
!
ok
{
if
value
,
ok
:=
values
[
i
]
.
(
*
sql
.
NullTime
);
!
ok
{
return
fmt
.
Errorf
(
"unexpected type %T for field created_at"
,
values
[
i
])
return
fmt
.
Errorf
(
"unexpected type %T for field created_at"
,
values
[
i
])
...
@@ -548,6 +556,9 @@ func (_m *UsageLog) String() string {
...
@@ -548,6 +556,9 @@ func (_m *UsageLog) String() string {
builder
.
WriteString
(
*
v
)
builder
.
WriteString
(
*
v
)
}
}
builder
.
WriteString
(
", "
)
builder
.
WriteString
(
", "
)
builder
.
WriteString
(
"cache_ttl_overridden="
)
builder
.
WriteString
(
fmt
.
Sprintf
(
"%v"
,
_m
.
CacheTTLOverridden
))
builder
.
WriteString
(
", "
)
builder
.
WriteString
(
"created_at="
)
builder
.
WriteString
(
"created_at="
)
builder
.
WriteString
(
_m
.
CreatedAt
.
Format
(
time
.
ANSIC
))
builder
.
WriteString
(
_m
.
CreatedAt
.
Format
(
time
.
ANSIC
))
builder
.
WriteByte
(
')'
)
builder
.
WriteByte
(
')'
)
...
...
backend/ent/usagelog/usagelog.go
View file @
beceb45d
...
@@ -72,6 +72,8 @@ const (
...
@@ -72,6 +72,8 @@ const (
FieldImageCount
=
"image_count"
FieldImageCount
=
"image_count"
// FieldImageSize holds the string denoting the image_size field in the database.
// FieldImageSize holds the string denoting the image_size field in the database.
FieldImageSize
=
"image_size"
FieldImageSize
=
"image_size"
// FieldCacheTTLOverridden holds the string denoting the cache_ttl_overridden field in the database.
FieldCacheTTLOverridden
=
"cache_ttl_overridden"
// FieldCreatedAt holds the string denoting the created_at field in the database.
// FieldCreatedAt holds the string denoting the created_at field in the database.
FieldCreatedAt
=
"created_at"
FieldCreatedAt
=
"created_at"
// EdgeUser holds the string denoting the user edge name in mutations.
// EdgeUser holds the string denoting the user edge name in mutations.
...
@@ -155,6 +157,7 @@ var Columns = []string{
...
@@ -155,6 +157,7 @@ var Columns = []string{
FieldIPAddress
,
FieldIPAddress
,
FieldImageCount
,
FieldImageCount
,
FieldImageSize
,
FieldImageSize
,
FieldCacheTTLOverridden
,
FieldCreatedAt
,
FieldCreatedAt
,
}
}
...
@@ -211,6 +214,8 @@ var (
...
@@ -211,6 +214,8 @@ var (
DefaultImageCount
int
DefaultImageCount
int
// ImageSizeValidator is a validator for the "image_size" field. It is called by the builders before save.
// ImageSizeValidator is a validator for the "image_size" field. It is called by the builders before save.
ImageSizeValidator
func
(
string
)
error
ImageSizeValidator
func
(
string
)
error
// DefaultCacheTTLOverridden holds the default value on creation for the "cache_ttl_overridden" field.
DefaultCacheTTLOverridden
bool
// DefaultCreatedAt holds the default value on creation for the "created_at" field.
// DefaultCreatedAt holds the default value on creation for the "created_at" field.
DefaultCreatedAt
func
()
time
.
Time
DefaultCreatedAt
func
()
time
.
Time
)
)
...
@@ -368,6 +373,11 @@ func ByImageSize(opts ...sql.OrderTermOption) OrderOption {
...
@@ -368,6 +373,11 @@ func ByImageSize(opts ...sql.OrderTermOption) OrderOption {
return
sql
.
OrderByField
(
FieldImageSize
,
opts
...
)
.
ToFunc
()
return
sql
.
OrderByField
(
FieldImageSize
,
opts
...
)
.
ToFunc
()
}
}
// ByCacheTTLOverridden orders the results by the cache_ttl_overridden field.
func
ByCacheTTLOverridden
(
opts
...
sql
.
OrderTermOption
)
OrderOption
{
return
sql
.
OrderByField
(
FieldCacheTTLOverridden
,
opts
...
)
.
ToFunc
()
}
// ByCreatedAt orders the results by the created_at field.
// ByCreatedAt orders the results by the created_at field.
func
ByCreatedAt
(
opts
...
sql
.
OrderTermOption
)
OrderOption
{
func
ByCreatedAt
(
opts
...
sql
.
OrderTermOption
)
OrderOption
{
return
sql
.
OrderByField
(
FieldCreatedAt
,
opts
...
)
.
ToFunc
()
return
sql
.
OrderByField
(
FieldCreatedAt
,
opts
...
)
.
ToFunc
()
...
...
backend/ent/usagelog/where.go
View file @
beceb45d
...
@@ -200,6 +200,11 @@ func ImageSize(v string) predicate.UsageLog {
...
@@ -200,6 +200,11 @@ func ImageSize(v string) predicate.UsageLog {
return
predicate
.
UsageLog
(
sql
.
FieldEQ
(
FieldImageSize
,
v
))
return
predicate
.
UsageLog
(
sql
.
FieldEQ
(
FieldImageSize
,
v
))
}
}
// CacheTTLOverridden applies equality check predicate on the "cache_ttl_overridden" field. It's identical to CacheTTLOverriddenEQ.
func
CacheTTLOverridden
(
v
bool
)
predicate
.
UsageLog
{
return
predicate
.
UsageLog
(
sql
.
FieldEQ
(
FieldCacheTTLOverridden
,
v
))
}
// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ.
// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ.
func
CreatedAt
(
v
time
.
Time
)
predicate
.
UsageLog
{
func
CreatedAt
(
v
time
.
Time
)
predicate
.
UsageLog
{
return
predicate
.
UsageLog
(
sql
.
FieldEQ
(
FieldCreatedAt
,
v
))
return
predicate
.
UsageLog
(
sql
.
FieldEQ
(
FieldCreatedAt
,
v
))
...
@@ -1440,6 +1445,16 @@ func ImageSizeContainsFold(v string) predicate.UsageLog {
...
@@ -1440,6 +1445,16 @@ func ImageSizeContainsFold(v string) predicate.UsageLog {
return
predicate
.
UsageLog
(
sql
.
FieldContainsFold
(
FieldImageSize
,
v
))
return
predicate
.
UsageLog
(
sql
.
FieldContainsFold
(
FieldImageSize
,
v
))
}
}
// CacheTTLOverriddenEQ applies the EQ predicate on the "cache_ttl_overridden" field.
func
CacheTTLOverriddenEQ
(
v
bool
)
predicate
.
UsageLog
{
return
predicate
.
UsageLog
(
sql
.
FieldEQ
(
FieldCacheTTLOverridden
,
v
))
}
// CacheTTLOverriddenNEQ applies the NEQ predicate on the "cache_ttl_overridden" field.
func
CacheTTLOverriddenNEQ
(
v
bool
)
predicate
.
UsageLog
{
return
predicate
.
UsageLog
(
sql
.
FieldNEQ
(
FieldCacheTTLOverridden
,
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
.
UsageLog
{
func
CreatedAtEQ
(
v
time
.
Time
)
predicate
.
UsageLog
{
return
predicate
.
UsageLog
(
sql
.
FieldEQ
(
FieldCreatedAt
,
v
))
return
predicate
.
UsageLog
(
sql
.
FieldEQ
(
FieldCreatedAt
,
v
))
...
...
backend/ent/usagelog_create.go
View file @
beceb45d
...
@@ -393,6 +393,20 @@ func (_c *UsageLogCreate) SetNillableImageSize(v *string) *UsageLogCreate {
...
@@ -393,6 +393,20 @@ func (_c *UsageLogCreate) SetNillableImageSize(v *string) *UsageLogCreate {
return
_c
return
_c
}
}
// SetCacheTTLOverridden sets the "cache_ttl_overridden" field.
func
(
_c
*
UsageLogCreate
)
SetCacheTTLOverridden
(
v
bool
)
*
UsageLogCreate
{
_c
.
mutation
.
SetCacheTTLOverridden
(
v
)
return
_c
}
// SetNillableCacheTTLOverridden sets the "cache_ttl_overridden" field if the given value is not nil.
func
(
_c
*
UsageLogCreate
)
SetNillableCacheTTLOverridden
(
v
*
bool
)
*
UsageLogCreate
{
if
v
!=
nil
{
_c
.
SetCacheTTLOverridden
(
*
v
)
}
return
_c
}
// SetCreatedAt sets the "created_at" field.
// SetCreatedAt sets the "created_at" field.
func
(
_c
*
UsageLogCreate
)
SetCreatedAt
(
v
time
.
Time
)
*
UsageLogCreate
{
func
(
_c
*
UsageLogCreate
)
SetCreatedAt
(
v
time
.
Time
)
*
UsageLogCreate
{
_c
.
mutation
.
SetCreatedAt
(
v
)
_c
.
mutation
.
SetCreatedAt
(
v
)
...
@@ -531,6 +545,10 @@ func (_c *UsageLogCreate) defaults() {
...
@@ -531,6 +545,10 @@ func (_c *UsageLogCreate) defaults() {
v
:=
usagelog
.
DefaultImageCount
v
:=
usagelog
.
DefaultImageCount
_c
.
mutation
.
SetImageCount
(
v
)
_c
.
mutation
.
SetImageCount
(
v
)
}
}
if
_
,
ok
:=
_c
.
mutation
.
CacheTTLOverridden
();
!
ok
{
v
:=
usagelog
.
DefaultCacheTTLOverridden
_c
.
mutation
.
SetCacheTTLOverridden
(
v
)
}
if
_
,
ok
:=
_c
.
mutation
.
CreatedAt
();
!
ok
{
if
_
,
ok
:=
_c
.
mutation
.
CreatedAt
();
!
ok
{
v
:=
usagelog
.
DefaultCreatedAt
()
v
:=
usagelog
.
DefaultCreatedAt
()
_c
.
mutation
.
SetCreatedAt
(
v
)
_c
.
mutation
.
SetCreatedAt
(
v
)
...
@@ -627,6 +645,9 @@ func (_c *UsageLogCreate) check() error {
...
@@ -627,6 +645,9 @@ func (_c *UsageLogCreate) check() error {
return
&
ValidationError
{
Name
:
"image_size"
,
err
:
fmt
.
Errorf
(
`ent: validator failed for field "UsageLog.image_size": %w`
,
err
)}
return
&
ValidationError
{
Name
:
"image_size"
,
err
:
fmt
.
Errorf
(
`ent: validator failed for field "UsageLog.image_size": %w`
,
err
)}
}
}
}
}
if
_
,
ok
:=
_c
.
mutation
.
CacheTTLOverridden
();
!
ok
{
return
&
ValidationError
{
Name
:
"cache_ttl_overridden"
,
err
:
errors
.
New
(
`ent: missing required field "UsageLog.cache_ttl_overridden"`
)}
}
if
_
,
ok
:=
_c
.
mutation
.
CreatedAt
();
!
ok
{
if
_
,
ok
:=
_c
.
mutation
.
CreatedAt
();
!
ok
{
return
&
ValidationError
{
Name
:
"created_at"
,
err
:
errors
.
New
(
`ent: missing required field "UsageLog.created_at"`
)}
return
&
ValidationError
{
Name
:
"created_at"
,
err
:
errors
.
New
(
`ent: missing required field "UsageLog.created_at"`
)}
}
}
...
@@ -762,6 +783,10 @@ func (_c *UsageLogCreate) createSpec() (*UsageLog, *sqlgraph.CreateSpec) {
...
@@ -762,6 +783,10 @@ func (_c *UsageLogCreate) createSpec() (*UsageLog, *sqlgraph.CreateSpec) {
_spec
.
SetField
(
usagelog
.
FieldImageSize
,
field
.
TypeString
,
value
)
_spec
.
SetField
(
usagelog
.
FieldImageSize
,
field
.
TypeString
,
value
)
_node
.
ImageSize
=
&
value
_node
.
ImageSize
=
&
value
}
}
if
value
,
ok
:=
_c
.
mutation
.
CacheTTLOverridden
();
ok
{
_spec
.
SetField
(
usagelog
.
FieldCacheTTLOverridden
,
field
.
TypeBool
,
value
)
_node
.
CacheTTLOverridden
=
value
}
if
value
,
ok
:=
_c
.
mutation
.
CreatedAt
();
ok
{
if
value
,
ok
:=
_c
.
mutation
.
CreatedAt
();
ok
{
_spec
.
SetField
(
usagelog
.
FieldCreatedAt
,
field
.
TypeTime
,
value
)
_spec
.
SetField
(
usagelog
.
FieldCreatedAt
,
field
.
TypeTime
,
value
)
_node
.
CreatedAt
=
value
_node
.
CreatedAt
=
value
...
@@ -1407,6 +1432,18 @@ func (u *UsageLogUpsert) ClearImageSize() *UsageLogUpsert {
...
@@ -1407,6 +1432,18 @@ func (u *UsageLogUpsert) ClearImageSize() *UsageLogUpsert {
return
u
return
u
}
}
// SetCacheTTLOverridden sets the "cache_ttl_overridden" field.
func
(
u
*
UsageLogUpsert
)
SetCacheTTLOverridden
(
v
bool
)
*
UsageLogUpsert
{
u
.
Set
(
usagelog
.
FieldCacheTTLOverridden
,
v
)
return
u
}
// UpdateCacheTTLOverridden sets the "cache_ttl_overridden" field to the value that was provided on create.
func
(
u
*
UsageLogUpsert
)
UpdateCacheTTLOverridden
()
*
UsageLogUpsert
{
u
.
SetExcluded
(
usagelog
.
FieldCacheTTLOverridden
)
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:
//
//
...
@@ -2040,6 +2077,20 @@ func (u *UsageLogUpsertOne) ClearImageSize() *UsageLogUpsertOne {
...
@@ -2040,6 +2077,20 @@ func (u *UsageLogUpsertOne) ClearImageSize() *UsageLogUpsertOne {
})
})
}
}
// SetCacheTTLOverridden sets the "cache_ttl_overridden" field.
func
(
u
*
UsageLogUpsertOne
)
SetCacheTTLOverridden
(
v
bool
)
*
UsageLogUpsertOne
{
return
u
.
Update
(
func
(
s
*
UsageLogUpsert
)
{
s
.
SetCacheTTLOverridden
(
v
)
})
}
// UpdateCacheTTLOverridden sets the "cache_ttl_overridden" field to the value that was provided on create.
func
(
u
*
UsageLogUpsertOne
)
UpdateCacheTTLOverridden
()
*
UsageLogUpsertOne
{
return
u
.
Update
(
func
(
s
*
UsageLogUpsert
)
{
s
.
UpdateCacheTTLOverridden
()
})
}
// Exec executes the query.
// Exec executes the query.
func
(
u
*
UsageLogUpsertOne
)
Exec
(
ctx
context
.
Context
)
error
{
func
(
u
*
UsageLogUpsertOne
)
Exec
(
ctx
context
.
Context
)
error
{
if
len
(
u
.
create
.
conflict
)
==
0
{
if
len
(
u
.
create
.
conflict
)
==
0
{
...
@@ -2839,6 +2890,20 @@ func (u *UsageLogUpsertBulk) ClearImageSize() *UsageLogUpsertBulk {
...
@@ -2839,6 +2890,20 @@ func (u *UsageLogUpsertBulk) ClearImageSize() *UsageLogUpsertBulk {
})
})
}
}
// SetCacheTTLOverridden sets the "cache_ttl_overridden" field.
func
(
u
*
UsageLogUpsertBulk
)
SetCacheTTLOverridden
(
v
bool
)
*
UsageLogUpsertBulk
{
return
u
.
Update
(
func
(
s
*
UsageLogUpsert
)
{
s
.
SetCacheTTLOverridden
(
v
)
})
}
// UpdateCacheTTLOverridden sets the "cache_ttl_overridden" field to the value that was provided on create.
func
(
u
*
UsageLogUpsertBulk
)
UpdateCacheTTLOverridden
()
*
UsageLogUpsertBulk
{
return
u
.
Update
(
func
(
s
*
UsageLogUpsert
)
{
s
.
UpdateCacheTTLOverridden
()
})
}
// Exec executes the query.
// Exec executes the query.
func
(
u
*
UsageLogUpsertBulk
)
Exec
(
ctx
context
.
Context
)
error
{
func
(
u
*
UsageLogUpsertBulk
)
Exec
(
ctx
context
.
Context
)
error
{
if
u
.
create
.
err
!=
nil
{
if
u
.
create
.
err
!=
nil
{
...
...
backend/ent/usagelog_update.go
View file @
beceb45d
...
@@ -612,6 +612,20 @@ func (_u *UsageLogUpdate) ClearImageSize() *UsageLogUpdate {
...
@@ -612,6 +612,20 @@ func (_u *UsageLogUpdate) ClearImageSize() *UsageLogUpdate {
return
_u
return
_u
}
}
// SetCacheTTLOverridden sets the "cache_ttl_overridden" field.
func
(
_u
*
UsageLogUpdate
)
SetCacheTTLOverridden
(
v
bool
)
*
UsageLogUpdate
{
_u
.
mutation
.
SetCacheTTLOverridden
(
v
)
return
_u
}
// SetNillableCacheTTLOverridden sets the "cache_ttl_overridden" field if the given value is not nil.
func
(
_u
*
UsageLogUpdate
)
SetNillableCacheTTLOverridden
(
v
*
bool
)
*
UsageLogUpdate
{
if
v
!=
nil
{
_u
.
SetCacheTTLOverridden
(
*
v
)
}
return
_u
}
// SetUser sets the "user" edge to the User entity.
// SetUser sets the "user" edge to the User entity.
func
(
_u
*
UsageLogUpdate
)
SetUser
(
v
*
User
)
*
UsageLogUpdate
{
func
(
_u
*
UsageLogUpdate
)
SetUser
(
v
*
User
)
*
UsageLogUpdate
{
return
_u
.
SetUserID
(
v
.
ID
)
return
_u
.
SetUserID
(
v
.
ID
)
...
@@ -894,6 +908,9 @@ func (_u *UsageLogUpdate) sqlSave(ctx context.Context) (_node int, err error) {
...
@@ -894,6 +908,9 @@ func (_u *UsageLogUpdate) sqlSave(ctx context.Context) (_node int, err error) {
if
_u
.
mutation
.
ImageSizeCleared
()
{
if
_u
.
mutation
.
ImageSizeCleared
()
{
_spec
.
ClearField
(
usagelog
.
FieldImageSize
,
field
.
TypeString
)
_spec
.
ClearField
(
usagelog
.
FieldImageSize
,
field
.
TypeString
)
}
}
if
value
,
ok
:=
_u
.
mutation
.
CacheTTLOverridden
();
ok
{
_spec
.
SetField
(
usagelog
.
FieldCacheTTLOverridden
,
field
.
TypeBool
,
value
)
}
if
_u
.
mutation
.
UserCleared
()
{
if
_u
.
mutation
.
UserCleared
()
{
edge
:=
&
sqlgraph
.
EdgeSpec
{
edge
:=
&
sqlgraph
.
EdgeSpec
{
Rel
:
sqlgraph
.
M2O
,
Rel
:
sqlgraph
.
M2O
,
...
@@ -1639,6 +1656,20 @@ func (_u *UsageLogUpdateOne) ClearImageSize() *UsageLogUpdateOne {
...
@@ -1639,6 +1656,20 @@ func (_u *UsageLogUpdateOne) ClearImageSize() *UsageLogUpdateOne {
return
_u
return
_u
}
}
// SetCacheTTLOverridden sets the "cache_ttl_overridden" field.
func
(
_u
*
UsageLogUpdateOne
)
SetCacheTTLOverridden
(
v
bool
)
*
UsageLogUpdateOne
{
_u
.
mutation
.
SetCacheTTLOverridden
(
v
)
return
_u
}
// SetNillableCacheTTLOverridden sets the "cache_ttl_overridden" field if the given value is not nil.
func
(
_u
*
UsageLogUpdateOne
)
SetNillableCacheTTLOverridden
(
v
*
bool
)
*
UsageLogUpdateOne
{
if
v
!=
nil
{
_u
.
SetCacheTTLOverridden
(
*
v
)
}
return
_u
}
// SetUser sets the "user" edge to the User entity.
// SetUser sets the "user" edge to the User entity.
func
(
_u
*
UsageLogUpdateOne
)
SetUser
(
v
*
User
)
*
UsageLogUpdateOne
{
func
(
_u
*
UsageLogUpdateOne
)
SetUser
(
v
*
User
)
*
UsageLogUpdateOne
{
return
_u
.
SetUserID
(
v
.
ID
)
return
_u
.
SetUserID
(
v
.
ID
)
...
@@ -1951,6 +1982,9 @@ func (_u *UsageLogUpdateOne) sqlSave(ctx context.Context) (_node *UsageLog, err
...
@@ -1951,6 +1982,9 @@ func (_u *UsageLogUpdateOne) sqlSave(ctx context.Context) (_node *UsageLog, err
if
_u
.
mutation
.
ImageSizeCleared
()
{
if
_u
.
mutation
.
ImageSizeCleared
()
{
_spec
.
ClearField
(
usagelog
.
FieldImageSize
,
field
.
TypeString
)
_spec
.
ClearField
(
usagelog
.
FieldImageSize
,
field
.
TypeString
)
}
}
if
value
,
ok
:=
_u
.
mutation
.
CacheTTLOverridden
();
ok
{
_spec
.
SetField
(
usagelog
.
FieldCacheTTLOverridden
,
field
.
TypeBool
,
value
)
}
if
_u
.
mutation
.
UserCleared
()
{
if
_u
.
mutation
.
UserCleared
()
{
edge
:=
&
sqlgraph
.
EdgeSpec
{
edge
:=
&
sqlgraph
.
EdgeSpec
{
Rel
:
sqlgraph
.
M2O
,
Rel
:
sqlgraph
.
M2O
,
...
...
backend/internal/handler/dto/mappers.go
View file @
beceb45d
...
@@ -211,6 +211,13 @@ func AccountFromServiceShallow(a *service.Account) *Account {
...
@@ -211,6 +211,13 @@ func AccountFromServiceShallow(a *service.Account) *Account {
enabled
:=
true
enabled
:=
true
out
.
EnableSessionIDMasking
=
&
enabled
out
.
EnableSessionIDMasking
=
&
enabled
}
}
// 缓存 TTL 强制替换
if
a
.
IsCacheTTLOverrideEnabled
()
{
enabled
:=
true
out
.
CacheTTLOverrideEnabled
=
&
enabled
target
:=
a
.
GetCacheTTLOverrideTarget
()
out
.
CacheTTLOverrideTarget
=
&
target
}
}
}
return
out
return
out
...
@@ -398,6 +405,7 @@ func usageLogFromServiceUser(l *service.UsageLog) UsageLog {
...
@@ -398,6 +405,7 @@ func usageLogFromServiceUser(l *service.UsageLog) UsageLog {
ImageCount
:
l
.
ImageCount
,
ImageCount
:
l
.
ImageCount
,
ImageSize
:
l
.
ImageSize
,
ImageSize
:
l
.
ImageSize
,
UserAgent
:
l
.
UserAgent
,
UserAgent
:
l
.
UserAgent
,
CacheTTLOverridden
:
l
.
CacheTTLOverridden
,
CreatedAt
:
l
.
CreatedAt
,
CreatedAt
:
l
.
CreatedAt
,
User
:
UserFromServiceShallow
(
l
.
User
),
User
:
UserFromServiceShallow
(
l
.
User
),
APIKey
:
APIKeyFromService
(
l
.
APIKey
),
APIKey
:
APIKeyFromService
(
l
.
APIKey
),
...
...
backend/internal/handler/dto/types.go
View file @
beceb45d
...
@@ -150,6 +150,11 @@ type Account struct {
...
@@ -150,6 +150,11 @@ type Account struct {
// 从 extra 字段提取,方便前端显示和编辑
// 从 extra 字段提取,方便前端显示和编辑
EnableSessionIDMasking
*
bool
`json:"session_id_masking_enabled,omitempty"`
EnableSessionIDMasking
*
bool
`json:"session_id_masking_enabled,omitempty"`
// 缓存 TTL 强制替换(仅 Anthropic OAuth/SetupToken 账号有效)
// 启用后将所有 cache creation tokens 归入指定的 TTL 类型计费
CacheTTLOverrideEnabled
*
bool
`json:"cache_ttl_override_enabled,omitempty"`
CacheTTLOverrideTarget
*
string
`json:"cache_ttl_override_target,omitempty"`
Proxy
*
Proxy
`json:"proxy,omitempty"`
Proxy
*
Proxy
`json:"proxy,omitempty"`
AccountGroups
[]
AccountGroup
`json:"account_groups,omitempty"`
AccountGroups
[]
AccountGroup
`json:"account_groups,omitempty"`
...
@@ -273,6 +278,9 @@ type UsageLog struct {
...
@@ -273,6 +278,9 @@ type UsageLog struct {
// User-Agent
// User-Agent
UserAgent
*
string
`json:"user_agent"`
UserAgent
*
string
`json:"user_agent"`
// Cache TTL Override 标记
CacheTTLOverridden
bool
`json:"cache_ttl_overridden"`
CreatedAt
time
.
Time
`json:"created_at"`
CreatedAt
time
.
Time
`json:"created_at"`
User
*
User
`json:"user,omitempty"`
User
*
User
`json:"user,omitempty"`
...
...
backend/internal/repository/usage_log_repo.go
View file @
beceb45d
...
@@ -22,7 +22,7 @@ import (
...
@@ -22,7 +22,7 @@ import (
"github.com/lib/pq"
"github.com/lib/pq"
)
)
const
usageLogSelectColumns
=
"id, user_id, api_key_id, account_id, request_id, model, group_id, subscription_id, input_tokens, output_tokens, cache_creation_tokens, cache_read_tokens, cache_creation_5m_tokens, cache_creation_1h_tokens, input_cost, output_cost, cache_creation_cost, cache_read_cost, total_cost, actual_cost, rate_multiplier, account_rate_multiplier, billing_type, stream, duration_ms, first_token_ms, user_agent, ip_address, image_count, image_size, reasoning_effort, created_at"
const
usageLogSelectColumns
=
"id, user_id, api_key_id, account_id, request_id, model, group_id, subscription_id, input_tokens, output_tokens, cache_creation_tokens, cache_read_tokens, cache_creation_5m_tokens, cache_creation_1h_tokens, input_cost, output_cost, cache_creation_cost, cache_read_cost, total_cost, actual_cost, rate_multiplier, account_rate_multiplier, billing_type, stream, duration_ms, first_token_ms, user_agent, ip_address, image_count, image_size, reasoning_effort,
cache_ttl_overridden,
created_at"
type
usageLogRepository
struct
{
type
usageLogRepository
struct
{
client
*
dbent
.
Client
client
*
dbent
.
Client
...
@@ -115,6 +115,7 @@ func (r *usageLogRepository) Create(ctx context.Context, log *service.UsageLog)
...
@@ -115,6 +115,7 @@ func (r *usageLogRepository) Create(ctx context.Context, log *service.UsageLog)
image_count,
image_count,
image_size,
image_size,
reasoning_effort,
reasoning_effort,
cache_ttl_overridden,
created_at
created_at
) VALUES (
) VALUES (
$1, $2, $3, $4, $5,
$1, $2, $3, $4, $5,
...
@@ -122,7 +123,7 @@ func (r *usageLogRepository) Create(ctx context.Context, log *service.UsageLog)
...
@@ -122,7 +123,7 @@ func (r *usageLogRepository) Create(ctx context.Context, log *service.UsageLog)
$8, $9, $10, $11,
$8, $9, $10, $11,
$12, $13,
$12, $13,
$14, $15, $16, $17, $18, $19,
$14, $15, $16, $17, $18, $19,
$20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31
$20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31
, $32
)
)
ON CONFLICT (request_id, api_key_id) DO NOTHING
ON CONFLICT (request_id, api_key_id) DO NOTHING
RETURNING id, created_at
RETURNING id, created_at
...
@@ -173,6 +174,7 @@ func (r *usageLogRepository) Create(ctx context.Context, log *service.UsageLog)
...
@@ -173,6 +174,7 @@ func (r *usageLogRepository) Create(ctx context.Context, log *service.UsageLog)
log
.
ImageCount
,
log
.
ImageCount
,
imageSize
,
imageSize
,
reasoningEffort
,
reasoningEffort
,
log
.
CacheTTLOverridden
,
createdAt
,
createdAt
,
}
}
if
err
:=
scanSingleRow
(
ctx
,
sqlq
,
query
,
args
,
&
log
.
ID
,
&
log
.
CreatedAt
);
err
!=
nil
{
if
err
:=
scanSingleRow
(
ctx
,
sqlq
,
query
,
args
,
&
log
.
ID
,
&
log
.
CreatedAt
);
err
!=
nil
{
...
@@ -2195,6 +2197,7 @@ func scanUsageLog(scanner interface{ Scan(...any) error }) (*service.UsageLog, e
...
@@ -2195,6 +2197,7 @@ func scanUsageLog(scanner interface{ Scan(...any) error }) (*service.UsageLog, e
imageCount
int
imageCount
int
imageSize
sql
.
NullString
imageSize
sql
.
NullString
reasoningEffort
sql
.
NullString
reasoningEffort
sql
.
NullString
cacheTTLOverridden
bool
createdAt
time
.
Time
createdAt
time
.
Time
)
)
...
@@ -2230,6 +2233,7 @@ func scanUsageLog(scanner interface{ Scan(...any) error }) (*service.UsageLog, e
...
@@ -2230,6 +2233,7 @@ func scanUsageLog(scanner interface{ Scan(...any) error }) (*service.UsageLog, e
&
imageCount
,
&
imageCount
,
&
imageSize
,
&
imageSize
,
&
reasoningEffort
,
&
reasoningEffort
,
&
cacheTTLOverridden
,
&
createdAt
,
&
createdAt
,
);
err
!=
nil
{
);
err
!=
nil
{
return
nil
,
err
return
nil
,
err
...
@@ -2258,6 +2262,7 @@ func scanUsageLog(scanner interface{ Scan(...any) error }) (*service.UsageLog, e
...
@@ -2258,6 +2262,7 @@ func scanUsageLog(scanner interface{ Scan(...any) error }) (*service.UsageLog, e
BillingType
:
int8
(
billingType
),
BillingType
:
int8
(
billingType
),
Stream
:
stream
,
Stream
:
stream
,
ImageCount
:
imageCount
,
ImageCount
:
imageCount
,
CacheTTLOverridden
:
cacheTTLOverridden
,
CreatedAt
:
createdAt
,
CreatedAt
:
createdAt
,
}
}
...
...
backend/internal/server/api_contract_test.go
View file @
beceb45d
...
@@ -401,6 +401,7 @@ func TestAPIContracts(t *testing.T) {
...
@@ -401,6 +401,7 @@ func TestAPIContracts(t *testing.T) {
"first_token_ms": 50,
"first_token_ms": 50,
"image_count": 0,
"image_count": 0,
"image_size": null,
"image_size": null,
"cache_ttl_overridden": false,
"created_at": "2025-01-02T03:04:05Z",
"created_at": "2025-01-02T03:04:05Z",
"user_agent": null
"user_agent": null
}
}
...
...
backend/internal/service/account.go
View file @
beceb45d
...
@@ -752,6 +752,38 @@ func (a *Account) IsSessionIDMaskingEnabled() bool {
...
@@ -752,6 +752,38 @@ func (a *Account) IsSessionIDMaskingEnabled() bool {
return
false
return
false
}
}
// IsCacheTTLOverrideEnabled 检查是否启用缓存 TTL 强制替换
// 仅适用于 Anthropic OAuth/SetupToken 类型账号
// 启用后将所有 cache creation tokens 归入指定的 TTL 类型(5m 或 1h)
func
(
a
*
Account
)
IsCacheTTLOverrideEnabled
()
bool
{
if
!
a
.
IsAnthropicOAuthOrSetupToken
()
{
return
false
}
if
a
.
Extra
==
nil
{
return
false
}
if
v
,
ok
:=
a
.
Extra
[
"cache_ttl_override_enabled"
];
ok
{
if
enabled
,
ok
:=
v
.
(
bool
);
ok
{
return
enabled
}
}
return
false
}
// GetCacheTTLOverrideTarget 获取缓存 TTL 强制替换的目标类型
// 返回 "5m" 或 "1h",默认 "5m"
func
(
a
*
Account
)
GetCacheTTLOverrideTarget
()
string
{
if
a
.
Extra
==
nil
{
return
"5m"
}
if
v
,
ok
:=
a
.
Extra
[
"cache_ttl_override_target"
];
ok
{
if
target
,
ok
:=
v
.
(
string
);
ok
&&
(
target
==
"5m"
||
target
==
"1h"
)
{
return
target
}
}
return
"5m"
}
// GetWindowCostLimit 获取 5h 窗口费用阈值(美元)
// GetWindowCostLimit 获取 5h 窗口费用阈值(美元)
// 返回 0 表示未启用
// 返回 0 表示未启用
func
(
a
*
Account
)
GetWindowCostLimit
()
float64
{
func
(
a
*
Account
)
GetWindowCostLimit
()
float64
{
...
...
backend/internal/service/gateway_service.go
View file @
beceb45d
...
@@ -4276,6 +4276,23 @@ func (s *GatewayService) handleStreamingResponse(ctx context.Context, resp *http
...
@@ -4276,6 +4276,23 @@ func (s *GatewayService) handleStreamingResponse(ctx context.Context, resp *http
}
}
}
}
// Cache TTL Override: 重写 SSE 事件中的 cache_creation 分类
if
account
.
IsCacheTTLOverrideEnabled
()
{
overrideTarget
:=
account
.
GetCacheTTLOverrideTarget
()
if
eventType
==
"message_start"
{
if
msg
,
ok
:=
event
[
"message"
]
.
(
map
[
string
]
any
);
ok
{
if
u
,
ok
:=
msg
[
"usage"
]
.
(
map
[
string
]
any
);
ok
{
rewriteCacheCreationJSON
(
u
,
overrideTarget
)
}
}
}
if
eventType
==
"message_delta"
{
if
u
,
ok
:=
event
[
"usage"
]
.
(
map
[
string
]
any
);
ok
{
rewriteCacheCreationJSON
(
u
,
overrideTarget
)
}
}
}
if
needModelReplace
{
if
needModelReplace
{
if
msg
,
ok
:=
event
[
"message"
]
.
(
map
[
string
]
any
);
ok
{
if
msg
,
ok
:=
event
[
"message"
]
.
(
map
[
string
]
any
);
ok
{
if
model
,
ok
:=
msg
[
"model"
]
.
(
string
);
ok
&&
model
==
mappedModel
{
if
model
,
ok
:=
msg
[
"model"
]
.
(
string
);
ok
&&
model
==
mappedModel
{
...
@@ -4450,6 +4467,58 @@ func (s *GatewayService) parseSSEUsage(data string, usage *ClaudeUsage) {
...
@@ -4450,6 +4467,58 @@ func (s *GatewayService) parseSSEUsage(data string, usage *ClaudeUsage) {
}
}
}
}
// applyCacheTTLOverride 将所有 cache creation tokens 归入指定的 TTL 类型。
// target 为 "5m" 或 "1h"。返回 true 表示发生了变更。
func
applyCacheTTLOverride
(
usage
*
ClaudeUsage
,
target
string
)
bool
{
// Fallback: 如果只有聚合字段但无 5m/1h 明细,将聚合字段归入 5m 默认类别
if
usage
.
CacheCreation5mTokens
==
0
&&
usage
.
CacheCreation1hTokens
==
0
&&
usage
.
CacheCreationInputTokens
>
0
{
usage
.
CacheCreation5mTokens
=
usage
.
CacheCreationInputTokens
}
total
:=
usage
.
CacheCreation5mTokens
+
usage
.
CacheCreation1hTokens
if
total
==
0
{
return
false
}
switch
target
{
case
"1h"
:
if
usage
.
CacheCreation1hTokens
==
total
{
return
false
// 已经全是 1h
}
usage
.
CacheCreation1hTokens
=
total
usage
.
CacheCreation5mTokens
=
0
default
:
// "5m"
if
usage
.
CacheCreation5mTokens
==
total
{
return
false
// 已经全是 5m
}
usage
.
CacheCreation5mTokens
=
total
usage
.
CacheCreation1hTokens
=
0
}
return
true
}
// rewriteCacheCreationJSON 在 JSON usage 对象中重写 cache_creation 嵌套对象的 TTL 分类。
// usageObj 是 usage JSON 对象(map[string]any)。
func
rewriteCacheCreationJSON
(
usageObj
map
[
string
]
any
,
target
string
)
{
ccObj
,
ok
:=
usageObj
[
"cache_creation"
]
.
(
map
[
string
]
any
)
if
!
ok
{
return
}
v5m
,
_
:=
ccObj
[
"ephemeral_5m_input_tokens"
]
.
(
float64
)
v1h
,
_
:=
ccObj
[
"ephemeral_1h_input_tokens"
]
.
(
float64
)
total
:=
v5m
+
v1h
if
total
==
0
{
return
}
switch
target
{
case
"1h"
:
ccObj
[
"ephemeral_1h_input_tokens"
]
=
total
ccObj
[
"ephemeral_5m_input_tokens"
]
=
float64
(
0
)
default
:
// "5m"
ccObj
[
"ephemeral_5m_input_tokens"
]
=
total
ccObj
[
"ephemeral_1h_input_tokens"
]
=
float64
(
0
)
}
}
func
(
s
*
GatewayService
)
handleNonStreamingResponse
(
ctx
context
.
Context
,
resp
*
http
.
Response
,
c
*
gin
.
Context
,
account
*
Account
,
originalModel
,
mappedModel
string
)
(
*
ClaudeUsage
,
error
)
{
func
(
s
*
GatewayService
)
handleNonStreamingResponse
(
ctx
context
.
Context
,
resp
*
http
.
Response
,
c
*
gin
.
Context
,
account
*
Account
,
originalModel
,
mappedModel
string
)
(
*
ClaudeUsage
,
error
)
{
// 更新5h窗口状态
// 更新5h窗口状态
s
.
rateLimitService
.
UpdateSessionWindow
(
ctx
,
account
,
resp
.
Header
)
s
.
rateLimitService
.
UpdateSessionWindow
(
ctx
,
account
,
resp
.
Header
)
...
@@ -4486,6 +4555,20 @@ func (s *GatewayService) handleNonStreamingResponse(ctx context.Context, resp *h
...
@@ -4486,6 +4555,20 @@ func (s *GatewayService) handleNonStreamingResponse(ctx context.Context, resp *h
}
}
}
}
// Cache TTL Override: 重写 non-streaming 响应中的 cache_creation 分类
if
account
.
IsCacheTTLOverrideEnabled
()
{
overrideTarget
:=
account
.
GetCacheTTLOverrideTarget
()
if
applyCacheTTLOverride
(
&
response
.
Usage
,
overrideTarget
)
{
// 同步更新 body JSON 中的嵌套 cache_creation 对象
if
newBody
,
err
:=
sjson
.
SetBytes
(
body
,
"usage.cache_creation.ephemeral_5m_input_tokens"
,
response
.
Usage
.
CacheCreation5mTokens
);
err
==
nil
{
body
=
newBody
}
if
newBody
,
err
:=
sjson
.
SetBytes
(
body
,
"usage.cache_creation.ephemeral_1h_input_tokens"
,
response
.
Usage
.
CacheCreation1hTokens
);
err
==
nil
{
body
=
newBody
}
}
}
// 如果有模型映射,替换响应中的model字段
// 如果有模型映射,替换响应中的model字段
if
originalModel
!=
mappedModel
{
if
originalModel
!=
mappedModel
{
body
=
s
.
replaceModelInResponseBody
(
body
,
mappedModel
,
originalModel
)
body
=
s
.
replaceModelInResponseBody
(
body
,
mappedModel
,
originalModel
)
...
@@ -4562,6 +4645,13 @@ func (s *GatewayService) RecordUsage(ctx context.Context, input *RecordUsageInpu
...
@@ -4562,6 +4645,13 @@ func (s *GatewayService) RecordUsage(ctx context.Context, input *RecordUsageInpu
result
.
Usage
.
InputTokens
=
0
result
.
Usage
.
InputTokens
=
0
}
}
// Cache TTL Override: 确保计费时 token 分类与账号设置一致
cacheTTLOverridden
:=
false
if
account
.
IsCacheTTLOverrideEnabled
()
{
applyCacheTTLOverride
(
&
result
.
Usage
,
account
.
GetCacheTTLOverrideTarget
())
cacheTTLOverridden
=
(
result
.
Usage
.
CacheCreation5mTokens
+
result
.
Usage
.
CacheCreation1hTokens
)
>
0
}
// 获取费率倍数(优先级:用户专属 > 分组默认 > 系统默认)
// 获取费率倍数(优先级:用户专属 > 分组默认 > 系统默认)
multiplier
:=
s
.
cfg
.
Default
.
RateMultiplier
multiplier
:=
s
.
cfg
.
Default
.
RateMultiplier
if
apiKey
.
GroupID
!=
nil
&&
apiKey
.
Group
!=
nil
{
if
apiKey
.
GroupID
!=
nil
&&
apiKey
.
Group
!=
nil
{
...
@@ -4647,6 +4737,7 @@ func (s *GatewayService) RecordUsage(ctx context.Context, input *RecordUsageInpu
...
@@ -4647,6 +4737,7 @@ func (s *GatewayService) RecordUsage(ctx context.Context, input *RecordUsageInpu
FirstTokenMs
:
result
.
FirstTokenMs
,
FirstTokenMs
:
result
.
FirstTokenMs
,
ImageCount
:
result
.
ImageCount
,
ImageCount
:
result
.
ImageCount
,
ImageSize
:
imageSize
,
ImageSize
:
imageSize
,
CacheTTLOverridden
:
cacheTTLOverridden
,
CreatedAt
:
time
.
Now
(),
CreatedAt
:
time
.
Now
(),
}
}
...
@@ -4747,6 +4838,13 @@ func (s *GatewayService) RecordUsageWithLongContext(ctx context.Context, input *
...
@@ -4747,6 +4838,13 @@ func (s *GatewayService) RecordUsageWithLongContext(ctx context.Context, input *
result
.
Usage
.
InputTokens
=
0
result
.
Usage
.
InputTokens
=
0
}
}
// Cache TTL Override: 确保计费时 token 分类与账号设置一致
cacheTTLOverridden
:=
false
if
account
.
IsCacheTTLOverrideEnabled
()
{
applyCacheTTLOverride
(
&
result
.
Usage
,
account
.
GetCacheTTLOverrideTarget
())
cacheTTLOverridden
=
(
result
.
Usage
.
CacheCreation5mTokens
+
result
.
Usage
.
CacheCreation1hTokens
)
>
0
}
// 获取费率倍数(优先级:用户专属 > 分组默认 > 系统默认)
// 获取费率倍数(优先级:用户专属 > 分组默认 > 系统默认)
multiplier
:=
s
.
cfg
.
Default
.
RateMultiplier
multiplier
:=
s
.
cfg
.
Default
.
RateMultiplier
if
apiKey
.
GroupID
!=
nil
&&
apiKey
.
Group
!=
nil
{
if
apiKey
.
GroupID
!=
nil
&&
apiKey
.
Group
!=
nil
{
...
@@ -4832,6 +4930,7 @@ func (s *GatewayService) RecordUsageWithLongContext(ctx context.Context, input *
...
@@ -4832,6 +4930,7 @@ func (s *GatewayService) RecordUsageWithLongContext(ctx context.Context, input *
FirstTokenMs
:
result
.
FirstTokenMs
,
FirstTokenMs
:
result
.
FirstTokenMs
,
ImageCount
:
result
.
ImageCount
,
ImageCount
:
result
.
ImageCount
,
ImageSize
:
imageSize
,
ImageSize
:
imageSize
,
CacheTTLOverridden
:
cacheTTLOverridden
,
CreatedAt
:
time
.
Now
(),
CreatedAt
:
time
.
Now
(),
}
}
...
...
backend/internal/service/usage_log.go
View file @
beceb45d
...
@@ -46,6 +46,9 @@ type UsageLog struct {
...
@@ -46,6 +46,9 @@ type UsageLog struct {
UserAgent
*
string
UserAgent
*
string
IPAddress
*
string
IPAddress
*
string
// Cache TTL Override 标记(管理员强制替换了缓存 TTL 计费)
CacheTTLOverridden
bool
// 图片生成字段
// 图片生成字段
ImageCount
int
ImageCount
int
ImageSize
*
string
ImageSize
*
string
...
...
backend/migrations/055_add_cache_ttl_overridden.sql
0 → 100644
View file @
beceb45d
-- Add cache_ttl_overridden flag to usage_logs for tracking cache TTL override per account.
ALTER
TABLE
usage_logs
ADD
COLUMN
IF
NOT
EXISTS
cache_ttl_overridden
BOOLEAN
NOT
NULL
DEFAULT
FALSE
;
frontend/src/components/account/CreateAccountModal.vue
View file @
beceb45d
...
@@ -1527,6 +1527,46 @@
...
@@ -1527,6 +1527,46 @@
<
/button
>
<
/button
>
<
/div
>
<
/div
>
<
/div
>
<
/div
>
<!--
Cache
TTL
Override
-->
<
div
class
=
"
rounded-lg border border-gray-200 p-4 dark:border-dark-600
"
>
<
div
class
=
"
flex items-center justify-between
"
>
<
div
>
<
label
class
=
"
input-label mb-0
"
>
{{
t
(
'
admin.accounts.quotaControl.cacheTTLOverride.label
'
)
}}
<
/label
>
<
p
class
=
"
mt-1 text-xs text-gray-500 dark:text-gray-400
"
>
{{
t
(
'
admin.accounts.quotaControl.cacheTTLOverride.hint
'
)
}}
<
/p
>
<
/div
>
<
button
type
=
"
button
"
@
click
=
"
cacheTTLOverrideEnabled = !cacheTTLOverrideEnabled
"
:
class
=
"
[
'relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2',
cacheTTLOverrideEnabled ? 'bg-primary-600' : 'bg-gray-200 dark:bg-dark-600'
]
"
>
<
span
:
class
=
"
[
'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out',
cacheTTLOverrideEnabled ? 'translate-x-5' : 'translate-x-0'
]
"
/>
<
/button
>
<
/div
>
<
div
v
-
if
=
"
cacheTTLOverrideEnabled
"
class
=
"
mt-3
"
>
<
label
class
=
"
input-label text-xs
"
>
{{
t
(
'
admin.accounts.quotaControl.cacheTTLOverride.target
'
)
}}
<
/label
>
<
select
v
-
model
=
"
cacheTTLOverrideTarget
"
class
=
"
mt-1 block w-full rounded-md border border-gray-300 bg-white px-3 py-2 text-sm shadow-sm focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500 dark:border-dark-500 dark:bg-dark-700 dark:text-white
"
>
<
option
value
=
"
5m
"
>
5
m
<
/option
>
<
option
value
=
"
1h
"
>
1
h
<
/option
>
<
/select
>
<
p
class
=
"
mt-1 text-xs text-gray-500 dark:text-gray-400
"
>
{{
t
(
'
admin.accounts.quotaControl.cacheTTLOverride.targetHint
'
)
}}
<
/p
>
<
/div
>
<
/div
>
<
/div
>
<
/div
>
<
div
>
<
div
>
...
@@ -2146,6 +2186,8 @@ const maxSessions = ref<number | null>(null)
...
@@ -2146,6 +2186,8 @@ const maxSessions = ref<number | null>(null)
const
sessionIdleTimeout
=
ref
<
number
|
null
>
(
null
)
const
sessionIdleTimeout
=
ref
<
number
|
null
>
(
null
)
const
tlsFingerprintEnabled
=
ref
(
false
)
const
tlsFingerprintEnabled
=
ref
(
false
)
const
sessionIdMaskingEnabled
=
ref
(
false
)
const
sessionIdMaskingEnabled
=
ref
(
false
)
const
cacheTTLOverrideEnabled
=
ref
(
false
)
const
cacheTTLOverrideTarget
=
ref
<
string
>
(
'
5m
'
)
// Gemini tier selection (used as fallback when auto-detection is unavailable/fails)
// Gemini tier selection (used as fallback when auto-detection is unavailable/fails)
const
geminiTierGoogleOne
=
ref
<
'
google_one_free
'
|
'
google_ai_pro
'
|
'
google_ai_ultra
'
>
(
'
google_one_free
'
)
const
geminiTierGoogleOne
=
ref
<
'
google_one_free
'
|
'
google_ai_pro
'
|
'
google_ai_ultra
'
>
(
'
google_one_free
'
)
...
@@ -2597,6 +2639,8 @@ const resetForm = () => {
...
@@ -2597,6 +2639,8 @@ const resetForm = () => {
sessionIdleTimeout
.
value
=
null
sessionIdleTimeout
.
value
=
null
tlsFingerprintEnabled
.
value
=
false
tlsFingerprintEnabled
.
value
=
false
sessionIdMaskingEnabled
.
value
=
false
sessionIdMaskingEnabled
.
value
=
false
cacheTTLOverrideEnabled
.
value
=
false
cacheTTLOverrideTarget
.
value
=
'
5m
'
antigravityAccountType
.
value
=
'
oauth
'
antigravityAccountType
.
value
=
'
oauth
'
upstreamBaseUrl
.
value
=
''
upstreamBaseUrl
.
value
=
''
upstreamApiKey
.
value
=
''
upstreamApiKey
.
value
=
''
...
@@ -3174,6 +3218,12 @@ const handleAnthropicExchange = async (authCode: string) => {
...
@@ -3174,6 +3218,12 @@ const handleAnthropicExchange = async (authCode: string) => {
extra
.
session_id_masking_enabled
=
true
extra
.
session_id_masking_enabled
=
true
}
}
// Add cache TTL override settings
if
(
cacheTTLOverrideEnabled
.
value
)
{
extra
.
cache_ttl_override_enabled
=
true
extra
.
cache_ttl_override_target
=
cacheTTLOverrideTarget
.
value
}
const
credentials
=
{
const
credentials
=
{
...
tokenInfo
,
...
tokenInfo
,
...(
interceptWarmupRequests
.
value
?
{
intercept_warmup_requests
:
true
}
:
{
}
)
...(
interceptWarmupRequests
.
value
?
{
intercept_warmup_requests
:
true
}
:
{
}
)
...
@@ -3267,6 +3317,12 @@ const handleCookieAuth = async (sessionKey: string) => {
...
@@ -3267,6 +3317,12 @@ const handleCookieAuth = async (sessionKey: string) => {
extra
.
session_id_masking_enabled
=
true
extra
.
session_id_masking_enabled
=
true
}
}
// Add cache TTL override settings
if
(
cacheTTLOverrideEnabled
.
value
)
{
extra
.
cache_ttl_override_enabled
=
true
extra
.
cache_ttl_override_target
=
cacheTTLOverrideTarget
.
value
}
const
accountName
=
keys
.
length
>
1
?
`${form.name
}
#${i + 1
}
`
:
form
.
name
const
accountName
=
keys
.
length
>
1
?
`${form.name
}
#${i + 1
}
`
:
form
.
name
// Merge interceptWarmupRequests into credentials
// Merge interceptWarmupRequests into credentials
...
...
frontend/src/components/account/EditAccountModal.vue
View file @
beceb45d
...
@@ -904,6 +904,46 @@
...
@@ -904,6 +904,46 @@
<
/button
>
<
/button
>
<
/div
>
<
/div
>
<
/div
>
<
/div
>
<!--
Cache
TTL
Override
-->
<
div
class
=
"
rounded-lg border border-gray-200 p-4 dark:border-dark-600
"
>
<
div
class
=
"
flex items-center justify-between
"
>
<
div
>
<
label
class
=
"
input-label mb-0
"
>
{{
t
(
'
admin.accounts.quotaControl.cacheTTLOverride.label
'
)
}}
<
/label
>
<
p
class
=
"
mt-1 text-xs text-gray-500 dark:text-gray-400
"
>
{{
t
(
'
admin.accounts.quotaControl.cacheTTLOverride.hint
'
)
}}
<
/p
>
<
/div
>
<
button
type
=
"
button
"
@
click
=
"
cacheTTLOverrideEnabled = !cacheTTLOverrideEnabled
"
:
class
=
"
[
'relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2',
cacheTTLOverrideEnabled ? 'bg-primary-600' : 'bg-gray-200 dark:bg-dark-600'
]
"
>
<
span
:
class
=
"
[
'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out',
cacheTTLOverrideEnabled ? 'translate-x-5' : 'translate-x-0'
]
"
/>
<
/button
>
<
/div
>
<
div
v
-
if
=
"
cacheTTLOverrideEnabled
"
class
=
"
mt-3
"
>
<
label
class
=
"
input-label text-xs
"
>
{{
t
(
'
admin.accounts.quotaControl.cacheTTLOverride.target
'
)
}}
<
/label
>
<
select
v
-
model
=
"
cacheTTLOverrideTarget
"
class
=
"
mt-1 block w-full rounded-md border border-gray-300 bg-white px-3 py-2 text-sm shadow-sm focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500 dark:border-dark-500 dark:bg-dark-700 dark:text-white
"
>
<
option
value
=
"
5m
"
>
5
m
<
/option
>
<
option
value
=
"
1h
"
>
1
h
<
/option
>
<
/select
>
<
p
class
=
"
mt-1 text-xs text-gray-500 dark:text-gray-400
"
>
{{
t
(
'
admin.accounts.quotaControl.cacheTTLOverride.targetHint
'
)
}}
<
/p
>
<
/div
>
<
/div
>
<
/div
>
<
/div
>
<
div
class
=
"
border-t border-gray-200 pt-4 dark:border-dark-600
"
>
<
div
class
=
"
border-t border-gray-200 pt-4 dark:border-dark-600
"
>
...
@@ -1102,6 +1142,8 @@ const maxSessions = ref<number | null>(null)
...
@@ -1102,6 +1142,8 @@ const maxSessions = ref<number | null>(null)
const
sessionIdleTimeout
=
ref
<
number
|
null
>
(
null
)
const
sessionIdleTimeout
=
ref
<
number
|
null
>
(
null
)
const
tlsFingerprintEnabled
=
ref
(
false
)
const
tlsFingerprintEnabled
=
ref
(
false
)
const
sessionIdMaskingEnabled
=
ref
(
false
)
const
sessionIdMaskingEnabled
=
ref
(
false
)
const
cacheTTLOverrideEnabled
=
ref
(
false
)
const
cacheTTLOverrideTarget
=
ref
<
string
>
(
'
5m
'
)
// Computed: current preset mappings based on platform
// Computed: current preset mappings based on platform
const
presetMappings
=
computed
(()
=>
getPresetMappingsByPlatform
(
props
.
account
?.
platform
||
'
anthropic
'
))
const
presetMappings
=
computed
(()
=>
getPresetMappingsByPlatform
(
props
.
account
?.
platform
||
'
anthropic
'
))
...
@@ -1489,6 +1531,8 @@ function loadQuotaControlSettings(account: Account) {
...
@@ -1489,6 +1531,8 @@ function loadQuotaControlSettings(account: Account) {
sessionIdleTimeout
.
value
=
null
sessionIdleTimeout
.
value
=
null
tlsFingerprintEnabled
.
value
=
false
tlsFingerprintEnabled
.
value
=
false
sessionIdMaskingEnabled
.
value
=
false
sessionIdMaskingEnabled
.
value
=
false
cacheTTLOverrideEnabled
.
value
=
false
cacheTTLOverrideTarget
.
value
=
'
5m
'
// Only applies to Anthropic OAuth/SetupToken accounts
// Only applies to Anthropic OAuth/SetupToken accounts
if
(
account
.
platform
!==
'
anthropic
'
||
(
account
.
type
!==
'
oauth
'
&&
account
.
type
!==
'
setup-token
'
))
{
if
(
account
.
platform
!==
'
anthropic
'
||
(
account
.
type
!==
'
oauth
'
&&
account
.
type
!==
'
setup-token
'
))
{
...
@@ -1517,6 +1561,12 @@ function loadQuotaControlSettings(account: Account) {
...
@@ -1517,6 +1561,12 @@ function loadQuotaControlSettings(account: Account) {
if
(
account
.
session_id_masking_enabled
===
true
)
{
if
(
account
.
session_id_masking_enabled
===
true
)
{
sessionIdMaskingEnabled
.
value
=
true
sessionIdMaskingEnabled
.
value
=
true
}
}
// Load cache TTL override setting
if
(
account
.
cache_ttl_override_enabled
===
true
)
{
cacheTTLOverrideEnabled
.
value
=
true
cacheTTLOverrideTarget
.
value
=
account
.
cache_ttl_override_target
||
'
5m
'
}
}
}
function
formatTempUnschedKeywords
(
value
:
unknown
)
{
function
formatTempUnschedKeywords
(
value
:
unknown
)
{
...
@@ -1723,6 +1773,15 @@ const handleSubmit = async () => {
...
@@ -1723,6 +1773,15 @@ const handleSubmit = async () => {
delete
newExtra
.
session_id_masking_enabled
delete
newExtra
.
session_id_masking_enabled
}
}
// Cache TTL override setting
if
(
cacheTTLOverrideEnabled
.
value
)
{
newExtra
.
cache_ttl_override_enabled
=
true
newExtra
.
cache_ttl_override_target
=
cacheTTLOverrideTarget
.
value
}
else
{
delete
newExtra
.
cache_ttl_override_enabled
delete
newExtra
.
cache_ttl_override_target
}
updatePayload
.
extra
=
newExtra
updatePayload
.
extra
=
newExtra
}
}
...
...
Prev
1
2
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