Compare commits
10
Commits
76d5b0979f
...
a74af30b06
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a74af30b06 | ||
|
|
0035b7463a | ||
|
|
e1340d6350 | ||
|
|
3e804fd0e6 | ||
|
|
28d0c5a134 | ||
|
|
e3330b634a | ||
|
|
26a8400ab1 | ||
|
|
ec732d29cb | ||
|
|
28d441fd93 | ||
|
|
41b1a98e99 |
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
FROM alpine:3.9
|
FROM node:12.9.0-alpine
|
||||||
RUN apk add npm git nodejs
|
RUN apk add git
|
||||||
|
|
||||||
RUN git clone https://gitea.frontblock.me/fb-dist/admin.git dist
|
RUN git clone https://gitea.frontblock.me/fb-dist/admin.git dist
|
||||||
|
|
||||||
|
|||||||
-1188
File diff suppressed because it is too large
Load Diff
@@ -1,182 +0,0 @@
|
|||||||
## How to contribute to Knex.js
|
|
||||||
|
|
||||||
- Make changes in the `/lib` directory.
|
|
||||||
|
|
||||||
- Before sending a pull request for a feature or bug fix, be sure to have
|
|
||||||
[tests](https://github.com/tgriesser/knex/tree/master/test). Every pull request that changes the queries should have
|
|
||||||
also **integration tests which are ran against real database** (in addition to unit tests which checks which kind of queries
|
|
||||||
are being created).
|
|
||||||
|
|
||||||
- Use the same coding style as the rest of the
|
|
||||||
[codebase](https://github.com/tgriesser/knex/blob/master/knex.js).
|
|
||||||
|
|
||||||
- All pull requests should be made to the `master` branch.
|
|
||||||
|
|
||||||
- Pull request description should have link to corresponding PR of documentation branch.
|
|
||||||
|
|
||||||
- All pull requests that modify the public API should be updated in [types/index.d.ts](https://github.com/tgriesser/knex/blob/master/types/index.d.ts)
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
Documentation is no longer maintained in knex master repository. All the documentation pull requests should be sent to https://github.com/knex/documentation
|
|
||||||
|
|
||||||
Documentation pull requests should not be merged before knex version which has the new documented feature is released.
|
|
||||||
|
|
||||||
## I would like to add support for new dialect to knex, is it possible?
|
|
||||||
|
|
||||||
Currently there are already way too many dialects supported in `knex` and instead of adding new dialect to central codebase, all the dialects should be moved to separate npm packages out from `knex` core library with their respective maintainers and test suites.
|
|
||||||
|
|
||||||
So if you like to write your own dialect, you can just inherit own dialect from knex base classes and use it by passing dilaect to knex in knex configuration (https://runkit.com/embed/90b3cpyr4jh2):
|
|
||||||
|
|
||||||
```js
|
|
||||||
// simple dialect overriding sqlite3 dialect to use sqlite3-offline driver
|
|
||||||
require('sqlite3-offline');
|
|
||||||
const Knex = require('knex');
|
|
||||||
|
|
||||||
const Dialect = require(`knex/lib/dialects/sqlite3/index.js`);
|
|
||||||
Dialect.prototype._driver = () => require('sqlite3-offline');
|
|
||||||
|
|
||||||
const knex = Knex({
|
|
||||||
client: Dialect,
|
|
||||||
connection: ':memory:',
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(knex.select(knex.raw(1)).toSQL());
|
|
||||||
|
|
||||||
await knex.schema.createTable('fooobar', (t) => {
|
|
||||||
t.bigincrements('id');
|
|
||||||
t.string('data');
|
|
||||||
});
|
|
||||||
await knex('fooobar').insert({ data: 'nomnom' });
|
|
||||||
|
|
||||||
console.log('Gimme all the data:', await knex('fooobar'));
|
|
||||||
```
|
|
||||||
|
|
||||||
## What is minimal code to reproduce bug and why I have to provide that when I can just tell whats the problem is
|
|
||||||
|
|
||||||
Writing minimal reproduction code for the problem is time-consuming and sometimes it is also really hard, for
|
|
||||||
example when the original code where the bug happens is written using express or mocha. So why is it necessary
|
|
||||||
for me to commit so much time to it when the problem is in `knex`? Contributors should be grateful that I reported
|
|
||||||
the bug I found.
|
|
||||||
|
|
||||||
The point of runnable code to reproduce the problem is to easily verify that there really is a problem and that the one
|
|
||||||
who did the report did nothing wrong (surprisingly often problem is in the user code). So instead of just description
|
|
||||||
what to do the complete code encourages devs to actually test out that problem exists and start solving it and it
|
|
||||||
saves lots of time.
|
|
||||||
|
|
||||||
tl;dr list:
|
|
||||||
|
|
||||||
1. Actually in most of the cases developer already figures out what was the problem when writing the minimal test case
|
|
||||||
or if there was problem how stuff was initialized or how async code was written it is easy to point out the problem.
|
|
||||||
|
|
||||||
2. It motivates developer to actually try out if the bug really exist by not having to figure out from incomplete example
|
|
||||||
environment in which and how bug actually manifests.
|
|
||||||
|
|
||||||
3. There are currently very few people fixing knex issues and if one has to put easily 15-30 minutes time to issue just
|
|
||||||
to see that I cannot reproduce this issue it just wastes development hours that were available for improving knex.
|
|
||||||
|
|
||||||
Test case should initialize needed tables, insert needed data and fail...
|
|
||||||
|
|
||||||
```
|
|
||||||
const knex = require('knex')({
|
|
||||||
client: 'pg',
|
|
||||||
connection: 'postgres:///knex_test'
|
|
||||||
});
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
await knex.schema.createTable(...);
|
|
||||||
await knex('table').insert({foo: 'bar}');
|
|
||||||
await knex.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
main();
|
|
||||||
```
|
|
||||||
|
|
||||||
Usually issues without reproduction code available are just closed and if the same issue is reported multiple
|
|
||||||
times maybe someone looks into it.
|
|
||||||
|
|
||||||
One easy way to setup database for your reproduction is to use database from knex's docker-compose setup (npm run db:start) and by checking the connection settings from tests' `test/knexfile.js`.
|
|
||||||
|
|
||||||
## Integration Tests
|
|
||||||
|
|
||||||
### The Easy Way
|
|
||||||
|
|
||||||
By default, Knex runs tests against sqlite3, postgresql, mysql, mysql2, mssql and oracledb drivers. All databases can be initialized and ran with docker.
|
|
||||||
|
|
||||||
Docker databases can be started and initialized and started with:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run db:start
|
|
||||||
```
|
|
||||||
|
|
||||||
and stopped with:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run db:stop
|
|
||||||
```
|
|
||||||
|
|
||||||
### Installing support for oracledb
|
|
||||||
|
|
||||||
Oracle has started providing precompiled driver libs for all the platforms, which makes it viable to run oracle tests also locally against oracledb running in docker.
|
|
||||||
|
|
||||||
Check message when running
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm install oracledb
|
|
||||||
```
|
|
||||||
|
|
||||||
and download driver library binary packages and unzip it to ~/lib directory.
|
|
||||||
|
|
||||||
### Specifying Databases
|
|
||||||
|
|
||||||
You can optionally specify which dialects to test using the `DB` environment variable. Values should be space separated and can include:
|
|
||||||
|
|
||||||
- mysql
|
|
||||||
- mysql2
|
|
||||||
- postgres
|
|
||||||
- sqlite3
|
|
||||||
- oracledb
|
|
||||||
- mssql
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ DB='postgres mysql' npm test
|
|
||||||
```
|
|
||||||
|
|
||||||
### Custom Configuration
|
|
||||||
|
|
||||||
If you'd like to override the database configuration (to use a different host, for example), you can override the path to the [default test configuration](https://github.com/tgriesser/knex/blob/master/test/knexfile.js) using the `KNEX_TEST` environment variable.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ KNEX_TEST='./path/to/my/config.js' npm test
|
|
||||||
```
|
|
||||||
|
|
||||||
### Creating Postgres User
|
|
||||||
|
|
||||||
If you are running tests agains own local database one might need to setup test user and databse for knex to connect.
|
|
||||||
|
|
||||||
To create a new user, login to Postgres and use the following queries to add the user. This assumes you've already created the `knex_test` database.
|
|
||||||
|
|
||||||
```
|
|
||||||
CREATE ROLE postgres WITH LOGIN PASSWORD '';
|
|
||||||
GRANT ALL PRIVILEGES ON DATABASE "knex_test" TO postgres;
|
|
||||||
```
|
|
||||||
|
|
||||||
Once this is done, check it works by attempting to login:
|
|
||||||
|
|
||||||
```
|
|
||||||
psql -h localhost -U postgres -d knex_test
|
|
||||||
```
|
|
||||||
|
|
||||||
## Want to be Collaborator?
|
|
||||||
|
|
||||||
There is always room for more collaborators. Be active on resolving github issues / sending pull requests / reviewing code and we will ask you to join.
|
|
||||||
|
|
||||||
### Etiquette (/ˈɛtᵻkɛt/ or /ˈɛtᵻkɪt/, French: [e.ti.kɛt])
|
|
||||||
|
|
||||||
Make pull requests for your changes, do not commit directly to master (release stuff like fixing changelog are ok though).
|
|
||||||
|
|
||||||
All the pull requests must be peer reviewed by other collaborator, so don't merge your request before that. If there is no response ping others.
|
|
||||||
|
|
||||||
If you are going to add new feature to knex (not just a bugfix) it should be discussed first with others to agree on details.
|
|
||||||
|
|
||||||
Join Gitter chat if you feel to chat outside of github issues.
|
|
||||||
-22
@@ -1,22 +0,0 @@
|
|||||||
Copyright (c) 2013-present Tim Griesser
|
|
||||||
|
|
||||||
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.
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
# [knex.js](http://knexjs.org)
|
|
||||||
|
|
||||||
[](https://npmjs.org/package/knex)
|
|
||||||
[](https://npmjs.org/package/knex)
|
|
||||||
[](https://travis-ci.org/tgriesser/knex)
|
|
||||||
[](https://coveralls.io/r/tgriesser/knex?branch=master)
|
|
||||||
[](https://david-dm.org/tgriesser/knex)
|
|
||||||
[](https://gitter.im/tgriesser/knex)
|
|
||||||
[](https://lgtm.com/projects/g/tgriesser/knex/context:javascript)
|
|
||||||
|
|
||||||
> **A SQL query builder that is _flexible_, _portable_, and _fun_ to use!**
|
|
||||||
|
|
||||||
A batteries-included, multi-dialect (MSSQL, MySQL, PostgreSQL, SQLite3, Oracle (including Oracle Wallet Authentication)) query builder for
|
|
||||||
Node.js, featuring:
|
|
||||||
|
|
||||||
- [transactions](http://knexjs.org/#Transactions)
|
|
||||||
- [connection pooling](http://knexjs.org/#Installation-pooling)
|
|
||||||
- [streaming queries](http://knexjs.org/#Interfaces-Streams)
|
|
||||||
- both a [promise](http://knexjs.org/#Interfaces-Promises) and [callback](http://knexjs.org/#Interfaces-Callbacks) API
|
|
||||||
- a [thorough test suite](https://travis-ci.org/tgriesser/knex)
|
|
||||||
- the ability to [run in the Browser](http://knexjs.org/#Installation-browser)
|
|
||||||
|
|
||||||
Node.js versions 8+ are supported.
|
|
||||||
|
|
||||||
[Read the full documentation to get started!](http://knexjs.org)
|
|
||||||
[Or check out our Recipes wiki to search for solutions to some specific problems](https://github.com/tgriesser/knex/wiki/Recipes)
|
|
||||||
If upgrading from older version, see [Upgrading instructions](https://github.com/tgriesser/knex/blob/master/UPGRADING.md)
|
|
||||||
|
|
||||||
For support and questions, join the `#bookshelf` channel on freenode IRC
|
|
||||||
|
|
||||||
For an Object Relational Mapper, see:
|
|
||||||
|
|
||||||
- http://bookshelfjs.org
|
|
||||||
- https://github.com/Vincit/objection.js
|
|
||||||
|
|
||||||
To see the SQL that Knex will generate for a given query, see: [Knex Query Lab](http://michaelavila.com/knex-querylab/)
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
We have several examples [on the website](http://knexjs.org). Here is the first one to get you started:
|
|
||||||
|
|
||||||
```js
|
|
||||||
const knex = require('knex')({
|
|
||||||
dialect: 'sqlite3',
|
|
||||||
connection: {
|
|
||||||
filename: './data.db',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create a table
|
|
||||||
knex.schema
|
|
||||||
.createTable('users', function(table) {
|
|
||||||
table.increments('id');
|
|
||||||
table.string('user_name');
|
|
||||||
})
|
|
||||||
|
|
||||||
// ...and another
|
|
||||||
.createTable('accounts', function(table) {
|
|
||||||
table.increments('id');
|
|
||||||
table.string('account_name');
|
|
||||||
table
|
|
||||||
.integer('user_id')
|
|
||||||
.unsigned()
|
|
||||||
.references('users.id');
|
|
||||||
})
|
|
||||||
|
|
||||||
// Then query the table...
|
|
||||||
.then(function() {
|
|
||||||
return knex('users').insert({ user_name: 'Tim' });
|
|
||||||
})
|
|
||||||
|
|
||||||
// ...and using the insert id, insert into the other table.
|
|
||||||
.then(function(rows) {
|
|
||||||
return knex('accounts').insert({ account_name: 'knex', user_id: rows[0] });
|
|
||||||
})
|
|
||||||
|
|
||||||
// Query both of the rows.
|
|
||||||
.then(function() {
|
|
||||||
return knex('users')
|
|
||||||
.join('accounts', 'users.id', 'accounts.user_id')
|
|
||||||
.select('users.user_name as user', 'accounts.account_name as account');
|
|
||||||
})
|
|
||||||
|
|
||||||
// .map over the results
|
|
||||||
.map(function(row) {
|
|
||||||
console.log(row);
|
|
||||||
})
|
|
||||||
|
|
||||||
// Finally, add a .catch handler for the promise chain
|
|
||||||
.catch(function(e) {
|
|
||||||
console.error(e);
|
|
||||||
});
|
|
||||||
```
|
|
||||||
@@ -1,203 +0,0 @@
|
|||||||
{
|
|
||||||
"_from": "knex@0.19.2",
|
|
||||||
"_id": "knex@0.19.2",
|
|
||||||
"_inBundle": false,
|
|
||||||
"_integrity": "sha512-TVYvlp2esS4LjjJSz8XuE48bPJq4N3lWnETQVgJ3hXPEqjiDjxcTa3bCn6F5ipQuBaMAAaFHNrqsZm7BttogdA==",
|
|
||||||
"_location": "/knex",
|
|
||||||
"_phantomChildren": {},
|
|
||||||
"_requested": {
|
|
||||||
"type": "version",
|
|
||||||
"registry": true,
|
|
||||||
"raw": "knex@0.19.2",
|
|
||||||
"name": "knex",
|
|
||||||
"escapedName": "knex",
|
|
||||||
"rawSpec": "0.19.2",
|
|
||||||
"saveSpec": null,
|
|
||||||
"fetchSpec": "0.19.2"
|
|
||||||
},
|
|
||||||
"_requiredBy": [
|
|
||||||
"#USER",
|
|
||||||
"/",
|
|
||||||
"/@types/knex",
|
|
||||||
"/frontblock-generic"
|
|
||||||
],
|
|
||||||
"_resolved": "https://registry.npmjs.org/knex/-/knex-0.19.2.tgz",
|
|
||||||
"_shasum": "056efdb33fb8c77d3d76266b5d1d12dc483c21b5",
|
|
||||||
"_spec": "knex@0.19.2",
|
|
||||||
"_where": "/home/cake/FB/development/repos/vendor/admin",
|
|
||||||
"author": {
|
|
||||||
"name": "Tim Griesser",
|
|
||||||
"url": "https://github.com/tgriesser"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"knex": "./bin/cli.js"
|
|
||||||
},
|
|
||||||
"browser": {
|
|
||||||
"./lib/migrate/Migrator.js": "./lib/util/noop.js",
|
|
||||||
"./lib/bin/cli.js": "./lib/util/noop.js",
|
|
||||||
"./lib/seed/Seeder.js": "./lib/util/noop.js",
|
|
||||||
"mssql": false,
|
|
||||||
"mssql/lib/base": false,
|
|
||||||
"tedious": false,
|
|
||||||
"mysql": false,
|
|
||||||
"mysql2": false,
|
|
||||||
"pg": false,
|
|
||||||
"pg-query-stream": false,
|
|
||||||
"oracle": false,
|
|
||||||
"sqlite3": false,
|
|
||||||
"oracledb": false
|
|
||||||
},
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/tgriesser/knex/issues"
|
|
||||||
},
|
|
||||||
"buildDependencies": [
|
|
||||||
"rimraf"
|
|
||||||
],
|
|
||||||
"bundleDependencies": false,
|
|
||||||
"contributors": [
|
|
||||||
{
|
|
||||||
"name": "Simon Liden"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Mikael Lepisto"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Paul Gaurab",
|
|
||||||
"url": "https://lorefnon.tech"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Igor Savin",
|
|
||||||
"url": "https://www.codeflashbacks.com"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"dependencies": {
|
|
||||||
"bluebird": "^3.5.5",
|
|
||||||
"colorette": "1.0.8",
|
|
||||||
"commander": "^2.20.0",
|
|
||||||
"debug": "4.1.1",
|
|
||||||
"getopts": "2.2.4",
|
|
||||||
"inherits": "~2.0.4",
|
|
||||||
"interpret": "^1.2.0",
|
|
||||||
"liftoff": "3.1.0",
|
|
||||||
"lodash": "^4.17.15",
|
|
||||||
"mkdirp": "^0.5.1",
|
|
||||||
"pg-connection-string": "2.0.0",
|
|
||||||
"tarn": "^2.0.0",
|
|
||||||
"tildify": "2.0.0",
|
|
||||||
"uuid": "^3.3.2",
|
|
||||||
"v8flags": "^3.1.3"
|
|
||||||
},
|
|
||||||
"deprecated": false,
|
|
||||||
"description": "A batteries-included SQL query & schema builder for Postgres, MySQL and SQLite3 and the Browser",
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/node": "^10.14.13",
|
|
||||||
"JSONStream": "^1.3.5",
|
|
||||||
"chai": "^4.2.0",
|
|
||||||
"chai-subset-in-order": "^2.1.3",
|
|
||||||
"cli-testlab": "^1.7.0",
|
|
||||||
"coveralls": "^3.0.5",
|
|
||||||
"cross-env": "^5.2.0",
|
|
||||||
"dtslint": "^0.9.0",
|
|
||||||
"eslint": "^6.1.0",
|
|
||||||
"eslint-config-prettier": "^6.0.0",
|
|
||||||
"eslint-plugin-import": "^2.18.2",
|
|
||||||
"husky": "^3.0.1",
|
|
||||||
"jake": "^8.1.1",
|
|
||||||
"lint-staged": "^9.2.0",
|
|
||||||
"mocha": "^6.2.0",
|
|
||||||
"mock-fs": "^4.10.1",
|
|
||||||
"mssql": "^5.1.0",
|
|
||||||
"mysql": "^2.17.1",
|
|
||||||
"mysql2": "^1.6.5",
|
|
||||||
"nyc": "^14.1.1",
|
|
||||||
"pg": "^7.11.0",
|
|
||||||
"pg-query-stream": "^2.0.0",
|
|
||||||
"prettier": "^1.18.2",
|
|
||||||
"rimraf": "^2.6.3",
|
|
||||||
"sinon": "^7.3.2",
|
|
||||||
"sinon-chai": "^3.3.0",
|
|
||||||
"source-map-support": "^0.5.12",
|
|
||||||
"sqlite3": "^4.0.9",
|
|
||||||
"tap-spec": "^5.0.0",
|
|
||||||
"tape": "^4.11.0",
|
|
||||||
"toxiproxy-node-client": "^2.0.6",
|
|
||||||
"typescript": "^3.5.3",
|
|
||||||
"webpack-cli": "^3.3.6"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"CONTRIBUTING.md",
|
|
||||||
"README.md",
|
|
||||||
"bin/*",
|
|
||||||
"lib/*",
|
|
||||||
"lib/*",
|
|
||||||
"knex.js",
|
|
||||||
"LICENSE",
|
|
||||||
"CHANGELOG.md",
|
|
||||||
"scripts/*",
|
|
||||||
"types/index.d.ts",
|
|
||||||
"types/result.d.ts"
|
|
||||||
],
|
|
||||||
"homepage": "https://knexjs.org",
|
|
||||||
"husky": {
|
|
||||||
"hooks": {
|
|
||||||
"pre-commit": "lint-staged"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"sql",
|
|
||||||
"query",
|
|
||||||
"postgresql",
|
|
||||||
"mysql",
|
|
||||||
"sqlite3",
|
|
||||||
"oracle",
|
|
||||||
"mssql"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"lint-staged": {
|
|
||||||
"*.{js,json}": [
|
|
||||||
"prettier --write",
|
|
||||||
"git add"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"main": "knex.js",
|
|
||||||
"name": "knex",
|
|
||||||
"nyc": {
|
|
||||||
"check-coverage": true,
|
|
||||||
"lines": 84,
|
|
||||||
"statements": 82,
|
|
||||||
"functions": 83,
|
|
||||||
"branches": 69
|
|
||||||
},
|
|
||||||
"react-native": {
|
|
||||||
"./lib/migrate": "./lib/util/noop.js",
|
|
||||||
"./lib/seed": "./lib/util/noop.js"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git://github.com/tgriesser/knex.git"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"coveralls": "nyc report --reporter=text-lcov | coveralls",
|
|
||||||
"db:start": "docker-compose -f scripts/docker-compose.yml up --build -d mysql oracledbxe postgres mssql; docker-compose -f scripts/docker-compose.yml up initmssqlknexdb waitmysql waitpostgres waitoracledbxe",
|
|
||||||
"db:stop": "docker-compose -f scripts/docker-compose.yml down",
|
|
||||||
"debug:tape": "node --inspect-brk test/tape/index.js",
|
|
||||||
"debug:test": "mocha --inspect-brk --exit -t 0 test/index.js",
|
|
||||||
"format": "prettier --write \"{lib,bin,scripts,test}/**/*.js\"",
|
|
||||||
"lint": "eslint \"lib/**/*.js\" \"test/**/*.js\"",
|
|
||||||
"lint:types": "dtslint types",
|
|
||||||
"stress:destroy": "docker-compose -f scripts/stress-test/docker-compose.yml stop",
|
|
||||||
"stress:init": "docker-compose -f scripts/stress-test/docker-compose.yml up --no-start && docker-compose -f scripts/stress-test/docker-compose.yml start",
|
|
||||||
"stress:test": "node scripts/stress-test/knex-stress-test.js | grep -A 5 -B 60 -- '- STATS '",
|
|
||||||
"test": "mocha --exit -t 10000 test/index.js && npm run test:tape && npm run test:cli",
|
|
||||||
"test:cli": "cross-env KNEX_PATH=../knex.js KNEX=bin/cli.js jake -f test/jake/Jakefile",
|
|
||||||
"test:nyc": "nyc mocha --exit --check-leaks --globals __core-js_shared__ -t 10000 test/index.js && npm run test:tape && npm run test:cli",
|
|
||||||
"test:sqlite": "cross-env DB=sqlite3 npm test",
|
|
||||||
"test:tape": "node test/tape/index.js | tap-spec"
|
|
||||||
},
|
|
||||||
"tonicExampleFilename": "scripts/runkit-example.js",
|
|
||||||
"types": "types/index.d.ts",
|
|
||||||
"version": "0.19.2"
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
|
|
||||||
services:
|
|
||||||
mssql:
|
|
||||||
image: microsoft/mssql-server-linux:2017-latest
|
|
||||||
ports:
|
|
||||||
- '21433:1433'
|
|
||||||
environment:
|
|
||||||
- ACCEPT_EULA=Y
|
|
||||||
- SA_PASSWORD=S0meVeryHardPassword
|
|
||||||
healthcheck:
|
|
||||||
test: /opt/mssql-tools/bin/sqlcmd -S mssql -U sa -P 'S0meVeryHardPassword' -Q 'select 1'
|
|
||||||
initmssqlknexdb:
|
|
||||||
image: microsoft/mssql-server-linux:2017-latest
|
|
||||||
links:
|
|
||||||
- mssql
|
|
||||||
depends_on:
|
|
||||||
- mssql
|
|
||||||
entrypoint:
|
|
||||||
- bash
|
|
||||||
- -c
|
|
||||||
- 'until /opt/mssql-tools/bin/sqlcmd -S mssql -U sa -P S0meVeryHardPassword -d master -Q "CREATE DATABASE knex_test"; do sleep 5; done'
|
|
||||||
|
|
||||||
mysql:
|
|
||||||
image: mysql
|
|
||||||
command: --default-authentication-plugin=mysql_native_password
|
|
||||||
ports:
|
|
||||||
- '23306:3306'
|
|
||||||
environment:
|
|
||||||
- MYSQL_ROOT_PASSWORD=testrootpassword
|
|
||||||
- MYSQL_DATABASE=knex_test
|
|
||||||
- MYSQL_USER=testuser
|
|
||||||
- MYSQL_PASSWORD=testpassword
|
|
||||||
healthcheck:
|
|
||||||
test:
|
|
||||||
[
|
|
||||||
'CMD',
|
|
||||||
'/usr/bin/mysql',
|
|
||||||
'-hlocalhost',
|
|
||||||
'-utestuser',
|
|
||||||
'-ptestpassword',
|
|
||||||
'-e',
|
|
||||||
'SELECT 1',
|
|
||||||
]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 3
|
|
||||||
restart: always
|
|
||||||
waitmysql:
|
|
||||||
image: mysql
|
|
||||||
links:
|
|
||||||
- mysql
|
|
||||||
depends_on:
|
|
||||||
- mysql
|
|
||||||
entrypoint:
|
|
||||||
- bash
|
|
||||||
- -c
|
|
||||||
- 'until /usr/bin/mysql -hmysql -utestuser -ptestpassword -e "SELECT 1"; do sleep 5; done'
|
|
||||||
|
|
||||||
postgres:
|
|
||||||
image: postgres:alpine
|
|
||||||
ports:
|
|
||||||
- '25432:5432'
|
|
||||||
environment:
|
|
||||||
- POSTGRES_USER=testuser
|
|
||||||
- POSTGRES_PASSWORD=knextest
|
|
||||||
- POSTGRES_DB=knex_test
|
|
||||||
waitpostgres:
|
|
||||||
image: postgres:alpine
|
|
||||||
links:
|
|
||||||
- postgres
|
|
||||||
depends_on:
|
|
||||||
- postgres
|
|
||||||
entrypoint:
|
|
||||||
- bash
|
|
||||||
- -c
|
|
||||||
- 'until /usr/local/bin/psql postgres://testuser:knextest@postgres/knex_test -c "SELECT 1"; do sleep 5; done'
|
|
||||||
|
|
||||||
oracledbxe:
|
|
||||||
image: quillbuilduser/oracle-18-xe
|
|
||||||
container_name: oracledbxe_container
|
|
||||||
ports:
|
|
||||||
- '21521:1521'
|
|
||||||
environment:
|
|
||||||
- ORACLE_ALLOW_REMOTE=true
|
|
||||||
waitoracledbxe:
|
|
||||||
image: quillbuilduser/oracle-18-xe
|
|
||||||
links:
|
|
||||||
- oracledbxe
|
|
||||||
depends_on:
|
|
||||||
- oracledbxe
|
|
||||||
environment:
|
|
||||||
- ORACLE_HOME=/opt/oracle/product/18c/dbhomeXE
|
|
||||||
entrypoint:
|
|
||||||
- bash
|
|
||||||
- -c
|
|
||||||
- 'until /opt/oracle/product/18c/dbhomeXE/bin/sqlplus -s sys/Oracle18@oracledbxe/XE as sysdba <<< "SELECT 13376411 FROM DUAL; exit;" | grep "13376411"; do echo "Could not connect to oracle... sleep for a while"; sleep 5; done'
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
# Checklist for crating knex @next releases
|
|
||||||
|
|
||||||
1. Go through all commits since the last release and add them to CHANGELOG.md under unreleased changes section.
|
|
||||||
2. Commit changes to CHANGELOG
|
|
||||||
3. Check that master compiles and tests are running fine (check also that CI tests are passing)
|
|
||||||
|
|
||||||
```
|
|
||||||
npm run build
|
|
||||||
|
|
||||||
# run bunch of tests, but skipping coverage which doesn't really work locally at least
|
|
||||||
npm plaintest
|
|
||||||
npm bin_test
|
|
||||||
npm oracledb:test
|
|
||||||
npm mssql:init
|
|
||||||
npm mssql:test
|
|
||||||
npm mssql:destroy
|
|
||||||
```
|
|
||||||
|
|
||||||
4. Update package.json version to be e.g. 0.16.0-next1 or 0.16.0-next2 and commit yo master
|
|
||||||
5. Publish it under @next tag
|
|
||||||
|
|
||||||
```
|
|
||||||
npm publish --tag next
|
|
||||||
```
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
#!/bin/bash -e
|
|
||||||
|
|
||||||
changelog=node_modules/.bin/changelog
|
|
||||||
|
|
||||||
update_version() {
|
|
||||||
echo "$(node -p "p=require('./${1}');p.version='${2}';JSON.stringify(p,null,2)")" > $1
|
|
||||||
echo "Updated ${1} version to ${2}"
|
|
||||||
}
|
|
||||||
|
|
||||||
current_version=$(node -p "require('./package').version")
|
|
||||||
|
|
||||||
printf "Next version (current is $current_version)? "
|
|
||||||
read next_version
|
|
||||||
|
|
||||||
if ! [[ $next_version =~ ^[0-9]\.[0-9]+\.[0-9](-.+)? ]]; then
|
|
||||||
echo "Version must be a valid semver string, e.g. 1.0.2 or 2.3.0-beta.1"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
next_ref="v$next_version"
|
|
||||||
|
|
||||||
git add -u
|
|
||||||
|
|
||||||
npm run build
|
|
||||||
npm test
|
|
||||||
|
|
||||||
update_version 'package.json' $next_version
|
|
||||||
|
|
||||||
git commit -am "release $next_version"
|
|
||||||
git tag $next_version
|
|
||||||
|
|
||||||
git push --tags
|
|
||||||
|
|
||||||
npm publish
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
# Test scripts to evaluate stability of drivers / pool etc.
|
|
||||||
|
|
||||||
# To run this test you need to be in this directory + have node >= 8
|
|
||||||
# and startup docker containers with proxy and sql servers
|
|
||||||
|
|
||||||
docker-compose up --no-start
|
|
||||||
docker-compose start
|
|
||||||
|
|
||||||
# Select different test script to run:
|
|
||||||
|
|
||||||
node mysql2-random-hanging-every-now-and-then.js 2> /dev/null | grep -B500 -A2 -- "- STATS"
|
|
||||||
node mysql2-sudden-exit-without-error
|
|
||||||
node knex-stress-test.js | grep -A 3 -- "- STATS "
|
|
||||||
node reconnect-test-mysql-based-drivers.js 2> /dev/null | grep -A 3 -- "- STATS "
|
|
||||||
|
|
||||||
# Shut down docker instances when done:
|
|
||||||
|
|
||||||
docker-compose down
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
|
|
||||||
services:
|
|
||||||
toxiproxy:
|
|
||||||
image: shopify/toxiproxy
|
|
||||||
ports:
|
|
||||||
- "8474:8474"
|
|
||||||
- "23306:23306"
|
|
||||||
- "25432:25432"
|
|
||||||
- "21521:21521"
|
|
||||||
- "21433:21433"
|
|
||||||
links:
|
|
||||||
- "mysql"
|
|
||||||
- "postgresql"
|
|
||||||
- "oracledbxe"
|
|
||||||
- "mssql"
|
|
||||||
|
|
||||||
mysql:
|
|
||||||
image: mysql:5.7
|
|
||||||
ports:
|
|
||||||
- "33306:3306"
|
|
||||||
environment:
|
|
||||||
- TZ=UTC
|
|
||||||
- MYSQL_ROOT_PASSWORD=mysqlrootpassword
|
|
||||||
|
|
||||||
postgresql:
|
|
||||||
image: mdillon/postgis
|
|
||||||
ports:
|
|
||||||
- "35432:5432"
|
|
||||||
environment:
|
|
||||||
- POSTGRES_PASSWORD=postgresrootpassword
|
|
||||||
- POSTGRES_USER=postgres
|
|
||||||
|
|
||||||
oracledbxe:
|
|
||||||
image: wnameless/oracle-xe-11g
|
|
||||||
ports:
|
|
||||||
- "31521:1521"
|
|
||||||
environment:
|
|
||||||
- ORACLE_ALLOW_REMOTE=true
|
|
||||||
|
|
||||||
mssql:
|
|
||||||
image: microsoft/mssql-server-linux:2017-latest
|
|
||||||
ports:
|
|
||||||
- "31433:1433"
|
|
||||||
environment:
|
|
||||||
- ACCEPT_EULA=Y
|
|
||||||
- SA_PASSWORD=S0meVeryHardPassword
|
|
||||||
Generated
+304
-378
File diff suppressed because it is too large
Load Diff
+5
-5
@@ -9,8 +9,8 @@
|
|||||||
"build-backend": "tsc; npm run webpack",
|
"build-backend": "tsc; npm run webpack",
|
||||||
"build-frontend": "npm run build-dashboard; cp ./dist/FrontblockLib.js ./dist/static",
|
"build-frontend": "npm run build-dashboard; cp ./dist/FrontblockLib.js ./dist/static",
|
||||||
"build-dashboard": "git submodule init && git submodule update --merge; cd src/frontend; npm i && npm run build; mkdir ../../dist/static; cp -r dist/* ../../dist/static",
|
"build-dashboard": "git submodule init && git submodule update --merge; cd src/frontend; npm i && npm run build; mkdir ../../dist/static; cp -r dist/* ../../dist/static",
|
||||||
"clean": "rm -rf lib static plugins conf dist widget .rpt2_cache *.js *.ts src/frontend/dist",
|
"clean": "rm -rf lib static plugins conf dist widget .rpt2_cache *.js *.ts src/frontend/dist data",
|
||||||
"update-frontblock": "rm -rf node_modules/frontblock*; npm install",
|
"update-frontblock": "npm remove frontblock frontblock-generic; npm install frontblock-generic@latest frontblock@latest",
|
||||||
"webpack": "webpack --config src/backend/webpack.prod.js --progress --colors"
|
"webpack": "webpack --config src/backend/webpack.prod.js --progress --colors"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -25,13 +25,13 @@
|
|||||||
"child-process-promise": "^2.2.1",
|
"child-process-promise": "^2.2.1",
|
||||||
"debug": "^4.1.1",
|
"debug": "^4.1.1",
|
||||||
"express": "^4.16.4",
|
"express": "^4.16.4",
|
||||||
"frontblock": "^0.14.0",
|
"frontblock": "^0.15.1",
|
||||||
"frontblock-generic": "^0.30.6",
|
"frontblock-generic": "^0.34.8",
|
||||||
"git-cherrypicker": "0.0.3",
|
"git-cherrypicker": "0.0.3",
|
||||||
"git-describe": "^4.0.4",
|
"git-describe": "^4.0.4",
|
||||||
"http": "0.0.0",
|
"http": "0.0.0",
|
||||||
"knex": "^0.19.2",
|
"knex": "^0.19.2",
|
||||||
"log4js": "^4.4.0",
|
"log4js": "^4.5.1",
|
||||||
"lowdb": "^1.0.0",
|
"lowdb": "^1.0.0",
|
||||||
"node-fetch": "^2.6.0",
|
"node-fetch": "^2.6.0",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
|
|||||||
+170
-150
@@ -1,41 +1,32 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
import * as Logger from 'log4js'
|
import * as Logger from 'log4js'
|
||||||
import * as Knex from 'knex'
|
import * as Knex from 'knex'
|
||||||
|
import * as Path from 'path'
|
||||||
import { FrontblockApiClient, FrontblockApiConf } from 'frontblock';
|
import { FrontblockApiClient, FrontblockApiConf } from 'frontblock';
|
||||||
import { AdminBase } from 'frontblock-generic/Admin';
|
import { getLogger, SubscriptionResponse, Coin, AccountMap, TransactionMap } from 'frontblock-generic/Types';
|
||||||
import { FrontblockApi } from 'frontblock-generic/Api';
|
import { AdminBase, NotificationSeverity, TableDefiniton } from 'frontblock-generic/Admin';
|
||||||
import { Plugin } from 'frontblock-generic/Plugin';
|
import { IFrontblockApiClient, FrontblockApi } from 'frontblock-generic/Api';
|
||||||
import { socketioRPC } from 'frontblock-generic/RPC';
|
import { Plugin, RPCExporter } from 'frontblock-generic/Plugin';
|
||||||
import { GitUpdater, RepoFolderStatus } from './GitUpdater';
|
|
||||||
|
|
||||||
Logger.configure({
|
const logger:Logger.Logger = getLogger("admin", 'debug')
|
||||||
appenders:
|
|
||||||
{
|
|
||||||
"admin": { type: 'stdout' },
|
|
||||||
//app: { type: 'file', filename: 'application.log' }
|
|
||||||
},
|
|
||||||
categories:
|
|
||||||
{
|
|
||||||
default: { appenders: [ 'admin' ], level: 'debug' }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const logger = Logger.getLogger("admin")
|
|
||||||
|
|
||||||
|
export type AdminConf = {
|
||||||
export type AdminConf = { httpPort: number}
|
httpPort: number,
|
||||||
|
apiConf: FrontblockApiConf
|
||||||
|
dbConf:Knex.Config,
|
||||||
|
eventBusConf: { [topic in string]: NotificationSeverity}
|
||||||
|
}
|
||||||
|
|
||||||
export class FrontblockAdmin extends AdminBase<AdminConf>{
|
export class FrontblockAdmin extends AdminBase<AdminConf>{
|
||||||
|
|
||||||
private pluginUpdaters:{[name in string]:GitUpdater} = {}
|
public apiClient: IFrontblockApiClient = this.makeApiClient(this.getConfigKey("apiConf"))
|
||||||
|
|
||||||
constructor(runningPlugins: Plugin[] = []){
|
getDefaultConfig(): AdminConf {
|
||||||
super(runningPlugins)
|
|
||||||
}
|
|
||||||
|
|
||||||
getDefaultConfig(): { apiConf: FrontblockApiConf; } & AdminConf & { dbConf:Knex.Config; } {
|
|
||||||
return {
|
return {
|
||||||
httpPort: 8080,
|
httpPort: 8080,
|
||||||
|
eventBusConf: {
|
||||||
|
|
||||||
|
},
|
||||||
apiConf: {
|
apiConf: {
|
||||||
apiHost: "api.testnet.frontblock.me",
|
apiHost: "api.testnet.frontblock.me",
|
||||||
apiKey: "",
|
apiKey: "",
|
||||||
@@ -45,144 +36,173 @@ export class FrontblockAdmin extends AdminBase<AdminConf>{
|
|||||||
dbConf: {
|
dbConf: {
|
||||||
client: 'sqlite3',
|
client: 'sqlite3',
|
||||||
connection: {
|
connection: {
|
||||||
filename: "./data/ApiClient.sqlite"
|
filename: Path.join(__dirname, "data/Admin.sqlite")
|
||||||
},
|
},
|
||||||
useNullAsDefault: true
|
useNullAsDefault: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected makeApiClient(conf: FrontblockApiConf): FrontblockApi {
|
constructor(runningPlugins: Plugin[] = []){
|
||||||
// @ts-ignore
|
super(runningPlugins)
|
||||||
|
this.initialize().then(() => this.makeKnex(this.getConfigKey('dbConf'))).then(() => logger.debug("Admin initialization finished"))
|
||||||
|
}
|
||||||
|
|
||||||
|
initApis(socket){
|
||||||
|
const quit = async(uid:string) => {return await this.quit(this.getConfigKey('apiConf').apiKey, uid) }
|
||||||
|
const exporter:RPCExporter = {
|
||||||
|
name: 'ApiClient',
|
||||||
|
exportRPCs: () => {
|
||||||
|
return [{
|
||||||
|
name: "subscribe",
|
||||||
|
func: async <C extends Coin>(coin: C, account: AccountMap[C]) => { return await this.subscribe(this.getConfigKey('apiConf').apiKey, coin, account)},
|
||||||
|
type: 'call',
|
||||||
|
visibility: 'private'
|
||||||
|
},{
|
||||||
|
name: "subsume",
|
||||||
|
func: async <C extends Coin>(coin: C, account: AccountMap[C], callback: (tx: TransactionMap[C]) => void) => { return await this.subsume(this.getConfigKey('apiConf').apiKey, coin, account, callback)},
|
||||||
|
type: 'hook',
|
||||||
|
unhook: quit,
|
||||||
|
visibility: 'private'
|
||||||
|
},{
|
||||||
|
name: 'unsubscribe',
|
||||||
|
func: async(uid:string) => { return await this.unsubscribe(this.getConfigKey('apiConf').apiKey, uid) },
|
||||||
|
type: 'call',
|
||||||
|
visibility: 'private'
|
||||||
|
},{
|
||||||
|
name: 'consume',
|
||||||
|
func: async<C extends Coin>(uid: string, callback: (tx: TransactionMap[C]) => void) => {return await this.consume(this.getConfigKey('apiConf').apiKey, uid, callback)},
|
||||||
|
type: 'hook',
|
||||||
|
unhook: quit,
|
||||||
|
visibility: 'private'
|
||||||
|
},{
|
||||||
|
name: 'quit',
|
||||||
|
func: quit,
|
||||||
|
type: 'call',
|
||||||
|
visibility: 'private'
|
||||||
|
},{
|
||||||
|
name: "getSubscriptions",
|
||||||
|
func: async() => { return await this.getSubscriptions() },
|
||||||
|
type: "call",
|
||||||
|
visibility: "private"
|
||||||
|
},{
|
||||||
|
name: "getConsumers",
|
||||||
|
func: async() => { return await this.getConsumers() },
|
||||||
|
type: "call",
|
||||||
|
visibility: "private"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.initApis(socket, [exporter])
|
||||||
|
}
|
||||||
|
|
||||||
|
async getConsumers(): Promise<SubscriptionResponse[]> {
|
||||||
|
const records = await this.knex.select('*').from('consumers')
|
||||||
|
return records.map(r => JSON.parse(r.JSON))
|
||||||
|
}
|
||||||
|
|
||||||
|
async getSubscriptions(): Promise<SubscriptionResponse[]> {
|
||||||
|
const records = await this.knex.select('*').from('subscriptions')
|
||||||
|
return records.map(r => JSON.parse(r.JSON))
|
||||||
|
}
|
||||||
|
|
||||||
|
private subscribe:FrontblockApi['subscribe'] = async (apiKey, coin, account) => {
|
||||||
|
const res = await this.apiClient.subscribe(apiKey, coin, account)
|
||||||
|
if(res instanceof SubscriptionResponse){
|
||||||
|
await this.insert('subscriptions', [{uuid: res.uid, coin:coin, JSON: JSON.stringify(res)}], "apiclient", "Info")
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
private consume:FrontblockApi['consume'] = async (apikey, uuid, callback) => {
|
||||||
|
const res = await this.apiClient.consume(apikey, uuid, callback)
|
||||||
|
if(res instanceof SubscriptionResponse){
|
||||||
|
await this.insert('consumers', [{uuid: res.uid, coin:undefined, JSON: JSON.stringify(res)}], "apiclient", "Info")
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsubscribe:FrontblockApi['unsubscribe'] = async (apikey, uuid) => {
|
||||||
|
const res = await this.apiClient.unsubscribe(apikey, uuid)
|
||||||
|
await this.knex('subscriptions')
|
||||||
|
.where('uuid', uuid)
|
||||||
|
.del()
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
private quit:FrontblockApi['quit'] = async (apikey, uuid) => {
|
||||||
|
const res = await this.apiClient.quit(apikey, uuid)
|
||||||
|
await this.knex('consumers')
|
||||||
|
.where('uuid', uuid)
|
||||||
|
.del()
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
private subsume:FrontblockApi['subsume'] = async (apikey, coin, account, callback) => {
|
||||||
|
const res = await this.apiClient.subsume(apikey, coin, account, callback)
|
||||||
|
if(res instanceof SubscriptionResponse){
|
||||||
|
await Promise.all([
|
||||||
|
this.insert('consumers', [{
|
||||||
|
uuid: res.uid,
|
||||||
|
coin:coin,
|
||||||
|
JSON: JSON.stringify(res)
|
||||||
|
}], "apiclient", "Info"),
|
||||||
|
|
||||||
|
this.insert('subscriptions', [{
|
||||||
|
uuid: res.uid,
|
||||||
|
coin:coin,
|
||||||
|
JSON: JSON.stringify(new SubscriptionResponse(res.message!))
|
||||||
|
}], "apiclient", "Info")
|
||||||
|
])
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
setConfig(conf:AdminConf & { dbConf: Knex.Config; eventBusConf: {[x in string]:NotificationSeverity} }):AdminConf & { dbConf: Knex.Config; eventBusConf: {[x in string]:NotificationSeverity} }{
|
||||||
|
const currConf = this.getConfig()
|
||||||
|
if(JSON.stringify(currConf.apiConf) !== JSON.stringify(conf.apiConf)){
|
||||||
|
this.makeApiClient(conf.apiConf)
|
||||||
|
}
|
||||||
|
return super.setConfig(conf)
|
||||||
|
}
|
||||||
|
|
||||||
|
setConfigKey(key: string, value: any):AdminConf & { dbConf: Knex.Config; eventBusConf: {[x in string]:NotificationSeverity} }{
|
||||||
|
if(key === 'apiConf'){
|
||||||
|
this.makeApiClient(value)
|
||||||
|
}
|
||||||
|
return super.setConfigKey(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
getTableDefinitions(): TableDefiniton[] {
|
||||||
|
return [
|
||||||
|
...super.getTableDefinitions(),
|
||||||
|
{
|
||||||
|
name: 'subscriptions',
|
||||||
|
tableBuilder: function (table) {
|
||||||
|
table.string('uuid').primary();
|
||||||
|
table.string('coin');
|
||||||
|
table.string('JSON');
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
name: 'consumers',
|
||||||
|
tableBuilder: function (table) {
|
||||||
|
table.string('uuid').primary();
|
||||||
|
table.string('coin');
|
||||||
|
table.string('JSON');
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
protected makeApiClient(conf: FrontblockApiConf): IFrontblockApiClient {
|
||||||
if(this.apiClient) this.apiClient.disconnect()
|
if(this.apiClient) this.apiClient.disconnect()
|
||||||
this.apiClient = new FrontblockApiClient(conf)
|
this.apiClient = new FrontblockApiClient(conf)
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
this.apiClient.connect()
|
this.apiClient.connect()
|
||||||
return this.apiClient
|
return this.apiClient
|
||||||
}
|
}
|
||||||
|
|
||||||
exportRPCs():socketioRPC[]{
|
|
||||||
return [
|
|
||||||
...super.exportRPCs(),
|
|
||||||
{
|
|
||||||
name: 'installPlugin',
|
|
||||||
func: async (name:string, force = false) => {return await this.installPlugin(name, force)},
|
|
||||||
type: 'call',
|
|
||||||
visibility: 'private'
|
|
||||||
},{
|
|
||||||
name: 'startPlugin',
|
|
||||||
func: async (name:string) => {return await this.startPlugin(name)},
|
|
||||||
type: 'call',
|
|
||||||
visibility: 'private'
|
|
||||||
},{
|
|
||||||
name: 'updatePlugin',
|
|
||||||
func: async (name) => {return await this.updatePlugin(name)},
|
|
||||||
type: 'call',
|
|
||||||
visibility: 'private'
|
|
||||||
},{
|
|
||||||
name: 'setPluginVersion',
|
|
||||||
func: async (name, tag) => {return await this.setPluginVersion(name, tag)},
|
|
||||||
type: 'call',
|
|
||||||
visibility: 'private'
|
|
||||||
},{
|
|
||||||
name: 'getLoadedPluginNames',
|
|
||||||
func: async () => {return await this.getPlugins().map(p => p.name)},
|
|
||||||
type: 'call',
|
|
||||||
visibility: 'private'
|
|
||||||
},{
|
|
||||||
name: 'selfUpdate',
|
|
||||||
type:'call',
|
|
||||||
func: async (force: boolean) => {return await this.selfUpdate(force)},
|
|
||||||
visibility: 'private'
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
private async selfUpdate(force:boolean = false){
|
|
||||||
const updater = new GitUpdater("./dist")
|
|
||||||
let status = await updater.getStatus()
|
|
||||||
if(force || !status.remote || !status.remote.includes("fb-dist/admin") || !status.exists || status.empty || !status.currentTag){
|
|
||||||
logger.warn("Cloning fb-dist/admin into ./dist ..."+(force?" USING FORCE!":""))
|
|
||||||
status = await updater.cloneRepo("https://gitea.frontblock.me/fb-dist/admin.git", force)
|
|
||||||
|
|
||||||
this.destroy()
|
|
||||||
const installer = require("./Installer").install
|
|
||||||
installer(this.getPlugins())
|
|
||||||
}
|
|
||||||
return status
|
|
||||||
}
|
|
||||||
|
|
||||||
async installPlugin(name: string, force:boolean = false):Promise<RepoFolderStatus>{
|
|
||||||
logger.warn("Cloning fb-dist/"+name+".git into ./plugins/"+name+" ..."+(force?" USING FORCE!":""))
|
|
||||||
this.pluginUpdaters[name] = new GitUpdater("./plugins/"+name)
|
|
||||||
const status = await this.pluginUpdaters[name].cloneRepo("https://gitea.frontblock.me/fb-dist/"+name.toLowerCase()+".git", force)
|
|
||||||
return status
|
|
||||||
}
|
|
||||||
|
|
||||||
async startPlugin(name:string):Promise<boolean>{
|
|
||||||
if(!this.pluginUpdaters[name]) return false
|
|
||||||
const status = await this.pluginUpdaters[name].getStatus()
|
|
||||||
if(!status.exists || status.empty || !status.tags || status.tags.length === 0){
|
|
||||||
if(status.currentTag && !status.latestTag){
|
|
||||||
//git glitches sometimes if you check immediately after clone
|
|
||||||
logger.warn("re-fetching tag for "+name+"...")
|
|
||||||
return await this.startPlugin(name)
|
|
||||||
}
|
|
||||||
logger.error("Bad repo status", name, status)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if(this.loadedPlugins[name]){
|
|
||||||
logger.error("Plugin", name, "is already started")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
let str = "../plugins/"+name+"/Plugin"
|
|
||||||
const pluginClass = await eval('require')(str)
|
|
||||||
const pluginObj = new pluginClass.default()
|
|
||||||
await pluginObj.start()
|
|
||||||
this.addPlugin(pluginObj)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
async updatePlugin(name:string):Promise<boolean>{
|
|
||||||
if(!this.pluginUpdaters[name]) return false
|
|
||||||
const status = await this.pluginUpdaters[name].getStatus()
|
|
||||||
if(!status.exists || status.empty || !status.tags || status.tags.length === 0){
|
|
||||||
logger.error("Bad repo status", name, status)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if(status.currentTag == status.latestTag){
|
|
||||||
logger.warn(name, "already at latest tag")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
if(this.loadedPlugins[name]){
|
|
||||||
logger.error("Plugin", name, "is running. Stop it first")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
this.pluginUpdaters[name].checkoutTag(status.latestTag!)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
async setPluginVersion(pluginName:string, tag:string):Promise<RepoFolderStatus>{
|
|
||||||
const status = await this.pluginUpdaters[pluginName].getStatus()
|
|
||||||
if(!status.exists || !status.tags || status.tags.length === 0 || !status.tags.includes(tag)){
|
|
||||||
logger.error("Bad repo status", pluginName, status)
|
|
||||||
return status
|
|
||||||
}
|
|
||||||
|
|
||||||
return await this.pluginUpdaters[pluginName].checkoutTag(tag)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
process.on( 'SIGINT', function() {
|
process.on( 'SIGINT', function() {
|
||||||
logger.info( "Gracefully shutting down from SIGINT (Ctrl-C)" );
|
logger.info( "Gracefully shutting down from SIGINT (Ctrl-C)" );
|
||||||
// some other closing procedures go here
|
// some other closing procedures go here
|
||||||
process.exit( );
|
process.exit(0);
|
||||||
})
|
})
|
||||||
@@ -1,35 +1,22 @@
|
|||||||
import * as Logger from 'log4js'
|
|
||||||
import { Plugin } from "frontblock-generic/Plugin"
|
import { Plugin } from "frontblock-generic/Plugin"
|
||||||
|
import { getLogger } from "frontblock-generic/Types"
|
||||||
var exec = require('child-process-promise').exec;
|
var exec = require('child-process-promise').exec;
|
||||||
|
|
||||||
Logger.configure({
|
const logger = getLogger("installer", 'info')
|
||||||
appenders:
|
|
||||||
{
|
|
||||||
"installer": { type: 'stdout' },
|
|
||||||
//app: { type: 'file', filename: 'application.log' }
|
|
||||||
},
|
|
||||||
categories:
|
|
||||||
{
|
|
||||||
default: { appenders: [ 'installer' ], level: 'debug' }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const logger = Logger.getLogger("installer")
|
|
||||||
|
|
||||||
export type NPMPkgName = string
|
export type NPMPkgName = string
|
||||||
export type NPMVersion = string
|
export type NPMVersion = string
|
||||||
|
|
||||||
export const install = (plugins: Plugin[] = []) => {
|
export const installAdmin = (plugins: Plugin[] = []) => {
|
||||||
|
|
||||||
const npmPkgs:[NPMPkgName, NPMVersion][] = [['sqlite3', '4.1.0'], ['knex', '0.19.2']]
|
const npmPkgs:[NPMPkgName, NPMVersion][] = [['sqlite3', '4.1.0'], ['knex', '0.19.2']]
|
||||||
const deps = npmPkgs.map(tuple => tuple.join('@') ).join(" ")
|
const deps = npmPkgs.map(tuple => tuple.join('@') ).join(" ")
|
||||||
logger.info("Installing plaform dependencies: "+deps)
|
logger.info("Installing plaform dependencies: "+deps)
|
||||||
|
|
||||||
exec("npm i --prefix ./plugins " + deps).then(process => {
|
exec("npm i " + deps).then(async process => {
|
||||||
|
|
||||||
logger.debug(process.stdout)
|
logger.debug(process.stdout)
|
||||||
const Admin = require("./Admin").FrontblockAdmin
|
const Admin = require("./Admin").FrontblockAdmin
|
||||||
new Admin(plugins)
|
const fbAdmin = new Admin(plugins)
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
import { install } from "./Installer";
|
import { installAdmin } from "./Installer";
|
||||||
install([])
|
installAdmin([])
|
||||||
|
|||||||
@@ -5,9 +5,13 @@ module.exports = [{
|
|||||||
target: "web",
|
target: "web",
|
||||||
|
|
||||||
entry: path.resolve(__dirname, '../../lib/FrontblockLib.js'),
|
entry: path.resolve(__dirname, '../../lib/FrontblockLib.js'),
|
||||||
|
externals: ['log4js'],
|
||||||
output: {
|
output: {
|
||||||
path: path.resolve(__dirname, '../../dist'),
|
path: path.resolve(__dirname, '../../dist'),
|
||||||
filename: 'FrontblockLib.js',
|
filename: 'FrontblockLib.js',
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
minimize: false, // <---- disables uglify.
|
||||||
}
|
}
|
||||||
},{
|
},{
|
||||||
mode: 'production',
|
mode: 'production',
|
||||||
@@ -20,7 +24,6 @@ module.exports = [{
|
|||||||
Buffer: true,
|
Buffer: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
resolve: {
|
resolve: {
|
||||||
// Add `.ts` and `.tsx` as a resolvable extension.
|
// Add `.ts` and `.tsx` as a resolvable extension.
|
||||||
|
|
||||||
@@ -32,9 +35,7 @@ module.exports = [{
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
externals:{
|
externals: ['knex'],
|
||||||
knex: "../plugins/node_modules/knex"
|
|
||||||
},
|
|
||||||
optimization: {
|
optimization: {
|
||||||
minimize: false
|
minimize: false
|
||||||
},
|
},
|
||||||
@@ -67,9 +68,7 @@ module.exports = [{
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
externals:{
|
externals:["./Installer"],
|
||||||
"./Installer": "./Installer"
|
|
||||||
},
|
|
||||||
optimization: {
|
optimization: {
|
||||||
minimize: false
|
minimize: false
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[submodule "src/app/apiclient"]
|
[submodule "src/app/paymentmanager"]
|
||||||
path = src/app/apiclient
|
path = src/app/paymentmanager
|
||||||
url = ssh://git@gitea.frontblock.me:2222/fb-plugin/apiclient.git
|
url = ssh://git@gitea.frontblock.me:2222/fb-plugin/paymentmanager.git
|
||||||
[submodule "src/app/wallet"]
|
[submodule "src/app/wallet"]
|
||||||
path = src/app/wallet
|
path = src/app/wallet
|
||||||
url = ssh://git@gitea.frontblock.me:2222/fb-plugin/wallet.git
|
url = ssh://git@gitea.frontblock.me:2222/fb-plugin/wallet.git
|
||||||
@@ -15,8 +15,10 @@
|
|||||||
"prefix": "app",
|
"prefix": "app",
|
||||||
"architect": {
|
"architect": {
|
||||||
"build": {
|
"build": {
|
||||||
"builder": "@angular-devkit/build-angular:browser",
|
"builder": "@angular-builders/custom-webpack:browser",
|
||||||
"options": {
|
"options": {
|
||||||
|
"customWebpackConfig": {"path": "./custom-webpack.config.js"},
|
||||||
|
|
||||||
"outputPath": "dist",
|
"outputPath": "dist",
|
||||||
"index": "src/index.html",
|
"index": "src/index.html",
|
||||||
"main": "src/main.ts",
|
"main": "src/main.ts",
|
||||||
@@ -78,7 +80,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"serve": {
|
"serve": {
|
||||||
"builder": "@angular-devkit/build-angular:dev-server",
|
"builder": "@angular-builders/custom-webpack:dev-server",
|
||||||
"tsConfig": "tsconfig.app.json",
|
"tsConfig": "tsconfig.app.json",
|
||||||
"options": {
|
"options": {
|
||||||
"browserTarget": "dashboard:build"
|
"browserTarget": "dashboard:build"
|
||||||
@@ -99,7 +101,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"test": {
|
"test": {
|
||||||
"builder": "@angular-devkit/build-angular:karma",
|
"builder": "@angular-builders/custom-webpack:karma",
|
||||||
"options": {
|
"options": {
|
||||||
"main": "src/test.ts",
|
"main": "src/test.ts",
|
||||||
"polyfills": "src/polyfills.ts",
|
"polyfills": "src/polyfills.ts",
|
||||||
@@ -116,7 +118,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lint": {
|
"lint": {
|
||||||
"builder": "@angular-devkit/build-angular:tslint",
|
"builder": "@angular-builders/custom-webpack:tslint",
|
||||||
"options": {
|
"options": {
|
||||||
"tsConfig": [
|
"tsConfig": [
|
||||||
"tsconfig.app.json",
|
"tsconfig.app.json",
|
||||||
@@ -130,7 +132,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"e2e": {
|
"e2e": {
|
||||||
"builder": "@angular-devkit/build-angular:protractor",
|
"builder": "@angular-builders/custom-webpack:protractor",
|
||||||
"options": {
|
"options": {
|
||||||
"protractorConfig": "e2e/protractor.conf.js",
|
"protractorConfig": "e2e/protractor.conf.js",
|
||||||
"devServerTarget": "dashboard:serve"
|
"devServerTarget": "dashboard:serve"
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = {
|
||||||
|
externals: ['log4js']
|
||||||
|
}
|
||||||
Generated
+180
-488
File diff suppressed because it is too large
Load Diff
@@ -7,13 +7,13 @@
|
|||||||
"start-prodlike": "npm run build-assets && ng serve --aot=false --optimization=false --proxy-config proxy.conf.json --configuration=prodlike",
|
"start-prodlike": "npm run build-assets && ng serve --aot=false --optimization=false --proxy-config proxy.conf.json --configuration=prodlike",
|
||||||
"build": "rm -f src/assets/*.js; ng build --prod --aot=false --optimization=false --build-optimizer=false",
|
"build": "rm -f src/assets/*.js; ng build --prod --aot=false --optimization=false --build-optimizer=false",
|
||||||
"build-assets": "npm run copy-frontends; npm run deep-clean-plugins",
|
"build-assets": "npm run copy-frontends; npm run deep-clean-plugins",
|
||||||
"update-frontblock": "rm -rf node_modules/frontblock*; npm install",
|
|
||||||
"get-submodules": "git submodule update --init && git submodule foreach git checkout master",
|
"get-submodules": "git submodule update --init && git submodule foreach git checkout master",
|
||||||
"copy-frontends": "for module in $(git config --file .gitmodules --get-regexp path | awk '{ print $2 }'); do npm i --prefix $module && npm run --prefix $module build && cp $module/dist/FrontendPlugin.js ./src/assets/$(basename $module).js; done",
|
"copy-frontends": "for module in $(git config --file .gitmodules --get-regexp path | awk '{ print $2 }'); do npm i --prefix $module && npm run --prefix $module build && cp $module/dist/FrontendPlugin.js ./src/assets/$(basename $module).js; done",
|
||||||
"deep-clean-plugins": "for module in $(git config --file .gitmodules --get-regexp path | awk '{ print $2 }'); do rm -rf $module/node_modules; done",
|
"deep-clean-plugins": "for module in $(git config --file .gitmodules --get-regexp path | awk '{ print $2 }'); do rm -rf $module/node_modules; done",
|
||||||
"test": "ng test",
|
"test": "ng test",
|
||||||
"lint": "ng lint",
|
"lint": "ng lint",
|
||||||
"e2e": "ng e2e"
|
"e2e": "ng e2e",
|
||||||
|
"update-frontblock": "npm remove frontblock frontblock-generic; npm install frontblock-generic@latest frontblock@latest"
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -32,8 +32,8 @@
|
|||||||
"@webcomponents/custom-elements": "^1.0.0",
|
"@webcomponents/custom-elements": "^1.0.0",
|
||||||
"btc-hdkey": "0.0.17",
|
"btc-hdkey": "0.0.17",
|
||||||
"coinselect": "^3.1.11",
|
"coinselect": "^3.1.11",
|
||||||
"frontblock": "latest",
|
"frontblock": "^0.15.1",
|
||||||
"frontblock-generic": "^0.23.0",
|
"frontblock-generic": "^0.34.1",
|
||||||
"key-file-storage": "^2.2.4",
|
"key-file-storage": "^2.2.4",
|
||||||
"node-fetch": "^2.6.0",
|
"node-fetch": "^2.6.0",
|
||||||
"rxjs": "~6.5.2",
|
"rxjs": "~6.5.2",
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
kind: pipeline
|
|
||||||
name: default
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: restore cache
|
|
||||||
image: drillster/drone-volume-cache
|
|
||||||
settings:
|
|
||||||
restore: true
|
|
||||||
mount:
|
|
||||||
- ./node_modules
|
|
||||||
volumes:
|
|
||||||
- name: cache
|
|
||||||
path: /cache
|
|
||||||
|
|
||||||
- name: npm install
|
|
||||||
image: node:12
|
|
||||||
commands:
|
|
||||||
- npm install
|
|
||||||
|
|
||||||
- name: npm run build
|
|
||||||
image: node:12
|
|
||||||
commands:
|
|
||||||
- npm run build
|
|
||||||
|
|
||||||
- name: rebuild cache
|
|
||||||
image: drillster/drone-volume-cache
|
|
||||||
settings:
|
|
||||||
rebuild: true
|
|
||||||
mount:
|
|
||||||
- ./node_modules
|
|
||||||
volumes:
|
|
||||||
- name: cache
|
|
||||||
path: /cache
|
|
||||||
|
|
||||||
- name: npm publish
|
|
||||||
image: plugins/npm
|
|
||||||
settings:
|
|
||||||
username: frontblock
|
|
||||||
password:
|
|
||||||
from_secret: npm_password
|
|
||||||
email: frontblock.me@gmail.com
|
|
||||||
event:
|
|
||||||
- tag
|
|
||||||
|
|
||||||
- name: deploy plugin
|
|
||||||
image: node:12
|
|
||||||
commands:
|
|
||||||
- git config --global user.email "${DRONE_COMMIT_AUTHOR_EMAIL}"
|
|
||||||
- git config --global user.name "${DRONE_COMMIT_AUTHOR}"
|
|
||||||
- git clone https://gitea.frontblock.me/fb-dist/${DRONE_REPO_NAME}.git
|
|
||||||
- cp -r ./dist/* ./${DRONE_REPO_NAME}
|
|
||||||
- cd ./${DRONE_REPO_NAME}
|
|
||||||
- git add -A
|
|
||||||
- git commit --allow-empty -m "drone tagged as version ${DRONE_TAG}"
|
|
||||||
- git tag ${DRONE_TAG}
|
|
||||||
- git push https://$GIT_USER:$GIT_PASSWORD@gitea.frontblock.me/fb-dist/${DRONE_REPO_NAME}.git master ${DRONE_TAG}
|
|
||||||
environment:
|
|
||||||
GIT_USER:
|
|
||||||
from_secret: git_user
|
|
||||||
GIT_PASSWORD:
|
|
||||||
from_secret: git_password
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
- tag
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
- name: cache
|
|
||||||
host:
|
|
||||||
path: /tmp
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
dist
|
|
||||||
.rpt2_cache
|
|
||||||
node_modules
|
|
||||||
lib
|
|
||||||
|
|
||||||
*.d.ts
|
|
||||||
*.js
|
|
||||||
*.ts
|
|
||||||
|
|
||||||
!src/**/*
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
**/*
|
|
||||||
!FrontblockApiClient.*
|
|
||||||
+3
-6
@@ -1,16 +1,15 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { FrontblockApiConf } from '../backend/FrontblockApiClient';
|
|
||||||
import { isDevMode } from '@angular/core';
|
import { isDevMode } from '@angular/core';
|
||||||
|
|
||||||
declare const fb
|
declare const fb
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'settings',
|
selector: '[apiclientconfig]',
|
||||||
template: `
|
template: `
|
||||||
<div class="clr-row">
|
<div class="clr-row">
|
||||||
<div class="card clr-col-12 clr-col-sm-12 clr-col-md-12 clr-col-lg-auto clr-col-xl-auto">
|
<div class="card clr-col-12 clr-col-sm-12 clr-col-md-12 clr-col-lg-auto clr-col-xl-auto">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
Settings
|
Frontblock API Client
|
||||||
</div>
|
</div>
|
||||||
<div class="card-block">
|
<div class="card-block">
|
||||||
<div class="card-text">
|
<div class="card-text">
|
||||||
@@ -61,15 +60,13 @@ declare const fb
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<knex-config fbPlugin="ApiClient"></knex-config>
|
|
||||||
`
|
`
|
||||||
})
|
})
|
||||||
export class ApiclientFormComponent implements OnInit {
|
export class ApiclientFormComponent implements OnInit {
|
||||||
testnet: boolean = true
|
testnet: boolean = true
|
||||||
saving: boolean = false
|
saving: boolean = false
|
||||||
advanced: boolean = false
|
advanced: boolean = false
|
||||||
data: FrontblockApiConf = {
|
data: any = {
|
||||||
apiHost: "api.testnet.frontblock.me",
|
apiHost: "api.testnet.frontblock.me",
|
||||||
apiPort: 10001,
|
apiPort: 10001,
|
||||||
tls: false,
|
tls: false,
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
|
||||||
|
declare const fb
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: '[apiclientconfig]',
|
||||||
|
template: `
|
||||||
|
consumers {{consumers}} subscribers {{subscribers}}
|
||||||
|
`
|
||||||
|
})
|
||||||
|
export class ApiclientWidgetComponent implements OnInit {
|
||||||
|
|
||||||
|
consumers = 0
|
||||||
|
subscribers = 0
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
async ngOnInit() {
|
||||||
|
await new Promise((resolve, reject)=>{
|
||||||
|
let awaitAdmin: { (): void; (...args: any[]): void; }
|
||||||
|
(awaitAdmin = () => {
|
||||||
|
if(fb.ApiClient != null){
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
setTimeout(awaitAdmin,25)
|
||||||
|
})()
|
||||||
|
})
|
||||||
|
|
||||||
|
fb.ApiClient.getConsumers().then(cons => this.consumers = cons.length)
|
||||||
|
fb.ApiClient.getSubscriptions().then(subs => this.subscribers = subs.length)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"apiConf": {
|
|
||||||
"apiHost": "api.testnet.frontblock.me",
|
|
||||||
"apiKey": "",
|
|
||||||
"apiPort": 10001,
|
|
||||||
"tls": false
|
|
||||||
},
|
|
||||||
"dbConf": {
|
|
||||||
"type": "sqlite",
|
|
||||||
"database": "./data/ApiClient.sqlite",
|
|
||||||
"synchronize": true,
|
|
||||||
"entities": [
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+25
-16
@@ -6,47 +6,56 @@ import { ApiclientFormComponent } from './apiclient-settings-form.component';
|
|||||||
import { ApiclientConsumptionComponent } from './apiclient-consumptions.component';
|
import { ApiclientConsumptionComponent } from './apiclient-consumptions.component';
|
||||||
import { ApiclientSubscriptionComponent } from './apiclient-subscriptions.component';
|
import { ApiclientSubscriptionComponent } from './apiclient-subscriptions.component';
|
||||||
|
|
||||||
import { FrontendPlugin, SidebarEntries } from 'frontblock-generic/Plugin';
|
|
||||||
import { ClarityModule } from '@clr/angular';
|
import { ClarityModule } from '@clr/angular';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
// @ts-ignore
|
|
||||||
import { KnexConfigModule } from "../../../knex-config/knex-config.module";
|
import {FrontendPlugin, SidebarEntries, SidebarEntry} from 'frontblock-generic/Plugin'
|
||||||
|
import { ApiclientWidgetComponent } from './apiclient-widget.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
FormsModule,
|
FormsModule,
|
||||||
ClarityModule,
|
ClarityModule,
|
||||||
KnexConfigModule,
|
|
||||||
CommonModule,
|
CommonModule,
|
||||||
RouterModule.forChild([
|
RouterModule.forChild([
|
||||||
{path: "settings", component: ApiclientFormComponent},
|
|
||||||
{path: "subscriptions", component: ApiclientSubscriptionComponent},
|
{path: "subscriptions", component: ApiclientSubscriptionComponent},
|
||||||
{path: "consumers", component: ApiclientConsumptionComponent},
|
{path: "consumers", component: ApiclientConsumptionComponent},
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
exports: [RouterModule],
|
exports: [RouterModule, ApiclientFormComponent],
|
||||||
declarations: [
|
declarations: [
|
||||||
ApiclientFormComponent,
|
|
||||||
ApiclientConsumptionComponent,
|
ApiclientConsumptionComponent,
|
||||||
ApiclientSubscriptionComponent
|
ApiclientSubscriptionComponent,
|
||||||
|
ApiclientFormComponent,
|
||||||
|
ApiclientWidgetComponent
|
||||||
|
],
|
||||||
|
entryComponents: [
|
||||||
|
ApiclientFormComponent,
|
||||||
|
ApiclientWidgetComponent
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class PluginModule implements FrontendPlugin{
|
export class ApiclientModule implements FrontendPlugin<typeof ApiclientFormComponent, typeof ApiclientWidgetComponent>{
|
||||||
getSidebarEntry(): SidebarEntries {
|
getSidebarEntry(): SidebarEntry | SidebarEntries {
|
||||||
return {
|
return {
|
||||||
icon: "terminal",
|
icon: "terminal",
|
||||||
parentRoute: "apiclient",
|
parentRoute: "apiclient",
|
||||||
text: "Api client",
|
text: "Api client",
|
||||||
links: [{
|
links: [{
|
||||||
route: "settings",
|
route: "apiclient/consumers",
|
||||||
text: "Settings"
|
|
||||||
},{
|
|
||||||
route: "consumers",
|
|
||||||
text: "Consumers"
|
text: "Consumers"
|
||||||
},{
|
},{
|
||||||
route: "subscriptions",
|
route: "apiclient/subscriptions",
|
||||||
text: "Subscriptions"
|
text: "Subscriptions"
|
||||||
},]
|
}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getSettingsComponent(): typeof ApiclientFormComponent{
|
||||||
|
return ApiclientFormComponent
|
||||||
|
}
|
||||||
|
|
||||||
|
getWidget(): typeof ApiclientWidgetComponent{
|
||||||
|
return ApiclientWidgetComponent
|
||||||
|
}
|
||||||
}
|
}
|
||||||
-4652
File diff suppressed because it is too large
Load Diff
@@ -1,45 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "frontblock",
|
|
||||||
"version": "0.14.0",
|
|
||||||
"description": "frontblock shop-side library ",
|
|
||||||
"main": "FrontblockApiClient.js",
|
|
||||||
"scripts": {
|
|
||||||
"tsc": "tsc",
|
|
||||||
"build-backend": "webpack --config src/backend/webpack.prod.js --progress --colors",
|
|
||||||
"build-frontend": "webpack --config src/frontend/webpack.prod.js --progress --colors",
|
|
||||||
"build": "npm run clean; npm run tsc; npm run build-backend; npm run build-frontend",
|
|
||||||
"clean": "rm -rf *.js *.ts backend frontend .rpt2_cache dist lib",
|
|
||||||
"update-frontblock": "rm -rf node_modules/frontblock*; npm install"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"api",
|
|
||||||
"client",
|
|
||||||
"frontblock"
|
|
||||||
],
|
|
||||||
"author": "",
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"@angular/common": "^8.2.1",
|
|
||||||
"@angular/core": "^8.2.1",
|
|
||||||
"@angular/forms": "^8.2.1",
|
|
||||||
"@angular/platform-browser": "^8.2.1",
|
|
||||||
"@angular/router": "^8.2.1",
|
|
||||||
"@clr/angular": "^2.1.1",
|
|
||||||
"@types/node": "^11.13.10",
|
|
||||||
"frontblock-generic": "^0.28.4",
|
|
||||||
"fs": "0.0.1-security",
|
|
||||||
"knex": "^0.19.2",
|
|
||||||
"log4js": "^4.3.1",
|
|
||||||
"node-fetch": "^2.5.0",
|
|
||||||
"path": "^0.12.7",
|
|
||||||
"rxjs": "^6.5.2",
|
|
||||||
"rxjs-compat": "^6.5.2"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"ts-loader": "^6.0.4",
|
|
||||||
"typescript": "^3.4.5",
|
|
||||||
"webpack": "^4.39.1",
|
|
||||||
"webpack-cli": "^3.3.6",
|
|
||||||
"webpack-node-externals": "^1.7.2"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
import { Coin, AccountMap, TransactionMap, SubscriptionResponse, ErrorResponse, SuccessResponse, parseResponse, parseSubResponse } from "frontblock-generic/Types";
|
|
||||||
import { FrontblockApi as FrontblockApi } from "frontblock-generic/Api";
|
|
||||||
import * as Logger from 'log4js'
|
|
||||||
|
|
||||||
Logger.configure({
|
|
||||||
appenders: {
|
|
||||||
"frontblock-api-client": { type: 'stdout' },
|
|
||||||
//app: { type: 'file', filename: 'application.log' }
|
|
||||||
},
|
|
||||||
categories: {
|
|
||||||
default: { appenders: ['frontblock-api-client'], level: 'debug' }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const logger = Logger.getLogger("frontblock-api-client")
|
|
||||||
const bsock = require('bsock')
|
|
||||||
|
|
||||||
export type FrontblockApiConf = {
|
|
||||||
apiHost: string
|
|
||||||
apiPort: number
|
|
||||||
tls?: boolean
|
|
||||||
apiKey?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Frontblock api connection lib
|
|
||||||
*/
|
|
||||||
export class FrontblockApiClient implements FrontblockApi {
|
|
||||||
protected started: boolean = false
|
|
||||||
protected apikey: string = ""
|
|
||||||
protected socket
|
|
||||||
|
|
||||||
constructor(private conf: FrontblockApiConf){
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
async consume<C extends Coin>(apikey: string, uid: string, callback: (tx: TransactionMap[C]) => void): Promise<SubscriptionResponse | ErrorResponse> {
|
|
||||||
const r = await this.socket.call("consume", apikey, uid)
|
|
||||||
const res = parseSubResponse(r)
|
|
||||||
if (res instanceof SubscriptionResponse) {
|
|
||||||
this.socket.hook(res.uid, (tx) => {
|
|
||||||
const deserialized = <TransactionMap[C]>JSON.parse(tx)
|
|
||||||
callback(deserialized)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
async quit(apikey: string, consumerUid: string): Promise<ErrorResponse | SuccessResponse> {
|
|
||||||
|
|
||||||
const r = await this.socket.call("quit", apikey, consumerUid)
|
|
||||||
const res = parseResponse(r)
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
async subscribe<C extends Coin>(apikey: string, coin: C, account: AccountMap[C]): Promise<SubscriptionResponse | ErrorResponse> {
|
|
||||||
const r = await this.socket.call("subscribe", apikey, coin, account)
|
|
||||||
const res = parseSubResponse(r)
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
async subsume<C extends Coin>(apikey: string, coin: C, account: AccountMap[C], callback: (tx: TransactionMap[C]) => void): Promise<SubscriptionResponse | ErrorResponse> {
|
|
||||||
const r = await this.socket.call("subsume", apikey, coin, account)
|
|
||||||
const res = parseSubResponse(r)
|
|
||||||
if (res instanceof SubscriptionResponse) {
|
|
||||||
this.socket.hook(res.uid, (tx) => {
|
|
||||||
const deserialized = <TransactionMap[C]>JSON.parse(tx)
|
|
||||||
callback(deserialized)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
async unsubscribe(apikey: string, uid: string): Promise<ErrorResponse | SuccessResponse> {
|
|
||||||
const r = await this.socket.call('unsubscribe', apikey, uid)
|
|
||||||
const res = parseResponse(r)
|
|
||||||
if (res instanceof SuccessResponse)
|
|
||||||
this.socket.unhook(uid)
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
async getPluginList(): Promise<string[]> {
|
|
||||||
return await this.socket.call('getPluginList')
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
connect(): void {
|
|
||||||
if (this.started) {
|
|
||||||
logger.warn("FrontblockApiClient has already been started. Ignoring")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logger.info("Starting apiClient with ", this.conf)
|
|
||||||
this.socket = bsock.connect(this.conf.apiPort, this.conf.apiHost, this.conf.tls != null ? this.conf.tls : false)
|
|
||||||
this.started = true
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnect(): void {
|
|
||||||
if (!this.started) {
|
|
||||||
logger.warn("FrontblockApiClient has not been started. Ignoring")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.socket.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,201 +0,0 @@
|
|||||||
import { FrontblockApiClient, FrontblockApiConf } from "./FrontblockApiClient";
|
|
||||||
import { socketioRPC } from "frontblock-generic/RPC"
|
|
||||||
import { DatabasePlugin, TableDefiniton } from "frontblock-generic/DatabasePlugin"
|
|
||||||
|
|
||||||
import { Coin, AccountMap, TransactionMap, SubscriptionResponse } from "frontblock-generic/Types";
|
|
||||||
import * as knex from 'knex';
|
|
||||||
import * as Logger from 'log4js'
|
|
||||||
Logger.configure({
|
|
||||||
appenders: {
|
|
||||||
"frontblock-api-client": { type: 'stdout' },
|
|
||||||
//app: { type: 'file', filename: 'application.log' }
|
|
||||||
},
|
|
||||||
categories: {
|
|
||||||
default: { appenders: [ 'frontblock-api-client' ], level: 'debug' }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const logger = Logger.getLogger("frontblock-api-client")
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Frontblock admin Plugin for FrontblockApiClient
|
|
||||||
*/
|
|
||||||
|
|
||||||
type ApiClientPluginConf = {
|
|
||||||
apiConf: FrontblockApiConf,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class FrontblockApiClientPlugin extends DatabasePlugin<ApiClientPluginConf>{
|
|
||||||
|
|
||||||
private apiClient : FrontblockApiClient
|
|
||||||
constructor(){
|
|
||||||
super("ApiClient")
|
|
||||||
}
|
|
||||||
|
|
||||||
getDefaultConfig(): ApiClientPluginConf & { dbConf: knex.Config; } {
|
|
||||||
return {
|
|
||||||
apiConf: {
|
|
||||||
apiHost: "api.testnet.frontblock.me",
|
|
||||||
apiKey: "",
|
|
||||||
apiPort: 10001,
|
|
||||||
tls: false
|
|
||||||
},
|
|
||||||
dbConf: {
|
|
||||||
client: 'sqlite3',
|
|
||||||
connection: {
|
|
||||||
filename: "./data/ApiClient.sqlite"
|
|
||||||
},
|
|
||||||
useNullAsDefault: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getConsumers(): Promise<SubscriptionResponse[]> {
|
|
||||||
const records = await this.knex.select('*').from('consumers')
|
|
||||||
return records.map(r => JSON.parse(r.JSON))
|
|
||||||
}
|
|
||||||
|
|
||||||
async getSubscriptions(): Promise<SubscriptionResponse[]> {
|
|
||||||
const records = await this.knex.select('*').from('subscriptions')
|
|
||||||
return records.map(r => JSON.parse(r.JSON))
|
|
||||||
}
|
|
||||||
|
|
||||||
exportExtraRPCs(): socketioRPC[] {
|
|
||||||
const quit = async(uid:string) => {return await this.quit(this.getConfigKey('apiConf').apiKey, uid) }
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
name: "subscribe",
|
|
||||||
func: async <C extends Coin>(coin: C, account: AccountMap[C]) => { return await this.subscribe(this.getConfigKey('apiConf').apiKey, coin, account)},
|
|
||||||
type: 'call',
|
|
||||||
visibility: 'private'
|
|
||||||
},{
|
|
||||||
name: "subsume",
|
|
||||||
func: async <C extends Coin>(coin: C, account: AccountMap[C], callback: (tx: TransactionMap[C]) => void) => { return await this.subsume(this.getConfigKey('apiConf').apiKey, coin, account, callback)},
|
|
||||||
type: 'hook',
|
|
||||||
unhook: quit,
|
|
||||||
visibility: 'private'
|
|
||||||
},{
|
|
||||||
name: 'unsubscribe',
|
|
||||||
func: async(uid:string) => { return await this.unsubscribe(this.getConfigKey('apiConf').apiKey, uid) },
|
|
||||||
type: 'call',
|
|
||||||
visibility: 'private'
|
|
||||||
},{
|
|
||||||
name: 'consume',
|
|
||||||
func: async<C extends Coin>(uid: string, callback: (tx: TransactionMap[C]) => void) => {return await this.consume(this.getConfigKey('apiConf').apiKey, uid, callback)},
|
|
||||||
type: 'hook',
|
|
||||||
unhook: quit,
|
|
||||||
visibility: 'private'
|
|
||||||
},{
|
|
||||||
name: 'quit',
|
|
||||||
func: quit,
|
|
||||||
type: 'call',
|
|
||||||
visibility: 'private'
|
|
||||||
},{
|
|
||||||
name: "getSubscriptions",
|
|
||||||
func: async() => { return await this.getSubscriptions() },
|
|
||||||
type: "call",
|
|
||||||
visibility: "private"
|
|
||||||
},{
|
|
||||||
name: "getConsumers",
|
|
||||||
func: async() => { return await this.getConsumers() },
|
|
||||||
type: "call",
|
|
||||||
visibility: "private"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
private subscribe:FrontblockApiClient['subscribe'] = async (apiKey, coin, account) => {
|
|
||||||
const res = await this.apiClient.subscribe(apiKey, coin, account)
|
|
||||||
if(res instanceof SubscriptionResponse){
|
|
||||||
await this.knex('subscriptions').insert([{uuid: res.uid, JSON: JSON.stringify(res)}])
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
private consume:FrontblockApiClient['consume'] = async (apikey, uuid, callback) => {
|
|
||||||
const res = await this.apiClient.consume(apikey, uuid, callback)
|
|
||||||
if(res instanceof SubscriptionResponse){
|
|
||||||
await this.knex('consumers').insert([{uuid: res.uid, JSON: JSON.stringify(res)}])
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
private unsubscribe:FrontblockApiClient['unsubscribe'] = async (apikey, uuid) => {
|
|
||||||
const res = await this.apiClient.unsubscribe(apikey, uuid)
|
|
||||||
await this.knex('subscriptions')
|
|
||||||
.where('uuid', uuid)
|
|
||||||
.del()
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
private quit:FrontblockApiClient['quit'] = async (apikey, uuid) => {
|
|
||||||
const res = await this.apiClient.quit(apikey, uuid)
|
|
||||||
await this.knex('consumers')
|
|
||||||
.where('uuid', uuid)
|
|
||||||
.del()
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
private subsume:FrontblockApiClient['subsume'] = async (apikey, coin, account, callback) => {
|
|
||||||
const res = await this.apiClient.subsume(apikey, coin, account, callback)
|
|
||||||
if(res instanceof SubscriptionResponse){
|
|
||||||
await Promise.all([
|
|
||||||
this.knex('consumers')
|
|
||||||
.insert([{uuid: res.uid, JSON: JSON.stringify(res)}]),
|
|
||||||
|
|
||||||
this.knex('subscriptions')
|
|
||||||
.insert([{uuid: res.uid, JSON: JSON.stringify(new SubscriptionResponse(res.message))}])
|
|
||||||
])
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
private makeApiClient(conf: FrontblockApiConf):FrontblockApiClient{
|
|
||||||
if(this.apiClient)
|
|
||||||
this.apiClient.disconnect()
|
|
||||||
this.apiClient = new FrontblockApiClient(conf)
|
|
||||||
this.apiClient.connect()
|
|
||||||
return this.apiClient
|
|
||||||
}
|
|
||||||
|
|
||||||
setConfig(conf:ApiClientPluginConf & { dbConf: knex.Config }):ApiClientPluginConf & { dbConf: knex.Config }{
|
|
||||||
const currConf = this.getConfig()
|
|
||||||
if(JSON.stringify(currConf.apiConf) !== JSON.stringify(conf.apiConf)){
|
|
||||||
this.makeApiClient(conf.apiConf)
|
|
||||||
}
|
|
||||||
return super.setConfig(conf)
|
|
||||||
}
|
|
||||||
|
|
||||||
setConfigKey(key:keyof ApiClientPluginConf, value: any):ApiClientPluginConf & { dbConf: knex.Config; }{
|
|
||||||
if(key === 'apiConf'){
|
|
||||||
this.makeApiClient(value)
|
|
||||||
}
|
|
||||||
return super.setConfigKey(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async start(): Promise<void> {
|
|
||||||
this.makeApiClient(this.getConfig().apiConf)
|
|
||||||
super.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
stop(): void {
|
|
||||||
this.apiClient.disconnect()
|
|
||||||
super.stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
protected getTableDefinitions(): TableDefiniton[] {
|
|
||||||
return [{
|
|
||||||
name: 'subscriptions',
|
|
||||||
tableBuilder: function (table) {
|
|
||||||
table.string('uuid').primary();
|
|
||||||
table.string('JSON');
|
|
||||||
}
|
|
||||||
},{
|
|
||||||
name: 'consumers',
|
|
||||||
tableBuilder: function (table) {
|
|
||||||
table.string('uuid').primary();
|
|
||||||
table.string('JSON');
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
const nodeExternals = require('webpack-node-externals');
|
|
||||||
const TerserPlugin = require('terser-webpack-plugin');
|
|
||||||
var webpack = require('webpack');
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
mode: 'production',
|
|
||||||
target: "node",
|
|
||||||
entry: path.resolve(__dirname, 'Plugin.ts'),
|
|
||||||
output: {
|
|
||||||
path: path.resolve(__dirname, '../../dist'),
|
|
||||||
filename: 'Plugin.js',
|
|
||||||
libraryTarget: 'commonjs',
|
|
||||||
},
|
|
||||||
resolve: {
|
|
||||||
// Add `.ts` and `.tsx` as a resolvable extension.
|
|
||||||
|
|
||||||
extensions: [".ts", ".tsx", ".js"]
|
|
||||||
},
|
|
||||||
module: {
|
|
||||||
rules: [
|
|
||||||
{ test: /\.ts?$/, loader: "ts-loader" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
optimization: {
|
|
||||||
minimize: false
|
|
||||||
},
|
|
||||||
node: {
|
|
||||||
global: true,
|
|
||||||
process: true,
|
|
||||||
__filename: false,
|
|
||||||
__dirname: false,
|
|
||||||
Buffer: true,
|
|
||||||
},
|
|
||||||
externals:{
|
|
||||||
knex: "../node_modules/knex"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "./tsconfig.json",
|
|
||||||
"include": ["src/frontend"],
|
|
||||||
"exclude": [
|
|
||||||
"src/backend"
|
|
||||||
],
|
|
||||||
"compilerOptions": {
|
|
||||||
"sourceMap": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"allowSyntheticDefaultImports": true,
|
|
||||||
"alwaysStrict": false,
|
|
||||||
"noImplicitAny": false,
|
|
||||||
"target": "ES2015",
|
|
||||||
"module": "commonjs",
|
|
||||||
"declaration": true,
|
|
||||||
"strict": true,
|
|
||||||
"outDir": ".",
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"strictPropertyInitialization": false,
|
|
||||||
"importHelpers": true,
|
|
||||||
"strictNullChecks": false,
|
|
||||||
},
|
|
||||||
"include": ["src/backend"],
|
|
||||||
"exclude": []
|
|
||||||
}
|
|
||||||
@@ -2,13 +2,24 @@ import { NgModule } from '@angular/core';
|
|||||||
import { Routes, RouterModule } from '@angular/router';
|
import { Routes, RouterModule } from '@angular/router';
|
||||||
import { HomeComponent } from './home/home.component';
|
import { HomeComponent } from './home/home.component';
|
||||||
import { ErrorDisplayComponent } from './error-display/error-display.component';
|
import { ErrorDisplayComponent } from './error-display/error-display.component';
|
||||||
|
import { SettingsComponent } from './settings/settings.component';
|
||||||
|
|
||||||
const routes: Routes = [{
|
const routes: Routes = [{
|
||||||
|
path: "apiclient",
|
||||||
|
loadChildren: () => import('./apiclient/module').then(mod => mod.ApiclientModule)
|
||||||
|
},{
|
||||||
path: "pluginmanager",
|
path: "pluginmanager",
|
||||||
loadChildren: () => import('./pluginmanager/module').then(mod => mod.PluginModule)
|
loadChildren: () => import('./pluginmanager/module').then(mod => mod.PluginmanagerModule)
|
||||||
|
},{
|
||||||
|
path: "settings",
|
||||||
|
component: SettingsComponent
|
||||||
|
},{
|
||||||
|
path: "home",
|
||||||
|
component: HomeComponent
|
||||||
},{
|
},{
|
||||||
path: "",
|
path: "",
|
||||||
component: HomeComponent
|
pathMatch: "full",
|
||||||
|
redirectTo: "home",
|
||||||
},{
|
},{
|
||||||
path: "**",
|
path: "**",
|
||||||
component: ErrorDisplayComponent
|
component: ErrorDisplayComponent
|
||||||
|
|||||||
@@ -20,13 +20,16 @@ export class AppComponent implements AfterContentInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngAfterContentInit(){
|
ngAfterContentInit(){
|
||||||
const _this = this;
|
let awaitSidebar: { (): void; (...args: any[]): void; }
|
||||||
(function awaitSidebar(){
|
(awaitSidebar = () => {
|
||||||
if(_this.sidebar != null){
|
if(this.sidebar != null){
|
||||||
_this.sidebarService.setSidebar(_this.sidebar)
|
this.sidebarService.setSidebar(this.sidebar)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setTimeout(awaitSidebar,25)
|
setTimeout(awaitSidebar,25)
|
||||||
})()
|
})()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,8 +17,12 @@ import { HomeComponent } from './home/home.component';
|
|||||||
import { ErrorDisplayComponent } from './error-display/error-display.component';
|
import { ErrorDisplayComponent } from './error-display/error-display.component';
|
||||||
import { HeaderBarComponent } from './header-bar/header-bar.component';
|
import { HeaderBarComponent } from './header-bar/header-bar.component';
|
||||||
import { SubnavComponent } from './subnav/subnav.component';
|
import { SubnavComponent } from './subnav/subnav.component';
|
||||||
|
import { KnexConfigComponent } from './knex-config/knex-config.component';
|
||||||
|
import { ApiclientFormComponent } from './apiclient/apiclient-settings-form.component';
|
||||||
|
import { SettingsComponent } from './settings/settings.component';
|
||||||
|
import { ApiclientModule } from './apiclient/module';
|
||||||
|
import { WalletsModule } from './wallets/module';
|
||||||
|
|
||||||
import { environment } from 'src/environments/environment';
|
|
||||||
|
|
||||||
export function createCompiler(fn: CompilerFactory): Compiler {
|
export function createCompiler(fn: CompilerFactory): Compiler {
|
||||||
return fn.createCompiler();
|
return fn.createCompiler();
|
||||||
@@ -34,16 +38,20 @@ const declarations = [
|
|||||||
HeaderBarComponent,
|
HeaderBarComponent,
|
||||||
ErrorDisplayComponent,
|
ErrorDisplayComponent,
|
||||||
SubnavComponent,
|
SubnavComponent,
|
||||||
|
KnexConfigComponent,
|
||||||
|
SettingsComponent
|
||||||
]
|
]
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: declarations,
|
declarations: declarations,
|
||||||
imports: [
|
imports: [
|
||||||
|
WalletsModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
BrowserAnimationsModule,
|
BrowserAnimationsModule,
|
||||||
ClarityModule,
|
ClarityModule,
|
||||||
AppRoutingModule,
|
AppRoutingModule,
|
||||||
|
ApiclientModule
|
||||||
],
|
],
|
||||||
entryComponents: [],
|
entryComponents: [],
|
||||||
providers: [
|
providers: [
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import * as btcHdkey from "btc-hdkey"
|
|||||||
import * as bitcoinjslib from "bitcoinjs-lib"
|
import * as bitcoinjslib from "bitcoinjs-lib"
|
||||||
import * as fetch from "node-fetch"
|
import * as fetch from "node-fetch"
|
||||||
import * as coinselect from "coinselect/accumulative"
|
import * as coinselect from "coinselect/accumulative"
|
||||||
import * as KnexModule from '../knex-config/knex-config.module'
|
|
||||||
|
|
||||||
SystemJS.set('@clr/angular', SystemJS.newModule(clarityModule));
|
SystemJS.set('@clr/angular', SystemJS.newModule(clarityModule));
|
||||||
SystemJS.set('@angular/router', SystemJS.newModule(angularRouter));
|
SystemJS.set('@angular/router', SystemJS.newModule(angularRouter));
|
||||||
@@ -40,7 +39,6 @@ SystemJS.set('btc-hdkey', SystemJS.newModule(btcHdkey))
|
|||||||
SystemJS.set('bitcoinjs-lib', SystemJS.newModule(bitcoinjslib))
|
SystemJS.set('bitcoinjs-lib', SystemJS.newModule(bitcoinjslib))
|
||||||
SystemJS.set('node-fetch', SystemJS.newModule(fetch))
|
SystemJS.set('node-fetch', SystemJS.newModule(fetch))
|
||||||
SystemJS.set('coinselect/accumulative', SystemJS.newModule(coinselect))
|
SystemJS.set('coinselect/accumulative', SystemJS.newModule(coinselect))
|
||||||
SystemJS.set('knexconfig', SystemJS.newModule(KnexModule))
|
|
||||||
|
|
||||||
SystemJS.config({ meta: { '*': { authorization: true } } });
|
SystemJS.config({ meta: { '*': { authorization: true } } });
|
||||||
/** --------- */
|
/** --------- */
|
||||||
@@ -53,7 +51,7 @@ import { environment } from "../../environments/environment"
|
|||||||
|
|
||||||
const fb = environment.production ? window["fb"] : {
|
const fb = environment.production ? window["fb"] : {
|
||||||
Admin: {
|
Admin: {
|
||||||
getLoadedPluginNames: () => ["ApiClient", "Wallet"]
|
getLoadedPluginNames: () => []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,6 +86,7 @@ export class DynamicLoaderComponent implements AfterViewInit {
|
|||||||
private async installWidget(pluginName: string) {
|
private async installWidget(pluginName: string) {
|
||||||
let module;
|
let module;
|
||||||
if (!angularCore.isDevMode()){
|
if (!angularCore.isDevMode()){
|
||||||
|
console.log("Loading "+pluginName+" from backend")
|
||||||
module = await SystemJS.import("plugins/" + pluginName + ".js");
|
module = await SystemJS.import("plugins/" + pluginName + ".js");
|
||||||
}else{
|
}else{
|
||||||
if(environment.loadLocal){
|
if(environment.loadLocal){
|
||||||
@@ -99,7 +98,18 @@ export class DynamicLoaderComponent implements AfterViewInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.injectPlugin(module)
|
||||||
|
this.compileModule(module)
|
||||||
|
}
|
||||||
|
|
||||||
|
compileModule(module:any){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
injectPlugin(module:any):void{
|
||||||
const plugin:FrontendPlugin = new module['PluginModule']()
|
const plugin:FrontendPlugin = new module['PluginModule']()
|
||||||
|
if(!plugin.getSidebarEntry) return
|
||||||
|
|
||||||
const entry: SidebarEntries | SidebarEntry = plugin.getSidebarEntry()
|
const entry: SidebarEntries | SidebarEntry = plugin.getSidebarEntry()
|
||||||
const rc = this.router.config
|
const rc = this.router.config
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
<clr-header class="header header-1">
|
<clr-header class="header header-7">
|
||||||
<div class="branding">
|
<div class="branding">
|
||||||
<span class="title">Frontblock</span>
|
<a class="nav-link" [routerLink]="'home'">
|
||||||
|
<img src="assets/logo_real.png" class="clr-icon" />
|
||||||
|
<span class="title"> Frontblock</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="header-actions">
|
<div class="header-actions" style="z-index:2">
|
||||||
<a class="nav-link nav-icon-text">
|
<a class="nav-link nav-icon" aria-label="bell" routerLinkActive="active" [routerLink]="'notifications'">
|
||||||
<clr-icon shape="bug"></clr-icon>
|
<clr-icon class="has-badge" shape="bell"> </clr-icon>
|
||||||
<span class="nav-text">ALPHA {{devmode}}</span>
|
</a>
|
||||||
|
<a class="nav-link nav-icon" routerLinkActive="active" [routerLink]="'settings'">
|
||||||
|
<clr-icon class="" shape="cog" ></clr-icon>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</clr-header>
|
</clr-header>
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
<p>home works!</p>
|
<ng-container #dynamicWidgets></ng-container>
|
||||||
|
|||||||
@@ -1,15 +1,35 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit, ViewChild, ViewContainerRef, Injector, ComponentFactoryResolver, AfterViewInit } from '@angular/core';
|
||||||
|
import { FrontendPlugin } from 'frontblock-generic/Plugin';
|
||||||
|
import { ApiclientModule } from '../apiclient/module';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-home',
|
selector: 'app-home',
|
||||||
templateUrl: './home.component.html',
|
templateUrl: './home.component.html',
|
||||||
styleUrls: ['./home.component.scss']
|
styleUrls: ['./home.component.scss']
|
||||||
})
|
})
|
||||||
export class HomeComponent implements OnInit {
|
export class HomeComponent implements AfterViewInit {
|
||||||
|
|
||||||
constructor() { }
|
@ViewChild('dynamicWidgets', {read: ViewContainerRef, static: false})
|
||||||
|
settingsContainer: ViewContainerRef
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private componentFactoryResolver: ComponentFactoryResolver,
|
||||||
|
private injector: Injector
|
||||||
|
) { }
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
this.injectModule(new ApiclientModule())
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
injectModule(module:FrontendPlugin<any>) {
|
||||||
|
if(!module.getSettingsComponent) return
|
||||||
|
|
||||||
|
//@ts-ignore
|
||||||
|
const factory = this.componentFactoryResolver.resolveComponentFactory(module.getWidget())
|
||||||
|
const component = factory.create(this.injector)
|
||||||
|
setTimeout(() => this.settingsContainer.insert(component.hostView), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
<p>knex-config works!</p>
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Component, OnInit, Input } from '@angular/core';
|
import { Component, OnInit, Input } from '@angular/core';
|
||||||
|
|
||||||
declare const fb
|
declare const fb : { Admin: { getConfig:()=>any , setConfigKey:(knex:"dbConf", conf:KnexConfig)=>any } }
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'knex-config',
|
selector: 'knex-config',
|
||||||
@@ -78,7 +78,7 @@ declare const fb
|
|||||||
<form clrForm clrLayout="horizontal">
|
<form clrForm clrLayout="horizontal">
|
||||||
<clr-input-container >
|
<clr-input-container >
|
||||||
<label class="clr-col-12 clr-col-md-4">file</label>
|
<label class="clr-col-12 clr-col-md-4">file</label>
|
||||||
<input class="clr-col-12 clr-col-md-8" clrInput type="text" name="sqlite-file" [(ngModel)]="sqlite3['connection']['filename']" placeholder="./data/benis.sqlite" required />
|
<input class="clr-col-12 clr-col-md-8" clrInput type="text" name="sqlite-file" [(ngModel)]="sqlite3['connection']['filename']" placeholder="./data/db.sqlite" required />
|
||||||
</clr-input-container>
|
</clr-input-container>
|
||||||
</form>
|
</form>
|
||||||
</clr-tab-content>
|
</clr-tab-content>
|
||||||
@@ -98,8 +98,6 @@ declare const fb
|
|||||||
})
|
})
|
||||||
export class KnexConfigComponent implements OnInit{
|
export class KnexConfigComponent implements OnInit{
|
||||||
|
|
||||||
@Input("fbPlugin")
|
|
||||||
fbPlugin:string
|
|
||||||
conf: KnexConfig
|
conf: KnexConfig
|
||||||
|
|
||||||
pg:PgConfig = {
|
pg:PgConfig = {
|
||||||
@@ -138,10 +136,17 @@ export class KnexConfigComponent implements OnInit{
|
|||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit(){
|
async ngOnInit(){
|
||||||
if(!fb[this.fbPlugin]){
|
await new Promise((resolve, reject)=>{
|
||||||
throw new Error("Knex config component doesn't have fb."+this.fbPlugin)
|
let awaitAdmin: { (): void; (...args: any[]): void; }
|
||||||
}
|
(awaitAdmin = () => {
|
||||||
const c = await fb[this.fbPlugin].getConfig()
|
if(fb.Admin != null){
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
setTimeout(awaitAdmin,25)
|
||||||
|
})()
|
||||||
|
})
|
||||||
|
|
||||||
|
const c = await fb.Admin.getConfig()
|
||||||
this.conf = c.dbConf
|
this.conf = c.dbConf
|
||||||
|
|
||||||
Object.keys(this.mode).forEach(knexType => {
|
Object.keys(this.mode).forEach(knexType => {
|
||||||
@@ -155,9 +160,13 @@ export class KnexConfigComponent implements OnInit{
|
|||||||
window['knex'] = this
|
window['knex'] = this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getSettingsComponentClassName(){
|
||||||
|
return KnexConfigComponent
|
||||||
|
}
|
||||||
|
|
||||||
save(){
|
save(){
|
||||||
const modeName = Object.entries(this.mode).find(([key, active]) => active)[0]
|
const modeName = Object.entries(this.mode).find(([key, active]) => active)[0]
|
||||||
fb[this.fbPlugin].setConfigKey('dbConf', this[modeName]).then(console.log)
|
fb.Admin.setConfigKey('dbConf', this[modeName]).then(console.log)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { RouterModule } from '@angular/router';
|
|
||||||
import { KnexConfigComponent } from './knex-config.component';
|
|
||||||
import { ClarityModule } from '@clr/angular';
|
|
||||||
import { FormsModule } from '@angular/forms';
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
imports: [
|
|
||||||
CommonModule,
|
|
||||||
FormsModule,
|
|
||||||
ClarityModule,
|
|
||||||
],
|
|
||||||
exports: [
|
|
||||||
KnexConfigComponent
|
|
||||||
],
|
|
||||||
declarations: [
|
|
||||||
KnexConfigComponent
|
|
||||||
],
|
|
||||||
entryComponents: [],
|
|
||||||
providers: []
|
|
||||||
})
|
|
||||||
export class KnexConfigModule{}
|
|
||||||
@@ -27,5 +27,5 @@ import { FormsModule } from '@angular/forms';
|
|||||||
PluginsComponent
|
PluginsComponent
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class PluginModule{
|
export class PluginmanagerModule{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<knex-config></knex-config>
|
||||||
|
|
||||||
|
|
||||||
|
<ng-container #dynamicSettings></ng-container>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { SettingsComponent } from './settings.component';
|
||||||
|
|
||||||
|
describe('SettingsComponent', () => {
|
||||||
|
let component: SettingsComponent;
|
||||||
|
let fixture: ComponentFixture<SettingsComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ SettingsComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(SettingsComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { Component, ViewChild, ViewContainerRef, ComponentFactoryResolver, Injector, AfterViewInit } from '@angular/core';
|
||||||
|
import { ApiclientModule } from '../apiclient/module'
|
||||||
|
import { FrontendPlugin } from 'frontblock-generic/Plugin';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-settings',
|
||||||
|
templateUrl: './settings.component.html',
|
||||||
|
styleUrls: ['./settings.component.scss']
|
||||||
|
})
|
||||||
|
export class SettingsComponent implements AfterViewInit {
|
||||||
|
ngAfterViewInit(): void {
|
||||||
|
this.injectModule(new ApiclientModule())
|
||||||
|
// this.injectModule(new ApiclientModule())
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewChild('dynamicSettings', {read: ViewContainerRef, static: false})
|
||||||
|
settingsContainer: ViewContainerRef
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private componentFactoryResolver: ComponentFactoryResolver,
|
||||||
|
private injector: Injector
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
injectModule(module:FrontendPlugin<any>) {
|
||||||
|
if(!module.getSettingsComponent) return
|
||||||
|
|
||||||
|
const factory = this.componentFactoryResolver.resolveComponentFactory(module.getSettingsComponent())
|
||||||
|
const component = factory.create(this.injector)
|
||||||
|
setTimeout(() => this.settingsContainer.insert(component.hostView), 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,26 @@
|
|||||||
|
<!--
|
||||||
|
|
||||||
|
<nav class="sidenav">
|
||||||
|
<section class="sidenav-content">
|
||||||
|
<a *ngFor="let entry of entries" routerLinkActive="active" [routerLink]="entry.route" class="nav-link">
|
||||||
|
<clr-icon [attr.shape]="entry.icon" class="is-solid" clrVerticalNavIcon></clr-icon>{{entry.text}}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
<section class="nav-group" *ngFor="let e of multientires" >
|
||||||
|
<label>
|
||||||
|
<clr-icon [attr.shape]="e.icon" class="is-solid"></clr-icon>
|
||||||
|
{{e.text}}
|
||||||
|
</label>
|
||||||
|
<ul class="nav-list">
|
||||||
|
<li *ngFor="let l of e.links"><a class="nav-link" routerLinkActive="active" [routerLink]="l.route" >{{l.text}}</a></li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
<clr-vertical-nav [clr-nav-level]="1" class="nav-trigger--bottom" style="height: 100%;" [clrVerticalNavCollapsible]="true" [(clrVerticalNavCollapsed)]="collapsed" >
|
<clr-vertical-nav [clr-nav-level]="1" class="nav-trigger--bottom" style="height: 100%;" [clrVerticalNavCollapsible]="true" [(clrVerticalNavCollapsed)]="collapsed" >
|
||||||
<clr-vertical-nav-group *ngFor="let e of multientires" routerLinkActive="active">
|
<clr-vertical-nav-group *ngFor="let e of multientires" routerLinkActive="active">
|
||||||
<clr-icon [attr.shape]="e.icon" class="is-solid" clrVerticalNavIcon></clr-icon>
|
<clr-icon [attr.shape]="e.icon" class="is-solid" clrVerticalNavIcon></clr-icon>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { SidebarEntries, SidebarEntry } from 'frontblock-generic/Plugin';
|
import { SidebarEntries, SidebarEntry } from 'frontblock-generic/Plugin';
|
||||||
|
import { ApiclientModule } from '../apiclient/module';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'sidebar',
|
selector: 'sidebar',
|
||||||
@@ -10,23 +11,47 @@ export class SidebarComponent implements OnInit {
|
|||||||
|
|
||||||
collapsed = true
|
collapsed = true
|
||||||
|
|
||||||
entries:SidebarEntry[] = [
|
entries:SidebarEntry[] = []
|
||||||
]
|
|
||||||
|
|
||||||
multientires:SidebarEntries[] = [{
|
multientires:SidebarEntries[] = [
|
||||||
icon: "bundle",
|
<SidebarEntries> new ApiclientModule().getSidebarEntry(),
|
||||||
text: "Update Manager",
|
{
|
||||||
parentRoute: "pluginmanager",
|
icon: "bundle",
|
||||||
links: [{
|
text: "Update Manager",
|
||||||
route: "pluginmanager/debug",
|
parentRoute: "pluginmanager",
|
||||||
text: "DEBUG"
|
links: [{
|
||||||
},{
|
route: "pluginmanager/debug",
|
||||||
route: "pluginmanager/admin",
|
text: "DEBUG"
|
||||||
text: "Dashboard"
|
},{
|
||||||
},{
|
route: "pluginmanager/admin",
|
||||||
route: "pluginmanager/plugins",
|
text: "Dashboard"
|
||||||
text: "Plugins"
|
},{
|
||||||
}]
|
route: "pluginmanager/plugins",
|
||||||
|
text: "Plugins"
|
||||||
|
}]
|
||||||
|
},{
|
||||||
|
icon: "wallet",
|
||||||
|
text: "Wallets",
|
||||||
|
parentRoute: "wallet",
|
||||||
|
links: [{
|
||||||
|
route: "btc",
|
||||||
|
text: "BTC Wallet"
|
||||||
|
},{
|
||||||
|
route: "ltc",
|
||||||
|
text: "LTC Wallet"
|
||||||
|
},{
|
||||||
|
route: "eth",
|
||||||
|
text: "ETH Wallet"
|
||||||
|
},{
|
||||||
|
route: "xrp",
|
||||||
|
text: "XRP Wallet"
|
||||||
|
},{
|
||||||
|
route: "xlm",
|
||||||
|
text: "XLM Wallet"
|
||||||
|
},{
|
||||||
|
route: "settings",
|
||||||
|
text: "Settings"
|
||||||
|
}]
|
||||||
}]
|
}]
|
||||||
|
|
||||||
constructor() { }
|
constructor() { }
|
||||||
|
|||||||
@@ -1,7 +1,17 @@
|
|||||||
|
<!--
|
||||||
<nav class="subnav">
|
<nav class="subnav">
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link active" href="#">Dashboard</a>
|
<a class="header-nav" routerLinkActive="active" [routerLink]="'home'">
|
||||||
|
<clr-icon class="is-solid" shape="home" size="24"></clr-icon>
|
||||||
|
</a>
|
||||||
|
<a class="header-nav" routerLinkActive="active" [routerLink]="'settings'">
|
||||||
|
<clr-icon class="is-solid" shape="cog" size="24"></clr-icon>
|
||||||
|
</a>
|
||||||
|
<a class="header-nav" routerLinkActive="active" [routerLink]="'notifications'">
|
||||||
|
<clr-icon class="is-solid has-badge" shape="bell" size="24"></clr-icon>
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
-->
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
kind: pipeline
|
|
||||||
name: default
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: restore cache
|
|
||||||
image: drillster/drone-volume-cache
|
|
||||||
settings:
|
|
||||||
restore: true
|
|
||||||
mount:
|
|
||||||
- ./node_modules
|
|
||||||
volumes:
|
|
||||||
- name: cache
|
|
||||||
path: /cache
|
|
||||||
|
|
||||||
- name: npm install
|
|
||||||
image: node:12
|
|
||||||
commands:
|
|
||||||
- npm install
|
|
||||||
|
|
||||||
- name: npm run build
|
|
||||||
image: node:12
|
|
||||||
commands:
|
|
||||||
- npm run build
|
|
||||||
|
|
||||||
- name: rebuild cache
|
|
||||||
image: drillster/drone-volume-cache
|
|
||||||
settings:
|
|
||||||
rebuild: true
|
|
||||||
mount:
|
|
||||||
- ./node_modules
|
|
||||||
volumes:
|
|
||||||
- name: cache
|
|
||||||
path: /cache
|
|
||||||
|
|
||||||
- name: deploy plugin
|
|
||||||
image: node:12
|
|
||||||
commands:
|
|
||||||
- git config --global user.email "${DRONE_COMMIT_AUTHOR_EMAIL}"
|
|
||||||
- git config --global user.name "${DRONE_COMMIT_AUTHOR}"
|
|
||||||
- git clone https://gitea.frontblock.me/fb-dist/${DRONE_REPO_NAME}.git
|
|
||||||
- cp -r ./dist/* ./${DRONE_REPO_NAME}
|
|
||||||
- cd ./${DRONE_REPO_NAME}
|
|
||||||
- git add -A
|
|
||||||
- git commit --allow-empty -m "drone tagged as version ${DRONE_TAG}"
|
|
||||||
- git tag ${DRONE_TAG}
|
|
||||||
- git push https://$GIT_USER:$GIT_PASSWORD@gitea.frontblock.me/fb-dist/${DRONE_REPO_NAME}.git master ${DRONE_TAG}
|
|
||||||
environment:
|
|
||||||
GIT_USER:
|
|
||||||
from_secret: git_user
|
|
||||||
GIT_PASSWORD:
|
|
||||||
from_secret: git_password
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
- tag
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
- name: cache
|
|
||||||
host:
|
|
||||||
path: /tmp
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
dist
|
|
||||||
kfs
|
|
||||||
.rpt2_cache
|
|
||||||
node_modules
|
|
||||||
lib
|
|
||||||
|
|
||||||
*.d.ts
|
|
||||||
*.js
|
|
||||||
*.ts
|
|
||||||
|
|
||||||
!src/**/*
|
|
||||||
-5477
File diff suppressed because it is too large
Load Diff
@@ -1,57 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "frontblock",
|
|
||||||
"version": "0.9.10",
|
|
||||||
"description": "frontblock shop-side library ",
|
|
||||||
"main": "FrontblockApiClient.js",
|
|
||||||
"scripts": {
|
|
||||||
"build-backend": "webpack --config src/backend/webpack.prod.js --progress --colors",
|
|
||||||
"build-frontend": "webpack --config src/frontend/webpack.prod.js --progress --colors",
|
|
||||||
"build": "npm run clean; npm run build-backend; npm run build-frontend",
|
|
||||||
"clean": "rm -rf *.js *.ts backend frontend .rpt2_cache lib dist",
|
|
||||||
"update-frontblock": "rm -rf node_modules/frontblock*; npm install"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"api",
|
|
||||||
"client",
|
|
||||||
"frontblock"
|
|
||||||
],
|
|
||||||
"author": "",
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"@angular/common": "^8.2.1",
|
|
||||||
"@angular/core": "^8.2.1",
|
|
||||||
"@angular/forms": "^8.2.1",
|
|
||||||
"@angular/platform-browser": "^8.2.1",
|
|
||||||
"@angular/router": "^8.2.1",
|
|
||||||
"@clr/angular": "^2.1.1",
|
|
||||||
"@types/node": "^11.13.10",
|
|
||||||
"adm-zip": "^0.4.13",
|
|
||||||
"angular": "^1.7.8",
|
|
||||||
"bip39": "^3.0.2",
|
|
||||||
"bitcoinjs-lib": "^5.0.4",
|
|
||||||
"bsock": "^0.1.9",
|
|
||||||
"btc-hdkey": "0.0.17",
|
|
||||||
"coinselect": "^3.1.11",
|
|
||||||
"easy-unzip": "^1.1.0",
|
|
||||||
"express": "^4.16.4",
|
|
||||||
"frontblock-generic": "^0.28.4",
|
|
||||||
"hdkey": "^1.1.1",
|
|
||||||
"key-file-storage": "^2.2.1",
|
|
||||||
"knex": "^0.19.2",
|
|
||||||
"log4js": "^4.3.1",
|
|
||||||
"minimist": "^1.2.0",
|
|
||||||
"node-fetch": "^2.5.0",
|
|
||||||
"original-fs": "^1.1.0",
|
|
||||||
"rxjs": "^6.5.2",
|
|
||||||
"rxjs-compat": "^6.5.2",
|
|
||||||
"unzip": "^0.1.11",
|
|
||||||
"uuid": "^3.3.2"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"ts-loader": "^6.0.4",
|
|
||||||
"typescript": "^3.4.5",
|
|
||||||
"webpack": "^4.39.2",
|
|
||||||
"webpack-cli": "^3.3.6",
|
|
||||||
"webpack-node-externals": "^1.7.2"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
import { socketioRPC } from "frontblock-generic/RPC";
|
|
||||||
import { DatabasePlugin, TableDefiniton } from "frontblock-generic/DatabasePlugin"
|
|
||||||
import { Coin } from "frontblock-generic/Types";
|
|
||||||
import * as knex from 'knex';
|
|
||||||
import * as Logger from 'log4js'
|
|
||||||
Logger.configure({
|
|
||||||
appenders: {
|
|
||||||
"frontblock-wallet": { type: 'stdout' },
|
|
||||||
//app: { type: 'file', filename: 'application.log' }
|
|
||||||
},
|
|
||||||
categories: {
|
|
||||||
default: { appenders: [ 'frontblock-wallet' ], level: 'debug' }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const logger = Logger.getLogger("frontblock-wallet")
|
|
||||||
|
|
||||||
export type PublicNode = {
|
|
||||||
coin: Coin
|
|
||||||
net: 'testnet' | 'mainnet'
|
|
||||||
url: URL
|
|
||||||
}
|
|
||||||
|
|
||||||
export type WalletPluginConf = {
|
|
||||||
btcConf: {
|
|
||||||
publicNodes: PublicNode[]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Key = {
|
|
||||||
name: string
|
|
||||||
type: 'plain' | 'encrypted'
|
|
||||||
value: string
|
|
||||||
coin: Coin
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class WalletPlugin extends DatabasePlugin<WalletPluginConf>{
|
|
||||||
constructor(){
|
|
||||||
super("Wallet")
|
|
||||||
}
|
|
||||||
|
|
||||||
getDefaultConfig(): WalletPluginConf & { dbConf: knex.Config; } {
|
|
||||||
return {
|
|
||||||
btcConf: {
|
|
||||||
publicNodes: [
|
|
||||||
{coin: 'BTC', net: 'testnet', url: new URL('https://testnet-api.smartbit.com.au/v1/blockchain')},
|
|
||||||
{coin: 'BTC', net: 'mainnet', url: new URL('https://api.smartbit.com.au/v1/blockchain')}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
dbConf: {
|
|
||||||
client: 'sqlite3',
|
|
||||||
connection: {
|
|
||||||
filename: "./data/Wallet.sqlite"
|
|
||||||
},
|
|
||||||
useNullAsDefault: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected exportExtraRPCs(): socketioRPC[] {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
name: "putKey",
|
|
||||||
func: async(
|
|
||||||
name: string,
|
|
||||||
type: 'plain' | 'encrypted',
|
|
||||||
value: string,
|
|
||||||
coin: Coin
|
|
||||||
) => {
|
|
||||||
return await this.putKey({
|
|
||||||
name: name,
|
|
||||||
type:type,
|
|
||||||
value:value,
|
|
||||||
coin:coin
|
|
||||||
})
|
|
||||||
},
|
|
||||||
type: 'call',
|
|
||||||
visibility: 'private'
|
|
||||||
},{
|
|
||||||
name: "delKey",
|
|
||||||
func: async(name: string) => { return await this.delKey(name) },
|
|
||||||
type: 'call',
|
|
||||||
visibility: 'private'
|
|
||||||
},{
|
|
||||||
name: "getKeys",
|
|
||||||
func: async() => { return await this.getKeys() },
|
|
||||||
type: 'call',
|
|
||||||
visibility: 'private'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
public async putKey(key: Key){
|
|
||||||
return await this.knex('keys').insert([key])
|
|
||||||
}
|
|
||||||
|
|
||||||
public async delKey(name: string){
|
|
||||||
return await this.knex('keys')
|
|
||||||
.where('name', name)
|
|
||||||
.del()
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getKeys(): Promise<Key[]>{
|
|
||||||
return await this.knex.select('*').from('keys')
|
|
||||||
}
|
|
||||||
|
|
||||||
protected getTableDefinitions(): TableDefiniton[] {
|
|
||||||
return [{
|
|
||||||
name: 'keys',
|
|
||||||
tableBuilder: function (table) {
|
|
||||||
table.string('name').primary();
|
|
||||||
table.string('type');
|
|
||||||
table.string('value');
|
|
||||||
table.string('coin');
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
const nodeExternals = require('webpack-node-externals');
|
|
||||||
const TerserPlugin = require('terser-webpack-plugin');
|
|
||||||
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
mode: 'production',
|
|
||||||
target: "node",
|
|
||||||
entry: path.resolve(__dirname, 'Plugin.ts'),
|
|
||||||
output: {
|
|
||||||
path: path.resolve(__dirname, '../../dist'),
|
|
||||||
filename: 'Plugin.js',
|
|
||||||
libraryTarget: 'commonjs',
|
|
||||||
},
|
|
||||||
resolve: {
|
|
||||||
// Add `.ts` and `.tsx` as a resolvable extension.
|
|
||||||
extensions: [".ts", ".tsx", ".js"]
|
|
||||||
},
|
|
||||||
module: {
|
|
||||||
rules: [
|
|
||||||
{ test: /\.ts?$/, loader: "ts-loader" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
optimization: {
|
|
||||||
minimize: false
|
|
||||||
},
|
|
||||||
node: {
|
|
||||||
global: true,
|
|
||||||
process: true,
|
|
||||||
__filename: false,
|
|
||||||
__dirname: false,
|
|
||||||
Buffer: true,
|
|
||||||
},
|
|
||||||
externals:{
|
|
||||||
knex: "../node_modules/knex"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
const path = require('path');
|
|
||||||
const TerserPlugin = require('terser-webpack-plugin');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
mode: 'production',
|
|
||||||
target: "web",
|
|
||||||
entry: path.resolve(__dirname, 'module.ts'),
|
|
||||||
output: {
|
|
||||||
path: path.resolve(__dirname, '../../dist'),
|
|
||||||
filename: 'FrontendPlugin.js',
|
|
||||||
libraryTarget: 'commonjs',
|
|
||||||
},
|
|
||||||
resolve: {
|
|
||||||
// Add `.ts` and `.tsx` as a resolvable extension.
|
|
||||||
extensions: [".ts", ".tsx", ".js"]
|
|
||||||
},
|
|
||||||
module: {
|
|
||||||
rules: [
|
|
||||||
{ test: /\.ts?$/, loader: "ts-loader" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
optimization: {
|
|
||||||
minimizer: [
|
|
||||||
new TerserPlugin({
|
|
||||||
exclude: [
|
|
||||||
/\.\/(.*)\/.ts/,
|
|
||||||
/\.\/(.*).ts/,
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
externals: {
|
|
||||||
'@angular/core': '@angular/core',
|
|
||||||
'@angular/common': '@angular/common',
|
|
||||||
'@angular/router': '@angular/router',
|
|
||||||
'@angular/animations': '@angular/animations',
|
|
||||||
'@angular/forms': '@angular/forms',
|
|
||||||
'@clr/angular': '@clr/angular',
|
|
||||||
'../../../knex-config/knex-config.module': 'knexconfig'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "./tsconfig.json",
|
|
||||||
"include": ["src/frontend"],
|
|
||||||
"exclude": [
|
|
||||||
"src/backend"
|
|
||||||
],
|
|
||||||
"compilerOptions": {
|
|
||||||
"sourceMap": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"allowSyntheticDefaultImports": true,
|
|
||||||
"alwaysStrict": false,
|
|
||||||
"noImplicitAny": false,
|
|
||||||
"target": "ES2015",
|
|
||||||
"module": "commonjs",
|
|
||||||
"declaration": false,
|
|
||||||
"strict": true,
|
|
||||||
"outDir": "./lib",
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"strictPropertyInitialization": false,
|
|
||||||
"importHelpers": true,
|
|
||||||
"strictNullChecks": false,
|
|
||||||
|
|
||||||
},
|
|
||||||
"include": ["src/backend"],
|
|
||||||
"exclude": []
|
|
||||||
}
|
|
||||||
+27
-22
@@ -1,4 +1,4 @@
|
|||||||
import { Component, OnInit, isDevMode, ViewChild, AfterViewInit, OnDestroy, HostListener } from '@angular/core';
|
import { Component, OnInit, isDevMode, ViewChild, AfterViewInit, OnDestroy } from '@angular/core';
|
||||||
import { WalletGeneratorComponent } from './wallet-generator.component';
|
import { WalletGeneratorComponent } from './wallet-generator.component';
|
||||||
import { WalletPickerComponent } from './wallet-picker.component';
|
import { WalletPickerComponent } from './wallet-picker.component';
|
||||||
import { HDKey } from "btc-hdkey"
|
import { HDKey } from "btc-hdkey"
|
||||||
@@ -8,7 +8,7 @@ const fetch = require("node-fetch")
|
|||||||
import { TransactionBuilder, Signer } from 'bitcoinjs-lib'
|
import { TransactionBuilder, Signer } from 'bitcoinjs-lib'
|
||||||
import { networks } from 'bitcoinjs-lib'
|
import { networks } from 'bitcoinjs-lib'
|
||||||
import { SubscriptionResponse, ErrorResponse } from 'frontblock-generic/Types';
|
import { SubscriptionResponse, ErrorResponse } from 'frontblock-generic/Types';
|
||||||
let feeRate = 5 // satoshis per byte
|
let feeRate = 55 // satoshis per byte
|
||||||
|
|
||||||
const hdkey = new HDKey()
|
const hdkey = new HDKey()
|
||||||
declare const fb
|
declare const fb
|
||||||
@@ -247,44 +247,45 @@ export class PluginsComponent implements AfterViewInit, OnDestroy {
|
|||||||
@ViewChild(WalletPickerComponent, { static: false })
|
@ViewChild(WalletPickerComponent, { static: false })
|
||||||
WalletPickerComponent: WalletPickerComponent
|
WalletPickerComponent: WalletPickerComponent
|
||||||
|
|
||||||
@HostListener('window:beforeunload', ['$event'])
|
|
||||||
unloadNotification($event: any) {
|
|
||||||
this.unsubscribe()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
async ngAfterViewInit(): Promise<void> {
|
async ngAfterViewInit(): Promise<void> {
|
||||||
this.WalletGeneratorComponent.setParent(this)
|
this.WalletGeneratorComponent.setParent(this)
|
||||||
this.WalletPickerComponent.setParent(this)
|
this.WalletPickerComponent.setParent(this)
|
||||||
this.AddressViewerComponent.setParent(this)
|
this.AddressViewerComponent.setParent(this)
|
||||||
this.apiClientPlugin = fb.ApiClient != null
|
this.apiClientPlugin = fb.ApiClient != null
|
||||||
window['btc'] = this
|
window['btc'] = this
|
||||||
const keys: any[] = await fb.Wallet.getKeys()
|
const keys: any[] = []
|
||||||
|
|
||||||
keys.filter(key => key.coin === "BTC").forEach(key => {
|
/*
|
||||||
|
if(this.apiClientPlugin){
|
||||||
|
window.addEventListener('beforeunload', async (e) => {
|
||||||
|
await Promise.all(this.subscriptions.map(async s => {
|
||||||
|
const RR = await fb.ApiClient.unsubscribe(s)
|
||||||
|
console.log(RR)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
keys.filter(key => key.type === "BTC").forEach(key => {
|
||||||
this.wallets[key.name] = {key: key.value, name: key.name}
|
this.wallets[key.name] = {key: key.value, name: key.name}
|
||||||
})
|
})
|
||||||
this.WalletPickerComponent.setWallets(this.wallets)
|
|
||||||
|
|
||||||
this.timerRefresh = setInterval(async () => { if (this.autorefresh && !this.refreshing && !this.loading) this.refresh() }, 30000)
|
this.timerRefresh = setInterval(async () => { if (this.autorefresh && !this.refreshing && !this.loading) this.refresh() }, 30000)
|
||||||
|
this.loadKey("tprv8cXm1S7PGS2fHuLjZajuDZZsvxerzv4zZJJAhkLAWZzwz5rE7efHm31xsqpzfoGMHBNC8bkRcsjiMdmkSuNn5C1qNqk7rrk1pCuhk4HfVak")
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.unsubscribe()
|
|
||||||
clearInterval(this.timerRefresh)
|
clearInterval(this.timerRefresh)
|
||||||
}
|
}
|
||||||
|
|
||||||
unsubscribe(){
|
|
||||||
this.subscriptions.map(s => {
|
|
||||||
fb.ApiClient.unsubscribe(s)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
addWallet(wallet: {key:string, name:string}) {
|
addWallet(wallet: {key:string, name:string}) {
|
||||||
fb.Wallet.putKey(wallet.name, "plain", wallet.key, "BTC")
|
/* store somewhere */
|
||||||
|
|
||||||
|
//fb.Wallet.putKey(wallet.name, "plain", wallet.key, "BTC")
|
||||||
|
|
||||||
this.wallets[wallet.name] = wallet
|
this.wallets[wallet.name] = wallet
|
||||||
this.WalletPickerComponent.setWallets(this.wallets)
|
this.WalletPickerComponent.setWallets(this.wallets)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
showWizard() {
|
showWizard() {
|
||||||
@@ -311,19 +312,21 @@ export class PluginsComponent implements AfterViewInit, OnDestroy {
|
|||||||
this.loadingN = 1
|
this.loadingN = 1
|
||||||
|
|
||||||
await this.refresh()
|
await this.refresh()
|
||||||
|
/*
|
||||||
if (fb.ApiClient) {
|
if (fb.ApiClient) {
|
||||||
const res: (SubscriptionResponse | ErrorResponse)[] = await Promise.all([
|
const res: (SubscriptionResponse | ErrorResponse)[] = await Promise.all([
|
||||||
...this.regaddresses,
|
...this.regaddresses,
|
||||||
...this.changeaddresses
|
...this.changeaddresses
|
||||||
].map(async a => await fb.ApiClient.subsume("BTC", { address: a.address }, tx => { this.transactions.unshift(tx) })))
|
].map(async a => await fb.ApiClient.subsume("BTC", { address: a.address }, tx => { this.transactions.unshift(tx) })))
|
||||||
this.subscriptions = this.subscriptions.concat(res.map(r => {
|
this.subscriptions.push(res.map(r => {
|
||||||
if (r.result = "Success") {
|
if (r.result = "Success") {
|
||||||
return r.message
|
return r.message
|
||||||
} else {
|
} else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
})).filter(s => !(!s))
|
}))
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
this.loading = false
|
this.loading = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -592,7 +595,9 @@ They are completely safe and mean no more addresses were found`)
|
|||||||
vout: t.n,
|
vout: t.n,
|
||||||
value: Math.floor(parseFloat(t.value) * 100000000)
|
value: Math.floor(parseFloat(t.value) * 100000000)
|
||||||
}
|
}
|
||||||
}).filter(utx => utx.value > 0 && utx.address != target)
|
}).filter(utx => utx.value > 0)
|
||||||
|
|
||||||
|
console.log(utxos, target, value, feeRate)
|
||||||
|
|
||||||
let { inputs, outputs, fee } = coinselect(utxos, [{ address: target, value: Math.floor(parseFloat(<any>value) * 100000000) }], feeRate)
|
let { inputs, outputs, fee } = coinselect(utxos, [{ address: target, value: Math.floor(parseFloat(<any>value) * 100000000) }], feeRate)
|
||||||
|
|
||||||
+4
-34
@@ -6,26 +6,20 @@ import { PluginsComponent } from './btc/btc.component';
|
|||||||
import { WalletPickerComponent } from './btc/wallet-picker.component';
|
import { WalletPickerComponent } from './btc/wallet-picker.component';
|
||||||
import { AddressViewerComponent } from './btc/address-viewer.component';
|
import { AddressViewerComponent } from './btc/address-viewer.component';
|
||||||
|
|
||||||
import { FrontendPlugin, SidebarEntries } from 'frontblock-generic/Plugin';
|
|
||||||
import { ClarityModule } from '@clr/angular';
|
import { ClarityModule } from '@clr/angular';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { WalletGeneratorComponent } from './btc/wallet-generator.component';
|
import { WalletGeneratorComponent } from './btc/wallet-generator.component';
|
||||||
import { WalletSettingsComponent } from './settings.component';
|
import { WalletSettingsComponent } from './settings.component';
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
import { KnexConfigModule } from "../../../knex-config/knex-config.module";
|
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
FormsModule,
|
FormsModule,
|
||||||
ClarityModule,
|
ClarityModule,
|
||||||
KnexConfigModule,
|
|
||||||
CommonModule,
|
CommonModule,
|
||||||
RouterModule.forChild([
|
RouterModule.forChild([
|
||||||
{path: "btc", component: PluginsComponent},
|
{path: "btc", component: PluginsComponent},
|
||||||
{path: "settings", component: WalletSettingsComponent},
|
{path: "settings", component: WalletSettingsComponent},
|
||||||
|
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
exports: [RouterModule],
|
exports: [RouterModule],
|
||||||
@@ -35,33 +29,9 @@ import { KnexConfigModule } from "../../../knex-config/knex-config.module";
|
|||||||
WalletPickerComponent,
|
WalletPickerComponent,
|
||||||
AddressViewerComponent,
|
AddressViewerComponent,
|
||||||
WalletGeneratorComponent
|
WalletGeneratorComponent
|
||||||
|
],
|
||||||
|
entryComponents: [
|
||||||
|
WalletSettingsComponent
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class PluginModule implements FrontendPlugin{
|
export class WalletsModule{}
|
||||||
getSidebarEntry(): SidebarEntries {
|
|
||||||
return {
|
|
||||||
icon: "wallet",
|
|
||||||
text: "Wallets",
|
|
||||||
parentRoute: "wallet",
|
|
||||||
links: [{
|
|
||||||
route: "btc",
|
|
||||||
text: "BTC Wallet"
|
|
||||||
},{
|
|
||||||
route: "ltc",
|
|
||||||
text: "LTC Wallet"
|
|
||||||
},{
|
|
||||||
route: "eth",
|
|
||||||
text: "ETH Wallet"
|
|
||||||
},{
|
|
||||||
route: "xrp",
|
|
||||||
text: "XRP Wallet"
|
|
||||||
},{
|
|
||||||
route: "xlm",
|
|
||||||
text: "XLM Wallet"
|
|
||||||
},{
|
|
||||||
route: "settings",
|
|
||||||
text: "Settings"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+1
-1
@@ -7,7 +7,7 @@ declare const fb
|
|||||||
|
|
||||||
template:
|
template:
|
||||||
`
|
`
|
||||||
<knex-config fbPlugin="Wallet"></knex-config>
|
WALLET CONFIG XD
|
||||||
`
|
`
|
||||||
})
|
})
|
||||||
export class WalletSettingsComponent implements OnInit {
|
export class WalletSettingsComponent implements OnInit {
|
||||||
Executable
BIN
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
@@ -10,8 +10,7 @@
|
|||||||
"src/app/**/*/node_modules/**/*",
|
"src/app/**/*/node_modules/**/*",
|
||||||
"src/app/**/*/dist/**/*",
|
"src/app/**/*/dist/**/*",
|
||||||
"src/app/**/*/backend/**/*",
|
"src/app/**/*/backend/**/*",
|
||||||
"src/app/**/*/frontend/**/*",
|
"src/app/**/*/frontend/**/*"
|
||||||
"src/app/apiclient/**/*",
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user