Improve exercise type validation and encryption in settings service: Enhance error handling for exercise type name checks in both create-bisaya-course-content and update-week1-bisaya-exercises scripts. Implement encryption for API keys and user settings in settingsService, ensuring sensitive data is securely stored. Update localization files to include new terms related to model patterns in English, German, and Spanish.
This commit is contained in:
@@ -1367,20 +1367,25 @@ async function resolveExerciseTypeId(exercise) {
|
||||
return exercise.exerciseTypeId;
|
||||
}
|
||||
|
||||
if (!exercise.exerciseTypeName) {
|
||||
throw new Error(`Kein exerciseTypeId oder exerciseTypeName für "${exercise.title}" definiert`);
|
||||
const trimmedName =
|
||||
exercise.exerciseTypeName != null && exercise.exerciseTypeName !== ''
|
||||
? String(exercise.exerciseTypeName).trim()
|
||||
: '';
|
||||
|
||||
if (!trimmedName) {
|
||||
throw new Error(`Kein exerciseTypeId oder exerciseTypeName für Übung "${exercise.title || 'unbenannt'}" definiert`);
|
||||
}
|
||||
|
||||
const [type] = await sequelize.query(
|
||||
`SELECT id FROM community.vocab_grammar_exercise_type WHERE name = :name LIMIT 1`,
|
||||
{
|
||||
replacements: { name: exercise.exerciseTypeName },
|
||||
replacements: { name: trimmedName },
|
||||
type: sequelize.QueryTypes.SELECT
|
||||
}
|
||||
);
|
||||
|
||||
if (!type) {
|
||||
throw new Error(`Übungstyp "${exercise.exerciseTypeName}" nicht gefunden`);
|
||||
throw new Error(`Übungstyp "${trimmedName}" nicht gefunden`);
|
||||
}
|
||||
|
||||
return Number(type.id);
|
||||
|
||||
@@ -48,16 +48,21 @@ async function resolveExerciseTypeId(exercise) {
|
||||
return exercise.exerciseTypeId;
|
||||
}
|
||||
|
||||
const name = exercise.exerciseTypeName;
|
||||
if (name === undefined || name === null || String(name).trim() === '') {
|
||||
throw new Error(`Kein exerciseTypeId oder exerciseTypeName für Übung "${exercise.title || 'unbenannt'}"`);
|
||||
}
|
||||
|
||||
const [type] = await sequelize.query(
|
||||
`SELECT id FROM community.vocab_grammar_exercise_type WHERE name = :name LIMIT 1`,
|
||||
{
|
||||
replacements: { name: exercise.exerciseTypeName },
|
||||
replacements: { name: String(name).trim() },
|
||||
type: sequelize.QueryTypes.SELECT
|
||||
}
|
||||
);
|
||||
|
||||
if (!type) {
|
||||
throw new Error(`Übungstyp "${exercise.exerciseTypeName}" nicht gefunden`);
|
||||
throw new Error(`Übungstyp "${String(name).trim()}" nicht gefunden`);
|
||||
}
|
||||
|
||||
return Number(type.id);
|
||||
|
||||
@@ -10,6 +10,18 @@ import InterestTranslation from '../models/type/interest_translation.js';
|
||||
import { Op } from 'sequelize';
|
||||
import UserParamVisibilityType from '../models/type/user_param_visibility.js';
|
||||
import UserParamVisibility from '../models/community/user_param_visibility.js';
|
||||
import { encrypt } from '../utils/encryption.js';
|
||||
import { sequelize } from '../utils/sequelize.js';
|
||||
|
||||
/** Wie UserParam.value-Setter: bei Verschlüsselungsfehler leeren String speichern, nicht crashen. */
|
||||
function encryptUserParamValue(plain) {
|
||||
try {
|
||||
return encrypt(plain);
|
||||
} catch (error) {
|
||||
console.error('Error encrypting user_param value:', error);
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
class SettingsService extends BaseService{
|
||||
async getUserParams(userId, paramDescriptions) {
|
||||
@@ -451,55 +463,61 @@ class SettingsService extends BaseService{
|
||||
|
||||
const { apiKey, clearKey, baseUrl, model, enabled } = payload;
|
||||
|
||||
if (clearKey) {
|
||||
const keyRow = await UserParam.findOne({
|
||||
where: { userId: user.id, paramTypeId: apiKeyType.id }
|
||||
});
|
||||
if (keyRow) {
|
||||
await keyRow.destroy();
|
||||
await sequelize.transaction(async (transaction) => {
|
||||
if (clearKey) {
|
||||
const keyRow = await UserParam.findOne({
|
||||
where: { userId: user.id, paramTypeId: apiKeyType.id },
|
||||
transaction
|
||||
});
|
||||
if (keyRow) {
|
||||
await keyRow.destroy({ transaction });
|
||||
}
|
||||
delete parsed.keyLast4;
|
||||
} else if (apiKey !== undefined && String(apiKey).trim() !== '') {
|
||||
const plain = String(apiKey).trim();
|
||||
parsed.keyLast4 = plain.length >= 4 ? plain.slice(-4) : plain;
|
||||
const encKey = encryptUserParamValue(plain);
|
||||
const [keyRow] = await UserParam.findOrCreate({
|
||||
where: { userId: user.id, paramTypeId: apiKeyType.id },
|
||||
defaults: {
|
||||
userId: user.id,
|
||||
paramTypeId: apiKeyType.id,
|
||||
// Platzhalter: Setter verschlüsselt; wird sofort durch encKey überschrieben.
|
||||
value: ' '
|
||||
},
|
||||
transaction
|
||||
});
|
||||
keyRow.setDataValue('value', encKey);
|
||||
await keyRow.save({ fields: ['value'], transaction });
|
||||
}
|
||||
delete parsed.keyLast4;
|
||||
} else if (apiKey !== undefined && String(apiKey).trim() !== '') {
|
||||
const plain = String(apiKey).trim();
|
||||
parsed.keyLast4 = plain.length >= 4 ? plain.slice(-4) : plain;
|
||||
const [keyRow, keyCreated] = await UserParam.findOrCreate({
|
||||
where: { userId: user.id, paramTypeId: apiKeyType.id },
|
||||
|
||||
if (baseUrl !== undefined) {
|
||||
parsed.baseUrl = String(baseUrl).trim();
|
||||
}
|
||||
if (model !== undefined) {
|
||||
parsed.model = String(model).trim() || 'gpt-4o-mini';
|
||||
}
|
||||
if (enabled !== undefined) {
|
||||
parsed.enabled = Boolean(enabled);
|
||||
}
|
||||
if (!parsed.model) {
|
||||
parsed.model = 'gpt-4o-mini';
|
||||
}
|
||||
|
||||
const jsonStr = JSON.stringify(parsed);
|
||||
const encMeta = encryptUserParamValue(jsonStr);
|
||||
const [metaRow] = await UserParam.findOrCreate({
|
||||
where: { userId: user.id, paramTypeId: settingsType.id },
|
||||
defaults: {
|
||||
userId: user.id,
|
||||
paramTypeId: apiKeyType.id,
|
||||
value: plain
|
||||
}
|
||||
paramTypeId: settingsType.id,
|
||||
value: ' '
|
||||
},
|
||||
transaction
|
||||
});
|
||||
if (!keyCreated) {
|
||||
await keyRow.update({ value: plain });
|
||||
}
|
||||
}
|
||||
|
||||
if (baseUrl !== undefined) {
|
||||
parsed.baseUrl = String(baseUrl).trim();
|
||||
}
|
||||
if (model !== undefined) {
|
||||
parsed.model = String(model).trim() || 'gpt-4o-mini';
|
||||
}
|
||||
if (enabled !== undefined) {
|
||||
parsed.enabled = Boolean(enabled);
|
||||
}
|
||||
if (!parsed.model) {
|
||||
parsed.model = 'gpt-4o-mini';
|
||||
}
|
||||
|
||||
const jsonStr = JSON.stringify(parsed);
|
||||
const [metaRow, metaCreated] = await UserParam.findOrCreate({
|
||||
where: { userId: user.id, paramTypeId: settingsType.id },
|
||||
defaults: {
|
||||
userId: user.id,
|
||||
paramTypeId: settingsType.id,
|
||||
value: jsonStr
|
||||
}
|
||||
metaRow.setDataValue('value', encMeta);
|
||||
await metaRow.save({ fields: ['value'], transaction });
|
||||
});
|
||||
if (!metaCreated) {
|
||||
await metaRow.update({ value: jsonStr });
|
||||
}
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user