Files
poshmanager/server/models/vcenterConnectionModel.js
2026-06-25 18:34:45 -05:00

191 lines
7.0 KiB
JavaScript

import { db, id, now, visibleClause } from '../db.js';
import { vcenterConnectionSchema } from '../forms/schemas.js';
import { decryptSecret, encryptSecret } from '../services/cryptoStore.js';
import { camelVCenterConnection } from './mappers.js';
function normalizeConnection(body, existing = {}) {
const parsed = vcenterConnectionSchema.parse({
...existing,
...body,
password: body.password || '',
credentialId: body.credentialId ?? existing.credentialId ?? existing.credential_id ?? null
});
const targetType = parsed.targetType || 'vcenter';
const apiMode = targetType === 'host'
? 'web-services'
: (parsed.apiMode === 'web-services' ? 'auto' : parsed.apiMode);
return {
...parsed,
targetType,
apiMode,
baseUrl: String(parsed.baseUrl || '').trim().replace(/\/+$/, ''),
credentialId: parsed.credentialId || null,
username: parsed.username || '',
groupId: parsed.visibility === 'group' ? parsed.groupId || null : null
};
}
function connectionSelect() {
return `
SELECT c.*, g.name AS group_name, cred.name AS credential_name, cred.username AS credential_username,
cred.kind AS credential_kind, cred.secret_cipher AS credential_secret_cipher,
cred.secret_iv AS credential_secret_iv, cred.secret_tag AS credential_secret_tag
FROM vcenter_connections c
LEFT JOIN groups g ON g.id = c.group_id
LEFT JOIN credentials cred ON cred.id = c.credential_id
`;
}
function getVisibleCredential(credentialId, userId) {
if (!credentialId) return null;
return db.prepare(`SELECT * FROM credentials WHERE id = ? AND ${visibleClause()}`).get(credentialId, userId, userId);
}
function assertUsableCredential(credentialId, userId) {
const credential = getVisibleCredential(credentialId, userId);
if (!credential) throw new Error('Choose a visible Credential Vault entry for this VMware connection.');
if (credential.kind !== 'username_password') throw new Error('VMware connections require a username/password Credential Vault entry.');
if (!credential.username) throw new Error('The selected Credential Vault entry must include a username.');
return credential;
}
function attachSecret(row, includeSecret) {
if (!includeSecret) return camelVCenterConnection(row);
if (row.credential_id) {
return {
...row,
username: row.credential_username || '',
password: decryptSecret({
secret_cipher: row.credential_secret_cipher,
secret_iv: row.credential_secret_iv,
secret_tag: row.credential_secret_tag
})
};
}
return { ...row, password: decryptSecret(row) };
}
export function listVCenterConnections(userId) {
return db.prepare(`
${connectionSelect()}
WHERE ${visibleClause('c')}
ORDER BY c.name
`).all(userId, userId).map(camelVCenterConnection);
}
export function getVCenterConnection(connectionId, userId, includeSecret = false) {
if (!connectionId) return null;
const row = db.prepare(`
${connectionSelect()}
WHERE c.id = ? AND ${visibleClause('c')}
`).get(connectionId, userId, userId);
if (!row) return null;
return attachSecret(row, includeSecret);
}
export function getVCenterConnectionSystem(connectionId, includeSecret = false) {
if (!connectionId) return null;
const row = db.prepare(`${connectionSelect()} WHERE c.id = ?`).get(connectionId);
if (!row) return null;
return attachSecret(row, includeSecret);
}
export function createVCenterConnection(body, userId) {
const payload = normalizeConnection(body);
const credential = payload.credentialId ? assertUsableCredential(payload.credentialId, userId) : null;
if (!credential && !payload.password) throw new Error('Choose a Credential Vault entry for this VMware connection.');
const encrypted = credential ? encryptSecret('') : encryptSecret(payload.password);
const connectionId = id('vc');
db.prepare(`
INSERT INTO vcenter_connections (
id, name, base_url, username, secret_cipher, secret_iv, secret_tag, credential_id,
target_type, api_mode, request_timeout_ms, allow_untrusted_tls, enabled,
visibility, owner_user_id, group_id, created_by, created_at, updated_at
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`).run(
connectionId,
payload.name,
payload.baseUrl,
credential?.username || payload.username || '',
encrypted.cipher,
encrypted.iv,
encrypted.tag,
payload.credentialId,
payload.targetType,
payload.apiMode,
payload.requestTimeoutMs,
payload.allowUntrustedTls ? 1 : 0,
payload.enabled ? 1 : 0,
payload.visibility,
userId,
payload.groupId,
userId,
now(),
now()
);
return getVCenterConnection(connectionId, userId);
}
export function updateVCenterConnection(connectionId, body, userId) {
const existing = getVCenterConnection(connectionId, userId, true);
if (!existing) return null;
const payload = normalizeConnection(body, {
name: existing.name,
baseUrl: existing.base_url,
username: existing.username,
credentialId: existing.credential_id,
targetType: existing.target_type || 'vcenter',
apiMode: existing.api_mode,
requestTimeoutMs: existing.request_timeout_ms,
allowUntrustedTls: Boolean(existing.allow_untrusted_tls),
enabled: Boolean(existing.enabled),
visibility: existing.visibility,
groupId: existing.group_id
});
const credential = payload.credentialId ? assertUsableCredential(payload.credentialId, userId) : null;
if (!credential && !payload.password && !existing.secret_cipher) throw new Error('Choose a Credential Vault entry for this VMware connection.');
const encrypted = credential ? encryptSecret('') : payload.password ? encryptSecret(payload.password) : {
cipher: existing.secret_cipher,
iv: existing.secret_iv,
tag: existing.secret_tag
};
db.prepare(`
UPDATE vcenter_connections
SET name = ?, base_url = ?, username = ?, secret_cipher = ?, secret_iv = ?, secret_tag = ?, credential_id = ?,
target_type = ?, api_mode = ?, request_timeout_ms = ?, allow_untrusted_tls = ?, enabled = ?,
visibility = ?, group_id = ?, updated_at = ?
WHERE id = ?
`).run(
payload.name,
payload.baseUrl,
credential?.username || payload.username || '',
encrypted.cipher,
encrypted.iv,
encrypted.tag,
payload.credentialId,
payload.targetType,
payload.apiMode,
payload.requestTimeoutMs,
payload.allowUntrustedTls ? 1 : 0,
payload.enabled ? 1 : 0,
payload.visibility,
payload.groupId,
now(),
connectionId
);
return getVCenterConnection(connectionId, userId);
}
export function deleteVCenterConnection(connectionId, userId) {
db.prepare(`DELETE FROM vcenter_connections WHERE id = ? AND ${visibleClause()}`).run(connectionId, userId, userId);
}
export function recordVCenterConnectionTest(connectionId, status, message) {
db.prepare(`
UPDATE vcenter_connections
SET last_test_status = ?, last_test_message = ?, last_test_at = ?, updated_at = ?
WHERE id = ?
`).run(status, message || '', now(), now(), connectionId);
}