Unverified Commit b6d46fd5 authored by InCerryGit's avatar InCerryGit Committed by GitHub
Browse files

Merge branch 'Wei-Shaw:main' into main

parents fa68cbad fdd8499f
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
"errors"
"fmt"
"time"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/dialect/sql/sqljson"
"entgo.io/ent/schema/field"
"github.com/Wei-Shaw/sub2api/ent/predicate"
"github.com/Wei-Shaw/sub2api/ent/tlsfingerprintprofile"
)
// TLSFingerprintProfileUpdate is the builder for updating TLSFingerprintProfile entities.
type TLSFingerprintProfileUpdate struct {
config
hooks []Hook
mutation *TLSFingerprintProfileMutation
}
// Where appends a list predicates to the TLSFingerprintProfileUpdate builder.
func (_u *TLSFingerprintProfileUpdate) Where(ps ...predicate.TLSFingerprintProfile) *TLSFingerprintProfileUpdate {
_u.mutation.Where(ps...)
return _u
}
// SetUpdatedAt sets the "updated_at" field.
func (_u *TLSFingerprintProfileUpdate) SetUpdatedAt(v time.Time) *TLSFingerprintProfileUpdate {
_u.mutation.SetUpdatedAt(v)
return _u
}
// SetName sets the "name" field.
func (_u *TLSFingerprintProfileUpdate) SetName(v string) *TLSFingerprintProfileUpdate {
_u.mutation.SetName(v)
return _u
}
// SetNillableName sets the "name" field if the given value is not nil.
func (_u *TLSFingerprintProfileUpdate) SetNillableName(v *string) *TLSFingerprintProfileUpdate {
if v != nil {
_u.SetName(*v)
}
return _u
}
// SetDescription sets the "description" field.
func (_u *TLSFingerprintProfileUpdate) SetDescription(v string) *TLSFingerprintProfileUpdate {
_u.mutation.SetDescription(v)
return _u
}
// SetNillableDescription sets the "description" field if the given value is not nil.
func (_u *TLSFingerprintProfileUpdate) SetNillableDescription(v *string) *TLSFingerprintProfileUpdate {
if v != nil {
_u.SetDescription(*v)
}
return _u
}
// ClearDescription clears the value of the "description" field.
func (_u *TLSFingerprintProfileUpdate) ClearDescription() *TLSFingerprintProfileUpdate {
_u.mutation.ClearDescription()
return _u
}
// SetEnableGrease sets the "enable_grease" field.
func (_u *TLSFingerprintProfileUpdate) SetEnableGrease(v bool) *TLSFingerprintProfileUpdate {
_u.mutation.SetEnableGrease(v)
return _u
}
// SetNillableEnableGrease sets the "enable_grease" field if the given value is not nil.
func (_u *TLSFingerprintProfileUpdate) SetNillableEnableGrease(v *bool) *TLSFingerprintProfileUpdate {
if v != nil {
_u.SetEnableGrease(*v)
}
return _u
}
// SetCipherSuites sets the "cipher_suites" field.
func (_u *TLSFingerprintProfileUpdate) SetCipherSuites(v []uint16) *TLSFingerprintProfileUpdate {
_u.mutation.SetCipherSuites(v)
return _u
}
// AppendCipherSuites appends value to the "cipher_suites" field.
func (_u *TLSFingerprintProfileUpdate) AppendCipherSuites(v []uint16) *TLSFingerprintProfileUpdate {
_u.mutation.AppendCipherSuites(v)
return _u
}
// ClearCipherSuites clears the value of the "cipher_suites" field.
func (_u *TLSFingerprintProfileUpdate) ClearCipherSuites() *TLSFingerprintProfileUpdate {
_u.mutation.ClearCipherSuites()
return _u
}
// SetCurves sets the "curves" field.
func (_u *TLSFingerprintProfileUpdate) SetCurves(v []uint16) *TLSFingerprintProfileUpdate {
_u.mutation.SetCurves(v)
return _u
}
// AppendCurves appends value to the "curves" field.
func (_u *TLSFingerprintProfileUpdate) AppendCurves(v []uint16) *TLSFingerprintProfileUpdate {
_u.mutation.AppendCurves(v)
return _u
}
// ClearCurves clears the value of the "curves" field.
func (_u *TLSFingerprintProfileUpdate) ClearCurves() *TLSFingerprintProfileUpdate {
_u.mutation.ClearCurves()
return _u
}
// SetPointFormats sets the "point_formats" field.
func (_u *TLSFingerprintProfileUpdate) SetPointFormats(v []uint16) *TLSFingerprintProfileUpdate {
_u.mutation.SetPointFormats(v)
return _u
}
// AppendPointFormats appends value to the "point_formats" field.
func (_u *TLSFingerprintProfileUpdate) AppendPointFormats(v []uint16) *TLSFingerprintProfileUpdate {
_u.mutation.AppendPointFormats(v)
return _u
}
// ClearPointFormats clears the value of the "point_formats" field.
func (_u *TLSFingerprintProfileUpdate) ClearPointFormats() *TLSFingerprintProfileUpdate {
_u.mutation.ClearPointFormats()
return _u
}
// SetSignatureAlgorithms sets the "signature_algorithms" field.
func (_u *TLSFingerprintProfileUpdate) SetSignatureAlgorithms(v []uint16) *TLSFingerprintProfileUpdate {
_u.mutation.SetSignatureAlgorithms(v)
return _u
}
// AppendSignatureAlgorithms appends value to the "signature_algorithms" field.
func (_u *TLSFingerprintProfileUpdate) AppendSignatureAlgorithms(v []uint16) *TLSFingerprintProfileUpdate {
_u.mutation.AppendSignatureAlgorithms(v)
return _u
}
// ClearSignatureAlgorithms clears the value of the "signature_algorithms" field.
func (_u *TLSFingerprintProfileUpdate) ClearSignatureAlgorithms() *TLSFingerprintProfileUpdate {
_u.mutation.ClearSignatureAlgorithms()
return _u
}
// SetAlpnProtocols sets the "alpn_protocols" field.
func (_u *TLSFingerprintProfileUpdate) SetAlpnProtocols(v []string) *TLSFingerprintProfileUpdate {
_u.mutation.SetAlpnProtocols(v)
return _u
}
// AppendAlpnProtocols appends value to the "alpn_protocols" field.
func (_u *TLSFingerprintProfileUpdate) AppendAlpnProtocols(v []string) *TLSFingerprintProfileUpdate {
_u.mutation.AppendAlpnProtocols(v)
return _u
}
// ClearAlpnProtocols clears the value of the "alpn_protocols" field.
func (_u *TLSFingerprintProfileUpdate) ClearAlpnProtocols() *TLSFingerprintProfileUpdate {
_u.mutation.ClearAlpnProtocols()
return _u
}
// SetSupportedVersions sets the "supported_versions" field.
func (_u *TLSFingerprintProfileUpdate) SetSupportedVersions(v []uint16) *TLSFingerprintProfileUpdate {
_u.mutation.SetSupportedVersions(v)
return _u
}
// AppendSupportedVersions appends value to the "supported_versions" field.
func (_u *TLSFingerprintProfileUpdate) AppendSupportedVersions(v []uint16) *TLSFingerprintProfileUpdate {
_u.mutation.AppendSupportedVersions(v)
return _u
}
// ClearSupportedVersions clears the value of the "supported_versions" field.
func (_u *TLSFingerprintProfileUpdate) ClearSupportedVersions() *TLSFingerprintProfileUpdate {
_u.mutation.ClearSupportedVersions()
return _u
}
// SetKeyShareGroups sets the "key_share_groups" field.
func (_u *TLSFingerprintProfileUpdate) SetKeyShareGroups(v []uint16) *TLSFingerprintProfileUpdate {
_u.mutation.SetKeyShareGroups(v)
return _u
}
// AppendKeyShareGroups appends value to the "key_share_groups" field.
func (_u *TLSFingerprintProfileUpdate) AppendKeyShareGroups(v []uint16) *TLSFingerprintProfileUpdate {
_u.mutation.AppendKeyShareGroups(v)
return _u
}
// ClearKeyShareGroups clears the value of the "key_share_groups" field.
func (_u *TLSFingerprintProfileUpdate) ClearKeyShareGroups() *TLSFingerprintProfileUpdate {
_u.mutation.ClearKeyShareGroups()
return _u
}
// SetPskModes sets the "psk_modes" field.
func (_u *TLSFingerprintProfileUpdate) SetPskModes(v []uint16) *TLSFingerprintProfileUpdate {
_u.mutation.SetPskModes(v)
return _u
}
// AppendPskModes appends value to the "psk_modes" field.
func (_u *TLSFingerprintProfileUpdate) AppendPskModes(v []uint16) *TLSFingerprintProfileUpdate {
_u.mutation.AppendPskModes(v)
return _u
}
// ClearPskModes clears the value of the "psk_modes" field.
func (_u *TLSFingerprintProfileUpdate) ClearPskModes() *TLSFingerprintProfileUpdate {
_u.mutation.ClearPskModes()
return _u
}
// SetExtensions sets the "extensions" field.
func (_u *TLSFingerprintProfileUpdate) SetExtensions(v []uint16) *TLSFingerprintProfileUpdate {
_u.mutation.SetExtensions(v)
return _u
}
// AppendExtensions appends value to the "extensions" field.
func (_u *TLSFingerprintProfileUpdate) AppendExtensions(v []uint16) *TLSFingerprintProfileUpdate {
_u.mutation.AppendExtensions(v)
return _u
}
// ClearExtensions clears the value of the "extensions" field.
func (_u *TLSFingerprintProfileUpdate) ClearExtensions() *TLSFingerprintProfileUpdate {
_u.mutation.ClearExtensions()
return _u
}
// Mutation returns the TLSFingerprintProfileMutation object of the builder.
func (_u *TLSFingerprintProfileUpdate) Mutation() *TLSFingerprintProfileMutation {
return _u.mutation
}
// Save executes the query and returns the number of nodes affected by the update operation.
func (_u *TLSFingerprintProfileUpdate) Save(ctx context.Context) (int, error) {
_u.defaults()
return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
}
// SaveX is like Save, but panics if an error occurs.
func (_u *TLSFingerprintProfileUpdate) SaveX(ctx context.Context) int {
affected, err := _u.Save(ctx)
if err != nil {
panic(err)
}
return affected
}
// Exec executes the query.
func (_u *TLSFingerprintProfileUpdate) Exec(ctx context.Context) error {
_, err := _u.Save(ctx)
return err
}
// ExecX is like Exec, but panics if an error occurs.
func (_u *TLSFingerprintProfileUpdate) ExecX(ctx context.Context) {
if err := _u.Exec(ctx); err != nil {
panic(err)
}
}
// defaults sets the default values of the builder before save.
func (_u *TLSFingerprintProfileUpdate) defaults() {
if _, ok := _u.mutation.UpdatedAt(); !ok {
v := tlsfingerprintprofile.UpdateDefaultUpdatedAt()
_u.mutation.SetUpdatedAt(v)
}
}
// check runs all checks and user-defined validators on the builder.
func (_u *TLSFingerprintProfileUpdate) check() error {
if v, ok := _u.mutation.Name(); ok {
if err := tlsfingerprintprofile.NameValidator(v); err != nil {
return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "TLSFingerprintProfile.name": %w`, err)}
}
}
return nil
}
func (_u *TLSFingerprintProfileUpdate) sqlSave(ctx context.Context) (_node int, err error) {
if err := _u.check(); err != nil {
return _node, err
}
_spec := sqlgraph.NewUpdateSpec(tlsfingerprintprofile.Table, tlsfingerprintprofile.Columns, sqlgraph.NewFieldSpec(tlsfingerprintprofile.FieldID, field.TypeInt64))
if ps := _u.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
if value, ok := _u.mutation.UpdatedAt(); ok {
_spec.SetField(tlsfingerprintprofile.FieldUpdatedAt, field.TypeTime, value)
}
if value, ok := _u.mutation.Name(); ok {
_spec.SetField(tlsfingerprintprofile.FieldName, field.TypeString, value)
}
if value, ok := _u.mutation.Description(); ok {
_spec.SetField(tlsfingerprintprofile.FieldDescription, field.TypeString, value)
}
if _u.mutation.DescriptionCleared() {
_spec.ClearField(tlsfingerprintprofile.FieldDescription, field.TypeString)
}
if value, ok := _u.mutation.EnableGrease(); ok {
_spec.SetField(tlsfingerprintprofile.FieldEnableGrease, field.TypeBool, value)
}
if value, ok := _u.mutation.CipherSuites(); ok {
_spec.SetField(tlsfingerprintprofile.FieldCipherSuites, field.TypeJSON, value)
}
if value, ok := _u.mutation.AppendedCipherSuites(); ok {
_spec.AddModifier(func(u *sql.UpdateBuilder) {
sqljson.Append(u, tlsfingerprintprofile.FieldCipherSuites, value)
})
}
if _u.mutation.CipherSuitesCleared() {
_spec.ClearField(tlsfingerprintprofile.FieldCipherSuites, field.TypeJSON)
}
if value, ok := _u.mutation.Curves(); ok {
_spec.SetField(tlsfingerprintprofile.FieldCurves, field.TypeJSON, value)
}
if value, ok := _u.mutation.AppendedCurves(); ok {
_spec.AddModifier(func(u *sql.UpdateBuilder) {
sqljson.Append(u, tlsfingerprintprofile.FieldCurves, value)
})
}
if _u.mutation.CurvesCleared() {
_spec.ClearField(tlsfingerprintprofile.FieldCurves, field.TypeJSON)
}
if value, ok := _u.mutation.PointFormats(); ok {
_spec.SetField(tlsfingerprintprofile.FieldPointFormats, field.TypeJSON, value)
}
if value, ok := _u.mutation.AppendedPointFormats(); ok {
_spec.AddModifier(func(u *sql.UpdateBuilder) {
sqljson.Append(u, tlsfingerprintprofile.FieldPointFormats, value)
})
}
if _u.mutation.PointFormatsCleared() {
_spec.ClearField(tlsfingerprintprofile.FieldPointFormats, field.TypeJSON)
}
if value, ok := _u.mutation.SignatureAlgorithms(); ok {
_spec.SetField(tlsfingerprintprofile.FieldSignatureAlgorithms, field.TypeJSON, value)
}
if value, ok := _u.mutation.AppendedSignatureAlgorithms(); ok {
_spec.AddModifier(func(u *sql.UpdateBuilder) {
sqljson.Append(u, tlsfingerprintprofile.FieldSignatureAlgorithms, value)
})
}
if _u.mutation.SignatureAlgorithmsCleared() {
_spec.ClearField(tlsfingerprintprofile.FieldSignatureAlgorithms, field.TypeJSON)
}
if value, ok := _u.mutation.AlpnProtocols(); ok {
_spec.SetField(tlsfingerprintprofile.FieldAlpnProtocols, field.TypeJSON, value)
}
if value, ok := _u.mutation.AppendedAlpnProtocols(); ok {
_spec.AddModifier(func(u *sql.UpdateBuilder) {
sqljson.Append(u, tlsfingerprintprofile.FieldAlpnProtocols, value)
})
}
if _u.mutation.AlpnProtocolsCleared() {
_spec.ClearField(tlsfingerprintprofile.FieldAlpnProtocols, field.TypeJSON)
}
if value, ok := _u.mutation.SupportedVersions(); ok {
_spec.SetField(tlsfingerprintprofile.FieldSupportedVersions, field.TypeJSON, value)
}
if value, ok := _u.mutation.AppendedSupportedVersions(); ok {
_spec.AddModifier(func(u *sql.UpdateBuilder) {
sqljson.Append(u, tlsfingerprintprofile.FieldSupportedVersions, value)
})
}
if _u.mutation.SupportedVersionsCleared() {
_spec.ClearField(tlsfingerprintprofile.FieldSupportedVersions, field.TypeJSON)
}
if value, ok := _u.mutation.KeyShareGroups(); ok {
_spec.SetField(tlsfingerprintprofile.FieldKeyShareGroups, field.TypeJSON, value)
}
if value, ok := _u.mutation.AppendedKeyShareGroups(); ok {
_spec.AddModifier(func(u *sql.UpdateBuilder) {
sqljson.Append(u, tlsfingerprintprofile.FieldKeyShareGroups, value)
})
}
if _u.mutation.KeyShareGroupsCleared() {
_spec.ClearField(tlsfingerprintprofile.FieldKeyShareGroups, field.TypeJSON)
}
if value, ok := _u.mutation.PskModes(); ok {
_spec.SetField(tlsfingerprintprofile.FieldPskModes, field.TypeJSON, value)
}
if value, ok := _u.mutation.AppendedPskModes(); ok {
_spec.AddModifier(func(u *sql.UpdateBuilder) {
sqljson.Append(u, tlsfingerprintprofile.FieldPskModes, value)
})
}
if _u.mutation.PskModesCleared() {
_spec.ClearField(tlsfingerprintprofile.FieldPskModes, field.TypeJSON)
}
if value, ok := _u.mutation.Extensions(); ok {
_spec.SetField(tlsfingerprintprofile.FieldExtensions, field.TypeJSON, value)
}
if value, ok := _u.mutation.AppendedExtensions(); ok {
_spec.AddModifier(func(u *sql.UpdateBuilder) {
sqljson.Append(u, tlsfingerprintprofile.FieldExtensions, value)
})
}
if _u.mutation.ExtensionsCleared() {
_spec.ClearField(tlsfingerprintprofile.FieldExtensions, field.TypeJSON)
}
if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil {
if _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{tlsfingerprintprofile.Label}
} else if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
return 0, err
}
_u.mutation.done = true
return _node, nil
}
// TLSFingerprintProfileUpdateOne is the builder for updating a single TLSFingerprintProfile entity.
type TLSFingerprintProfileUpdateOne struct {
config
fields []string
hooks []Hook
mutation *TLSFingerprintProfileMutation
}
// SetUpdatedAt sets the "updated_at" field.
func (_u *TLSFingerprintProfileUpdateOne) SetUpdatedAt(v time.Time) *TLSFingerprintProfileUpdateOne {
_u.mutation.SetUpdatedAt(v)
return _u
}
// SetName sets the "name" field.
func (_u *TLSFingerprintProfileUpdateOne) SetName(v string) *TLSFingerprintProfileUpdateOne {
_u.mutation.SetName(v)
return _u
}
// SetNillableName sets the "name" field if the given value is not nil.
func (_u *TLSFingerprintProfileUpdateOne) SetNillableName(v *string) *TLSFingerprintProfileUpdateOne {
if v != nil {
_u.SetName(*v)
}
return _u
}
// SetDescription sets the "description" field.
func (_u *TLSFingerprintProfileUpdateOne) SetDescription(v string) *TLSFingerprintProfileUpdateOne {
_u.mutation.SetDescription(v)
return _u
}
// SetNillableDescription sets the "description" field if the given value is not nil.
func (_u *TLSFingerprintProfileUpdateOne) SetNillableDescription(v *string) *TLSFingerprintProfileUpdateOne {
if v != nil {
_u.SetDescription(*v)
}
return _u
}
// ClearDescription clears the value of the "description" field.
func (_u *TLSFingerprintProfileUpdateOne) ClearDescription() *TLSFingerprintProfileUpdateOne {
_u.mutation.ClearDescription()
return _u
}
// SetEnableGrease sets the "enable_grease" field.
func (_u *TLSFingerprintProfileUpdateOne) SetEnableGrease(v bool) *TLSFingerprintProfileUpdateOne {
_u.mutation.SetEnableGrease(v)
return _u
}
// SetNillableEnableGrease sets the "enable_grease" field if the given value is not nil.
func (_u *TLSFingerprintProfileUpdateOne) SetNillableEnableGrease(v *bool) *TLSFingerprintProfileUpdateOne {
if v != nil {
_u.SetEnableGrease(*v)
}
return _u
}
// SetCipherSuites sets the "cipher_suites" field.
func (_u *TLSFingerprintProfileUpdateOne) SetCipherSuites(v []uint16) *TLSFingerprintProfileUpdateOne {
_u.mutation.SetCipherSuites(v)
return _u
}
// AppendCipherSuites appends value to the "cipher_suites" field.
func (_u *TLSFingerprintProfileUpdateOne) AppendCipherSuites(v []uint16) *TLSFingerprintProfileUpdateOne {
_u.mutation.AppendCipherSuites(v)
return _u
}
// ClearCipherSuites clears the value of the "cipher_suites" field.
func (_u *TLSFingerprintProfileUpdateOne) ClearCipherSuites() *TLSFingerprintProfileUpdateOne {
_u.mutation.ClearCipherSuites()
return _u
}
// SetCurves sets the "curves" field.
func (_u *TLSFingerprintProfileUpdateOne) SetCurves(v []uint16) *TLSFingerprintProfileUpdateOne {
_u.mutation.SetCurves(v)
return _u
}
// AppendCurves appends value to the "curves" field.
func (_u *TLSFingerprintProfileUpdateOne) AppendCurves(v []uint16) *TLSFingerprintProfileUpdateOne {
_u.mutation.AppendCurves(v)
return _u
}
// ClearCurves clears the value of the "curves" field.
func (_u *TLSFingerprintProfileUpdateOne) ClearCurves() *TLSFingerprintProfileUpdateOne {
_u.mutation.ClearCurves()
return _u
}
// SetPointFormats sets the "point_formats" field.
func (_u *TLSFingerprintProfileUpdateOne) SetPointFormats(v []uint16) *TLSFingerprintProfileUpdateOne {
_u.mutation.SetPointFormats(v)
return _u
}
// AppendPointFormats appends value to the "point_formats" field.
func (_u *TLSFingerprintProfileUpdateOne) AppendPointFormats(v []uint16) *TLSFingerprintProfileUpdateOne {
_u.mutation.AppendPointFormats(v)
return _u
}
// ClearPointFormats clears the value of the "point_formats" field.
func (_u *TLSFingerprintProfileUpdateOne) ClearPointFormats() *TLSFingerprintProfileUpdateOne {
_u.mutation.ClearPointFormats()
return _u
}
// SetSignatureAlgorithms sets the "signature_algorithms" field.
func (_u *TLSFingerprintProfileUpdateOne) SetSignatureAlgorithms(v []uint16) *TLSFingerprintProfileUpdateOne {
_u.mutation.SetSignatureAlgorithms(v)
return _u
}
// AppendSignatureAlgorithms appends value to the "signature_algorithms" field.
func (_u *TLSFingerprintProfileUpdateOne) AppendSignatureAlgorithms(v []uint16) *TLSFingerprintProfileUpdateOne {
_u.mutation.AppendSignatureAlgorithms(v)
return _u
}
// ClearSignatureAlgorithms clears the value of the "signature_algorithms" field.
func (_u *TLSFingerprintProfileUpdateOne) ClearSignatureAlgorithms() *TLSFingerprintProfileUpdateOne {
_u.mutation.ClearSignatureAlgorithms()
return _u
}
// SetAlpnProtocols sets the "alpn_protocols" field.
func (_u *TLSFingerprintProfileUpdateOne) SetAlpnProtocols(v []string) *TLSFingerprintProfileUpdateOne {
_u.mutation.SetAlpnProtocols(v)
return _u
}
// AppendAlpnProtocols appends value to the "alpn_protocols" field.
func (_u *TLSFingerprintProfileUpdateOne) AppendAlpnProtocols(v []string) *TLSFingerprintProfileUpdateOne {
_u.mutation.AppendAlpnProtocols(v)
return _u
}
// ClearAlpnProtocols clears the value of the "alpn_protocols" field.
func (_u *TLSFingerprintProfileUpdateOne) ClearAlpnProtocols() *TLSFingerprintProfileUpdateOne {
_u.mutation.ClearAlpnProtocols()
return _u
}
// SetSupportedVersions sets the "supported_versions" field.
func (_u *TLSFingerprintProfileUpdateOne) SetSupportedVersions(v []uint16) *TLSFingerprintProfileUpdateOne {
_u.mutation.SetSupportedVersions(v)
return _u
}
// AppendSupportedVersions appends value to the "supported_versions" field.
func (_u *TLSFingerprintProfileUpdateOne) AppendSupportedVersions(v []uint16) *TLSFingerprintProfileUpdateOne {
_u.mutation.AppendSupportedVersions(v)
return _u
}
// ClearSupportedVersions clears the value of the "supported_versions" field.
func (_u *TLSFingerprintProfileUpdateOne) ClearSupportedVersions() *TLSFingerprintProfileUpdateOne {
_u.mutation.ClearSupportedVersions()
return _u
}
// SetKeyShareGroups sets the "key_share_groups" field.
func (_u *TLSFingerprintProfileUpdateOne) SetKeyShareGroups(v []uint16) *TLSFingerprintProfileUpdateOne {
_u.mutation.SetKeyShareGroups(v)
return _u
}
// AppendKeyShareGroups appends value to the "key_share_groups" field.
func (_u *TLSFingerprintProfileUpdateOne) AppendKeyShareGroups(v []uint16) *TLSFingerprintProfileUpdateOne {
_u.mutation.AppendKeyShareGroups(v)
return _u
}
// ClearKeyShareGroups clears the value of the "key_share_groups" field.
func (_u *TLSFingerprintProfileUpdateOne) ClearKeyShareGroups() *TLSFingerprintProfileUpdateOne {
_u.mutation.ClearKeyShareGroups()
return _u
}
// SetPskModes sets the "psk_modes" field.
func (_u *TLSFingerprintProfileUpdateOne) SetPskModes(v []uint16) *TLSFingerprintProfileUpdateOne {
_u.mutation.SetPskModes(v)
return _u
}
// AppendPskModes appends value to the "psk_modes" field.
func (_u *TLSFingerprintProfileUpdateOne) AppendPskModes(v []uint16) *TLSFingerprintProfileUpdateOne {
_u.mutation.AppendPskModes(v)
return _u
}
// ClearPskModes clears the value of the "psk_modes" field.
func (_u *TLSFingerprintProfileUpdateOne) ClearPskModes() *TLSFingerprintProfileUpdateOne {
_u.mutation.ClearPskModes()
return _u
}
// SetExtensions sets the "extensions" field.
func (_u *TLSFingerprintProfileUpdateOne) SetExtensions(v []uint16) *TLSFingerprintProfileUpdateOne {
_u.mutation.SetExtensions(v)
return _u
}
// AppendExtensions appends value to the "extensions" field.
func (_u *TLSFingerprintProfileUpdateOne) AppendExtensions(v []uint16) *TLSFingerprintProfileUpdateOne {
_u.mutation.AppendExtensions(v)
return _u
}
// ClearExtensions clears the value of the "extensions" field.
func (_u *TLSFingerprintProfileUpdateOne) ClearExtensions() *TLSFingerprintProfileUpdateOne {
_u.mutation.ClearExtensions()
return _u
}
// Mutation returns the TLSFingerprintProfileMutation object of the builder.
func (_u *TLSFingerprintProfileUpdateOne) Mutation() *TLSFingerprintProfileMutation {
return _u.mutation
}
// Where appends a list predicates to the TLSFingerprintProfileUpdate builder.
func (_u *TLSFingerprintProfileUpdateOne) Where(ps ...predicate.TLSFingerprintProfile) *TLSFingerprintProfileUpdateOne {
_u.mutation.Where(ps...)
return _u
}
// Select allows selecting one or more fields (columns) of the returned entity.
// The default is selecting all fields defined in the entity schema.
func (_u *TLSFingerprintProfileUpdateOne) Select(field string, fields ...string) *TLSFingerprintProfileUpdateOne {
_u.fields = append([]string{field}, fields...)
return _u
}
// Save executes the query and returns the updated TLSFingerprintProfile entity.
func (_u *TLSFingerprintProfileUpdateOne) Save(ctx context.Context) (*TLSFingerprintProfile, error) {
_u.defaults()
return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
}
// SaveX is like Save, but panics if an error occurs.
func (_u *TLSFingerprintProfileUpdateOne) SaveX(ctx context.Context) *TLSFingerprintProfile {
node, err := _u.Save(ctx)
if err != nil {
panic(err)
}
return node
}
// Exec executes the query on the entity.
func (_u *TLSFingerprintProfileUpdateOne) Exec(ctx context.Context) error {
_, err := _u.Save(ctx)
return err
}
// ExecX is like Exec, but panics if an error occurs.
func (_u *TLSFingerprintProfileUpdateOne) ExecX(ctx context.Context) {
if err := _u.Exec(ctx); err != nil {
panic(err)
}
}
// defaults sets the default values of the builder before save.
func (_u *TLSFingerprintProfileUpdateOne) defaults() {
if _, ok := _u.mutation.UpdatedAt(); !ok {
v := tlsfingerprintprofile.UpdateDefaultUpdatedAt()
_u.mutation.SetUpdatedAt(v)
}
}
// check runs all checks and user-defined validators on the builder.
func (_u *TLSFingerprintProfileUpdateOne) check() error {
if v, ok := _u.mutation.Name(); ok {
if err := tlsfingerprintprofile.NameValidator(v); err != nil {
return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "TLSFingerprintProfile.name": %w`, err)}
}
}
return nil
}
func (_u *TLSFingerprintProfileUpdateOne) sqlSave(ctx context.Context) (_node *TLSFingerprintProfile, err error) {
if err := _u.check(); err != nil {
return _node, err
}
_spec := sqlgraph.NewUpdateSpec(tlsfingerprintprofile.Table, tlsfingerprintprofile.Columns, sqlgraph.NewFieldSpec(tlsfingerprintprofile.FieldID, field.TypeInt64))
id, ok := _u.mutation.ID()
if !ok {
return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "TLSFingerprintProfile.id" for update`)}
}
_spec.Node.ID.Value = id
if fields := _u.fields; len(fields) > 0 {
_spec.Node.Columns = make([]string, 0, len(fields))
_spec.Node.Columns = append(_spec.Node.Columns, tlsfingerprintprofile.FieldID)
for _, f := range fields {
if !tlsfingerprintprofile.ValidColumn(f) {
return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
}
if f != tlsfingerprintprofile.FieldID {
_spec.Node.Columns = append(_spec.Node.Columns, f)
}
}
}
if ps := _u.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
if value, ok := _u.mutation.UpdatedAt(); ok {
_spec.SetField(tlsfingerprintprofile.FieldUpdatedAt, field.TypeTime, value)
}
if value, ok := _u.mutation.Name(); ok {
_spec.SetField(tlsfingerprintprofile.FieldName, field.TypeString, value)
}
if value, ok := _u.mutation.Description(); ok {
_spec.SetField(tlsfingerprintprofile.FieldDescription, field.TypeString, value)
}
if _u.mutation.DescriptionCleared() {
_spec.ClearField(tlsfingerprintprofile.FieldDescription, field.TypeString)
}
if value, ok := _u.mutation.EnableGrease(); ok {
_spec.SetField(tlsfingerprintprofile.FieldEnableGrease, field.TypeBool, value)
}
if value, ok := _u.mutation.CipherSuites(); ok {
_spec.SetField(tlsfingerprintprofile.FieldCipherSuites, field.TypeJSON, value)
}
if value, ok := _u.mutation.AppendedCipherSuites(); ok {
_spec.AddModifier(func(u *sql.UpdateBuilder) {
sqljson.Append(u, tlsfingerprintprofile.FieldCipherSuites, value)
})
}
if _u.mutation.CipherSuitesCleared() {
_spec.ClearField(tlsfingerprintprofile.FieldCipherSuites, field.TypeJSON)
}
if value, ok := _u.mutation.Curves(); ok {
_spec.SetField(tlsfingerprintprofile.FieldCurves, field.TypeJSON, value)
}
if value, ok := _u.mutation.AppendedCurves(); ok {
_spec.AddModifier(func(u *sql.UpdateBuilder) {
sqljson.Append(u, tlsfingerprintprofile.FieldCurves, value)
})
}
if _u.mutation.CurvesCleared() {
_spec.ClearField(tlsfingerprintprofile.FieldCurves, field.TypeJSON)
}
if value, ok := _u.mutation.PointFormats(); ok {
_spec.SetField(tlsfingerprintprofile.FieldPointFormats, field.TypeJSON, value)
}
if value, ok := _u.mutation.AppendedPointFormats(); ok {
_spec.AddModifier(func(u *sql.UpdateBuilder) {
sqljson.Append(u, tlsfingerprintprofile.FieldPointFormats, value)
})
}
if _u.mutation.PointFormatsCleared() {
_spec.ClearField(tlsfingerprintprofile.FieldPointFormats, field.TypeJSON)
}
if value, ok := _u.mutation.SignatureAlgorithms(); ok {
_spec.SetField(tlsfingerprintprofile.FieldSignatureAlgorithms, field.TypeJSON, value)
}
if value, ok := _u.mutation.AppendedSignatureAlgorithms(); ok {
_spec.AddModifier(func(u *sql.UpdateBuilder) {
sqljson.Append(u, tlsfingerprintprofile.FieldSignatureAlgorithms, value)
})
}
if _u.mutation.SignatureAlgorithmsCleared() {
_spec.ClearField(tlsfingerprintprofile.FieldSignatureAlgorithms, field.TypeJSON)
}
if value, ok := _u.mutation.AlpnProtocols(); ok {
_spec.SetField(tlsfingerprintprofile.FieldAlpnProtocols, field.TypeJSON, value)
}
if value, ok := _u.mutation.AppendedAlpnProtocols(); ok {
_spec.AddModifier(func(u *sql.UpdateBuilder) {
sqljson.Append(u, tlsfingerprintprofile.FieldAlpnProtocols, value)
})
}
if _u.mutation.AlpnProtocolsCleared() {
_spec.ClearField(tlsfingerprintprofile.FieldAlpnProtocols, field.TypeJSON)
}
if value, ok := _u.mutation.SupportedVersions(); ok {
_spec.SetField(tlsfingerprintprofile.FieldSupportedVersions, field.TypeJSON, value)
}
if value, ok := _u.mutation.AppendedSupportedVersions(); ok {
_spec.AddModifier(func(u *sql.UpdateBuilder) {
sqljson.Append(u, tlsfingerprintprofile.FieldSupportedVersions, value)
})
}
if _u.mutation.SupportedVersionsCleared() {
_spec.ClearField(tlsfingerprintprofile.FieldSupportedVersions, field.TypeJSON)
}
if value, ok := _u.mutation.KeyShareGroups(); ok {
_spec.SetField(tlsfingerprintprofile.FieldKeyShareGroups, field.TypeJSON, value)
}
if value, ok := _u.mutation.AppendedKeyShareGroups(); ok {
_spec.AddModifier(func(u *sql.UpdateBuilder) {
sqljson.Append(u, tlsfingerprintprofile.FieldKeyShareGroups, value)
})
}
if _u.mutation.KeyShareGroupsCleared() {
_spec.ClearField(tlsfingerprintprofile.FieldKeyShareGroups, field.TypeJSON)
}
if value, ok := _u.mutation.PskModes(); ok {
_spec.SetField(tlsfingerprintprofile.FieldPskModes, field.TypeJSON, value)
}
if value, ok := _u.mutation.AppendedPskModes(); ok {
_spec.AddModifier(func(u *sql.UpdateBuilder) {
sqljson.Append(u, tlsfingerprintprofile.FieldPskModes, value)
})
}
if _u.mutation.PskModesCleared() {
_spec.ClearField(tlsfingerprintprofile.FieldPskModes, field.TypeJSON)
}
if value, ok := _u.mutation.Extensions(); ok {
_spec.SetField(tlsfingerprintprofile.FieldExtensions, field.TypeJSON, value)
}
if value, ok := _u.mutation.AppendedExtensions(); ok {
_spec.AddModifier(func(u *sql.UpdateBuilder) {
sqljson.Append(u, tlsfingerprintprofile.FieldExtensions, value)
})
}
if _u.mutation.ExtensionsCleared() {
_spec.ClearField(tlsfingerprintprofile.FieldExtensions, field.TypeJSON)
}
_node = &TLSFingerprintProfile{config: _u.config}
_spec.Assign = _node.assignValues
_spec.ScanValues = _node.scanValues
if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil {
if _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{tlsfingerprintprofile.Label}
} else if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
return nil, err
}
_u.mutation.done = true
return _node, nil
}
......@@ -42,6 +42,8 @@ type Tx struct {
SecuritySecret *SecuritySecretClient
// Setting is the client for interacting with the Setting builders.
Setting *SettingClient
// TLSFingerprintProfile is the client for interacting with the TLSFingerprintProfile builders.
TLSFingerprintProfile *TLSFingerprintProfileClient
// UsageCleanupTask is the client for interacting with the UsageCleanupTask builders.
UsageCleanupTask *UsageCleanupTaskClient
// UsageLog is the client for interacting with the UsageLog builders.
......@@ -201,6 +203,7 @@ func (tx *Tx) init() {
tx.RedeemCode = NewRedeemCodeClient(tx.config)
tx.SecuritySecret = NewSecuritySecretClient(tx.config)
tx.Setting = NewSettingClient(tx.config)
tx.TLSFingerprintProfile = NewTLSFingerprintProfileClient(tx.config)
tx.UsageCleanupTask = NewUsageCleanupTaskClient(tx.config)
tx.UsageLog = NewUsageLogClient(tx.config)
tx.User = NewUserClient(tx.config)
......
......@@ -656,17 +656,33 @@ type TLSFingerprintConfig struct {
}
// TLSProfileConfig 单个TLS指纹模板的配置
// 所有列表字段为空时使用内置默认值(Claude CLI 2.x / Node.js 20.x)
// 建议通过 TLS 指纹采集工具 (tests/tls-fingerprint-web) 获取完整配置
type TLSProfileConfig struct {
// Name: 模板显示名称
Name string `mapstructure:"name"`
// EnableGREASE: 是否启用GREASE扩展(Chrome使用,Node.js不使用)
EnableGREASE bool `mapstructure:"enable_grease"`
// CipherSuites: TLS加密套件列表(空则使用内置默认值)
// CipherSuites: TLS加密套件列表
CipherSuites []uint16 `mapstructure:"cipher_suites"`
// Curves: 椭圆曲线列表(空则使用内置默认值)
// Curves: 椭圆曲线列表
Curves []uint16 `mapstructure:"curves"`
// PointFormats: 点格式列表(空则使用内置默认值)
PointFormats []uint8 `mapstructure:"point_formats"`
// PointFormats: 点格式列表
PointFormats []uint16 `mapstructure:"point_formats"`
// SignatureAlgorithms: 签名算法列表
SignatureAlgorithms []uint16 `mapstructure:"signature_algorithms"`
// ALPNProtocols: ALPN协议列表(如 ["h2", "http/1.1"])
ALPNProtocols []string `mapstructure:"alpn_protocols"`
// SupportedVersions: 支持的TLS版本列表(如 [0x0304, 0x0303] 即 TLS1.3, TLS1.2)
SupportedVersions []uint16 `mapstructure:"supported_versions"`
// KeyShareGroups: Key Share中发送的曲线组(如 [29] 即 X25519)
KeyShareGroups []uint16 `mapstructure:"key_share_groups"`
// PSKModes: PSK密钥交换模式(如 [1] 即 psk_dhe_ke)
PSKModes []uint16 `mapstructure:"psk_modes"`
// Extensions: TLS扩展类型ID列表,按发送顺序排列
// 空则使用内置默认顺序 [0,11,10,35,16,22,23,13,43,45,51]
// GREASE值(如0x0a0a)会自动插入GREASE扩展
Extensions []uint16 `mapstructure:"extensions"`
}
// GatewaySchedulingConfig accounts scheduling configuration.
......
......@@ -267,6 +267,9 @@ func (h *AccountHandler) importData(ctx context.Context, req DataImportRequest)
}
}
// 收集需要异步设置隐私的 Antigravity OAuth 账号
var privacyAccounts []*service.Account
for i := range dataPayload.Accounts {
item := dataPayload.Accounts[i]
if err := validateDataAccount(item); err != nil {
......@@ -314,7 +317,8 @@ func (h *AccountHandler) importData(ctx context.Context, req DataImportRequest)
SkipDefaultGroupBind: skipDefaultGroupBind,
}
if _, err := h.adminService.CreateAccount(ctx, accountInput); err != nil {
created, err := h.adminService.CreateAccount(ctx, accountInput)
if err != nil {
result.AccountFailed++
result.Errors = append(result.Errors, DataImportError{
Kind: "account",
......@@ -323,9 +327,30 @@ func (h *AccountHandler) importData(ctx context.Context, req DataImportRequest)
})
continue
}
// 收集 Antigravity OAuth 账号,稍后异步设置隐私
if created.Platform == service.PlatformAntigravity && created.Type == service.AccountTypeOAuth {
privacyAccounts = append(privacyAccounts, created)
}
result.AccountCreated++
}
// 异步设置 Antigravity 隐私,避免大量导入时阻塞请求
if len(privacyAccounts) > 0 {
adminSvc := h.adminService
go func() {
defer func() {
if r := recover(); r != nil {
slog.Error("import_antigravity_privacy_panic", "recover", r)
}
}()
bgCtx := context.Background()
for _, acc := range privacyAccounts {
adminSvc.ForceAntigravityPrivacy(bgCtx, acc)
}
slog.Info("import_antigravity_privacy_done", "count", len(privacyAccounts))
}()
}
return result, nil
}
......
......@@ -9,6 +9,7 @@ import (
"errors"
"fmt"
"log"
"log/slog"
"net/http"
"strconv"
"strings"
......@@ -536,6 +537,10 @@ func (h *AccountHandler) Create(c *gin.Context) {
if execErr != nil {
return nil, execErr
}
// Antigravity OAuth: 新账号直接设置隐私
h.adminService.ForceAntigravityPrivacy(ctx, account)
// OpenAI OAuth: 新账号直接设置隐私
h.adminService.ForceOpenAIPrivacy(ctx, account)
return h.buildAccountResponseWithRuntime(ctx, account), nil
})
if err != nil {
......@@ -782,6 +787,8 @@ func (h *AccountHandler) refreshSingleAccount(ctx context.Context, account *serv
if account.IsOpenAI() {
tokenInfo, err := h.openaiOAuthService.RefreshAccountToken(ctx, account)
if err != nil {
// 刷新失败但 access_token 可能仍有效,尝试设置隐私
h.adminService.EnsureOpenAIPrivacy(ctx, account)
return nil, "", err
}
......@@ -883,6 +890,8 @@ func (h *AccountHandler) refreshSingleAccount(ctx context.Context, account *serv
// OpenAI OAuth: 刷新成功后检查并设置 privacy_mode
h.adminService.EnsureOpenAIPrivacy(ctx, updatedAccount)
// Antigravity OAuth: 刷新成功后检查并设置 privacy_mode
h.adminService.EnsureAntigravityPrivacy(ctx, updatedAccount)
return updatedAccount, "", nil
}
......@@ -1154,6 +1163,9 @@ func (h *AccountHandler) BatchCreate(c *gin.Context) {
success := 0
failed := 0
results := make([]gin.H, 0, len(req.Accounts))
// 收集需要异步设置隐私的 OAuth 账号
var antigravityPrivacyAccounts []*service.Account
var openaiPrivacyAccounts []*service.Account
for _, item := range req.Accounts {
if item.RateMultiplier != nil && *item.RateMultiplier < 0 {
......@@ -1196,6 +1208,15 @@ func (h *AccountHandler) BatchCreate(c *gin.Context) {
})
continue
}
// 收集需要异步设置隐私的 OAuth 账号
if account.Type == service.AccountTypeOAuth {
switch account.Platform {
case service.PlatformAntigravity:
antigravityPrivacyAccounts = append(antigravityPrivacyAccounts, account)
case service.PlatformOpenAI:
openaiPrivacyAccounts = append(openaiPrivacyAccounts, account)
}
}
success++
results = append(results, gin.H{
"name": item.Name,
......@@ -1204,6 +1225,37 @@ func (h *AccountHandler) BatchCreate(c *gin.Context) {
})
}
// 异步设置隐私,避免批量创建时阻塞请求
adminSvc := h.adminService
if len(antigravityPrivacyAccounts) > 0 {
accounts := antigravityPrivacyAccounts
go func() {
defer func() {
if r := recover(); r != nil {
slog.Error("batch_create_antigravity_privacy_panic", "recover", r)
}
}()
bgCtx := context.Background()
for _, acc := range accounts {
adminSvc.ForceAntigravityPrivacy(bgCtx, acc)
}
}()
}
if len(openaiPrivacyAccounts) > 0 {
accounts := openaiPrivacyAccounts
go func() {
defer func() {
if r := recover(); r != nil {
slog.Error("batch_create_openai_privacy_panic", "recover", r)
}
}()
bgCtx := context.Background()
for _, acc := range accounts {
adminSvc.ForceOpenAIPrivacy(bgCtx, acc)
}
}()
}
return gin.H{
"success": success,
"failed": failed,
......@@ -1869,6 +1921,51 @@ func (h *AccountHandler) GetAvailableModels(c *gin.Context) {
response.Success(c, models)
}
// SetPrivacy handles setting privacy for a single OpenAI/Antigravity OAuth account
// POST /api/v1/admin/accounts/:id/set-privacy
func (h *AccountHandler) SetPrivacy(c *gin.Context) {
accountID, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
response.BadRequest(c, "Invalid account ID")
return
}
account, err := h.adminService.GetAccount(c.Request.Context(), accountID)
if err != nil {
response.NotFound(c, "Account not found")
return
}
if account.Type != service.AccountTypeOAuth {
response.BadRequest(c, "Only OAuth accounts support privacy setting")
return
}
var mode string
switch account.Platform {
case service.PlatformOpenAI:
mode = h.adminService.ForceOpenAIPrivacy(c.Request.Context(), account)
case service.PlatformAntigravity:
mode = h.adminService.ForceAntigravityPrivacy(c.Request.Context(), account)
default:
response.BadRequest(c, "Only OpenAI and Antigravity OAuth accounts support privacy setting")
return
}
if mode == "" {
response.BadRequest(c, "Cannot set privacy: missing access_token")
return
}
// 从 DB 重新读取以确保返回最新状态
updated, err := h.adminService.GetAccount(c.Request.Context(), accountID)
if err != nil {
// 隐私已设置成功但读取失败,回退到内存更新
if account.Extra == nil {
account.Extra = make(map[string]any)
}
account.Extra["privacy_mode"] = mode
response.Success(c, h.buildAccountResponseWithRuntime(c.Request.Context(), account))
return
}
response.Success(c, h.buildAccountResponseWithRuntime(c.Request.Context(), updated))
}
// RefreshTier handles refreshing Google One tier for a single account
// POST /api/v1/admin/accounts/:id/refresh-tier
func (h *AccountHandler) RefreshTier(c *gin.Context) {
......
......@@ -445,6 +445,18 @@ func (s *stubAdminService) EnsureOpenAIPrivacy(ctx context.Context, account *ser
return ""
}
func (s *stubAdminService) EnsureAntigravityPrivacy(ctx context.Context, account *service.Account) string {
return ""
}
func (s *stubAdminService) ForceOpenAIPrivacy(ctx context.Context, account *service.Account) string {
return ""
}
func (s *stubAdminService) ForceAntigravityPrivacy(ctx context.Context, account *service.Account) string {
return ""
}
func (s *stubAdminService) ReplaceUserGroup(ctx context.Context, userID, oldGroupID, newGroupID int64) (*service.ReplaceUserGroupResult, error) {
return &service.ReplaceUserGroupResult{MigratedKeys: 0}, nil
}
......
......@@ -129,6 +129,8 @@ func (h *SettingHandler) GetSettings(c *gin.Context) {
MaxClaudeCodeVersion: settings.MaxClaudeCodeVersion,
AllowUngroupedKeyScheduling: settings.AllowUngroupedKeyScheduling,
BackendModeEnabled: settings.BackendModeEnabled,
EnableFingerprintUnification: settings.EnableFingerprintUnification,
EnableMetadataPassthrough: settings.EnableMetadataPassthrough,
})
}
......@@ -209,6 +211,10 @@ type UpdateSettingsRequest struct {
// Backend Mode
BackendModeEnabled bool `json:"backend_mode_enabled"`
// Gateway forwarding behavior
EnableFingerprintUnification *bool `json:"enable_fingerprint_unification"`
EnableMetadataPassthrough *bool `json:"enable_metadata_passthrough"`
}
// UpdateSettings 更新系统设置
......@@ -601,6 +607,18 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
}
return previousSettings.OpsMetricsIntervalSeconds
}(),
EnableFingerprintUnification: func() bool {
if req.EnableFingerprintUnification != nil {
return *req.EnableFingerprintUnification
}
return previousSettings.EnableFingerprintUnification
}(),
EnableMetadataPassthrough: func() bool {
if req.EnableMetadataPassthrough != nil {
return *req.EnableMetadataPassthrough
}
return previousSettings.EnableMetadataPassthrough
}(),
}
if err := h.settingService.UpdateSettings(c.Request.Context(), settings); err != nil {
......@@ -679,6 +697,8 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
MaxClaudeCodeVersion: updatedSettings.MaxClaudeCodeVersion,
AllowUngroupedKeyScheduling: updatedSettings.AllowUngroupedKeyScheduling,
BackendModeEnabled: updatedSettings.BackendModeEnabled,
EnableFingerprintUnification: updatedSettings.EnableFingerprintUnification,
EnableMetadataPassthrough: updatedSettings.EnableMetadataPassthrough,
})
}
......@@ -851,6 +871,12 @@ func diffSettings(before *service.SystemSettings, after *service.SystemSettings,
if before.CustomMenuItems != after.CustomMenuItems {
changed = append(changed, "custom_menu_items")
}
if before.EnableFingerprintUnification != after.EnableFingerprintUnification {
changed = append(changed, "enable_fingerprint_unification")
}
if before.EnableMetadataPassthrough != after.EnableMetadataPassthrough {
changed = append(changed, "enable_metadata_passthrough")
}
return changed
}
......@@ -1568,10 +1594,16 @@ func (h *SettingHandler) GetRectifierSettings(c *gin.Context) {
return
}
patterns := settings.APIKeySignaturePatterns
if patterns == nil {
patterns = []string{}
}
response.Success(c, dto.RectifierSettings{
Enabled: settings.Enabled,
ThinkingSignatureEnabled: settings.ThinkingSignatureEnabled,
ThinkingBudgetEnabled: settings.ThinkingBudgetEnabled,
APIKeySignatureEnabled: settings.APIKeySignatureEnabled,
APIKeySignaturePatterns: patterns,
})
}
......@@ -1580,6 +1612,8 @@ type UpdateRectifierSettingsRequest struct {
Enabled bool `json:"enabled"`
ThinkingSignatureEnabled bool `json:"thinking_signature_enabled"`
ThinkingBudgetEnabled bool `json:"thinking_budget_enabled"`
APIKeySignatureEnabled bool `json:"apikey_signature_enabled"`
APIKeySignaturePatterns []string `json:"apikey_signature_patterns"`
}
// UpdateRectifierSettings 更新请求整流器配置
......@@ -1591,10 +1625,32 @@ func (h *SettingHandler) UpdateRectifierSettings(c *gin.Context) {
return
}
// 校验并清理自定义匹配关键词
const maxPatterns = 50
const maxPatternLen = 500
if len(req.APIKeySignaturePatterns) > maxPatterns {
response.BadRequest(c, "Too many signature patterns (max 50)")
return
}
var cleanedPatterns []string
for _, p := range req.APIKeySignaturePatterns {
p = strings.TrimSpace(p)
if p == "" {
continue
}
if len(p) > maxPatternLen {
response.BadRequest(c, "Signature pattern too long (max 500 characters)")
return
}
cleanedPatterns = append(cleanedPatterns, p)
}
settings := &service.RectifierSettings{
Enabled: req.Enabled,
ThinkingSignatureEnabled: req.ThinkingSignatureEnabled,
ThinkingBudgetEnabled: req.ThinkingBudgetEnabled,
APIKeySignatureEnabled: req.APIKeySignatureEnabled,
APIKeySignaturePatterns: cleanedPatterns,
}
if err := h.settingService.SetRectifierSettings(c.Request.Context(), settings); err != nil {
......@@ -1609,10 +1665,16 @@ func (h *SettingHandler) UpdateRectifierSettings(c *gin.Context) {
return
}
updatedPatterns := updatedSettings.APIKeySignaturePatterns
if updatedPatterns == nil {
updatedPatterns = []string{}
}
response.Success(c, dto.RectifierSettings{
Enabled: updatedSettings.Enabled,
ThinkingSignatureEnabled: updatedSettings.ThinkingSignatureEnabled,
ThinkingBudgetEnabled: updatedSettings.ThinkingBudgetEnabled,
APIKeySignatureEnabled: updatedSettings.APIKeySignatureEnabled,
APIKeySignaturePatterns: updatedPatterns,
})
}
......
package admin
import (
"strconv"
"github.com/Wei-Shaw/sub2api/internal/model"
"github.com/Wei-Shaw/sub2api/internal/pkg/response"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/gin-gonic/gin"
)
// TLSFingerprintProfileHandler 处理 TLS 指纹模板的 HTTP 请求
type TLSFingerprintProfileHandler struct {
service *service.TLSFingerprintProfileService
}
// NewTLSFingerprintProfileHandler 创建 TLS 指纹模板处理器
func NewTLSFingerprintProfileHandler(service *service.TLSFingerprintProfileService) *TLSFingerprintProfileHandler {
return &TLSFingerprintProfileHandler{service: service}
}
// CreateTLSFingerprintProfileRequest 创建模板请求
type CreateTLSFingerprintProfileRequest struct {
Name string `json:"name" binding:"required"`
Description *string `json:"description"`
EnableGREASE *bool `json:"enable_grease"`
CipherSuites []uint16 `json:"cipher_suites"`
Curves []uint16 `json:"curves"`
PointFormats []uint16 `json:"point_formats"`
SignatureAlgorithms []uint16 `json:"signature_algorithms"`
ALPNProtocols []string `json:"alpn_protocols"`
SupportedVersions []uint16 `json:"supported_versions"`
KeyShareGroups []uint16 `json:"key_share_groups"`
PSKModes []uint16 `json:"psk_modes"`
Extensions []uint16 `json:"extensions"`
}
// UpdateTLSFingerprintProfileRequest 更新模板请求(部分更新)
type UpdateTLSFingerprintProfileRequest struct {
Name *string `json:"name"`
Description *string `json:"description"`
EnableGREASE *bool `json:"enable_grease"`
CipherSuites []uint16 `json:"cipher_suites"`
Curves []uint16 `json:"curves"`
PointFormats []uint16 `json:"point_formats"`
SignatureAlgorithms []uint16 `json:"signature_algorithms"`
ALPNProtocols []string `json:"alpn_protocols"`
SupportedVersions []uint16 `json:"supported_versions"`
KeyShareGroups []uint16 `json:"key_share_groups"`
PSKModes []uint16 `json:"psk_modes"`
Extensions []uint16 `json:"extensions"`
}
// List 获取所有模板
// GET /api/v1/admin/tls-fingerprint-profiles
func (h *TLSFingerprintProfileHandler) List(c *gin.Context) {
profiles, err := h.service.List(c.Request.Context())
if err != nil {
response.ErrorFrom(c, err)
return
}
response.Success(c, profiles)
}
// GetByID 根据 ID 获取模板
// GET /api/v1/admin/tls-fingerprint-profiles/:id
func (h *TLSFingerprintProfileHandler) GetByID(c *gin.Context) {
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
response.BadRequest(c, "Invalid profile ID")
return
}
profile, err := h.service.GetByID(c.Request.Context(), id)
if err != nil {
response.ErrorFrom(c, err)
return
}
if profile == nil {
response.NotFound(c, "Profile not found")
return
}
response.Success(c, profile)
}
// Create 创建模板
// POST /api/v1/admin/tls-fingerprint-profiles
func (h *TLSFingerprintProfileHandler) Create(c *gin.Context) {
var req CreateTLSFingerprintProfileRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "Invalid request: "+err.Error())
return
}
profile := &model.TLSFingerprintProfile{
Name: req.Name,
Description: req.Description,
CipherSuites: req.CipherSuites,
Curves: req.Curves,
PointFormats: req.PointFormats,
SignatureAlgorithms: req.SignatureAlgorithms,
ALPNProtocols: req.ALPNProtocols,
SupportedVersions: req.SupportedVersions,
KeyShareGroups: req.KeyShareGroups,
PSKModes: req.PSKModes,
Extensions: req.Extensions,
}
if req.EnableGREASE != nil {
profile.EnableGREASE = *req.EnableGREASE
}
created, err := h.service.Create(c.Request.Context(), profile)
if err != nil {
if _, ok := err.(*model.ValidationError); ok {
response.BadRequest(c, err.Error())
return
}
response.ErrorFrom(c, err)
return
}
response.Success(c, created)
}
// Update 更新模板(支持部分更新)
// PUT /api/v1/admin/tls-fingerprint-profiles/:id
func (h *TLSFingerprintProfileHandler) Update(c *gin.Context) {
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
response.BadRequest(c, "Invalid profile ID")
return
}
var req UpdateTLSFingerprintProfileRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "Invalid request: "+err.Error())
return
}
existing, err := h.service.GetByID(c.Request.Context(), id)
if err != nil {
response.ErrorFrom(c, err)
return
}
if existing == nil {
response.NotFound(c, "Profile not found")
return
}
// 部分更新
profile := &model.TLSFingerprintProfile{
ID: id,
Name: existing.Name,
Description: existing.Description,
EnableGREASE: existing.EnableGREASE,
CipherSuites: existing.CipherSuites,
Curves: existing.Curves,
PointFormats: existing.PointFormats,
SignatureAlgorithms: existing.SignatureAlgorithms,
ALPNProtocols: existing.ALPNProtocols,
SupportedVersions: existing.SupportedVersions,
KeyShareGroups: existing.KeyShareGroups,
PSKModes: existing.PSKModes,
Extensions: existing.Extensions,
}
if req.Name != nil {
profile.Name = *req.Name
}
if req.Description != nil {
profile.Description = req.Description
}
if req.EnableGREASE != nil {
profile.EnableGREASE = *req.EnableGREASE
}
if req.CipherSuites != nil {
profile.CipherSuites = req.CipherSuites
}
if req.Curves != nil {
profile.Curves = req.Curves
}
if req.PointFormats != nil {
profile.PointFormats = req.PointFormats
}
if req.SignatureAlgorithms != nil {
profile.SignatureAlgorithms = req.SignatureAlgorithms
}
if req.ALPNProtocols != nil {
profile.ALPNProtocols = req.ALPNProtocols
}
if req.SupportedVersions != nil {
profile.SupportedVersions = req.SupportedVersions
}
if req.KeyShareGroups != nil {
profile.KeyShareGroups = req.KeyShareGroups
}
if req.PSKModes != nil {
profile.PSKModes = req.PSKModes
}
if req.Extensions != nil {
profile.Extensions = req.Extensions
}
updated, err := h.service.Update(c.Request.Context(), profile)
if err != nil {
if _, ok := err.(*model.ValidationError); ok {
response.BadRequest(c, err.Error())
return
}
response.ErrorFrom(c, err)
return
}
response.Success(c, updated)
}
// Delete 删除模板
// DELETE /api/v1/admin/tls-fingerprint-profiles/:id
func (h *TLSFingerprintProfileHandler) Delete(c *gin.Context) {
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
response.BadRequest(c, "Invalid profile ID")
return
}
if err := h.service.Delete(c.Request.Context(), id); err != nil {
response.ErrorFrom(c, err)
return
}
response.Success(c, gin.H{"message": "Profile deleted successfully"})
}
......@@ -252,6 +252,10 @@ func AccountFromServiceShallow(a *service.Account) *Account {
enabled := true
out.EnableTLSFingerprint = &enabled
}
// TLS指纹模板ID
if profileID := a.GetTLSFingerprintProfileID(); profileID > 0 {
out.TLSFingerprintProfileID = &profileID
}
// 会话ID伪装开关
if a.IsSessionIDMaskingEnabled() {
enabled := true
......
......@@ -94,6 +94,10 @@ type SystemSettings struct {
// Backend Mode
BackendModeEnabled bool `json:"backend_mode_enabled"`
// Gateway forwarding behavior
EnableFingerprintUnification bool `json:"enable_fingerprint_unification"`
EnableMetadataPassthrough bool `json:"enable_metadata_passthrough"`
}
type DefaultSubscriptionSetting struct {
......@@ -187,6 +191,8 @@ type RectifierSettings struct {
Enabled bool `json:"enabled"`
ThinkingSignatureEnabled bool `json:"thinking_signature_enabled"`
ThinkingBudgetEnabled bool `json:"thinking_budget_enabled"`
APIKeySignatureEnabled bool `json:"apikey_signature_enabled"`
APIKeySignaturePatterns []string `json:"apikey_signature_patterns"`
}
// BetaPolicyRule Beta 策略规则 DTO
......
......@@ -186,6 +186,7 @@ type Account struct {
// TLS指纹伪装(仅 Anthropic OAuth/SetupToken 账号有效)
// 从 extra 字段提取,方便前端显示和编辑
EnableTLSFingerprint *bool `json:"enable_tls_fingerprint,omitempty"`
TLSFingerprintProfileID *int64 `json:"tls_fingerprint_profile_id,omitempty"`
// 会话ID伪装(仅 Anthropic OAuth/SetupToken 账号有效)
// 启用后将在15分钟内固定 metadata.user_id 中的 session ID
......
......@@ -422,11 +422,24 @@ func (h *GatewayHandler) Messages(c *gin.Context) {
}
}
wroteFallback := h.ensureForwardErrorResponse(c, streamStarted)
reqLog.Error("gateway.forward_failed",
forwardFailedFields := []zap.Field{
zap.Int64("account_id", account.ID),
zap.String("account_name", account.Name),
zap.String("account_platform", account.Platform),
zap.Bool("fallback_error_response_written", wroteFallback),
zap.Error(err),
}
if account.Proxy != nil {
forwardFailedFields = append(forwardFailedFields,
zap.Int64("proxy_id", account.Proxy.ID),
zap.String("proxy_name", account.Proxy.Name),
zap.String("proxy_host", account.Proxy.Host),
zap.Int("proxy_port", account.Proxy.Port),
)
} else if account.ProxyID != nil {
forwardFailedFields = append(forwardFailedFields, zap.Int64p("proxy_id", account.ProxyID))
}
reqLog.Error("gateway.forward_failed", forwardFailedFields...)
return
}
......@@ -741,11 +754,24 @@ func (h *GatewayHandler) Messages(c *gin.Context) {
}
}
wroteFallback := h.ensureForwardErrorResponse(c, streamStarted)
reqLog.Error("gateway.forward_failed",
forwardFailedFields := []zap.Field{
zap.Int64("account_id", account.ID),
zap.String("account_name", account.Name),
zap.String("account_platform", account.Platform),
zap.Bool("fallback_error_response_written", wroteFallback),
zap.Error(err),
}
if account.Proxy != nil {
forwardFailedFields = append(forwardFailedFields,
zap.Int64("proxy_id", account.Proxy.ID),
zap.String("proxy_name", account.Proxy.Name),
zap.String("proxy_host", account.Proxy.Host),
zap.Int("proxy_port", account.Proxy.Port),
)
} else if account.ProxyID != nil {
forwardFailedFields = append(forwardFailedFields, zap.Int64p("proxy_id", account.ProxyID))
}
reqLog.Error("gateway.forward_failed", forwardFailedFields...)
return
}
......
......@@ -76,7 +76,9 @@ func (f *fakeGroupRepo) ListActiveByPlatform(context.Context, string) ([]service
return nil, nil
}
func (f *fakeGroupRepo) ExistsByName(context.Context, string) (bool, error) { return false, nil }
func (f *fakeGroupRepo) GetAccountCount(context.Context, int64) (int64, int64, error) { return 0, 0, nil }
func (f *fakeGroupRepo) GetAccountCount(context.Context, int64) (int64, int64, error) {
return 0, 0, nil
}
func (f *fakeGroupRepo) DeleteAccountGroupsByGroupID(context.Context, int64) (int64, error) {
return 0, nil
}
......@@ -158,6 +160,7 @@ func newTestGatewayHandler(t *testing.T, group *service.Group, accounts []*servi
nil, // rpmCache
nil, // digestStore
nil, // settingService
nil, // tlsFPProfileService
)
// RunModeSimple:跳过计费检查,避免引入 repo/cache 依赖。
......
......@@ -27,6 +27,7 @@ type AdminHandlers struct {
Usage *admin.UsageHandler
UserAttribute *admin.UserAttributeHandler
ErrorPassthrough *admin.ErrorPassthroughHandler
TLSFingerprintProfile *admin.TLSFingerprintProfileHandler
APIKey *admin.AdminAPIKeyHandler
ScheduledTest *admin.ScheduledTestHandler
}
......
......@@ -2224,7 +2224,7 @@ func (s *stubSoraClientForHandler) GetVideoTask(_ context.Context, _ *service.Ac
func newMinimalGatewayService(accountRepo service.AccountRepository) *service.GatewayService {
return service.NewGatewayService(
accountRepo, nil, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
)
}
......
......@@ -464,6 +464,7 @@ func TestSoraGatewayHandler_ChatCompletions(t *testing.T) {
nil, // rpmCache
nil, // digestStore
nil, // settingService
nil, // tlsFPProfileService
)
soraClient := &stubSoraClient{imageURLs: []string{"https://example.com/a.png"}}
......
......@@ -30,6 +30,7 @@ func ProvideAdminHandlers(
usageHandler *admin.UsageHandler,
userAttributeHandler *admin.UserAttributeHandler,
errorPassthroughHandler *admin.ErrorPassthroughHandler,
tlsFingerprintProfileHandler *admin.TLSFingerprintProfileHandler,
apiKeyHandler *admin.AdminAPIKeyHandler,
scheduledTestHandler *admin.ScheduledTestHandler,
) *AdminHandlers {
......@@ -55,6 +56,7 @@ func ProvideAdminHandlers(
Usage: usageHandler,
UserAttribute: userAttributeHandler,
ErrorPassthrough: errorPassthroughHandler,
TLSFingerprintProfile: tlsFingerprintProfileHandler,
APIKey: apiKeyHandler,
ScheduledTest: scheduledTestHandler,
}
......@@ -145,6 +147,7 @@ var ProviderSet = wire.NewSet(
admin.NewUsageHandler,
admin.NewUserAttributeHandler,
admin.NewErrorPassthroughHandler,
admin.NewTLSFingerprintProfileHandler,
admin.NewAdminAPIKeyHandler,
admin.NewScheduledTestHandler,
......
// Package model 定义服务层使用的数据模型。
package model
import (
"time"
"github.com/Wei-Shaw/sub2api/internal/pkg/tlsfingerprint"
)
// TLSFingerprintProfile TLS 指纹配置模板
// 包含完整的 ClientHello 参数,用于模拟特定客户端的 TLS 握手特征
type TLSFingerprintProfile struct {
ID int64 `json:"id"`
Name string `json:"name"`
Description *string `json:"description"`
EnableGREASE bool `json:"enable_grease"`
CipherSuites []uint16 `json:"cipher_suites"`
Curves []uint16 `json:"curves"`
PointFormats []uint16 `json:"point_formats"`
SignatureAlgorithms []uint16 `json:"signature_algorithms"`
ALPNProtocols []string `json:"alpn_protocols"`
SupportedVersions []uint16 `json:"supported_versions"`
KeyShareGroups []uint16 `json:"key_share_groups"`
PSKModes []uint16 `json:"psk_modes"`
Extensions []uint16 `json:"extensions"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// Validate 验证模板配置的有效性
func (p *TLSFingerprintProfile) Validate() error {
if p.Name == "" {
return &ValidationError{Field: "name", Message: "name is required"}
}
return nil
}
// ToTLSProfile 将领域模型转换为运行时使用的 tlsfingerprint.Profile
// 空切片字段会在 dialer 中 fallback 到内置默认值
func (p *TLSFingerprintProfile) ToTLSProfile() *tlsfingerprint.Profile {
return &tlsfingerprint.Profile{
Name: p.Name,
EnableGREASE: p.EnableGREASE,
CipherSuites: p.CipherSuites,
Curves: p.Curves,
PointFormats: p.PointFormats,
SignatureAlgorithms: p.SignatureAlgorithms,
ALPNProtocols: p.ALPNProtocols,
SupportedVersions: p.SupportedVersions,
KeyShareGroups: p.KeyShareGroups,
PSKModes: p.PSKModes,
Extensions: p.Extensions,
}
}
......@@ -79,6 +79,8 @@ type UserInfo struct {
type LoadCodeAssistRequest struct {
Metadata struct {
IDEType string `json:"ideType"`
IDEVersion string `json:"ideVersion"`
IDEName string `json:"ideName"`
} `json:"metadata"`
}
......@@ -223,6 +225,23 @@ func (r *LoadCodeAssistResponse) GetAvailableCredits() []AvailableCredit {
return r.PaidTier.AvailableCredits
}
// TierIDToPlanType 将 tier ID 映射为用户可见的套餐名。
func TierIDToPlanType(tierID string) string {
switch strings.ToLower(strings.TrimSpace(tierID)) {
case "free-tier":
return "Free"
case "g1-pro-tier":
return "Pro"
case "g1-ultra-tier":
return "Ultra"
default:
if tierID == "" {
return "Free"
}
return tierID
}
}
// Client Antigravity API 客户端
type Client struct {
httpClient *http.Client
......@@ -421,6 +440,8 @@ func (c *Client) GetUserInfo(ctx context.Context, accessToken string) (*UserInfo
func (c *Client) LoadCodeAssist(ctx context.Context, accessToken string) (*LoadCodeAssistResponse, map[string]any, error) {
reqBody := LoadCodeAssistRequest{}
reqBody.Metadata.IDEType = "ANTIGRAVITY"
reqBody.Metadata.IDEVersion = "1.20.6"
reqBody.Metadata.IDEName = "antigravity"
bodyBytes, err := json.Marshal(reqBody)
if err != nil {
......@@ -704,3 +725,139 @@ func (c *Client) FetchAvailableModels(ctx context.Context, accessToken, projectI
return nil, nil, lastErr
}
// ── Privacy API ──────────────────────────────────────────────────────
// privacyBaseURL 隐私设置 API 仅使用 daily 端点(与 Antigravity 客户端行为一致)
const privacyBaseURL = antigravityDailyBaseURL
// SetUserSettingsRequest setUserSettings 请求体
type SetUserSettingsRequest struct {
UserSettings map[string]any `json:"user_settings"`
}
// FetchUserInfoRequest fetchUserInfo 请求体
type FetchUserInfoRequest struct {
Project string `json:"project"`
}
// FetchUserInfoResponse fetchUserInfo 响应体
type FetchUserInfoResponse struct {
UserSettings map[string]any `json:"userSettings,omitempty"`
RegionCode string `json:"regionCode,omitempty"`
}
// IsPrivate 判断隐私是否已设置:userSettings 为空或不含 telemetryEnabled 表示已设置
func (r *FetchUserInfoResponse) IsPrivate() bool {
if r == nil || r.UserSettings == nil {
return true
}
_, hasTelemetry := r.UserSettings["telemetryEnabled"]
return !hasTelemetry
}
// SetUserSettingsResponse setUserSettings 响应体
type SetUserSettingsResponse struct {
UserSettings map[string]any `json:"userSettings,omitempty"`
}
// IsSuccess 判断 setUserSettings 是否成功:返回 {"userSettings":{}} 且无 telemetryEnabled
func (r *SetUserSettingsResponse) IsSuccess() bool {
if r == nil {
return false
}
// userSettings 为 nil 或空 map 均视为成功
if len(r.UserSettings) == 0 {
return true
}
// 如果包含 telemetryEnabled 字段,说明未成功清除
_, hasTelemetry := r.UserSettings["telemetryEnabled"]
return !hasTelemetry
}
// SetUserSettings 调用 setUserSettings API 设置用户隐私,返回解析后的响应
func (c *Client) SetUserSettings(ctx context.Context, accessToken string) (*SetUserSettingsResponse, error) {
// 发送空 user_settings 以清除隐私设置
payload := SetUserSettingsRequest{UserSettings: map[string]any{}}
bodyBytes, err := json.Marshal(payload)
if err != nil {
return nil, fmt.Errorf("序列化请求失败: %w", err)
}
apiURL := privacyBaseURL + "/v1internal:setUserSettings"
req, err := http.NewRequestWithContext(ctx, http.MethodPost, apiURL, bytes.NewReader(bodyBytes))
if err != nil {
return nil, fmt.Errorf("创建请求失败: %w", err)
}
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "*/*")
req.Header.Set("User-Agent", GetUserAgent())
req.Header.Set("X-Goog-Api-Client", "gl-node/22.21.1")
req.Host = "daily-cloudcode-pa.googleapis.com"
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("setUserSettings 请求失败: %w", err)
}
defer func() { _ = resp.Body.Close() }()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("读取响应失败: %w", err)
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("setUserSettings 失败 (HTTP %d): %s", resp.StatusCode, string(respBody))
}
var result SetUserSettingsResponse
if err := json.Unmarshal(respBody, &result); err != nil {
return nil, fmt.Errorf("响应解析失败: %w", err)
}
return &result, nil
}
// FetchUserInfo 调用 fetchUserInfo API 获取用户隐私设置状态
func (c *Client) FetchUserInfo(ctx context.Context, accessToken, projectID string) (*FetchUserInfoResponse, error) {
reqBody := FetchUserInfoRequest{Project: projectID}
bodyBytes, err := json.Marshal(reqBody)
if err != nil {
return nil, fmt.Errorf("序列化请求失败: %w", err)
}
apiURL := privacyBaseURL + "/v1internal:fetchUserInfo"
req, err := http.NewRequestWithContext(ctx, http.MethodPost, apiURL, bytes.NewReader(bodyBytes))
if err != nil {
return nil, fmt.Errorf("创建请求失败: %w", err)
}
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "*/*")
req.Header.Set("User-Agent", GetUserAgent())
req.Header.Set("X-Goog-Api-Client", "gl-node/22.21.1")
req.Host = "daily-cloudcode-pa.googleapis.com"
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("fetchUserInfo 请求失败: %w", err)
}
defer func() { _ = resp.Body.Close() }()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("读取响应失败: %w", err)
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("fetchUserInfo 失败 (HTTP %d): %s", resp.StatusCode, string(respBody))
}
var result FetchUserInfoResponse
if err := json.Unmarshal(respBody, &result); err != nil {
return nil, fmt.Errorf("响应解析失败: %w", err)
}
return &result, nil
}
......@@ -250,6 +250,27 @@ func TestGetTier_两者都为nil(t *testing.T) {
}
}
func TestTierIDToPlanType(t *testing.T) {
tests := []struct {
tierID string
want string
}{
{"free-tier", "Free"},
{"g1-pro-tier", "Pro"},
{"g1-ultra-tier", "Ultra"},
{"FREE-TIER", "Free"},
{"", "Free"},
{"unknown-tier", "unknown-tier"},
}
for _, tt := range tests {
t.Run(tt.tierID, func(t *testing.T) {
if got := TierIDToPlanType(tt.tierID); got != tt.want {
t.Errorf("TierIDToPlanType(%q) = %q, want %q", tt.tierID, got, tt.want)
}
})
}
}
// ---------------------------------------------------------------------------
// NewClient
// ---------------------------------------------------------------------------
......@@ -800,6 +821,12 @@ type redirectRoundTripper struct {
transport http.RoundTripper
}
type roundTripperFunc func(*http.Request) (*http.Response, error)
func (f roundTripperFunc) RoundTrip(req *http.Request) (*http.Response, error) {
return f(req)
}
func (rt *redirectRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
originalURL := req.URL.String()
for prefix, target := range rt.redirects {
......@@ -1271,6 +1298,12 @@ func TestClient_LoadCodeAssist_Success_RealCall(t *testing.T) {
if reqBody.Metadata.IDEType != "ANTIGRAVITY" {
t.Errorf("IDEType 不匹配: got %s, want ANTIGRAVITY", reqBody.Metadata.IDEType)
}
if strings.TrimSpace(reqBody.Metadata.IDEVersion) == "" {
t.Errorf("IDEVersion 不应为空")
}
if reqBody.Metadata.IDEName != "antigravity" {
t.Errorf("IDEName 不匹配: got %s, want antigravity", reqBody.Metadata.IDEName)
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment