|
8 | 8 | "strings" |
9 | 9 | "time" |
10 | 10 |
|
| 11 | + "log/slog" |
| 12 | + |
| 13 | + "github.com/Wei-Shaw/sub2api/internal/pkg/openai" |
11 | 14 | "github.com/Wei-Shaw/sub2api/internal/pkg/response" |
12 | 15 | "github.com/Wei-Shaw/sub2api/internal/service" |
13 | 16 | "github.com/gin-gonic/gin" |
@@ -292,6 +295,8 @@ func (h *AccountHandler) importData(ctx context.Context, req DataImportRequest) |
292 | 295 | } |
293 | 296 | } |
294 | 297 |
|
| 298 | + enrichCredentialsFromIDToken(&item) |
| 299 | + |
295 | 300 | accountInput := &service.CreateAccountInput{ |
296 | 301 | Name: item.Name, |
297 | 302 | Notes: item.Notes, |
@@ -535,6 +540,57 @@ func defaultProxyName(name string) string { |
535 | 540 | return name |
536 | 541 | } |
537 | 542 |
|
| 543 | +// enrichCredentialsFromIDToken performs best-effort extraction of user info fields |
| 544 | +// (email, plan_type, chatgpt_account_id, etc.) from id_token in credentials. |
| 545 | +// Only applies to OpenAI/Sora OAuth accounts. Skips expired token errors silently. |
| 546 | +// Existing credential values are never overwritten — only missing fields are filled. |
| 547 | +func enrichCredentialsFromIDToken(item *DataAccount) { |
| 548 | + if item.Credentials == nil { |
| 549 | + return |
| 550 | + } |
| 551 | + // Only enrich OpenAI/Sora OAuth accounts |
| 552 | + platform := strings.ToLower(strings.TrimSpace(item.Platform)) |
| 553 | + if platform != service.PlatformOpenAI && platform != service.PlatformSora { |
| 554 | + return |
| 555 | + } |
| 556 | + if strings.ToLower(strings.TrimSpace(item.Type)) != service.AccountTypeOAuth { |
| 557 | + return |
| 558 | + } |
| 559 | + |
| 560 | + idToken, _ := item.Credentials["id_token"].(string) |
| 561 | + if strings.TrimSpace(idToken) == "" { |
| 562 | + return |
| 563 | + } |
| 564 | + |
| 565 | + // DecodeIDToken skips expiry validation — safe for imported data |
| 566 | + claims, err := openai.DecodeIDToken(idToken) |
| 567 | + if err != nil { |
| 568 | + slog.Debug("import_enrich_id_token_decode_failed", "account", item.Name, "error", err) |
| 569 | + return |
| 570 | + } |
| 571 | + |
| 572 | + userInfo := claims.GetUserInfo() |
| 573 | + if userInfo == nil { |
| 574 | + return |
| 575 | + } |
| 576 | + |
| 577 | + // Fill missing fields only (never overwrite existing values) |
| 578 | + setIfMissing := func(key, value string) { |
| 579 | + if value == "" { |
| 580 | + return |
| 581 | + } |
| 582 | + if existing, _ := item.Credentials[key].(string); existing == "" { |
| 583 | + item.Credentials[key] = value |
| 584 | + } |
| 585 | + } |
| 586 | + |
| 587 | + setIfMissing("email", userInfo.Email) |
| 588 | + setIfMissing("plan_type", userInfo.PlanType) |
| 589 | + setIfMissing("chatgpt_account_id", userInfo.ChatGPTAccountID) |
| 590 | + setIfMissing("chatgpt_user_id", userInfo.ChatGPTUserID) |
| 591 | + setIfMissing("organization_id", userInfo.OrganizationID) |
| 592 | +} |
| 593 | + |
538 | 594 | func normalizeProxyStatus(status string) string { |
539 | 595 | normalized := strings.TrimSpace(strings.ToLower(status)) |
540 | 596 | switch normalized { |
|
0 commit comments