diff --git a/.env.example b/.env.example index 28b6803..fc403b1 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,6 @@ +# Logging levels => TRACE = 0|DEBUG = 1|INFO = 2|LOG = 3|WARN = 4|ERROR = 5|FATAL = 6|OFF = 7 LOG_LEVEL= -SERVER_LEVEL= +SERVER_LOG_LEVEL= CIC_CHAIN_ID= CIC_LOGGING_URL= CIC_META_URL= diff --git a/package-lock.json b/package-lock.json index 2448635..0420d2f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,6 @@ "@angular/platform-browser": "~10.2.0", "@angular/platform-browser-dynamic": "~10.2.0", "@angular/router": "~10.2.0", - "@angular/service-worker": "~10.2.0", "@popperjs/core": "^2.5.4", "angular-datatables": "^9.0.2", "block-syncer": "^0.2.4", @@ -29,7 +28,6 @@ "datatables.net": "^1.10.22", "datatables.net-dt": "^1.10.22", "ethers": "^5.0.31", - "http-server": "^0.12.3", "jquery": "^3.5.1", "mocha": "^8.2.1", "moolb": "^0.1.0", @@ -513,15 +511,11 @@ } }, "node_modules/@angular/common": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-10.2.4.tgz", - "integrity": "sha512-bBfsLJNDQaC2OI1mReDJuSZ/uBb7Pf3HVpRmlQKNIPllIxqX1hLH8I3Plodrns9m32JMJ6FMsQthcP0KMdRCJA==", + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-10.2.1.tgz", + "integrity": "sha512-aJtgokgWxibd7wGmktHm0uYkR/lOrbcStrn6Qisj/PIJf9xTGXYFB0yusnk103aiuBfCIKq+Wl0ZGc1s81Okaw==", "dependencies": { "tslib": "^2.0.0" - }, - "peerDependencies": { - "@angular/core": "10.2.4", - "rxjs": "^6.5.3" } }, "node_modules/@angular/compiler": { @@ -776,15 +770,11 @@ } }, "node_modules/@angular/core": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-10.2.4.tgz", - "integrity": "sha512-5xpAvmZwD9nZ8eWx10urjibqEeePGEiFXVMEn3IaJWgfdOcMmeSoioW9JUllT3w85+DlNVWbRbhz0YfE9a4jyw==", + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-10.2.1.tgz", + "integrity": "sha512-zt9G5Ei1nxB6yVJqpiH7K6npaiEUrPWlDCq6vwXeJbmO3tbw2WWiqD55Wkx5hRfysY43swC5j7VveNytHidkkQ==", "dependencies": { "tslib": "^2.0.0" - }, - "peerDependencies": { - "rxjs": "^6.5.3", - "zone.js": "~0.10.3" } }, "node_modules/@angular/forms": { @@ -827,21 +817,6 @@ "tslib": "^2.0.0" } }, - "node_modules/@angular/service-worker": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-10.2.4.tgz", - "integrity": "sha512-1miQ5iNKPDelY11qpsU/4LyQcZce5zTRoYj8Qw7JfFQo9NG01HxcQs+FXoFbs1ZggmUIJS9L2C3++Sp9hVhu+A==", - "dependencies": { - "tslib": "^2.0.0" - }, - "bin": { - "ngsw-config": "ngsw-config.js" - }, - "peerDependencies": { - "@angular/common": "10.2.4", - "@angular/core": "10.2.4" - } - }, "node_modules/@babel/code-frame": { "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", @@ -4055,6 +4030,7 @@ "version": "2.6.3", "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, "dependencies": { "lodash": "^4.17.14" } @@ -4324,14 +4300,6 @@ "node": "^4.5.0 || >= 5.9" } }, - "node_modules/basic-auth": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-1.1.0.tgz", - "integrity": "sha1-RSIe5Cn37h5QNb4/UVM/HN/SmIQ=", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -5876,6 +5844,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true, "engines": { "node": ">=0.1.90" } @@ -6219,14 +6188,6 @@ "node": ">= 0.10" } }, - "node_modules/corser": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", - "integrity": "sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c=", - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/cosmiconfig": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", @@ -7200,20 +7161,6 @@ "safer-buffer": "^2.1.0" } }, - "node_modules/ecstatic": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/ecstatic/-/ecstatic-3.3.2.tgz", - "integrity": "sha512-fLf9l1hnwrHI2xn9mEDT7KIi22UDqA2jaCwyCbSUJh9a1V+LEUSL/JO/6TIz/QyuBURWUHrFL5Kg2TtO1bkkog==", - "dependencies": { - "he": "^1.1.1", - "mime": "^1.6.0", - "minimist": "^1.1.0", - "url-join": "^2.0.5" - }, - "bin": { - "ecstatic": "lib/ecstatic.js" - } - }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -7848,7 +7795,8 @@ "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true }, "node_modules/events": { "version": "3.2.0", @@ -8451,6 +8399,7 @@ "version": "1.13.0", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz", "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==", + "dev": true, "engines": { "node": ">=4.0" } @@ -9139,6 +9088,7 @@ "version": "1.18.1", "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, "dependencies": { "eventemitter3": "^4.0.0", "follow-redirects": "^1.0.0", @@ -9309,30 +9259,6 @@ "node": ">=0.10.0" } }, - "node_modules/http-server": { - "version": "0.12.3", - "resolved": "https://registry.npmjs.org/http-server/-/http-server-0.12.3.tgz", - "integrity": "sha512-be0dKG6pni92bRjq0kvExtj/NrrAd28/8fCXkaI/4piTwQMSDSLMhWyW0NI1V+DBI3aa1HMlQu46/HjVLfmugA==", - "dependencies": { - "basic-auth": "^1.0.3", - "colors": "^1.4.0", - "corser": "^2.0.1", - "ecstatic": "^3.3.2", - "http-proxy": "^1.18.0", - "minimist": "^1.2.5", - "opener": "^1.5.1", - "portfinder": "^1.0.25", - "secure-compare": "3.0.1", - "union": "~0.5.0" - }, - "bin": { - "hs": "bin/http-server", - "http-server": "bin/http-server" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -13451,14 +13377,6 @@ "node": ">=8" } }, - "node_modules/opener": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", - "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", - "bin": { - "opener": "bin/opener-bin.js" - } - }, "node_modules/openpgp": { "version": "4.10.10", "resolved": "https://registry.npmjs.org/openpgp/-/openpgp-4.10.10.tgz", @@ -14210,6 +14128,7 @@ "version": "1.0.28", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "dev": true, "dependencies": { "async": "^2.6.2", "debug": "^3.1.1", @@ -14223,6 +14142,7 @@ "version": "3.2.6", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, "dependencies": { "ms": "^2.1.1" } @@ -16008,7 +15928,8 @@ "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true }, "node_modules/resolve": { "version": "1.18.1", @@ -16423,11 +16344,6 @@ "node": ">=10.0.0" } }, - "node_modules/secure-compare": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", - "integrity": "sha1-8aAymzCLIh+uN7mXTz1XjQypmeM=" - }, "node_modules/select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", @@ -18500,17 +18416,6 @@ "node": ">=4" } }, - "node_modules/union": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", - "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", - "dependencies": { - "qs": "^6.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", @@ -18669,11 +18574,6 @@ "querystring": "0.2.0" } }, - "node_modules/url-join": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.5.tgz", - "integrity": "sha1-WvIvGMBSoACkjXuCxenC4v7tpyg=" - }, "node_modules/url-parse": { "version": "1.4.7", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", @@ -21177,9 +21077,9 @@ } }, "@angular/common": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-10.2.4.tgz", - "integrity": "sha512-bBfsLJNDQaC2OI1mReDJuSZ/uBb7Pf3HVpRmlQKNIPllIxqX1hLH8I3Plodrns9m32JMJ6FMsQthcP0KMdRCJA==", + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-10.2.1.tgz", + "integrity": "sha512-aJtgokgWxibd7wGmktHm0uYkR/lOrbcStrn6Qisj/PIJf9xTGXYFB0yusnk103aiuBfCIKq+Wl0ZGc1s81Okaw==", "requires": { "tslib": "^2.0.0" } @@ -21381,9 +21281,9 @@ } }, "@angular/core": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-10.2.4.tgz", - "integrity": "sha512-5xpAvmZwD9nZ8eWx10urjibqEeePGEiFXVMEn3IaJWgfdOcMmeSoioW9JUllT3w85+DlNVWbRbhz0YfE9a4jyw==", + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-10.2.1.tgz", + "integrity": "sha512-zt9G5Ei1nxB6yVJqpiH7K6npaiEUrPWlDCq6vwXeJbmO3tbw2WWiqD55Wkx5hRfysY43swC5j7VveNytHidkkQ==", "requires": { "tslib": "^2.0.0" } @@ -21428,14 +21328,6 @@ "tslib": "^2.0.0" } }, - "@angular/service-worker": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-10.2.4.tgz", - "integrity": "sha512-1miQ5iNKPDelY11qpsU/4LyQcZce5zTRoYj8Qw7JfFQo9NG01HxcQs+FXoFbs1ZggmUIJS9L2C3++Sp9hVhu+A==", - "requires": { - "tslib": "^2.0.0" - } - }, "@babel/code-frame": { "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", @@ -24253,6 +24145,7 @@ "version": "2.6.3", "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, "requires": { "lodash": "^4.17.14" } @@ -24478,11 +24371,6 @@ "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", "dev": true }, - "basic-auth": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-1.1.0.tgz", - "integrity": "sha1-RSIe5Cn37h5QNb4/UVM/HN/SmIQ=" - }, "batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -25801,7 +25689,8 @@ "colors": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true }, "combined-stream": { "version": "1.0.8", @@ -26102,11 +25991,6 @@ "vary": "^1" } }, - "corser": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", - "integrity": "sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c=" - }, "cosmiconfig": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", @@ -26928,17 +26812,6 @@ "safer-buffer": "^2.1.0" } }, - "ecstatic": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/ecstatic/-/ecstatic-3.3.2.tgz", - "integrity": "sha512-fLf9l1hnwrHI2xn9mEDT7KIi22UDqA2jaCwyCbSUJh9a1V+LEUSL/JO/6TIz/QyuBURWUHrFL5Kg2TtO1bkkog==", - "requires": { - "he": "^1.1.1", - "mime": "^1.6.0", - "minimist": "^1.1.0", - "url-join": "^2.0.5" - } - }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -27501,7 +27374,8 @@ "eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true }, "events": { "version": "3.2.0", @@ -28010,7 +27884,8 @@ "follow-redirects": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz", - "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==" + "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==", + "dev": true }, "for-in": { "version": "1.0.2", @@ -28587,6 +28462,7 @@ "version": "1.18.1", "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, "requires": { "eventemitter3": "^4.0.0", "follow-redirects": "^1.0.0", @@ -28734,23 +28610,6 @@ } } }, - "http-server": { - "version": "0.12.3", - "resolved": "https://registry.npmjs.org/http-server/-/http-server-0.12.3.tgz", - "integrity": "sha512-be0dKG6pni92bRjq0kvExtj/NrrAd28/8fCXkaI/4piTwQMSDSLMhWyW0NI1V+DBI3aa1HMlQu46/HjVLfmugA==", - "requires": { - "basic-auth": "^1.0.3", - "colors": "^1.4.0", - "corser": "^2.0.1", - "ecstatic": "^3.3.2", - "http-proxy": "^1.18.0", - "minimist": "^1.2.5", - "opener": "^1.5.1", - "portfinder": "^1.0.25", - "secure-compare": "3.0.1", - "union": "~0.5.0" - } - }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -32094,11 +31953,6 @@ "is-wsl": "^2.1.1" } }, - "opener": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", - "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==" - }, "openpgp": { "version": "4.10.10", "resolved": "https://registry.npmjs.org/openpgp/-/openpgp-4.10.10.tgz", @@ -32724,6 +32578,7 @@ "version": "1.0.28", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "dev": true, "requires": { "async": "^2.6.2", "debug": "^3.1.1", @@ -32734,6 +32589,7 @@ "version": "3.2.6", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, "requires": { "ms": "^2.1.1" } @@ -34244,7 +34100,8 @@ "requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true }, "resolve": { "version": "1.18.1", @@ -34587,11 +34444,6 @@ "node-gyp-build": "^4.2.0" } }, - "secure-compare": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", - "integrity": "sha1-8aAymzCLIh+uN7mXTz1XjQypmeM=" - }, "select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", @@ -36350,14 +36202,6 @@ "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", "dev": true }, - "union": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", - "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", - "requires": { - "qs": "^6.4.0" - } - }, "union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", @@ -36503,11 +36347,6 @@ } } }, - "url-join": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.5.tgz", - "integrity": "sha1-WvIvGMBSoACkjXuCxenC4v7tpyg=" - }, "url-parse": { "version": "1.4.7", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", diff --git a/set-env.ts b/set-env.ts index ddafca7..fce0e37 100644 --- a/set-env.ts +++ b/set-env.ts @@ -13,8 +13,8 @@ const environmentVars = `import {NgxLoggerLevel} from 'ngx-logger'; export const environment = { production: ${isProduction}, bloxbergChainId: ${process.env.CIC_CHAIN_ID || 8996}, - level: ${process.env.LOG_LEVEL || 'NgxLoggerLevel.OFF'}, - serverLogLevel: ${process.env.SERVER_LEVEL || 'NgxLoggerLevel.OFF'}, + logLevel: ${process.env.LOG_LEVEL || 'NgxLoggerLevel.ERROR'}, + serverLogLevel: ${process.env.SERVER_LOG_LEVEL || 'NgxLoggerLevel.OFF'}, loggingUrl: '${process.env.CIC_LOGGING_URL || 'http://localhost:8000'}', cicMetaUrl: '${process.env.CIC_META_URL || 'https://meta.dev.grassrootseconomics.net'}', publicKeysUrl: '${process.env.CIC_KEYS_URL || 'http://localhost:8000/keys.asc'}', diff --git a/src/app/_helpers/export-csv.ts b/src/app/_helpers/export-csv.ts new file mode 100644 index 0000000..6962ae0 --- /dev/null +++ b/src/app/_helpers/export-csv.ts @@ -0,0 +1,37 @@ +function exportCsv(arrayData: any[], filename: string, delimiter = ','): void { + if (arrayData === undefined) { return; } + const header = Object.keys(arrayData[0]).join(delimiter) + '\n'; + let csv = header; + arrayData.forEach(obj => { + let row = []; + for (const key in obj) { + if (obj.hasOwnProperty(key)) { + row.push(obj[key]); + } + } + csv += row.join(delimiter) + '\n'; + }); + + const csvData = new Blob([csv], {type: 'text/csv'}); + const csvUrl = URL.createObjectURL(csvData); + // csvUrl = 'data:text/csv;charset=utf-8,' + encodeURI(csv); + + let downloadLink = document.createElement('a'); + downloadLink.href = csvUrl; + downloadLink.target = '_blank'; + downloadLink.download = filename + '.csv'; + downloadLink.style.display = 'none'; + document.body.appendChild(downloadLink); + downloadLink.click(); +} + +function removeSpecialChar(str: string): string { + if (str === null || str === '') { + return ''; + } + return str.replace(/[^a-zA-Z0-9 ]/g, ''); +} + +export { + exportCsv +}; diff --git a/src/app/_helpers/global-error-handler.ts b/src/app/_helpers/global-error-handler.ts index 5d440f9..e524bd0 100644 --- a/src/app/_helpers/global-error-handler.ts +++ b/src/app/_helpers/global-error-handler.ts @@ -1,12 +1,16 @@ import {ErrorHandler, Injectable} from '@angular/core'; import {LoggingService} from '@app/_services/logging.service'; import {HttpErrorResponse} from '@angular/common/http'; +import {Router} from '@angular/router'; @Injectable() export class GlobalErrorHandler extends ErrorHandler { private sentencesForWarningLogging: string[] = []; - constructor(private loggingService: LoggingService) { + constructor( + private loggingService: LoggingService, + private router: Router + ) { super(); } @@ -31,15 +35,17 @@ export class GlobalErrorHandler extends ErrorHandler { } logError(error: any): void { + const route = this.router.url; if (error instanceof HttpErrorResponse) { this.loggingService.sendErrorLevelMessage( - `There was an HTTP error. ${error.message}, Status code: ${(error as HttpErrorResponse).status}`, this, {error}); + `There was an HTTP error on route ${route}.\n${error.message}.\nStatus code: ${(error as HttpErrorResponse).status}`, + this, {error}); } else if (error instanceof TypeError) { - this.loggingService.sendErrorLevelMessage(`There was a Type error. ${error.message}`, this, {error}); + this.loggingService.sendErrorLevelMessage(`There was a Type error on route ${route}.\n${error.message}`, this, {error}); } else if (error instanceof Error) { - this.loggingService.sendErrorLevelMessage(`There was a general error. ${error.message}`, this, {error}); + this.loggingService.sendErrorLevelMessage(`There was a general error on route ${route}.\n${error.message}`, this, {error}); } else { - this.loggingService.sendErrorLevelMessage('Nobody threw an error but something happened!', this, {error}); + this.loggingService.sendErrorLevelMessage(`Nobody threw an error but something happened on route ${route}!`, this, {error}); } } diff --git a/src/app/_helpers/index.ts b/src/app/_helpers/index.ts index e340b56..0294677 100644 --- a/src/app/_helpers/index.ts +++ b/src/app/_helpers/index.ts @@ -4,3 +4,5 @@ export * from '@app/_helpers/mock-backend'; export * from '@app/_helpers/array-sum'; export * from '@app/_helpers/http-getter'; export * from '@app/_helpers/global-error-handler'; +export * from '@app/_helpers/export-csv'; +export * from '@app/_helpers/read-csv'; diff --git a/src/app/_helpers/read-csv.ts b/src/app/_helpers/read-csv.ts new file mode 100644 index 0000000..8ba24af --- /dev/null +++ b/src/app/_helpers/read-csv.ts @@ -0,0 +1,31 @@ +let objCsv = { + size: 0, + dataFile: [] +}; + +function readCsv(input: any): any { + if (input.files && input.files[0]) { + let reader = new FileReader(); + reader.readAsBinaryString(input.files[0]); + reader.onload = event => { + objCsv.size = event.total; + // @ts-ignore + objCsv.dataFile = event.target.result; + return parseData(objCsv.dataFile); + }; + } +} + +function parseData(data: any): any { + let csvData = []; + const lineBreak = data.split('\n'); + lineBreak.forEach(res => { + csvData.push(res.split(',')); + }); + console.table(csvData); + return csvData; +} + +export { + readCsv +}; diff --git a/src/app/_interceptors/error.interceptor.ts b/src/app/_interceptors/error.interceptor.ts index adf16bf..9763c8d 100644 --- a/src/app/_interceptors/error.interceptor.ts +++ b/src/app/_interceptors/error.interceptor.ts @@ -6,26 +6,43 @@ import { HttpInterceptor, HttpErrorResponse } from '@angular/common/http'; import {Observable, throwError} from 'rxjs'; -import {catchError} from 'rxjs/operators'; -import {ErrorDialogService} from '@app/_services'; +import {catchError, retry} from 'rxjs/operators'; +import {ErrorDialogService, LoggingService} from '@app/_services'; +import {Router} from '@angular/router'; @Injectable() export class ErrorInterceptor implements HttpInterceptor { - constructor(private errorDialogService: ErrorDialogService) {} + constructor( + private errorDialogService: ErrorDialogService, + private loggingService: LoggingService, + private router: Router + ) {} intercept(request: HttpRequest, next: HttpHandler): Observable> { - return next.handle(request).pipe(catchError((err: HttpErrorResponse) => { - if (isDevMode()) { - this.errorDialogService.openDialog({ - message: err.error.message || err.statusText || 'Unknown Error', - status: err.status || 0 - }); - } - if ([401, 403].indexOf(err.status) !== -1) { - location.reload(true); - } - return throwError(err); - })); + return next.handle(request).pipe( + catchError((err: HttpErrorResponse) => { + let errorMessage; + if (err.error instanceof ErrorEvent) { + // A client-side or network error occurred. Handle it accordingly. + errorMessage = `An error occurred: ${err.error.message}`; + } else { + // The backend returned an unsuccessful response code. + // The response body may contain clues as to what went wrong. + errorMessage = `Backend returned code ${err.status}, body was: ${JSON.stringify(err.error)}`; + } + this.loggingService.sendErrorLevelMessage(errorMessage, this, {error: err}); + switch (err.status) { + case 401: // unauthorized + this.router.navigateByUrl('/auth').then(); + break; + case 403: // forbidden + location.reload(true); + break; + } + // Return an observable with a user-facing error message. + return throwError(err); + }) + ); } } diff --git a/src/app/_interceptors/http-config.interceptor.ts b/src/app/_interceptors/http-config.interceptor.ts index 67cf3c0..865b8f2 100644 --- a/src/app/_interceptors/http-config.interceptor.ts +++ b/src/app/_interceptors/http-config.interceptor.ts @@ -19,11 +19,6 @@ export class HttpConfigInterceptor implements HttpInterceptor { request = request.clone({headers: request.headers.set('Authorization', 'Bearer ' + token)}); } - if (!request.headers.has('Content-Type')) { - request = request.clone({headers: request.headers.set('Content-Type', 'application/json')}); - } - - request = request.clone({headers: request.headers.set('Accept', 'application/json')}); return next.handle(request); } } diff --git a/src/app/_models/account.ts b/src/app/_models/account.ts index 20b3404..7209861 100644 --- a/src/app/_models/account.ts +++ b/src/app/_models/account.ts @@ -78,10 +78,10 @@ export const defaultAccount: AccountDetails = { value: '', }], fn: [{ - value: 'GE', + value: 'Sarafu Contract', }], n: [{ - value: ['GE'], + value: ['Sarafu', 'Contract'], }], tel: [{ meta: { diff --git a/src/app/_pgp/pgp-key-store.ts b/src/app/_pgp/pgp-key-store.ts index ae8f03f..0b27f9f 100644 --- a/src/app/_pgp/pgp-key-store.ts +++ b/src/app/_pgp/pgp-key-store.ts @@ -1,11 +1,12 @@ import { KeyStore } from 'cic-client-meta'; -const openpgp = require('openpgp'); +// TODO should we put this on the mutalble key store object +import * as openpgp from 'openpgp'; const keyring = new openpgp.Keyring(); interface MutableKeyStore extends KeyStore { - loadKeyring(): Promise; + loadKeyring(): void; importKeyPair(publicKey: any, privateKey: any): Promise; - importPublicKey(publicKey: any): Promise; + importPublicKey(publicKey: any): void; importPrivateKey(privateKey: any): Promise; getPublicKeys(): Array; getTrustedKeys(): Array; @@ -13,7 +14,8 @@ interface MutableKeyStore extends KeyStore { getEncryptKeys(): Array; getPrivateKeys(): Array; getPrivateKey(): any; - isValidKey(key: any): boolean; + isValidKey(key: any): Promise; + isEncryptedPrivateKey(privateKey: any): Promise; getFingerprint(): string; getKeyId(key: any): string; getPrivateKeyId(): string; @@ -33,8 +35,6 @@ class MutablePgpKeyStore implements MutableKeyStore{ async loadKeyring(): Promise { await keyring.load(); - // clear any keys already in the keychain - // keyring.clear(); await keyring.store(); } @@ -43,8 +43,8 @@ class MutablePgpKeyStore implements MutableKeyStore{ await keyring.privateKeys.importKey(privateKey); } - async importPublicKey(publicKey: any): Promise { - await keyring.publicKeys.importKey(publicKey); + importPublicKey(publicKey: any): void { + keyring.publicKeys.importKey(publicKey); } async importPrivateKey(privateKey: any): Promise { @@ -72,15 +72,30 @@ class MutablePgpKeyStore implements MutableKeyStore{ } getPrivateKey(): any { - return keyring.privateKeys.keys[0]; + return keyring.privateKeys && keyring.privateKeys.keys[0]; } - isValidKey(key): boolean { - return typeof key === openpgp.Key; + async isValidKey(key): Promise { + // There is supposed to be an opengpg.readKey() method but I can't find it? + const _key = await openpgp.key.readArmored(key); + return !_key.err; + } + + async isEncryptedPrivateKey(privateKey: any): Promise { + const imported = await openpgp.key.readArmored(privateKey); + for (let i = 0; i < imported.keys.length; i++) { + const key = imported.keys[i]; + if (key.isDecrypted()) { + return false; + } + } + return true; } getFingerprint(): string { - return keyring.privateKeys.keys[0].keyPacket.fingerprint; + // TODO Handle multiple keys + return keyring.privateKeys && keyring.privateKeys.keys[0] && keyring.privateKeys.keys[0].keyPacket && + keyring.privateKeys.keys[0].keyPacket.fingerprint; } getKeyId(key: any): string { @@ -88,7 +103,8 @@ class MutablePgpKeyStore implements MutableKeyStore{ } getPrivateKeyId(): string { - return keyring.privateKeys.keys[0].getKeyId().toHex(); + // TODO is there a library that comes with angular for doing this? + return keyring.privateKeys && keyring.privateKeys.keys[0] && keyring.privateKeys.keys[0].getKeyId().toHex(); } getKeysForId(keyId: string): Array { diff --git a/src/app/_services/auth.service.ts b/src/app/_services/auth.service.ts index 3577ea8..ba4651a 100644 --- a/src/app/_services/auth.service.ts +++ b/src/app/_services/auth.service.ts @@ -3,10 +3,10 @@ import { hobaParseChallengeHeader } from '@src/assets/js/hoba.js'; import { signChallenge } from '@src/assets/js/hoba-pgp.js'; import {environment} from '@src/environments/environment'; import {LoggingService} from '@app/_services/logging.service'; -import {HttpWrapperService} from '@app/_services/http-wrapper.service'; import {MutableKeyStore, MutablePgpKeyStore} from '@app/_pgp'; import {ErrorDialogService} from '@app/_services/error-dialog.service'; -import {first} from 'rxjs/operators'; +import { HttpClient } from '@angular/common/http'; +import {Observable } from 'rxjs'; @Injectable({ providedIn: 'root' @@ -18,10 +18,11 @@ export class AuthService { mutableKeyStore: MutableKeyStore = new MutablePgpKeyStore(); constructor( - private httpWrapperService: HttpWrapperService, + private httpClient: HttpClient, private loggingService: LoggingService, private errorDialogService: ErrorDialogService ) { + // TODO setting these together shoulds be atomic if (sessionStorage.getItem(btoa('CICADA_SESSION_TOKEN'))) { this.sessionToken = sessionStorage.getItem(btoa('CICADA_SESSION_TOKEN')); } @@ -46,7 +47,7 @@ export class AuthService { throw new Error('login rejected'); } this.sessionLoginCount++; - this.setState('Click button to perform login ' + this.sessionLoginCount + ' with token ' + this.sessionToken); + this.setState('Click button to log in'); return; }); xhr.send(); @@ -66,7 +67,7 @@ export class AuthService { this.sessionToken = xhr.getResponseHeader('Token'); sessionStorage.setItem(btoa('CICADA_SESSION_TOKEN'), this.sessionToken); this.sessionLoginCount++; - this.setState('Click button to perform login ' + this.sessionLoginCount + ' with token ' + this.sessionToken); + this.setState('Click button to log in'); return; }); xhr.send(); @@ -107,8 +108,12 @@ export class AuthService { async loginResponse(o): Promise { - const r = await signChallenge(o.challenge, o.realm, environment.cicMetaUrl, this.mutableKeyStore); - this.sendResponse(r); + try { + const r = await signChallenge(o.challenge, o.realm, environment.cicMetaUrl, this.mutableKeyStore); + this.sendResponse(r); + } catch (error) { + this.errorDialogService.openDialog({message: 'Incorrect key passphrase.'}); + } } loginView(): void { @@ -119,13 +124,20 @@ export class AuthService { async setKey(privateKeyArmored): Promise { try { - await this.mutableKeyStore.importPrivateKey(privateKeyArmored); + const isValidKeyCheck = await this.mutableKeyStore.isValidKey(privateKeyArmored); + if (!isValidKeyCheck) { + throw Error('The private key is invalid'); + } + const isEncryptedKeyCheck = await this.mutableKeyStore.isEncryptedPrivateKey(privateKeyArmored); + if (!isEncryptedKeyCheck) { + throw Error('The private key doesn\'t have a password!'); + } + const key = await this.mutableKeyStore.importPrivateKey(privateKeyArmored); localStorage.setItem(btoa('CICADA_PRIVATE_KEY'), privateKeyArmored); } catch (err) { - this.loggingService.sendErrorLevelMessage('Failed setting key', this, {error: err}); + this.loggingService.sendErrorLevelMessage(`Failed to set key: ${err.message || err.statusText}`, this, {error: err}); this.errorDialogService.openDialog({ - message: `Failed to set key, Enter your private key again. Reason: ${err.error.message || err.statusText}`, - status: err.status + message: `Failed to set key: ${err.message || err.statusText}`, }); return false; } @@ -135,6 +147,7 @@ export class AuthService { logout(): void { sessionStorage.removeItem(btoa('CICADA_SESSION_TOKEN')); + this.sessionToken = undefined; window.location.reload(true); } @@ -144,12 +157,11 @@ export class AuthService { return trustedUsers; } - async getPublicKeys(): Promise { - this.httpWrapperService.get(`${environment.publicKeysUrl}`).pipe(first()).subscribe(async res => { - await this.mutableKeyStore.importPublicKey(res.body); - }, error => { - this.loggingService.sendErrorLevelMessage('There was an error fetching public keys!', this, {error}); - }); + getPublicKeys(): Observable { + return this.httpClient.get(`${environment.publicKeysUrl}`, {responseType: 'text'}); + } + + async getPrivateKeys(): Promise { if (this.privateKey !== undefined) { await this.mutableKeyStore.importPrivateKey(this.privateKey); } diff --git a/src/app/_services/block-sync.service.ts b/src/app/_services/block-sync.service.ts index cc07386..d96d28f 100644 --- a/src/app/_services/block-sync.service.ts +++ b/src/app/_services/block-sync.service.ts @@ -59,11 +59,11 @@ export class BlockSyncService { }); if (address === null) { this.transactionService.getAllTransactions(offset, limit).pipe(first()).subscribe(res => { - this.fetcher(settings, res.body); + this.fetcher(settings, res); }); } else { this.transactionService.getAddressTransactions(address, offset, limit).pipe(first()).subscribe(res => { - this.fetcher(settings, res.body); + this.fetcher(settings, res); }); } } diff --git a/src/app/_services/error-dialog.service.ts b/src/app/_services/error-dialog.service.ts index 9560674..37db8cd 100644 --- a/src/app/_services/error-dialog.service.ts +++ b/src/app/_services/error-dialog.service.ts @@ -1,7 +1,6 @@ import { Injectable } from '@angular/core'; import {MatDialog} from '@angular/material/dialog'; import {ErrorDialogComponent} from '@app/shared/error-dialog/error-dialog.component'; -import {LoggingService} from '@app/_services/logging.service'; @Injectable({ providedIn: 'root' @@ -11,7 +10,6 @@ export class ErrorDialogService { constructor( public dialog: MatDialog, - private loggingService: LoggingService ) { } openDialog(data): any { @@ -24,10 +22,6 @@ export class ErrorDialogService { data }); - dialogRef.afterClosed().subscribe(result => { - this.loggingService.sendInfoLevelMessage('The dialog was closed'); - this.isDialogOpen = false; - const res = result; - }); + dialogRef.afterClosed().subscribe(() => this.isDialogOpen = false); } } diff --git a/src/app/_services/http-wrapper.service.spec.ts b/src/app/_services/http-wrapper.service.spec.ts deleted file mode 100644 index b18b6b1..0000000 --- a/src/app/_services/http-wrapper.service.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { HttpWrapperService } from './http-wrapper.service'; - -describe('HttpWrapperService', () => { - let service: HttpWrapperService; - - beforeEach(() => { - TestBed.configureTestingModule({}); - service = TestBed.inject(HttpWrapperService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); -}); diff --git a/src/app/_services/http-wrapper.service.ts b/src/app/_services/http-wrapper.service.ts deleted file mode 100644 index 4987de9..0000000 --- a/src/app/_services/http-wrapper.service.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Injectable } from '@angular/core'; -import {HttpClient, HttpRequest} from '@angular/common/http'; -import {Observable} from 'rxjs'; -import * as moment from 'moment'; -import { Moment } from 'moment'; -import {LoggingService} from '@app/_services/logging.service'; - -@Injectable({ - providedIn: 'root' -}) -export class HttpWrapperService { - - constructor( - private http: HttpClient, - private loggingService: LoggingService, - ) { } - - get(url: string, options?: any): Observable { - return this.request('GET', url, null, options); - } - - post(url: string, body: any, options?: any): Observable { - return this.request('POST', url, body, options); - } - - put(url: string, body: any, options?: any): Observable { - return this.request('PUT', url, body, options); - } - - delete(url: string, options?: any): Observable { - return this.request('DELETE', url, null, options); - } - - private logTime(startMoment: Moment, url: string, method: string): void { - const requestDuration = moment().diff(startMoment, 'milliseconds'); - this.loggingService.sendInfoLevelMessage(`HTTP ${method}, URL: ${url}, Duration: ${requestDuration} ms`); - } - - private request(method: string, url: string, body?: any, options?: any): Observable { - this.loggingService.sendInfoLevelMessage(`Options: ${options}`); - return Observable.create((observer: any) => { - const requestBeginTime = moment(); - this.http.request(new HttpRequest(method, url, body, options)).subscribe((response) => { - this.loggingService.sendInfoLevelMessage(response); - this.logTime(requestBeginTime, `${url}`, method); - observer.next(response); - observer.complete(); - }, (error) => { - switch (error.status) { - case 403: - observer.complete(); - break; - default: - observer.error(error); - break; - } - }); - }); - } -} diff --git a/src/app/_services/index.ts b/src/app/_services/index.ts index ec50f32..6ded947 100644 --- a/src/app/_services/index.ts +++ b/src/app/_services/index.ts @@ -5,5 +5,4 @@ export * from '@app/_services/token.service'; export * from '@app/_services/block-sync.service'; export * from '@app/_services/location.service'; export * from '@app/_services/logging.service'; -export * from '@app/_services/http-wrapper.service'; export * from '@app/_services/error-dialog.service'; diff --git a/src/app/_services/location.service.ts b/src/app/_services/location.service.ts index cd163dc..834d02a 100644 --- a/src/app/_services/location.service.ts +++ b/src/app/_services/location.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import {BehaviorSubject} from 'rxjs'; import {environment} from '@src/environments/environment'; import {first} from 'rxjs/operators'; -import {HttpWrapperService} from '@app/_services/http-wrapper.service'; +import {HttpClient} from '@angular/common/http'; @Injectable({ providedIn: 'root' @@ -13,10 +13,10 @@ export class LocationService { locationsSubject = this.locationsList.asObservable(); constructor( - private httpWrapperService: HttpWrapperService, + private httpClient: HttpClient, ) { } getLocations(): void { - this.httpWrapperService.get(`${environment.cicCacheUrl}/locations`).pipe(first()).subscribe(res => this.locationsList.next(res.body)); + this.httpClient.get(`${environment.cicCacheUrl}/locations`).pipe(first()).subscribe(res => this.locationsList.next(res)); } } diff --git a/src/app/_services/token.service.ts b/src/app/_services/token.service.ts index 8dc81ce..2924f53 100644 --- a/src/app/_services/token.service.ts +++ b/src/app/_services/token.service.ts @@ -4,8 +4,8 @@ import {BehaviorSubject, Observable} from 'rxjs'; import {HttpGetter} from '@app/_helpers'; import {CICRegistry} from 'cic-client'; import Web3 from 'web3'; -import {HttpWrapperService} from '@app/_services/http-wrapper.service'; import {Registry, TokenRegistry} from '@app/_eth'; +import {HttpClient} from '@angular/common/http'; @Injectable({ providedIn: 'root' @@ -20,7 +20,7 @@ export class TokenService { tokensSubject = this.tokensList.asObservable(); constructor( - private httpWrapperService: HttpWrapperService, + private httpClient: HttpClient, ) { } async getTokens(): Promise { @@ -30,7 +30,7 @@ export class TokenService { } getTokenBySymbol(symbol: string): Observable { - return this.httpWrapperService.get(`${environment.cicCacheUrl}/tokens/${symbol}`); + return this.httpClient.get(`${environment.cicCacheUrl}/tokens/${symbol}`); } async getTokenBalance(address: string): Promise { diff --git a/src/app/_services/transaction.service.ts b/src/app/_services/transaction.service.ts index 365c296..c1f9ba9 100644 --- a/src/app/_services/transaction.service.ts +++ b/src/app/_services/transaction.service.ts @@ -13,8 +13,8 @@ import * as secp256k1 from 'secp256k1'; import {AuthService} from '@app/_services/auth.service'; import {defaultAccount} from '@app/_models'; import {LoggingService} from '@app/_services/logging.service'; -import {HttpWrapperService} from '@app/_services/http-wrapper.service'; import {Registry} from '@app/_eth'; +import {HttpClient} from '@angular/common/http'; const Web3 = require('web3'); const vCard = require('vcard-parser'); @@ -30,18 +30,18 @@ export class TransactionService { registry = new Registry(environment.registryAddress); constructor( - private httpWrapperService: HttpWrapperService, + private httpClient: HttpClient, private authService: AuthService, private userService: UserService, private loggingService: LoggingService ) { } getAllTransactions(offset: number, limit: number): Observable { - return this.httpWrapperService.get(`${environment.cicCacheUrl}/tx/${offset}/${limit}`); + return this.httpClient.get(`${environment.cicCacheUrl}/tx/${offset}/${limit}`); } getAddressTransactions(address: string, offset: number, limit: number): Observable { - return this.httpWrapperService.get(`${environment.cicCacheUrl}/tx/${address}/${offset}/${limit}`); + return this.httpClient.get(`${environment.cicCacheUrl}/tx/${address}/${offset}/${limit}`); } async setTransaction(transaction, cacheSize: number): Promise { diff --git a/src/app/_services/user.service.ts b/src/app/_services/user.service.ts index a7c6430..b803775 100644 --- a/src/app/_services/user.service.ts +++ b/src/app/_services/user.service.ts @@ -6,7 +6,6 @@ import {first} from 'rxjs/operators'; import {ArgPair, Envelope, Syncable, User} from 'cic-client-meta'; import {MetaResponse} from '@app/_models'; import {LoggingService} from '@app/_services/logging.service'; -import {HttpWrapperService} from '@app/_services/http-wrapper.service'; import {TokenService} from '@app/_services/token.service'; import {AccountIndex, Registry} from '@app/_eth'; import {MutableKeyStore, MutablePgpKeyStore, PGPSigner, Signer} from '@app/_pgp'; @@ -35,8 +34,7 @@ export class UserService { staffSubject = this.staffList.asObservable(); constructor( - private http: HttpClient, - private httpWrapperService: HttpWrapperService, + private httpClient: HttpClient, private loggingService: LoggingService, private tokenService: TokenService ) { @@ -44,16 +42,16 @@ export class UserService { resetPin(phone: string): Observable { const params = new HttpParams().set('phoneNumber', phone); - return this.httpWrapperService.get(`${environment.cicUssdUrl}/pin`, {params}); + return this.httpClient.get(`${environment.cicUssdUrl}/pin`, {params}); } getAccountStatus(phone: string): any { const params = new HttpParams().set('phoneNumber', phone); - return this.httpWrapperService.get(`${environment.cicUssdUrl}/pin`, {params}); + return this.httpClient.get(`${environment.cicUssdUrl}/pin`, {params}); } getLockedAccounts(offset: number, limit: number): any { - return this.httpWrapperService.get(`${environment.cicUssdUrl}/accounts/locked/${offset}/${limit}`); + return this.httpClient.get(`${environment.cicUssdUrl}/accounts/locked/${offset}/${limit}`); } async changeAccountInfo(address: string, name: string, phoneNumber: string, age: string, type: string, bio: string, gender: string, @@ -75,8 +73,8 @@ export class UserService { accountInfo.vcard = vCard.generate(accountInfo.vcard); reqBody.m.data = accountInfo; const accountKey = await User.toKey(address); - this.httpWrapperService.get(`${environment.cicMetaUrl}/${accountKey}`, { headers: this.headers }).pipe(first()).subscribe(async res => { - const syncableAccount: Syncable = Envelope.fromJSON(JSON.stringify(res.body)).unwrap(); + this.httpClient.get(`${environment.cicMetaUrl}/${accountKey}`, { headers: this.headers }).pipe(first()).subscribe(async res => { + const syncableAccount: Syncable = Envelope.fromJSON(JSON.stringify(res)).unwrap(); let update = []; for (const prop in reqBody) { update.push(new ArgPair(prop, reqBody[prop])); @@ -94,67 +92,47 @@ export class UserService { async updateMeta(syncableAccount: Syncable, accountKey: string, headers: HttpHeaders): Promise { const envelope = await this.wrap(syncableAccount , this.signer); const reqBody = envelope.toJSON(); - this.httpWrapperService.put(`${environment.cicMetaUrl}/${accountKey}`, reqBody , { headers }).pipe(first()).subscribe(res => { - this.loggingService.sendInfoLevelMessage(`Response: ${res.body}`); + this.httpClient.put(`${environment.cicMetaUrl}/${accountKey}`, reqBody , { headers }).pipe(first()).subscribe(res => { + this.loggingService.sendInfoLevelMessage(`Response: ${res}`); }); } getAccounts(): void { - this.httpWrapperService.get(`${environment.cicCacheUrl}/accounts`).pipe(first()).subscribe(res => this.accountsList.next(res.body)); + this.httpClient.get(`${environment.cicCacheUrl}/accounts`).pipe(first()).subscribe(res => this.accountsList.next(res)); } getAccountById(id: number): Observable { - return this.httpWrapperService.get(`${environment.cicCacheUrl}/accounts/${id}`); + return this.httpClient.get(`${environment.cicCacheUrl}/accounts/${id}`); } getActions(): void { - this.httpWrapperService.get(`${environment.cicCacheUrl}/actions`).pipe(first()).subscribe(res => this.actionsList.next(res.body)); + this.httpClient.get(`${environment.cicCacheUrl}/actions`).pipe(first()).subscribe(res => this.actionsList.next(res)); } getActionById(id: string): any { - return this.httpWrapperService.get(`${environment.cicCacheUrl}/actions/${id}`); + return this.httpClient.get(`${environment.cicCacheUrl}/actions/${id}`); } approveAction(id: string): Observable { - return this.httpWrapperService.post(`${environment.cicCacheUrl}/actions/${id}`, { approval: true }); + return this.httpClient.post(`${environment.cicCacheUrl}/actions/${id}`, { approval: true }); } revokeAction(id: string): Observable { - return this.httpWrapperService.post(`${environment.cicCacheUrl}/actions/${id}`, { approval: false }); + return this.httpClient.post(`${environment.cicCacheUrl}/actions/${id}`, { approval: false }); } getHistoryByUser(id: string): Observable { - return this.httpWrapperService.get(`${environment.cicCacheUrl}/history/${id}`); - } - - getStaff(): void { - this.httpWrapperService.get(`${environment.cicCacheUrl}/staff`).pipe(first()).subscribe(res => this.staffList.next(res.body)); - } - - getStaffById(id: string): Observable { - return this.httpWrapperService.get(`${environment.cicCacheUrl}/staff/${id}`); - } - - activateStaff(id: string): Observable { - return this.httpWrapperService.post(`${environment.cicCacheUrl}/staff/${id}`, {status: 'activated'}); - } - - deactivateStaff(id: string): Observable { - return this.httpWrapperService.post(`${environment.cicCacheUrl}/staff/${id}`, {status: 'deactivated'}); - } - - changeStaffType(id: string, type: string): Observable { - return this.httpWrapperService.post(`${environment.cicCacheUrl}/staff/${id}`, {accountType: type}); + return this.httpClient.get(`${environment.cicCacheUrl}/history/${id}`); } getAccountDetailsFromMeta(userKey: string): Observable { - return this.http.get(`${environment.cicMetaUrl}/${userKey}`, { headers: this.headers }); + return this.httpClient.get(`${environment.cicMetaUrl}/${userKey}`, { headers: this.headers }); } getUser(userKey: string): any { - return this.httpWrapperService.get(`${environment.cicMetaUrl}/${userKey}`, { headers: this.headers }) + return this.httpClient.get(`${environment.cicMetaUrl}/${userKey}`, { headers: this.headers }) .pipe(first()).subscribe(async res => { - return Envelope.fromJSON(JSON.stringify(res.body)).unwrap(); + return Envelope.fromJSON(JSON.stringify(res)).unwrap(); }); } diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 81c4281..c82099e 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,5 +1,6 @@ import {ChangeDetectionStrategy, Component, HostListener, OnInit} from '@angular/core'; -import {AuthService, LoggingService, TokenService, TransactionService} from '@app/_services'; +import {AuthService, ErrorDialogService, LoggingService, TransactionService} from '@app/_services'; +import {catchError} from 'rxjs/operators'; import {SwUpdate} from '@angular/service-worker'; @Component({ @@ -16,15 +17,18 @@ export class AppComponent implements OnInit { constructor( private authService: AuthService, - private tokenService: TokenService, private transactionService: TransactionService, private loggingService: LoggingService, + private errorDialogService: ErrorDialogService private swUpdate: SwUpdate ) { (async () => { await this.authService.mutableKeyStore.loadKeyring(); - await this.authService.getPublicKeys(); - this.loggingService.sendInfoLevelMessage(await this.tokenService.getTokens()); + this.authService.getPublicKeys() + .pipe(catchError(async (error) => { + this.loggingService.sendErrorLevelMessage('Unable to load trusted public keys.', this, {error}); + this.errorDialogService.openDialog({message: 'Trusted keys endpoint can\'t be reached. Please try again later.'}); + })).subscribe(this.authService.mutableKeyStore.importPublicKey); })(); this.mediaQuery.addListener(this.onResize); this.onResize(this.mediaQuery); diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 648dddf..dcf9eef 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -32,7 +32,7 @@ import { ServiceWorkerModule } from '@angular/service-worker'; SharedModule, MatTableModule, LoggerModule.forRoot({ - level: environment.level, + level: environment.logLevel, serverLogLevel: environment.serverLogLevel, serverLoggingUrl: `${environment.loggingUrl}/api/logs/`, disableConsoleLogging: false diff --git a/src/app/auth/auth.component.ts b/src/app/auth/auth.component.ts index cc06dd3..5f7264a 100644 --- a/src/app/auth/auth.component.ts +++ b/src/app/auth/auth.component.ts @@ -29,8 +29,7 @@ export class AuthComponent implements OnInit { if (this.authService.privateKey !== undefined) { const setKey = await this.authService.setKey(this.authService.privateKey); if (setKey && this.authService.sessionToken !== undefined) { - this.authService.setState( - 'Click button to perform login ' + this.authService.sessionLoginCount + ' with token ' + this.authService.sessionToken); + this.authService.setState('Click button to log in'); } } } diff --git a/src/app/pages/accounts/account-details/account-details.component.html b/src/app/pages/accounts/account-details/account-details.component.html index aeb37fd..c1ee838 100644 --- a/src/app/pages/accounts/account-details/account-details.component.html +++ b/src/app/pages/accounts/account-details/account-details.component.html @@ -14,7 +14,7 @@
@@ -33,13 +33,11 @@

