fix: password placeholder (#53)

This commit is contained in:
Ringo.Typowriter
2026-02-16 19:48:50 +08:00
committed by GitHub
parent 09d7840a5f
commit f9c613b4f9
3 changed files with 78 additions and 12 deletions
+13 -4
View File
@@ -142,10 +142,7 @@ func (s *Service) Update(ctx context.Context, id string, req UpdateRequest) (Get
baseURL = *req.BaseURL
}
apiKey := existing.ApiKey
if req.APIKey != nil {
apiKey = *req.APIKey
}
apiKey := resolveUpdatedAPIKey(existing.ApiKey, req.APIKey)
metadata := existing.Metadata
if req.Metadata != nil {
@@ -253,3 +250,15 @@ func maskAPIKey(apiKey string) string {
}
return apiKey[:8] + strings.Repeat("*", len(apiKey)-8)
}
// resolveUpdatedAPIKey keeps the original key when the request value matches the masked version.
// This prevents masked placeholder values from overwriting the real stored credential.
func resolveUpdatedAPIKey(existing string, updated *string) string {
if updated == nil {
return existing
}
if *updated == maskAPIKey(existing) {
return existing
}
return *updated
}
+40
View File
@@ -0,0 +1,40 @@
package providers
import "testing"
func TestResolveUpdatedAPIKey(t *testing.T) {
t.Parallel()
existing := "sk-1234567890abcdef"
masked := maskAPIKey(existing)
t.Run("nil update keeps existing", func(t *testing.T) {
t.Parallel()
if got := resolveUpdatedAPIKey(existing, nil); got != existing {
t.Fatalf("expected existing key, got %q", got)
}
})
t.Run("masked update keeps existing", func(t *testing.T) {
t.Parallel()
if got := resolveUpdatedAPIKey(existing, &masked); got != existing {
t.Fatalf("expected existing key, got %q", got)
}
})
t.Run("new key replaces existing", func(t *testing.T) {
t.Parallel()
next := "sk-new-secret"
if got := resolveUpdatedAPIKey(existing, &next); got != next {
t.Fatalf("expected new key, got %q", got)
}
})
t.Run("empty update clears key", func(t *testing.T) {
t.Parallel()
empty := ""
if got := resolveUpdatedAPIKey(existing, &empty); got != empty {
t.Fatalf("expected empty key, got %q", got)
}
})
}
@@ -32,8 +32,8 @@
<FormItem>
<FormControl>
<Input
type="text"
:placeholder="$t('provider.apiKeyPlaceholder')"
type="password"
:placeholder="props.provider?.api_key || $t('provider.apiKeyPlaceholder')"
v-bind="componentField"
/>
</FormControl>
@@ -109,7 +109,7 @@ const props = defineProps<{
}>()
const emit = defineEmits<{
submit: [values: typeof form.values]
submit: [values: Record<string, unknown>]
delete: []
}>()
@@ -117,7 +117,7 @@ const providerSchema = toTypedSchema(z.object({
name: z.string().min(1),
base_url: z.string().min(1),
client_type: z.string().min(1),
api_key: z.string().min(1),
api_key: z.string().optional(),
metadata: z.object({
additionalProp1: z.object({}),
}),
@@ -133,23 +133,40 @@ watch(() => props.provider, (newVal) => {
name: newVal.name,
base_url: newVal.base_url,
client_type: newVal.client_type,
api_key: newVal.api_key,
// Keep key input empty by default so masked placeholders are never submitted back.
api_key: '',
})
}
}, { immediate: true })
const hasChanges = computed(() => {
const raw = props.provider
return JSON.stringify(form.values) !== JSON.stringify({
const baseChanged = JSON.stringify({
name: form.values.name,
base_url: form.values.base_url,
client_type: form.values.client_type,
metadata: form.values.metadata,
}) !== JSON.stringify({
name: raw?.name,
base_url: raw?.base_url,
client_type: raw?.client_type,
api_key: raw?.api_key,
metadata: { additionalProp1: {} },
})
const apiKeyChanged = Boolean(form.values.api_key && form.values.api_key.trim() !== '')
return baseChanged || apiKeyChanged
})
const editProvider = form.handleSubmit(async (value) => {
emit('submit', value)
const payload: Record<string, unknown> = {
name: value.name,
base_url: value.base_url,
client_type: value.client_type,
metadata: value.metadata,
}
if (value.api_key && value.api_key.trim() !== '') {
payload.api_key = value.api_key
}
emit('submit', payload)
})
</script>