mirror of
https://github.com/Start9Labs/registry.git
synced 2026-03-26 02:11:53 +00:00
Merge pull request #75 from Start9Labs/bugfix/package-index-version
Bugfix/package index version
This commit is contained in:
14
README.md
14
README.md
@@ -3,10 +3,10 @@
|
||||
After installing Postgres, run:
|
||||
|
||||
```
|
||||
createuser start9-companion-server --pwprompt --superuser
|
||||
# Enter password start9-companion-server when prompted
|
||||
createdb start9-companion-server
|
||||
createdb start9-companion-server_test
|
||||
createuser start9-registry --pwprompt --superuser
|
||||
# Enter password start9-registry when prompted
|
||||
createdb start9-registry
|
||||
createdb start9-registry_test
|
||||
```
|
||||
|
||||
## Haskell Setup
|
||||
@@ -44,11 +44,15 @@ To create `hie.yaml` if it does not exist:
|
||||
## Tests
|
||||
|
||||
```
|
||||
stack test --flag start9-companion-server:library-only --flag start9-companion-server:dev
|
||||
stack test --flag start9-registry:library-only --flag start9-registry:dev
|
||||
```
|
||||
|
||||
(Because `yesod devel` passes the `library-only` and `dev` flags, matching those flags means you don't need to recompile between tests and development, and it disables optimization to speed up your test compile times).
|
||||
|
||||
## Builds
|
||||
|
||||
`make`
|
||||
|
||||
### Tests with HIE Setup
|
||||
- install hspec-discover globally `cabal install hspec-discover` (requires cabal installation)
|
||||
- Current [issue](https://github.com/haskell/haskell-ide-engine/issues/1564) open for error pertaining to obtaining flags for test files
|
||||
|
||||
1
resources/apps/bitcoind/0.21.1.1/hash.bin
Normal file
1
resources/apps/bitcoind/0.21.1.1/hash.bin
Normal file
@@ -0,0 +1 @@
|
||||
JAPEX7ZLZIBZUO6YLUQAAGYACA4E5IFBLJPZR3OVQNQOAPNYVHVIEVXRS5RZ65EDCAAQE65PIVOZ46Z6CR4KIX3MWL62YFU5IXBHD4I
|
||||
BIN
resources/apps/bitcoind/0.21.1.1/icon.png
Normal file
BIN
resources/apps/bitcoind/0.21.1.1/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
23
resources/apps/bitcoind/0.21.1.1/instructions.md
Normal file
23
resources/apps/bitcoind/0.21.1.1/instructions.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Bitcoin Core
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Config
|
||||
|
||||
Your node is highly configurable. Many settings are considered _advanced_ and should be used with caution. For the vast majority of users and use-cases, we recommend using the defaults. This is where you can change RPC credentials as well. Once configured, you may start your node!
|
||||
|
||||
### Syncing
|
||||
|
||||
Depending on your Internet bandwidth, your node should take approximately 5-7 days to sync from genesis to present.
|
||||
|
||||
### Using a Wallet
|
||||
|
||||
Enter your QuickConnect QR code **OR** your raw RPC credentials (both located in `Properties`) into any wallet that supports connecting to a remote node over Tor. For a full list of compatible wallets, see <a href="https://github.com/start9labs/bitcoind-wrapper/blob/master/docs/wallets.md" target="_blank">https://github.com/start9labs/bitcoind-wrapper/blob/master/docs/wallets.md</a>.
|
||||
|
||||
## Pruning
|
||||
|
||||
Pruning is a process by which your node discards old blocks and transactions after it verifies them. Pruned nodes and archival nodes are both "full nodes" in that they are fully validating - they validate every block and transaction. Archival nodes store the entire blockchain and are useful to people interested in doing general or historical analysis, or being a provider of blockchain data to others (eg. a blockexplorer).
|
||||
|
||||
The target of pruning on your Embassy is configurable and set by default to the minimum of 550MB (0.55 GB!), meaning the resulting blockchain will occupy a negligible amount of storage space. The maximum amount of blockchain data you can retain depends on the storage capacity your device. The config menu will not permit you to select a target that exceeds a certain percentage of your device's available capacity.
|
||||
|
||||
For most use cases, we recommend sticking with a very low pruning setting.
|
||||
22
resources/apps/bitcoind/0.21.1.1/license.md
Normal file
22
resources/apps/bitcoind/0.21.1.1/license.md
Normal file
@@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2009-2020 The Bitcoin Core developers
|
||||
Copyright (c) 2009-2020 Bitcoin Developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
272
resources/apps/bitcoind/0.21.1.1/manifest.json
Normal file
272
resources/apps/bitcoind/0.21.1.1/manifest.json
Normal file
@@ -0,0 +1,272 @@
|
||||
{
|
||||
"id": "bitcoind",
|
||||
"title": "Bitcoin Core",
|
||||
"version": "0.21.1.1",
|
||||
"description": {
|
||||
"short": "A Bitcoin Full Node by Bitcoin Core",
|
||||
"long": "Bitcoin is an innovative payment network and a new kind of money. Bitcoin uses peer-to-peer technology to operate with no central authority or banks; managing transactions and the issuing of bitcoins is carried out collectively by the network. Bitcoin is open-source; its design is public, nobody owns or controls Bitcoin and everyone can take part. Through many of its unique properties, Bitcoin allows exciting uses that could not be covered by any previous payment system."
|
||||
},
|
||||
"assets": {
|
||||
"license": "LICENSE",
|
||||
"instructions": "instructions.md",
|
||||
"icon": "icon.png",
|
||||
"docker-images": "image.tar",
|
||||
"assets": null
|
||||
},
|
||||
"build": [
|
||||
"make"
|
||||
],
|
||||
"release-notes": "Upgrade to EmbassyOS v0.3.0",
|
||||
"license": "mit",
|
||||
"wrapper-repo": "https://github.com/Start9Labs/bitcoind-wrapper",
|
||||
"upstream-repo": "https://github.com/bitcoin/bitcoin",
|
||||
"support-site": "https://github.com/bitcoin/bitcoin/issues",
|
||||
"marketing-site": "https://bitcoincore.org/",
|
||||
"donation-url": null,
|
||||
"alerts": {
|
||||
"install": null,
|
||||
"uninstall": "Uninstalling Bitcoin Core will result in permanent loss of data. Without a backup, any funds stored on your node's default hot wallet will be lost forever. If you are unsure, we recommend making a backup, just to be safe.",
|
||||
"restore": "Restoring Bitcoin Core will overwrite its current data. You will lose any transactions recorded in watch-only wallets, and any funds you have received to the hot wallet, since the last backup.",
|
||||
"start": null,
|
||||
"stop": null
|
||||
},
|
||||
"main": {
|
||||
"type": "docker",
|
||||
"image": "main",
|
||||
"system": false,
|
||||
"entrypoint": "docker_entrypoint.sh",
|
||||
"args": [],
|
||||
"mounts": {
|
||||
"compat": "/mnt/assets",
|
||||
"main": "/root/.bitcoin"
|
||||
},
|
||||
"io-format": "yaml",
|
||||
"inject": false,
|
||||
"shm-size-mb": null
|
||||
},
|
||||
"health-checks": {
|
||||
"rpc": {
|
||||
"type": "docker",
|
||||
"image": "main",
|
||||
"system": false,
|
||||
"entrypoint": "bitcoin-cli",
|
||||
"args": [
|
||||
"-rpcconnect=bitcoind.embassy",
|
||||
"-getinfo"
|
||||
],
|
||||
"mounts": {},
|
||||
"io-format": "yaml",
|
||||
"inject": true,
|
||||
"shm-size-mb": null,
|
||||
"critical": false
|
||||
},
|
||||
"synced": {
|
||||
"type": "docker",
|
||||
"image": "utils",
|
||||
"system": true,
|
||||
"entrypoint": "/mnt/assets/check-synced.sh",
|
||||
"args": [],
|
||||
"mounts": {
|
||||
"main": "/root/.bitcoin",
|
||||
"utils": "/mnt/assets"
|
||||
},
|
||||
"io-format": "yaml",
|
||||
"inject": false,
|
||||
"shm-size-mb": null,
|
||||
"critical": false
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"get": {
|
||||
"type": "docker",
|
||||
"image": "compat",
|
||||
"system": true,
|
||||
"entrypoint": "compat",
|
||||
"args": [
|
||||
"config",
|
||||
"get",
|
||||
"/root/.bitcoin",
|
||||
"/mnt/assets/config_spec.yaml"
|
||||
],
|
||||
"mounts": {
|
||||
"compat": "/mnt/assets",
|
||||
"main": "/root/.bitcoin"
|
||||
},
|
||||
"io-format": "yaml",
|
||||
"inject": false,
|
||||
"shm-size-mb": null
|
||||
},
|
||||
"set": {
|
||||
"type": "docker",
|
||||
"image": "compat",
|
||||
"system": true,
|
||||
"entrypoint": "compat",
|
||||
"args": [
|
||||
"config",
|
||||
"set",
|
||||
"bitcoind",
|
||||
"/root/.bitcoin",
|
||||
"/mnt/assets/config_rules.yaml"
|
||||
],
|
||||
"mounts": {
|
||||
"compat": "/mnt/assets",
|
||||
"main": "/root/.bitcoin"
|
||||
},
|
||||
"io-format": "yaml",
|
||||
"inject": false,
|
||||
"shm-size-mb": null
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"type": "docker",
|
||||
"image": "compat",
|
||||
"system": true,
|
||||
"entrypoint": "compat",
|
||||
"args": [
|
||||
"properties",
|
||||
"/root/.bitcoin"
|
||||
],
|
||||
"mounts": {
|
||||
"main": "/root/.bitcoin"
|
||||
},
|
||||
"io-format": "yaml",
|
||||
"inject": false,
|
||||
"shm-size-mb": null
|
||||
},
|
||||
"volumes": {
|
||||
"compat": {
|
||||
"type": "assets"
|
||||
},
|
||||
"main": {
|
||||
"type": "data"
|
||||
},
|
||||
"utils": {
|
||||
"type": "assets"
|
||||
}
|
||||
},
|
||||
"min-os-version": "0.3.0",
|
||||
"interfaces": {
|
||||
"peer": {
|
||||
"name": "Peer Interface",
|
||||
"description": "Listens for incoming connections from peers on the bitcoin network",
|
||||
"tor-config": {
|
||||
"port-mapping": {
|
||||
"8333": "8333"
|
||||
}
|
||||
},
|
||||
"lan-config": null,
|
||||
"ui": false,
|
||||
"protocols": [
|
||||
"tcp",
|
||||
"bitcoin"
|
||||
]
|
||||
},
|
||||
"rpc": {
|
||||
"name": "RPC Interface",
|
||||
"description": "Listens for JSON-RPC commands",
|
||||
"tor-config": {
|
||||
"port-mapping": {
|
||||
"8332": "8332"
|
||||
}
|
||||
},
|
||||
"lan-config": {
|
||||
"8332": {
|
||||
"ssl": false,
|
||||
"mapping": 8332
|
||||
}
|
||||
},
|
||||
"ui": false,
|
||||
"protocols": [
|
||||
"tcp",
|
||||
"http",
|
||||
"json-rpc"
|
||||
]
|
||||
},
|
||||
"zmq": {
|
||||
"name": "ZeroMQ Interface",
|
||||
"description": "Listens for subscriptions to the ZeroMQ raw block and raw transaction event streams",
|
||||
"tor-config": {
|
||||
"port-mapping": {
|
||||
"28332": "28332",
|
||||
"28333": "28333"
|
||||
}
|
||||
},
|
||||
"lan-config": null,
|
||||
"ui": false,
|
||||
"protocols": [
|
||||
"tcp",
|
||||
"zmq"
|
||||
]
|
||||
}
|
||||
},
|
||||
"backup": {
|
||||
"create": {
|
||||
"type": "docker",
|
||||
"image": "compat",
|
||||
"system": true,
|
||||
"entrypoint": "compat",
|
||||
"args": [
|
||||
"duplicity",
|
||||
"create",
|
||||
"/mnt/backup",
|
||||
"/root/.bitcoin"
|
||||
],
|
||||
"mounts": {
|
||||
"BACKUP": "/mnt/backup",
|
||||
"main": "/root/.bitcoin"
|
||||
},
|
||||
"io-format": null,
|
||||
"inject": false,
|
||||
"shm-size-mb": null
|
||||
},
|
||||
"restore": {
|
||||
"type": "docker",
|
||||
"image": "compat",
|
||||
"system": true,
|
||||
"entrypoint": "compat",
|
||||
"args": [
|
||||
"duplicity",
|
||||
"restore",
|
||||
"/mnt/backup",
|
||||
"/root/.bitcoin"
|
||||
],
|
||||
"mounts": {
|
||||
"BACKUP": "/mnt/backup",
|
||||
"main": "/root/.bitcoin"
|
||||
},
|
||||
"io-format": null,
|
||||
"inject": false,
|
||||
"shm-size-mb": null
|
||||
}
|
||||
},
|
||||
"migrations": {
|
||||
"from": {},
|
||||
"to": {}
|
||||
},
|
||||
"actions": {
|
||||
"reindex": {
|
||||
"name": "Reindex Blockchain",
|
||||
"description": "Rebuilds the block and chainstate databases starting from genesis. If blocks already exist on disk, these are used rather than being redownloaded. However, since embassy bitcoin nodes are pruned by default, this usually means downloading the entire blockchain over again.",
|
||||
"warning": "Blocks not stored on disk will be redownloaded in order to rebuild the database. If your node is pruned (embasssy nodes are pruned by default), this action is equivalent to syncing the node from scratch, so this process could take a couple of weeks.",
|
||||
"implementation": {
|
||||
"type": "docker",
|
||||
"image": "main",
|
||||
"system": false,
|
||||
"entrypoint": "reindex.sh",
|
||||
"args": [],
|
||||
"mounts": {
|
||||
"main": "/root/.bitcoin"
|
||||
},
|
||||
"io-format": "json",
|
||||
"inject": false,
|
||||
"shm-size-mb": null
|
||||
},
|
||||
"allowed-statuses": [
|
||||
"running",
|
||||
"stopped"
|
||||
],
|
||||
"input-spec": {}
|
||||
}
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
1
resources/apps/bitcoind/0.21.1.2/hash.bin
Normal file
1
resources/apps/bitcoind/0.21.1.2/hash.bin
Normal file
@@ -0,0 +1 @@
|
||||
JAPEX7ZLZIBZUO6YLUQAAGYACA4E5IFBLJPZR3OVQNQOAPNYVHVIEVXRS5RZ65EDCAAQE65PIVOZ46Z6CR4KIX3MWL62YFU5IXBHD4I
|
||||
BIN
resources/apps/bitcoind/0.21.1.2/icon.png
Normal file
BIN
resources/apps/bitcoind/0.21.1.2/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
23
resources/apps/bitcoind/0.21.1.2/instructions.md
Normal file
23
resources/apps/bitcoind/0.21.1.2/instructions.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Bitcoin Core
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Config
|
||||
|
||||
Your node is highly configurable. Many settings are considered _advanced_ and should be used with caution. For the vast majority of users and use-cases, we recommend using the defaults. This is where you can change RPC credentials as well. Once configured, you may start your node!
|
||||
|
||||
### Syncing
|
||||
|
||||
Depending on your Internet bandwidth, your node should take approximately 5-7 days to sync from genesis to present.
|
||||
|
||||
### Using a Wallet
|
||||
|
||||
Enter your QuickConnect QR code **OR** your raw RPC credentials (both located in `Properties`) into any wallet that supports connecting to a remote node over Tor. For a full list of compatible wallets, see <a href="https://github.com/start9labs/bitcoind-wrapper/blob/master/docs/wallets.md" target="_blank">https://github.com/start9labs/bitcoind-wrapper/blob/master/docs/wallets.md</a>.
|
||||
|
||||
## Pruning
|
||||
|
||||
Pruning is a process by which your node discards old blocks and transactions after it verifies them. Pruned nodes and archival nodes are both "full nodes" in that they are fully validating - they validate every block and transaction. Archival nodes store the entire blockchain and are useful to people interested in doing general or historical analysis, or being a provider of blockchain data to others (eg. a blockexplorer).
|
||||
|
||||
The target of pruning on your Embassy is configurable and set by default to the minimum of 550MB (0.55 GB!), meaning the resulting blockchain will occupy a negligible amount of storage space. The maximum amount of blockchain data you can retain depends on the storage capacity your device. The config menu will not permit you to select a target that exceeds a certain percentage of your device's available capacity.
|
||||
|
||||
For most use cases, we recommend sticking with a very low pruning setting.
|
||||
22
resources/apps/bitcoind/0.21.1.2/license.md
Normal file
22
resources/apps/bitcoind/0.21.1.2/license.md
Normal file
@@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2009-2020 The Bitcoin Core developers
|
||||
Copyright (c) 2009-2020 Bitcoin Developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
272
resources/apps/bitcoind/0.21.1.2/manifest.json
Normal file
272
resources/apps/bitcoind/0.21.1.2/manifest.json
Normal file
@@ -0,0 +1,272 @@
|
||||
{
|
||||
"id": "bitcoind",
|
||||
"title": "Bitcoin Core",
|
||||
"version": "0.21.1.2",
|
||||
"description": {
|
||||
"short": "A Bitcoin Full Node by Bitcoin Core",
|
||||
"long": "Bitcoin is an innovative payment network and a new kind of money. Bitcoin uses peer-to-peer technology to operate with no central authority or banks; managing transactions and the issuing of bitcoins is carried out collectively by the network. Bitcoin is open-source; its design is public, nobody owns or controls Bitcoin and everyone can take part. Through many of its unique properties, Bitcoin allows exciting uses that could not be covered by any previous payment system."
|
||||
},
|
||||
"assets": {
|
||||
"license": "LICENSE",
|
||||
"instructions": "instructions.md",
|
||||
"icon": "icon.png",
|
||||
"docker-images": "image.tar",
|
||||
"assets": null
|
||||
},
|
||||
"build": [
|
||||
"make"
|
||||
],
|
||||
"release-notes": "Upgrade to EmbassyOS v0.3.0",
|
||||
"license": "mit",
|
||||
"wrapper-repo": "https://github.com/Start9Labs/bitcoind-wrapper",
|
||||
"upstream-repo": "https://github.com/bitcoin/bitcoin",
|
||||
"support-site": "https://github.com/bitcoin/bitcoin/issues",
|
||||
"marketing-site": "https://bitcoincore.org/",
|
||||
"donation-url": null,
|
||||
"alerts": {
|
||||
"install": null,
|
||||
"uninstall": "Uninstalling Bitcoin Core will result in permanent loss of data. Without a backup, any funds stored on your node's default hot wallet will be lost forever. If you are unsure, we recommend making a backup, just to be safe.",
|
||||
"restore": "Restoring Bitcoin Core will overwrite its current data. You will lose any transactions recorded in watch-only wallets, and any funds you have received to the hot wallet, since the last backup.",
|
||||
"start": null,
|
||||
"stop": null
|
||||
},
|
||||
"main": {
|
||||
"type": "docker",
|
||||
"image": "main",
|
||||
"system": false,
|
||||
"entrypoint": "docker_entrypoint.sh",
|
||||
"args": [],
|
||||
"mounts": {
|
||||
"compat": "/mnt/assets",
|
||||
"main": "/root/.bitcoin"
|
||||
},
|
||||
"io-format": "yaml",
|
||||
"inject": false,
|
||||
"shm-size-mb": null
|
||||
},
|
||||
"health-checks": {
|
||||
"rpc": {
|
||||
"type": "docker",
|
||||
"image": "main",
|
||||
"system": false,
|
||||
"entrypoint": "bitcoin-cli",
|
||||
"args": [
|
||||
"-rpcconnect=bitcoind.embassy",
|
||||
"-getinfo"
|
||||
],
|
||||
"mounts": {},
|
||||
"io-format": "yaml",
|
||||
"inject": true,
|
||||
"shm-size-mb": null,
|
||||
"critical": false
|
||||
},
|
||||
"synced": {
|
||||
"type": "docker",
|
||||
"image": "utils",
|
||||
"system": true,
|
||||
"entrypoint": "/mnt/assets/check-synced.sh",
|
||||
"args": [],
|
||||
"mounts": {
|
||||
"main": "/root/.bitcoin",
|
||||
"utils": "/mnt/assets"
|
||||
},
|
||||
"io-format": "yaml",
|
||||
"inject": false,
|
||||
"shm-size-mb": null,
|
||||
"critical": false
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"get": {
|
||||
"type": "docker",
|
||||
"image": "compat",
|
||||
"system": true,
|
||||
"entrypoint": "compat",
|
||||
"args": [
|
||||
"config",
|
||||
"get",
|
||||
"/root/.bitcoin",
|
||||
"/mnt/assets/config_spec.yaml"
|
||||
],
|
||||
"mounts": {
|
||||
"compat": "/mnt/assets",
|
||||
"main": "/root/.bitcoin"
|
||||
},
|
||||
"io-format": "yaml",
|
||||
"inject": false,
|
||||
"shm-size-mb": null
|
||||
},
|
||||
"set": {
|
||||
"type": "docker",
|
||||
"image": "compat",
|
||||
"system": true,
|
||||
"entrypoint": "compat",
|
||||
"args": [
|
||||
"config",
|
||||
"set",
|
||||
"bitcoind",
|
||||
"/root/.bitcoin",
|
||||
"/mnt/assets/config_rules.yaml"
|
||||
],
|
||||
"mounts": {
|
||||
"compat": "/mnt/assets",
|
||||
"main": "/root/.bitcoin"
|
||||
},
|
||||
"io-format": "yaml",
|
||||
"inject": false,
|
||||
"shm-size-mb": null
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"type": "docker",
|
||||
"image": "compat",
|
||||
"system": true,
|
||||
"entrypoint": "compat",
|
||||
"args": [
|
||||
"properties",
|
||||
"/root/.bitcoin"
|
||||
],
|
||||
"mounts": {
|
||||
"main": "/root/.bitcoin"
|
||||
},
|
||||
"io-format": "yaml",
|
||||
"inject": false,
|
||||
"shm-size-mb": null
|
||||
},
|
||||
"volumes": {
|
||||
"compat": {
|
||||
"type": "assets"
|
||||
},
|
||||
"main": {
|
||||
"type": "data"
|
||||
},
|
||||
"utils": {
|
||||
"type": "assets"
|
||||
}
|
||||
},
|
||||
"min-os-version": "0.3.0",
|
||||
"interfaces": {
|
||||
"peer": {
|
||||
"name": "Peer Interface",
|
||||
"description": "Listens for incoming connections from peers on the bitcoin network",
|
||||
"tor-config": {
|
||||
"port-mapping": {
|
||||
"8333": "8333"
|
||||
}
|
||||
},
|
||||
"lan-config": null,
|
||||
"ui": false,
|
||||
"protocols": [
|
||||
"tcp",
|
||||
"bitcoin"
|
||||
]
|
||||
},
|
||||
"rpc": {
|
||||
"name": "RPC Interface",
|
||||
"description": "Listens for JSON-RPC commands",
|
||||
"tor-config": {
|
||||
"port-mapping": {
|
||||
"8332": "8332"
|
||||
}
|
||||
},
|
||||
"lan-config": {
|
||||
"8332": {
|
||||
"ssl": false,
|
||||
"mapping": 8332
|
||||
}
|
||||
},
|
||||
"ui": false,
|
||||
"protocols": [
|
||||
"tcp",
|
||||
"http",
|
||||
"json-rpc"
|
||||
]
|
||||
},
|
||||
"zmq": {
|
||||
"name": "ZeroMQ Interface",
|
||||
"description": "Listens for subscriptions to the ZeroMQ raw block and raw transaction event streams",
|
||||
"tor-config": {
|
||||
"port-mapping": {
|
||||
"28332": "28332",
|
||||
"28333": "28333"
|
||||
}
|
||||
},
|
||||
"lan-config": null,
|
||||
"ui": false,
|
||||
"protocols": [
|
||||
"tcp",
|
||||
"zmq"
|
||||
]
|
||||
}
|
||||
},
|
||||
"backup": {
|
||||
"create": {
|
||||
"type": "docker",
|
||||
"image": "compat",
|
||||
"system": true,
|
||||
"entrypoint": "compat",
|
||||
"args": [
|
||||
"duplicity",
|
||||
"create",
|
||||
"/mnt/backup",
|
||||
"/root/.bitcoin"
|
||||
],
|
||||
"mounts": {
|
||||
"BACKUP": "/mnt/backup",
|
||||
"main": "/root/.bitcoin"
|
||||
},
|
||||
"io-format": null,
|
||||
"inject": false,
|
||||
"shm-size-mb": null
|
||||
},
|
||||
"restore": {
|
||||
"type": "docker",
|
||||
"image": "compat",
|
||||
"system": true,
|
||||
"entrypoint": "compat",
|
||||
"args": [
|
||||
"duplicity",
|
||||
"restore",
|
||||
"/mnt/backup",
|
||||
"/root/.bitcoin"
|
||||
],
|
||||
"mounts": {
|
||||
"BACKUP": "/mnt/backup",
|
||||
"main": "/root/.bitcoin"
|
||||
},
|
||||
"io-format": null,
|
||||
"inject": false,
|
||||
"shm-size-mb": null
|
||||
}
|
||||
},
|
||||
"migrations": {
|
||||
"from": {},
|
||||
"to": {}
|
||||
},
|
||||
"actions": {
|
||||
"reindex": {
|
||||
"name": "Reindex Blockchain",
|
||||
"description": "Rebuilds the block and chainstate databases starting from genesis. If blocks already exist on disk, these are used rather than being redownloaded. However, since embassy bitcoin nodes are pruned by default, this usually means downloading the entire blockchain over again.",
|
||||
"warning": "Blocks not stored on disk will be redownloaded in order to rebuild the database. If your node is pruned (embasssy nodes are pruned by default), this action is equivalent to syncing the node from scratch, so this process could take a couple of weeks.",
|
||||
"implementation": {
|
||||
"type": "docker",
|
||||
"image": "main",
|
||||
"system": false,
|
||||
"entrypoint": "reindex.sh",
|
||||
"args": [],
|
||||
"mounts": {
|
||||
"main": "/root/.bitcoin"
|
||||
},
|
||||
"io-format": "json",
|
||||
"inject": false,
|
||||
"shm-size-mb": null
|
||||
},
|
||||
"allowed-statuses": [
|
||||
"running",
|
||||
"stopped"
|
||||
],
|
||||
"input-spec": {}
|
||||
}
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
1
resources/apps/btc-rpc-proxy/0.3.2.1/hash.bin
Normal file
1
resources/apps/btc-rpc-proxy/0.3.2.1/hash.bin
Normal file
@@ -0,0 +1 @@
|
||||
U4QIPEBA7F3J54XJK5X6A37SOFUY3RSUWZAL7UEYMNFPDSGQXNAJUB4CRE5C2CNS5JP4BHJEGKXNGVEYW3DN6K5HBOEX7SCK6IR2GHY
|
||||
BIN
resources/apps/btc-rpc-proxy/0.3.2.1/icon.png
Normal file
BIN
resources/apps/btc-rpc-proxy/0.3.2.1/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
17
resources/apps/btc-rpc-proxy/0.3.2.1/instructions.md
Normal file
17
resources/apps/btc-rpc-proxy/0.3.2.1/instructions.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Usage Instructions
|
||||
|
||||
## Dependencies
|
||||
|
||||
Bitcoin Proxy requires a Bitcoin node to function. By default, Bitcoin Proxy will connect to the Bitcoin Core node running on your Embassy.
|
||||
|
||||
## Config
|
||||
|
||||
You must create at least one user to your Bitcoin Proxy config. Each user you create will receive their own RPC username and password, which can be used for remote access. This is also where you can edit RPC credentials.
|
||||
|
||||
## Properties
|
||||
|
||||
This section includes a convenient list of copyable properties, such as the quick connect URLs and RPC username and passwords for each configured user.
|
||||
|
||||
## Using a Wallet
|
||||
|
||||
Enter your user's QuickConnect QR code **OR** your raw user credentials (both located in `properties`) into any wallet that supports connecting to a remote node over Tor. For a full list of compatible wallets, see <a href="https://github.com/Start9Labs/btc-rpc-proxy-wrapper/blob/master/docs/wallets.md" target="_blank">https://github.com/Start9Labs/btc-rpc-proxy-wrapper/blob/master/docs/wallets.md</a>.
|
||||
21
resources/apps/btc-rpc-proxy/0.3.2.1/license.md
Normal file
21
resources/apps/btc-rpc-proxy/0.3.2.1/license.md
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Martin Habovštiak
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
260
resources/apps/btc-rpc-proxy/0.3.2.1/manifest.json
Normal file
260
resources/apps/btc-rpc-proxy/0.3.2.1/manifest.json
Normal file
@@ -0,0 +1,260 @@
|
||||
{
|
||||
"id": "btc-rpc-proxy",
|
||||
"title": "Bitcoin Proxy",
|
||||
"version": "0.3.2.1",
|
||||
"description": {
|
||||
"short": "Super charge your Bitcoin node",
|
||||
"long": "Bitcoin Proxy enables you to specify several users and, for each user, the list of RPC calls they are allowed to make against your Bitcoin node. It also acts as a super charger for your pruned node. If a user or application requires a block not retained by your node, Bitcoin Proxy will dynamically fetch the block over the P2P network, then verify its hash to ensure validity. Your pruned node will now act like a full archival node!\n"
|
||||
},
|
||||
"assets": {
|
||||
"license": "LICENSE",
|
||||
"instructions": "instructions.md",
|
||||
"icon": "icon.png",
|
||||
"docker-images": "image.tar",
|
||||
"assets": null
|
||||
},
|
||||
"build": [
|
||||
"make"
|
||||
],
|
||||
"release-notes": "Upgrade to EmbassyOS v0.3.0",
|
||||
"license": "mit",
|
||||
"wrapper-repo": "https://github.com/Start9Labs/btc-rpc-proxy-wrapper",
|
||||
"upstream-repo": "https://github.com/Kixunil/btc-rpc-proxy",
|
||||
"support-site": "https://github.com/Kixunil/btc-rpc-proxy/issues",
|
||||
"marketing-site": null,
|
||||
"donation-url": null,
|
||||
"alerts": {
|
||||
"install": "After installing, you will need to manually enable the new RPC requests if you intend to use them. We strongly recommend enabling ALL requests for your primary user.\n",
|
||||
"uninstall": null,
|
||||
"restore": null,
|
||||
"start": null,
|
||||
"stop": null
|
||||
},
|
||||
"main": {
|
||||
"type": "docker",
|
||||
"image": "main",
|
||||
"system": false,
|
||||
"entrypoint": "docker_entrypoint.sh",
|
||||
"args": [],
|
||||
"mounts": {
|
||||
"bitcoind": "/mnt/bitcoind",
|
||||
"main": "/root"
|
||||
},
|
||||
"io-format": "yaml",
|
||||
"inject": false,
|
||||
"shm-size-mb": null
|
||||
},
|
||||
"health-checks": {
|
||||
"main": {
|
||||
"type": "docker",
|
||||
"image": "main",
|
||||
"system": false,
|
||||
"entrypoint": "sh",
|
||||
"args": [
|
||||
"-c",
|
||||
"curl btc-rpc-proxy.embassy:8332 || exit 1"
|
||||
],
|
||||
"mounts": {},
|
||||
"io-format": "yaml",
|
||||
"inject": true,
|
||||
"shm-size-mb": null,
|
||||
"critical": true
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"get": {
|
||||
"type": "docker",
|
||||
"image": "compat",
|
||||
"system": true,
|
||||
"entrypoint": "compat",
|
||||
"args": [
|
||||
"config",
|
||||
"get",
|
||||
"/root",
|
||||
"/mnt/assets/config_spec.yaml"
|
||||
],
|
||||
"mounts": {
|
||||
"compat": "/mnt/assets",
|
||||
"main": "/root"
|
||||
},
|
||||
"io-format": "yaml",
|
||||
"inject": false,
|
||||
"shm-size-mb": null
|
||||
},
|
||||
"set": {
|
||||
"type": "docker",
|
||||
"image": "compat",
|
||||
"system": true,
|
||||
"entrypoint": "compat",
|
||||
"args": [
|
||||
"config",
|
||||
"set",
|
||||
"btc-rpc-proxy",
|
||||
"/root",
|
||||
"/mnt/assets/config_rules.yaml"
|
||||
],
|
||||
"mounts": {
|
||||
"compat": "/mnt/assets",
|
||||
"main": "/root"
|
||||
},
|
||||
"io-format": "yaml",
|
||||
"inject": false,
|
||||
"shm-size-mb": null
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"type": "docker",
|
||||
"image": "compat",
|
||||
"system": true,
|
||||
"entrypoint": "compat",
|
||||
"args": [
|
||||
"properties",
|
||||
"/root"
|
||||
],
|
||||
"mounts": {
|
||||
"compat": "/mnt/assets",
|
||||
"main": "/root"
|
||||
},
|
||||
"io-format": "yaml",
|
||||
"inject": false,
|
||||
"shm-size-mb": null
|
||||
},
|
||||
"volumes": {
|
||||
"bitcoind": {
|
||||
"type": "pointer",
|
||||
"package-id": "bitcoind",
|
||||
"volume-id": "main",
|
||||
"path": "/root/.bitcoin",
|
||||
"readonly": true
|
||||
},
|
||||
"compat": {
|
||||
"type": "assets"
|
||||
},
|
||||
"main": {
|
||||
"type": "data"
|
||||
}
|
||||
},
|
||||
"min-os-version": "0.3.0",
|
||||
"interfaces": {
|
||||
"main": {
|
||||
"name": "Network interface",
|
||||
"description": "Specifies the interface to listen on for HTTP connections.",
|
||||
"tor-config": {
|
||||
"port-mapping": {
|
||||
"8332": "8332"
|
||||
}
|
||||
},
|
||||
"lan-config": {
|
||||
"8332": {
|
||||
"ssl": true,
|
||||
"mapping": 443
|
||||
}
|
||||
},
|
||||
"ui": false,
|
||||
"protocols": [
|
||||
"tcp",
|
||||
"http"
|
||||
]
|
||||
}
|
||||
},
|
||||
"backup": {
|
||||
"create": {
|
||||
"type": "docker",
|
||||
"image": "compat",
|
||||
"system": true,
|
||||
"entrypoint": "compat",
|
||||
"args": [
|
||||
"duplicity",
|
||||
"create",
|
||||
"/mnt/backup",
|
||||
"/root/data"
|
||||
],
|
||||
"mounts": {
|
||||
"BACKUP": "/mnt/backup",
|
||||
"main": "/root/data"
|
||||
},
|
||||
"io-format": "yaml",
|
||||
"inject": false,
|
||||
"shm-size-mb": null
|
||||
},
|
||||
"restore": {
|
||||
"type": "docker",
|
||||
"image": "compat",
|
||||
"system": true,
|
||||
"entrypoint": "compat",
|
||||
"args": [
|
||||
"duplicity",
|
||||
"restore",
|
||||
"/mnt/backup",
|
||||
"/root/data"
|
||||
],
|
||||
"mounts": {
|
||||
"BACKUP": "/mnt/backup",
|
||||
"main": "/root/data"
|
||||
},
|
||||
"io-format": "yaml",
|
||||
"inject": false,
|
||||
"shm-size-mb": null
|
||||
}
|
||||
},
|
||||
"migrations": {
|
||||
"from": {},
|
||||
"to": {}
|
||||
},
|
||||
"actions": {},
|
||||
"dependencies": {
|
||||
"bitcoind": {
|
||||
"version": ">=0.21.1.2 <0.22.0",
|
||||
"requirement": {
|
||||
"type": "opt-out",
|
||||
"how": "Can alternatively configure an external bitcoin node."
|
||||
},
|
||||
"description": "Bitcoin Proxy sends RPC requests to a bitcoin node, like the Bitcoin Core service.",
|
||||
"critical": false,
|
||||
"config": {
|
||||
"check": {
|
||||
"type": "docker",
|
||||
"image": "compat",
|
||||
"system": true,
|
||||
"entrypoint": "compat",
|
||||
"args": [
|
||||
"dependency",
|
||||
"check",
|
||||
"btc-rpc-proxy",
|
||||
"bitcoind",
|
||||
"/root",
|
||||
"/mnt/assets/bitcoind_config_rules.yaml"
|
||||
],
|
||||
"mounts": {
|
||||
"compat": "/mnt/assets",
|
||||
"main": "/root"
|
||||
},
|
||||
"io-format": "yaml",
|
||||
"inject": false,
|
||||
"shm-size-mb": null
|
||||
},
|
||||
"auto-configure": {
|
||||
"type": "docker",
|
||||
"image": "compat",
|
||||
"system": true,
|
||||
"entrypoint": "compat",
|
||||
"args": [
|
||||
"dependency",
|
||||
"auto-configure",
|
||||
"btc-rpc-proxy",
|
||||
"bitcoind",
|
||||
"/root",
|
||||
"/mnt/assets/bitcoind_config_rules.yaml"
|
||||
],
|
||||
"mounts": {
|
||||
"compat": "/mnt/assets",
|
||||
"main": "/root"
|
||||
},
|
||||
"io-format": "yaml",
|
||||
"inject": false,
|
||||
"shm-size-mb": null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
resources/apps/lnd/0.13.3.1/hash.bin
Normal file
1
resources/apps/lnd/0.13.3.1/hash.bin
Normal file
@@ -0,0 +1 @@
|
||||
PFLZO3TPFC57UTPBE3NKBLOTBRHSZH4DMNYQNX4FEQ54XCC6OB3R3UZEAS62ZLPNVRVNPRE2Z67O6NW4NHV2NEP5NPJ2FTDVHNJZFMY
|
||||
BIN
resources/apps/lnd/0.13.3.1/icon.png
Normal file
BIN
resources/apps/lnd/0.13.3.1/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.1 KiB |
52
resources/apps/lnd/0.13.3.1/instructions.md
Normal file
52
resources/apps/lnd/0.13.3.1/instructions.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# Lightning Network Daemon (LND)
|
||||
|
||||
## Dependencies
|
||||
|
||||
LND on the Embassy requires a full archival Bitcoin node to function. Since your Embassy Bitcoin node is pruned by default, an additional service, Bitcoin Proxy, is also required.
|
||||
|
||||
## LND Config
|
||||
|
||||
Your LND node is highly configurable. Many settings are considered _advanced_ and should be used with caution. For the vast majority of users and use-cases, we recommend using the defaults. Once configured, you may start your node!
|
||||
|
||||
## Bitcoin Proxy Config
|
||||
|
||||
On the LND page, scroll down to find the Bitcoin Proxy dependency. Click `Configure`. This will automatically configure Bitcoin Proxy to satisfy LND.
|
||||
|
||||
# Lightning Usage Guide
|
||||
|
||||
## Using a Wallet
|
||||
|
||||
Enter your LND-Connect QR code (located in `properties`) into any wallet that supports connecting to a remote LND node over Tor. For a list of compatible wallets, see <a href="https://github.com/start9labs/lnd-wrapper/blob/master/docs/wallets.md" target="_blank">https://github.com/start9labs/lnd-wrapper/blob/master/docs/wallets.md</a>.
|
||||
|
||||
## Getting On-Chain Funds
|
||||
|
||||
Before you can open and channel and start transacting on the Lightning network, you need some Bitcoin stored on your LND node. Be advised, Bitcoin funds that you transfer to your LND node are hot, meaning, the are stored directly on your Embassy. There is no way to use cold storage when using Lightning, which is why people call it reckless. For this reason, it is usually unwise to move large amounts of Bitcoin to your LND node. That said, you don't want to move a tiny amount either, since that would limit your purchasing power on the Lightning network. We recommend moving about 500,000-5,000,000 satoshis, or .005-.05 Bitcoin, which at current (May 7, 2021) prices is about $250-$2,500. This gives you a solid amount of purchasing power, but hopefully wouldn't ruin your life if something were to go terribly wrong. If you feel comfortable using more Bitcoin, then by all means, go for it.
|
||||
|
||||
## Depositing to LND
|
||||
|
||||
When using LND or any wallet that is connected to LND it is important to note that until "Synced to Chain" in the Properties page is reporting ✅, your deposits to your LND on-chain wallet may not appear.
|
||||
|
||||
## Opening a Channel and Getting Outbound Liquidity
|
||||
|
||||
Once your LND node is synced, it's time to open a channel. Opening a channel with a well-connected node is how you get connected to the rest of the network, and it immediately grants you outbound liquidity. Meaning, you will be able to send money to others. Unless you are planning to become a Lightning Service Provider, you do not want to open more than a couple of channels at most. Managing many channels is difficult, it can be quite expensive, and unless you plan to devote significant resources in the form of time and Bitcoin, there is no profit in it. If your goal is to use Lightning to benefit from instant and near-free transactions, you only need 2-3 good channels.
|
||||
|
||||
If you are looking for destinations for your first channel, we suggest you open a channel with the [Start9 HQ](025d28dc4c4f5ce4194c31c3109129cd741fafc1ff2f6ea53f97de2f58877b2295@64.225.19.231:9735) node, which is already very well connected.
|
||||
|
||||
It is not recommended to open a channel less than 100,000 satoshi, or .001 BTC, or $50 USD in today's prices. Anything less, and it's possible that the cost to open and close the channel might approach the size of the channel itself. The bigger the channel you open, the more outbound liquidity you will have, which means you have more spending power on the network. In this tutorial, we are going to open a channel of 2,000,000 satoshi. When opening a channel with Start9 HQ, we ask that you make it a private channel, meaning it will not display publicly on network graph. The reason for this is that unless you plan to be a very active Lightning Node Operator, having public channels decreases not only the reliability of your node but also hurts Start9's ability to route payments for you. If you do intend to be a serious node operator, we require that your channel be for a minimum of 5,000,000 sats. Please contact us in one of our community channels for further details.
|
||||
|
||||
## Getting Inbound Liquidity
|
||||
|
||||
If you want to receive payments, you will need some inbound liquidity.
|
||||
|
||||
The first, easiest, and best way to get inbound liquidity is to use your outbound liquidity to buy something. Any Bitcoin you spend using your outbound liquidity is Bitcoin you can now receive back. So if there is something you want to buy, like a Start9 Embassy or a t-shirt from the Start9 store, simply make the purchase, and you will then have inbound liquidity equal to the amount of Satoshis you spend.
|
||||
|
||||
Option 2 is to personally ask Start9 for an invoice for however much inbound liquity you want. Then you send Bitcoin to the invoice, and in turn we will transfer fiat currency to you equal to the amount of the Bitcoin you send us. In other words, Start9 will buy some Bitcoin from you at market rate, such that you then have inbound liquidity. In either case, you are spending or selling some Bitcoin.
|
||||
|
||||
The only way to get inbound liquidity without spending or selling Bitcoin is to convince someone to open a channel with you, just as you opened a channel with Start9 HQ. This may be a difficult task, since there is not much incentive for someone to open a channel with you unless you are also very well connected. Also, you will need to make sure that they too, are well connected with plenty of inbound liquidity, or else your inbound liquidity with them will not really matter. In other words, they might be the only person capable of paying you.
|
||||
|
||||
So options 1 or 2 are best. Use your Lightning node's outbound liquidity to either purchase something or sell some Bitcoin. Now, you can pay and get paid using Lightning in an amount equal to your outbound and inbound liquidity.
|
||||
|
||||
## Sending payments over Lightning
|
||||
|
||||
Once you have open channels and are ready to transact on the Lightning Network, it is important to note that until "Synced to Graph" in the Properties page is reporting ✅, you may experience problems finding routes to your destination.
|
||||
|
||||
19
resources/apps/lnd/0.13.3.1/license.md
Normal file
19
resources/apps/lnd/0.13.3.1/license.md
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (C) 2015-2018 Lightning Labs and The Lightning Network Developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
348
resources/apps/lnd/0.13.3.1/manifest.json
Normal file
348
resources/apps/lnd/0.13.3.1/manifest.json
Normal file
@@ -0,0 +1,348 @@
|
||||
{
|
||||
"id": "lnd",
|
||||
"title": "Lightning Network Daemon",
|
||||
"version": "0.13.3.1",
|
||||
"description": {
|
||||
"short": "A complete implementation of a Lightning Network node by Lightning Labs",
|
||||
"long": "LND fully conforms to the Lightning Network specification (BOLTs). BOLT stands for: Basis of Lightning Technology. In the current state lnd is capable of: creating channels, closing channels, managing all channel states (including the exceptional ones!), maintaining a fully authenticated+validated channel graph, performing path finding within the network, passively forwarding incoming payments, sending outgoing onion-encrypted payments through the network, updating advertised fee schedules, and automatic channel management (autopilot).\n"
|
||||
},
|
||||
"assets": {
|
||||
"license": "LICENSE",
|
||||
"instructions": "instructions.md",
|
||||
"icon": "icon.png",
|
||||
"docker-images": "image.tar",
|
||||
"assets": null
|
||||
},
|
||||
"build": [
|
||||
"make"
|
||||
],
|
||||
"release-notes": "Upgrade to EmbassyOS v0.3.0",
|
||||
"license": "mit",
|
||||
"wrapper-repo": "https://github.com/Start9Labs/lnd-wrapper",
|
||||
"upstream-repo": "https://github.com/lightningnetwork/lnd",
|
||||
"support-site": "https://lightning.engineering/slack.html",
|
||||
"marketing-site": "https://lightning.engineering/",
|
||||
"donation-url": null,
|
||||
"alerts": {
|
||||
"install": "READ CAREFULLY! LND and the Lightning Network are considered beta software. Please use with caution and do not risk more money than you are willing to lose. We encourage frequent backups. If for any reason, you need to restore LND from a backup, your on-chain wallet will be restored, but all your channels will be closed and their funds returned to your on-chain wallet, minus fees. It may also take some time for this process to occur.\n",
|
||||
"uninstall": "READ CAREFULLY! Uninstalling LND will result in permanent loss of data, including its private keys for its on-chain wallet and all channel states. Please make a backup if you have any funds in your on-chain wallet or in any channels. Recovering from backup will restore your on-chain wallet, but due to the architecture of the Lightning Network, your channels cannot be recovered. All your channels will be closed and their funds returned to your on-chain wallet, minus fees. \n",
|
||||
"restore": "Restoring LND will overwrite its current data, including its on-chain wallet and channels. Any channels opened since the last backup will be forgotten and may linger indefinitely, and channels contained in the backup will be closed and their funds returned to your on-chain wallet, minus fees.\n",
|
||||
"start": null,
|
||||
"stop": null
|
||||
},
|
||||
"main": {
|
||||
"type": "docker",
|
||||
"image": "main",
|
||||
"system": false,
|
||||
"entrypoint": "docker_entrypoint.sh",
|
||||
"args": [],
|
||||
"mounts": {
|
||||
"bitcoind": "/mnt/bitcoind",
|
||||
"btc-rpc-proxy": "/mnt/btc-rpc-proxy",
|
||||
"main": "/root/.lnd"
|
||||
},
|
||||
"io-format": "yaml",
|
||||
"inject": false,
|
||||
"shm-size-mb": null
|
||||
},
|
||||
"health-checks": {
|
||||
"synced": {
|
||||
"type": "docker",
|
||||
"image": "main",
|
||||
"system": false,
|
||||
"entrypoint": "health-check",
|
||||
"args": [],
|
||||
"mounts": {},
|
||||
"io-format": "yaml",
|
||||
"inject": true,
|
||||
"shm-size-mb": null,
|
||||
"critical": false
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"get": {
|
||||
"type": "docker",
|
||||
"image": "compat",
|
||||
"system": true,
|
||||
"entrypoint": "compat",
|
||||
"args": [
|
||||
"config",
|
||||
"get",
|
||||
"/root/.lnd",
|
||||
"/mnt/assets/config_spec.yaml"
|
||||
],
|
||||
"mounts": {
|
||||
"compat": "/mnt/assets",
|
||||
"main": "/root/.lnd"
|
||||
},
|
||||
"io-format": "yaml",
|
||||
"inject": false,
|
||||
"shm-size-mb": null
|
||||
},
|
||||
"set": {
|
||||
"type": "docker",
|
||||
"image": "compat",
|
||||
"system": true,
|
||||
"entrypoint": "compat",
|
||||
"args": [
|
||||
"config",
|
||||
"set",
|
||||
"lnd",
|
||||
"/root/.lnd",
|
||||
"/mnt/assets/config_rules.yaml"
|
||||
],
|
||||
"mounts": {
|
||||
"compat": "/mnt/assets",
|
||||
"main": "/root/.lnd"
|
||||
},
|
||||
"io-format": "yaml",
|
||||
"inject": false,
|
||||
"shm-size-mb": null
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"type": "docker",
|
||||
"image": "compat",
|
||||
"system": true,
|
||||
"entrypoint": "compat",
|
||||
"args": [
|
||||
"properties",
|
||||
"/root/.lnd"
|
||||
],
|
||||
"mounts": {
|
||||
"main": "/root/.lnd"
|
||||
},
|
||||
"io-format": "yaml",
|
||||
"inject": false,
|
||||
"shm-size-mb": null
|
||||
},
|
||||
"volumes": {
|
||||
"bitcoind": {
|
||||
"type": "pointer",
|
||||
"package-id": "bitcoind",
|
||||
"volume-id": "main",
|
||||
"path": "/root",
|
||||
"readonly": true
|
||||
},
|
||||
"btc-rpc-proxy": {
|
||||
"type": "pointer",
|
||||
"package-id": "btc-rpc-proxy",
|
||||
"volume-id": "main",
|
||||
"path": "/root",
|
||||
"readonly": false
|
||||
},
|
||||
"certificates": {
|
||||
"type": "certificate",
|
||||
"interface-id": "control"
|
||||
},
|
||||
"compat": {
|
||||
"type": "assets"
|
||||
},
|
||||
"main": {
|
||||
"type": "data"
|
||||
}
|
||||
},
|
||||
"min-os-version": "0.3.0",
|
||||
"interfaces": {
|
||||
"control": {
|
||||
"name": "Control Interface",
|
||||
"description": "Specifies the interfaces to listen on for RPC and REST connections.",
|
||||
"tor-config": {
|
||||
"port-mapping": {
|
||||
"8080": "8080",
|
||||
"10009": "10009"
|
||||
}
|
||||
},
|
||||
"lan-config": null,
|
||||
"ui": false,
|
||||
"protocols": [
|
||||
"tcp",
|
||||
"http",
|
||||
"grpc"
|
||||
]
|
||||
},
|
||||
"peer": {
|
||||
"name": "Peer Interface",
|
||||
"description": "Specifies the interfaces to listen on for p2p connections.",
|
||||
"tor-config": {
|
||||
"port-mapping": {
|
||||
"9735": "9735"
|
||||
}
|
||||
},
|
||||
"lan-config": null,
|
||||
"ui": false,
|
||||
"protocols": [
|
||||
"tcp",
|
||||
"http"
|
||||
]
|
||||
},
|
||||
"watchtower": {
|
||||
"name": "Watchtower Interface",
|
||||
"description": "Specifies the interfaces to listen on for watchtower client connections.",
|
||||
"tor-config": {
|
||||
"port-mapping": {
|
||||
"9911": "9911"
|
||||
}
|
||||
},
|
||||
"lan-config": null,
|
||||
"ui": false,
|
||||
"protocols": [
|
||||
"tcp",
|
||||
"grpc"
|
||||
]
|
||||
}
|
||||
},
|
||||
"backup": {
|
||||
"create": {
|
||||
"type": "docker",
|
||||
"image": "compat",
|
||||
"system": true,
|
||||
"entrypoint": "compat",
|
||||
"args": [
|
||||
"duplicity",
|
||||
"create",
|
||||
"/mnt/backup",
|
||||
"/root/.lnd"
|
||||
],
|
||||
"mounts": {
|
||||
"BACKUP": "/mnt/backup",
|
||||
"main": "/root/.lnd"
|
||||
},
|
||||
"io-format": "yaml",
|
||||
"inject": false,
|
||||
"shm-size-mb": null
|
||||
},
|
||||
"restore": {
|
||||
"type": "docker",
|
||||
"image": "compat",
|
||||
"system": true,
|
||||
"entrypoint": "/mnt/assets/backup-restore.sh",
|
||||
"args": [
|
||||
"duplicity",
|
||||
"restore",
|
||||
"/mnt/backup",
|
||||
"/root/.lnd"
|
||||
],
|
||||
"mounts": {
|
||||
"BACKUP": "/mnt/backup",
|
||||
"compat": "/mnt/assets",
|
||||
"main": "/root/.lnd"
|
||||
},
|
||||
"io-format": "yaml",
|
||||
"inject": false,
|
||||
"shm-size-mb": null
|
||||
}
|
||||
},
|
||||
"migrations": {
|
||||
"from": {},
|
||||
"to": {}
|
||||
},
|
||||
"actions": {},
|
||||
"dependencies": {
|
||||
"bitcoind": {
|
||||
"version": ">=0.21.1.2 <0.22.0",
|
||||
"requirement": {
|
||||
"type": "opt-out",
|
||||
"how": "Can alternatively configure an external bitcoin node."
|
||||
},
|
||||
"description": "Used to subscribe to new block events.",
|
||||
"critical": true,
|
||||
"config": {
|
||||
"check": {
|
||||
"type": "docker",
|
||||
"image": "compat",
|
||||
"system": true,
|
||||
"entrypoint": "compat",
|
||||
"args": [
|
||||
"dependency",
|
||||
"check",
|
||||
"lnd",
|
||||
"bitcoind",
|
||||
"/root/.lnd",
|
||||
"/mnt/assets/bitcoind_config_rules.yaml"
|
||||
],
|
||||
"mounts": {
|
||||
"compat": "/mnt/assets",
|
||||
"main": "/root/.lnd"
|
||||
},
|
||||
"io-format": null,
|
||||
"inject": false,
|
||||
"shm-size-mb": null
|
||||
},
|
||||
"auto-configure": {
|
||||
"type": "docker",
|
||||
"image": "compat",
|
||||
"system": true,
|
||||
"entrypoint": "compat",
|
||||
"args": [
|
||||
"dependency",
|
||||
"auto-configure",
|
||||
"lnd",
|
||||
"bitcoind",
|
||||
"/root/.lnd",
|
||||
"/mnt/assets/bitcoind_config_rules.yaml"
|
||||
],
|
||||
"mounts": {
|
||||
"compat": "/mnt/assets"
|
||||
},
|
||||
"io-format": null,
|
||||
"inject": false,
|
||||
"shm-size-mb": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"btc-rpc-proxy": {
|
||||
"version": ">=0.3.2.1 <0.4.0",
|
||||
"requirement": {
|
||||
"type": "opt-out",
|
||||
"how": "Can alternatively use the internal full archival bitcoind node or configure an external bitcoin node."
|
||||
},
|
||||
"description": "Used to fetch validated blocks.",
|
||||
"critical": false,
|
||||
"config": {
|
||||
"check": {
|
||||
"type": "docker",
|
||||
"image": "compat",
|
||||
"system": true,
|
||||
"entrypoint": "compat",
|
||||
"args": [
|
||||
"dependency",
|
||||
"check",
|
||||
"lnd",
|
||||
"btc-rpc-proxy",
|
||||
"/root/.lnd",
|
||||
"/mnt/assets/btc-rpc-proxy_config_rules.yaml"
|
||||
],
|
||||
"mounts": {
|
||||
"compat": "/mnt/assets",
|
||||
"main": "/root/.lnd"
|
||||
},
|
||||
"io-format": "yaml",
|
||||
"inject": false,
|
||||
"shm-size-mb": null
|
||||
},
|
||||
"auto-configure": {
|
||||
"type": "docker",
|
||||
"image": "compat",
|
||||
"system": true,
|
||||
"entrypoint": "compat",
|
||||
"args": [
|
||||
"dependency",
|
||||
"auto-configure",
|
||||
"lnd",
|
||||
"btc-rpc-proxy",
|
||||
"/root/.lnd",
|
||||
"/mnt/assets/btc-rpc-proxy_config_rules.yaml"
|
||||
],
|
||||
"mounts": {
|
||||
"compat": "/mnt/assets",
|
||||
"main": "/root/.lnd"
|
||||
},
|
||||
"io-format": "yaml",
|
||||
"inject": false,
|
||||
"shm-size-mb": null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -132,13 +132,14 @@ makeFoundation appSettings = do
|
||||
mkFoundation (panic "connPool forced in tempFoundation") (panic "stopFsNotify forced in tempFoundation")
|
||||
logFunc = messageLoggerSource tempFoundation appLogger
|
||||
|
||||
stop <- runLoggingT (runReaderT watchPkgRepoRoot appSettings) logFunc
|
||||
createDirectoryIfMissing True (errorLogRoot appSettings)
|
||||
|
||||
-- Create the database connection pool
|
||||
pool <- flip runLoggingT logFunc
|
||||
$ createPostgresqlPool (pgConnStr $ appDatabaseConf appSettings) (pgPoolSize . appDatabaseConf $ appSettings)
|
||||
|
||||
stop <- runLoggingT (runReaderT (watchPkgRepoRoot pool) appSettings) logFunc
|
||||
|
||||
-- Preform database migration using application logging settings
|
||||
runLoggingT (runSqlPool (runMigration migrateAll) pool) logFunc
|
||||
|
||||
|
||||
@@ -14,9 +14,8 @@ import Database.Esqueleto.Experimental
|
||||
( (%)
|
||||
, (&&.)
|
||||
, (++.)
|
||||
, (:&)(..)
|
||||
, (==.)
|
||||
, Entity(entityKey, entityVal)
|
||||
, SqlBackend
|
||||
, (^.)
|
||||
, desc
|
||||
, from
|
||||
@@ -27,14 +26,20 @@ import Database.Esqueleto.Experimental
|
||||
, orderBy
|
||||
, select
|
||||
, selectSource
|
||||
, table
|
||||
, val
|
||||
, valList
|
||||
, where_
|
||||
, (||.)
|
||||
)
|
||||
import Database.Esqueleto.Experimental
|
||||
( (:&)(..)
|
||||
, table
|
||||
import qualified Database.Persist as P
|
||||
import Database.Persist.Postgresql
|
||||
hiding ( (==.)
|
||||
, getJust
|
||||
, selectSource
|
||||
, (||.)
|
||||
)
|
||||
import Handler.Types.Marketplace ( PackageDependencyMetadata(..)
|
||||
)
|
||||
import Lib.Types.AppIndex ( PkgId )
|
||||
import Lib.Types.Category
|
||||
@@ -90,23 +95,80 @@ getPkgData pkgs = selectSource $ do
|
||||
where_ (pkgData ^. PkgRecordId `in_` valList (PkgRecordKey <$> pkgs))
|
||||
pure pkgData
|
||||
|
||||
getPkgDependencyData :: MonadIO m
|
||||
=> Key PkgRecord
|
||||
-> Version
|
||||
-> ReaderT SqlBackend m ([(Entity PkgDependency, Entity PkgRecord)])
|
||||
getPkgDependencyData pkgId pkgVersion = select $ do
|
||||
pd <- from
|
||||
(do
|
||||
(pkgDepRecord :& depPkgRecord) <-
|
||||
from
|
||||
$ table @PkgDependency
|
||||
`innerJoin` table @PkgRecord
|
||||
`on` (\(pdr :& dpr) -> dpr ^. PkgRecordId ==. pdr ^. PkgDependencyDepId)
|
||||
where_ (pkgDepRecord ^. PkgDependencyPkgId ==. (val pkgId))
|
||||
where_ (pkgDepRecord ^. PkgDependencyPkgVersion ==. val pkgVersion)
|
||||
pure (pkgDepRecord, depPkgRecord)
|
||||
)
|
||||
pure pd
|
||||
|
||||
zipCategories :: MonadUnliftIO m
|
||||
=> ConduitT
|
||||
(Entity PkgRecord, [Entity VersionRecord])
|
||||
(Entity PkgRecord, [Entity VersionRecord], [Entity Category])
|
||||
(ReaderT SqlBackend m)
|
||||
()
|
||||
zipCategories = awaitForever $ \(pkg, vers) -> do
|
||||
let pkgDbId = entityKey pkg
|
||||
raw <- lift $ select $ do
|
||||
(sc :& cat) <-
|
||||
from
|
||||
$ table @PkgCategory
|
||||
`innerJoin` table @Category
|
||||
`on` (\(sc :& cat) -> sc ^. PkgCategoryCategoryId ==. cat ^. CategoryId)
|
||||
where_ (sc ^. PkgCategoryPkgId ==. val pkgDbId)
|
||||
pure cat
|
||||
yield (pkg, vers, raw)
|
||||
|
||||
zipVersions :: MonadUnliftIO m
|
||||
=> ConduitT (Entity PkgRecord) (Entity PkgRecord, [Entity VersionRecord]) (ReaderT SqlBackend m) ()
|
||||
zipVersions = awaitForever $ \i -> do
|
||||
let appDbId = entityKey i
|
||||
zipVersions = awaitForever $ \pkg -> do
|
||||
let appDbId = entityKey pkg
|
||||
res <- lift $ select $ do
|
||||
v <- from $ table @VersionRecord
|
||||
where_ $ v ^. VersionRecordPkgId ==. val appDbId
|
||||
-- first value in list will be latest version
|
||||
orderBy [desc (v ^. VersionRecordNumber)]
|
||||
pure v
|
||||
yield (i, res)
|
||||
yield (pkg, res)
|
||||
|
||||
filterOsCompatible :: Monad m
|
||||
=> (Version -> Bool)
|
||||
-> ConduitT
|
||||
(Entity PkgRecord, [Entity VersionRecord])
|
||||
(Entity PkgRecord, [Entity VersionRecord])
|
||||
m
|
||||
()
|
||||
filterOsCompatible p = awaitForever $ \(app, versions) -> do
|
||||
let compatible = filter (p . versionRecordOsVersion . entityVal) versions
|
||||
when (not $ null compatible) $ yield (app, compatible)
|
||||
zipDependencyVersions :: (Monad m, MonadIO m)
|
||||
=> (Entity PkgDependency, Entity PkgRecord)
|
||||
-> ReaderT SqlBackend m PackageDependencyMetadata
|
||||
zipDependencyVersions (pkgDepRecord, depRecord) = do
|
||||
let pkgDbId = entityKey $ depRecord
|
||||
depVers <- select $ do
|
||||
v <- from $ table @VersionRecord
|
||||
where_ $ v ^. VersionRecordPkgId ==. val pkgDbId
|
||||
pure v
|
||||
pure $ PackageDependencyMetadata { packageDependencyMetadataPkgDependencyRecord = pkgDepRecord
|
||||
, packageDependencyMetadataDepPkgRecord = depRecord
|
||||
, packageDependencyMetadataDepVersions = depVers
|
||||
}
|
||||
|
||||
fetchAllAppVersions :: MonadUnliftIO m => ConnectionPool -> PkgId -> m [VersionRecord]
|
||||
fetchAllAppVersions appConnPool appId = do
|
||||
entityAppVersions <- runSqlPool (P.selectList [VersionRecordPkgId P.==. PkgRecordKey appId] []) appConnPool
|
||||
pure $ entityVal <$> entityAppVersions
|
||||
|
||||
fetchLatestApp :: MonadIO m => PkgId -> ReaderT SqlBackend m (Maybe (P.Entity PkgRecord, P.Entity VersionRecord))
|
||||
fetchLatestApp appId = fmap headMay . sortResults . select $ do
|
||||
(service :& version) <-
|
||||
from
|
||||
$ table @PkgRecord
|
||||
`innerJoin` table @VersionRecord
|
||||
`on` (\(service :& version) -> service ^. PkgRecordId ==. version ^. VersionRecordPkgId)
|
||||
where_ (service ^. PkgRecordId ==. val (PkgRecordKey appId))
|
||||
pure (service, version)
|
||||
where sortResults = fmap $ sortOn (Down . versionRecordNumber . entityVal . snd)
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
{-# LANGUAGE ViewPatterns #-}
|
||||
{-# LANGUAGE TypeApplications #-}
|
||||
{-# OPTIONS_GHC -Wno-orphans #-}
|
||||
|
||||
module Foundation where
|
||||
|
||||
import Startlude hiding ( Handler )
|
||||
@@ -75,9 +76,6 @@ instance Has AppSettings RegistryCtx where
|
||||
extract = appSettings
|
||||
update f ctx = ctx { appSettings = f (appSettings ctx) }
|
||||
|
||||
|
||||
|
||||
|
||||
setWebProcessThreadId :: (ThreadId, ThreadId) -> RegistryCtx -> IO ()
|
||||
setWebProcessThreadId tid a = putMVar (appWebServerThreadId a) $ tid
|
||||
|
||||
|
||||
@@ -20,31 +20,17 @@ import Startlude hiding ( Any
|
||||
import Conduit ( (.|)
|
||||
, awaitForever
|
||||
, dropC
|
||||
, mapC
|
||||
, runConduit
|
||||
, sinkList
|
||||
, sourceFile
|
||||
, takeC
|
||||
)
|
||||
import Control.Monad.Except.CoHas ( liftEither )
|
||||
import Control.Monad.Reader.Has ( Has
|
||||
, ask
|
||||
)
|
||||
import Control.Parallel.Strategies ( parMap
|
||||
, rpar
|
||||
|
||||
)
|
||||
import Crypto.Hash ( SHA256 )
|
||||
import Crypto.Hash.Conduit ( hashFile )
|
||||
import Data.Aeson ( (.:)
|
||||
, FromJSON(parseJSON)
|
||||
, KeyValue((.=))
|
||||
, ToJSON(toJSON)
|
||||
, Value(String)
|
||||
, decode
|
||||
import Data.Aeson ( decode
|
||||
, eitherDecode
|
||||
, eitherDecodeStrict
|
||||
, object
|
||||
, withObject
|
||||
)
|
||||
import qualified Data.Attoparsec.Text as Atto
|
||||
import Data.ByteArray.Encoding ( Base(Base16)
|
||||
@@ -57,63 +43,52 @@ import Data.List ( head
|
||||
, lookup
|
||||
, sortOn
|
||||
)
|
||||
import Data.Semigroup ( Max(Max, getMax) )
|
||||
import Data.String.Interpolate.IsString
|
||||
( i )
|
||||
import qualified Data.Text as T
|
||||
import Database.Esqueleto.Experimental
|
||||
( (:&)((:&))
|
||||
, (==.)
|
||||
, Entity(entityKey, entityVal)
|
||||
( Entity(entityKey, entityVal)
|
||||
, SqlBackend
|
||||
, Value(unValue)
|
||||
, (^.)
|
||||
, desc
|
||||
, from
|
||||
, in_
|
||||
, innerJoin
|
||||
, on
|
||||
, orderBy
|
||||
, select
|
||||
, table
|
||||
, val
|
||||
, valList
|
||||
, where_
|
||||
)
|
||||
import Database.Marketplace ( filterOsCompatible
|
||||
, getPkgData
|
||||
import Database.Marketplace ( getPkgData
|
||||
, searchServices
|
||||
, zipVersions
|
||||
, fetchAllAppVersions
|
||||
, fetchLatestApp
|
||||
, getPkgDependencyData, zipDependencyVersions, zipCategories
|
||||
)
|
||||
import qualified Database.Persist as P
|
||||
import Database.Persist ( PersistUniqueRead(getBy)
|
||||
, insertUnique
|
||||
)
|
||||
import Foundation ( Handler
|
||||
, RegistryCtx(appSettings)
|
||||
, RegistryCtx(appSettings, appConnPool)
|
||||
)
|
||||
import Lib.Error ( S9Error(..)
|
||||
|
||||
)
|
||||
import Lib.Error ( S9Error(..) )
|
||||
import Lib.PkgRepository ( getManifest )
|
||||
import Lib.Types.AppIndex ( PkgId(PkgId)
|
||||
, ServiceDependencyInfo(serviceDependencyInfoVersion)
|
||||
, ServiceManifest(serviceManifestDependencies)
|
||||
, VersionInfo(..)
|
||||
|
||||
|
||||
)
|
||||
import Lib.Types.AppIndex ( )
|
||||
import Lib.Types.Category ( CategoryTitle(..) )
|
||||
import Lib.Types.Emver ( (<||)
|
||||
, Version
|
||||
, VersionRange(Any)
|
||||
import Lib.Types.Emver ( Version
|
||||
, parseRange
|
||||
, parseVersion
|
||||
, satisfies
|
||||
, satisfies, VersionRange
|
||||
)
|
||||
import Model ( Category(..)
|
||||
, EntityField(..)
|
||||
, EosHash(EosHash, eosHashHash)
|
||||
, Key(PkgRecordKey, unPkgRecordKey)
|
||||
, Key(unPkgRecordKey)
|
||||
, OsVersion(..)
|
||||
, PkgCategory
|
||||
, PkgRecord(..)
|
||||
, Unique(UniqueVersion)
|
||||
, VersionRecord(..)
|
||||
@@ -129,120 +104,27 @@ import UnliftIO.Async ( concurrently
|
||||
, mapConcurrently
|
||||
)
|
||||
import UnliftIO.Directory ( listDirectory )
|
||||
import Util.Shared ( getVersionSpecFromQuery )
|
||||
import Util.Shared ( getVersionSpecFromQuery, filterLatestVersionFromSpec, filterPkgOsCompatible, filterDependencyOsCompatible, filterDependencyBestVersion )
|
||||
import Yesod.Core ( MonadResource
|
||||
, ToContent(..)
|
||||
, ToTypedContent(..)
|
||||
, TypedContent
|
||||
, YesodRequest(..)
|
||||
, addHeader
|
||||
, getRequest
|
||||
, getsYesod
|
||||
, logWarn
|
||||
, lookupGetParam
|
||||
, respondSource
|
||||
, sendChunkBS
|
||||
, sendResponseStatus
|
||||
, typeOctet
|
||||
, getYesod
|
||||
)
|
||||
import Yesod.Persist ( YesodDB )
|
||||
import Yesod.Persist.Core ( YesodPersist(runDB) )
|
||||
|
||||
type URL = Text
|
||||
newtype CategoryRes = CategoryRes {
|
||||
categories :: [CategoryTitle]
|
||||
} deriving (Show, Generic)
|
||||
instance ToJSON CategoryRes
|
||||
instance ToContent CategoryRes where
|
||||
toContent = toContent . toJSON
|
||||
instance ToTypedContent CategoryRes where
|
||||
toTypedContent = toTypedContent . toJSON
|
||||
data ServiceRes = ServiceRes
|
||||
{ serviceResIcon :: URL
|
||||
, serviceResManifest :: Data.Aeson.Value -- ServiceManifest
|
||||
, serviceResCategories :: [CategoryTitle]
|
||||
, serviceResInstructions :: URL
|
||||
, serviceResLicense :: URL
|
||||
, serviceResVersions :: [Version]
|
||||
, serviceResDependencyInfo :: HM.HashMap PkgId DependencyInfo
|
||||
}
|
||||
deriving Generic
|
||||
|
||||
newtype ReleaseNotes = ReleaseNotes { unReleaseNotes :: HM.HashMap Version Text }
|
||||
deriving (Eq, Show)
|
||||
instance ToJSON ReleaseNotes where
|
||||
toJSON ReleaseNotes {..} = object [ t .= v | (k, v) <- HM.toList unReleaseNotes, let (String t) = toJSON k ]
|
||||
instance ToContent ReleaseNotes where
|
||||
toContent = toContent . toJSON
|
||||
instance ToTypedContent ReleaseNotes where
|
||||
toTypedContent = toTypedContent . toJSON
|
||||
instance ToJSON ServiceRes where
|
||||
toJSON ServiceRes {..} = object
|
||||
[ "icon" .= serviceResIcon
|
||||
, "license" .= serviceResLicense
|
||||
, "instructions" .= serviceResInstructions
|
||||
, "manifest" .= serviceResManifest
|
||||
, "categories" .= serviceResCategories
|
||||
, "versions" .= serviceResVersions
|
||||
, "dependency-metadata" .= serviceResDependencyInfo
|
||||
]
|
||||
data DependencyInfo = DependencyInfo
|
||||
{ dependencyInfoTitle :: PkgId
|
||||
, dependencyInfoIcon :: URL
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
instance ToJSON DependencyInfo where
|
||||
toJSON DependencyInfo {..} = object ["icon" .= dependencyInfoIcon, "title" .= dependencyInfoTitle]
|
||||
|
||||
newtype ServiceAvailableRes = ServiceAvailableRes [ServiceRes]
|
||||
deriving (Generic)
|
||||
instance ToJSON ServiceAvailableRes
|
||||
instance ToContent ServiceAvailableRes where
|
||||
toContent = toContent . toJSON
|
||||
instance ToTypedContent ServiceAvailableRes where
|
||||
toTypedContent = toTypedContent . toJSON
|
||||
|
||||
newtype VersionLatestRes = VersionLatestRes (HM.HashMap PkgId (Maybe Version))
|
||||
deriving (Show, Generic)
|
||||
instance ToJSON VersionLatestRes
|
||||
instance ToContent VersionLatestRes where
|
||||
toContent = toContent . toJSON
|
||||
instance ToTypedContent VersionLatestRes where
|
||||
toTypedContent = toTypedContent . toJSON
|
||||
data OrderArrangement = ASC | DESC
|
||||
deriving (Eq, Show, Read)
|
||||
data ServiceListDefaults = ServiceListDefaults
|
||||
{ serviceListOrder :: OrderArrangement
|
||||
, serviceListPageLimit :: Int -- the number of items per page
|
||||
, serviceListPageNumber :: Int -- the page you are on
|
||||
, serviceListCategory :: Maybe CategoryTitle
|
||||
, serviceListQuery :: Text
|
||||
}
|
||||
deriving (Eq, Show, Read)
|
||||
data EosRes = EosRes
|
||||
{ eosResVersion :: Version
|
||||
, eosResHeadline :: Text
|
||||
, eosResReleaseNotes :: ReleaseNotes
|
||||
}
|
||||
deriving (Eq, Show, Generic)
|
||||
instance ToJSON EosRes where
|
||||
toJSON EosRes {..} =
|
||||
object ["version" .= eosResVersion, "headline" .= eosResHeadline, "release-notes" .= eosResReleaseNotes]
|
||||
instance ToContent EosRes where
|
||||
toContent = toContent . toJSON
|
||||
instance ToTypedContent EosRes where
|
||||
toTypedContent = toTypedContent . toJSON
|
||||
|
||||
data PackageVersion = PackageVersion
|
||||
{ packageVersionId :: PkgId
|
||||
, packageVersionVersion :: VersionRange
|
||||
}
|
||||
deriving Show
|
||||
instance FromJSON PackageVersion where
|
||||
parseJSON = withObject "package version" $ \o -> do
|
||||
packageVersionId <- o .: "id"
|
||||
packageVersionVersion <- o .: "version"
|
||||
pure PackageVersion { .. }
|
||||
import Control.Monad.Logger
|
||||
import Control.Monad.Reader.Has ( Has
|
||||
, ask
|
||||
)
|
||||
import Handler.Types.Marketplace
|
||||
|
||||
getCategoriesR :: Handler CategoryRes
|
||||
getCategoriesR = do
|
||||
@@ -277,8 +159,13 @@ getReleaseNotesR = do
|
||||
case lookup "id" getParameters of
|
||||
Nothing -> sendResponseStatus status400 (InvalidParamsE "get:id" "<MISSING>")
|
||||
Just package -> do
|
||||
(_, notes) <- fetchAllAppVersions (PkgId package)
|
||||
pure notes
|
||||
appConnPool <- appConnPool <$> getYesod
|
||||
versionRecords <- runDB $ fetchAllAppVersions appConnPool (PkgId package)
|
||||
pure $ constructReleaseNotesApiRes versionRecords
|
||||
where
|
||||
constructReleaseNotesApiRes :: [VersionRecord] -> ReleaseNotes
|
||||
constructReleaseNotesApiRes vers = do
|
||||
ReleaseNotes $ HM.fromList $ sortOn Down $ (versionRecordNumber &&& versionRecordReleaseNotes) <$> vers
|
||||
|
||||
getEosR :: Handler TypedContent
|
||||
getEosR = do
|
||||
@@ -308,6 +195,7 @@ getEosR = do
|
||||
void $ insertUnique (EosHash v t) -- lazily populate
|
||||
pure t
|
||||
|
||||
-- TODO refactor with conduit
|
||||
getVersionLatestR :: Handler VersionLatestRes
|
||||
getVersionLatestR = do
|
||||
getParameters <- reqGetParams <$> getRequest
|
||||
@@ -329,63 +217,54 @@ getVersionLatestR = do
|
||||
)
|
||||
$ HM.fromList packageList
|
||||
|
||||
getPackageListR :: Handler ServiceAvailableRes
|
||||
getPackageListR :: Handler PackageListRes
|
||||
getPackageListR = do
|
||||
osPredicate <- getOsVersionQuery <&> \case
|
||||
Nothing -> const True
|
||||
Just v -> flip satisfies v
|
||||
pkgIds <- getPkgIdsQuery
|
||||
filteredServices <- case pkgIds of
|
||||
filteredPackages <- case pkgIds of
|
||||
Nothing -> do
|
||||
-- query for all
|
||||
category <- getCategoryQuery
|
||||
page <- getPageQuery
|
||||
limit' <- getLimitQuery
|
||||
query <- T.strip . fromMaybe (serviceListQuery defaults) <$> lookupGetParam "query"
|
||||
query <- T.strip . fromMaybe (packageListQuery defaults) <$> lookupGetParam "query"
|
||||
runDB
|
||||
$ runConduit
|
||||
$ searchServices category query
|
||||
.| zipVersions
|
||||
.| filterOsCompatible osPredicate
|
||||
.| zipCategories
|
||||
-- empty list since there are no requested packages in this case
|
||||
.| filterLatestVersionFromSpec []
|
||||
.| filterPkgOsCompatible osPredicate
|
||||
-- pages start at 1 for some reason. TODO: make pages start at 0
|
||||
.| (dropC (limit' * (page - 1)) *> takeC limit')
|
||||
.| sinkList
|
||||
Just packages -> do
|
||||
Just packages' -> do
|
||||
-- for each item in list get best available from version range
|
||||
let vMap = (packageVersionId &&& packageVersionVersion) <$> packages
|
||||
let vMap = (packageReqId &&& packageReqVersion) <$> packages'
|
||||
runDB
|
||||
-- TODO could probably be better with sequenceConduits
|
||||
. runConduit
|
||||
$ getPkgData (packageVersionId <$> packages)
|
||||
$ getPkgData (packageReqId <$> packages')
|
||||
.| zipVersions
|
||||
.| mapC
|
||||
(\(a, vs) ->
|
||||
let spec = fromMaybe Any $ lookup (unPkgRecordKey $ entityKey a) vMap
|
||||
in (a, filter ((<|| spec) . versionRecordNumber . entityVal) vs)
|
||||
)
|
||||
.| filterOsCompatible osPredicate
|
||||
.| zipCategories
|
||||
.| filterLatestVersionFromSpec vMap
|
||||
.| filterPkgOsCompatible osPredicate
|
||||
.| sinkList
|
||||
let keys = unPkgRecordKey . entityKey . fst <$> filteredServices
|
||||
cats <- runDB $ fetchAppCategories keys
|
||||
let vers =
|
||||
filteredServices
|
||||
<&> first (unPkgRecordKey . entityKey)
|
||||
<&> second (sortOn Down . fmap (versionRecordNumber . entityVal))
|
||||
& HM.fromListWith (++)
|
||||
let packageMetadata = HM.intersectionWith (,) vers (categoryName <<$>> cats)
|
||||
serviceDetailResult <- mapConcurrently (flip (getServiceDetails packageMetadata) Nothing)
|
||||
(unPkgRecordKey . entityKey . fst <$> filteredServices)
|
||||
let services = snd $ partitionEithers serviceDetailResult
|
||||
pure $ ServiceAvailableRes services
|
||||
|
||||
-- NOTE: if a package's dependencies do not meet the system requirements, it is currently omitted from the list
|
||||
pkgsWithDependencies <- runDB $ mapConcurrently (getPackageDependencies osPredicate) filteredPackages
|
||||
PackageListRes <$> mapConcurrently constructPackageListApiRes pkgsWithDependencies
|
||||
|
||||
where
|
||||
defaults = ServiceListDefaults { serviceListOrder = DESC
|
||||
, serviceListPageLimit = 20
|
||||
, serviceListPageNumber = 1
|
||||
, serviceListCategory = Nothing
|
||||
, serviceListQuery = ""
|
||||
defaults = PackageListDefaults { packageListOrder = DESC
|
||||
, packageListPageLimit = 20
|
||||
, packageListPageNumber = 1
|
||||
, packageListCategory = Nothing
|
||||
, packageListQuery = ""
|
||||
}
|
||||
getPkgIdsQuery :: Handler (Maybe [PackageVersion])
|
||||
getPkgIdsQuery :: Handler (Maybe [PackageReq])
|
||||
getPkgIdsQuery = lookupGetParam "ids" >>= \case
|
||||
Nothing -> pure Nothing
|
||||
Just ids -> case eitherDecodeStrict (encodeUtf8 ids) of
|
||||
@@ -405,7 +284,7 @@ getPackageListR = do
|
||||
Just t -> pure $ Just t
|
||||
getPageQuery :: Handler Int
|
||||
getPageQuery = lookupGetParam "page" >>= \case
|
||||
Nothing -> pure $ serviceListPageNumber defaults
|
||||
Nothing -> pure $ packageListPageNumber defaults
|
||||
Just p -> case readMaybe p of
|
||||
Nothing -> do
|
||||
let e = InvalidParamsE "get:page" p
|
||||
@@ -416,7 +295,7 @@ getPackageListR = do
|
||||
_ -> t
|
||||
getLimitQuery :: Handler Int
|
||||
getLimitQuery = lookupGetParam "per-page" >>= \case
|
||||
Nothing -> pure $ serviceListPageLimit defaults
|
||||
Nothing -> pure $ packageListPageLimit defaults
|
||||
Just pp -> case readMaybe pp of
|
||||
Nothing -> do
|
||||
let e = InvalidParamsE "get:per-page" pp
|
||||
@@ -432,105 +311,35 @@ getPackageListR = do
|
||||
$logWarn (show e)
|
||||
sendResponseStatus status400 e
|
||||
Right v -> pure $ Just v
|
||||
|
||||
getServiceDetails :: (MonadIO m, MonadResource m, MonadReader r m, Has AppSettings r)
|
||||
=> (HM.HashMap PkgId ([Version], [CategoryTitle]))
|
||||
-> PkgId
|
||||
-> Maybe Version
|
||||
-> m (Either S9Error ServiceRes)
|
||||
getServiceDetails metadata pkg maybeVersion = runExceptT $ do
|
||||
settings <- ask
|
||||
packageMetadata <- case HM.lookup pkg metadata of
|
||||
Nothing -> liftEither . Left $ NotFoundE [i|#{pkg} not found.|]
|
||||
Just m -> pure m
|
||||
let domain = registryHostname settings
|
||||
version <- case maybeVersion of
|
||||
Nothing -> do
|
||||
-- grab first value, which will be the latest version
|
||||
case fst packageMetadata of
|
||||
[] -> liftEither . Left $ NotFoundE $ [i|No latest version found for #{pkg}|]
|
||||
x : _ -> pure x
|
||||
Just v -> pure v
|
||||
manifest <- flip runReaderT settings $ (snd <$> getManifest pkg version) >>= \bs ->
|
||||
runConduit $ bs .| CL.foldMap BS.fromStrict
|
||||
case eitherDecode manifest of
|
||||
Left _ -> liftEither . Left $ AssetParseE [i|#{pkg}:manifest|] (decodeUtf8 $ BS.toStrict manifest)
|
||||
Right m -> do
|
||||
let d = parMap rpar (mapDependencyMetadata domain metadata) (HM.toList $ serviceManifestDependencies m)
|
||||
pure $ ServiceRes { serviceResIcon = [i|https://#{domain}/package/icon/#{pkg}|]
|
||||
-- pass through raw JSON Value, we have checked its correct parsing above
|
||||
, serviceResManifest = unsafeFromJust . decode $ manifest
|
||||
, serviceResCategories = snd packageMetadata
|
||||
, serviceResInstructions = [i|https://#{domain}/package/instructions/#{pkg}|]
|
||||
, serviceResLicense = [i|https://#{domain}/package/license/#{pkg}|]
|
||||
, serviceResVersions = fst packageMetadata
|
||||
, serviceResDependencyInfo = HM.fromList $ snd $ partitionEithers d
|
||||
}
|
||||
|
||||
mapDependencyMetadata :: Text
|
||||
-> HM.HashMap PkgId ([Version], [CategoryTitle])
|
||||
-> (PkgId, ServiceDependencyInfo)
|
||||
-> Either S9Error (PkgId, DependencyInfo)
|
||||
mapDependencyMetadata domain metadata (appId, depInfo) = do
|
||||
depMetadata <- case HM.lookup appId metadata of
|
||||
Nothing -> Left $ NotFoundE [i|dependency metadata for #{appId} not found.|]
|
||||
Just m -> pure m
|
||||
-- get best version from VersionRange of dependency
|
||||
let satisfactory = filter (<|| serviceDependencyInfoVersion depInfo) (fst depMetadata)
|
||||
let best = getMax <$> foldMap (Just . Max) satisfactory
|
||||
version <- case best of
|
||||
Nothing -> Left $ NotFoundE $ [i|No satisfactory version for dependent package #{appId}|]
|
||||
Just v -> pure v
|
||||
pure
|
||||
( appId
|
||||
, DependencyInfo { dependencyInfoTitle = appId
|
||||
, dependencyInfoIcon = [i|https://#{domain}/package/icon/#{appId}?spec==#{version}|]
|
||||
}
|
||||
)
|
||||
|
||||
fetchAllAppVersions :: PkgId -> Handler ([VersionInfo], ReleaseNotes)
|
||||
fetchAllAppVersions appId = do
|
||||
entityAppVersions <- runDB $ P.selectList [VersionRecordPkgId P.==. PkgRecordKey appId] []
|
||||
let vers = entityVal <$> entityAppVersions
|
||||
let vv = mapSVersionToVersionInfo vers
|
||||
let mappedVersions = ReleaseNotes $ HM.fromList $ (\v -> (versionInfoVersion v, versionInfoReleaseNotes v)) <$> vv
|
||||
pure (sortOn (Down . versionInfoVersion) vv, mappedVersions)
|
||||
where
|
||||
mapSVersionToVersionInfo :: [VersionRecord] -> [VersionInfo]
|
||||
mapSVersionToVersionInfo sv = do
|
||||
(\v -> VersionInfo { versionInfoVersion = versionRecordNumber v
|
||||
, versionInfoReleaseNotes = versionRecordReleaseNotes v
|
||||
, versionInfoDependencies = HM.empty
|
||||
, versionInfoOsVersion = versionRecordOsVersion v
|
||||
, versionInfoInstallAlert = Nothing
|
||||
}
|
||||
)
|
||||
<$> sv
|
||||
|
||||
|
||||
fetchLatestApp :: MonadIO m => PkgId -> ReaderT SqlBackend m (Maybe (P.Entity PkgRecord, P.Entity VersionRecord))
|
||||
fetchLatestApp appId = fmap headMay . sortResults . select $ do
|
||||
(service :& version) <-
|
||||
from
|
||||
$ table @PkgRecord
|
||||
`innerJoin` table @VersionRecord
|
||||
`on` (\(service :& version) -> service ^. PkgRecordId ==. version ^. VersionRecordPkgId)
|
||||
where_ (service ^. PkgRecordId ==. val (PkgRecordKey appId))
|
||||
pure (service, version)
|
||||
where sortResults = fmap $ sortOn (Down . versionRecordNumber . entityVal . snd)
|
||||
|
||||
|
||||
fetchAppCategories :: MonadIO m => [PkgId] -> ReaderT SqlBackend m (HM.HashMap PkgId [Category])
|
||||
fetchAppCategories appIds = do
|
||||
raw <- select $ do
|
||||
(sc :& app :& cat) <-
|
||||
from
|
||||
$ table @PkgCategory
|
||||
`innerJoin` table @PkgRecord
|
||||
`on` (\(sc :& app) -> sc ^. PkgCategoryPkgId ==. app ^. PkgRecordId)
|
||||
`innerJoin` table @Category
|
||||
`on` (\(sc :& _ :& cat) -> sc ^. PkgCategoryCategoryId ==. cat ^. CategoryId)
|
||||
where_ (sc ^. PkgCategoryPkgId `in_` valList (PkgRecordKey <$> appIds))
|
||||
pure (app ^. PkgRecordId, cat)
|
||||
let ls = fmap (first (unPkgRecordKey . unValue) . second (pure . entityVal)) raw
|
||||
pure $ HM.fromListWith (++) ls
|
||||
getPackageDependencies :: (MonadIO m, MonadLogger m) => (Version -> Bool) -> PackageMetadata -> ReaderT SqlBackend m (Key PkgRecord, [Category], [Version], Version, [(Key PkgRecord, Text, Version)])
|
||||
getPackageDependencies osPredicate PackageMetadata { packageMetadataPkgRecord = pkg, packageMetadataPkgVersionRecords = pkgVersions, packageMetadataPkgCategories = pkgCategories, packageMetadataPkgVersion = pkgVersion} = do
|
||||
let pkgId = entityKey pkg
|
||||
let pkgVersions' = versionRecordNumber . entityVal <$> pkgVersions
|
||||
let pkgCategories' = entityVal <$> pkgCategories
|
||||
pkgDepInfo <- getPkgDependencyData pkgId pkgVersion
|
||||
pkgDepInfoWithVersions <- traverse zipDependencyVersions pkgDepInfo
|
||||
let compatiblePkgDepInfo = fmap (filterDependencyOsCompatible osPredicate) pkgDepInfoWithVersions
|
||||
res <- catMaybes <$> traverse filterDependencyBestVersion compatiblePkgDepInfo
|
||||
pure $ (pkgId, pkgCategories', pkgVersions', pkgVersion, res)
|
||||
constructPackageListApiRes :: (MonadResource m, MonadReader r m, Has AppSettings r) => (Key PkgRecord, [Category], [Version], Version, [(Key PkgRecord, Text, Version)]) -> m PackageRes
|
||||
constructPackageListApiRes (pkgKey, pkgCategories, pkgVersions, pkgVersion, dependencies) = do
|
||||
settings <- ask
|
||||
let pkgId = unPkgRecordKey pkgKey
|
||||
let domain = registryHostname settings
|
||||
manifest <- flip runReaderT settings $ (snd <$> getManifest pkgId pkgVersion) >>= \bs ->
|
||||
runConduit $ bs .| CL.foldMap BS.fromStrict
|
||||
pure $ PackageRes { packageResIcon = [i|https://#{domain}/package/icon/#{pkgId}|]
|
||||
-- pass through raw JSON Value, we have checked its correct parsing above
|
||||
, packageResManifest = unsafeFromJust . decode $ manifest
|
||||
, packageResCategories = categoryName <$> pkgCategories
|
||||
, packageResInstructions = [i|https://#{domain}/package/instructions/#{pkgId}|]
|
||||
, packageResLicense = [i|https://#{domain}/package/license/#{pkgId}|]
|
||||
, packageResVersions = pkgVersions
|
||||
, packageResDependencies = HM.fromList $ constructDependenciesApiRes domain dependencies
|
||||
}
|
||||
constructDependenciesApiRes :: Text
|
||||
-> [(Key PkgRecord, Text, Version)]
|
||||
-> [(PkgId, DependencyRes)]
|
||||
constructDependenciesApiRes domain deps = fmap (\(depKey, depTitle, depVersion) -> do
|
||||
let depId = unPkgRecordKey depKey
|
||||
(depId, DependencyRes { dependencyResTitle = depTitle, dependencyResIcon = [i|https://#{domain}/package/icon/#{depId}?spec==#{depVersion}|]})) deps
|
||||
|
||||
141
src/Handler/Types/Marketplace.hs
Normal file
141
src/Handler/Types/Marketplace.hs
Normal file
@@ -0,0 +1,141 @@
|
||||
{-# LANGUAGE RecordWildCards #-}
|
||||
{-# LANGUAGE DeriveGeneric #-}
|
||||
|
||||
module Handler.Types.Marketplace where
|
||||
import Data.Aeson
|
||||
import qualified Data.HashMap.Internal.Strict as HM
|
||||
import Lib.Types.AppIndex ( PkgId )
|
||||
import Lib.Types.Category ( CategoryTitle )
|
||||
import Lib.Types.Emver ( Version
|
||||
, VersionRange
|
||||
)
|
||||
import Model ( Category
|
||||
, PkgDependency
|
||||
, PkgRecord
|
||||
, VersionRecord
|
||||
)
|
||||
import Startlude
|
||||
import Yesod
|
||||
|
||||
|
||||
type URL = Text
|
||||
newtype CategoryRes = CategoryRes {
|
||||
categories :: [CategoryTitle]
|
||||
} deriving (Show, Generic)
|
||||
instance ToJSON CategoryRes
|
||||
instance ToContent CategoryRes where
|
||||
toContent = toContent . toJSON
|
||||
instance ToTypedContent CategoryRes where
|
||||
toTypedContent = toTypedContent . toJSON
|
||||
data PackageRes = PackageRes
|
||||
{ packageResIcon :: URL
|
||||
, packageResManifest :: Data.Aeson.Value -- PackageManifest
|
||||
, packageResCategories :: [CategoryTitle]
|
||||
, packageResInstructions :: URL
|
||||
, packageResLicense :: URL
|
||||
, packageResVersions :: [Version]
|
||||
, packageResDependencies :: HM.HashMap PkgId DependencyRes
|
||||
}
|
||||
deriving (Show, Generic)
|
||||
newtype ReleaseNotes = ReleaseNotes { unReleaseNotes :: HM.HashMap Version Text }
|
||||
deriving (Eq, Show)
|
||||
instance ToJSON ReleaseNotes where
|
||||
toJSON ReleaseNotes {..} = object [ t .= v | (k, v) <- HM.toList unReleaseNotes, let (String t) = toJSON k ]
|
||||
instance ToContent ReleaseNotes where
|
||||
toContent = toContent . toJSON
|
||||
instance ToTypedContent ReleaseNotes where
|
||||
toTypedContent = toTypedContent . toJSON
|
||||
instance ToJSON PackageRes where
|
||||
toJSON PackageRes {..} = object
|
||||
[ "icon" .= packageResIcon
|
||||
, "license" .= packageResLicense
|
||||
, "instructions" .= packageResInstructions
|
||||
, "manifest" .= packageResManifest
|
||||
, "categories" .= packageResCategories
|
||||
, "versions" .= packageResVersions
|
||||
, "dependency-metadata" .= packageResDependencies
|
||||
]
|
||||
instance FromJSON PackageRes where
|
||||
parseJSON = withObject "PackageRes" $ \o -> do
|
||||
packageResIcon <- o .: "icon"
|
||||
packageResLicense <- o .: "license"
|
||||
packageResInstructions <- o .: "instructions"
|
||||
packageResManifest <- o .: "manifest"
|
||||
packageResCategories <- o .: "categories"
|
||||
packageResVersions <- o .: "versions"
|
||||
packageResDependencies <- o .: "dependency-metadata"
|
||||
pure PackageRes { .. }
|
||||
data DependencyRes = DependencyRes
|
||||
{ dependencyResTitle :: Text -- TODO switch to `Text` to display actual title in Marketplace. Confirm with FE that this will not break loading. Perhaps return title and id?
|
||||
, dependencyResIcon :: URL
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
instance ToJSON DependencyRes where
|
||||
toJSON DependencyRes {..} = object ["icon" .= dependencyResIcon, "title" .= dependencyResTitle]
|
||||
instance FromJSON DependencyRes where
|
||||
parseJSON = withObject "DependencyRes" $ \o -> do
|
||||
dependencyResIcon <- o .: "icon"
|
||||
dependencyResTitle <- o .: "title"
|
||||
pure DependencyRes { .. }
|
||||
newtype PackageListRes = PackageListRes [PackageRes]
|
||||
deriving (Generic)
|
||||
instance ToJSON PackageListRes
|
||||
instance ToContent PackageListRes where
|
||||
toContent = toContent . toJSON
|
||||
instance ToTypedContent PackageListRes where
|
||||
toTypedContent = toTypedContent . toJSON
|
||||
|
||||
newtype VersionLatestRes = VersionLatestRes (HM.HashMap PkgId (Maybe Version))
|
||||
deriving (Show, Generic)
|
||||
instance ToJSON VersionLatestRes
|
||||
instance ToContent VersionLatestRes where
|
||||
toContent = toContent . toJSON
|
||||
instance ToTypedContent VersionLatestRes where
|
||||
toTypedContent = toTypedContent . toJSON
|
||||
data OrderArrangement = ASC | DESC
|
||||
deriving (Eq, Show, Read)
|
||||
data PackageListDefaults = PackageListDefaults
|
||||
{ packageListOrder :: OrderArrangement
|
||||
, packageListPageLimit :: Int -- the number of items per page
|
||||
, packageListPageNumber :: Int -- the page you are on
|
||||
, packageListCategory :: Maybe CategoryTitle
|
||||
, packageListQuery :: Text
|
||||
}
|
||||
deriving (Eq, Show, Read)
|
||||
data EosRes = EosRes
|
||||
{ eosResVersion :: Version
|
||||
, eosResHeadline :: Text
|
||||
, eosResReleaseNotes :: ReleaseNotes
|
||||
}
|
||||
deriving (Eq, Show, Generic)
|
||||
instance ToJSON EosRes where
|
||||
toJSON EosRes {..} =
|
||||
object ["version" .= eosResVersion, "headline" .= eosResHeadline, "release-notes" .= eosResReleaseNotes]
|
||||
instance ToContent EosRes where
|
||||
toContent = toContent . toJSON
|
||||
instance ToTypedContent EosRes where
|
||||
toTypedContent = toTypedContent . toJSON
|
||||
|
||||
data PackageReq = PackageReq
|
||||
{ packageReqId :: PkgId
|
||||
, packageReqVersion :: VersionRange
|
||||
}
|
||||
deriving Show
|
||||
instance FromJSON PackageReq where
|
||||
parseJSON = withObject "package version" $ \o -> do
|
||||
packageReqId <- o .: "id"
|
||||
packageReqVersion <- o .: "version"
|
||||
pure PackageReq { .. }
|
||||
data PackageMetadata = PackageMetadata
|
||||
{ packageMetadataPkgRecord :: Entity PkgRecord
|
||||
, packageMetadataPkgVersionRecords :: [Entity VersionRecord]
|
||||
, packageMetadataPkgCategories :: [Entity Category]
|
||||
, packageMetadataPkgVersion :: Version
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
data PackageDependencyMetadata = PackageDependencyMetadata
|
||||
{ packageDependencyMetadataPkgDependencyRecord :: Entity PkgDependency
|
||||
, packageDependencyMetadataDepPkgRecord :: Entity PkgRecord
|
||||
, packageDependencyMetadataDepVersions :: [Entity VersionRecord]
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
@@ -31,19 +31,29 @@ import qualified Data.Attoparsec.Text as Atto
|
||||
import Data.ByteString ( readFile
|
||||
, writeFile
|
||||
)
|
||||
import qualified Data.HashMap.Strict as HM
|
||||
import Data.String.Interpolate.IsString
|
||||
( i )
|
||||
import qualified Data.Text as T
|
||||
import Data.Time ( getCurrentTime )
|
||||
import Database.Esqueleto.Experimental
|
||||
( ConnectionPool
|
||||
, insertUnique
|
||||
, runSqlPool
|
||||
)
|
||||
import Lib.Error ( S9Error(NotFoundE) )
|
||||
import qualified Lib.External.AppMgr as AppMgr
|
||||
import Lib.Types.AppIndex ( PkgId(..)
|
||||
, ServiceManifest(serviceManifestIcon)
|
||||
import Lib.Types.AppIndex ( PackageManifest(..)
|
||||
, PkgId(..)
|
||||
, packageDependencyVersion
|
||||
, packageManifestDependencies
|
||||
)
|
||||
import Lib.Types.Emver ( Version
|
||||
, VersionRange
|
||||
, parseVersion
|
||||
, satisfies
|
||||
)
|
||||
import Model
|
||||
import Startlude ( ($)
|
||||
, (&&)
|
||||
, (.)
|
||||
@@ -64,12 +74,15 @@ import Startlude ( ($)
|
||||
, SomeException(..)
|
||||
, filter
|
||||
, find
|
||||
, first
|
||||
, for_
|
||||
, fst
|
||||
, headMay
|
||||
, not
|
||||
, partitionEithers
|
||||
, pure
|
||||
, show
|
||||
, snd
|
||||
, sortOn
|
||||
, throwIO
|
||||
, void
|
||||
@@ -111,7 +124,6 @@ import Yesod.Core.Content ( typeGif
|
||||
, typeSvg
|
||||
)
|
||||
import Yesod.Core.Types ( ContentType )
|
||||
|
||||
data ManifestParseException = ManifestParseException FilePath
|
||||
deriving Show
|
||||
instance Exception ManifestParseException
|
||||
@@ -143,9 +155,27 @@ getBestVersion :: (MonadIO m, MonadReader r m, Has PkgRepo r, MonadLogger m)
|
||||
-> m (Maybe Version)
|
||||
getBestVersion pkg spec = headMay . sortOn Down <$> getViableVersions pkg spec
|
||||
|
||||
loadPkgDependencies :: MonadUnliftIO m => ConnectionPool -> PackageManifest -> m ()
|
||||
loadPkgDependencies appConnPool manifest = do
|
||||
let pkgId = packageManifestId manifest
|
||||
let pkgVersion = packageManifestVersion manifest
|
||||
let deps = packageManifestDependencies manifest
|
||||
time <- liftIO getCurrentTime
|
||||
let deps' = first PkgRecordKey <$> HM.toList deps
|
||||
for_
|
||||
deps'
|
||||
(\d ->
|
||||
(runSqlPool
|
||||
( insertUnique
|
||||
$ PkgDependency time (PkgRecordKey pkgId) pkgVersion (fst d) (packageDependencyVersion . snd $ d)
|
||||
)
|
||||
appConnPool
|
||||
)
|
||||
)
|
||||
|
||||
-- extract all package assets into their own respective files
|
||||
extractPkg :: (MonadUnliftIO m, MonadReader r m, Has PkgRepo r, MonadLoggerIO m) => FilePath -> m ()
|
||||
extractPkg fp = handle @_ @SomeException cleanup $ do
|
||||
extractPkg :: (MonadUnliftIO m, MonadReader r m, Has PkgRepo r, MonadLoggerIO m) => ConnectionPool -> FilePath -> m ()
|
||||
extractPkg pool fp = handle @_ @SomeException cleanup $ do
|
||||
$logInfo [i|Extracting package: #{fp}|]
|
||||
PkgRepo { pkgRepoAppMgrBin = appmgr } <- ask
|
||||
let pkgRoot = takeDirectory fp
|
||||
@@ -163,11 +193,12 @@ extractPkg fp = handle @_ @SomeException cleanup $ do
|
||||
liftIO . throwIO $ ManifestParseException (pkgRoot </> "manifest.json")
|
||||
Right manifest -> do
|
||||
wait iconTask
|
||||
let iconDest = "icon" <.> case serviceManifestIcon manifest of
|
||||
let iconDest = "icon" <.> case packageManifestIcon manifest of
|
||||
Nothing -> "png"
|
||||
Just x -> case takeExtension (T.unpack x) of
|
||||
"" -> "png"
|
||||
other -> other
|
||||
loadPkgDependencies pool manifest
|
||||
liftIO $ renameFile (pkgRoot </> "icon.tmp") (pkgRoot </> iconDest)
|
||||
hash <- wait pkgHashTask
|
||||
liftIO $ writeFile (pkgRoot </> "hash.bin") hash
|
||||
@@ -183,8 +214,8 @@ extractPkg fp = handle @_ @SomeException cleanup $ do
|
||||
mapConcurrently_ (removeFile . (pkgRoot </>)) toRemove
|
||||
throwIO e
|
||||
|
||||
watchPkgRepoRoot :: (MonadUnliftIO m, MonadReader r m, Has PkgRepo r, MonadLoggerIO m) => m (IO Bool)
|
||||
watchPkgRepoRoot = do
|
||||
watchPkgRepoRoot :: (MonadUnliftIO m, MonadReader r m, Has PkgRepo r, MonadLoggerIO m) => ConnectionPool -> m (IO Bool)
|
||||
watchPkgRepoRoot pool = do
|
||||
$logInfo "Starting FSNotify Watch Manager"
|
||||
root <- asks pkgRepoFileRoot
|
||||
runInIO <- askRunInIO
|
||||
@@ -193,7 +224,8 @@ watchPkgRepoRoot = do
|
||||
stop <- watchTree watchManager root onlyAdded $ \evt -> do
|
||||
let pkg = eventPath evt
|
||||
-- TODO: validate that package path is an actual s9pk and is in a correctly conforming path.
|
||||
void . forkIO $ runInIO (extractPkg pkg)
|
||||
void . forkIO $ runInIO $ do
|
||||
extractPkg pool pkg
|
||||
takeMVar box
|
||||
stop
|
||||
pure $ tryPutMVar box ()
|
||||
|
||||
@@ -8,6 +8,7 @@ module Lib.Types.AppIndex where
|
||||
|
||||
import Startlude
|
||||
|
||||
-- NOTE: leave eitherDecode for inline test evaluation below
|
||||
import Control.Monad ( fail )
|
||||
import Data.Aeson ( (.:)
|
||||
, (.:?)
|
||||
@@ -75,43 +76,43 @@ data VersionInfo = VersionInfo
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
|
||||
data ServiceDependencyInfo = ServiceDependencyInfo
|
||||
{ serviceDependencyInfoOptional :: Maybe Text
|
||||
, serviceDependencyInfoVersion :: VersionRange
|
||||
, serviceDependencyInfoDescription :: Maybe Text
|
||||
, serviceDependencyInfoCritical :: Bool
|
||||
data PackageDependency = PackageDependency
|
||||
{ packageDependencyOptional :: Maybe Text
|
||||
, packageDependencyVersion :: VersionRange
|
||||
, packageDependencyDescription :: Maybe Text
|
||||
, packageDependencyCritical :: Bool
|
||||
}
|
||||
deriving Show
|
||||
instance FromJSON ServiceDependencyInfo where
|
||||
instance FromJSON PackageDependency where
|
||||
parseJSON = withObject "service dependency info" $ \o -> do
|
||||
serviceDependencyInfoOptional <- o .:? "optional"
|
||||
serviceDependencyInfoVersion <- o .: "version"
|
||||
serviceDependencyInfoDescription <- o .:? "description"
|
||||
serviceDependencyInfoCritical <- o .: "critical"
|
||||
pure ServiceDependencyInfo { .. }
|
||||
packageDependencyOptional <- o .:? "optional"
|
||||
packageDependencyVersion <- o .: "version"
|
||||
packageDependencyDescription <- o .:? "description"
|
||||
packageDependencyCritical <- o .: "critical"
|
||||
pure PackageDependency { .. }
|
||||
data ServiceAlert = INSTALL | UNINSTALL | RESTORE | START | STOP
|
||||
deriving (Show, Eq, Generic, Hashable, Read)
|
||||
data ServiceManifest = ServiceManifest
|
||||
{ serviceManifestId :: !PkgId
|
||||
, serviceManifestTitle :: !Text
|
||||
, serviceManifestVersion :: !Version
|
||||
, serviceManifestDescriptionLong :: !Text
|
||||
, serviceManifestDescriptionShort :: !Text
|
||||
, serviceManifestReleaseNotes :: !Text
|
||||
, serviceManifestIcon :: !(Maybe Text)
|
||||
, serviceManifestAlerts :: !(HM.HashMap ServiceAlert (Maybe Text))
|
||||
, serviceManifestDependencies :: !(HM.HashMap PkgId ServiceDependencyInfo)
|
||||
data PackageManifest = PackageManifest
|
||||
{ packageManifestId :: !PkgId
|
||||
, packageManifestTitle :: !Text
|
||||
, packageManifestVersion :: !Version
|
||||
, packageManifestDescriptionLong :: !Text
|
||||
, packageManifestDescriptionShort :: !Text
|
||||
, packageManifestReleaseNotes :: !Text
|
||||
, packageManifestIcon :: !(Maybe Text)
|
||||
, packageManifestAlerts :: !(HM.HashMap ServiceAlert (Maybe Text))
|
||||
, packageManifestDependencies :: !(HM.HashMap PkgId PackageDependency)
|
||||
}
|
||||
deriving Show
|
||||
instance FromJSON ServiceManifest where
|
||||
instance FromJSON PackageManifest where
|
||||
parseJSON = withObject "service manifest" $ \o -> do
|
||||
serviceManifestId <- o .: "id"
|
||||
serviceManifestTitle <- o .: "title"
|
||||
serviceManifestVersion <- o .: "version"
|
||||
serviceManifestDescriptionLong <- o .: "description" >>= (.: "long")
|
||||
serviceManifestDescriptionShort <- o .: "description" >>= (.: "short")
|
||||
serviceManifestIcon <- o .: "assets" >>= (.: "icon")
|
||||
serviceManifestReleaseNotes <- o .: "release-notes"
|
||||
packageManifestId <- o .: "id"
|
||||
packageManifestTitle <- o .: "title"
|
||||
packageManifestVersion <- o .: "version"
|
||||
packageManifestDescriptionLong <- o .: "description" >>= (.: "long")
|
||||
packageManifestDescriptionShort <- o .: "description" >>= (.: "short")
|
||||
packageManifestIcon <- o .: "assets" >>= (.: "icon")
|
||||
packageManifestReleaseNotes <- o .: "release-notes"
|
||||
alerts <- o .: "alerts"
|
||||
a <- for (HM.toList alerts) $ \(key, value) -> do
|
||||
alertType <- case readMaybe $ T.toUpper key of
|
||||
@@ -119,12 +120,12 @@ instance FromJSON ServiceManifest where
|
||||
Just t -> pure t
|
||||
alertDesc <- parseJSON value
|
||||
pure (alertType, alertDesc)
|
||||
let serviceManifestAlerts = HM.fromList a
|
||||
serviceManifestDependencies <- o .: "dependencies"
|
||||
pure ServiceManifest { .. }
|
||||
let packageManifestAlerts = HM.fromList a
|
||||
packageManifestDependencies <- o .: "dependencies"
|
||||
pure PackageManifest { .. }
|
||||
|
||||
-- >>> eitherDecode testManifest :: Either String ServiceManifest
|
||||
-- Right (ServiceManifest {serviceManifestId = embassy-pages, serviceManifestTitle = "Embassy Pages", serviceManifestVersion = 0.1.3, serviceManifestDescriptionLong = "Embassy Pages is a simple web server that uses directories inside File Browser to serve Tor websites.", serviceManifestDescriptionShort = "Create Tor websites, hosted on your Embassy.", serviceManifestReleaseNotes = "Upgrade to EmbassyOS v0.3.0", serviceManifestIcon = Just "icon.png", serviceManifestAlerts = fromList [(INSTALL,Nothing),(UNINSTALL,Nothing),(STOP,Nothing),(RESTORE,Nothing),(START,Nothing)], serviceManifestDependencies = fromList [(filebrowser,ServiceDependencyInfo {serviceDependencyInfoOptional = Nothing, serviceDependencyInfoVersion = >=2.14.1.1 <3.0.0, serviceDependencyInfoDescription = Just "Used to upload files to serve.", serviceDependencyInfoCritical = False})]})
|
||||
-- >>> eitherDecode testManifest :: Either String PackageManifest
|
||||
-- Right (PackageManifest {packageManifestId = embassy-pages, packageManifestTitle = "Embassy Pages", packageManifestVersion = 0.1.3, packageManifestDescriptionLong = "Embassy Pages is a simple web server that uses directories inside File Browser to serve Tor websites.", packageManifestDescriptionShort = "Create Tor websites, hosted on your Embassy.", packageManifestReleaseNotes = "Upgrade to EmbassyOS v0.3.0", packageManifestIcon = Just "icon.png", packageManifestAlerts = fromList [(INSTALL,Nothing),(UNINSTALL,Nothing),(STOP,Nothing),(RESTORE,Nothing),(START,Nothing)], packageManifestDependencies = fromList [(filebrowser,PackageDependency {packageDependencyOptional = Nothing, packageDependencyVersion = >=2.14.1.1 <3.0.0, packageDependencyDescription = Just "Used to upload files to serve.", packageDependencyCritical = False})]})
|
||||
testManifest :: BS.ByteString
|
||||
testManifest = [i|{
|
||||
"id": "embassy-pages",
|
||||
|
||||
10
src/Model.hs
10
src/Model.hs
@@ -91,4 +91,14 @@ ErrorLogRecord
|
||||
message Text
|
||||
incidents Word32
|
||||
UniqueLogRecord epoch commitHash sourceFile line target level message
|
||||
|
||||
PkgDependency
|
||||
createdAt UTCTime
|
||||
pkgId PkgRecordId
|
||||
pkgVersion Version
|
||||
depId PkgRecordId
|
||||
depVersionRange VersionRange
|
||||
UniquePkgDepVersion pkgId pkgVersion depId
|
||||
deriving Eq
|
||||
deriving Show
|
||||
|]
|
||||
|
||||
@@ -1,20 +1,51 @@
|
||||
{-# LANGUAGE FlexibleContexts #-}
|
||||
{-# LANGUAGE TemplateHaskell #-}
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Util.Shared where
|
||||
|
||||
import Startlude hiding ( Handler )
|
||||
import Startlude hiding ( Any
|
||||
, Handler
|
||||
, yield
|
||||
)
|
||||
|
||||
import qualified Data.Text as T
|
||||
import Network.HTTP.Types
|
||||
import Yesod.Core
|
||||
|
||||
import Conduit ( ConduitT
|
||||
, awaitForever
|
||||
, yield
|
||||
)
|
||||
import Control.Monad.Reader.Has ( Has )
|
||||
import Data.Semigroup ( Max(Max)
|
||||
, getMax
|
||||
)
|
||||
import Data.String.Interpolate.IsString
|
||||
( i )
|
||||
import Database.Esqueleto.Experimental
|
||||
( Entity
|
||||
, Key
|
||||
, entityKey
|
||||
, entityVal
|
||||
)
|
||||
import Foundation
|
||||
import GHC.List ( lookup )
|
||||
import Handler.Types.Marketplace ( PackageDependencyMetadata(..)
|
||||
, PackageMetadata(..)
|
||||
)
|
||||
import Lib.PkgRepository ( PkgRepo
|
||||
, getHash
|
||||
)
|
||||
import Lib.Types.AppIndex ( PkgId )
|
||||
import Lib.Types.Emver
|
||||
import Model ( Category
|
||||
, Key(unPkgRecordKey)
|
||||
, PkgDependency(pkgDependencyDepId, pkgDependencyDepVersionRange)
|
||||
, PkgRecord(pkgRecordTitle)
|
||||
, VersionRecord(versionRecordNumber, versionRecordOsVersion)
|
||||
, pkgDependencyPkgId
|
||||
)
|
||||
|
||||
getVersionSpecFromQuery :: Handler VersionRange
|
||||
getVersionSpecFromQuery = do
|
||||
@@ -32,3 +63,62 @@ orThrow :: MonadHandler m => m (Maybe a) -> m a -> m a
|
||||
orThrow action other = action >>= \case
|
||||
Nothing -> other
|
||||
Just x -> pure x
|
||||
|
||||
|
||||
filterPkgOsCompatible :: Monad m => (Version -> Bool) -> ConduitT PackageMetadata PackageMetadata m ()
|
||||
filterPkgOsCompatible p =
|
||||
awaitForever
|
||||
$ \PackageMetadata { packageMetadataPkgRecord = pkg, packageMetadataPkgVersionRecords = versions, packageMetadataPkgCategories = cats, packageMetadataPkgVersion = requestedVersion } ->
|
||||
do
|
||||
let compatible = filter (p . versionRecordOsVersion . entityVal) versions
|
||||
when (not $ null compatible) $ yield PackageMetadata { packageMetadataPkgRecord = pkg
|
||||
, packageMetadataPkgVersionRecords = compatible
|
||||
, packageMetadataPkgCategories = cats
|
||||
, packageMetadataPkgVersion = requestedVersion
|
||||
}
|
||||
|
||||
filterDependencyOsCompatible :: (Version -> Bool) -> PackageDependencyMetadata -> PackageDependencyMetadata
|
||||
filterDependencyOsCompatible p PackageDependencyMetadata { packageDependencyMetadataPkgDependencyRecord = pkgDeps, packageDependencyMetadataDepPkgRecord = pkg, packageDependencyMetadataDepVersions = depVersions }
|
||||
= do
|
||||
let compatible = filter (p . versionRecordOsVersion . entityVal) depVersions
|
||||
PackageDependencyMetadata { packageDependencyMetadataPkgDependencyRecord = pkgDeps
|
||||
, packageDependencyMetadataDepPkgRecord = pkg
|
||||
, packageDependencyMetadataDepVersions = compatible
|
||||
}
|
||||
|
||||
filterLatestVersionFromSpec :: (Monad m, MonadLogger m)
|
||||
=> [(PkgId, VersionRange)]
|
||||
-> ConduitT
|
||||
(Entity PkgRecord, [Entity VersionRecord], [Entity Category])
|
||||
PackageMetadata
|
||||
m
|
||||
()
|
||||
filterLatestVersionFromSpec versionMap = awaitForever $ \(a, vs, cats) -> do
|
||||
let pkgId = entityKey a
|
||||
-- if no packages are specified, the VersionRange is implicitly `*`
|
||||
let spec = fromMaybe Any $ lookup (unPkgRecordKey $ entityKey a) versionMap
|
||||
case headMay . sortOn Down $ filter (`satisfies` spec) $ fmap (versionRecordNumber . entityVal) vs of
|
||||
Nothing -> $logInfo [i|No version for #{pkgId} satisfying #{spec}|]
|
||||
Just v -> yield $ PackageMetadata { packageMetadataPkgRecord = a
|
||||
, packageMetadataPkgVersionRecords = vs
|
||||
, packageMetadataPkgCategories = cats
|
||||
, packageMetadataPkgVersion = v
|
||||
}
|
||||
|
||||
-- get best version of the dependency based on what is specified in the db (ie. what is specified in the manifest for the package)
|
||||
filterDependencyBestVersion :: MonadLogger m => PackageDependencyMetadata -> m (Maybe (Key PkgRecord, Text, Version))
|
||||
filterDependencyBestVersion PackageDependencyMetadata { packageDependencyMetadataPkgDependencyRecord = pkgDepRecord, packageDependencyMetadataDepPkgRecord = depRecord, packageDependencyMetadataDepVersions = depVersions }
|
||||
= do
|
||||
-- get best version from VersionRange of dependency
|
||||
let pkgId = pkgDependencyPkgId $ entityVal pkgDepRecord
|
||||
let depId = pkgDependencyDepId $ entityVal pkgDepRecord
|
||||
let depTitle = pkgRecordTitle $ entityVal depRecord
|
||||
let satisfactory = filter (<|| (pkgDependencyDepVersionRange $ entityVal pkgDepRecord))
|
||||
(versionRecordNumber . entityVal <$> depVersions)
|
||||
case getMax <$> foldMap (Just . Max) satisfactory of
|
||||
-- QUESTION is this an acceptable transformation here? These are the only values that we care about after this filter.
|
||||
Just bestVersion -> pure $ Just (depId, depTitle, bestVersion)
|
||||
Nothing -> do
|
||||
$logInfo [i|No satisfactory version of #{depId} for dependent package #{pkgId}|]
|
||||
-- TODO it would be better if we could return the requirements for display
|
||||
pure Nothing
|
||||
|
||||
@@ -1,78 +1,140 @@
|
||||
{-# LANGUAGE TypeFamilies #-}
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Handler.AppSpec
|
||||
( spec
|
||||
)
|
||||
where
|
||||
) where
|
||||
|
||||
import Startlude
|
||||
import Database.Persist.Sql
|
||||
import Data.Maybe
|
||||
import Database.Persist.Sql
|
||||
import Startlude
|
||||
|
||||
import TestImport
|
||||
import Data.Aeson
|
||||
import Data.Aeson.Types ( parseEither )
|
||||
import Data.String.Interpolate.IsString
|
||||
( i )
|
||||
import Handler.Types.Marketplace ( PackageRes(packageResDependencies, packageResManifest) )
|
||||
import Lib.Types.AppIndex
|
||||
import Model
|
||||
import Seed
|
||||
import TestImport
|
||||
|
||||
spec :: Spec
|
||||
spec = do
|
||||
describe "GET /package/index" $ withApp $ it "returns list of apps" $ do
|
||||
describe "GET /package/index" $ withApp $ it "returns list of packages" $ do
|
||||
_ <- seedBitcoinLndStack
|
||||
request $ do
|
||||
setMethod "GET"
|
||||
setUrl ("/package/index" :: Text)
|
||||
bodyContains "embassy-pages"
|
||||
bodyContains "version: 0.1.3"
|
||||
statusIs 200
|
||||
describe "GET /package/:appId with unknown version spec for embassy-pages"
|
||||
(res :: [PackageRes]) <- requireJSONResponse
|
||||
assertEq "response should have two packages" (length res) 3
|
||||
describe "GET /package/index?ids" $ withApp $ it "returns list of packages at specified version" $ do
|
||||
_ <- seedBitcoinLndStack
|
||||
request $ do
|
||||
setMethod "GET"
|
||||
setUrl ("/package/index?ids=[{\"id\":\"bitcoind\",\"version\":\"=0.21.1.2\"}]" :: Text)
|
||||
statusIs 200
|
||||
(res :: [PackageRes]) <- requireJSONResponse
|
||||
assertEq "response should have one package" (length res) 1
|
||||
let pkg = fromJust $ head res
|
||||
(manifest :: PackageManifest) <- either (\e -> panic [i|failed to parse package manifest: #{e}|])
|
||||
pure
|
||||
(parseEither parseJSON $ packageResManifest pkg)
|
||||
assertEq "manifest id should be bitcoind" (packageManifestId manifest) "bitcoind"
|
||||
describe "GET /package/index?ids"
|
||||
$ withApp
|
||||
$ it "fails to get unknown app"
|
||||
$ it "returns list of packages and dependencies at specified version"
|
||||
$ do
|
||||
_ <- seedBitcoinLndStack
|
||||
request $ do
|
||||
setMethod "GET"
|
||||
setUrl ("/package/embassy-pages.s9pk?spec=0.1.4" :: Text)
|
||||
statusIs 404
|
||||
describe "GET /package/:appId with unknown app" $ withApp $ it "fails to get an unregistered app" $ do
|
||||
setUrl ("/package/index?ids=[{\"id\":\"lnd\",\"version\":\"=0.13.3.1\"}]" :: Text)
|
||||
statusIs 200
|
||||
(res :: [PackageRes]) <- requireJSONResponse
|
||||
assertEq "response should have one package" (length res) 1
|
||||
let pkg = fromJust $ head res
|
||||
assertEq "package dependency metadata should not be empty" (null $ packageResDependencies pkg) False
|
||||
describe "GET /package/index?ids" $ withApp $ it "returns list of packages at exactly specified version" $ do
|
||||
_ <- seedBitcoinLndStack
|
||||
request $ do
|
||||
setMethod "GET"
|
||||
setUrl ("/package/index?ids=[{\"id\":\"bitcoind\",\"version\":\"=0.21.1.1\"}]" :: Text)
|
||||
statusIs 200
|
||||
(res :: [PackageRes]) <- requireJSONResponse
|
||||
assertEq "response should have one package" (length res) 1
|
||||
let pkg = fromJust $ head res
|
||||
(manifest :: PackageManifest) <- either (\e -> panic [i|failed to parse package manifest: #{e}|])
|
||||
pure
|
||||
(parseEither parseJSON $ packageResManifest pkg)
|
||||
assertEq "manifest version should be 0.21.1.1" (packageManifestVersion manifest) "0.21.1.1"
|
||||
describe "GET /package/index?ids" $ withApp $ it "returns list of packages at specified version or greater" $ do
|
||||
_ <- seedBitcoinLndStack
|
||||
request $ do
|
||||
setMethod "GET"
|
||||
setUrl ("/package/index?ids=[{\"id\":\"bitcoind\",\"version\":\">=0.21.1.1\"}]" :: Text)
|
||||
statusIs 200
|
||||
(res :: [PackageRes]) <- requireJSONResponse
|
||||
assertEq "response should have one package" (length res) 1
|
||||
let pkg = fromJust $ head res
|
||||
(manifest :: PackageManifest) <- either (\e -> panic [i|failed to parse package manifest: #{e}|])
|
||||
pure
|
||||
(parseEither parseJSON $ packageResManifest pkg)
|
||||
assertEq "manifest version should be 0.21.1.2" (packageManifestVersion manifest) "0.21.1.2"
|
||||
describe "GET /package/index?ids" $ withApp $ it "returns list of packages at specified version or greater" $ do
|
||||
_ <- seedBitcoinLndStack
|
||||
request $ do
|
||||
setMethod "GET"
|
||||
setUrl ("/package/index?ids=[{\"id\":\"bitcoind\",\"version\":\">=0.21.1.2\"}]" :: Text)
|
||||
statusIs 200
|
||||
(res :: [PackageRes]) <- requireJSONResponse
|
||||
assertEq "response should have one package" (length res) 1
|
||||
let pkg = fromJust $ head res
|
||||
(manifest :: PackageManifest) <- either (\e -> panic [i|failed to parse package manifest: #{e}|])
|
||||
pure
|
||||
(parseEither parseJSON $ packageResManifest pkg)
|
||||
assertEq "manifest version should be 0.21.1.2" (packageManifestVersion manifest) "0.21.1.2"
|
||||
describe "GET /package/:pkgId with unknown version spec for bitcoind" $ withApp $ it "fails to get unknown app" $ do
|
||||
_ <- seedBitcoinLndStack
|
||||
request $ do
|
||||
setMethod "GET"
|
||||
setUrl ("/package/bitcoind.s9pk?spec==0.20.0" :: Text)
|
||||
statusIs 404
|
||||
describe "GET /package/:pkgId with unknown package" $ withApp $ it "fails to get an unregistered app" $ do
|
||||
_ <- seedBitcoinLndStack
|
||||
request $ do
|
||||
setMethod "GET"
|
||||
setUrl ("/package/tempapp.s9pk?spec=0.0.1" :: Text)
|
||||
statusIs 404
|
||||
describe "GET /package/:appId with existing version spec for embassy-pages"
|
||||
describe "GET /package/:pkgId with package at unknown version"
|
||||
$ withApp
|
||||
$ it "fails to get an unregistered app"
|
||||
$ do
|
||||
_ <- seedBitcoinLndStack
|
||||
request $ do
|
||||
setMethod "GET"
|
||||
setUrl ("/package/lightning.s9pk?spec==0.0.1" :: Text)
|
||||
statusIs 404
|
||||
describe "GET /package/:pkgId with existing version spec for bitcoind"
|
||||
$ withApp
|
||||
$ it "creates app and metric records"
|
||||
$ do
|
||||
_ <- seedBitcoinLndStack
|
||||
request $ do
|
||||
setMethod "GET"
|
||||
setUrl ("/package/embassy-pages.s9pk?spec==0.1.3" :: Text)
|
||||
setUrl ("/package/bitcoind.s9pk?spec==0.21.1.2" :: Text)
|
||||
statusIs 200
|
||||
apps <- runDBtest $ selectList [SAppAppId ==. "embassy-pages"] []
|
||||
assertEq "app should exist" (length apps) 1
|
||||
let app = fromJust $ head apps
|
||||
metrics <- runDBtest $ selectList [MetricAppId ==. entityKey app] []
|
||||
packages <- runDBtest $ selectList [PkgRecordId ==. PkgRecordKey "bitcoind"] []
|
||||
assertEq "app should exist" (length packages) 1
|
||||
let app = fromJust $ head packages
|
||||
metrics <- runDBtest $ selectList [MetricPkgId ==. entityKey app] []
|
||||
assertEq "metric should exist" (length metrics) 1
|
||||
describe "GET /package/:appId with existing version spec for filebrowser"
|
||||
$ withApp
|
||||
$ it "creates app and metric records"
|
||||
$ do
|
||||
request $ do
|
||||
setMethod "GET"
|
||||
setUrl ("/package/filebrowser.s9pk?spec==2.14.1.1" :: Text)
|
||||
statusIs 200
|
||||
apps <- runDBtest $ selectList [SAppAppId ==. "filebrowser"] []
|
||||
assertEq "app should exist" (length apps) 1
|
||||
let app = fromJust $ head apps
|
||||
metrics <- runDBtest $ selectList [MetricAppId ==. entityKey app] []
|
||||
assertEq "metric should exist" (length metrics) 1
|
||||
version <- runDBtest $ selectList [SVersionAppId ==. entityKey app] []
|
||||
assertEq "version should exist" (length version) 1
|
||||
describe "GET /sys/proxy.pac" $ withApp $ it "does not record metric but request successful" $ do
|
||||
describe "GET /package/:pkgId with existing version spec for lnd" $ withApp $ it "creates metric records" $ do
|
||||
_ <- seedBitcoinLndStack
|
||||
request $ do
|
||||
setMethod "GET"
|
||||
setUrl ("/sys/proxy.pac?spec=0.1.0" :: Text)
|
||||
setUrl ("/package/lnd.s9pk?spec=>=0.13.3.0" :: Text)
|
||||
statusIs 200
|
||||
apps <- runDBtest $ selectList ([] :: [Filter SApp]) []
|
||||
assertEq "no apps should exist" (length apps) 0
|
||||
describe "GET /sys/:sysId" $ withApp $ it "does not record metric but request successful" $ do
|
||||
request $ do
|
||||
setMethod "GET"
|
||||
setUrl ("/sys/appmgr?spec=0.0.0" :: Text)
|
||||
statusIs 200
|
||||
apps <- runDBtest $ selectList ([] :: [Filter SApp]) []
|
||||
assertEq "no apps should exist" (length apps) 0
|
||||
metrics <- runDBtest $ selectList [MetricPkgId ==. PkgRecordKey "lnd"] []
|
||||
assertEq "metric should exist" (length metrics) 1
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
module Handler.MarketplaceSpec
|
||||
( spec
|
||||
) where
|
||||
)
|
||||
where
|
||||
|
||||
import Data.Maybe
|
||||
import Database.Persist.Sql
|
||||
@@ -14,117 +15,41 @@ import Conduit ( (.|)
|
||||
)
|
||||
import Database.Marketplace
|
||||
import Lib.Types.Category
|
||||
import Lib.Types.Emver
|
||||
import Model
|
||||
import TestImport
|
||||
import Seed
|
||||
|
||||
spec :: Spec
|
||||
spec = do
|
||||
describe "searchServices with category" $ withApp $ it "should filter services with featured category" $ do
|
||||
time <- liftIO getCurrentTime
|
||||
btc <- runDBtest $ insert $ SApp time
|
||||
(Just time)
|
||||
"Bitcoin Core"
|
||||
"bitcoind"
|
||||
"short desc bitcoin"
|
||||
"long desc bitcoin"
|
||||
"png"
|
||||
lnd <- runDBtest $ insert $ SApp time
|
||||
(Just time)
|
||||
"Lightning Network Daemon"
|
||||
"lnd"
|
||||
"short desc lnd"
|
||||
"long desc lnd"
|
||||
"png"
|
||||
featuredCat <- runDBtest $ insert $ Category time FEATURED Nothing "desc" 0
|
||||
btcCat <- runDBtest $ insert $ Category time BITCOIN Nothing "desc" 0
|
||||
lnCat <- runDBtest $ insert $ Category time LIGHTNING Nothing "desc" 0
|
||||
_ <- runDBtest $ insert_ $ ServiceCategory time btc featuredCat "bitcoin" FEATURED Nothing
|
||||
_ <- runDBtest $ insert_ $ ServiceCategory time lnd lnCat "lnd" LIGHTNING Nothing
|
||||
_ <- runDBtest $ insert_ $ ServiceCategory time lnd btcCat "lnd" BITCOIN Nothing
|
||||
_ <- runDBtest $ insert_ $ ServiceCategory time btc btcCat "bitcon" BITCOIN Nothing
|
||||
apps <- runDBtest $ runConduit $ searchServices (Just FEATURED) "" .| sinkList
|
||||
assertEq "should exist" (length apps) 1
|
||||
let app' = fromJust $ head apps
|
||||
assertEq "should be bitcoin" (sAppTitle $ entityVal app') "Bitcoin Core"
|
||||
_ <- seedBitcoinLndStack
|
||||
packages <- runDBtest $ runConduit $ searchServices (Just FEATURED) "" .| sinkList
|
||||
assertEq "should exist" (length packages) 1
|
||||
let pkg = fromJust $ head packages
|
||||
assertEq "should be bitcoin" (pkgRecordTitle $ entityVal pkg) "Bitcoin Core"
|
||||
describe "searchServices with category" $ withApp $ it "should filter services with bitcoin category" $ do
|
||||
time <- liftIO getCurrentTime
|
||||
btc <- runDBtest $ insert $ SApp time
|
||||
(Just time)
|
||||
"Bitcoin Core"
|
||||
"bitcoind"
|
||||
"short desc bitcoin"
|
||||
"long desc bitcoin"
|
||||
"png"
|
||||
lnd <- runDBtest $ insert $ SApp time
|
||||
(Just time)
|
||||
"Lightning Network Daemon"
|
||||
"lnd"
|
||||
"short desc lnd"
|
||||
"long desc lnd"
|
||||
"png"
|
||||
featuredCat <- runDBtest $ insert $ Category time FEATURED Nothing "desc" 0
|
||||
btcCat <- runDBtest $ insert $ Category time BITCOIN Nothing "desc" 0
|
||||
lnCat <- runDBtest $ insert $ Category time LIGHTNING Nothing "desc" 0
|
||||
_ <- runDBtest $ insert_ $ ServiceCategory time btc featuredCat "bitcoind" FEATURED Nothing
|
||||
_ <- runDBtest $ insert_ $ ServiceCategory time lnd lnCat "lnd" LIGHTNING Nothing
|
||||
_ <- runDBtest $ insert_ $ ServiceCategory time lnd btcCat "lnd" BITCOIN Nothing
|
||||
_ <- runDBtest $ insert_ $ ServiceCategory time btc btcCat "bitcoind" BITCOIN Nothing
|
||||
apps <- runDBtest $ runConduit $ searchServices (Just BITCOIN) "" .| sinkList
|
||||
assertEq "should exist" (length apps) 2
|
||||
_ <- seedBitcoinLndStack
|
||||
packages <- runDBtest $ runConduit $ searchServices (Just BITCOIN) "" .| sinkList
|
||||
assertEq "should exist" (length packages) 3
|
||||
describe "searchServices with fuzzy query"
|
||||
$ withApp
|
||||
$ it "runs search service with fuzzy text in long description"
|
||||
$ it "runs search service with fuzzy text in long description and no category"
|
||||
$ do
|
||||
time <- liftIO getCurrentTime
|
||||
app1 <- runDBtest $ insert $ SApp time
|
||||
(Just time)
|
||||
"Bitcoin Core"
|
||||
"bitcoind"
|
||||
"short desc"
|
||||
"long desc"
|
||||
"png"
|
||||
app2 <- runDBtest $ insert $ SApp time
|
||||
(Just time)
|
||||
"Lightning Network Daemon"
|
||||
"lnd"
|
||||
"short desc"
|
||||
"lightning long desc"
|
||||
"png"
|
||||
cate <- runDBtest $ insert $ Category time FEATURED Nothing "desc" 0
|
||||
_ <- runDBtest $ insert_ $ ServiceCategory time app1 cate "bitcoind" FEATURED Nothing
|
||||
_ <- runDBtest $ insert_ $ ServiceCategory time app2 cate "lnd" FEATURED Nothing
|
||||
apps <- runDBtest $ runConduit $ searchServices (Just FEATURED) "lightning" .| sinkList
|
||||
assertEq "should exist" (length apps) 1
|
||||
let app' = fromJust $ head apps
|
||||
print app'
|
||||
_ <- seedBitcoinLndStack
|
||||
packages <- runDBtest $ runConduit $ searchServices Nothing "lightning" .| sinkList
|
||||
assertEq "should exist" (length packages) 1
|
||||
let pkg = fromJust $ head packages
|
||||
assertEq "package should be lnd" (entityKey pkg) (PkgRecordKey "lnd")
|
||||
describe "searchServices with fuzzy query"
|
||||
$ withApp
|
||||
$ it "runs search service with fuzzy text in long description and bitcoin category"
|
||||
$ do
|
||||
_ <- seedBitcoinLndStack
|
||||
packages <- runDBtest $ runConduit $ searchServices (Just BITCOIN) "proxy" .| sinkList
|
||||
assertEq "should exist" (length packages) 1
|
||||
let pkg = fromJust $ head packages
|
||||
assertEq "package should be lnc" (entityKey pkg) (PkgRecordKey "btc-rpc-proxy")
|
||||
describe "searchServices with any category" $ withApp $ it "runs search service for any category" $ do
|
||||
time <- liftIO getCurrentTime
|
||||
btc <- runDBtest $ insert $ SApp time
|
||||
(Just time)
|
||||
"Bitcoin Core"
|
||||
"bitcoind"
|
||||
"short desc bitcoin"
|
||||
"long desc bitcoin"
|
||||
"png"
|
||||
print btc
|
||||
_ <- runDBtest $ insert $ SVersion time (Just time) btc "0.19.0" "notes" Any Any Nothing
|
||||
_ <- runDBtest $ insert $ SVersion time (Just time) btc "0.20.0" "notes" Any Any Nothing
|
||||
lnd <- runDBtest $ insert $ SApp time
|
||||
(Just time)
|
||||
"Lightning Network Daemon"
|
||||
"lnd"
|
||||
"short desc lnd"
|
||||
"long desc lnd"
|
||||
"png"
|
||||
_ <- runDBtest $ insert $ SVersion time (Just time) lnd "0.18.0" "notes" Any Any Nothing
|
||||
_ <- runDBtest $ insert $ SVersion time (Just time) lnd "0.17.0" "notes" Any Any Nothing
|
||||
featuredCat <- runDBtest $ insert $ Category time FEATURED Nothing "desc" 0
|
||||
btcCat <- runDBtest $ insert $ Category time BITCOIN Nothing "desc" 0
|
||||
lnCat <- runDBtest $ insert $ Category time LIGHTNING Nothing "desc" 0
|
||||
_ <- runDBtest $ insert_ $ ServiceCategory time btc featuredCat "bitcoin" FEATURED Nothing
|
||||
_ <- runDBtest $ insert_ $ ServiceCategory time lnd lnCat "lnd" LIGHTNING Nothing
|
||||
_ <- runDBtest $ insert_ $ ServiceCategory time lnd btcCat "lnd" BITCOIN Nothing
|
||||
_ <- runDBtest $ insert_ $ ServiceCategory time btc btcCat "bitcon" BITCOIN Nothing
|
||||
apps <- runDBtest $ runConduit $ searchServices Nothing "" .| sinkList
|
||||
assertEq "should exist" (length apps) 2
|
||||
_ <- seedBitcoinLndStack
|
||||
packages <- runDBtest $ runConduit $ searchServices Nothing "" .| sinkList
|
||||
assertEq "should exist" (length packages) 3
|
||||
|
||||
55
test/Seed.hs
Normal file
55
test/Seed.hs
Normal file
@@ -0,0 +1,55 @@
|
||||
module Seed where
|
||||
|
||||
import Database.Persist.Sql ( PersistStoreWrite(insert, insertKey, insert_) )
|
||||
import Model ( Category(Category)
|
||||
, Key(PkgRecordKey)
|
||||
, PkgCategory(PkgCategory)
|
||||
, PkgDependency(PkgDependency)
|
||||
, PkgRecord(PkgRecord)
|
||||
, VersionRecord(VersionRecord)
|
||||
)
|
||||
import Startlude ( ($)
|
||||
, Applicative(pure)
|
||||
, Maybe(Just, Nothing)
|
||||
, MonadIO(liftIO)
|
||||
, getCurrentTime
|
||||
)
|
||||
|
||||
import Lib.Types.Category ( CategoryTitle(BITCOIN, FEATURED, LIGHTNING) )
|
||||
import Prelude ( read )
|
||||
import TestImport ( RegistryCtx
|
||||
, SIO
|
||||
, YesodExampleData
|
||||
, runDBtest
|
||||
)
|
||||
|
||||
seedBitcoinLndStack :: SIO (YesodExampleData RegistryCtx) ()
|
||||
seedBitcoinLndStack = runDBtest $ do
|
||||
time <- liftIO getCurrentTime
|
||||
insertKey (PkgRecordKey "bitcoind")
|
||||
$ PkgRecord time (Just time) "Bitcoin Core" "short desc bitcoin" "long desc bitcoin" "png"
|
||||
_ <- insert $ VersionRecord time (Just time) (PkgRecordKey "bitcoind") "0.21.1.2" "notes" "0.3.0" Nothing
|
||||
_ <- insert $ VersionRecord time (Just time) (PkgRecordKey "bitcoind") "0.21.1.1" "notes" "0.3.0" Nothing
|
||||
_ <- insertKey (PkgRecordKey "lnd")
|
||||
$ PkgRecord time (Just time) "Lightning Network Daemon" "short desc lnd" "long desc lnd" "png"
|
||||
_ <- insert $ VersionRecord time (Just time) (PkgRecordKey "lnd") "0.13.3.0" "notes" "0.3.0" Nothing
|
||||
_ <- insert $ VersionRecord time (Just time) (PkgRecordKey "lnd") "0.13.3.1" "notes" "0.3.0" Nothing
|
||||
_ <- insertKey (PkgRecordKey "btc-rpc-proxy")
|
||||
$ PkgRecord time (Just time) "BTC RPC Proxy" "short desc btc-rpc-proxy" "long desc btc-rpc-proxy" "png"
|
||||
_ <- insert $ VersionRecord time (Just time) (PkgRecordKey "btc-rpc-proxy") "0.3.2.1" "notes" "0.3.0" Nothing
|
||||
featuredCat <- insert $ Category time FEATURED Nothing "desc" 0
|
||||
btcCat <- insert $ Category time BITCOIN Nothing "desc" 0
|
||||
lnCat <- insert $ Category time LIGHTNING Nothing "desc" 0
|
||||
_ <- insert_ $ PkgCategory time (PkgRecordKey "bitcoind") featuredCat
|
||||
_ <- insert_ $ PkgCategory time (PkgRecordKey "lnd") lnCat
|
||||
_ <- insert_ $ PkgCategory time (PkgRecordKey "lnd") btcCat
|
||||
_ <- insert_ $ PkgCategory time (PkgRecordKey "bitcoind") btcCat
|
||||
_ <- insert_ $ PkgCategory time (PkgRecordKey "btc-rpc-proxy") btcCat
|
||||
_ <- insert_
|
||||
$ PkgDependency time (PkgRecordKey "lnd") "0.13.3.1" (PkgRecordKey "bitcoind") (read ">=0.21.1.2 <0.22.0")
|
||||
_ <- insert_ $ PkgDependency time
|
||||
(PkgRecordKey "lnd")
|
||||
"0.13.3.1"
|
||||
(PkgRecordKey "btc-rpc-proxy")
|
||||
(read ">=0.3.2.1 <0.4.0")
|
||||
pure ()
|
||||
Reference in New Issue
Block a user