{{account?.vcard?.fn[0].value}}

- Balance: {{account?.balance}} SRF + Balance: {{account?.balance | tokenRatio}} SRF Created: {{account?.date_registered | date}} Address: {{account?.identities.evm['bloxberg:8996']}}
- -
@@ -217,22 +215,22 @@ NAME - ACCOUNT TYPE + BALANCE CREATED STATUS - {{account?.name}} - {{account?.type}} - {{account?.created}} + {{account?.vcard?.fn[0].value}} + {{account?.balance | tokenRatio}} + {{account?.date_registered | date}} - - {{account?.status}} + + {{accountStatus}} - - {{account?.status}} + + {{accountStatus}} @@ -242,50 +240,6 @@
-
- - History - -
- - Filter - - search - - - - - - User - {{history.userName}} - - - - Action - {{history.action}} - - - - Staff - {{history.staff}} - - - - Timestamp - {{history.timestamp | date}} - - - - - - - - -
-
- @@ -303,6 +257,7 @@ RECLAMATION + @@ -376,6 +331,7 @@ GROUPACCOUNT + diff --git a/src/app/pages/accounts/account-details/account-details.component.ts b/src/app/pages/accounts/account-details/account-details.component.ts index 2d4d8df..8695592 100644 --- a/src/app/pages/accounts/account-details/account-details.component.ts +++ b/src/app/pages/accounts/account-details/account-details.component.ts @@ -6,7 +6,7 @@ import {BlockSyncService, LocationService, LoggingService, TokenService, Transac import {ActivatedRoute, Params, Router} from '@angular/router'; import {first} from 'rxjs/operators'; import {FormBuilder, FormGroup, Validators} from '@angular/forms'; -import {CustomErrorStateMatcher} from '@app/_helpers'; +import {CustomErrorStateMatcher, exportCsv} from '@app/_helpers'; import {Envelope, User} from 'cic-client-meta'; const vCard = require('vcard-parser'); @@ -31,21 +31,14 @@ export class AccountDetailsComponent implements OnInit { @ViewChild('UserTablePaginator', {static: true}) userTablePaginator: MatPaginator; @ViewChild('UserTableSort', {static: true}) userTableSort: MatSort; - historyDataSource: MatTableDataSource; - historyDisplayedColumns = ['user', 'action', 'staff', 'timestamp']; - @ViewChild('HistoryTablePaginator', {static: true}) historyTablePaginator: MatPaginator; - @ViewChild('HistoryTableSort', {static: true}) historyTableSort: MatSort; - accountInfoForm: FormGroup; account: any; accountAddress: string; accountBalance: number; + accountStatus: any; metaAccount: any; accounts: any[] = []; accountsType = 'all'; - date: string; - time: number; - isDisbursing = false; locations: any; transaction: any; transactions: any[]; @@ -86,6 +79,7 @@ export class AccountDetailsComponent implements OnInit { this.loggingService.sendInfoLevelMessage(this.account); this.accountBalance = await this.tokenService.getTokenBalance(this.accountAddress); this.account.vcard = vCard.parse(atob(this.account.vcard)); + this.userService.getAccountStatus(this.account.vcard?.tel[0].value).pipe(first()).subscribe(response => this.accountStatus = response); this.accountInfoForm.patchValue({ name: this.account.vcard?.fn[0].value, phoneNumber: this.account.vcard?.tel[0].value, @@ -99,11 +93,6 @@ export class AccountDetailsComponent implements OnInit { locationType: this.account.location.area_type, }); }); - this.userService.getHistoryByUser(this.accountAddress).pipe(first()).subscribe(response => { - this.historyDataSource = new MatTableDataSource(response.body); - this.historyDataSource.paginator = this.historyTablePaginator; - this.historyDataSource.sort = this.historyTableSort; - }); this.blockSyncService.blockSync(this.accountAddress); }); this.userService.getAccounts(); @@ -127,13 +116,6 @@ export class AccountDetailsComponent implements OnInit { this.transactionsDataSource.sort = this.transactionTableSort; this.transactions = transactions; }); - - const d = new Date(); - this.date = `${d.getDate()}/${d.getMonth()}/${d.getFullYear()}`; - } - - addTransfer(): void { - this.isDisbursing = !this.isDisbursing; } doTransactionFilter(value: string): void { @@ -144,10 +126,6 @@ export class AccountDetailsComponent implements OnInit { this.userDataSource.filter = value.trim().toLocaleLowerCase(); } - doHistoryFilter(value: string): void { - this.historyDataSource.filter = value.trim().toLocaleLowerCase(); - } - viewTransaction(transaction): void { this.transaction = transaction; } @@ -160,7 +138,7 @@ export class AccountDetailsComponent implements OnInit { async saveInfo(): Promise { this.submitted = true; - if (this.accountInfoForm.invalid) { return; } + if (this.accountInfoForm.invalid || !confirm('Change user\'s profile information?')) { return; } const accountKey = await this.userService.changeAccountInfo( this.account.address, this.accountInfoFormStub.name.value, @@ -202,12 +180,17 @@ export class AccountDetailsComponent implements OnInit { } resetPin(): void { + if (!confirm('Reset user\'s pin?')) { return; } this.userService.resetPin(this.account.phone).pipe(first()).subscribe(res => { - this.loggingService.sendInfoLevelMessage(`Response: ${res.body}`); + this.loggingService.sendInfoLevelMessage(`Response: ${res}`); }); } public trackByName(index, item): string { return item.name; } + + downloadCsv(data: any, filename: string): void { + exportCsv(data, filename); + } } diff --git a/src/app/pages/accounts/accounts.component.html b/src/app/pages/accounts/accounts.component.html index 09426e7..18f045e 100644 --- a/src/app/pages/accounts/accounts.component.html +++ b/src/app/pages/accounts/accounts.component.html @@ -33,7 +33,7 @@ GROUPACCOUNT - + diff --git a/src/app/pages/accounts/accounts.component.ts b/src/app/pages/accounts/accounts.component.ts index 5069b86..007152c 100644 --- a/src/app/pages/accounts/accounts.component.ts +++ b/src/app/pages/accounts/accounts.component.ts @@ -4,6 +4,7 @@ import {MatPaginator} from '@angular/material/paginator'; import {MatSort} from '@angular/material/sort'; import {LoggingService, UserService} from '@app/_services'; import {Router} from '@angular/router'; +import {exportCsv} from '@app/_helpers'; @Component({ selector: 'app-accounts', @@ -63,4 +64,16 @@ export class AccountsComponent implements OnInit { this.dataSource.data = this.accounts.filter(account => account.type === this.accountsType); } } + + refreshPaginator(): void { + if (!this.dataSource.paginator) { + this.dataSource.paginator = this.paginator; + } + + this.paginator._changePageSize(this.paginator.pageSize); + } + + downloadCsv(): void { + exportCsv(this.accounts, 'accounts'); + } } diff --git a/src/app/pages/accounts/create-account/create-account.component.ts b/src/app/pages/accounts/create-account/create-account.component.ts index 1dbdf06..81b5ff8 100644 --- a/src/app/pages/accounts/create-account/create-account.component.ts +++ b/src/app/pages/accounts/create-account/create-account.component.ts @@ -43,7 +43,7 @@ export class CreateAccountComponent implements OnInit { onSubmit(): void { this.submitted = true; - if (this.createForm.invalid) { return; } + if (this.createForm.invalid || !confirm('Create account?')) { return; } this.submitted = false; } diff --git a/src/app/pages/accounts/disbursement/disbursement.component.ts b/src/app/pages/accounts/disbursement/disbursement.component.ts index 123bbb3..4c6820a 100644 --- a/src/app/pages/accounts/disbursement/disbursement.component.ts +++ b/src/app/pages/accounts/disbursement/disbursement.component.ts @@ -33,7 +33,7 @@ export class DisbursementComponent implements OnInit { async createTransfer(): Promise { this.submitted = true; - if (this.disbursementForm.invalid) { return; } + if (this.disbursementForm.invalid || !confirm('Make transfer?')) { return; } if (this.disbursementFormStub.transactionType.value === 'transfer') { await this.transactionService.transferRequest( this.account.token, diff --git a/src/app/pages/accounts/export-accounts/export-accounts.component.ts b/src/app/pages/accounts/export-accounts/export-accounts.component.ts index 5e5c874..b0fa3cc 100644 --- a/src/app/pages/accounts/export-accounts/export-accounts.component.ts +++ b/src/app/pages/accounts/export-accounts/export-accounts.component.ts @@ -28,7 +28,7 @@ export class ExportAccountsComponent implements OnInit { export(): void { this.submitted = true; - if (this.exportForm.invalid) { return; } + if (this.exportForm.invalid || !confirm('Export accounts?')) { return; } this.submitted = false; } } diff --git a/src/app/pages/admin/admin.component.html b/src/app/pages/admin/admin.component.html index 6f6ae09..453f743 100644 --- a/src/app/pages/admin/admin.component.html +++ b/src/app/pages/admin/admin.component.html @@ -18,7 +18,10 @@
- Actions +
+ Actions + +
@@ -66,7 +69,7 @@ APPROVE - + diff --git a/src/app/pages/admin/admin.component.ts b/src/app/pages/admin/admin.component.ts index 129083a..6e133f4 100644 --- a/src/app/pages/admin/admin.component.ts +++ b/src/app/pages/admin/admin.component.ts @@ -5,6 +5,7 @@ import {MatSort} from '@angular/material/sort'; import {LoggingService, UserService} from '@app/_services'; import {animate, state, style, transition, trigger} from '@angular/animations'; import {first} from 'rxjs/operators'; +import {exportCsv} from '@app/_helpers'; @Component({ selector: 'app-admin', @@ -23,6 +24,7 @@ export class AdminComponent implements OnInit { dataSource: MatTableDataSource; displayedColumns = ['expand', 'user', 'role', 'action', 'status', 'approve']; action: any; + actions: any; @ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatSort) sort: MatSort; @@ -36,6 +38,8 @@ export class AdminComponent implements OnInit { this.dataSource = new MatTableDataSource(actions); this.dataSource.paginator = this.paginator; this.dataSource.sort = this.sort; + this.actions = actions; + console.log(this.actions); }); } @@ -51,16 +55,22 @@ export class AdminComponent implements OnInit { } approveAction(action: any): void { - this.userService.approveAction(action.id).pipe(first()).subscribe(res => this.loggingService.sendInfoLevelMessage(res.body)); + if (!confirm('Approve action?')) { return; } + this.userService.approveAction(action.id).pipe(first()).subscribe(res => this.loggingService.sendInfoLevelMessage(res)); this.userService.getActions(); } - revertAction(action: any): void { - this.userService.revokeAction(action.id).pipe(first()).subscribe(res => this.loggingService.sendInfoLevelMessage(res.body)); + disapproveAction(action: any): void { + if (!confirm('Disapprove action?')) { return; } + this.userService.revokeAction(action.id).pipe(first()).subscribe(res => this.loggingService.sendInfoLevelMessage(res)); this.userService.getActions(); } expandCollapse(row): void { row.isExpanded = !row.isExpanded; } + + downloadCsv(): void { + exportCsv(this.actions, 'actions'); + } } diff --git a/src/app/pages/settings/invite/invite.component.ts b/src/app/pages/settings/invite/invite.component.ts index a3a0e04..cb8dc2f 100644 --- a/src/app/pages/settings/invite/invite.component.ts +++ b/src/app/pages/settings/invite/invite.component.ts @@ -28,7 +28,7 @@ export class InviteComponent implements OnInit { invite(): void { this.submitted = true; - if (this.inviteForm.invalid) { return; } + if (this.inviteForm.invalid || !confirm('Invite user?')) { return; } this.submitted = false; } } diff --git a/src/app/pages/settings/organization/organization.component.ts b/src/app/pages/settings/organization/organization.component.ts index b251adb..e8b6126 100644 --- a/src/app/pages/settings/organization/organization.component.ts +++ b/src/app/pages/settings/organization/organization.component.ts @@ -29,7 +29,7 @@ export class OrganizationComponent implements OnInit { onSubmit(): void { this.submitted = true; - if (this.organizationForm.invalid) { return; } + if (this.organizationForm.invalid || !confirm('Set organization information?')) { return; } this.submitted = false; } } diff --git a/src/app/pages/settings/settings.component.html b/src/app/pages/settings/settings.component.html index 7fdd713..9ed5869 100644 --- a/src/app/pages/settings/settings.component.html +++ b/src/app/pages/settings/settings.component.html @@ -50,7 +50,7 @@

- +
@@ -59,6 +59,7 @@
TRUSTED USERS +
diff --git a/src/app/pages/settings/settings.component.ts b/src/app/pages/settings/settings.component.ts index 2154554..5d733bc 100644 --- a/src/app/pages/settings/settings.component.ts +++ b/src/app/pages/settings/settings.component.ts @@ -4,6 +4,7 @@ import {MatPaginator} from '@angular/material/paginator'; import {MatSort} from '@angular/material/sort'; import {AuthService} from '@app/_services'; import {Staff} from '@app/_models/staff'; +import {exportCsv} from '@app/_helpers'; @Component({ selector: 'app-settings', @@ -36,4 +37,12 @@ export class SettingsComponent implements OnInit { doFilter(value: string): void { this.dataSource.filter = value.trim().toLocaleLowerCase(); } + + downloadCsv(): void { + exportCsv(this.trustedUsers, 'users'); + } + + logout(): void { + this.authService.logout(); + } } diff --git a/src/app/pages/tokens/token-details/token-details.component.ts b/src/app/pages/tokens/token-details/token-details.component.ts index 63bc9a1..46277fe 100644 --- a/src/app/pages/tokens/token-details/token-details.component.ts +++ b/src/app/pages/tokens/token-details/token-details.component.ts @@ -18,7 +18,7 @@ export class TokenDetailsComponent implements OnInit { ) { this.route.paramMap.subscribe((params: Params) => { this.tokenService.getTokenBySymbol(params.get('id')).pipe(first()).subscribe(res => { - this.token = res.body; + this.token = res; }); }); } diff --git a/src/app/pages/tokens/tokens.component.html b/src/app/pages/tokens/tokens.component.html index 04397b3..486e39a 100644 --- a/src/app/pages/tokens/tokens.component.html +++ b/src/app/pages/tokens/tokens.component.html @@ -18,7 +18,10 @@
- Tokens +
+ Tokens + +
diff --git a/src/app/pages/tokens/tokens.component.ts b/src/app/pages/tokens/tokens.component.ts index d25ce51..8676f4f 100644 --- a/src/app/pages/tokens/tokens.component.ts +++ b/src/app/pages/tokens/tokens.component.ts @@ -4,6 +4,7 @@ import {MatSort} from '@angular/material/sort'; import {LoggingService, TokenService} from '@app/_services'; import {MatTableDataSource} from '@angular/material/table'; import {Router} from '@angular/router'; +import {exportCsv} from '@app/_helpers'; @Component({ selector: 'app-tokens', @@ -41,4 +42,8 @@ export class TokensComponent implements OnInit { async viewToken(token): Promise { await this.router.navigateByUrl(`/tokens/${token.symbol}`); } + + downloadCsv(): void { + exportCsv(this.tokens, 'tokens'); + } } diff --git a/src/app/pages/transactions/transaction-details/transaction-details.component.html b/src/app/pages/transactions/transaction-details/transaction-details.component.html index 8a63cd3..038b837 100644 --- a/src/app/pages/transactions/transaction-details/transaction-details.component.html +++ b/src/app/pages/transactions/transaction-details/transaction-details.component.html @@ -117,7 +117,7 @@
- +
diff --git a/src/app/pages/transactions/transaction-details/transaction-details.component.ts b/src/app/pages/transactions/transaction-details/transaction-details.component.ts index 36f647a..40389d3 100644 --- a/src/app/pages/transactions/transaction-details/transaction-details.component.ts +++ b/src/app/pages/transactions/transaction-details/transaction-details.component.ts @@ -1,5 +1,6 @@ import {ChangeDetectionStrategy, Component, Input, OnInit} from '@angular/core'; import {Router} from '@angular/router'; +import {TransactionService} from '@app/_services'; @Component({ selector: 'app-transaction-details', @@ -12,7 +13,10 @@ export class TransactionDetailsComponent implements OnInit { senderBloxbergLink: string; recipientBloxbergLink: string; - constructor(private router: Router) { } + constructor( + private router: Router, + private transactionService: TransactionService + ) { } ngOnInit(): void { this.senderBloxbergLink = 'https://blockexplorer.bloxberg.org/address/' + this.transaction?.from + '/transactions'; @@ -26,4 +30,13 @@ export class TransactionDetailsComponent implements OnInit { async viewRecipient(): Promise { await this.router.navigateByUrl(`/accounts/${this.transaction.to}`); } + + async reverseTransaction(): Promise { + await this.transactionService.transferRequest( + this.transaction.token.address, + this.transaction.to, + this.transaction.from, + this.transaction.value + ); + } } diff --git a/src/app/pages/transactions/transactions.component.html b/src/app/pages/transactions/transactions.component.html index 39e04db..fe178b2 100644 --- a/src/app/pages/transactions/transactions.component.html +++ b/src/app/pages/transactions/transactions.component.html @@ -36,6 +36,7 @@ RECLAMATION
+ diff --git a/src/app/pages/transactions/transactions.component.ts b/src/app/pages/transactions/transactions.component.ts index b65c50f..d9a7ce7 100644 --- a/src/app/pages/transactions/transactions.component.ts +++ b/src/app/pages/transactions/transactions.component.ts @@ -3,6 +3,7 @@ import {BlockSyncService, TransactionService} from '@app/_services'; import {MatTableDataSource} from '@angular/material/table'; import {MatPaginator} from '@angular/material/paginator'; import {MatSort} from '@angular/material/sort'; +import {exportCsv} from '@app/_helpers'; @Component({ selector: 'app-transactions', @@ -61,4 +62,8 @@ export class TransactionsComponent implements OnInit, AfterViewInit { this.transactionDataSource.paginator = this.paginator; this.transactionDataSource.sort = this.sort; } + + downloadCsv(): void { + exportCsv(this.transactions, 'transactions'); + } } diff --git a/src/app/shared/error-dialog/error-dialog.component.html b/src/app/shared/error-dialog/error-dialog.component.html index ec0825c..f508467 100644 --- a/src/app/shared/error-dialog/error-dialog.component.html +++ b/src/app/shared/error-dialog/error-dialog.component.html @@ -3,8 +3,8 @@

Message: {{ data.message }}

-

- Status: {{ data.status }} +

+ Status: {{ data?.status }}

diff --git a/src/index.html b/src/index.html index 3597d16..c67cbaa 100644 --- a/src/index.html +++ b/src/index.html @@ -17,9 +17,8 @@ - - - - + + +