Files

192 lines
9.8 KiB
JavaScript
Raw Permalink Normal View History

2026-10-30 12:28:48 +01:00
// Generated, do not edit. sha256:9946c137bb48925908ab17af61fbcdfb543a87d4be25e34812a95d4084af9425
2026-10-29 11:57:29 +01:00
import { CONFIG, PREFERENCES, SOURCE_CAPABILITIES, SOURCE_ENDPOINTS, DASHBOARD } from '../../shared/config.js';
import { toSearchName } from '../../shared/search-name.js';
import { run, one, all, lastId } from './conn.mjs';
function basisMunt() {
const r = one(`SELECT baseCurrency FROM Portfolio WHERE id = 1`);
if (!r) throw new Error('Seeds need the Portfolio row: write the unit of account first.');
return r.baseCurrency;
}
function fiatAssets(basis) {
const codes = [...new Set([basis, 'EUR', 'USD'])];
const namen = new Intl.DisplayNames(['nl-NL'], { type: 'currency' });
return codes.map(code => {
const naam = namen.of(code);
const decimals = new Intl.NumberFormat('nl-NL', { style: 'currency', currency: code }).resolvedOptions().maximumFractionDigits;
return { symbol: code, name: naam.charAt(0).toUpperCase() + naam.slice(1), kind: 'fiat', unit: code,
subunit: code === 'EUR' || code === 'USD' ? 'cent' : null, decimals, displayUnit: 'unit' };
});
}
function startBron(symbol, basis) {
if (symbol === basis) return 'fixed';
if (symbol === 'EUR' || symbol === 'USD') return 'frankfurter';
return { BTC: 'mempool', ETH: 'bitvavo', XAU: 'goldapi', XAG: 'goldapi' }[symbol] ?? null;
}
export function seedAssets() {
for (const a of fiatAssets(basisMunt())) {
if (one(`SELECT 1 AS x FROM Asset WHERE symbol = ?`, [a.symbol])) continue;
run(
`INSERT INTO Asset (symbol, name, searchName, kind, unit, subunit, decimals, displayUnit)
VALUES (?,?,?,?,?,?,?,?)`,
[a.symbol, a.name, toSearchName(a.name), a.kind, a.unit, a.subunit, a.decimals, a.displayUnit]
);
}
}
const SEED_METAL_PRODUCTS = [
{ symbol: 'XAU', name: 'Krugerrand 1 oz', form: 'coin', grams: CONFIG.METALS.GRAMS_PER_TROY_OUNCE },
{ symbol: 'XAU', name: 'Maple Leaf 1 oz', form: 'coin', grams: CONFIG.METALS.GRAMS_PER_TROY_OUNCE },
{ symbol: 'XAU', name: 'Wiener Philharmoniker 1 oz', form: 'coin', grams: CONFIG.METALS.GRAMS_PER_TROY_OUNCE },
{ symbol: 'XAU', name: 'Baar 1 g', form: 'bar', grams: 1 },
{ symbol: 'XAU', name: 'Baar 5 g', form: 'bar', grams: 5 },
{ symbol: 'XAU', name: 'Baar 10 g', form: 'bar', grams: 10 },
{ symbol: 'XAU', name: 'Baar 100 g', form: 'bar', grams: 100 },
{ symbol: 'XAU', name: 'Baar 1 kg', form: 'bar', grams: 1000 },
{ symbol: 'XAG', name: 'Maple Leaf 1 oz', form: 'coin', grams: CONFIG.METALS.GRAMS_PER_TROY_OUNCE },
{ symbol: 'XAG', name: 'Wiener Philharmoniker 1 oz', form: 'coin', grams: CONFIG.METALS.GRAMS_PER_TROY_OUNCE },
{ symbol: 'XAG', name: 'Baar 100 g', form: 'bar', grams: 100 },
{ symbol: 'XAG', name: 'Baar 1 kg', form: 'bar', grams: 1000 },
];
export function seedMetalProducts() {
const factor = Math.pow(10, CONFIG.METALS.BASE_DECIMALS);
for (const p of SEED_METAL_PRODUCTS) {
const a = one(`SELECT id FROM Asset WHERE symbol = ?`, [p.symbol]);
if (!a) continue;
if (one(`SELECT 1 AS x FROM MetalProduct WHERE assetId = ? AND name = ?`, [a.id, p.name])) continue;
run(`INSERT INTO MetalProduct (assetId, name, searchName, form, fineGrams) VALUES (?,?,?,?,?)`,
[a.id, p.name, toSearchName(p.name), p.form, Math.round(p.grams * factor)]);
}
}
const SEED_CONNECTIONS = [
{ kind: 'btc_explorer', name: 'mempool.space (publiek)', url: CONFIG.PRICE_SOURCES.MEMPOOL_PUBLIC_BASE, isActive: 1, isSystem: 1 },
];
export function seedConnections() {
const now = new Date().toISOString();
for (const c of SEED_CONNECTIONS) {
if (one(`SELECT 1 AS x FROM Connection WHERE kind = ? AND name = ?`, [c.kind, c.name])) continue;
const hasActive = !!one(`SELECT 1 AS x FROM Connection WHERE kind = ? AND isActive = 1`, [c.kind]);
run(`INSERT INTO Connection (name, searchName, url, kind, isActive, isSystem, lastStatus, lastCheckedAt, createdAt, updatedAt)
VALUES (?,?,?,?,?,?,NULL,NULL,?,?)`,
[c.name, toSearchName(c.name), c.url, c.kind, hasActive ? 0 : c.isActive, c.isSystem, now, now]);
}
}
function seedProviderList(basis) {
return [
{ key: 'manual', name: 'Handmatig', baseUrl: '', authenticationKind: 'none', authenticationRequired: 0, isSystem: true,
authenticationHeaderName: null, authenticationParameterName: null, hmac: null,
endpoints: [], symbols: [], coverage: [], outputs: [] },
{ key: CONFIG.MARKET_SERVICE.PROVIDER_KEY, name: 'Atreides Services', baseUrl: '', authenticationKind: 'none',
authenticationRequired: 0, isSystem: true, authenticationHeaderName: null, authenticationParameterName: null,
hmac: null, endpoints: [], symbols: [], coverage: [], outputs: [] },
{ key: 'fixed', name: 'Vaste koers', baseUrl: '', authenticationKind: 'none', authenticationRequired: 0, isSystem: true,
authenticationHeaderName: null, authenticationParameterName: null, hmac: null,
fixedRate: { fiatPrice: 1 },
endpoints: [
{ capability: 'spot', method: 'GET', pathTemplate: '', queryTemplate: null },
{ capability: 'history_point', method: 'GET', pathTemplate: '', queryTemplate: null },
],
symbols: [], coverage: [basis], outputs: [] },
];
}
export function seedProviders() {
for (const p of seedProviderList(basisMunt())) {
if (one(`SELECT 1 AS x FROM Provider WHERE key = ?`, [p.key])) continue;
const isActive = p.authenticationRequired ? 0 : 1;
run(`INSERT INTO Provider
(key, name, searchName, baseUrl, authenticationHeaderName, authenticationParameterName,
authenticationKind, authenticationRequired, isActive, isSeed, isSystem, createdAt)
VALUES (?,?,?,?,?,?,?,?,?,1,?,?)`,
[p.key, p.name, toSearchName(p.name), p.baseUrl, p.authenticationHeaderName, p.authenticationParameterName,
p.authenticationKind, p.authenticationRequired, isActive, p.isSystem ? 1 : 0, new Date().toISOString()]);
const pid = lastId();
for (const symbool of p.coverage || []) {
run(`INSERT INTO ProviderCoverage (providerId, symbol) VALUES (?,?)`, [pid, symbool]);
}
if (p.fixedRate) {
run(`INSERT INTO ProviderFixedRate (providerId, fiatPrice) VALUES (?,?)`, [pid, p.fixedRate.fiatPrice]);
}
if (p.callsPerMonth) {
run(`INSERT INTO ProviderCallLimit (providerId, callsPerMonth, createdAt) VALUES (?,?,?)`,
[pid, p.callsPerMonth, new Date().toISOString()]);
}
if (p.hmac) {
const h = p.hmac;
run(`INSERT INTO ProviderAuthenticationHmac
(providerId, messageParts, hashAlgorithm, encoding, keyHeader, signatureHeader, timestampHeader, windowHeader, windowMs)
VALUES (?,?,?,?,?,?,?,?,?)`,
[pid, h.messageParts, h.hashAlgorithm, h.encoding, h.keyHeader, h.signatureHeader, h.timestampHeader, h.windowHeader, h.windowMs]);
}
for (const e of p.endpoints)
run(`INSERT INTO ProviderEndpoint (providerId, capability, method, pathTemplate, queryTemplate, quoteCurrency) VALUES (?,?,?,?,?,?)`,
[pid, e.capability, e.method, e.pathTemplate, e.queryTemplate, e.quoteCurrency ?? null]);
for (const s of p.symbols)
run(`INSERT INTO ProviderInputMap (providerId, symbol, externalId, quoteUnit, isCatchAll) VALUES (?,?,?,?,?)`,
[pid, s.isCatchAll ? null : s.symbol, s.externalId, s.quoteUnit ?? null, s.isCatchAll ? 1 : 0]);
for (const o of p.outputs)
run(`INSERT INTO ProviderOutputMap (providerId, capability, internalField, jsonPath, transform) VALUES (?,?,?,?,?)`,
[pid, o.capability, o.internalField, o.jsonPath, o.transform]);
}
}
export function seedPriceSource(assetId, status) {
const assets = assetId
? all(`SELECT id, symbol FROM Asset WHERE id = ?`, [assetId])
: all(`SELECT id, symbol FROM Asset`);
const basis = basisMunt();
for (const asset of assets) {
const srcKey = startBron(asset.symbol, basis);
if (!srcKey) continue;
const provider = one(`SELECT id FROM Provider WHERE key = ?`, [srcKey]);
if (!provider) continue;
for (const capability of SOURCE_CAPABILITIES) {
if (one(`SELECT 1 AS x FROM PriceSource WHERE assetId = ? AND capability = ?`,
[asset.id, capability])) continue;
const eindpunten = SOURCE_ENDPOINTS[capability];
if (!one(`SELECT 1 AS x FROM ProviderEndpoint WHERE providerId = ? AND capability IN (${eindpunten.map(() => '?').join(',')})`,
[provider.id, ...eindpunten])) continue;
run(`INSERT INTO PriceSource (assetId, capability, providerId, rank, status) VALUES (?,?,?,1,?)`,
[asset.id, capability, provider.id, status]);
}
}
}
export function seedDashboardProfile() {
if (one(`SELECT 1 AS x FROM DashboardProfile WHERE isSystem = 1`)) return false;
const now = new Date().toISOString();
const hasActive = !!one(`SELECT 1 AS x FROM DashboardProfile WHERE isActive = 1`);
run(`INSERT INTO DashboardProfile (name, searchName, description, sortOrder, isActive, isSystem, createdAt, updatedAt)
VALUES (?,?,NULL,0,?,1,?,?)`,
['Standaard', toSearchName('Standaard'), hasActive ? 0 : 1, now, now]);
const profileId = lastId();
for (const w of DASHBOARD.DEFAULT_LAYOUT) {
const def = DASHBOARD.WIDGETS[w.type];
if (!def) continue;
run(`INSERT INTO DashboardWidget (profileId, assetId, instanceKey, gridX, gridY, spanWidth, spanHeight, type, settings)
VALUES (?,NULL,?,?,0,?,NULL,?,NULL)`, [profileId, w.instId, w.x || 0, def.defaultSpan, w.type]);
}
return true;
}
2026-10-30 12:28:48 +01:00
const FRESH_VALUES = Object.freeze({
app: Object.freeze({ market_spot: 'first' }),
ai: Object.freeze({ enabled: 0 }),
});
2026-10-29 11:57:29 +01:00
export function seedPreferences() {
for (const [scope, keys] of Object.entries(PREFERENCES)) {
for (const [key, spec] of Object.entries(keys)) {
if (spec.secret) continue;
run(`INSERT INTO Preference (scope, key, value) VALUES (?,?,?)`,
[scope, key, JSON.stringify(FRESH_VALUES[scope]?.[key] ?? spec.initial)]);
}
}
}