diff --git a/backend/internal/config/models.go b/backend/internal/config/models.go
index 739e5c7..628d184 100644
--- a/backend/internal/config/models.go
+++ b/backend/internal/config/models.go
@@ -48,11 +48,3 @@ func (UsageStats) TableName() string {
return "usage_stats"
}
-// MaskAPIKey 掩码 API Key(仅显示最后 4 个字符)
-func (p *Provider) MaskAPIKey() {
- if len(p.APIKey) > 4 {
- p.APIKey = "***" + p.APIKey[len(p.APIKey)-4:]
- } else {
- p.APIKey = "***"
- }
-}
diff --git a/backend/internal/domain/provider.go b/backend/internal/domain/provider.go
index f01b2d8..9ec0e47 100644
--- a/backend/internal/domain/provider.go
+++ b/backend/internal/domain/provider.go
@@ -14,11 +14,3 @@ type Provider struct {
UpdatedAt time.Time `json:"updated_at"`
}
-// MaskAPIKey 掩码 API Key(仅显示最后 4 个字符)
-func (p *Provider) MaskAPIKey() {
- if len(p.APIKey) > 4 {
- p.APIKey = "***" + p.APIKey[len(p.APIKey)-4:]
- } else {
- p.APIKey = "***"
- }
-}
diff --git a/backend/internal/handler/handler_supplemental_test.go b/backend/internal/handler/handler_supplemental_test.go
index 716c406..4938e2a 100644
--- a/backend/internal/handler/handler_supplemental_test.go
+++ b/backend/internal/handler/handler_supplemental_test.go
@@ -33,7 +33,7 @@ func TestProviderHandler_CreateProvider_Success(t *testing.T) {
var result domain.Provider
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &result))
assert.Equal(t, "p1", result.ID)
- assert.Contains(t, result.APIKey, "***")
+ assert.Equal(t, "sk-test", result.APIKey)
}
func TestProviderHandler_CreateProvider_WithProtocol(t *testing.T) {
@@ -57,7 +57,7 @@ func TestProviderHandler_CreateProvider_WithProtocol(t *testing.T) {
func TestProviderHandler_UpdateProvider(t *testing.T) {
h := NewProviderHandler(&mockProviderService{
- provider: &domain.Provider{ID: "p1", Name: "Updated", APIKey: "***"},
+ provider: &domain.Provider{ID: "p1", Name: "Updated", APIKey: "sk-test"},
})
body, _ := json.Marshal(map[string]string{"name": "Updated"})
diff --git a/backend/internal/handler/handler_test.go b/backend/internal/handler/handler_test.go
index 74e0bb5..480e492 100644
--- a/backend/internal/handler/handler_test.go
+++ b/backend/internal/handler/handler_test.go
@@ -65,7 +65,7 @@ func (m *mockProviderService) GetModelByProviderAndName(providerID, modelName st
}
func (m *mockProviderService) Create(provider *domain.Provider) error { return m.err }
-func (m *mockProviderService) Get(id string, maskKey bool) (*domain.Provider, error) {
+func (m *mockProviderService) Get(id string) (*domain.Provider, error) {
return m.provider, m.err
}
func (m *mockProviderService) List() ([]domain.Provider, error) { return m.providers, m.err }
@@ -148,7 +148,7 @@ func TestProviderHandler_ListProviders(t *testing.T) {
func TestProviderHandler_GetProvider(t *testing.T) {
h := NewProviderHandler(&mockProviderService{
- provider: &domain.Provider{ID: "p1", Name: "P1", APIKey: "***"},
+ provider: &domain.Provider{ID: "p1", Name: "P1", APIKey: "sk-test"},
})
w := httptest.NewRecorder()
diff --git a/backend/internal/handler/provider_handler.go b/backend/internal/handler/provider_handler.go
index db08a32..2ff6a02 100644
--- a/backend/internal/handler/provider_handler.go
+++ b/backend/internal/handler/provider_handler.go
@@ -66,7 +66,6 @@ func (h *ProviderHandler) CreateProvider(c *gin.Context) {
return
}
- provider.MaskAPIKey()
c.JSON(http.StatusCreated, provider)
}
@@ -85,7 +84,7 @@ func (h *ProviderHandler) ListProviders(c *gin.Context) {
func (h *ProviderHandler) GetProvider(c *gin.Context) {
id := c.Param("id")
- provider, err := h.providerService.Get(id, true)
+ provider, err := h.providerService.Get(id)
if err != nil {
if err == gorm.ErrRecordNotFound {
c.JSON(http.StatusNotFound, gin.H{
@@ -131,7 +130,7 @@ func (h *ProviderHandler) UpdateProvider(c *gin.Context) {
return
}
- provider, err := h.providerService.Get(id, true)
+ provider, err := h.providerService.Get(id)
if err != nil {
writeError(c, err)
return
diff --git a/backend/internal/handler/proxy_handler_test.go b/backend/internal/handler/proxy_handler_test.go
index d411306..6aaa8c5 100644
--- a/backend/internal/handler/proxy_handler_test.go
+++ b/backend/internal/handler/proxy_handler_test.go
@@ -80,7 +80,7 @@ func (m *mockProxyProviderService) GetModelByProviderAndName(providerID, modelNa
}
func (m *mockProxyProviderService) Create(p *domain.Provider) error { return nil }
-func (m *mockProxyProviderService) Get(id string, maskKey bool) (*domain.Provider, error) { return nil, nil }
+func (m *mockProxyProviderService) Get(id string) (*domain.Provider, error) { return nil, nil }
func (m *mockProxyProviderService) List() ([]domain.Provider, error) { return m.providers, m.err }
func (m *mockProxyProviderService) Update(id string, updates map[string]interface{}) error { return nil }
func (m *mockProxyProviderService) Delete(id string) error { return nil }
diff --git a/backend/internal/service/provider_service.go b/backend/internal/service/provider_service.go
index 2ed3f76..c23df26 100644
--- a/backend/internal/service/provider_service.go
+++ b/backend/internal/service/provider_service.go
@@ -5,7 +5,7 @@ import "nex/backend/internal/domain"
// ProviderService 供应商服务接口
type ProviderService interface {
Create(provider *domain.Provider) error
- Get(id string, maskKey bool) (*domain.Provider, error)
+ Get(id string) (*domain.Provider, error)
List() ([]domain.Provider, error)
Update(id string, updates map[string]interface{}) error
Delete(id string) error
diff --git a/backend/internal/service/provider_service_impl.go b/backend/internal/service/provider_service_impl.go
index 28a992a..080b540 100644
--- a/backend/internal/service/provider_service_impl.go
+++ b/backend/internal/service/provider_service_impl.go
@@ -32,26 +32,12 @@ func (s *providerService) Create(provider *domain.Provider) error {
return err
}
-func (s *providerService) Get(id string, maskKey bool) (*domain.Provider, error) {
- provider, err := s.providerRepo.GetByID(id)
- if err != nil {
- return nil, err
- }
- if maskKey {
- provider.MaskAPIKey()
- }
- return provider, nil
+func (s *providerService) Get(id string) (*domain.Provider, error) {
+ return s.providerRepo.GetByID(id)
}
func (s *providerService) List() ([]domain.Provider, error) {
- providers, err := s.providerRepo.List()
- if err != nil {
- return nil, err
- }
- for i := range providers {
- providers[i].MaskAPIKey()
- }
- return providers, nil
+ return s.providerRepo.List()
}
func (s *providerService) Update(id string, updates map[string]interface{}) error {
diff --git a/backend/internal/service/service_supplemental_test.go b/backend/internal/service/service_supplemental_test.go
index 5ba9e02..f29100f 100644
--- a/backend/internal/service/service_supplemental_test.go
+++ b/backend/internal/service/service_supplemental_test.go
@@ -21,7 +21,7 @@ func TestProviderService_Update(t *testing.T) {
err := svc.Update("p1", map[string]interface{}{"name": "Updated"})
require.NoError(t, err)
- result, err := svc.Get("p1", false)
+ result, err := svc.Get("p1")
require.NoError(t, err)
assert.Equal(t, "Updated", result.Name)
}
diff --git a/backend/internal/service/service_test.go b/backend/internal/service/service_test.go
index 224db39..88cef98 100644
--- a/backend/internal/service/service_test.go
+++ b/backend/internal/service/service_test.go
@@ -268,7 +268,7 @@ func TestProviderService_Update_Success(t *testing.T) {
})
require.NoError(t, err)
- updated, err := svc.Get("openai", false)
+ updated, err := svc.Get("openai")
require.NoError(t, err)
assert.Equal(t, "OpenAI Updated", updated.Name)
}
diff --git a/backend/tests/integration/conversion_test.go b/backend/tests/integration/conversion_test.go
index dd09194..f5834ea 100644
--- a/backend/tests/integration/conversion_test.go
+++ b/backend/tests/integration/conversion_test.go
@@ -533,8 +533,7 @@ func TestConversion_ProviderWithProtocol(t *testing.T) {
var created map[string]any
json.Unmarshal(w.Body.Bytes(), &created)
- // API Key 被掩码
- assert.Contains(t, created["api_key"], "***")
+ assert.Equal(t, "sk-test", created["api_key"])
// 获取时应包含 protocol
w = httptest.NewRecorder()
diff --git a/backend/tests/integration/integration_test.go b/backend/tests/integration/integration_test.go
index 518bb94..13916d9 100644
--- a/backend/tests/integration/integration_test.go
+++ b/backend/tests/integration/integration_test.go
@@ -103,7 +103,7 @@ func TestOpenAI_CompleteFlow(t *testing.T) {
var providers []domain.Provider
json.Unmarshal(w.Body.Bytes(), &providers)
assert.Len(t, providers, 1)
- assert.Contains(t, providers[0].APIKey, "***") // 已掩码
+ assert.Equal(t, "sk-test-key", providers[0].APIKey)
// 4. 列出 Model
w = httptest.NewRecorder()
diff --git a/frontend/e2e/providers.spec.ts b/frontend/e2e/providers.spec.ts
index f8bb50c..4b03d8b 100644
--- a/frontend/e2e/providers.spec.ts
+++ b/frontend/e2e/providers.spec.ts
@@ -12,7 +12,7 @@ function formInputs(page: import('@playwright/test').Page) {
return {
id: dialog.locator('input[placeholder="例如: openai"]'),
name: dialog.locator('input[placeholder="例如: OpenAI"]'),
- apiKey: dialog.locator('input[type="password"]'),
+ apiKey: dialog.locator('input[placeholder="sk-..."]'),
baseUrl: dialog.locator('input[placeholder="例如: https://api.openai.com/v1"]'),
protocol: dialog.locator('.t-select'),
saveBtn: dialog.locator('.t-dialog__footer').getByRole('button', { name: '保存' }),
@@ -66,9 +66,6 @@ test.describe('供应商管理', () => {
await responsePromise
await expect(page.locator('.t-table__body').getByText('Before Edit')).toBeVisible({ timeout: 5000 })
- await inputs.cancelBtn.click()
- await expect(page.locator('.t-dialog:visible')).not.toBeVisible({ timeout: 3000 })
-
await page.locator('.t-table__body button:has-text("编辑")').first().click()
await expect(page.locator('.t-dialog:visible')).toBeVisible()
@@ -97,9 +94,6 @@ test.describe('供应商管理', () => {
await page.waitForSelector('.t-select__dropdown', { state: 'hidden', timeout: 3000 })
await inputs.saveBtn.click()
await expect(page.locator('.t-table__body').getByText('To Delete')).toBeVisible({ timeout: 10000 })
-
- await inputs.cancelBtn.click()
- await expect(page.locator('.t-dialog:visible')).not.toBeVisible({ timeout: 3000 })
await page.locator('.t-table__body button:has-text("删除")').first().click()
await expect(page.getByText('确定要删除这个供应商吗?')).toBeVisible()
@@ -107,7 +101,7 @@ test.describe('供应商管理', () => {
await expect(page.locator('.t-table__body').getByText('To Delete')).not.toBeVisible({ timeout: 5000 })
})
- test('应正确脱敏显示 API Key', async ({ page }) => {
+ test('应正确显示完整 API Key', async ({ page }) => {
const testId = nextId()
await page.getByRole('button', { name: '添加供应商' }).click()
await expect(page.locator('.t-dialog:visible')).toBeVisible()
@@ -123,6 +117,6 @@ test.describe('供应商管理', () => {
await inputs.saveBtn.click()
await expect(page.locator('.t-table__body').getByText('Mask Test')).toBeVisible({ timeout: 10000 })
- await expect(page.locator('.t-table__body')).toContainText('****wxyz')
+ await expect(page.locator('.t-table__body')).toContainText('sk_abcdefghijklmnopqrstuvwxyz')
})
})
diff --git a/frontend/e2e/validation.spec.ts b/frontend/e2e/validation.spec.ts
index 15f4d7d..c8fa994 100644
--- a/frontend/e2e/validation.spec.ts
+++ b/frontend/e2e/validation.spec.ts
@@ -5,7 +5,7 @@ function formInputs(page: import('@playwright/test').Page) {
return {
id: dialog.locator('input[placeholder="例如: openai"]'),
name: dialog.locator('input[placeholder="例如: OpenAI"]'),
- apiKey: dialog.locator('input[type="password"]'),
+ apiKey: dialog.locator('input[placeholder="sk-..."]'),
baseUrl: dialog.locator('input[placeholder="例如: https://api.openai.com/v1"]'),
saveBtn: dialog.locator('.t-dialog__footer').getByRole('button', { name: '保存' }),
cancelBtn: dialog.locator('.t-dialog__footer').getByRole('button', { name: '取消' }),
@@ -20,7 +20,7 @@ test.describe('供应商表单验证', () => {
test('应显示必填字段验证', async ({ page }) => {
await page.getByRole('button', { name: '添加供应商' }).click()
- await expect(page.locator('.t-dialog')).toBeVisible()
+ await expect(page.locator('.t-dialog:visible')).toBeVisible()
await formInputs(page).saveBtn.click()
@@ -32,7 +32,7 @@ test.describe('供应商表单验证', () => {
test('应验证URL格式', async ({ page }) => {
await page.getByRole('button', { name: '添加供应商' }).click()
- await expect(page.locator('.t-dialog')).toBeVisible()
+ await expect(page.locator('.t-dialog:visible')).toBeVisible()
const inputs = formInputs(page)
await inputs.id.fill('test_url')
@@ -46,17 +46,17 @@ test.describe('供应商表单验证', () => {
test('取消后表单应重置', async ({ page }) => {
await page.getByRole('button', { name: '添加供应商' }).click()
- await expect(page.locator('.t-dialog')).toBeVisible()
+ await expect(page.locator('.t-dialog:visible')).toBeVisible()
let inputs = formInputs(page)
await inputs.id.fill('should_be_reset')
await inputs.name.fill('Should Be Reset')
await inputs.cancelBtn.click()
- await expect(page.locator('.t-dialog')).not.toBeVisible()
+ await expect(page.locator('.t-dialog:visible')).not.toBeVisible()
await page.getByRole('button', { name: '添加供应商' }).click()
- await expect(page.locator('.t-dialog')).toBeVisible()
+ await expect(page.locator('.t-dialog:visible')).toBeVisible()
inputs = formInputs(page)
await expect(inputs.id).toHaveValue('')
@@ -65,11 +65,11 @@ test.describe('供应商表单验证', () => {
test('快速连续点击只打开一个对话框', async ({ page }) => {
await page.getByRole('button', { name: '添加供应商' }).click()
- await expect(page.locator('.t-dialog')).toBeVisible()
+ await expect(page.locator('.t-dialog:visible')).toBeVisible()
- expect(await page.locator('.t-dialog').count()).toBe(1)
+ expect(await page.locator('.t-dialog:visible').count()).toBe(1)
await formInputs(page).cancelBtn.click()
- await expect(page.locator('.t-dialog')).not.toBeVisible()
+ await expect(page.locator('.t-dialog:visible')).not.toBeVisible()
})
})
diff --git a/frontend/src/__tests__/components/ModelForm.test.tsx b/frontend/src/__tests__/components/ModelForm.test.tsx
index 18ce390..353874e 100644
--- a/frontend/src/__tests__/components/ModelForm.test.tsx
+++ b/frontend/src/__tests__/components/ModelForm.test.tsx
@@ -125,9 +125,6 @@ describe('ModelForm', () => {
const dialog = getDialog();
expect(within(dialog).getByText('编辑模型')).toBeInTheDocument();
- // Check that unified ID field is displayed
- expect(within(dialog).getByText('统一模型 ID')).toBeInTheDocument();
-
// Check model name input
const modelNameInput = within(dialog).getByPlaceholderText('例如: gpt-4o') as HTMLInputElement;
expect(modelNameInput.value).toBe('gpt-4o');
diff --git a/frontend/src/__tests__/components/ProviderForm.test.tsx b/frontend/src/__tests__/components/ProviderForm.test.tsx
index 23806ae..6af758b 100644
--- a/frontend/src/__tests__/components/ProviderForm.test.tsx
+++ b/frontend/src/__tests__/components/ProviderForm.test.tsx
@@ -63,13 +63,16 @@ describe('ProviderForm', () => {
const baseUrlInput = within(dialog).getByPlaceholderText('例如: https://api.openai.com/v1') as HTMLInputElement;
expect(baseUrlInput.value).toBe('https://api.openai.com/v1');
+
+ const apiKeyInput = within(dialog).getByPlaceholderText('sk-...') as HTMLInputElement;
+ expect(apiKeyInput.value).toBe('sk-old-key');
});
- it('shows API Key label variant in edit mode', () => {
+ it('shows API Key label in edit mode', () => {
render(