Files

127 lines
6.3 KiB
JavaScript
Raw Permalink Normal View History

2026-10-29 11:57:29 +01:00
// Generated, do not edit. sha256:ddd8b3b7b2f9aadd1a75eeaa934fd9a7d05a36cbd2f06e3a6cc5379098c70228
import * as db from '../db.js';
import { toSearchName } from '../../shared/search-name.js';
import { ENUMS, CONNECTION_PROVIDER } from '../../shared/config.js';
import { assertEnum, nowIso } from './_shared.js';
import { emitProvidersChanged } from './providers.js';
import { emitChange } from './_events.js';
import { trackUpdate, trackUpdateWhere, trackUpdateField, trackDelete } from './change-log.js';
import { fail } from '../../shared/app-error.js';
import { hasFeature } from '../license.mjs';
function emitConnectionsChanged() {
emitChange('connections-changed');
}
function syncConnectionProvider(conn) {
const providerKey = CONNECTION_PROVIDER[conn.kind];
if (!providerKey) return;
const p = db.one(`SELECT id, baseUrl FROM Provider WHERE key = ?`, [providerKey]);
if (!p || p.baseUrl === conn.url) return;
db.run(`UPDATE Provider SET baseUrl = ? WHERE id = ?`, [conn.url, p.id]);
emitProvidersChanged();
}
export function listConnections(kind = null) {
if (kind != null) assertEnum('kind', ENUMS.CONNECTION_KIND, kind);
return kind
? db.all(`SELECT * FROM Connection WHERE kind = ? ORDER BY isSystem DESC, name`, [kind])
: db.all(`SELECT * FROM Connection ORDER BY kind, isSystem DESC, name`);
}
export function activeConnection(kind) {
assertEnum('kind', ENUMS.CONNECTION_KIND, kind);
if (!CONNECTION_PROVIDER[kind] && kind !== 'btc_explorer') {
throw new Error(`Kind '${kind}' is linked per account; use listConnections('${kind}').`);
}
const row = db.one(`SELECT * FROM Connection WHERE kind = ? AND isActive = 1`, [kind]);
if (!row) fail('connections.noActive', { kind }, `No active '${kind}' connection.`);
return row;
}
export async function saveConnection({ id = null, kind, name, url }) {
assertEnum('kind', ENUMS.CONNECTION_KIND, kind);
if ((kind === 'lnd' || kind === 'nwc') && !hasFeature('lightning')) {
fail('connections.lightningPaid', null,
'Lightning connections belong to the paid version.');
}
if (kind === 'btc_explorer' && !hasFeature('ownMempool')) {
fail('connections.ownExplorerPaid', null,
'Your own explorer belongs to the paid version; the standard one keeps working.');
}
name = (name || '').trim();
url = (url || '').trim().replace(/\/+$/, '');
if (!name) fail('connections.nameRequired', null, 'A name is required.');
if (!/^(https?|wss?):\/\//.test(url)) fail('connections.urlScheme', null, 'A URL starts with http(s):// or ws(s)://.');
const now = nowIso();
if (id == null) {
if (db.one(`SELECT 1 AS x FROM Connection WHERE kind = ? AND name = ?`, [kind, name])) {
fail('connections.nameInUse', { kind, naam: name }, `A '${kind}' connection named '${name}' already exists.`);
}
db.run(`INSERT INTO Connection (name, searchName, url, kind, isActive, isSystem, lastStatus, lastCheckedAt, createdAt, updatedAt)
VALUES (?,?,?,?,0,0,NULL,NULL,?,?)`, [name, toSearchName(name), url, kind, now, now]);
id = db.lastId();
} else {
const cur = db.one(`SELECT * FROM Connection WHERE id = ?`, [id]);
if (!cur) fail('connections.unknownConnection', { id }, `Connection #${id} does not exist.`);
if (cur.isSystem) throw new Error('This profile is a system row (the guaranteed fallback) and cannot be edited.');
if (cur.kind !== kind) throw new Error('The kind of an existing connection cannot be changed.');
const urlChanged = cur.url !== url;
trackUpdate('Connection', id, () =>
db.run(`UPDATE Connection SET name = ?, searchName = ?, url = ?, lastStatus = ${urlChanged ? 'NULL' : 'lastStatus'},
lastCheckedAt = ${urlChanged ? 'NULL' : 'lastCheckedAt'}, updatedAt = ? WHERE id = ?`,
[name, toSearchName(name), url, now, id]));
if (urlChanged && cur.isActive) syncConnectionProvider({ kind: cur.kind, url });
}
await db.persist();
emitConnectionsChanged();
return id;
}
export async function setActiveConnection(id) {
const c = db.one(`SELECT * FROM Connection WHERE id = ?`, [id]);
if (!c) fail('connections.unknownConnection', { id }, `Connection #${id} does not exist.`);
const now = nowIso();
if (c.kind === 'btc_explorer') {
trackUpdateWhere('Connection', `kind = ? AND isActive = 1 AND id != ?`, [c.kind, id], () =>
db.run(`UPDATE Connection SET isActive = 0, updatedAt = ? WHERE kind = ? AND isActive = 1 AND id != ?`, [now, c.kind, id]));
}
trackUpdate('Connection', id, () =>
db.run(`UPDATE Connection SET isActive = 1, updatedAt = ? WHERE id = ?`, [now, id]));
if (c.kind === 'btc_explorer') syncConnectionProvider({ kind: c.kind, url: c.url });
await db.persist();
emitConnectionsChanged();
}
export async function deleteConnection(id) {
const c = db.one(`SELECT * FROM Connection WHERE id = ?`, [id]);
if (!c) fail('connections.unknownConnection', { id }, `Connection #${id} does not exist.`);
if (c.isSystem) throw new Error('This profile is a system row (the guaranteed fallback) and cannot be deleted.');
const linked = db.one(`SELECT COUNT(*) AS n FROM WalletSource WHERE connectionId = ?`, [id]);
if (linked && linked.n > 0) {
fail('connections.linkedToSources', { n: linked.n },
`This connection is still attached to ${linked.n} tracking source(s).`);
}
if (c.kind === 'btc_explorer' && c.isActive) {
const fallback = db.one(`SELECT * FROM Connection WHERE kind = 'btc_explorer' AND isSystem = 1`);
if (!fallback) throw new Error('No system fallback profile found; refusing to delete.');
const now = nowIso();
trackUpdate('Connection', c.id, () =>
db.run(`UPDATE Connection SET isActive = 0, updatedAt = ? WHERE id = ?`, [now, c.id]));
trackUpdate('Connection', fallback.id, () =>
db.run(`UPDATE Connection SET isActive = 1, updatedAt = ? WHERE id = ?`, [now, fallback.id]));
syncConnectionProvider({ kind: 'btc_explorer', url: fallback.url });
}
trackDelete('Connection', id, () => db.run(`DELETE FROM Connection WHERE id = ?`, [id]));
await db.persist();
emitConnectionsChanged();
}
export async function recordConnectionStatus(id, ok) {
const now = nowIso();
trackUpdateField('Connection', id, 'lastStatus', () =>
db.run(`UPDATE Connection SET lastStatus = ?, lastCheckedAt = ?, updatedAt = ? WHERE id = ?`,
[ok ? 'ok' : 'fail', now, now, id]));
await db.persist();
emitConnectionsChanged();
}