mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-31 04:23:40 +00:00
Feature/fe new registry (#2647)
* bugfixes * update fe types * implement new registry types in marketplace and ui * fix marketplace types to have default params * add alt implementation toggle * merge cleanup * more cleanup and notes * fix build * cleanup sync with next/minor * add exver JS parser * parse ValidExVer to string * update types to interface * add VersionRange and comparative functions * Parse ExtendedVersion from string * add conjunction, disjunction, and inversion logic * consider flavor in satisfiedBy fn * consider prerelease for ordering * add compare fn for sorting * rename fns for consistency * refactoring * update compare fn to return null if flavors don't match * begin simplifying dependencies * under construction * wip * add dependency metadata to CurrentDependencyInfo * ditch inheritance for recursive VersionRange constructor. Recursive 'satisfiedBy' fn wip * preprocess manifest * misc fixes * use sdk version as osVersion in manifest * chore: Change the type to just validate and not generate all solutions. * add publishedAt * fix pegjs exports * integrate exver into sdk * misc fixes * complete satisfiedBy fn * refactor - use greaterThanOrEqual and lessThanOrEqual fns * fix tests * update dependency details * update types * remove interim types * rename alt implementation to flavor * cleanup os update * format exver.ts * add s9pk parsing endpoints * fix build * update to exver * exver and bug fixes * update static endpoints + cleanup * cleanup * update static proxy verification * make mocks more robust; fix dep icon fallback; cleanup * refactor alert versions and update fixtures * registry bugfixes * misc fixes * cleanup unused * convert patchdb ui seed to camelCase * update otherVersions type * change otherVersions: null to 'none' * refactor and complete feature * improve static endpoints * fix install params * mask systemd-networkd-wait-online * fix static file fetching * include non-matching versions in otherVersions * convert release notes to modal and clean up displayExver * alert for no other versions * Fix ack-instructions casing * fix indeterminate loader on service install --------- Co-authored-by: Aiden McClelland <me@drbonez.dev> Co-authored-by: Shadowy Super Coder <musashidisciple@proton.me> Co-authored-by: Aiden McClelland <3732071+dr-bonez@users.noreply.github.com> Co-authored-by: J H <dragondef@gmail.com> Co-authored-by: Matt Hill <mattnine@protonmail.com>
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -3,11 +3,26 @@ import {
|
||||
PackageDataEntry,
|
||||
} from 'src/app/services/patch-db/data-model'
|
||||
import { Metric, NotificationLevel, RR, ServerNotifications } from './api.types'
|
||||
import { BTC_ICON, LND_ICON, PROXY_ICON } from './api-icons'
|
||||
import { DependencyMetadata, MarketplacePkg } from '@start9labs/marketplace'
|
||||
import { BTC_ICON, LND_ICON, PROXY_ICON, REGISTRY_ICON } from './api-icons'
|
||||
import { Log } from '@start9labs/shared'
|
||||
import { configBuilderToSpec } from 'src/app/util/configBuilderToSpec'
|
||||
import { T, CB } from '@start9labs/start-sdk'
|
||||
import { GetPackagesRes } from '@start9labs/marketplace'
|
||||
|
||||
const mockBlake3Commitment: T.Blake3Commitment = {
|
||||
hash: 'fakehash',
|
||||
size: 0,
|
||||
}
|
||||
|
||||
const mockMerkleArchiveCommitment: T.MerkleArchiveCommitment = {
|
||||
rootSighash: 'fakehash',
|
||||
rootMaxsize: 0,
|
||||
}
|
||||
|
||||
const mockDescription = {
|
||||
short: 'Lorem ipsum dolor sit amet',
|
||||
long: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
|
||||
}
|
||||
|
||||
export module Mock {
|
||||
export const ServerUpdated: T.ServerStatus = {
|
||||
@@ -37,17 +52,42 @@ export module Mock {
|
||||
},
|
||||
}
|
||||
|
||||
export const ReleaseNotes: RR.GetReleaseNotesRes = {
|
||||
'0.19.2':
|
||||
'Contrary to popular belief, Lorem Ipsum is not simply random text.',
|
||||
'0.19.1': 'release notes for Bitcoin 0.19.1',
|
||||
'0.19.0': 'release notes for Bitcoin 0.19.0',
|
||||
export const RegistryInfo: T.RegistryInfo = {
|
||||
name: 'Start9 Registry',
|
||||
icon: REGISTRY_ICON,
|
||||
categories: {
|
||||
bitcoin: {
|
||||
name: 'Bitcoin',
|
||||
description: mockDescription,
|
||||
},
|
||||
featured: {
|
||||
name: 'Featured',
|
||||
description: mockDescription,
|
||||
},
|
||||
lightning: {
|
||||
name: 'Lightning',
|
||||
description: mockDescription,
|
||||
},
|
||||
communications: {
|
||||
name: 'Communications',
|
||||
description: mockDescription,
|
||||
},
|
||||
data: {
|
||||
name: 'Data',
|
||||
description: mockDescription,
|
||||
},
|
||||
ai: {
|
||||
name: 'AI',
|
||||
description: mockDescription,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const MockManifestBitcoind: T.Manifest = {
|
||||
id: 'bitcoind',
|
||||
title: 'Bitcoin Core',
|
||||
version: '0.21.0',
|
||||
version: '0.21.0:0',
|
||||
satisfies: [],
|
||||
gitHash: 'abcdefgh',
|
||||
description: {
|
||||
short: 'A Bitcoin full node by Bitcoin Core.',
|
||||
@@ -90,7 +130,8 @@ export module Mock {
|
||||
export const MockManifestLnd: T.Manifest = {
|
||||
id: 'lnd',
|
||||
title: 'Lightning Network Daemon',
|
||||
version: '0.11.1',
|
||||
version: '0.11.1:0',
|
||||
satisfies: [],
|
||||
gitHash: 'abcdefgh',
|
||||
description: {
|
||||
short: 'A bolt spec compliant client.',
|
||||
@@ -116,11 +157,13 @@ export module Mock {
|
||||
bitcoind: {
|
||||
description: 'LND needs bitcoin to live.',
|
||||
optional: true,
|
||||
s9pk: '',
|
||||
},
|
||||
'btc-rpc-proxy': {
|
||||
description:
|
||||
'As long as Bitcoin is pruned, LND needs Bitcoin Proxy to fetch block over the P2P network.',
|
||||
optional: true,
|
||||
s9pk: '',
|
||||
},
|
||||
},
|
||||
hasConfig: true,
|
||||
@@ -143,7 +186,8 @@ export module Mock {
|
||||
export const MockManifestBitcoinProxy: T.Manifest = {
|
||||
id: 'btc-rpc-proxy',
|
||||
title: 'Bitcoin Proxy',
|
||||
version: '0.2.2',
|
||||
version: '0.2.2:0',
|
||||
satisfies: [],
|
||||
gitHash: 'lmnopqrx',
|
||||
description: {
|
||||
short: 'A super charger for your Bitcoin node.',
|
||||
@@ -168,6 +212,7 @@ export module Mock {
|
||||
bitcoind: {
|
||||
description: 'Bitcoin Proxy requires a Bitcoin node.',
|
||||
optional: false,
|
||||
s9pk: '',
|
||||
},
|
||||
},
|
||||
hasConfig: false,
|
||||
@@ -187,149 +232,509 @@ export module Mock {
|
||||
},
|
||||
}
|
||||
|
||||
export const BitcoinDep: DependencyMetadata = {
|
||||
export const BitcoinDep: T.DependencyMetadata = {
|
||||
title: 'Bitcoin Core',
|
||||
icon: BTC_ICON,
|
||||
optional: false,
|
||||
hidden: true,
|
||||
description: 'Needed to run',
|
||||
}
|
||||
|
||||
export const ProxyDep: DependencyMetadata = {
|
||||
export const ProxyDep: T.DependencyMetadata = {
|
||||
title: 'Bitcoin Proxy',
|
||||
icon: PROXY_ICON,
|
||||
optional: true,
|
||||
hidden: false,
|
||||
description: 'Needed to run',
|
||||
}
|
||||
|
||||
export const MarketplacePkgs: {
|
||||
[id: string]: {
|
||||
[version: string]: MarketplacePkg
|
||||
}
|
||||
export const OtherPackageVersions: {
|
||||
[id: T.PackageId]: GetPackagesRes
|
||||
} = {
|
||||
bitcoind: {
|
||||
'0.19.0': {
|
||||
icon: BTC_ICON,
|
||||
license: 'licenseUrl',
|
||||
instructions: 'instructionsUrl',
|
||||
manifest: {
|
||||
...Mock.MockManifestBitcoind,
|
||||
version: '0.19.0',
|
||||
'=26.1.0:0.1.0': {
|
||||
best: {
|
||||
'26.1.0:0.1.0': {
|
||||
title: 'Bitcoin Core',
|
||||
description: mockDescription,
|
||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
||||
license: 'mit',
|
||||
wrapperRepo: 'https://github.com/start9labs/bitcoind-startos',
|
||||
upstreamRepo: 'https://github.com/bitcoin/bitcoin',
|
||||
supportSite: 'https://bitcoin.org',
|
||||
marketingSite: 'https://bitcoin.org',
|
||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||
osVersion: '0.3.6',
|
||||
gitHash: 'fakehash',
|
||||
icon: BTC_ICON,
|
||||
sourceVersion: null,
|
||||
dependencyMetadata: {},
|
||||
donationUrl: null,
|
||||
alerts: {
|
||||
install: 'test',
|
||||
uninstall: 'test',
|
||||
start: 'test',
|
||||
stop: 'test',
|
||||
restore: 'test',
|
||||
},
|
||||
s9pk: {
|
||||
url: 'https://github.com/Start9Labs/bitcoind-startos/releases/download/v26.1.0/bitcoind.s9pk',
|
||||
commitment: mockMerkleArchiveCommitment,
|
||||
signatures: {},
|
||||
publishedAt: Date.now().toString(),
|
||||
},
|
||||
},
|
||||
'#knots:26.1.20240325:0': {
|
||||
title: 'Bitcoin Knots',
|
||||
description: {
|
||||
short: 'An alternate fully verifying implementation of Bitcoin',
|
||||
long: 'Bitcoin Knots is a combined Bitcoin node and wallet. Not only is it easy to use, but it also ensures bitcoins you receive are both real bitcoins and really yours.',
|
||||
},
|
||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
||||
license: 'mit',
|
||||
wrapperRepo: 'https://github.com/start9labs/bitcoinknots-startos',
|
||||
upstreamRepo: 'https://github.com/bitcoinknots/bitcoin',
|
||||
supportSite: 'https://bitcoinknots.org',
|
||||
marketingSite: 'https://bitcoinknots.org',
|
||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||
osVersion: '0.3.6',
|
||||
gitHash: 'fakehash',
|
||||
icon: BTC_ICON,
|
||||
sourceVersion: null,
|
||||
dependencyMetadata: {},
|
||||
donationUrl: null,
|
||||
alerts: {
|
||||
install: 'test',
|
||||
uninstall: 'test',
|
||||
start: 'test',
|
||||
stop: 'test',
|
||||
restore: 'test',
|
||||
},
|
||||
s9pk: {
|
||||
url: 'https://github.com/Start9Labs/bitcoinknots-startos/releases/download/v26.1.20240513/bitcoind.s9pk',
|
||||
commitment: mockMerkleArchiveCommitment,
|
||||
signatures: {},
|
||||
publishedAt: Date.now().toString(),
|
||||
},
|
||||
},
|
||||
},
|
||||
categories: ['bitcoin', 'featured'],
|
||||
otherVersions: {
|
||||
'27.0.0:1.0.0': {
|
||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||
},
|
||||
'#knots:27.1.0:0': {
|
||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||
},
|
||||
},
|
||||
categories: ['bitcoin', 'cryptocurrency'],
|
||||
versions: ['0.19.0', '0.20.0', '0.21.0'],
|
||||
dependencyMetadata: {},
|
||||
publishedAt: new Date().toISOString(),
|
||||
},
|
||||
'0.20.0': {
|
||||
icon: BTC_ICON,
|
||||
license: 'licenseUrl',
|
||||
instructions: 'instructionsUrl',
|
||||
manifest: {
|
||||
...Mock.MockManifestBitcoind,
|
||||
version: '0.20.0',
|
||||
'=#knots:26.1.20240325:0': {
|
||||
best: {
|
||||
'26.1.0:0.1.0': {
|
||||
title: 'Bitcoin Core',
|
||||
description: mockDescription,
|
||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
||||
license: 'mit',
|
||||
wrapperRepo: 'https://github.com/start9labs/bitcoind-startos',
|
||||
upstreamRepo: 'https://github.com/bitcoin/bitcoin',
|
||||
supportSite: 'https://bitcoin.org',
|
||||
marketingSite: 'https://bitcoin.org',
|
||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||
osVersion: '0.3.6',
|
||||
gitHash: 'fakehash',
|
||||
icon: BTC_ICON,
|
||||
sourceVersion: null,
|
||||
dependencyMetadata: {},
|
||||
donationUrl: null,
|
||||
alerts: {
|
||||
install: 'test',
|
||||
uninstall: 'test',
|
||||
start: 'test',
|
||||
stop: 'test',
|
||||
restore: 'test',
|
||||
},
|
||||
s9pk: {
|
||||
url: 'https://github.com/Start9Labs/bitcoind-startos/releases/download/v26.1.0/bitcoind.s9pk',
|
||||
commitment: mockMerkleArchiveCommitment,
|
||||
signatures: {},
|
||||
publishedAt: Date.now().toString(),
|
||||
},
|
||||
},
|
||||
'#knots:26.1.20240325:0': {
|
||||
title: 'Bitcoin Knots',
|
||||
description: {
|
||||
short: 'An alternate fully verifying implementation of Bitcoin',
|
||||
long: 'Bitcoin Knots is a combined Bitcoin node and wallet. Not only is it easy to use, but it also ensures bitcoins you receive are both real bitcoins and really yours.',
|
||||
},
|
||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
||||
license: 'mit',
|
||||
wrapperRepo: 'https://github.com/start9labs/bitcoinknots-startos',
|
||||
upstreamRepo: 'https://github.com/bitcoinknots/bitcoin',
|
||||
supportSite: 'https://bitcoinknots.org',
|
||||
marketingSite: 'https://bitcoinknots.org',
|
||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||
osVersion: '0.3.6',
|
||||
gitHash: 'fakehash',
|
||||
icon: BTC_ICON,
|
||||
sourceVersion: null,
|
||||
dependencyMetadata: {},
|
||||
donationUrl: null,
|
||||
alerts: {
|
||||
install: 'test',
|
||||
uninstall: 'test',
|
||||
start: 'test',
|
||||
stop: 'test',
|
||||
restore: 'test',
|
||||
},
|
||||
s9pk: {
|
||||
url: 'https://github.com/Start9Labs/bitcoinknots-startos/releases/download/v26.1.20240513/bitcoind.s9pk',
|
||||
commitment: mockMerkleArchiveCommitment,
|
||||
signatures: {},
|
||||
publishedAt: Date.now().toString(),
|
||||
},
|
||||
},
|
||||
},
|
||||
categories: ['bitcoin', 'cryptocurrency'],
|
||||
versions: ['0.19.0', '0.20.0', '0.21.0'],
|
||||
dependencyMetadata: {},
|
||||
publishedAt: new Date().toISOString(),
|
||||
},
|
||||
'0.21.0': {
|
||||
icon: BTC_ICON,
|
||||
license: 'licenseUrl',
|
||||
instructions: 'instructionsUrl',
|
||||
manifest: {
|
||||
...Mock.MockManifestBitcoind,
|
||||
version: '0.21.0',
|
||||
releaseNotes:
|
||||
'For a complete list of changes, please visit <a href="https://bitcoincore.org/en/releases/0.21.0/">https://bitcoincore.org/en/releases/0.21.0/</a><br /><ul><li>Taproot!</li><li>New RPCs</li><li>Experimental Descriptor Wallets</li></ul>',
|
||||
categories: ['bitcoin', 'featured'],
|
||||
otherVersions: {
|
||||
'27.0.0:1.0.0': {
|
||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||
},
|
||||
'#knots:27.1.0:0': {
|
||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||
},
|
||||
},
|
||||
categories: ['bitcoin', 'cryptocurrency'],
|
||||
versions: ['0.19.0', '0.20.0', '0.21.0'],
|
||||
dependencyMetadata: {},
|
||||
publishedAt: new Date().toISOString(),
|
||||
},
|
||||
latest: {
|
||||
icon: BTC_ICON,
|
||||
license: 'licenseUrl',
|
||||
instructions: 'instructionsUrl',
|
||||
manifest: {
|
||||
...Mock.MockManifestBitcoind,
|
||||
releaseNotes:
|
||||
'For a complete list of changes, please visit <a href="https://bitcoincore.org/en/releases/0.21.0/" target="_blank">https://bitcoincore.org/en/releases/0.21.0/</a><br />Or in [markdown](https://bitcoincore.org/en/releases/0.21.0/)<ul><li>Taproot!</li><li>New RPCs</li><li>Experimental Descriptor Wallets</li></ul>',
|
||||
},
|
||||
categories: ['bitcoin', 'cryptocurrency'],
|
||||
versions: ['0.19.0', '0.20.0', '0.21.0'],
|
||||
dependencyMetadata: {},
|
||||
publishedAt: new Date().toISOString(),
|
||||
},
|
||||
},
|
||||
lnd: {
|
||||
'0.11.0': {
|
||||
icon: LND_ICON,
|
||||
license: 'licenseUrl',
|
||||
instructions: 'instructionsUrl',
|
||||
manifest: {
|
||||
...Mock.MockManifestLnd,
|
||||
version: '0.11.0',
|
||||
releaseNotes: 'release notes for LND 0.11.0',
|
||||
'=0.17.5:0': {
|
||||
best: {
|
||||
'0.17.5:0': {
|
||||
title: 'LND',
|
||||
description: mockDescription,
|
||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
||||
license: 'mit',
|
||||
wrapperRepo: 'https://github.com/start9labs/lnd-startos',
|
||||
upstreamRepo: 'https://github.com/lightningnetwork/lnd',
|
||||
supportSite: 'https://lightning.engineering/slack.html',
|
||||
marketingSite: 'https://lightning.engineering/',
|
||||
releaseNotes: 'Upstream release to 0.17.5',
|
||||
osVersion: '0.3.6',
|
||||
gitHash: 'fakehash',
|
||||
icon: LND_ICON,
|
||||
sourceVersion: null,
|
||||
dependencyMetadata: {
|
||||
bitcoind: {
|
||||
title: 'Bitcoin Core',
|
||||
icon: BTC_ICON,
|
||||
description: 'Used for RPC requests',
|
||||
optional: false,
|
||||
},
|
||||
'btc-rpc-proxy': {
|
||||
title: 'Bitcoin Proxy',
|
||||
icon: PROXY_ICON,
|
||||
description: 'Used for authorized proxying of RPC requests',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
donationUrl: null,
|
||||
alerts: {
|
||||
install: 'test',
|
||||
uninstall: 'test',
|
||||
start: 'test',
|
||||
stop: 'test',
|
||||
restore: 'test',
|
||||
},
|
||||
s9pk: {
|
||||
url: 'https://github.com/Start9Labs/lnd-startos/releases/download/v0.17.5/lnd.s9pk',
|
||||
commitment: mockMerkleArchiveCommitment,
|
||||
signatures: {},
|
||||
publishedAt: Date.now().toString(),
|
||||
},
|
||||
},
|
||||
},
|
||||
categories: ['bitcoin', 'lightning', 'cryptocurrency'],
|
||||
versions: ['0.11.0', '0.11.1'],
|
||||
dependencyMetadata: {
|
||||
bitcoind: BitcoinDep,
|
||||
'btc-rpc-proxy': ProxyDep,
|
||||
categories: ['lightning', 'featured'],
|
||||
otherVersions: {
|
||||
'0.18.0:0.0.1': {
|
||||
releaseNotes: 'Upstream release and minor fixes.',
|
||||
},
|
||||
'0.17.4-beta:1.0-alpha': {
|
||||
releaseNotes: 'Upstream release to 0.17.4',
|
||||
},
|
||||
},
|
||||
publishedAt: new Date().toISOString(),
|
||||
},
|
||||
'0.11.1': {
|
||||
icon: LND_ICON,
|
||||
license: 'licenseUrl',
|
||||
instructions: 'instructionsUrl',
|
||||
manifest: {
|
||||
...Mock.MockManifestLnd,
|
||||
version: '0.11.1',
|
||||
releaseNotes: 'release notes for LND 0.11.1',
|
||||
'=0.17.4-beta:1.0-alpha': {
|
||||
best: {
|
||||
'0.17.4-beta:1.0-alpha': {
|
||||
title: 'LND',
|
||||
description: mockDescription,
|
||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
||||
license: 'mit',
|
||||
wrapperRepo: 'https://github.com/start9labs/lnd-startos',
|
||||
upstreamRepo: 'https://github.com/lightningnetwork/lnd',
|
||||
supportSite: 'https://lightning.engineering/slack.html',
|
||||
marketingSite: 'https://lightning.engineering/',
|
||||
releaseNotes: 'Upstream release to 0.17.4',
|
||||
osVersion: '0.3.6',
|
||||
gitHash: 'fakehash',
|
||||
icon: LND_ICON,
|
||||
sourceVersion: null,
|
||||
dependencyMetadata: {
|
||||
bitcoind: {
|
||||
title: 'Bitcoin Core',
|
||||
icon: BTC_ICON,
|
||||
description: 'Used for RPC requests',
|
||||
optional: false,
|
||||
},
|
||||
'btc-rpc-proxy': {
|
||||
title: 'Bitcoin Proxy',
|
||||
icon: PROXY_ICON,
|
||||
description: 'Used for authorized proxying of RPC requests',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
donationUrl: null,
|
||||
alerts: {
|
||||
install: 'test',
|
||||
uninstall: 'test',
|
||||
start: 'test',
|
||||
stop: 'test',
|
||||
restore: 'test',
|
||||
},
|
||||
s9pk: {
|
||||
url: 'https://github.com/Start9Labs/lnd-startos/releases/download/v0.17.4/lnd.s9pk',
|
||||
commitment: mockMerkleArchiveCommitment,
|
||||
signatures: {},
|
||||
publishedAt: Date.now().toString(),
|
||||
},
|
||||
},
|
||||
},
|
||||
categories: ['bitcoin', 'lightning', 'cryptocurrency'],
|
||||
versions: ['0.11.0', '0.11.1'],
|
||||
dependencyMetadata: {
|
||||
bitcoind: BitcoinDep,
|
||||
'btc-rpc-proxy': ProxyDep,
|
||||
categories: ['lightning', 'featured'],
|
||||
otherVersions: {
|
||||
'0.18.0:0.0.1': {
|
||||
releaseNotes: 'Upstream release and minor fixes.',
|
||||
},
|
||||
'0.17.5:0': {
|
||||
releaseNotes: 'Upstream release to 0.17.5',
|
||||
},
|
||||
},
|
||||
publishedAt: new Date().toISOString(),
|
||||
},
|
||||
latest: {
|
||||
icon: LND_ICON,
|
||||
license: 'licenseUrl',
|
||||
instructions: 'instructionsUrl',
|
||||
manifest: Mock.MockManifestLnd,
|
||||
categories: ['bitcoin', 'lightning', 'cryptocurrency'],
|
||||
versions: ['0.11.0', '0.11.1'],
|
||||
dependencyMetadata: {
|
||||
bitcoind: BitcoinDep,
|
||||
'btc-rpc-proxy': ProxyDep,
|
||||
},
|
||||
publishedAt: new Date(new Date().valueOf() + 10).toISOString(),
|
||||
},
|
||||
},
|
||||
'btc-rpc-proxy': {
|
||||
latest: {
|
||||
icon: PROXY_ICON,
|
||||
license: 'licenseUrl',
|
||||
instructions: 'instructionsUrl',
|
||||
manifest: Mock.MockManifestBitcoinProxy,
|
||||
categories: ['bitcoin'],
|
||||
versions: ['0.2.2'],
|
||||
dependencyMetadata: {
|
||||
bitcoind: BitcoinDep,
|
||||
'=0.3.2.6:0': {
|
||||
best: {
|
||||
'0.3.2.6:0': {
|
||||
title: 'Bitcoin Proxy',
|
||||
description: mockDescription,
|
||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
||||
license: 'mit',
|
||||
wrapperRepo: 'https://github.com/Start9Labs/btc-rpc-proxy-wrappers',
|
||||
upstreamRepo: 'https://github.com/Kixunil/btc-rpc-proxy',
|
||||
supportSite: 'https://github.com/Kixunil/btc-rpc-proxy/issues',
|
||||
marketingSite: '',
|
||||
releaseNotes: 'Upstream release and minor fixes.',
|
||||
osVersion: '0.3.6',
|
||||
gitHash: 'fakehash',
|
||||
icon: PROXY_ICON,
|
||||
sourceVersion: null,
|
||||
dependencyMetadata: {},
|
||||
donationUrl: null,
|
||||
alerts: {
|
||||
install: 'test',
|
||||
uninstall: 'test',
|
||||
start: 'test',
|
||||
stop: 'test',
|
||||
restore: 'test',
|
||||
},
|
||||
s9pk: {
|
||||
url: 'https://github.com/Start9Labs/btc-rpc-proxy-startos/releases/download/v0.3.2.7.1/btc-rpc-proxy.s9pk',
|
||||
commitment: mockMerkleArchiveCommitment,
|
||||
signatures: {},
|
||||
publishedAt: Date.now().toString(),
|
||||
},
|
||||
},
|
||||
},
|
||||
categories: ['bitcoin'],
|
||||
otherVersions: {
|
||||
'0.3.2.7:0': {
|
||||
releaseNotes: 'Upstream release and minor fixes.',
|
||||
},
|
||||
},
|
||||
publishedAt: new Date().toISOString(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const MarketplacePkgsList: RR.GetMarketplacePackagesRes =
|
||||
Object.values(Mock.MarketplacePkgs).map(service => service['latest'])
|
||||
export const RegistryPackages: GetPackagesRes = {
|
||||
bitcoind: {
|
||||
best: {
|
||||
'27.0.0:1.0.0': {
|
||||
title: 'Bitcoin Core',
|
||||
description: mockDescription,
|
||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
||||
license: 'mit',
|
||||
wrapperRepo: 'https://github.com/start9labs/bitcoind-startos',
|
||||
upstreamRepo: 'https://github.com/bitcoin/bitcoin',
|
||||
supportSite: 'https://bitcoin.org',
|
||||
marketingSite: 'https://bitcoin.org',
|
||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||
osVersion: '0.3.6',
|
||||
gitHash: 'fakehash',
|
||||
icon: BTC_ICON,
|
||||
sourceVersion: null,
|
||||
dependencyMetadata: {},
|
||||
donationUrl: null,
|
||||
alerts: {
|
||||
install: 'test',
|
||||
uninstall: 'test',
|
||||
start: 'test',
|
||||
stop: 'test',
|
||||
restore: 'test',
|
||||
},
|
||||
s9pk: {
|
||||
url: 'https://github.com/Start9Labs/bitcoind-startos/releases/download/v27.0.0/bitcoind.s9pk',
|
||||
commitment: mockMerkleArchiveCommitment,
|
||||
signatures: {},
|
||||
publishedAt: Date.now().toString(),
|
||||
},
|
||||
},
|
||||
'#knots:27.1.0:0': {
|
||||
title: 'Bitcoin Knots',
|
||||
description: {
|
||||
short: 'An alternate fully verifying implementation of Bitcoin',
|
||||
long: 'Bitcoin Knots is a combined Bitcoin node and wallet. Not only is it easy to use, but it also ensures bitcoins you receive are both real bitcoins and really yours.',
|
||||
},
|
||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
||||
license: 'mit',
|
||||
wrapperRepo: 'https://github.com/start9labs/bitcoinknots-startos',
|
||||
upstreamRepo: 'https://github.com/bitcoinknots/bitcoin',
|
||||
supportSite: 'https://bitcoinknots.org',
|
||||
marketingSite: 'https://bitcoinknots.org',
|
||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||
osVersion: '0.3.6',
|
||||
gitHash: 'fakehash',
|
||||
icon: BTC_ICON,
|
||||
sourceVersion: null,
|
||||
dependencyMetadata: {},
|
||||
donationUrl: null,
|
||||
alerts: {
|
||||
install: 'test',
|
||||
uninstall: 'test',
|
||||
start: 'test',
|
||||
stop: 'test',
|
||||
restore: 'test',
|
||||
},
|
||||
s9pk: {
|
||||
url: 'https://github.com/Start9Labs/bitcoinknots-startos/releases/download/v26.1.20240513/bitcoind.s9pk',
|
||||
commitment: mockMerkleArchiveCommitment,
|
||||
signatures: {},
|
||||
publishedAt: Date.now().toString(),
|
||||
},
|
||||
},
|
||||
},
|
||||
categories: ['bitcoin', 'featured'],
|
||||
otherVersions: {
|
||||
'26.1.0:0.1.0': {
|
||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||
},
|
||||
'#knots:26.1.20240325:0': {
|
||||
releaseNotes: 'Even better Knots support for Bitcoin and wallets!',
|
||||
},
|
||||
},
|
||||
},
|
||||
lnd: {
|
||||
best: {
|
||||
'0.18.0:0.0.1': {
|
||||
title: 'LND',
|
||||
description: mockDescription,
|
||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
||||
license: 'mit',
|
||||
wrapperRepo: 'https://github.com/start9labs/lnd-startos',
|
||||
upstreamRepo: 'https://github.com/lightningnetwork/lnd',
|
||||
supportSite: 'https://lightning.engineering/slack.html',
|
||||
marketingSite: 'https://lightning.engineering/',
|
||||
releaseNotes: 'Upstream release and minor fixes.',
|
||||
osVersion: '0.3.6',
|
||||
gitHash: 'fakehash',
|
||||
icon: LND_ICON,
|
||||
sourceVersion: null,
|
||||
dependencyMetadata: {
|
||||
bitcoind: {
|
||||
title: 'Bitcoin Core',
|
||||
icon: BTC_ICON,
|
||||
description: 'Used for RPC requests',
|
||||
optional: false,
|
||||
},
|
||||
'btc-rpc-proxy': {
|
||||
title: 'Bitcoin Proxy',
|
||||
icon: null,
|
||||
description: 'Used for authorized RPC requests',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
donationUrl: null,
|
||||
alerts: {
|
||||
install: 'test',
|
||||
uninstall: 'test',
|
||||
start: 'test',
|
||||
stop: 'test',
|
||||
restore: 'test',
|
||||
},
|
||||
s9pk: {
|
||||
url: 'https://github.com/Start9Labs/lnd-startos/releases/download/v0.18.0.1/lnd.s9pk',
|
||||
commitment: mockMerkleArchiveCommitment,
|
||||
signatures: {},
|
||||
publishedAt: Date.now().toString(),
|
||||
},
|
||||
},
|
||||
},
|
||||
categories: ['lightning', 'featured'],
|
||||
otherVersions: {
|
||||
'0.17.5:0': {
|
||||
releaseNotes: 'Upstream release to 0.17.5',
|
||||
},
|
||||
'0.17.4-beta:1.0-alpha': {
|
||||
releaseNotes: 'Upstream release to 0.17.4',
|
||||
},
|
||||
},
|
||||
},
|
||||
'btc-rpc-proxy': {
|
||||
best: {
|
||||
'0.3.2.7:0': {
|
||||
title: 'Bitcoin Proxy',
|
||||
description: mockDescription,
|
||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
||||
license: 'mit',
|
||||
wrapperRepo: 'https://github.com/Start9Labs/btc-rpc-proxy-wrappers',
|
||||
upstreamRepo: 'https://github.com/Kixunil/btc-rpc-proxy',
|
||||
supportSite: 'https://github.com/Kixunil/btc-rpc-proxy/issues',
|
||||
marketingSite: '',
|
||||
releaseNotes: 'Upstream release and minor fixes.',
|
||||
osVersion: '0.3.6',
|
||||
gitHash: 'fakehash',
|
||||
icon: PROXY_ICON,
|
||||
sourceVersion: null,
|
||||
dependencyMetadata: {},
|
||||
donationUrl: null,
|
||||
alerts: {
|
||||
install: 'test',
|
||||
uninstall: 'test',
|
||||
start: 'test',
|
||||
stop: 'test',
|
||||
restore: 'test',
|
||||
},
|
||||
s9pk: {
|
||||
url: 'https://github.com/Start9Labs/btc-rpc-proxy-startos/releases/download/v0.3.2.7/btc-rpc-proxy.s9pk',
|
||||
commitment: mockMerkleArchiveCommitment,
|
||||
signatures: {},
|
||||
publishedAt: Date.now().toString(),
|
||||
},
|
||||
},
|
||||
},
|
||||
categories: ['bitcoin'],
|
||||
otherVersions: {
|
||||
'0.3.2.6:0': {
|
||||
releaseNotes: 'Upstream release and minor fixes.',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const Notifications: ServerNotifications = [
|
||||
{
|
||||
@@ -658,13 +1063,13 @@ export module Mock {
|
||||
packageBackups: {
|
||||
bitcoind: {
|
||||
title: 'Bitcoin Core',
|
||||
version: '0.21.0',
|
||||
version: '0.21.0:0',
|
||||
osVersion: '0.3.6',
|
||||
timestamp: new Date().toISOString(),
|
||||
},
|
||||
'btc-rpc-proxy': {
|
||||
title: 'Bitcoin Proxy',
|
||||
version: '0.2.2',
|
||||
version: '0.2.2:0',
|
||||
osVersion: '0.3.6',
|
||||
timestamp: new Date().toISOString(),
|
||||
},
|
||||
@@ -1489,8 +1894,7 @@ export module Mock {
|
||||
title: Mock.MockManifestBitcoind.title,
|
||||
icon: 'assets/img/service-icons/bitcoind.svg',
|
||||
kind: 'running',
|
||||
registryUrl: '',
|
||||
versionSpec: '>=26.0.0',
|
||||
versionRange: '>=26.0.0',
|
||||
healthChecks: [],
|
||||
configSatisfied: true,
|
||||
},
|
||||
@@ -1576,8 +1980,7 @@ export module Mock {
|
||||
title: Mock.MockManifestBitcoind.title,
|
||||
icon: 'assets/img/service-icons/bitcoind.svg',
|
||||
kind: 'running',
|
||||
registryUrl: 'https://registry.start9.com',
|
||||
versionSpec: '>=26.0.0',
|
||||
versionRange: '>=26.0.0',
|
||||
healthChecks: [],
|
||||
configSatisfied: true,
|
||||
},
|
||||
@@ -1585,8 +1988,7 @@ export module Mock {
|
||||
title: Mock.MockManifestBitcoinProxy.title,
|
||||
icon: 'assets/img/service-icons/btc-rpc-proxy.png',
|
||||
kind: 'exists',
|
||||
registryUrl: 'https://community-registry.start9.com',
|
||||
versionSpec: '>2.0.0',
|
||||
versionRange: '>2.0.0',
|
||||
configSatisfied: false,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Dump } from 'patch-db-client'
|
||||
import { MarketplacePkg, StoreInfo } from '@start9labs/marketplace'
|
||||
import { PackagePropertiesVersioned } from 'src/app/util/properties.util'
|
||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
import { StartOSDiskInfo, LogsRes, ServerLogsReq } from '@start9labs/shared'
|
||||
@@ -223,12 +222,7 @@ export module RR {
|
||||
export type GetPackageMetricsReq = { id: string } // package.metrics
|
||||
export type GetPackageMetricsRes = Metric
|
||||
|
||||
export type InstallPackageReq = {
|
||||
id: string
|
||||
versionSpec?: string
|
||||
versionPriority?: 'min' | 'max'
|
||||
registry: string
|
||||
} // package.install
|
||||
export type InstallPackageReq = T.InstallParams
|
||||
export type InstallPackageRes = null
|
||||
|
||||
export type GetPackageConfigReq = { id: string } // package.config.get
|
||||
@@ -287,26 +281,13 @@ export module RR {
|
||||
progress: string // guid
|
||||
}
|
||||
|
||||
// marketplace
|
||||
// registry
|
||||
|
||||
export type GetMarketplaceInfoReq = { serverId: string }
|
||||
export type GetMarketplaceInfoRes = StoreInfo
|
||||
/** these are returned in ASCENDING order. the newest available version will be the LAST in the object */
|
||||
export type GetRegistryOsUpdateRes = { [version: string]: T.OsVersionInfo }
|
||||
|
||||
export type CheckOSUpdateReq = { serverId: string }
|
||||
export type CheckOSUpdateRes = OSUpdate
|
||||
|
||||
export type GetMarketplacePackagesReq = {
|
||||
ids?: { id: string; version: string }[]
|
||||
// iff !ids
|
||||
category?: string
|
||||
query?: string
|
||||
page?: number
|
||||
perPage?: number
|
||||
}
|
||||
export type GetMarketplacePackagesRes = MarketplacePkg[]
|
||||
|
||||
export type GetReleaseNotesReq = { id: string }
|
||||
export type GetReleaseNotesRes = { [version: string]: string }
|
||||
}
|
||||
|
||||
export interface OSUpdate {
|
||||
|
||||
@@ -1,17 +1,32 @@
|
||||
import { Observable } from 'rxjs'
|
||||
import { RR } from './api.types'
|
||||
import { RPCOptions } from '@start9labs/shared'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
import {
|
||||
GetPackageRes,
|
||||
GetPackagesRes,
|
||||
MarketplacePkg,
|
||||
} from '@start9labs/marketplace'
|
||||
|
||||
export abstract class ApiService {
|
||||
// http
|
||||
|
||||
// for getting static files: ex icons, instructions, licenses
|
||||
abstract getStatic(url: string): Promise<string>
|
||||
|
||||
// for sideloading packages
|
||||
abstract uploadPackage(guid: string, body: Blob): Promise<string>
|
||||
|
||||
abstract uploadFile(body: Blob): Promise<string>
|
||||
|
||||
// for getting static files: ex icons, instructions, licenses
|
||||
abstract getStaticProxy(
|
||||
pkg: MarketplacePkg,
|
||||
path: 'LICENSE.md' | 'instructions.md',
|
||||
): Promise<string>
|
||||
|
||||
abstract getStaticInstalled(
|
||||
id: T.PackageId,
|
||||
path: 'LICENSE.md' | 'instructions.md',
|
||||
): Promise<string>
|
||||
|
||||
// websocket
|
||||
|
||||
abstract openWebsocket$<T>(
|
||||
@@ -120,14 +135,23 @@ export abstract class ApiService {
|
||||
|
||||
// marketplace URLs
|
||||
|
||||
abstract marketplaceProxy<T>(
|
||||
path: string,
|
||||
params: Record<string, unknown>,
|
||||
url: string,
|
||||
abstract registryRequest<T>(
|
||||
registryUrl: string,
|
||||
options: RPCOptions,
|
||||
): Promise<T>
|
||||
|
||||
abstract checkOSUpdate(qp: RR.CheckOSUpdateReq): Promise<RR.CheckOSUpdateRes>
|
||||
|
||||
abstract getRegistryInfo(registryUrl: string): Promise<T.RegistryInfo>
|
||||
|
||||
abstract getRegistryPackage(
|
||||
url: string,
|
||||
id: string,
|
||||
versionRange: string | null,
|
||||
): Promise<GetPackageRes>
|
||||
|
||||
abstract getRegistryPackages(registryUrl: string): Promise<GetPackagesRes>
|
||||
|
||||
// notification
|
||||
|
||||
abstract getNotifications(
|
||||
|
||||
@@ -18,6 +18,15 @@ import { AuthService } from '../auth.service'
|
||||
import { DOCUMENT } from '@angular/common'
|
||||
import { DataModel } from '../patch-db/data-model'
|
||||
import { Dump, pathFromArray } from 'patch-db-client'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
import {
|
||||
GetPackageReq,
|
||||
GetPackageRes,
|
||||
GetPackagesReq,
|
||||
GetPackagesRes,
|
||||
MarketplacePkg,
|
||||
} from '@start9labs/marketplace'
|
||||
import { blake3 } from '@noble/hashes/blake3'
|
||||
|
||||
@Injectable()
|
||||
export class LiveApiService extends ApiService {
|
||||
@@ -29,17 +38,7 @@ export class LiveApiService extends ApiService {
|
||||
@Inject(PATCH_CACHE) private readonly cache$: Observable<Dump<DataModel>>,
|
||||
) {
|
||||
super()
|
||||
; (window as any).rpcClient = this
|
||||
}
|
||||
|
||||
// for getting static files: ex icons, instructions, licenses
|
||||
|
||||
async getStatic(url: string): Promise<string> {
|
||||
return this.httpRequest({
|
||||
method: Method.GET,
|
||||
url,
|
||||
responseType: 'text',
|
||||
})
|
||||
;(window as any).rpcClient = this
|
||||
}
|
||||
|
||||
// for sideloading packages
|
||||
@@ -62,6 +61,36 @@ export class LiveApiService extends ApiService {
|
||||
})
|
||||
}
|
||||
|
||||
// for getting static files: ex. instructions, licenses
|
||||
|
||||
async getStaticProxy(
|
||||
pkg: MarketplacePkg,
|
||||
path: 'LICENSE.md' | 'instructions.md',
|
||||
): Promise<string> {
|
||||
const encodedUrl = encodeURIComponent(pkg.s9pk.url)
|
||||
|
||||
return this.httpRequest({
|
||||
method: Method.GET,
|
||||
url: `/s9pk/proxy/${encodedUrl}/${path}`,
|
||||
params: {
|
||||
rootSighash: pkg.s9pk.commitment.rootSighash,
|
||||
rootMaxsize: pkg.s9pk.commitment.rootMaxsize,
|
||||
},
|
||||
responseType: 'text',
|
||||
})
|
||||
}
|
||||
|
||||
async getStaticInstalled(
|
||||
id: T.PackageId,
|
||||
path: 'LICENSE.md' | 'instructions.md',
|
||||
): Promise<string> {
|
||||
return this.httpRequest({
|
||||
method: Method.GET,
|
||||
url: `/s9pk/installed/${id}.s9pk/${path}`,
|
||||
responseType: 'text',
|
||||
})
|
||||
}
|
||||
|
||||
// websocket
|
||||
|
||||
openWebsocket$<T>(
|
||||
@@ -257,24 +286,63 @@ export class LiveApiService extends ApiService {
|
||||
|
||||
// marketplace URLs
|
||||
|
||||
async marketplaceProxy<T>(
|
||||
path: string,
|
||||
qp: Record<string, string>,
|
||||
baseUrl: string,
|
||||
async registryRequest<T>(
|
||||
registryUrl: string,
|
||||
options: RPCOptions,
|
||||
): Promise<T> {
|
||||
const fullUrl = `${baseUrl}${path}?${new URLSearchParams(qp).toString()}`
|
||||
return this.rpcRequest({
|
||||
method: 'marketplace.get',
|
||||
params: { url: fullUrl },
|
||||
...options,
|
||||
method: `registry.${options.method}`,
|
||||
params: { registry: registryUrl, ...options.params },
|
||||
})
|
||||
}
|
||||
|
||||
async checkOSUpdate(qp: RR.CheckOSUpdateReq): Promise<RR.CheckOSUpdateRes> {
|
||||
return this.marketplaceProxy(
|
||||
'/eos/v0/latest',
|
||||
qp,
|
||||
this.config.marketplace.start9,
|
||||
)
|
||||
const { serverId } = qp
|
||||
|
||||
return this.registryRequest(this.config.marketplace.start9, {
|
||||
method: 'os.version.get',
|
||||
params: { serverId },
|
||||
})
|
||||
}
|
||||
|
||||
async getRegistryInfo(registryUrl: string): Promise<T.RegistryInfo> {
|
||||
return this.registryRequest(registryUrl, {
|
||||
method: 'info',
|
||||
params: {},
|
||||
})
|
||||
}
|
||||
|
||||
async getRegistryPackage(
|
||||
registryUrl: string,
|
||||
id: string,
|
||||
versionRange: string | null,
|
||||
): Promise<GetPackageRes> {
|
||||
const params: GetPackageReq = {
|
||||
id,
|
||||
version: versionRange,
|
||||
sourceVersion: null,
|
||||
otherVersions: 'short',
|
||||
}
|
||||
|
||||
return this.registryRequest<GetPackageRes>(registryUrl, {
|
||||
method: 'package.get',
|
||||
params,
|
||||
})
|
||||
}
|
||||
|
||||
async getRegistryPackages(registryUrl: string): Promise<GetPackagesRes> {
|
||||
const params: GetPackagesReq = {
|
||||
id: null,
|
||||
version: null,
|
||||
sourceVersion: null,
|
||||
otherVersions: 'short',
|
||||
}
|
||||
|
||||
return this.registryRequest<GetPackagesRes>(registryUrl, {
|
||||
method: 'package.get',
|
||||
params,
|
||||
})
|
||||
}
|
||||
|
||||
// notification
|
||||
@@ -504,6 +572,29 @@ export class LiveApiService extends ApiService {
|
||||
|
||||
private async httpRequest<T>(opts: HttpOptions): Promise<T> {
|
||||
const res = await this.http.httpRequest<T>(opts)
|
||||
if (res.headers.get('Repr-Digest')) {
|
||||
// verify
|
||||
const digest = res.headers.get('Repr-Digest')!
|
||||
let data: Uint8Array
|
||||
if (opts.responseType === 'arrayBuffer') {
|
||||
data = Buffer.from(res.body as ArrayBuffer)
|
||||
} else if (opts.responseType === 'text') {
|
||||
data = Buffer.from(res.body as string)
|
||||
} else if ((opts.responseType as string) === 'blob') {
|
||||
data = Buffer.from(await (res.body as Blob).arrayBuffer())
|
||||
} else {
|
||||
console.warn(
|
||||
`could not verify Repr-Digest for responseType ${
|
||||
opts.responseType || 'json'
|
||||
}`,
|
||||
)
|
||||
return res.body
|
||||
}
|
||||
const computedDigest = Buffer.from(blake3(data)).toString('base64')
|
||||
if (`blake3=:${computedDigest}:` === digest) return res.body
|
||||
console.debug(computedDigest, digest)
|
||||
throw new Error('File digest mismatch.')
|
||||
}
|
||||
return res.body
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { Log, RPCErrorDetails, pauseFor } from '@start9labs/shared'
|
||||
import { Log, RPCErrorDetails, RPCOptions, pauseFor } from '@start9labs/shared'
|
||||
import { ApiService } from './embassy-api.service'
|
||||
import {
|
||||
Operation,
|
||||
@@ -30,8 +30,12 @@ import {
|
||||
} from 'rxjs'
|
||||
import { mockPatchData } from './mock-patch'
|
||||
import { AuthService } from '../auth.service'
|
||||
import { StoreInfo } from '@start9labs/marketplace'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
import {
|
||||
GetPackageRes,
|
||||
GetPackagesRes,
|
||||
MarketplacePkg,
|
||||
} from '@start9labs/marketplace'
|
||||
|
||||
const PROGRESS: T.FullProgress = {
|
||||
overall: {
|
||||
@@ -48,10 +52,7 @@ const PROGRESS: T.FullProgress = {
|
||||
},
|
||||
{
|
||||
name: 'Validating',
|
||||
progress: {
|
||||
done: 0,
|
||||
total: 40,
|
||||
},
|
||||
progress: null,
|
||||
},
|
||||
{
|
||||
name: 'Installing',
|
||||
@@ -80,14 +81,25 @@ export class MockApiService extends ApiService {
|
||||
.subscribe()
|
||||
}
|
||||
|
||||
async getStatic(url: string): Promise<string> {
|
||||
async uploadPackage(guid: string, body: Blob): Promise<string> {
|
||||
await pauseFor(2000)
|
||||
return 'success'
|
||||
}
|
||||
|
||||
async getStaticProxy(
|
||||
pkg: MarketplacePkg,
|
||||
path: 'LICENSE.md' | 'instructions.md',
|
||||
): Promise<string> {
|
||||
await pauseFor(2000)
|
||||
return markdown
|
||||
}
|
||||
|
||||
async uploadPackage(guid: string, body: Blob): Promise<string> {
|
||||
async getStaticInstalled(
|
||||
id: T.PackageId,
|
||||
path: 'LICENSE.md' | 'instructions.md',
|
||||
): Promise<string> {
|
||||
await pauseFor(2000)
|
||||
return 'success'
|
||||
return markdown
|
||||
}
|
||||
|
||||
// websocket
|
||||
@@ -136,7 +148,7 @@ export class MockApiService extends ApiService {
|
||||
|
||||
this.stateIndex++
|
||||
|
||||
return this.stateIndex === 1 ? 'initializing' : 'running'
|
||||
return this.stateIndex === 1 ? 'running' : 'running'
|
||||
}
|
||||
|
||||
// db
|
||||
@@ -448,34 +460,13 @@ export class MockApiService extends ApiService {
|
||||
|
||||
// marketplace URLs
|
||||
|
||||
async marketplaceProxy(
|
||||
path: string,
|
||||
params: Record<string, string>,
|
||||
url: string,
|
||||
async registryRequest(
|
||||
registryUrl: string,
|
||||
options: RPCOptions,
|
||||
): Promise<any> {
|
||||
await pauseFor(2000)
|
||||
|
||||
if (path === '/package/v0/info') {
|
||||
const info: StoreInfo = {
|
||||
name: 'Start9 Registry',
|
||||
categories: [
|
||||
'bitcoin',
|
||||
'lightning',
|
||||
'data',
|
||||
'featured',
|
||||
'messaging',
|
||||
'social',
|
||||
'alt coin',
|
||||
],
|
||||
}
|
||||
return info
|
||||
} else if (path === '/package/v0/index') {
|
||||
return Mock.MarketplacePkgsList
|
||||
} else if (path.startsWith('/package/v0/release-notes')) {
|
||||
return Mock.ReleaseNotes
|
||||
} else if (path.includes('instructions') || path.includes('license')) {
|
||||
return markdown
|
||||
}
|
||||
return Error('do not call directly')
|
||||
}
|
||||
|
||||
async checkOSUpdate(qp: RR.CheckOSUpdateReq): Promise<RR.CheckOSUpdateRes> {
|
||||
@@ -483,6 +474,29 @@ export class MockApiService extends ApiService {
|
||||
return Mock.MarketplaceEos
|
||||
}
|
||||
|
||||
async getRegistryInfo(registryUrl: string): Promise<T.RegistryInfo> {
|
||||
await pauseFor(2000)
|
||||
return Mock.RegistryInfo
|
||||
}
|
||||
|
||||
async getRegistryPackage(
|
||||
url: string,
|
||||
id: string,
|
||||
versionRange: string,
|
||||
): Promise<GetPackageRes> {
|
||||
await pauseFor(2000)
|
||||
if (!versionRange) {
|
||||
return Mock.RegistryPackages[id]
|
||||
} else {
|
||||
return Mock.OtherPackageVersions[id][versionRange]
|
||||
}
|
||||
}
|
||||
|
||||
async getRegistryPackages(registryUrl: string): Promise<GetPackagesRes> {
|
||||
await pauseFor(2000)
|
||||
return Mock.RegistryPackages
|
||||
}
|
||||
|
||||
// notification
|
||||
|
||||
async getNotifications(
|
||||
@@ -742,11 +756,11 @@ export class MockApiService extends ApiService {
|
||||
...Mock.LocalPkgs[params.id],
|
||||
stateInfo: {
|
||||
// if installing
|
||||
// state: PackageState.Installing,
|
||||
state: 'installing',
|
||||
|
||||
// if updating
|
||||
state: 'updating',
|
||||
manifest: mockPatchData.packageData[params.id].stateInfo.manifest!,
|
||||
// state: 'updating',
|
||||
// manifest: mockPatchData.packageData[params.id].stateInfo.manifest!,
|
||||
|
||||
// both
|
||||
installingInfo: {
|
||||
@@ -1129,11 +1143,7 @@ export class MockApiService extends ApiService {
|
||||
const progress = JSON.parse(JSON.stringify(PROGRESS))
|
||||
|
||||
for (let [i, phase] of progress.phases.entries()) {
|
||||
if (
|
||||
!phase.progress ||
|
||||
typeof phase.progress !== 'object' ||
|
||||
!phase.progress.total
|
||||
) {
|
||||
if (!phase.progress || phase.progress === true || !phase.progress.total) {
|
||||
await pauseFor(2000)
|
||||
|
||||
const patches: Operation<any>[] = [
|
||||
|
||||
@@ -88,7 +88,7 @@ export const mockPatchData: DataModel = {
|
||||
state: 'installed',
|
||||
manifest: {
|
||||
...Mock.MockManifestBitcoind,
|
||||
version: '0.20.0',
|
||||
version: '0.20.0:0',
|
||||
},
|
||||
},
|
||||
icon: '/assets/img/service-icons/bitcoind.svg',
|
||||
@@ -295,7 +295,7 @@ export const mockPatchData: DataModel = {
|
||||
state: 'installed',
|
||||
manifest: {
|
||||
...Mock.MockManifestLnd,
|
||||
version: '0.11.0',
|
||||
version: '0.11.0:0.0.1',
|
||||
},
|
||||
},
|
||||
icon: '/assets/img/service-icons/lnd.png',
|
||||
@@ -368,8 +368,7 @@ export const mockPatchData: DataModel = {
|
||||
title: 'Bitcoin Core',
|
||||
icon: 'assets/img/service-icons/bitcoind.svg',
|
||||
kind: 'running',
|
||||
registryUrl: 'https://registry.start9.com',
|
||||
versionSpec: '>=26.0.0',
|
||||
versionRange: '>=26.0.0',
|
||||
healthChecks: [],
|
||||
configSatisfied: true,
|
||||
},
|
||||
@@ -377,8 +376,7 @@ export const mockPatchData: DataModel = {
|
||||
title: 'Bitcoin Proxy',
|
||||
icon: 'assets/img/service-icons/btc-rpc-proxy.png',
|
||||
kind: 'running',
|
||||
registryUrl: 'https://community-registry.start9.com',
|
||||
versionSpec: '>2.0.0',
|
||||
versionRange: '>2.0.0',
|
||||
healthChecks: [],
|
||||
configSatisfied: false,
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { Emver } from '@start9labs/shared'
|
||||
import { Exver } from '@start9labs/shared'
|
||||
import { distinctUntilChanged, map, shareReplay } from 'rxjs/operators'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import {
|
||||
@@ -39,7 +39,7 @@ export class DepErrorService {
|
||||
)
|
||||
|
||||
constructor(
|
||||
private readonly emver: Emver,
|
||||
private readonly exver: Exver,
|
||||
private readonly patch: PatchDB<DataModel>,
|
||||
) {}
|
||||
|
||||
@@ -87,11 +87,17 @@ export class DepErrorService {
|
||||
const depManifest = dep.stateInfo.manifest
|
||||
|
||||
// incorrect version
|
||||
if (!this.emver.satisfies(depManifest.version, currentDep.versionSpec)) {
|
||||
return {
|
||||
type: 'incorrectVersion',
|
||||
expected: currentDep.versionSpec,
|
||||
received: depManifest.version,
|
||||
if (!this.exver.satisfies(depManifest.version, currentDep.versionRange)) {
|
||||
if (
|
||||
depManifest.satisfies.some(
|
||||
v => !this.exver.satisfies(v, currentDep.versionRange),
|
||||
)
|
||||
) {
|
||||
return {
|
||||
type: 'incorrectVersion',
|
||||
expected: currentDep.versionRange,
|
||||
received: depManifest.version,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { Emver } from '@start9labs/shared'
|
||||
import { BehaviorSubject, combineLatest } from 'rxjs'
|
||||
import { distinctUntilChanged, map } from 'rxjs/operators'
|
||||
import { OSUpdate } from 'src/app/services/api/api.types'
|
||||
@@ -7,6 +6,7 @@ import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { getServerInfo } from 'src/app/util/get-server-info'
|
||||
import { DataModel } from './patch-db/data-model'
|
||||
import { Exver } from '@start9labs/shared'
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@@ -47,15 +47,15 @@ export class EOSService {
|
||||
|
||||
constructor(
|
||||
private readonly api: ApiService,
|
||||
private readonly emver: Emver,
|
||||
private readonly patch: PatchDB<DataModel>,
|
||||
private readonly exver: Exver,
|
||||
) {}
|
||||
|
||||
async loadEos(): Promise<void> {
|
||||
const { version, id } = await getServerInfo(this.patch)
|
||||
this.osUpdate = await this.api.checkOSUpdate({ serverId: id })
|
||||
const updateAvailable =
|
||||
this.emver.compare(this.osUpdate.version, version) === 1
|
||||
this.exver.compareOsVersion(this.osUpdate.version, version) === 'greater'
|
||||
this.updateAvailable$.next(updateAvailable)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { Inject, Injectable } from '@angular/core'
|
||||
import {
|
||||
MarketplacePkg,
|
||||
AbstractMarketplaceService,
|
||||
StoreData,
|
||||
Marketplace,
|
||||
StoreInfo,
|
||||
StoreIdentity,
|
||||
MarketplacePkg,
|
||||
GetPackageRes,
|
||||
} from '@start9labs/marketplace'
|
||||
import {
|
||||
BehaviorSubject,
|
||||
@@ -37,8 +37,9 @@ import {
|
||||
tap,
|
||||
} from 'rxjs/operators'
|
||||
import { ConfigService } from './config.service'
|
||||
import { sameUrl } from '@start9labs/shared'
|
||||
import { Exver, sameUrl } from '@start9labs/shared'
|
||||
import { ClientStorageService } from './client-storage.service'
|
||||
import { ExtendedVersion, T } from '@start9labs/start-sdk'
|
||||
|
||||
@Injectable()
|
||||
export class MarketplaceService implements AbstractMarketplaceService {
|
||||
@@ -93,11 +94,9 @@ export class MarketplaceService implements AbstractMarketplaceService {
|
||||
mergeMap(({ url, name }) =>
|
||||
this.fetchStore$(url).pipe(
|
||||
tap(data => {
|
||||
if (data?.info) this.updateStoreName(url, name, data.info.name)
|
||||
}),
|
||||
map<StoreData | null, [string, StoreData | null]>(data => {
|
||||
return [url, data]
|
||||
if (data?.info.name) this.updateStoreName(url, name, data.info.name)
|
||||
}),
|
||||
map<StoreData | null, [string, StoreData | null]>(data => [url, data]),
|
||||
startWith<[string, StoreData | null]>([url, null]),
|
||||
),
|
||||
),
|
||||
@@ -148,6 +147,7 @@ export class MarketplaceService implements AbstractMarketplaceService {
|
||||
private readonly patch: PatchDB<DataModel>,
|
||||
private readonly config: ConfigService,
|
||||
private readonly clientStorageService: ClientStorageService,
|
||||
private readonly exver: Exver,
|
||||
) {}
|
||||
|
||||
getKnownHosts$(filtered = false): Observable<StoreIdentity[]> {
|
||||
@@ -170,28 +170,29 @@ export class MarketplaceService implements AbstractMarketplaceService {
|
||||
|
||||
getPackage$(
|
||||
id: string,
|
||||
version: string,
|
||||
optionalUrl?: string,
|
||||
version: string | null,
|
||||
flavor: string | null,
|
||||
registryUrl?: string,
|
||||
): Observable<MarketplacePkg> {
|
||||
return this.patch.watch$('ui', 'marketplace').pipe(
|
||||
switchMap(uiMarketplace => {
|
||||
const url = optionalUrl || uiMarketplace.selectedUrl
|
||||
return this.selectedHost$.pipe(
|
||||
switchMap(selected =>
|
||||
this.marketplace$.pipe(
|
||||
switchMap(m => {
|
||||
const url = registryUrl || selected.url
|
||||
|
||||
if (version !== '*' || !uiMarketplace.knownHosts[url]) {
|
||||
return this.fetchPackage$(id, version, url)
|
||||
}
|
||||
const pkg = m[url]?.packages.find(
|
||||
p =>
|
||||
p.id === id &&
|
||||
p.flavor === flavor &&
|
||||
(!version || this.exver.compareExver(p.version, version) === 0),
|
||||
)
|
||||
|
||||
return this.marketplace$.pipe(
|
||||
map(m => m[url]),
|
||||
filter(Boolean),
|
||||
take(1),
|
||||
map(
|
||||
store =>
|
||||
store.packages.find(p => p.manifest.id === id) ||
|
||||
({} as MarketplacePkg),
|
||||
),
|
||||
)
|
||||
}),
|
||||
return !!pkg
|
||||
? of(pkg)
|
||||
: this.fetchPackage$(url, id, version, flavor)
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -210,56 +211,22 @@ export class MarketplaceService implements AbstractMarketplaceService {
|
||||
): Promise<void> {
|
||||
const params: RR.InstallPackageReq = {
|
||||
id,
|
||||
versionSpec: `=${version}`,
|
||||
version,
|
||||
registry: url,
|
||||
}
|
||||
|
||||
await this.api.installPackage(params)
|
||||
}
|
||||
|
||||
fetchInfo$(url: string): Observable<StoreInfo> {
|
||||
return this.patch.watch$('serverInfo').pipe(
|
||||
take(1),
|
||||
switchMap(serverInfo => {
|
||||
const qp: RR.GetMarketplaceInfoReq = { serverId: serverInfo.id }
|
||||
return this.api.marketplaceProxy<RR.GetMarketplaceInfoRes>(
|
||||
'/package/v0/info',
|
||||
qp,
|
||||
url,
|
||||
)
|
||||
}),
|
||||
)
|
||||
fetchInfo$(url: string): Observable<T.RegistryInfo> {
|
||||
return from(this.api.getRegistryInfo(url))
|
||||
}
|
||||
|
||||
fetchReleaseNotes$(
|
||||
id: string,
|
||||
url?: string,
|
||||
): Observable<Record<string, string>> {
|
||||
return this.selectedHost$.pipe(
|
||||
switchMap(m => {
|
||||
return from(
|
||||
this.api.marketplaceProxy<Record<string, string>>(
|
||||
`/package/v0/release-notes/${id}`,
|
||||
{},
|
||||
url || m.url,
|
||||
),
|
||||
)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
fetchStatic$(id: string, type: string, url?: string): Observable<string> {
|
||||
return this.selectedHost$.pipe(
|
||||
switchMap(m => {
|
||||
return from(
|
||||
this.api.marketplaceProxy<string>(
|
||||
`/package/v0/${type}/${id}`,
|
||||
{},
|
||||
url || m.url,
|
||||
),
|
||||
)
|
||||
}),
|
||||
)
|
||||
fetchStatic$(
|
||||
pkg: MarketplacePkg,
|
||||
type: 'LICENSE.md' | 'instructions.md',
|
||||
): Observable<string> {
|
||||
return from(this.api.getStaticProxy(pkg, type))
|
||||
}
|
||||
|
||||
private fetchStore$(url: string): Observable<StoreData | null> {
|
||||
@@ -273,33 +240,57 @@ export class MarketplaceService implements AbstractMarketplaceService {
|
||||
)
|
||||
}
|
||||
|
||||
private fetchPackages$(
|
||||
url: string,
|
||||
params: Omit<RR.GetMarketplacePackagesReq, 'page' | 'per-page'> = {},
|
||||
): Observable<MarketplacePkg[]> {
|
||||
const qp: RR.GetMarketplacePackagesReq = {
|
||||
...params,
|
||||
page: 1,
|
||||
perPage: 100,
|
||||
}
|
||||
if (qp.ids) qp.ids = JSON.stringify(qp.ids)
|
||||
|
||||
return from(
|
||||
this.api.marketplaceProxy<RR.GetMarketplacePackagesRes>(
|
||||
'/package/v0/index',
|
||||
qp,
|
||||
url,
|
||||
),
|
||||
private fetchPackages$(url: string): Observable<MarketplacePkg[]> {
|
||||
return from(this.api.getRegistryPackages(url)).pipe(
|
||||
map(packages => {
|
||||
return Object.entries(packages).flatMap(([id, pkgInfo]) =>
|
||||
Object.keys(pkgInfo.best).map(version =>
|
||||
this.convertToMarketplacePkg(
|
||||
id,
|
||||
version,
|
||||
this.exver.getFlavor(version),
|
||||
pkgInfo,
|
||||
),
|
||||
),
|
||||
)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
private fetchPackage$(
|
||||
convertToMarketplacePkg(
|
||||
id: string,
|
||||
version: string,
|
||||
version: string | null,
|
||||
flavor: string | null,
|
||||
pkgInfo: GetPackageRes,
|
||||
): MarketplacePkg {
|
||||
version =
|
||||
version ||
|
||||
Object.keys(pkgInfo.best).find(v => this.exver.getFlavor(v) === flavor) ||
|
||||
null
|
||||
|
||||
return !version || !pkgInfo.best[version]
|
||||
? ({} as MarketplacePkg)
|
||||
: {
|
||||
id,
|
||||
version,
|
||||
flavor,
|
||||
...pkgInfo,
|
||||
...pkgInfo.best[version],
|
||||
}
|
||||
}
|
||||
|
||||
private fetchPackage$(
|
||||
url: string,
|
||||
id: string,
|
||||
version: string | null,
|
||||
flavor: string | null,
|
||||
): Observable<MarketplacePkg> {
|
||||
return this.fetchPackages$(url, { ids: [{ id, version }] }).pipe(
|
||||
map(pkgs => pkgs[0] || {}),
|
||||
return from(
|
||||
this.api.getRegistryPackage(url, id, version ? `=${version}` : null),
|
||||
).pipe(
|
||||
map(pkgInfo =>
|
||||
this.convertToMarketplacePkg(id, version, flavor, pkgInfo),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,10 @@ export type PackageDataEntry<T extends StateInfo = StateInfo> =
|
||||
stateInfo: T
|
||||
}
|
||||
|
||||
export type AllPackageData = NonNullable<
|
||||
T.AllPackageData & Record<string, PackageDataEntry<StateInfo>>
|
||||
>
|
||||
|
||||
export type StateInfo = InstalledState | InstallingState | UpdatingState
|
||||
|
||||
export type InstalledState = {
|
||||
|
||||
Reference in New Issue
Block a user