Skip to content

Commit 0cce395

Browse files
committed
修复用户插入数据库问题
1 parent b80a146 commit 0cce395

8 files changed

Lines changed: 834 additions & 16 deletions

File tree

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ temp/
8686
.cache/
8787
.dev/
8888
.serena/
89-
89+
test/
9090
# ===================
9191
# 构建产物
9292
# ===================
@@ -131,8 +131,10 @@ docs/*
131131
!docs/PAYMENT.md
132132
!docs/PAYMENT_CN.md
133133
!docs/ADMIN_PAYMENT_INTEGRATION_API.md
134+
!docs/OFFICIAL_UPDATE_AND_DEPLOY_CN.md
134135
.serena/
135136
.codex/
136137
frontend/coverage/
137138
aicodex
138139
output/
140+
test/images/

backend/internal/service/account_credential_import.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import (
1010

1111
const MaxAccountCredentialImportItems = 200
1212

13+
const codexManagerOpenAIIssuer = "https://auth.openai.com"
14+
1315
type AccountCredentialImportKind string
1416

1517
const (
@@ -218,6 +220,10 @@ func accountCredentialImportSourcesFromValue(value any) ([]AccountCredentialImpo
218220
}
219221

220222
func accountCredentialImportSourceFromMap(item map[string]any) (AccountCredentialImportSource, error) {
223+
if source, handled, err := accountCredentialImportSourceFromCodexManagerExport(item); handled || err != nil {
224+
return source, err
225+
}
226+
221227
if field, found := findDisallowedCredentialImportField(item); found {
222228
return AccountCredentialImportSource{}, fmt.Errorf("disallowed credential field: %s", field)
223229
}
@@ -342,6 +348,69 @@ func accountCredentialImportSourceFromMap(item map[string]any) (AccountCredentia
342348
return AccountCredentialImportSource{}, fmt.Errorf("unsupported credential import item")
343349
}
344350

351+
func accountCredentialImportSourceFromCodexManagerExport(item map[string]any) (AccountCredentialImportSource, bool, error) {
352+
tokens := importMapField(item, "tokens")
353+
meta := importMapField(item, "meta")
354+
if len(tokens) == 0 || len(meta) == 0 {
355+
return AccountCredentialImportSource{}, false, nil
356+
}
357+
358+
topLevel := copyImportMap(item)
359+
removeImportMapField(topLevel, "tokens")
360+
removeImportMapField(topLevel, "meta")
361+
if field, found := findDisallowedCredentialImportField(topLevel); found {
362+
return AccountCredentialImportSource{}, true, fmt.Errorf("disallowed credential field: %s", field)
363+
}
364+
if field, found := findDisallowedCredentialImportField(tokens); found {
365+
return AccountCredentialImportSource{}, true, fmt.Errorf("disallowed credential field: %s", field)
366+
}
367+
metaForSafety := copyImportMap(meta)
368+
removeImportMapField(metaForSafety, "issuer")
369+
if field, found := findDisallowedCredentialImportField(metaForSafety); found {
370+
return AccountCredentialImportSource{}, true, fmt.Errorf("disallowed credential field: %s", field)
371+
}
372+
373+
issuer := strings.TrimRight(strings.TrimSpace(importStringField(meta, "issuer")), "/")
374+
if issuer != "" && !strings.EqualFold(issuer, codexManagerOpenAIIssuer) {
375+
return AccountCredentialImportSource{}, true, fmt.Errorf("unsupported Codex-Manager issuer: %s", issuer)
376+
}
377+
378+
accessToken := importStringField(tokens, "access_token", "accessToken")
379+
if accessToken == "" {
380+
return AccountCredentialImportSource{}, true, fmt.Errorf("OAuth credentials must include access_token")
381+
}
382+
383+
credentials := map[string]any{
384+
"access_token": accessToken,
385+
}
386+
if idToken := importStringField(tokens, "id_token", "idToken"); idToken != "" {
387+
credentials["id_token"] = idToken
388+
}
389+
if refreshToken := importStringField(tokens, "refresh_token", "refreshToken"); refreshToken != "" {
390+
credentials["refresh_token"] = refreshToken
391+
}
392+
if chatgptAccountID := importStringField(meta, "chatgpt_account_id", "chatgptAccountId"); chatgptAccountID != "" {
393+
credentials["chatgpt_account_id"] = chatgptAccountID
394+
}
395+
if workspaceID := importStringField(meta, "workspace_id", "workspaceId"); workspaceID != "" {
396+
credentials["workspace_id"] = workspaceID
397+
}
398+
399+
notes := importOptionalStringField(meta, "note", "notes", "description")
400+
if notes == nil {
401+
notes = importOptionalStringField(item, "note", "notes", "description")
402+
}
403+
404+
return AccountCredentialImportSource{
405+
Kind: AccountCredentialImportKindOAuthCredentials,
406+
Name: credentialImportFirstNonEmptyString(importStringField(meta, "label", "name"), importStringField(item, "name", "label")),
407+
Notes: notes,
408+
Platform: PlatformOpenAI,
409+
Credentials: credentials,
410+
Extra: map[string]any{},
411+
}, true, nil
412+
}
413+
345414
func accountCredentialImportSourceFromString(value, name string, notes *string) (AccountCredentialImportSource, error) {
346415
text := strings.TrimSpace(value)
347416
if text == "" {
@@ -469,6 +538,15 @@ func copyImportMap(values map[string]any) map[string]any {
469538
return out
470539
}
471540

541+
func removeImportMapField(values map[string]any, key string) {
542+
normalizedTarget := normalizeCredentialImportKey(key)
543+
for existingKey := range values {
544+
if normalizeCredentialImportKey(existingKey) == normalizedTarget {
545+
delete(values, existingKey)
546+
}
547+
}
548+
}
549+
472550
func normalizeCredentialImportKey(key string) string {
473551
return strings.NewReplacer("-", "_", ".", "_").Replace(strings.ToLower(strings.TrimSpace(key)))
474552
}

backend/internal/service/account_service.go

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -586,19 +586,20 @@ func (s *AccountService) UpdateOwned(ctx context.Context, ownerUserID, accountID
586586
var groupIDs []int64
587587
if req.ShareMode != nil {
588588
nextMode := NormalizeAccountShareMode(*req.ShareMode)
589+
managedGroupIDs, err := s.managedOwnedAccountGroupIDsForShareMode(ctx, ownerUserID, account, nextMode)
590+
if err != nil {
591+
return nil, err
592+
}
589593
if nextMode == AccountShareModePrivate {
590594
account.ShareMode = AccountShareModePrivate
591595
account.ShareStatus = AccountShareStatusApproved
592596
account.ErrorMessage = ""
593-
} else if NormalizeAccountShareMode(account.ShareMode) != AccountShareModePublic ||
594-
NormalizeAccountShareStatus(account.ShareStatus) != AccountShareStatusApproved {
597+
} else if account.IsPublicShareApproved() {
598+
account.ShareMode = AccountShareModePublic
599+
} else {
595600
account.ShareMode = AccountShareModePublic
596601
account.ShareStatus = AccountShareStatusPending
597602
}
598-
managedGroupIDs, err := s.initialOwnedAccountGroupIDs(ctx, ownerUserID, account.Platform, account.Type, nextMode, nil)
599-
if err != nil {
600-
return nil, err
601-
}
602603
groupIDs = managedGroupIDs
603604
shouldBindGroups = true
604605
}
@@ -695,6 +696,16 @@ func (s *AccountService) getPrivateGroupForOwnedAccount(ctx context.Context, own
695696
return nil, ErrOwnedAccountGroupValidationUnavailable
696697
}
697698
group, err := s.privateGroupProvisioner.GetActiveUserPrivateGroup(ctx, ownerUserID, platform)
699+
if err == nil {
700+
return group, nil
701+
}
702+
if !errors.Is(err, ErrGroupNotFound) && !errors.Is(err, ErrGroupNotAllowed) {
703+
return nil, err
704+
}
705+
if provisionErr := s.privateGroupProvisioner.ProvisionUserPrivateGroups(ctx, ownerUserID); provisionErr != nil {
706+
return nil, provisionErr
707+
}
708+
group, err = s.privateGroupProvisioner.GetActiveUserPrivateGroup(ctx, ownerUserID, platform)
698709
if err != nil {
699710
return nil, err
700711
}
@@ -712,6 +723,20 @@ func (s *AccountService) initialOwnedAccountGroupIDs(ctx context.Context, ownerU
712723
return s.validateOwnedAccountGroupBinding(ctx, ownerUserID, platform, accountType, requestedGroupIDs)
713724
}
714725

726+
func (s *AccountService) managedOwnedAccountGroupIDsForShareMode(ctx context.Context, ownerUserID int64, account *Account, nextMode string) ([]int64, error) {
727+
if account == nil {
728+
return nil, ErrAccountNotFound
729+
}
730+
if NormalizeAccountShareMode(nextMode) == AccountShareModePublic && account.IsPublicShareApproved() {
731+
publicGroup, err := s.resolveOwnedPublicShareGroup(ctx, account)
732+
if err != nil {
733+
return nil, err
734+
}
735+
return s.publicOwnedAccountGroupIDs(ctx, ownerUserID, account, publicGroup)
736+
}
737+
return s.initialOwnedAccountGroupIDs(ctx, ownerUserID, account.Platform, account.Type, nextMode, nil)
738+
}
739+
715740
func (s *AccountService) ApproveOwnedPublicShare(ctx context.Context, ownerUserID, accountID int64) (*Account, error) {
716741
return s.ApproveOwnedPublicShareWithOptions(ctx, ownerUserID, accountID, OwnedPublicShareApprovalOptions{})
717742
}

backend/internal/service/account_service_owned_group_test.go

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,18 +104,24 @@ func (s *ownedPublicSharePolicyRepoStub) DeleteAccountSharePolicy(context.Contex
104104
}
105105

106106
type ownedPrivateGroupProvisionerStub struct {
107-
group *Group
108-
err error
107+
group *Group
108+
err error
109+
provisionErr error
110+
provisionCalls int
109111
}
110112

111113
func (s *ownedPrivateGroupProvisionerStub) ProvisionUserPrivateGroups(context.Context, int64) error {
112-
panic("unexpected ProvisionUserPrivateGroups call")
114+
s.provisionCalls++
115+
return s.provisionErr
113116
}
114117

115118
func (s *ownedPrivateGroupProvisionerStub) GetActiveUserPrivateGroup(context.Context, int64, string) (*Group, error) {
116-
if s.err != nil {
119+
if s.err != nil && s.provisionCalls == 0 {
117120
return nil, s.err
118121
}
122+
if s.group == nil {
123+
return nil, ErrGroupNotFound
124+
}
119125
cp := *s.group
120126
return &cp, nil
121127
}
@@ -291,6 +297,47 @@ func TestAccountServiceInitialOwnedAccountGroupIDsUsesPrivateGroupForPublicMode(
291297
require.Equal(t, []int64{99}, groupIDs)
292298
}
293299

300+
func TestAccountServiceGetPrivateGroupForOwnedAccountProvisionsMissingPrivateGroup(t *testing.T) {
301+
provisioner := &ownedPrivateGroupProvisionerStub{
302+
group: &Group{ID: 99, Platform: PlatformOpenAI, Status: StatusActive, Scope: GroupScopeUserPrivate},
303+
err: ErrGroupNotFound,
304+
}
305+
svc := &AccountService{privateGroupProvisioner: provisioner}
306+
307+
group, err := svc.getPrivateGroupForOwnedAccount(context.Background(), 101, PlatformOpenAI)
308+
309+
require.NoError(t, err)
310+
require.Equal(t, int64(99), group.ID)
311+
require.Equal(t, 1, provisioner.provisionCalls)
312+
}
313+
314+
func TestAccountServiceManagedGroupIDsKeepsApprovedPublicAccountInPublicPool(t *testing.T) {
315+
ownerID := int64(101)
316+
svc := &AccountService{
317+
privateGroupProvisioner: &ownedPrivateGroupProvisionerStub{
318+
group: &Group{ID: 99, Platform: PlatformOpenAI, Status: StatusActive, Scope: GroupScopeUserPrivate},
319+
},
320+
groupRepo: &ownedPublicShareGroupRepoStub{
321+
groups: []Group{
322+
{ID: 18, Name: "Plus Shared Pool", Platform: PlatformOpenAI, Status: StatusActive, Scope: GroupScopePublic},
323+
},
324+
},
325+
}
326+
account := &Account{
327+
ID: 20,
328+
Platform: PlatformOpenAI,
329+
Type: AccountTypeOAuth,
330+
OwnerUserID: &ownerID,
331+
ShareMode: AccountShareModePublic,
332+
ShareStatus: AccountShareStatusApproved,
333+
}
334+
335+
groupIDs, err := svc.managedOwnedAccountGroupIDsForShareMode(context.Background(), ownerID, account, AccountShareModePublic)
336+
337+
require.NoError(t, err)
338+
require.Equal(t, []int64{99, 18}, groupIDs)
339+
}
340+
294341
func TestIsOwnedAccountPublicShareApprovableAllowsRateLimitedAccountWithOption(t *testing.T) {
295342
resetAt := time.Now().Add(time.Hour)
296343
account := &Account{

0 commit comments

Comments
 (0)