128 lines
6.3 KiB
JavaScript
128 lines
6.3 KiB
JavaScript
// 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();
|
|
}
|