import { db, id, now, visibleClause } from '../db.js'; import { customVariableSchema } from '../forms/schemas.js'; import { fullVariableCatalog } from '../data/variableCatalog.js'; import { decryptSecret, encryptSecret } from '../services/cryptoStore.js'; import { camelCustomVariable } from './mappers.js'; function normalizeVariable(body, existing = {}) { const parsed = customVariableSchema.parse({ ...existing, ...body }); return { ...parsed, sensitive: Boolean(parsed.sensitive), groupId: parsed.visibility === 'group' ? parsed.groupId || null : null }; } function visibleCustomVariableRows(userId) { return db.prepare(` SELECT v.*, g.name AS group_name FROM custom_variables v LEFT JOIN groups g ON g.id = v.group_id WHERE ${visibleClause('v')} ORDER BY v.category, v.name `).all(userId, userId); } export function listVariableCatalog(userId) { const catalog = fullVariableCatalog(); return { ...catalog, custom: visibleCustomVariableRows(userId).map((row) => { const includeValue = !Boolean(row.sensitive); const value = includeValue ? decryptSecret(row) : undefined; return camelCustomVariable({ ...row, value }, includeValue); }) }; } export function listCustomVariables(userId) { return visibleCustomVariableRows(userId).map((row) => { const includeValue = !Boolean(row.sensitive); const value = includeValue ? decryptSecret(row) : undefined; return camelCustomVariable({ ...row, value }, includeValue); }); } export function listExecutableCustomVariables(userId) { return visibleCustomVariableRows(userId).map((row) => ({ id: row.id, name: row.name, value: decryptSecret(row), valueType: row.value_type || 'string', sensitive: Boolean(row.sensitive) })); } export function createCustomVariable(body, userId) { const payload = normalizeVariable(body); const encrypted = encryptSecret(payload.value); const variableId = id('var'); db.prepare(` INSERT INTO custom_variables ( id, name, description, category, value_type, secret_cipher, secret_iv, secret_tag, sensitive, visibility, owner_user_id, group_id, created_by, created_at, updated_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) `).run( variableId, payload.name, payload.description, payload.category, payload.valueType, encrypted.cipher, encrypted.iv, encrypted.tag, payload.sensitive ? 1 : 0, payload.visibility, userId, payload.groupId, userId, now(), now() ); return loadCustomVariable(variableId, userId); } export function loadCustomVariable(variableId, userId) { const row = db.prepare(` SELECT v.*, g.name AS group_name FROM custom_variables v LEFT JOIN groups g ON g.id = v.group_id WHERE v.id = ? AND ${visibleClause('v')} `).get(variableId, userId, userId); if (!row) return null; const includeValue = !Boolean(row.sensitive); const value = includeValue ? decryptSecret(row) : undefined; return camelCustomVariable({ ...row, value }, includeValue); } export function updateCustomVariable(variableId, body, userId) { const existing = db.prepare(`SELECT * FROM custom_variables WHERE id = ? AND ${visibleClause()}`).get(variableId, userId, userId); if (!existing) return null; const payload = normalizeVariable(body, { name: existing.name, description: existing.description || '', category: existing.category || 'Custom', value: body.value ?? decryptSecret(existing), valueType: existing.value_type || 'string', sensitive: Boolean(existing.sensitive), visibility: existing.visibility, groupId: existing.group_id }); const encrypted = body.value === undefined ? { cipher: existing.secret_cipher, iv: existing.secret_iv, tag: existing.secret_tag } : encryptSecret(payload.value); db.prepare(` UPDATE custom_variables SET name = ?, description = ?, category = ?, value_type = ?, secret_cipher = ?, secret_iv = ?, secret_tag = ?, sensitive = ?, visibility = ?, group_id = ?, updated_at = ? WHERE id = ? `).run( payload.name, payload.description, payload.category, payload.valueType, encrypted.cipher, encrypted.iv, encrypted.tag, payload.sensitive ? 1 : 0, payload.visibility, payload.groupId, now(), variableId ); return loadCustomVariable(variableId, userId); } export function deleteCustomVariable(variableId, userId) { db.prepare(`DELETE FROM custom_variables WHERE id = ? AND ${visibleClause()}`).run(variableId, userId, userId); }