diff --git a/backend/services/settingsService.js b/backend/services/settingsService.js
index 1d3bafe..10ad35a 100644
--- a/backend/services/settingsService.js
+++ b/backend/services/settingsService.js
@@ -428,14 +428,16 @@ class SettingsService extends BaseService{
}
}
- const hasKey = Boolean(keyRow && keyRow.value && String(keyRow.value).trim());
+ const hasStoredKey = Boolean(keyRow && keyRow.getDataValue('value') && String(keyRow.getDataValue('value')).trim());
+ const hasReadableKey = Boolean(keyRow && keyRow.value && String(keyRow.value).trim());
return {
enabled: parsed.enabled !== false,
baseUrl: parsed.baseUrl || '',
model: parsed.model || 'gpt-4o-mini',
- hasKey,
- keyLast4: parsed.keyLast4 || null
+ hasKey: hasStoredKey,
+ keyLast4: parsed.keyLast4 || null,
+ keyStatus: hasStoredKey ? (hasReadableKey ? 'stored' : 'invalid') : 'missing'
};
}
diff --git a/backend/services/vocabService.js b/backend/services/vocabService.js
index cd46739..8dc01c2 100644
--- a/backend/services/vocabService.js
+++ b/backend/services/vocabService.js
@@ -12,7 +12,6 @@ import UserParam from '../models/community/user_param.js';
import { sequelize } from '../utils/sequelize.js';
import { notifyUser } from '../utils/socket.js';
import { Op } from 'sequelize';
-import { decrypt } from '../utils/encryption.js';
export default class VocabService {
async _getUserByHashedId(hashedUserId) {
@@ -56,7 +55,7 @@ export default class VocabService {
}
}
- const decryptedKey = keyRow?.value ? decrypt(keyRow.value) : null;
+ const decryptedKey = keyRow?.value ? String(keyRow.value).trim() : null;
const hasKey = Boolean(decryptedKey && String(decryptedKey).trim());
const enabled = parsed.enabled !== false;
const baseUrl = String(parsed.baseUrl || '').trim();
diff --git a/frontend/src/i18n/locales/de/settings.json b/frontend/src/i18n/locales/de/settings.json
index 1f9b868..01d5439 100644
--- a/frontend/src/i18n/locales/de/settings.json
+++ b/frontend/src/i18n/locales/de/settings.json
@@ -169,7 +169,10 @@
"save": "Speichern",
"saved": "Einstellungen gespeichert.",
"saveError": "Speichern fehlgeschlagen.",
- "confirmClear": "API-Schlüssel wirklich löschen?"
+ "confirmClear": "API-Schlüssel wirklich löschen?",
+ "keyStatusStored": "API-Schlüssel gespeichert.",
+ "keyStatusInvalid": "Ein gespeicherter API-Schlüssel ist vorhanden, kann aber nicht gelesen werden. Bitte neu speichern.",
+ "keyStatusMissing": "Derzeit ist kein API-Schlüssel gespeichert."
},
"interests": {
"title": "Interessen",
diff --git a/frontend/src/i18n/locales/en/settings.json b/frontend/src/i18n/locales/en/settings.json
index 053e7f0..d3a9f00 100644
--- a/frontend/src/i18n/locales/en/settings.json
+++ b/frontend/src/i18n/locales/en/settings.json
@@ -169,7 +169,10 @@
"save": "Save",
"saved": "Settings saved.",
"saveError": "Could not save.",
- "confirmClear": "Really delete the API key?"
+ "confirmClear": "Really delete the API key?",
+ "keyStatusStored": "API key stored.",
+ "keyStatusInvalid": "A stored API key exists, but it cannot be read. Please save it again.",
+ "keyStatusMissing": "No API key is currently stored."
},
"interests": {
"title": "Interests",
@@ -198,4 +201,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/frontend/src/i18n/locales/es/settings.json b/frontend/src/i18n/locales/es/settings.json
index 09ea7f3..9055e60 100644
--- a/frontend/src/i18n/locales/es/settings.json
+++ b/frontend/src/i18n/locales/es/settings.json
@@ -169,7 +169,10 @@
"save": "Guardar",
"saved": "Ajustes guardados.",
"saveError": "No se pudo guardar.",
- "confirmClear": "¿Eliminar realmente la clave API?"
+ "confirmClear": "¿Eliminar realmente la clave API?",
+ "keyStatusStored": "Clave API guardada.",
+ "keyStatusInvalid": "Existe una clave API guardada, pero no se puede leer. Guárdala de nuevo.",
+ "keyStatusMissing": "Actualmente no hay ninguna clave API guardada."
},
"interests": {
"title": "Intereses",
diff --git a/frontend/src/views/settings/LanguageAssistantView.vue b/frontend/src/views/settings/LanguageAssistantView.vue
index 4845cf4..71e7d06 100644
--- a/frontend/src/views/settings/LanguageAssistantView.vue
+++ b/frontend/src/views/settings/LanguageAssistantView.vue
@@ -51,6 +51,9 @@
:placeholder="apiKeyPlaceholder"
/>
{{ $t('settings.languageAssistant.apiKeyHint') }}
+
+ {{ keyStatusLabel }}
+
@@ -84,6 +87,7 @@ export default {
},
hasKey: false,
keyLast4: null,
+ keyStatus: 'missing',
saving: false,
loadError: null
};
@@ -100,6 +104,14 @@ export default {
});
}
return this.$t('settings.languageAssistant.apiKeyPlaceholderNew');
+ },
+ keyStatusLabel() {
+ const statusKey = {
+ stored: 'settings.languageAssistant.keyStatusStored',
+ invalid: 'settings.languageAssistant.keyStatusInvalid',
+ missing: 'settings.languageAssistant.keyStatusMissing'
+ }[this.keyStatus] || 'settings.languageAssistant.keyStatusMissing';
+ return this.$t(statusKey);
}
},
async mounted() {
@@ -115,6 +127,7 @@ export default {
this.form.model = data.model || 'gpt-4o-mini';
this.hasKey = data.hasKey;
this.keyLast4 = data.keyLast4;
+ this.keyStatus = data.keyStatus || (data.hasKey ? 'stored' : 'missing');
this.form.apiKey = '';
this.form.clearKey = false;
} catch (e) {
@@ -208,6 +221,18 @@ export default {
font-size: 0.8rem;
opacity: 0.8;
}
+.language-assistant-settings__key-status {
+ font-weight: 600;
+}
+.language-assistant-settings__key-status--stored {
+ color: #2e7d32;
+}
+.language-assistant-settings__key-status--invalid {
+ color: #c62828;
+}
+.language-assistant-settings__key-status--missing {
+ color: #8a6d3b;
+}
.language-assistant-settings__toggle {
display: flex;
align-items: center;