From 9ab6a337d3324388ba3202b046c80f4e01ee3c78 Mon Sep 17 00:00:00 2001 From: Barry Low Date: Tue, 18 Sep 2018 00:06:55 -0700 Subject: [PATCH 01/12] Client: Upload and crop image --- client/.env.sample | 2 + client/package-lock.json | 825 ++++++++++++++---- client/package.json | 5 + client/src/views/common/UploadImage.js | 247 ++++++ client/src/views/common/index.js | 3 +- .../dashboard/components/ManageTeamEdit.js | 3 + client/tools/webpack/webpack.dev.config.js | 3 +- client/tools/webpack/webpack.prod.config.js | 7 +- 8 files changed, 902 insertions(+), 193 deletions(-) create mode 100644 client/src/views/common/UploadImage.js diff --git a/client/.env.sample b/client/.env.sample index 789a271c6..4a54c8fd9 100644 --- a/client/.env.sample +++ b/client/.env.sample @@ -9,3 +9,5 @@ AUTH0_AUDIENCE= AUTH0_ISSUER= AUTH0_CLIENTID= AUTH0_CALLBACKURL= + +AZURE_STORAGE_CONNECTION_STRING= diff --git a/client/package-lock.json b/client/package-lock.json index 8f803109b..219ddf2b8 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -2626,7 +2626,6 @@ "version": "5.5.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "dev": true, "requires": { "co": "^4.6.0", "fast-deep-equal": "^1.0.0", @@ -2682,14 +2681,12 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -2832,8 +2829,7 @@ "aproba": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" }, "arch": { "version": "2.1.1", @@ -2845,7 +2841,6 @@ "version": "1.1.5", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "dev": true, "requires": { "delegates": "^1.0.0", "readable-stream": "^2.0.6" @@ -2894,6 +2889,11 @@ "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=", "dev": true }, + "array-filter": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz", + "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=" + }, "array-find-index": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", @@ -2910,6 +2910,16 @@ "es-abstract": "^1.7.0" } }, + "array-map": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz", + "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=" + }, + "array-reduce": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz", + "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=" + }, "array-union": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", @@ -2946,7 +2956,6 @@ "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, "requires": { "safer-buffer": "~2.1.0" } @@ -2991,8 +3000,7 @@ "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, "assign-symbols": { "version": "1.0.0", @@ -3046,6 +3054,14 @@ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, + "attr-accept": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-1.1.3.tgz", + "integrity": "sha512-iT40nudw8zmCweivz6j58g+RT33I4KbaIvRUhjNmDwO2WmsQUxFEZZYZ5w3vXe5x5MX9D7mfvA/XaLOZYFR9EQ==", + "requires": { + "core-js": "^2.5.0" + } + }, "auth0-js": { "version": "9.7.3", "resolved": "https://registry.npmjs.org/auth0-js/-/auth0-js-9.7.3.tgz", @@ -3207,14 +3223,12 @@ "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", - "dev": true + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" }, "axobject-query": { "version": "2.0.1", @@ -3225,6 +3239,59 @@ "ast-types-flow": "0.0.7" } }, + "azure-storage": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/azure-storage/-/azure-storage-2.10.1.tgz", + "integrity": "sha512-rnFo1uMIPtilusRCpK91tfY3P4Q7qRsDNwriXdp+OeTIGkGt0cTxL4mhqYfNPYPK+WBQmBdGWhOk+iROM05dcw==", + "requires": { + "browserify-mime": "~1.2.9", + "extend": "~1.2.1", + "json-edm-parser": "0.1.2", + "md5.js": "1.3.4", + "readable-stream": "~2.0.0", + "request": "^2.86.0", + "underscore": "~1.8.3", + "uuid": "^3.0.0", + "validator": "~9.4.1", + "xml2js": "0.2.8", + "xmlbuilder": "0.4.3" + }, + "dependencies": { + "extend": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-1.2.1.tgz", + "integrity": "sha1-oPX9bPyDpf5J72mNYOyKYk3UV2w=" + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" + }, + "readable-stream": { + "version": "2.0.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + } + } + }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -4480,8 +4547,7 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "base": { "version": "0.11.2", @@ -4547,7 +4613,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, "optional": true, "requires": { "tweetnacl": "^0.14.3" @@ -4571,6 +4636,15 @@ "integrity": "sha512-XBaoWE9RW8pPdPQNibZsW2zh8TW6gcarXp1FZPwT8Uop8ScSNldJEWf2k9l3HeTqdrEwsOsFcq74RiJECW34yA==", "dev": true }, + "bl": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", + "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, "block-stream": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", @@ -4630,7 +4704,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4713,6 +4786,11 @@ "safe-buffer": "^5.1.2" } }, + "browserify-mime": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/browserify-mime/-/browserify-mime-1.2.9.tgz", + "integrity": "sha1-rrGvKN5sDXpqLOQK22j/GEIq8x8=" + }, "browserify-rsa": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", @@ -4787,7 +4865,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", - "dev": true, "requires": { "buffer-alloc-unsafe": "^1.1.0", "buffer-fill": "^1.0.0" @@ -4796,14 +4873,12 @@ "buffer-alloc-unsafe": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", - "dev": true + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" }, "buffer-fill": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", - "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", - "dev": true + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=" }, "buffer-from": { "version": "1.0.0", @@ -4820,8 +4895,7 @@ "builtin-modules": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" }, "builtin-status-codes": { "version": "3.0.0", @@ -5037,14 +5111,12 @@ "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -5086,8 +5158,7 @@ "chownr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", - "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", - "dev": true + "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=" }, "chrome-trace-event": { "version": "1.0.0", @@ -5426,8 +5497,7 @@ "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" }, "coa": { "version": "1.0.4", @@ -5441,8 +5511,7 @@ "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, "collection-visit": { "version": "1.0.0", @@ -5469,7 +5538,6 @@ "version": "1.9.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", - "dev": true, "requires": { "color-name": "^1.1.1" } @@ -5477,8 +5545,7 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "color-string": { "version": "0.3.0", @@ -5543,8 +5610,7 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "concat-stream": { "version": "1.6.2", @@ -5603,8 +5669,7 @@ "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" }, "constants-browserify": { "version": "1.0.0", @@ -6044,7 +6109,6 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, "requires": { "assert-plus": "^1.0.0" } @@ -6106,7 +6170,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dev": true, "requires": { "mimic-response": "^1.0.0" } @@ -6120,8 +6183,7 @@ "deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" }, "deep-is": { "version": "0.1.3", @@ -6138,7 +6200,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", - "dev": true, "requires": { "foreach": "^2.0.5", "object-keys": "^1.0.8" @@ -6214,8 +6275,7 @@ "delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" }, "depd": { "version": "1.1.2", @@ -6254,6 +6314,11 @@ "repeating": "^2.0.0" } }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" + }, "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", @@ -6406,6 +6471,11 @@ "dotenv": "^5.0.1" } }, + "duplexer": { + "version": "0.1.1", + "resolved": "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=" + }, "duplexer3": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", @@ -6428,7 +6498,6 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, "optional": true, "requires": { "jsbn": "~0.1.0", @@ -6504,7 +6573,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "dev": true, "requires": { "once": "^1.4.0" } @@ -6555,7 +6623,6 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, "requires": { "is-arrayish": "^0.2.1" } @@ -6570,7 +6637,6 @@ "version": "1.11.0", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.11.0.tgz", "integrity": "sha512-ZnQrE/lXTTQ39ulXZ+J1DTFazV9qBy61x2bY071B+qGco8Z8q1QddsLdt/EF8Ai9hcWH72dWS0kFqXLxOxqslA==", - "dev": true, "requires": { "es-to-primitive": "^1.1.1", "function-bind": "^1.1.1", @@ -6583,7 +6649,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", - "dev": true, "requires": { "is-callable": "^1.1.1", "is-date-object": "^1.0.1", @@ -6631,8 +6696,7 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "eslint": { "version": "5.5.0", @@ -7141,6 +7205,21 @@ "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", "dev": true }, + "event-stream": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.6.tgz", + "integrity": "sha512-dGXNg4F/FgVzlApjzItL+7naHutA3fDqbV/zAZqDDlXTjiMnQmZKu+prImWKszeBM5UQeGvAl3u1wBiKeDh61g==", + "requires": { + "duplexer": "^0.1.1", + "flatmap-stream": "^0.1.0", + "from": "^0.1.7", + "map-stream": "0.0.7", + "pause-stream": "^0.0.11", + "split": "^1.0.1", + "stream-combiner": "^0.2.2", + "through": "^2.3.8" + } + }, "eventemitter3": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", @@ -7278,6 +7357,11 @@ } } }, + "expand-template": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-1.1.1.tgz", + "integrity": "sha512-cebqLtV8KOZfw0UI8TEFWxtczxxC1jvyUvx6H4fyp1K1FN7A4Q+uggVUlOsI1K8AGU0rwOGqP8nCapdrw8CYQg==" + }, "expand-tilde": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", @@ -7392,14 +7476,12 @@ "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, "fast-deep-equal": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" }, "fast-diff": { "version": "1.1.2", @@ -7561,6 +7643,11 @@ "write": "^0.2.1" } }, + "flatmap-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/flatmap-stream/-/flatmap-stream-0.1.0.tgz", + "integrity": "sha512-Nlic4ZRYxikqnK5rj3YoxDVKGGtUjcNDUtvQ7XsdGLZmMwdUYnXf10o1zcXtzEZTBgc6GxeRpQxV/Wu3WPIIHA==" + }, "flatten": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz", @@ -7601,14 +7688,12 @@ "foreach": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", - "dev": true + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" }, "form-data": { "version": "2.3.2", @@ -7640,6 +7725,11 @@ "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", "dev": true }, + "from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=" + }, "from2": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", @@ -7650,6 +7740,16 @@ "readable-stream": "^2.0.0" } }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "fs-copy-file-sync": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/fs-copy-file-sync/-/fs-copy-file-sync-1.1.1.tgz", + "integrity": "sha512-2QY5eeqVv4m2PfyMiEuy9adxNP+ajf+8AR05cEi+OAzPcOj90hvFImeZhTmKLBgSd9EvG33jsD7ZRxsx9dThkQ==" + }, "fs-extra": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", @@ -7661,6 +7761,14 @@ "universalify": "^0.1.0" } }, + "fs-minipass": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", + "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", + "requires": { + "minipass": "^2.2.1" + } + }, "fs-write-stream-atomic": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", @@ -8223,8 +8331,7 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "functional-red-black-tree": { "version": "1.0.1", @@ -8236,7 +8343,6 @@ "version": "2.7.4", "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "dev": true, "requires": { "aproba": "^1.0.3", "console-control-strings": "^1.0.0", @@ -8252,7 +8358,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, "requires": { "number-is-nan": "^1.0.0" } @@ -8261,7 +8366,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -8272,7 +8376,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, "requires": { "ansi-regex": "^2.0.0" } @@ -8322,7 +8425,6 @@ "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, "requires": { "assert-plus": "^1.0.0" } @@ -8385,6 +8487,11 @@ } } }, + "github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=" + }, "github-username": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/github-username/-/github-username-4.1.0.tgz", @@ -8598,8 +8705,7 @@ "graceful-fs": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" }, "graphql": { "version": "0.13.2", @@ -8634,14 +8740,12 @@ "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" }, "har-validator": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", - "dev": true, "requires": { "ajv": "^5.1.0", "har-schema": "^2.0.0" @@ -8651,7 +8755,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", - "dev": true, "requires": { "function-bind": "^1.0.2" } @@ -8674,8 +8777,7 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, "has-symbol-support-x": { "version": "1.4.2", @@ -8701,8 +8803,7 @@ "has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" }, "has-value": { "version": "1.0.0", @@ -8740,7 +8841,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", - "dev": true, "requires": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -8828,8 +8928,7 @@ "hosted-git-info": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", - "dev": true + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==" }, "html-comment-regex": { "version": "1.1.1", @@ -9003,7 +9102,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, "requires": { "assert-plus": "^1.0.0", "jsprim": "^1.2.2", @@ -9213,8 +9311,7 @@ "ini": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" }, "inquirer": { "version": "3.3.0", @@ -9297,8 +9394,7 @@ "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" }, "is-binary-path": { "version": "1.0.1", @@ -9319,7 +9415,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", - "dev": true, "requires": { "builtin-modules": "^1.0.0" } @@ -9327,8 +9422,7 @@ "is-callable": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", - "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=", - "dev": true + "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=" }, "is-ci": { "version": "1.2.0", @@ -9362,8 +9456,7 @@ "is-date-object": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", - "dev": true + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" }, "is-descriptor": { "version": "0.1.6", @@ -9429,8 +9522,7 @@ "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" }, "is-function": { "version": "1.0.1", @@ -9588,7 +9680,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "dev": true, "requires": { "has": "^1.0.1" } @@ -9631,14 +9722,12 @@ "is-symbol": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", - "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", - "dev": true + "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=" }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, "is-utf8": { "version": "0.2.1", @@ -9692,8 +9781,7 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, "isobject": { "version": "3.0.1", @@ -9712,8 +9800,7 @@ "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, "istextorbinary": { "version": "2.2.1", @@ -9921,23 +10008,28 @@ "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", "dev": true }, + "json-edm-parser": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/json-edm-parser/-/json-edm-parser-0.1.2.tgz", + "integrity": "sha1-HmCw/vG8CvZ7wNFG393lSGzWFbQ=", + "requires": { + "jsonparse": "~1.2.0" + } + }, "json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" }, "json-schema-traverse": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -9948,8 +10040,7 @@ "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, "json5": { "version": "0.5.1", @@ -9966,6 +10057,11 @@ "graceful-fs": "^4.1.6" } }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" + }, "jsonp": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/jsonp/-/jsonp-0.2.1.tgz", @@ -9984,11 +10080,15 @@ } } }, + "jsonparse": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.2.0.tgz", + "integrity": "sha1-XAxWhRBxYOcv50ib3eoLRMK8Z70=" + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, "requires": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", @@ -10899,6 +10999,11 @@ "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", "dev": true }, + "map-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", + "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=" + }, "map-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", @@ -10924,7 +11029,6 @@ "version": "1.3.4", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", - "dev": true, "requires": { "hash-base": "^3.0.0", "inherits": "^2.0.1" @@ -11039,6 +11143,11 @@ "readable-stream": "^2.0.1" } }, + "memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=" + }, "meow": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", @@ -11248,8 +11357,7 @@ "mimic-response": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" }, "mini-css-extract-plugin": { "version": "0.4.2", @@ -11330,7 +11438,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -11338,8 +11445,7 @@ "minimist": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "minimist-options": { "version": "3.0.2", @@ -11351,6 +11457,30 @@ "is-plain-obj": "^1.1.0" } }, + "minipass": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.4.tgz", + "integrity": "sha512-mlouk1OHlaUE8Odt1drMtG1bAJA4ZA6B/ehysgV0LUIrDHdKgo1KorZq3pK0b/7Z7LJIQ12MNM6aC+Tn6lUZ5w==", + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + }, + "dependencies": { + "yallist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", + "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=" + } + } + }, + "minizlib": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.1.0.tgz", + "integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==", + "requires": { + "minipass": "^2.2.1" + } + }, "mississippi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz", @@ -11412,7 +11542,6 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, "requires": { "minimist": "0.0.8" } @@ -11468,8 +11597,7 @@ "nan": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.0.tgz", - "integrity": "sha512-F4miItu2rGnV2ySkXOQoA8FKz/SR2Q2sWP0sbTxNxz/tuokeC8WxOhPMcwi0qIyGtVn/rrSeLbvVkznqCdwYnw==", - "dev": true + "integrity": "sha512-F4miItu2rGnV2ySkXOQoA8FKz/SR2Q2sWP0sbTxNxz/tuokeC8WxOhPMcwi0qIyGtVn/rrSeLbvVkznqCdwYnw==" }, "nanoassert": { "version": "1.1.0", @@ -11552,8 +11680,7 @@ "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" }, "no-case": { "version": "2.3.2", @@ -11564,6 +11691,14 @@ "lower-case": "^1.1.1" } }, + "node-abi": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.4.3.tgz", + "integrity": "sha512-b656V5C0628gOOA2kwcpNA/bxdlqYF9FvxJ+qqVX0ctdXNVZpS8J6xEUYir3WAKc7U0BH/NRlSpNbGsy+azjeg==", + "requires": { + "semver": "^5.4.1" + } + }, "node-dir": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/node-dir/-/node-dir-0.1.8.tgz", @@ -11755,6 +11890,11 @@ } } }, + "noop-logger": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", + "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=" + }, "nopt": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", @@ -11768,7 +11908,6 @@ "version": "2.4.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", - "dev": true, "requires": { "hosted-git-info": "^2.1.4", "is-builtin-module": "^1.0.0", @@ -11808,6 +11947,79 @@ "sort-keys": "^1.0.0" } }, + "npm-run-all": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.3.tgz", + "integrity": "sha512-aOG0N3Eo/WW+q6sUIdzcV2COS8VnTZCmdji0VQIAZF3b+a3YWb0AD0vFIyjKec18A7beLGbaQ5jFTNI2bPt9Cg==", + "requires": { + "ansi-styles": "^3.2.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.4", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "ps-tree": "^1.1.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + } + } + }, "npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", @@ -11821,7 +12033,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "dev": true, "requires": { "are-we-there-yet": "~1.1.2", "console-control-strings": "~1.1.0", @@ -11847,14 +12058,12 @@ "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" }, "oauth-sign": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", - "dev": true + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" }, "object-assign": { "version": "4.1.1", @@ -11895,8 +12104,7 @@ "object-keys": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", - "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=", - "dev": true + "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=" }, "object-visit": { "version": "1.0.1", @@ -11961,7 +12169,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -12035,8 +12242,7 @@ "os-homedir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" }, "os-locale": { "version": "1.4.0", @@ -12319,8 +12525,7 @@ "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" }, "path-parse": { "version": "1.0.5", @@ -12352,6 +12557,14 @@ "pify": "^2.0.0" } }, + "pause-stream": { + "version": "0.0.11", + "resolved": "http://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", + "requires": { + "through": "~2.3" + } + }, "pbkdf2": { "version": "3.0.16", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.16.tgz", @@ -12368,8 +12581,7 @@ "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "pify": { "version": "2.3.0", @@ -12934,6 +13146,35 @@ "uniqs": "^2.0.0" } }, + "prebuild-install": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-4.0.0.tgz", + "integrity": "sha512-7tayxeYboJX0RbVzdnKyGl2vhQRWr6qfClEXDhOkXjuaOKCw2q8aiuFhONRYVsG/czia7KhpykIlI2S2VaPunA==", + "requires": { + "detect-libc": "^1.0.3", + "expand-template": "^1.0.2", + "github-from-package": "0.0.0", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "node-abi": "^2.2.0", + "noop-logger": "^0.1.1", + "npmlog": "^4.0.1", + "os-homedir": "^1.0.1", + "pump": "^2.0.1", + "rc": "^1.1.6", + "simple-get": "^2.7.0", + "tar-fs": "^1.13.0", + "tunnel-agent": "^0.6.0", + "which-pm-runs": "^1.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -13324,6 +13565,14 @@ "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", "dev": true }, + "ps-tree": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.1.0.tgz", + "integrity": "sha1-tCGyQUDWID8e08dplrRCewjowBQ=", + "requires": { + "event-stream": "~3.3.0" + } + }, "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", @@ -13347,7 +13596,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "dev": true, "requires": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -13367,8 +13615,7 @@ "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" }, "q": { "version": "1.5.1", @@ -13457,7 +13704,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, "requires": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -13468,8 +13714,7 @@ "minimist": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" } } }, @@ -13528,6 +13773,15 @@ "schedule": "^0.3.0" } }, + "react-dropzone": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-5.1.0.tgz", + "integrity": "sha512-cIYp3GCm0I4fckmQJhrFSKq/W91Ma1BZTCXMr7hg1jnVYmERsa6bruJroci4Lq9QvdoAnh+6r6SVQ2v6CVQ6Hg==", + "requires": { + "attr-accept": "^1.1.3", + "prop-types": "^15.6.2" + } + }, "react-event-listener": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/react-event-listener/-/react-event-listener-0.6.3.tgz", @@ -13553,6 +13807,30 @@ } } }, + "react-image-crop": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/react-image-crop/-/react-image-crop-5.0.0.tgz", + "integrity": "sha512-MBT18tcHeEpmrhbSS6hx00MmfNgrnlhZuwY96cGRPhUmNeabxM7UBCcoU+oJCZXB0iGZk38zy/YGoZ4ELVp/LQ==" + }, + "react-image-cropper": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/react-image-cropper/-/react-image-cropper-1.3.0.tgz", + "integrity": "sha512-OPEMv66xXQdNsAixqxhlsSX3+D3KVRQMYc6YHmgGINeKx433xB9l04hMFynngPuUa+ChiKal6aQmbzpg79iJ+Q==", + "requires": { + "deep-extend": "^0.4.1", + "npm-run-all": "^4.1.1", + "prop-types": "^15.6.0", + "react": "^16.2.0", + "react-dom": "^16.2.0" + }, + "dependencies": { + "deep-extend": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", + "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=" + } + } + }, "react-jss": { "version": "8.6.1", "resolved": "https://registry.npmjs.org/react-jss/-/react-jss-8.6.1.tgz", @@ -13999,7 +14277,6 @@ "version": "2.87.0", "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz", "integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==", - "dev": true, "requires": { "aws-sign2": "~0.7.0", "aws4": "^1.6.0", @@ -14026,8 +14303,7 @@ "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" } } }, @@ -14375,8 +14651,7 @@ "semver": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", - "dev": true + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" }, "semver-diff": { "version": "2.1.0", @@ -14396,8 +14671,7 @@ "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, "set-immediate-shim": { "version": "1.0.1", @@ -14468,11 +14742,71 @@ } } }, + "sharp": { + "version": "0.20.8", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.20.8.tgz", + "integrity": "sha512-A8NaPGWRDKpmHTi8sl2xzozYXhTQWBb/GaJ8ZPU7L/vKW8wVvd4Yq+isJ0c7p9sX5gnjPQcM3eOfHuvvnZ2fOQ==", + "requires": { + "color": "^3.0.0", + "detect-libc": "^1.0.3", + "fs-copy-file-sync": "^1.1.1", + "nan": "^2.11.0", + "npmlog": "^4.1.2", + "prebuild-install": "^4.0.0", + "semver": "^5.5.1", + "simple-get": "^2.8.1", + "tar": "^4.4.6", + "tunnel-agent": "^0.6.0" + }, + "dependencies": { + "color": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", + "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", + "requires": { + "color-convert": "^1.9.1", + "color-string": "^1.5.2" + } + }, + "color-string": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", + "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "semver": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", + "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==" + }, + "tar": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.6.tgz", + "integrity": "sha512-tMkTnh9EdzxyfW+6GK6fCahagXsnYk6kE6S9Gr9pjVdys769+laCTbodXDhPAjzVtEBazRgP0gYqOjnk9dQzLg==", + "requires": { + "chownr": "^1.0.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.3", + "minizlib": "^1.1.0", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "yallist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", + "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=" + } + } + }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, "requires": { "shebang-regex": "^1.0.0" } @@ -14480,8 +14814,18 @@ "shebang-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + }, + "shell-quote": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz", + "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=", + "requires": { + "array-filter": "~0.0.0", + "array-map": "~0.0.0", + "array-reduce": "~0.0.0", + "jsonify": "~0.0.0" + } }, "shelljs": { "version": "0.8.2", @@ -14497,8 +14841,37 @@ "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "simple-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz", + "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=" + }, + "simple-get": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz", + "integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==", + "requires": { + "decompress-response": "^3.3.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "requires": { + "is-arrayish": "^0.3.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + } + } }, "slash": { "version": "1.0.0", @@ -14690,7 +15063,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", - "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" @@ -14699,14 +15071,12 @@ "spdx-exceptions": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", - "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==", - "dev": true + "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==" }, "spdx-expression-parse": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "dev": true, "requires": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" @@ -14715,8 +15085,15 @@ "spdx-license-ids": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", - "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==", - "dev": true + "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==" + }, + "split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "requires": { + "through": "2" + } }, "split-string": { "version": "3.1.0", @@ -14737,7 +15114,6 @@ "version": "1.14.2", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", - "dev": true, "requires": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -14805,6 +15181,15 @@ "readable-stream": "^2.0.2" } }, + "stream-combiner": { + "version": "0.2.2", + "resolved": "http://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", + "integrity": "sha1-rsjLrBd7Vrb0+kec7YwZEs7lKFg=", + "requires": { + "duplexer": "~0.1.1", + "through": "~2.3.4" + } + }, "stream-each": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", @@ -14850,12 +15235,21 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, "requires": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" } }, + "string.prototype.padend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.0.0.tgz", + "integrity": "sha1-86rvfBcZ8XDF6rHDK/eA2W4h8vA=", + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.4.3", + "function-bind": "^1.0.2" + } + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -14868,7 +15262,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, "requires": { "ansi-regex": "^3.0.0" }, @@ -14876,16 +15269,14 @@ "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" } } }, "strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" }, "strip-bom-stream": { "version": "2.0.0", @@ -14926,8 +15317,7 @@ "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" }, "style-loader": { "version": "0.21.0", @@ -14979,7 +15369,6 @@ "version": "5.4.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -15080,6 +15469,42 @@ "inherits": "2" } }, + "tar-fs": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.16.3.tgz", + "integrity": "sha512-NvCeXpYx7OsmOh8zIOP/ebG55zZmxLE0etfWRbWok+q2Qo8x/vOR/IJT1taADXPe+jsiu9axDb3X4B+iIgNlKw==", + "requires": { + "chownr": "^1.0.1", + "mkdirp": "^0.5.1", + "pump": "^1.0.0", + "tar-stream": "^1.1.2" + }, + "dependencies": { + "pump": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz", + "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "tar-stream": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.1.tgz", + "integrity": "sha512-IFLM5wp3QrJODQFPm6/to3LJZrONdBY/otxcvDIQzu217zKye6yVR3hhi9lAjrC2Z+m/j5oDxMPb1qcd8cIvpA==", + "requires": { + "bl": "^1.0.0", + "buffer-alloc": "^1.1.0", + "end-of-stream": "^1.0.0", + "fs-constants": "^1.0.0", + "readable-stream": "^2.3.0", + "to-buffer": "^1.1.0", + "xtend": "^4.0.0" + } + }, "temp": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz", @@ -15151,8 +15576,7 @@ "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, "through2": { "version": "2.0.3", @@ -15200,6 +15624,11 @@ "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", "dev": true }, + "to-buffer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==" + }, "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -15273,7 +15702,6 @@ "version": "2.3.4", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", - "dev": true, "requires": { "punycode": "^1.4.1" } @@ -15320,7 +15748,6 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, "requires": { "safe-buffer": "^5.0.1" } @@ -15329,7 +15756,6 @@ "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true, "optional": true }, "type-check": { @@ -15731,8 +16157,7 @@ "uuid": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", - "dev": true + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" }, "v8-compile-cache": { "version": "2.0.2", @@ -15744,12 +16169,16 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, "requires": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" } }, + "validator": { + "version": "9.4.1", + "resolved": "http://registry.npmjs.org/validator/-/validator-9.4.1.tgz", + "integrity": "sha512-YV5KjzvRmSyJ1ee/Dm5UED0G+1L4GZnLN3w6/T+zZm8scVua4sOhYKWTUrKa0H/tMiJyO9QLHMPN+9mB/aMunA==" + }, "value-equal": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-0.4.0.tgz", @@ -15771,7 +16200,6 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, "requires": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", @@ -16578,7 +17006,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", - "dev": true, "requires": { "isexe": "^2.0.0" } @@ -16589,11 +17016,15 @@ "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", "dev": true }, + "which-pm-runs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", + "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=" + }, "wide-align": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, "requires": { "string-width": "^1.0.2 || 2" } @@ -16831,8 +17262,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write": { "version": "0.2.1", @@ -16868,11 +17298,30 @@ "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", "dev": true }, + "xml2js": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.2.8.tgz", + "integrity": "sha1-m4FpCTFjH/CdGVdUn69U9PmAs8I=", + "requires": { + "sax": "0.5.x" + }, + "dependencies": { + "sax": { + "version": "0.5.8", + "resolved": "http://registry.npmjs.org/sax/-/sax-0.5.8.tgz", + "integrity": "sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE=" + } + } + }, + "xmlbuilder": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-0.4.3.tgz", + "integrity": "sha1-xGFLp04K0ZbmCcknLNnh3bKKilg=" + }, "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" }, "y18n": { "version": "3.2.1", diff --git a/client/package.json b/client/package.json index 27b67cb88..3fb9b227e 100644 --- a/client/package.json +++ b/client/package.json @@ -78,6 +78,7 @@ "apollo-link-ws": "^1.0.8", "apollo-utilities": "^1.0.20", "auth0-lock": "^11.9.0", + "azure-storage": "^2.10.1", "babel-polyfill": "^6.26.0", "classnames": "^2.2.6", "cross-fetch": "^2.2.2", @@ -88,8 +89,12 @@ "react": "^16.5.0", "react-apollo": "^2.1.11", "react-dom": "^16.5.0", + "react-dropzone": "^5.1.0", + "react-image-crop": "^5.0.0", + "react-image-cropper": "^1.3.0", "react-router-dom": "^4.3.1", "recompose": "^0.30.0", + "sharp": "^0.20.8", "subscriptions-transport-ws": "^0.9.14" }, "standard": { diff --git a/client/src/views/common/UploadImage.js b/client/src/views/common/UploadImage.js new file mode 100644 index 000000000..f5b625c8f --- /dev/null +++ b/client/src/views/common/UploadImage.js @@ -0,0 +1,247 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import Dropzone from 'react-dropzone' +// import ReactCrop from 'react-image-crop' +import { Cropper } from 'react-image-cropper' +// import azure from 'azure-storage' +import { withStyles } from '@material-ui/core/styles' + +import Dialog from '@material-ui/core/Dialog' +import DialogActions from '@material-ui/core/DialogActions' +import DialogContent from '@material-ui/core/DialogContent' +import Button from '@material-ui/core/Button' + +const styles = theme => ({ + dropzoneContainer: { + display: 'block', + margin: '1em', + minWidth: 200, + height: 200 + }, + dropzone: { + display: 'block', + padding: '3em', + minWidth: 200, + height: 200, + borderWidth: 2, + borderColor: '#999', + backgroundColor: '#eee' + }, + dropzoneText: { + textAlign: 'center' + }, + cropPreview: { + width: 200, + height: 200 + }, + cropBox: { + width: 350, + height: 350 + }, + reactCrop: { + } +}) + +class UploadImage extends Component { + constructor (props) { + super(props) + + // this.saveToStorage = this.saveToStorage.bind(this) + this.onDrop = this.onDrop.bind(this) + this.handleCrop = this.handleCrop.bind(this) + this.onCropComplete = this.onCropComplete.bind(this) + this.onCropChange = this.onCropChange.bind(this) + this.getCroppedImg = this.getCroppedImg.bind(this) + this.handleClickOpen = this.handleClickOpen.bind(this) + this.handleClose = this.handleClose.bind(this) + + this.state = { + open: false, + file: null, + src: null, + crop: { + x: 10, + y: 10, + width: 200, + height: 200 + }, + cropped: null, + rejected: null + } + } + + render () { + const { classes, team, authId } = this.props + console.log('UploadImage', team, authId, this.state) + return ( +
+ {this.state.src === null && ( + +

Drag-and-drop team avater here or click to select files.
Only *.jpeg and *.png images

+
+ )} + {this.state.cropped !== null && ( + Crop Preview + )} + {this.state.src !== null && ( + +
+ +
+ { this.cropper = ref }} + onImgLoad={this.onImageLoaded} + /> +
+
+ + + + +
+
+ )} +
+ ) + } + + onDrop (file) { + console.log('onDrop', file) + this.onSelectFile(file) + this.setState({ + file + }) + } + + onSelectFile (file) { + if (file && file.length > 0) { + const reader = new window.FileReader() + reader.addEventListener( + 'load', + () => + this.setState({ + src: reader.result, + open: true + }), + false + ) + reader.readAsDataURL(file[0]) + } + } + + handleCrop () { + console.log('handleCrop', this.cropper) + this.setState({ cropped: this.cropper.crop(), open: false }) + } + + onCropComplete (crop, pixelCrop) { + console.log('onCropComplete', crop, pixelCrop, this.state) + const cropped = this.getCroppedImg(crop, pixelCrop, this.state.src) + this.setState({ cropped }) + } + + onCropChange (crop) { + this.setState({ crop }) + } + + getCroppedImg (image, pixelCrop, fileName) { + const canvas = document.createElement('canvas') + canvas.width = pixelCrop.width + canvas.height = pixelCrop.height + const ctx = canvas.getContext('2d') + + ctx.drawImage( + image, + pixelCrop.x, + pixelCrop.y, + pixelCrop.width, + pixelCrop.height, + 0, + 0, + pixelCrop.width, + pixelCrop.height + ) + + // As Base64 string + return canvas.toDataURL('image/jpeg') + + // As a blob + /* + return new Promise((resolve, reject) => { + canvas.toBlob(file => { + file.name = fileName + resolve(file) + }, 'image/jpeg') + }); + */ + } + + handleClickOpen () { + this.setState({ open: true }) + } + + handleClose () { + this.setState({ open: false, src: null }) + } + /* + saveToStorage (xhr) { + const blobService = azure.createBlobService() + const container = '' + const Stream = require('stream') + const readable = new Stream.Readable() + readable.push(xhr.responseText) + readable.push(null) + + blobService.createBlockBlobFromStream( + container, + this.state.accepted[0].name, + readable, + xhr.responseText.length, + (error, result, response) => { + if (error) return (

{error}

) + return 'Success' + } + ) + xhr.send() + } + */ +} + +UploadImage.propTypes = { + classes: PropTypes.object.isRequired, + team: PropTypes.object, + authId: PropTypes.string.isRequired +} + +export default withStyles(styles)(UploadImage) + +/* + +*/ diff --git a/client/src/views/common/index.js b/client/src/views/common/index.js index fe07e8f49..12cfe68a0 100644 --- a/client/src/views/common/index.js +++ b/client/src/views/common/index.js @@ -1,3 +1,4 @@ import MessageCard from './MessageCard' +import UploadImage from './UploadImage' -export { MessageCard } +export { MessageCard, UploadImage } diff --git a/client/src/views/dashboard/components/ManageTeamEdit.js b/client/src/views/dashboard/components/ManageTeamEdit.js index 7d10e1a7a..9630c04be 100644 --- a/client/src/views/dashboard/components/ManageTeamEdit.js +++ b/client/src/views/dashboard/components/ManageTeamEdit.js @@ -20,6 +20,8 @@ import GET_TEAMS_BY_OWNER from '../../../graphql/teams/GetTeamsByOwnerQuery' import { checkGraphQLError } from '../../../utils/graphql-errors' +import { UploadImage } from '../../common' + const styles = theme => ({ dialogContainer: { display: 'block', @@ -101,6 +103,7 @@ export class ManageTeamEdit extends Component { Edit Team Details + Date: Thu, 20 Sep 2018 08:24:35 -0700 Subject: [PATCH 02/12] Basic file upload to Azure BlockBLob --- api/package-lock.json | 456 +++++++++++++++--- api/package.json | 6 +- api/src/index.js | 74 ++- api/src/schema.graphql | 1 + api/src/storage/azure.js | 133 +++++ client/package-lock.json | 171 ++++++- client/package.json | 2 + .../src/graphql/teams/GetAzureSASMutation.js | 10 + client/src/views/common/UploadImage.js | 81 +++- 9 files changed, 850 insertions(+), 84 deletions(-) create mode 100644 api/src/storage/azure.js create mode 100644 client/src/graphql/teams/GetAzureSASMutation.js diff --git a/api/package-lock.json b/api/package-lock.json index 81ed969ea..6d1c72d4f 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -3,6 +3,14 @@ "requires": true, "lockfileVersion": 1, "dependencies": { + "@azure/storage-blob": { + "version": "10.0.0-preview", + "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-10.0.0-preview.tgz", + "integrity": "sha512-PFXKm+gipKDfg5h08hPiEr0Za4RMiFzzte+ewh4ACZeRQa0XZK55NAZxW67ZytGBd7WXdQq6pcAgo5dBnwQANQ==", + "requires": { + "ms-rest-js": "0.21.413" + } + }, "@babel/generator": { "version": "7.0.0-beta.38", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.0.0-beta.38.tgz", @@ -213,6 +221,14 @@ "@types/express": "*" } }, + "@types/form-data": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz", + "integrity": "sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==", + "requires": { + "@types/node": "*" + } + }, "@types/graphql": { "version": "0.12.6", "resolved": "http://registry.npmjs.org/@types/graphql/-/graphql-0.12.6.tgz", @@ -247,6 +263,14 @@ "@types/mime": "*" } }, + "@types/uuid": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.4.tgz", + "integrity": "sha512-tPIgT0GUmdJQNSHxp0X2jnpQfBSTfGxUMc/2CXBU2mnyTFVYVa2ojpoQ74w0U2yn2vw3jnC640+77lkFFpdVDw==", + "requires": { + "@types/node": "*" + } + }, "@types/zen-observable": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.5.4.tgz", @@ -808,6 +832,86 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" }, + "axios": { + "version": "0.18.0", + "resolved": "http://registry.npmjs.org/axios/-/axios-0.18.0.tgz", + "integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=", + "requires": { + "follow-redirects": "^1.3.0", + "is-buffer": "^1.1.5" + } + }, + "azure-storage": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/azure-storage/-/azure-storage-2.10.1.tgz", + "integrity": "sha512-rnFo1uMIPtilusRCpK91tfY3P4Q7qRsDNwriXdp+OeTIGkGt0cTxL4mhqYfNPYPK+WBQmBdGWhOk+iROM05dcw==", + "requires": { + "browserify-mime": "~1.2.9", + "extend": "~1.2.1", + "json-edm-parser": "0.1.2", + "md5.js": "1.3.4", + "readable-stream": "~2.0.0", + "request": "^2.86.0", + "underscore": "~1.8.3", + "uuid": "^3.0.0", + "validator": "~9.4.1", + "xml2js": "0.2.8", + "xmlbuilder": "0.4.3" + }, + "dependencies": { + "extend": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-1.2.1.tgz", + "integrity": "sha1-oPX9bPyDpf5J72mNYOyKYk3UV2w=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" + }, + "readable-stream": { + "version": "2.0.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" + } + }, + "sax": { + "version": "0.5.8", + "resolved": "http://registry.npmjs.org/sax/-/sax-0.5.8.tgz", + "integrity": "sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE=" + }, + "validator": { + "version": "9.4.1", + "resolved": "http://registry.npmjs.org/validator/-/validator-9.4.1.tgz", + "integrity": "sha512-YV5KjzvRmSyJ1ee/Dm5UED0G+1L4GZnLN3w6/T+zZm8scVua4sOhYKWTUrKa0H/tMiJyO9QLHMPN+9mB/aMunA==" + }, + "xml2js": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.2.8.tgz", + "integrity": "sha1-m4FpCTFjH/CdGVdUn69U9PmAs8I=", + "requires": { + "sax": "0.5.x" + } + }, + "xmlbuilder": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-0.4.3.tgz", + "integrity": "sha1-xGFLp04K0ZbmCcknLNnh3bKKilg=" + } + } + }, "babel-runtime": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", @@ -968,6 +1072,21 @@ "qs": "6.5.2", "raw-body": "2.3.3", "type-is": "~1.6.16" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } } }, "body-parser-graphql": { @@ -1028,6 +1147,11 @@ } } }, + "browserify-mime": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/browserify-mime/-/browserify-mime-1.2.9.tgz", + "integrity": "sha1-rrGvKN5sDXpqLOQK22j/GEIq8x8=" + }, "buffer": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", @@ -1746,18 +1870,11 @@ "integrity": "sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==" }, "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.0.1.tgz", + "integrity": "sha512-K23FHJ/Mt404FSlp6gSZCevIbTMLX0j3fmHhUEhQ3Wq0FMODW3+cUSoLdy1Gx4polAf4t/lphhmHH35BB8cLYw==", "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } + "ms": "^2.1.1" } }, "decamelize": { @@ -2271,6 +2388,14 @@ "to-regex": "^3.0.1" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", @@ -2286,6 +2411,11 @@ "requires": { "is-extendable": "^0.1.0" } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" } } }, @@ -2409,11 +2539,24 @@ "type-is": "~1.6.15" } }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, "iconv-lite": { "version": "0.4.19", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, "qs": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", @@ -2680,6 +2823,14 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" }, + "fast-xml-parser": { + "version": "3.12.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-3.12.5.tgz", + "integrity": "sha512-g8TSGUF1a2vdFmQ29vKcYBNnwuJQQuyr6It3cjGsiD3dkUXqVWuXZQvjEkgrrCe5K8D30X125ACyxaj7XaaH8g==", + "requires": { + "nimnjs": "^1.3.2" + } + }, "fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", @@ -2762,6 +2913,19 @@ "unpipe": "~1.0.0" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, "statuses": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", @@ -2786,6 +2950,29 @@ "locate-path": "^2.0.0" } }, + "follow-redirects": { + "version": "1.5.8", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.8.tgz", + "integrity": "sha512-sy1mXPmv7kLAMKW/8XofG7o9T+6gAjzdZK4AJF6ryqQYUa/hnzgiypoeUecZ53x7XiqKNEpNqLtS97MshW2nxg==", + "requires": { + "debug": "=3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -4225,6 +4412,15 @@ } } }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, "header-case": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/header-case/-/header-case-1.0.1.tgz", @@ -4822,6 +5018,14 @@ "integrity": "sha1-5CGiqOINawgZ3yiQj3glJrlt0f4=", "dev": true }, + "json-edm-parser": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/json-edm-parser/-/json-edm-parser-0.1.2.tgz", + "integrity": "sha1-HmCw/vG8CvZ7wNFG393lSGzWFbQ=", + "requires": { + "jsonparse": "~1.2.0" + } + }, "json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", @@ -4898,6 +5102,11 @@ "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", "dev": true }, + "jsonparse": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.2.0.tgz", + "integrity": "sha1-XAxWhRBxYOcv50ib3eoLRMK8Z70=" + }, "jsonwebtoken": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.3.0.tgz", @@ -4946,6 +5155,23 @@ "lru-memoizer": "^1.6.0", "ms": "^2.0.0", "request": "^2.73.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + } } }, "jws": { @@ -5405,6 +5631,15 @@ "integrity": "sha1-izqsWIuKZuSXXjzepn97sylgH6w=", "dev": true }, + "md5.js": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", + "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -5419,9 +5654,9 @@ } }, "memfs": { - "version": "2.9.4", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-2.9.4.tgz", - "integrity": "sha512-wstvgqSTBGf9rQHoaRpX3wJiGzB/sj649XaO+byuHiPZQaR3Y/mOgU6n9iMFLtAHlR4BwwBWFgA4hobiU6EcCg==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-2.10.0.tgz", + "integrity": "sha512-GuQXfXghDdDdkokv6cwaWLh8bOD9Ots4z11qUR9yKiCeO7oMPhBjFjKVc991DIHTXFbonqKYOEDAVTa4CvvkJg==", "dev": true, "requires": { "fast-extend": "0.0.2", @@ -5555,6 +5790,30 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" }, + "ms-rest-js": { + "version": "0.21.413", + "resolved": "https://registry.npmjs.org/ms-rest-js/-/ms-rest-js-0.21.413.tgz", + "integrity": "sha512-eX+M98Y9s+zo85Unt83TYkpuJkW4d0wuiHrpWjAxx/7YzZd3uVq8HRhJHXGDqCd22nlEvtwlchfgwILmBcWnOg==", + "requires": { + "@types/express": "^4.11.1", + "@types/form-data": "^2.2.1", + "@types/node": "^9.4.6", + "@types/uuid": "^3.4.3", + "axios": "^0.18.0", + "form-data": "^2.3.2", + "tough-cookie": "^2.4.3", + "tslib": "^1.9.2", + "uuid": "^3.2.1", + "xml2js": "^0.4.19" + }, + "dependencies": { + "@types/node": { + "version": "9.6.31", + "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.31.tgz", + "integrity": "sha512-kIVlvUBizL51ALNMPbmcZoM7quHyB7J6fLRwQe22JsMp39nrVSHdBeVVS3fnQCK1orxI3O8LScmb8cuiihkAfA==" + } + } + }, "multimatch": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-2.1.0.tgz", @@ -5608,6 +5867,25 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "nimn-date-parser": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/nimn-date-parser/-/nimn-date-parser-1.0.0.tgz", + "integrity": "sha512-1Nf+x3EeMvHUiHsVuEhiZnwA8RMeOBVTQWfB1S2n9+i6PYCofHd2HRMD+WOHIHYshy4T4Gk8wQoCol7Hq3av8Q==" + }, + "nimn_schema_builder": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/nimn_schema_builder/-/nimn_schema_builder-1.1.0.tgz", + "integrity": "sha512-DK5/B8CM4qwzG2URy130avcwPev4uO0ev836FbQyKo1ms6I9z/i6EJyiZ+d9xtgloxUri0W+5gfR8YbPq7SheA==" + }, + "nimnjs": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/nimnjs/-/nimnjs-1.3.2.tgz", + "integrity": "sha512-TIOtI4iqkQrUM1tiM76AtTQem0c7e56SkDZ7sj1d1MfUsqRcq2ZWQvej/O+HBTZV7u/VKnwlKTDugK/75IRPPw==", + "requires": { + "nimn-date-parser": "^1.0.0", + "nimn_schema_builder": "^1.0.0" + } + }, "no-case": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", @@ -6330,6 +6608,23 @@ "async": "^1.5.2", "debug": "^2.2.0", "mkdirp": "0.5.x" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } } }, "posix-character-classes": { @@ -6338,9 +6633,9 @@ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" }, "postgres-array": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-1.0.2.tgz", - "integrity": "sha1-jgsy6wO/d6XAp4UeBEHBaaJWojg=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-1.0.3.tgz", + "integrity": "sha512-5wClXrAP0+78mcsNX3/ithQ5exKvCyK5lr5NEEEeGwwM6NJdQgzIJBVxLvRW+huFpX92F2QnZ5CcokH0VhK2qQ==", "dev": true }, "postgres-bytea": { @@ -6376,14 +6671,14 @@ "dev": true }, "prisma": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-1.16.0.tgz", - "integrity": "sha512-8jZYCrkd1xjy+JJbQi81f8PBOLe8LLWwSu0fK0cqekm3FeafBd8xk5b0RWcIsHyVd6ypKCQt3tnZEMVzmbKrdQ==", + "version": "1.16.4", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-1.16.4.tgz", + "integrity": "sha512-ha+SJDnJ+NVtkoP4nivcmnKV/PmkbQiXVJUPEdYfkQPpuite1kOuFp9seolE4DJFaz3m8DlF24AQf7aePf09AQ==", "dev": true, "requires": { "fs-extra": "^5.0.0", - "prisma-cli-core": "1.0.76", - "prisma-cli-engine": "1.0.79", + "prisma-cli-core": "1.0.79", + "prisma-cli-engine": "1.0.82", "semver": "^5.4.1", "source-map-support": "^0.4.18" }, @@ -6502,9 +6797,9 @@ } }, "prisma-cli-core": { - "version": "1.0.76", - "resolved": "https://registry.npmjs.org/prisma-cli-core/-/prisma-cli-core-1.0.76.tgz", - "integrity": "sha512-HtHn/RM2GlJi23KoIXHoboP97aC54SYDqr7B4uT/80JP6A6nQp5HLt4sJgNfKapaHFZVzXYM7M3LEfGq3tgQbw==", + "version": "1.0.79", + "resolved": "https://registry.npmjs.org/prisma-cli-core/-/prisma-cli-core-1.0.79.tgz", + "integrity": "sha512-3xaFE0wK48nZ8LOqx62gMoFgjIob121Uo6RHOnXNXMcCOdtGM7VfGw4o6gyDVwNDvu66fz0EVLaQGcJb5hnTpA==", "dev": true, "requires": { "adm-zip": "^0.4.7", @@ -6538,9 +6833,9 @@ "opn": "^5.1.0", "pause": "^0.1.0", "portfinder": "^1.0.13", - "prisma-db-introspection": "0.0.46", + "prisma-db-introspection": "0.0.50", "prisma-json-schema": "0.0.4", - "prisma-yml": "1.0.96", + "prisma-yml": "1.0.100", "scuid": "^1.0.2", "semver": "^5.4.1", "sillyname": "^0.1.0", @@ -6646,12 +6941,12 @@ } }, "graphql-config": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/graphql-config/-/graphql-config-2.1.0.tgz", - "integrity": "sha512-LWpkME3x+KSFMVuMpRmDHTIZLyQQsBoKtMXJrT4RgXk6y0GRf0lsJ81R3S2FmT1CKZQOJfFsOpUfPpAcfmoN/A==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/graphql-config/-/graphql-config-2.2.1.tgz", + "integrity": "sha512-U8+1IAhw9m6WkZRRcyj8ZarK96R6lQBQ0an4lp76Ps9FyhOXENC5YQOxOFGm5CxPrX2rD0g3Je4zG5xdNJjwzQ==", "dev": true, "requires": { - "graphql-import": "^0.4.4", + "graphql-import": "^0.7.1", "graphql-request": "^1.5.0", "js-yaml": "^3.10.0", "lodash": "^4.17.4", @@ -6697,12 +6992,13 @@ } }, "graphql-import": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/graphql-import/-/graphql-import-0.4.5.tgz", - "integrity": "sha512-G/+I08Qp6/QGTb9qapknCm3yPHV0ZL7wbaalWFpxsfR8ZhZoTBe//LsbsCKlbALQpcMegchpJhpTSKiJjhaVqQ==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/graphql-import/-/graphql-import-0.7.1.tgz", + "integrity": "sha512-YpwpaPjRUVlw2SN3OPljpWbVRWAhMAyfSba5U47qGMOSsPLi2gYeJtngGpymjm9nk57RFWEpjqwh4+dpYuFAPw==", "dev": true, "requires": { - "lodash": "^4.17.4" + "lodash": "^4.17.4", + "resolve-from": "^4.0.0" } }, "inquirer": { @@ -6778,9 +7074,9 @@ "dev": true }, "prisma-yml": { - "version": "1.0.96", - "resolved": "https://registry.npmjs.org/prisma-yml/-/prisma-yml-1.0.96.tgz", - "integrity": "sha512-w3XGzJHUq5pwBoFy/amkGOcTDj4XoHh8UFUUUZcHo5fSyoF8AWeRTu1yWINaPVGxbcmpwKNetntAW4ijiq7M5g==", + "version": "1.0.100", + "resolved": "https://registry.npmjs.org/prisma-yml/-/prisma-yml-1.0.100.tgz", + "integrity": "sha512-W2a0dpfbXyapc3WZqcvUBqtsl2r9upQRUR2p53AqQR4tQz+/6nsMcqe30vqgnOW/WmNAnknqQT3Z5P0rsq4K3Q==", "dev": true, "requires": { "ajv": "5", @@ -6803,6 +7099,12 @@ "yaml-ast-parser": "^0.0.40" } }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -6827,9 +7129,9 @@ } }, "prisma-cli-engine": { - "version": "1.0.79", - "resolved": "https://registry.npmjs.org/prisma-cli-engine/-/prisma-cli-engine-1.0.79.tgz", - "integrity": "sha512-CdEIJs4ld1IiQQt2CFFPmeDypN4Ct4sTgmFODVjJix790mpEtxMagzCcA+mhfnRL65P6xiHn76Gdxbz60GyYBA==", + "version": "1.0.82", + "resolved": "https://registry.npmjs.org/prisma-cli-engine/-/prisma-cli-engine-1.0.82.tgz", + "integrity": "sha512-kEqNtH0bjzVX17Vcs77UiAwk0Ei9Za03bOINcrpIjNUyI9GOOU5p+6ss8cePfK21lxvy5RSlYwthBFLkXE73Gg==", "dev": true, "requires": { "@heroku/linewrap": "^1.0.0", @@ -6872,7 +7174,7 @@ "memfs": "^2.5.3", "opn": "^5.1.0", "prisma-json-schema": "0.0.4", - "prisma-yml": "1.0.96", + "prisma-yml": "1.0.100", "raven": "2.3.0", "replaceall": "^0.1.6", "rwlockfile": "^1.4.8", @@ -6937,9 +7239,9 @@ "dev": true }, "prisma-yml": { - "version": "1.0.96", - "resolved": "https://registry.npmjs.org/prisma-yml/-/prisma-yml-1.0.96.tgz", - "integrity": "sha512-w3XGzJHUq5pwBoFy/amkGOcTDj4XoHh8UFUUUZcHo5fSyoF8AWeRTu1yWINaPVGxbcmpwKNetntAW4ijiq7M5g==", + "version": "1.0.100", + "resolved": "https://registry.npmjs.org/prisma-yml/-/prisma-yml-1.0.100.tgz", + "integrity": "sha512-W2a0dpfbXyapc3WZqcvUBqtsl2r9upQRUR2p53AqQR4tQz+/6nsMcqe30vqgnOW/WmNAnknqQT3Z5P0rsq4K3Q==", "dev": true, "requires": { "ajv": "5", @@ -6995,9 +7297,9 @@ } }, "prisma-db-introspection": { - "version": "0.0.46", - "resolved": "https://registry.npmjs.org/prisma-db-introspection/-/prisma-db-introspection-0.0.46.tgz", - "integrity": "sha512-/b7evS9b5xKS7UrcOERkCmV+IWC6OAgFtInFsvdwn4MeLEaVbkOfzRakdOo+t2gb+N7Yy8Eey9eKYgB5jiJAbA==", + "version": "0.0.50", + "resolved": "https://registry.npmjs.org/prisma-db-introspection/-/prisma-db-introspection-0.0.50.tgz", + "integrity": "sha512-coeJ3SRumezVnsQwc/kwNzxWFUBKHNwnD+keUJeZFFY3j7EDtRFHoL5DD7vTREvMEugoWOiovc+MYHFkDt0l+w==", "dev": true, "requires": { "ajv": "5", @@ -7637,6 +7939,14 @@ "statuses": "~1.4.0" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -7799,6 +8109,14 @@ "use": "^3.1.0" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", @@ -7815,6 +8133,11 @@ "is-extendable": "^0.1.0" } }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -8061,9 +8384,9 @@ "dev": true }, "string-similarity": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/string-similarity/-/string-similarity-1.2.1.tgz", - "integrity": "sha512-XqC6lRZF3UIYdxBRHjIfJwU2nYzPm+E8udKFHjIplnRQflkXP1A1Ie3HoEv04W/zY1wGnV6TQpklM2WwZO3gIA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/string-similarity/-/string-similarity-1.2.2.tgz", + "integrity": "sha512-IoHUjcw3Srl8nsPlW04U3qwWPk3oG2ffLM0tN853d/E/JlIvcmZmDY2Kz5HzKp4lEi2T7QD7Zuvjq/1rDw+XcQ==", "dev": true, "requires": { "lodash.every": "^4.6.0", @@ -8463,6 +8786,11 @@ "yn": "^2.0.0" } }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -8492,9 +8820,9 @@ "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" }, "unbzip2-stream": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.2.5.tgz", - "integrity": "sha512-izD3jxT8xkzwtXRUZjtmRwKnZoeECrfZ8ra/ketwOcusbZEp4mjULMnJOCfTDZBgGQAAY1AJ/IgxcwkavcX9Og==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.3.0.tgz", + "integrity": "sha512-kE2WkurNnPUMcryNioS68DDbhoPB8Qxsd8btHSj+sd5Pjh2GsjmeHLzMSqV9HHziAo8FzVxVCJl9ZYhk7yY1pA==", "dev": true, "requires": { "buffer": "^3.0.1", @@ -8532,8 +8860,28 @@ "integrity": "sha1-Il9rngM3Zj4Njnz9aG/Cg2zKznY=", "requires": { "debug": "^2.2.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } } }, + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + }, "union-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", diff --git a/api/package.json b/api/package.json index 784b66264..9e5311fb1 100644 --- a/api/package.json +++ b/api/package.json @@ -7,9 +7,13 @@ "dev": "npm-run-all --parallel debug playground" }, "dependencies": { + "@azure/storage-blob": "^10.0.0-preview", + "azure-storage": "^2.10.1", "cors": "^2.8.4", + "debug": "^4.0.1", "dotenv": "^6.0.0", "express-jwt": "^5.3.1", + "fast-xml-parser": "^3.12.5", "graphql-import": "^0.6.0", "graphql-tag": "^2.9.2", "graphql-tools": "3.1.1", @@ -23,6 +27,6 @@ "devDependencies": { "graphql-cli": "^2.16.5", "npm-run-all": "4.1.3", - "prisma": "^1.16.0" + "prisma": "^1.16.4" } } diff --git a/api/src/index.js b/api/src/index.js index d640b8682..792f8505e 100644 --- a/api/src/index.js +++ b/api/src/index.js @@ -7,11 +7,32 @@ const { } = require('prisma-binding') const { makeExecutableSchema } = require('graphql-tools') const { importSchema } = require('graphql-import') +const parser = require('fast-xml-parser') const checkJwt = require('./auth/middleware/jwt') const { getMember } = require('./auth/middleware/getMember') const { validateIdToken } = require('./auth/validateIdToken') const { directiveResolvers } = require('./auth/authDirectives') +const { + createStoragePipline, + createServiceURL, + listContainers, + createContainer, + createBlob, + uploadBlob, + downloadBlob, + deleteBlob, + deleteContainer +} = require('./storage/azure') +//var azure = require('azure-storage') +const Azure = require("@azure/storage-blob") +const azureAccount = process.env.AZURE_STORAGE_ACCOUNT +const azureKey = process.env.AZURE_STORAGE_ACCESS_KEY +const azureStorageUrl = process.env.AZURE_STORAGE_URL + +var debug = require('debug')('http') + , http = require('http') + , name = 'My App'; const TEAMS_FRAGMENT = require('./graphql/TeamsFragment') const TEAMS_CONNECTIONS_FRAGMENT = require('./graphql/TeamsConnectionsFragment') @@ -115,6 +136,57 @@ const resolvers = { return error.message }) }) + }, + async getAzureSAS(parent, { teamSlug }, ctx, info) { + // Create start and expiry times + let today = new Date() + let tomorrow = new Date() + tomorrow.setDate(today.getDate() + 7) + // Create SharedKeyCredential and attach to pipline + const SKC = new Azure.SharedKeyCredential(azureAccount, azureKey) + const pipeline = Azure.StorageURL.newPipeline(SKC) + // Create container URL + const serviceURL = new Azure.ServiceURL(azureStorageUrl, pipeline) + const containerName = teamSlug + const containerURL = Azure.ContainerURL.fromServiceURL(serviceURL, containerName) + + let marker + let containerCheck = null + do { + const listContainersResponse = await serviceURL.listContainersSegment( + Azure.Aborter.None, + marker, + ) + console.log(`ContainerCheck marker: `, listContainersResponse) + marker = listContainersResponse.marker; + for (const container of listContainersResponse.containerItems) { + console.log(`ContainerCheck: ${container.name} | ${containerName} | ${marker}`) + if(container.name === containerName){ + containerCheck = container.name + } + } + } while (marker) + let newContainer + if(containerCheck === null){ + newContainer = await containerURL.create(Azure.Aborter.None, { access: 'blob' }) + } + console.log('getAzureSAS container: ', containerURL, containerCheck, newContainer) + + // Set permissions for service, resource types and containers + const SASServicePerms = Azure.AccountSASServices.parse('b') + const SASResourceTypes = Azure.AccountSASResourceTypes.parse('co') + const SASContainerPerms = Azure.ContainerSASPermissions.parse('rwl') + // Generate SAS url + const SASQueryParameters = Azure.generateAccountSASQueryParameters( + { + containerName, + permissions: SASContainerPerms, + startTime: today, + expiryTime: tomorrow, + services: SASServicePerms, + resourceTypes: SASResourceTypes + }, SKC ) + return SASQueryParameters } } } @@ -148,7 +220,7 @@ server.express.use( server.options.endpoint, checkJwt, function (err, req, res, next) { - console.log('checkJwt: ', err, req) + // console.log('checkJwt: ', err, req) if (err) { return res.status(201).send(err.message) } else { diff --git a/api/src/schema.graphql b/api/src/schema.graphql index 1450c6000..731948163 100644 --- a/api/src/schema.graphql +++ b/api/src/schema.graphql @@ -21,6 +21,7 @@ type Mutation { createTeam(name: String!, slug: String!, owner: String!): Team @isAuthenticated updateTeamProfile(slug: String!, owner: String!, description: String, motto: String, avatar: String): Team @isAuthenticated deleteTeam(slug: String!): Team @isAuthenticated @isOwner(type: "Team") + getAzureSAS(teamSlug: String!):String } input MemberProfileInput { diff --git a/api/src/storage/azure.js b/api/src/storage/azure.js new file mode 100644 index 000000000..324181b04 --- /dev/null +++ b/api/src/storage/azure.js @@ -0,0 +1,133 @@ +const Azure = require('@azure/storage-blob') + +/* Create Azure Storage pipeline +* @accountName: Azure Blob Storage account name (required) - Environment variable +* @accountKey: Azure Blob Storage storage key (required) - Environment variable +*/ +const createStoragePipline = async (accountNAme, accountKey) => { + return Azure.StorageURL.newPipeline( + new Azure.SharedKeyCredential(accountName, accountKey) + ) +} + +/* Create Azure Storage Service URL +* @storageURL: URL for Azure Storage endpoint (required) - Environment variable +* @pipeline: Azure Storage Pipline (required) - created using createStoragePipeline() +*/ +const createServiceURL = async (storageURL, pipeline) => { + return new Azure.ServiceURL( + storageURL, + pipeline + ) +} + +/* List containers +* @pipeline: Azure Storage Pipline (required) - created using createServiceURL +* @service_url: URL for Azure Storage endpoint (required) - Environment variable +*/ +const listContainers = async (serviceURL) => { + let marker; + do { + const listContainersResponse = await serviceURL.listContainersSegment( + Azure.Aborter.None, + marker, + ); + + marker = listContainersResponse.marker; + for (const container of listContainersResponse.containerItems) { + console.log(`Container: ${container.name}`); + } + } while (marker); +} + + +/* Create containers +* @serviceURL: URL for Azure Storage endpoint (required) - created using createServiceURL +* @containerName: Container Name +* Returns: containerURL +*/ +const createContainer = async (serviceURL, containerName) => { + const containerURL = ContainerURL.fromServiceURL(serviceURL, containerName) + + const createContainerResponse = await containerURL.create(Aborter.None) + + console.log( + `Created container ${containerName} successfully`, + await createContainerResponse.requestId + ) + return containerURL +} + +/* Create containers +* @containerURL: continer URL (required) - created using createContainer +* @blobName: Blob Name +* Returns: blobURL +*/ +const createBlob = async (containerURL, blobName) => { + const blobURL = Azure.BlobURL.fromContainerURL(containerURL, blobName) + console.log(`created blob successfully`, await blobURL) + return blobURL +} + +/* Upload Blob +* @blobURL: blobURL (required) - created using createBlob +* @blobContent: Blob Content +* Returns: uploadBlobResponse +*/ +const uploadBlob = async (blobURL, blobContent) => { + const blockBlobURL = Azure.BlockBlobURL.fromBlobURL(blobURL) + const uploadBlobResponse = await blockBlobURL.upload( + Azure.Aborter.None, + blobContent, + blobContent.length + ); + console.log(`Upload block blob ${blobName} successfully`, await uploadBlobResponse) + return uploadBlobResponse +} + +/* Download Blob +* @blobURL: blobURL (required) - created using createBlob +* @blobContent: Blob Content +* Returns: downloadBlockBlobResponse +*/ +const downloadBlob = async (blobURL, blobContent) => { + const downloadBlockBlobResponse = await blobURL.download(Azure.Aborter.None, 0) + console.log( + "Downloaded blob content", + downloadBlockBlobResponse.readableStreamBody.read(content.length).toString() + ) + console.log(`BlockBlobResponse: ${downloadBlockBlobResponse}`) + return downloadBlockBlobResponse +} + +/* Delete Blob +* @blobURL: blobURL (required) - created using createBlob +* Returns: bool +*/ +const deleteBlob = async (blobrURL) => { + await blobrURL.delete(Azure.Aborter.None); + console.log("deleted blob", blobrURL); + return true +} + +/* Delete Container +* @containerURL: continer URL (required) - created using createContainer +* Returns: bool +*/ +const deleteContainer = async (containerURL) => { + await containerURL.delete(Azure.Aborter.None); + console.log("deleted container", containerURL); + return true +} + +module.exports = { + createStoragePipline, + createServiceURL, + listContainers, + createContainer, + createBlob, + uploadBlob, + downloadBlob, + deleteBlob, + deleteContainer +} diff --git a/client/package-lock.json b/client/package-lock.json index 219ddf2b8..52252110b 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -4,6 +4,14 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@azure/storage-blob": { + "version": "10.0.0-preview", + "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-10.0.0-preview.tgz", + "integrity": "sha512-PFXKm+gipKDfg5h08hPiEr0Za4RMiFzzte+ewh4ACZeRQa0XZK55NAZxW67ZytGBd7WXdQq6pcAgo5dBnwQANQ==", + "requires": { + "ms-rest-js": "0.21.413" + } + }, "@babel/code-frame": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", @@ -2204,6 +2212,56 @@ "integrity": "sha512-Benr3i5odUkvpFkOpzGqrltGdbSs+EVCkEBGXbuR7uT0VzhXKIkhem6PDzHdx5EonA+rfbB3QvP6aDOw5+zp5Q==", "optional": true }, + "@types/body-parser": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.0.tgz", + "integrity": "sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w==", + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.32", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz", + "integrity": "sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==", + "requires": { + "@types/node": "*" + } + }, + "@types/events": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", + "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==" + }, + "@types/express": { + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.16.0.tgz", + "integrity": "sha512-TtPEYumsmSTtTetAPXlJVf3kEqb6wZK0bZojpJQrnD/djV4q1oB6QQ8aKvKqwNPACoe02GNiy5zDzcYivR5Z2w==", + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.0.tgz", + "integrity": "sha512-lTeoCu5NxJU4OD9moCgm0ESZzweAx0YqsAcab6OB0EB3+As1OaHtKnaGJvcngQxYsi9UNv0abn4/DRavrRxt4w==", + "requires": { + "@types/events": "*", + "@types/node": "*", + "@types/range-parser": "*" + } + }, + "@types/form-data": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz", + "integrity": "sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==", + "requires": { + "@types/node": "*" + } + }, "@types/graphql": { "version": "0.12.6", "resolved": "https://registry.npmjs.org/@types/graphql/-/graphql-0.12.6.tgz", @@ -2218,11 +2276,15 @@ "indefinite-observable": "^1.0.1" } }, + "@types/mime": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.0.tgz", + "integrity": "sha512-A2TAGbTFdBw9azHbpVd+/FkdW2T6msN1uct1O9bH3vTerEHKZhTXJUQXy+hNq1B0RagfU8U+KBdqiZpxjhOUQA==" + }, "@types/node": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-10.3.0.tgz", - "integrity": "sha512-hWzNviaVFIr1TqcRA8ou49JaSHp+Rfabmnqg2kNvusKqLhPU0rIsGPUj5WJJ7ld4Bb7qdgLmIhLfCD1qS08IVA==", - "dev": true + "integrity": "sha512-hWzNviaVFIr1TqcRA8ou49JaSHp+Rfabmnqg2kNvusKqLhPU0rIsGPUj5WJJ7ld4Bb7qdgLmIhLfCD1qS08IVA==" }, "@types/prop-types": { "version": "15.5.5", @@ -2232,6 +2294,11 @@ "@types/react": "*" } }, + "@types/range-parser": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.2.tgz", + "integrity": "sha512-HtKGu+qG1NPvYe1z7ezLsyIaXYyi8SoAVqWDZgDQ8dLrsZvSzUNCwZyfX33uhWxL/SU0ZDQZ3nwZ0nimt507Kw==" + }, "@types/react": { "version": "16.4.14", "resolved": "https://registry.npmjs.org/@types/react/-/react-16.4.14.tgz", @@ -2249,6 +2316,15 @@ "@types/react": "*" } }, + "@types/serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-/BZ4QRLpH/bNYgZgwhKEh+5AsboDBcUdlBYgzoLX0fpj3Y2gp6EApyOlM3bK53wQS/OE1SrdSYBAbux2D1528Q==", + "requires": { + "@types/express-serve-static-core": "*", + "@types/mime": "*" + } + }, "@types/tapable": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-0.2.5.tgz", @@ -2272,6 +2348,14 @@ } } }, + "@types/uuid": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.4.tgz", + "integrity": "sha512-tPIgT0GUmdJQNSHxp0X2jnpQfBSTfGxUMc/2CXBU2mnyTFVYVa2ojpoQ74w0U2yn2vw3jnC640+77lkFFpdVDw==", + "requires": { + "@types/node": "*" + } + }, "@types/webpack": { "version": "3.8.12", "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-3.8.12.tgz", @@ -3230,6 +3314,15 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" }, + "axios": { + "version": "0.18.0", + "resolved": "http://registry.npmjs.org/axios/-/axios-0.18.0.tgz", + "integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=", + "requires": { + "follow-redirects": "^1.3.0", + "is-buffer": "^1.1.5" + } + }, "axobject-query": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.1.tgz", @@ -4645,6 +4738,11 @@ "safe-buffer": "^5.1.1" } }, + "blob-util": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", + "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==" + }, "block-stream": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", @@ -7670,6 +7768,14 @@ "readable-stream": "^2.0.4" } }, + "follow-redirects": { + "version": "1.5.8", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.8.tgz", + "integrity": "sha512-sy1mXPmv7kLAMKW/8XofG7o9T+6gAjzdZK4AJF6ryqQYUa/hnzgiypoeUecZ53x7XiqKNEpNqLtS97MshW2nxg==", + "requires": { + "debug": "=3.1.0" + } + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -9408,8 +9514,7 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, "is-builtin-module": { "version": "1.0.0", @@ -11565,6 +11670,53 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, + "ms-rest-js": { + "version": "0.21.413", + "resolved": "https://registry.npmjs.org/ms-rest-js/-/ms-rest-js-0.21.413.tgz", + "integrity": "sha512-eX+M98Y9s+zo85Unt83TYkpuJkW4d0wuiHrpWjAxx/7YzZd3uVq8HRhJHXGDqCd22nlEvtwlchfgwILmBcWnOg==", + "requires": { + "@types/express": "^4.11.1", + "@types/form-data": "^2.2.1", + "@types/node": "^9.4.6", + "@types/uuid": "^3.4.3", + "axios": "^0.18.0", + "form-data": "^2.3.2", + "tough-cookie": "^2.4.3", + "tslib": "^1.9.2", + "uuid": "^3.2.1", + "xml2js": "^0.4.19" + }, + "dependencies": { + "@types/node": { + "version": "9.6.31", + "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.31.tgz", + "integrity": "sha512-kIVlvUBizL51ALNMPbmcZoM7quHyB7J6fLRwQe22JsMp39nrVSHdBeVVS3fnQCK1orxI3O8LScmb8cuiihkAfA==" + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + } + }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + } + } + }, "multimatch": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-2.1.0.tgz", @@ -13579,6 +13731,11 @@ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", "dev": true }, + "psl": { + "version": "1.1.29", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz", + "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==" + }, "public-encrypt": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.2.tgz", @@ -14565,8 +14722,7 @@ "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "schedule": { "version": "0.3.0", @@ -15735,8 +15891,7 @@ "tslib": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", - "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", - "dev": true + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" }, "tty-browserify": { "version": "0.0.0", diff --git a/client/package.json b/client/package.json index 3fb9b227e..f40fe138f 100644 --- a/client/package.json +++ b/client/package.json @@ -67,6 +67,7 @@ "workbox-cli": "^3.4.1" }, "dependencies": { + "@azure/storage-blob": "^10.0.0-preview", "@material-ui/core": "^3.0.3", "@material-ui/icons": "^3.0.1", "apollo-cache-inmemory": "^1.2.9", @@ -80,6 +81,7 @@ "auth0-lock": "^11.9.0", "azure-storage": "^2.10.1", "babel-polyfill": "^6.26.0", + "blob-util": "^2.0.2", "classnames": "^2.2.6", "cross-fetch": "^2.2.2", "dotenv-webpack": "^1.5.7", diff --git a/client/src/graphql/teams/GetAzureSASMutation.js b/client/src/graphql/teams/GetAzureSASMutation.js new file mode 100644 index 000000000..abb9ffef3 --- /dev/null +++ b/client/src/graphql/teams/GetAzureSASMutation.js @@ -0,0 +1,10 @@ +/* eslint-disable no-unused-vars */ +import gql from 'graphql-tag' + +export const GET_AZURE_SAS = gql` + mutation getAzureSAS($teamSlug: String!) { + getAzureSAS(teamSlug: $teamSlug) + } +` + +export default GET_AZURE_SAS diff --git a/client/src/views/common/UploadImage.js b/client/src/views/common/UploadImage.js index f5b625c8f..e9662db08 100644 --- a/client/src/views/common/UploadImage.js +++ b/client/src/views/common/UploadImage.js @@ -1,9 +1,10 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' +import { Mutation } from 'react-apollo' import Dropzone from 'react-dropzone' -// import ReactCrop from 'react-image-crop' import { Cropper } from 'react-image-cropper' -// import azure from 'azure-storage' +import * as Azure from '@azure/storage-blob' +import { imgSrcToBlob } from 'blob-util' import { withStyles } from '@material-ui/core/styles' import Dialog from '@material-ui/core/Dialog' @@ -11,6 +12,8 @@ import DialogActions from '@material-ui/core/DialogActions' import DialogContent from '@material-ui/core/DialogContent' import Button from '@material-ui/core/Button' +import GET_AZURE_SAS from '../../graphql/teams/GetAzureSASMutation' + const styles = theme => ({ dropzoneContainer: { display: 'block', @@ -54,6 +57,8 @@ class UploadImage extends Component { this.getCroppedImg = this.getCroppedImg.bind(this) this.handleClickOpen = this.handleClickOpen.bind(this) this.handleClose = this.handleClose.bind(this) + this.handleSubmit = this.handleSubmit.bind(this) + this.saveAvatar = this.saveAvatar.bind(this) this.state = { open: false, @@ -86,7 +91,21 @@ class UploadImage extends Component { )} {this.state.cropped !== null && ( - Crop Preview + + {(getAzureSAS, { loading, error, data }) => { + console.log('getAzureSAS: ', error, data) + console.log('getAzureSAS keys: ', process.env.AZURE_STORAGE_URL, process.env.AZURE_SAS) + if (data !== undefined && data.getAzureSAS !== null) { + this.saveAvatar(data.getAzureSAS, team.slug) + } + return ( +
+ Crop Preview + +
+ ) + }} +
)} {this.state.src !== null && ( { - if (error) return (

{error}

) - return 'Success' - } - ) - xhr.send() + await uploadBrowserDataToBlockBlob(Aborter.None, file, blockBlobURL, { + blockSize: 4 * 1024 * 1024, // 4MB block size + parallelism: 20, // 20 concurrency + progress: ev => console.log(ev) + }); } */ } From ecc538648e1ebd33fe94fd3d768b6eb740acf659 Mon Sep 17 00:00:00 2001 From: Barry Low Date: Thu, 20 Sep 2018 17:51:41 -0700 Subject: [PATCH 03/12] Save uploaded avatar to team --- api/database/datamodel.graphql | 1 + api/src/generated/prisma.graphql | 77 +++++- api/src/graphql/TeamsFragment.js | 1 + api/src/index.js | 13 +- api/src/schema.graphql | 7 +- client/package-lock.json | 5 + client/package.json | 1 + client/src/graphql/teams/GetAllTeamsQuery.js | 1 + .../src/graphql/teams/GetTeamsByOwnerQuery.js | 1 + client/src/graphql/teams/Teams.js | 1 + .../teams/UpdateTeamProfileMutation.js | 19 +- client/src/views/common/UploadImage.js | 232 ++++++++---------- .../dashboard/components/ManageTeamEdit.js | 19 +- .../dashboard/components/ManageTeamsItem.js | 61 +++-- 14 files changed, 255 insertions(+), 184 deletions(-) diff --git a/api/database/datamodel.graphql b/api/database/datamodel.graphql index ddc89814c..b5627825e 100644 --- a/api/database/datamodel.graphql +++ b/api/database/datamodel.graphql @@ -41,6 +41,7 @@ type TeamProfile { description: String motto: String avatar: String + banner: String updatedAt: DateTime! createdAt: DateTime! } diff --git a/api/src/generated/prisma.graphql b/api/src/generated/prisma.graphql index 6d118e29a..99d9f7562 100644 --- a/api/src/generated/prisma.graphql +++ b/api/src/generated/prisma.graphql @@ -1,5 +1,5 @@ # source: http://localhost:4466 -# timestamp: Wed Sep 12 2018 08:41:57 GMT-0700 (Pacific Daylight Time) +# timestamp: Thu Sep 20 2018 16:52:44 GMT-0700 (Pacific Daylight Time) type AggregateFBStatus { count: Int! @@ -1400,7 +1400,9 @@ type TeamProfile { description: String motto: String avatar: String + banner: String updatedAt: DateTime! + createdAt: DateTime! } """A connection to a list of items.""" @@ -1417,6 +1419,7 @@ input TeamProfileCreateInput { description: String motto: String avatar: String + banner: String } input TeamProfileCreateOneInput { @@ -1439,19 +1442,23 @@ enum TeamProfileOrderByInput { motto_DESC avatar_ASC avatar_DESC + banner_ASC + banner_DESC updatedAt_ASC updatedAt_DESC - id_ASC - id_DESC createdAt_ASC createdAt_DESC + id_ASC + id_DESC } type TeamProfilePreviousValues { description: String motto: String avatar: String + banner: String updatedAt: DateTime! + createdAt: DateTime! } type TeamProfileSubscriptionPayload { @@ -1497,12 +1504,14 @@ input TeamProfileUpdateDataInput { description: String motto: String avatar: String + banner: String } input TeamProfileUpdateInput { description: String motto: String avatar: String + banner: String } input TeamProfileUpdateOneInput { @@ -1647,6 +1656,46 @@ input TeamProfileWhereInput { """All values not ending with the given string.""" avatar_not_ends_with: String + banner: String + + """All values that are not equal to given value.""" + banner_not: String + + """All values that are contained in given list.""" + banner_in: [String!] + + """All values that are not contained in given list.""" + banner_not_in: [String!] + + """All values less than the given value.""" + banner_lt: String + + """All values less than or equal the given value.""" + banner_lte: String + + """All values greater than the given value.""" + banner_gt: String + + """All values greater than or equal the given value.""" + banner_gte: String + + """All values containing the given string.""" + banner_contains: String + + """All values not containing the given string.""" + banner_not_contains: String + + """All values starting with the given string.""" + banner_starts_with: String + + """All values not starting with the given string.""" + banner_not_starts_with: String + + """All values ending with the given string.""" + banner_ends_with: String + + """All values not ending with the given string.""" + banner_not_ends_with: String updatedAt: DateTime """All values that are not equal to given value.""" @@ -1669,6 +1718,28 @@ input TeamProfileWhereInput { """All values greater than or equal the given value.""" updatedAt_gte: DateTime + createdAt: DateTime + + """All values that are not equal to given value.""" + createdAt_not: DateTime + + """All values that are contained in given list.""" + createdAt_in: [DateTime!] + + """All values that are not contained in given list.""" + createdAt_not_in: [DateTime!] + + """All values less than the given value.""" + createdAt_lt: DateTime + + """All values less than or equal the given value.""" + createdAt_lte: DateTime + + """All values greater than the given value.""" + createdAt_gt: DateTime + + """All values greater than or equal the given value.""" + createdAt_gte: DateTime } type TeamStatus { diff --git a/api/src/graphql/TeamsFragment.js b/api/src/graphql/TeamsFragment.js index e75ce8382..f243480f2 100644 --- a/api/src/graphql/TeamsFragment.js +++ b/api/src/graphql/TeamsFragment.js @@ -13,6 +13,7 @@ const TEAMS_FRAGMENT = gql`{ createdAt profile { avatar + banner description motto updatedAt diff --git a/api/src/index.js b/api/src/index.js index 792f8505e..04eab61d7 100644 --- a/api/src/index.js +++ b/api/src/index.js @@ -121,8 +121,8 @@ const resolvers = { return err }) }, - async updateTeamProfile(parent, { slug, owner, description, motto, avatar }, ctx, info) { - return ctx.db.mutation.updateTeam({data:{profile: {update: {description: description, motto: motto, avatar: avatar}}}, where:{slug: slug}}, TEAMS_FRAGMENT) + async updateTeamProfile(parent, { slug, owner, description, motto, avatar, banner }, ctx, info) { + return ctx.db.mutation.updateTeam({data:{profile: {update: {description: description, motto: motto, avatar: avatar, banner: banner}}}, where:{slug: slug}}, TEAMS_FRAGMENT) .catch((err) => { console.log('createTeam error: ', err) return err @@ -140,8 +140,8 @@ const resolvers = { async getAzureSAS(parent, { teamSlug }, ctx, info) { // Create start and expiry times let today = new Date() - let tomorrow = new Date() - tomorrow.setDate(today.getDate() + 7) + let week = new Date() + week.setDate(today.getDate() + 7) // Create SharedKeyCredential and attach to pipline const SKC = new Azure.SharedKeyCredential(azureAccount, azureKey) const pipeline = Azure.StorageURL.newPipeline(SKC) @@ -179,10 +179,11 @@ const resolvers = { // Generate SAS url const SASQueryParameters = Azure.generateAccountSASQueryParameters( { - containerName, + version: '2017-11-09', permissions: SASContainerPerms, startTime: today, - expiryTime: tomorrow, + expiryTime: week, + protocol: 'https', services: SASServicePerms, resourceTypes: SASResourceTypes }, SKC ) diff --git a/api/src/schema.graphql b/api/src/schema.graphql index 731948163..f27657ccb 100644 --- a/api/src/schema.graphql +++ b/api/src/schema.graphql @@ -19,16 +19,11 @@ type Query { type Mutation { authenticate(idToken: String!): Member createTeam(name: String!, slug: String!, owner: String!): Team @isAuthenticated - updateTeamProfile(slug: String!, owner: String!, description: String, motto: String, avatar: String): Team @isAuthenticated + updateTeamProfile(slug: String!, owner: String!, description: String, motto: String, avatar: String, banner: String): Team @isAuthenticated deleteTeam(slug: String!): Team @isAuthenticated @isOwner(type: "Team") getAzureSAS(teamSlug: String!):String } -input MemberProfileInput { - authId: String! - alias: String! -} - fragment MemberInfo on Member { id authId diff --git a/client/package-lock.json b/client/package-lock.json index 52252110b..c0fbda736 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -4847,6 +4847,11 @@ "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", "dev": true }, + "browser-image-resizer": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browser-image-resizer/-/browser-image-resizer-1.2.0.tgz", + "integrity": "sha512-xMiB6BnRsD6RWmkD6Av90qXY7uaAOXJPaS5D8cSazyao/bx6ENCm67XGl6BIhn1ETjK52yEs8A/atmMPp74M4A==" + }, "browserify-aes": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", diff --git a/client/package.json b/client/package.json index f40fe138f..34a09ec19 100644 --- a/client/package.json +++ b/client/package.json @@ -82,6 +82,7 @@ "azure-storage": "^2.10.1", "babel-polyfill": "^6.26.0", "blob-util": "^2.0.2", + "browser-image-resizer": "^1.2.0", "classnames": "^2.2.6", "cross-fetch": "^2.2.2", "dotenv-webpack": "^1.5.7", diff --git a/client/src/graphql/teams/GetAllTeamsQuery.js b/client/src/graphql/teams/GetAllTeamsQuery.js index a2aab251a..0bd9ed522 100644 --- a/client/src/graphql/teams/GetAllTeamsQuery.js +++ b/client/src/graphql/teams/GetAllTeamsQuery.js @@ -18,6 +18,7 @@ export const GET_ALL_TEAMS_QUERY = gql` createdAt profile { avatar + banner description motto updatedAt diff --git a/client/src/graphql/teams/GetTeamsByOwnerQuery.js b/client/src/graphql/teams/GetTeamsByOwnerQuery.js index d2f60c882..d15834b34 100644 --- a/client/src/graphql/teams/GetTeamsByOwnerQuery.js +++ b/client/src/graphql/teams/GetTeamsByOwnerQuery.js @@ -16,6 +16,7 @@ export const GET_TEAMS_BY_OWNER = gql` createdAt profile { avatar + banner description motto updatedAt diff --git a/client/src/graphql/teams/Teams.js b/client/src/graphql/teams/Teams.js index 76c60bbb8..ce0a6f36b 100644 --- a/client/src/graphql/teams/Teams.js +++ b/client/src/graphql/teams/Teams.js @@ -15,6 +15,7 @@ export const TEAMS_FRAGMENT = gql` createdAt profile { avatar + banner description motto updatedAt diff --git a/client/src/graphql/teams/UpdateTeamProfileMutation.js b/client/src/graphql/teams/UpdateTeamProfileMutation.js index 4a63adffe..cd5e26256 100644 --- a/client/src/graphql/teams/UpdateTeamProfileMutation.js +++ b/client/src/graphql/teams/UpdateTeamProfileMutation.js @@ -3,16 +3,20 @@ import gql from 'graphql-tag' const UPDATE_TEAM_PROFILE = gql` mutation UpdateTeamProfileMutation( - $slug: String! - $owner: String! - $description: String - $motto: String + $slug: String!, + $owner: String!, + $description: String, + $motto: String, + $avatar: String, + $banner: String ) { updateTeamProfile( - slug: $slug - owner: $owner - description: $description + slug: $slug, + owner: $owner, + description: $description, motto: $motto + avatar: $avatar, + banner: $banner ) { id name @@ -26,6 +30,7 @@ const UPDATE_TEAM_PROFILE = gql` createdAt profile { avatar + banner description motto updatedAt diff --git a/client/src/views/common/UploadImage.js b/client/src/views/common/UploadImage.js index e9662db08..94a9c9643 100644 --- a/client/src/views/common/UploadImage.js +++ b/client/src/views/common/UploadImage.js @@ -4,6 +4,7 @@ import { Mutation } from 'react-apollo' import Dropzone from 'react-dropzone' import { Cropper } from 'react-image-cropper' import * as Azure from '@azure/storage-blob' +import readAndCompressImage from 'browser-image-resizer' import { imgSrcToBlob } from 'blob-util' import { withStyles } from '@material-ui/core/styles' @@ -12,6 +13,8 @@ import DialogActions from '@material-ui/core/DialogActions' import DialogContent from '@material-ui/core/DialogContent' import Button from '@material-ui/core/Button' +import MessageCard from './MessageCard' + import GET_AZURE_SAS from '../../graphql/teams/GetAzureSASMutation' const styles = theme => ({ @@ -45,16 +48,21 @@ const styles = theme => ({ } }) +const imageConfig = { + quality: 0.5, + maxWidth: 200, + maxHeight: 200, + autoRotate: true, + debug: true, + mimeType: 'image/jpeg' +} + class UploadImage extends Component { constructor (props) { super(props) - // this.saveToStorage = this.saveToStorage.bind(this) this.onDrop = this.onDrop.bind(this) this.handleCrop = this.handleCrop.bind(this) - this.onCropComplete = this.onCropComplete.bind(this) - this.onCropChange = this.onCropChange.bind(this) - this.getCroppedImg = this.getCroppedImg.bind(this) this.handleClickOpen = this.handleClickOpen.bind(this) this.handleClose = this.handleClose.bind(this) this.handleSubmit = this.handleSubmit.bind(this) @@ -70,17 +78,27 @@ class UploadImage extends Component { width: 200, height: 200 }, + cropping: false, cropped: null, - rejected: null + rejected: null, + uploaded: false, + image: '' } } render () { - const { classes, team, authId } = this.props - console.log('UploadImage', team, authId, this.state) + const { classes, team, authId, handleAvatar } = this.props + let avatar = null + if (team.profile !== null && team.profile.avatar !== undefined && team.profile.avatar !== null && team.profile.avatar !== 'a') { + avatar = team.profile.avatar + } else { + if (this.state.image !== '') avatar = this.state.image + } + + console.log('UploadImage', team, avatar, authId, this.state) return (
- {this.state.src === null && ( + {avatar === null && this.state.src === null && ( Drag-and-drop team avater here or click to select files.
Only *.jpeg and *.png images

)} - {this.state.cropped !== null && ( + {(avatar !== null || this.state.src !== null) && ( {(getAzureSAS, { loading, error, data }) => { console.log('getAzureSAS: ', error, data) - console.log('getAzureSAS keys: ', process.env.AZURE_STORAGE_URL, process.env.AZURE_SAS) - if (data !== undefined && data.getAzureSAS !== null) { - this.saveAvatar(data.getAzureSAS, team.slug) + if (!this.state.uploaded) { + if (data !== undefined && data.getAzureSAS !== null) { + this.saveAvatar(data.getAzureSAS, team.slug, handleAvatar) + } + // this.handleSubmit(getAzureSAS, team.slug) + } + if (this.state.cropping) { + return ( +
+ +
+ ) + } else { + return ( + +
+ Crop Preview +
+ +
+ +
+ { this.cropper = ref }} + onImgLoad={this.onImageLoaded} + /> +
+
+ + + + +
+
+
+ ) } - return ( -
- Crop Preview - -
- ) }}
)} - {this.state.src !== null && ( - -
- -
- { this.cropper = ref }} - onImgLoad={this.onImageLoaded} - /> -
-
- - - - -
-
- )}
) } @@ -167,29 +194,30 @@ class UploadImage extends Component { } } - handleCrop () { - console.log('handleCrop', this.cropper) - this.setState({ cropped: this.cropper.crop(), open: false }) + async handleCrop (e, getAzureSAS, teamSlug, handleAvatar) { + e.preventDefault() + const data = await getAzureSAS({ variables: { teamSlug } }) + console.log('handleCrop', this.cropper, data) + const cropped = this.cropper.crop() + const croppedBlob = await imgSrcToBlob(cropped, 'image/png', 'Anonymous') + let resizedBlob = await readAndCompressImage(croppedBlob, imageConfig) + + return this.setState({ cropped: resizedBlob, cropping: true, open: false }, () => { + this.saveAvatar(data.getAzureSAS, teamSlug, handleAvatar) + }) } async handleSubmit (e, getAzureSAS, teamSlug) { e.preventDefault() - await getAzureSAS({ variables: { teamSlug } }) } - async saveAvatar (SASQueryParameters, teamSlug) { - const blob = await imgSrcToBlob(this.state.cropped, 'image/png', 'Anonymous') - console.log('handleSubmit0:', await blob, await blob.type) - const type = blob.type - let ext - if (type === 'image/jpeg') ext = 'jpg' - if (type === 'image/png') ext = 'png' - const fileName = `${teamSlug}-avatar.${ext}` + async saveAvatar (SASQueryParameters, teamSlug, handleAvatar) { + const fileName = `${teamSlug}-avatar.jpg` console.log('saveAvatar SASurl', process.env.AZURE_SAS_URL) - // const SASurl = `${process.env.AZURE_STORAGE_URL}?${SASQueryParameters}` + const SASurl2 = `${process.env.AZURE_STORAGE_URL}?${SASQueryParameters}` const SASurl = process.env.AZURE_SAS_URL - + console.log('getAzureSAS2 SASurl: ', SASurl2, SASurl) const pipeline = Azure.StorageURL.newPipeline(new Azure.AnonymousCredential()) const serviceURL = new Azure.ServiceURL(SASurl, pipeline) console.log('getAzureSAS2: ', serviceURL) @@ -198,54 +226,15 @@ class UploadImage extends Component { console.log('getAzureSAS3: ', containerURL) const blockBlobURL = Azure.BlockBlobURL.fromContainerURL(containerURL, fileName) - console.log(Azure.Aborter.None, await blob, blockBlobURL) - const avatar = await Azure.uploadBrowserDataToBlockBlob(Azure.Aborter.None, blob, blockBlobURL) + console.log('getAzureSAS34: ', blockBlobURL) + const avatar = await Azure.uploadBrowserDataToBlockBlob(Azure.Aborter.None, this.state.cropped, blockBlobURL) console.log('saveAvatar: ', avatar) + const image = `${process.env.AZURE_STORAGE_URL}${teamSlug}/${fileName}` + handleAvatar(image) + this.setState({ cropping: false, uploaded: true, image: image }) return avatar } - onCropComplete (crop, pixelCrop) { - console.log('onCropComplete', crop, pixelCrop, this.state) - const cropped = this.getCroppedImg(crop, pixelCrop, this.state.src) - this.setState({ cropped }) - } - - onCropChange (crop) { - this.setState({ crop }) - } - - getCroppedImg (image, pixelCrop, fileName) { - const canvas = document.createElement('canvas') - canvas.width = pixelCrop.width - canvas.height = pixelCrop.height - const ctx = canvas.getContext('2d') - - ctx.drawImage( - image, - pixelCrop.x, - pixelCrop.y, - pixelCrop.width, - pixelCrop.height, - 0, - 0, - pixelCrop.width, - pixelCrop.height - ) - - // As Base64 string - return canvas.toDataURL('image/jpeg') - - // As a blob - /* - return new Promise((resolve, reject) => { - canvas.toBlob(file => { - file.name = fileName - resolve(file) - }, 'image/jpeg') - }); - */ - } - handleClickOpen () { this.setState({ open: true }) } @@ -253,36 +242,13 @@ class UploadImage extends Component { handleClose () { this.setState({ open: false, src: null }) } - /* - async saveToStorage (file) { - const blobService = Azure.createBlobService() - const container = '' - - await uploadBrowserDataToBlockBlob(Aborter.None, file, blockBlobURL, { - blockSize: 4 * 1024 * 1024, // 4MB block size - parallelism: 20, // 20 concurrency - progress: ev => console.log(ev) - }); - } - */ } UploadImage.propTypes = { classes: PropTypes.object.isRequired, team: PropTypes.object, - authId: PropTypes.string.isRequired + authId: PropTypes.string.isRequired, + handleAvatar: PropTypes.func.isRequired } export default withStyles(styles)(UploadImage) - -/* - -*/ diff --git a/client/src/views/dashboard/components/ManageTeamEdit.js b/client/src/views/dashboard/components/ManageTeamEdit.js index 9630c04be..5a257cb4a 100644 --- a/client/src/views/dashboard/components/ManageTeamEdit.js +++ b/client/src/views/dashboard/components/ManageTeamEdit.js @@ -41,13 +41,18 @@ export class ManageTeamEdit extends Component { this.handleClose = this.handleClose.bind(this) this.handleChange = this.handleChange.bind(this) this.handleSubmit = this.handleSubmit.bind(this) + this.handleAvatar = this.handleAvatar.bind(this) console.log('ManageTeamEdit', props.team) const motto = props.team.profile.motto || '' const description = props.team.profile.description || '' + const avatar = props.team.profile.avatar || '' + const banner = props.team.profile.banner || '' this.state = { open: false, motto: motto, - description: description + description: description, + avatar: avatar, + banner: banner } } @@ -103,7 +108,7 @@ export class ManageTeamEdit extends Component { Edit Team Details - + ( - - -
- - - - {team.name} - - - {team.createdAt} | Members: {team.members.length} - - - - - - - -
-
-
-) +export const ManageTeamsItem = ({ classes, team, authId }) => { + console.log('ManageTeamsItem', team, team.profile, team.profile.avatar) + let avatar + if (team.profile.avatar !== undefined && team.profile.avatar !== 'a') { + avatar = team.profile.avatar + } else { + avatar = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22288%22%20height%3D%22225%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20288%20225%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_164edaf95ee%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A14pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_164edaf95ee%22%3E%3Crect%20width%3D%22288%22%20height%3D%22225%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%2296.32500076293945%22%20y%3D%22118.8%22%3EThumbnail%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E' + } + return ( + + +
+ + + + {team.name} + + + {team.createdAt} | Members: {team.members.length} + + + + + + + +
+
+
+ ) +} ManageTeamsItem.propTypes = { classes: PropTypes.object.isRequired, From 177f804c42de91e6991414529defbdb8477a7d27 Mon Sep 17 00:00:00 2001 From: Barry Low Date: Fri, 21 Sep 2018 00:45:45 -0700 Subject: [PATCH 04/12] Add banner image insert and update --- client/src/views/common/UploadImage.js | 283 ++++++++++++------ .../dashboard/components/ManageTeamDetails.js | 142 ++++++--- .../dashboard/components/ManageTeamEdit.js | 15 +- 3 files changed, 298 insertions(+), 142 deletions(-) diff --git a/client/src/views/common/UploadImage.js b/client/src/views/common/UploadImage.js index 94a9c9643..9aff6ce47 100644 --- a/client/src/views/common/UploadImage.js +++ b/client/src/views/common/UploadImage.js @@ -12,6 +12,7 @@ import Dialog from '@material-ui/core/Dialog' import DialogActions from '@material-ui/core/DialogActions' import DialogContent from '@material-ui/core/DialogContent' import Button from '@material-ui/core/Button' +import Typography from '@material-ui/core/Typography' import MessageCard from './MessageCard' @@ -36,20 +37,28 @@ const styles = theme => ({ dropzoneText: { textAlign: 'center' }, - cropPreview: { - width: 200, - height: 200 + cropAvatarPreview: { + width: 100, + height: 100 }, - cropBox: { + cropBannerPreview: { + width: 400, + height: 100 + }, + cropAvatarBox: { width: 350, height: 350 }, + cropBannerBox: { + width: 650, + height: 350 + }, reactCrop: { } }) -const imageConfig = { - quality: 0.5, +const imageAvatarConfig = { + quality: 0.6, maxWidth: 200, maxHeight: 200, autoRotate: true, @@ -57,6 +66,15 @@ const imageConfig = { mimeType: 'image/jpeg' } +const imageBannerConfig = { + quality: 0.6, + maxWidth: 800, + maxHeight: 200, + autoRotate: true, + debug: true, + mimeType: 'image/jpeg' +} + class UploadImage extends Component { constructor (props) { super(props) @@ -66,10 +84,14 @@ class UploadImage extends Component { this.handleClickOpen = this.handleClickOpen.bind(this) this.handleClose = this.handleClose.bind(this) this.handleSubmit = this.handleSubmit.bind(this) - this.saveAvatar = this.saveAvatar.bind(this) + this.handleChangeImage = this.handleChangeImage.bind(this) + this.cancelChangeImage = this.cancelChangeImage.bind(this) + this.saveImage = this.saveImage.bind(this) this.state = { open: false, + edit: 'avatar', + change: false, file: null, src: null, crop: { @@ -82,103 +104,147 @@ class UploadImage extends Component { cropped: null, rejected: null, uploaded: false, - image: '' + avatarImage: null, + bannerImage: null, + sasqr: null } } render () { - const { classes, team, authId, handleAvatar } = this.props + const { classes, team, authId, handleAvatar, handleBanner } = this.props let avatar = null - if (team.profile !== null && team.profile.avatar !== undefined && team.profile.avatar !== null && team.profile.avatar !== 'a') { - avatar = team.profile.avatar + let banner = null + if ((team.profile !== null && team.profile.avatar !== undefined && team.profile.avatar !== null && team.profile.avatar !== 'a') || this.state.avatarImage !== null) { + avatar = this.state.avatarImage !== null ? this.state.avatarImage : `${team.profile.avatar}?${Math.random()}` } else { - if (this.state.image !== '') avatar = this.state.image + avatar = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22288%22%20height%3D%22225%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20288%20225%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_164edaf95ee%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A14pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_164edaf95ee%22%3E%3Crect%20width%3D%22288%22%20height%3D%22225%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%2296.32500076293945%22%20y%3D%22118.8%22%3EAvatar%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E' + } + if ((team.profile !== null && team.profile.banner !== undefined && team.profile.banner !== null && team.profile.banner !== '') || this.state.bannerImage !== null) { + banner = this.state.bannerImage !== null ? this.state.bannerImage : `${team.profile.banner}?${Math.random()}` + } else { + banner = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22800%22%20height%3D%22200%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Asvg%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23banner_164edaf95ee%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A14pt%20%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%3E%3Ctitle%3EBanner%20Placeholder%3C%2Ftitle%3E%3Crect%20stroke%3D%22null%22%20id%3D%22svg_1%22%20fill%3D%22%2355595c%22%20height%3D%22200%22%20width%3D%22800%22%2F%3E%3Cg%20stroke%3D%22null%22%20id%3D%22banner_164edaf95ee%22%3E%3Ctext%20stroke%3D%22null%22%20id%3D%22svg_3%22%20y%3D%22106.5%22%20x%3D%22367.73438%22%3EBanner%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E' } - console.log('UploadImage', team, avatar, authId, this.state) + let handleImage + this.state.edit === 'avatar' ? handleImage = handleAvatar : handleImage = handleBanner + + console.log('UploadImage', team, avatar, banner, authId, this.state) return (
- {avatar === null && this.state.src === null && ( - -

Drag-and-drop team avater here or click to select files.
Only *.jpeg and *.png images

-
- )} - {(avatar !== null || this.state.src !== null) && ( - - {(getAzureSAS, { loading, error, data }) => { - console.log('getAzureSAS: ', error, data) - if (!this.state.uploaded) { - if (data !== undefined && data.getAzureSAS !== null) { - this.saveAvatar(data.getAzureSAS, team.slug, handleAvatar) - } - // this.handleSubmit(getAzureSAS, team.slug) + + {(getAzureSAS, { loading, error, data }) => { + console.log('getAzureSAS: ', error, data) + if (!this.state.uploaded) { + if (data !== undefined && data.getAzureSAS !== null) { + this.saveImage(data.getAzureSAS, team.slug, handleImage) } - if (this.state.cropping) { - return ( + // this.handleSubmit(getAzureSAS, team.slug) + } + if (this.state.cropping) { + return ( +
+ +
+ ) + } else { + return ( +
- + {(this.state.change || (avatar === null && banner === null && this.state.src === null)) && ( + + this.onDrop(e, this.state.edit)} + className={classes.dropzone} + multiple={false} + > +

Drag-and-drop team {this.state.edit} here or click to select files.
Only *.jpeg and *.png images

+
+ +
+ )} + {!this.state.change && ( + + Banner Crop Preview + + + )} + {!this.state.change && ( + + AvatarCrop Preview + + + )} + {(this.state.avatarImage !== null || this.state.bannerImage !== null) && ( + Click Update Team to save image changes! + )}
- ) - } else { - return ( - -
- Crop Preview -
- -
- -
+ +
+ +
+ {this.state.src !== null && ( { this.cropper = ref }} onImgLoad={this.onImageLoaded} + ratio={this.state.edit === 'avatar' ? 1 : 4} /> -
-
- - - - -
-
- - ) - } - }} - - )} + )} +
+
+ + + + +
+
+
+ ) + } + }} +
) } - onDrop (file) { + onDrop (file, editImageType) { console.log('onDrop', file) - this.onSelectFile(file) + this.onSelectFile(file, editImageType) this.setState({ file }) } - onSelectFile (file) { + onSelectFile (file, editImageType) { if (file && file.length > 0) { const reader = new window.FileReader() reader.addEventListener( @@ -194,53 +260,77 @@ class UploadImage extends Component { } } - async handleCrop (e, getAzureSAS, teamSlug, handleAvatar) { + async handleCrop (e, getAzureSAS, teamSlug, handleImage) { e.preventDefault() const data = await getAzureSAS({ variables: { teamSlug } }) console.log('handleCrop', this.cropper, data) const cropped = this.cropper.crop() const croppedBlob = await imgSrcToBlob(cropped, 'image/png', 'Anonymous') + let imageConfig = this.state.edit === 'avatar' ? imageAvatarConfig : imageBannerConfig let resizedBlob = await readAndCompressImage(croppedBlob, imageConfig) return this.setState({ cropped: resizedBlob, cropping: true, open: false }, () => { - this.saveAvatar(data.getAzureSAS, teamSlug, handleAvatar) + this.saveImage(data.getAzureSAS, teamSlug, handleImage) }) } async handleSubmit (e, getAzureSAS, teamSlug) { e.preventDefault() - await getAzureSAS({ variables: { teamSlug } }) + let SAS = await getAzureSAS({ variables: { teamSlug } }) + console.log('handleSubmit: ', await SAS) + return this.setState({ sasqr: SAS }) } - async saveAvatar (SASQueryParameters, teamSlug, handleAvatar) { - const fileName = `${teamSlug}-avatar.jpg` - console.log('saveAvatar SASurl', process.env.AZURE_SAS_URL) - const SASurl2 = `${process.env.AZURE_STORAGE_URL}?${SASQueryParameters}` - const SASurl = process.env.AZURE_SAS_URL - console.log('getAzureSAS2 SASurl: ', SASurl2, SASurl) + async saveImage (SASQueryParameters, teamSlug, handleImage) { + const fileName = this.state.edit === 'avatar' ? `${teamSlug}-avatar.jpg` : `${teamSlug}-banner.jpg` + let SASurl + if (this.state.sasqr === null) { + SASurl = process.env.AZURE_SAS_URL + } else { + SASurl = `${process.env.AZURE_STORAGE_URL}?${this.state.sasqr}` + } + console.log('getAzureSAS2 SASurl: ', SASurl, this.state.sasqr) + const pipeline = Azure.StorageURL.newPipeline(new Azure.AnonymousCredential()) const serviceURL = new Azure.ServiceURL(SASurl, pipeline) - console.log('getAzureSAS2: ', serviceURL) + const containerName = teamSlug const containerURL = Azure.ContainerURL.fromServiceURL(serviceURL, containerName) - console.log('getAzureSAS3: ', containerURL) const blockBlobURL = Azure.BlockBlobURL.fromContainerURL(containerURL, fileName) - console.log('getAzureSAS34: ', blockBlobURL) - const avatar = await Azure.uploadBrowserDataToBlockBlob(Azure.Aborter.None, this.state.cropped, blockBlobURL) - console.log('saveAvatar: ', avatar) - const image = `${process.env.AZURE_STORAGE_URL}${teamSlug}/${fileName}` - handleAvatar(image) - this.setState({ cropping: false, uploaded: true, image: image }) - return avatar + await Azure.uploadBrowserDataToBlockBlob(Azure.Aborter.None, this.state.cropped, blockBlobURL) + + const image = `${process.env.AZURE_STORAGE_URL}${teamSlug}/${fileName}?${Math.random()}` + handleImage(image) + let avatarImage = this.state.edit === 'avatar' ? image : null + let bannerImage = this.state.edit === 'banner' ? image : null + this.setState({ cropping: false, uploaded: true, change: false, avatarImage: avatarImage, bannerImage: bannerImage }) + return image } - handleClickOpen () { - this.setState({ open: true }) + handleChangeImage (e, editImageType) { + e.preventDefault() + let crop = editImageType === 'avatar' ? { x: 10, y: 10, width: 200, height: 200 } : { x: 10, y: 10, width: 800, height: 200 } + this.setState({ + edit: editImageType, + change: true, + crop: crop + }) + } + + cancelChangeImage () { + this.setState({ change: false }) + } + + handleClickOpen (e, editImageType) { + e.preventDefault() + this.setState({ + open: true + }) } handleClose () { - this.setState({ open: false, src: null }) + this.setState({ open: false, src: null, change: false }) } } @@ -248,7 +338,8 @@ UploadImage.propTypes = { classes: PropTypes.object.isRequired, team: PropTypes.object, authId: PropTypes.string.isRequired, - handleAvatar: PropTypes.func.isRequired + handleAvatar: PropTypes.func.isRequired, + handleBanner: PropTypes.func.isRequired } export default withStyles(styles)(UploadImage) diff --git a/client/src/views/dashboard/components/ManageTeamDetails.js b/client/src/views/dashboard/components/ManageTeamDetails.js index b4a736166..33eac05ed 100644 --- a/client/src/views/dashboard/components/ManageTeamDetails.js +++ b/client/src/views/dashboard/components/ManageTeamDetails.js @@ -5,10 +5,11 @@ import { withStyles } from '@material-ui/core/styles' import Button from '@material-ui/core/Button' import DetailsIcon from '@material-ui/icons/Details' import Dialog from '@material-ui/core/Dialog' -import DialogActions from '@material-ui/core/DialogActions' -import DialogContent from '@material-ui/core/DialogContent' -import DialogTitle from '@material-ui/core/DialogTitle' import Typography from '@material-ui/core/Typography' +import Card from '@material-ui/core/Card' +import CardContent from '@material-ui/core/CardContent' +import CardActions from '@material-ui/core/CardActions' +import CardMedia from '@material-ui/core/CardMedia' const styles = theme => ({ dialogContainer: { @@ -18,6 +19,33 @@ const styles = theme => ({ }, buttonRight: { justifyContent: 'flex-end' + }, + cardGrid: { + padding: `${theme.spacing.unit * 8}px 0` + }, + card: { + display: 'flex', + width: '100%' + }, + cardDetails: { + display: 'flex', + flex: 1 + }, + cardContent: { + flex: 1 + }, + cardAvatarMedia: { + flex: 1, + width: 100, + height: 100, + maxWidth: 100, + justifyContent: 'flex-start' + }, + cardBannerMedia: { + flex: 1, + width: 600, + height: 150, + justifyContent: 'flex-start' } }) @@ -35,7 +63,20 @@ export class ManageTeamDetails extends Component { render () { console.log(this.props.team) + const { classes } = this.props const { name, members, profile, createdAt } = this.props.team + let avatar + let banner + if (profile.avatar !== undefined && profile.avatar !== 'a') { + avatar = profile.avatar + } else { + avatar = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22288%22%20height%3D%22225%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20288%20225%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_164edaf95ee%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A14pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_164edaf95ee%22%3E%3Crect%20width%3D%22288%22%20height%3D%22225%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%2296.32500076293945%22%20y%3D%22118.8%22%3EAvatar%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E' + } + if (profile.banner !== undefined && profile.banner !== 'a') { + banner = profile.banner + } else { + banner = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22800%22%20height%3D%22200%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Asvg%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23banner_164edaf95ee%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A14pt%20%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%3E%3Ctitle%3EBanner%20Placeholder%3C%2Ftitle%3E%3Crect%20stroke%3D%22null%22%20id%3D%22svg_1%22%20fill%3D%22%2355595c%22%20height%3D%22200%22%20width%3D%22800%22%2F%3E%3Cg%20stroke%3D%22null%22%20id%3D%22banner_164edaf95ee%22%3E%3Ctext%20stroke%3D%22null%22%20id%3D%22svg_3%22%20y%3D%22106.5%22%20x%3D%22367.73438%22%3EBanner%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E' + } return (
- + })} + + + + + +
diff --git a/client/src/views/dashboard/components/ManageTeamEdit.js b/client/src/views/dashboard/components/ManageTeamEdit.js index 5a257cb4a..6cb6596b8 100644 --- a/client/src/views/dashboard/components/ManageTeamEdit.js +++ b/client/src/views/dashboard/components/ManageTeamEdit.js @@ -42,11 +42,14 @@ export class ManageTeamEdit extends Component { this.handleChange = this.handleChange.bind(this) this.handleSubmit = this.handleSubmit.bind(this) this.handleAvatar = this.handleAvatar.bind(this) + this.handleBanner = this.handleBanner.bind(this) + console.log('ManageTeamEdit', props.team) const motto = props.team.profile.motto || '' const description = props.team.profile.description || '' const avatar = props.team.profile.avatar || '' const banner = props.team.profile.banner || '' + this.state = { open: false, motto: motto, @@ -108,7 +111,12 @@ export class ManageTeamEdit extends Component { Edit Team Details - + Date: Tue, 25 Sep 2018 22:57:06 -0700 Subject: [PATCH 05/12] Connect to User Module --- api/src/index.js | 12 ++++- client/src/App.js | 6 +++ .../graphql/members/GetUserByAuthIDQuery.js | 25 ++++++++++ client/src/graphql/members/index.js | 3 +- client/src/views/navbar/LoggedIn.js | 46 +++++++++++++++---- client/src/views/navbar/LoggedInMenu.js | 2 +- 6 files changed, 82 insertions(+), 12 deletions(-) create mode 100644 client/src/graphql/members/GetUserByAuthIDQuery.js diff --git a/api/src/index.js b/api/src/index.js index 04eab61d7..6c61ceed8 100644 --- a/api/src/index.js +++ b/api/src/index.js @@ -64,8 +64,18 @@ const resolvers = { return ctx.db.query.member({ where: { authId: arg.authId } }, info) }, async currentMember(parent, args, ctx, info) { - console.log('currentMember: ', ctx.request.res.req.user) + console.log('currentMember: ', ctx.request.res.req.user, ctx.token) const authId = ctx.request.res.req.user.sub + + fetch('URL_GOES_HERE', { + method: 'post', + headers: new Headers({ + 'Authorization': 'Basic '+btoa('username:password'), + 'Content-Type': 'application/x-www-form-urlencoded' + }), + body: 'A=1&B=2' + }) + return ctx.db.query.member({ where: { authId } }, info) }, teams(parent, args, ctx, info) { diff --git a/client/src/App.js b/client/src/App.js index 2fdbb7578..1fd6e8cb8 100644 --- a/client/src/App.js +++ b/client/src/App.js @@ -64,6 +64,7 @@ wsClient.onReconnected(() => { }) const httpLink = new HttpLink({ uri: graphqlEndpoint }) +const httpUserLink = new HttpLink({ uri: 'http://104.43.240.255:4000/graphql' }) const link = split( ({ query }) => { @@ -134,6 +135,11 @@ export const client = new ApolloClient({ connectToDevTools: true }) +export const UserClient = new ApolloClient({ + link: ApolloLink.from([authRetryLink, authLink, httpUserLink]), + cache +}) + export const auth = new Auth( result => console.log('auth result', result), client diff --git a/client/src/graphql/members/GetUserByAuthIDQuery.js b/client/src/graphql/members/GetUserByAuthIDQuery.js new file mode 100644 index 000000000..14375c2f1 --- /dev/null +++ b/client/src/graphql/members/GetUserByAuthIDQuery.js @@ -0,0 +1,25 @@ +/* eslint-disable no-unused-vars */ +import gql from 'graphql-tag' + +export const USER_BY_AUTHID = gql` +query($authId: String){ + userByAuthId (authId: $authId){ + id + alias + firstName + middleName + lastName + bio + email + emailVerified + isDeveloper + isDataAnalyst + isTrader + role { + id + } + } +} +` + +export default USER_BY_AUTHID diff --git a/client/src/graphql/members/index.js b/client/src/graphql/members/index.js index a53b48a41..7ff5637c7 100644 --- a/client/src/graphql/members/index.js +++ b/client/src/graphql/members/index.js @@ -1,3 +1,4 @@ import { GetCurrentMember } from './GetCurrentMemberQuery' +import { USER_BY_AUTHID } from './GetUserByAuthIDQuery' -export { GetCurrentMember } +export { GetCurrentMember, USER_BY_AUTHID } diff --git a/client/src/views/navbar/LoggedIn.js b/client/src/views/navbar/LoggedIn.js index e8408d1b6..fd172000e 100644 --- a/client/src/views/navbar/LoggedIn.js +++ b/client/src/views/navbar/LoggedIn.js @@ -1,17 +1,23 @@ import React from 'react' import PropTypes from 'prop-types' -import { graphql } from 'react-apollo' +import { Query } from 'react-apollo' import LoggedInMenu from './LoggedInMenu' +import { MessageCard } from '../common/' -import { GetCurrentMember } from '../../graphql/members' +import { UserClient } from '../../App' +import { USER_BY_AUTHID } from '../../graphql/members' import { isDefined } from '../../utils/js-helpers' export const LoggedIn = props => { let { data, user, auth } = props - console.log('LoggedIn :', data, user) + console.log('LoggedIn :', data, user, auth) let displayName = 'No Display Name' + let authId = null + if (isDefined(user.authId)) { + authId = user.authId + } if (isDefined(user.alias)) { displayName = user.alias @@ -29,12 +35,34 @@ export const LoggedIn = props => { displayName = user.firstName + ' ' + user.lastName } + if (authId === null) { + return ( +
Loading...
+ ) + } + return ( -
-

- -

-
+ + {({ loading, error, data }) => { + console.log('USER_BY_AUTHID: ', loading, error, data) + + if (error) { + error.graphQLErrors.map(({ message }, i) => { + return + }) + } + return ( +

+ +

+ ) + }} +
) } @@ -44,4 +72,4 @@ LoggedIn.propTypes = { auth: PropTypes.object.isRequired } -export default graphql(GetCurrentMember)(LoggedIn) // This binds the querty to the component +export default LoggedIn diff --git a/client/src/views/navbar/LoggedInMenu.js b/client/src/views/navbar/LoggedInMenu.js index 574a8e0ed..dd0389983 100644 --- a/client/src/views/navbar/LoggedInMenu.js +++ b/client/src/views/navbar/LoggedInMenu.js @@ -8,7 +8,7 @@ import { Link } from 'react-router-dom' import ProfileIcon from '@material-ui/icons/Person' import LogoutIcon from '@material-ui/icons/DirectionsWalk' -const UserLink = props => +const UserLink = props => const LogoutLink = props => class LoggedInUserMenu extends Component { From 6c09eb704cc68a17ff8588769f1919943a8ba5e1 Mon Sep 17 00:00:00 2001 From: Barry Low Date: Wed, 26 Sep 2018 08:51:49 -0700 Subject: [PATCH 06/12] Client: Profile drawer --- client/src/views/navbar/LoggedIn.js | 9 ++- client/src/views/navbar/LoggedInMenu.js | 42 +++++++++-- client/src/views/navbar/Profile.js | 94 +++++++++++++++++++++++++ 3 files changed, 136 insertions(+), 9 deletions(-) create mode 100644 client/src/views/navbar/Profile.js diff --git a/client/src/views/navbar/LoggedIn.js b/client/src/views/navbar/LoggedIn.js index fd172000e..242feefb1 100644 --- a/client/src/views/navbar/LoggedIn.js +++ b/client/src/views/navbar/LoggedIn.js @@ -3,7 +3,6 @@ import PropTypes from 'prop-types' import { Query } from 'react-apollo' import LoggedInMenu from './LoggedInMenu' -import { MessageCard } from '../common/' import { UserClient } from '../../App' import { USER_BY_AUTHID } from '../../graphql/members' @@ -51,14 +50,18 @@ export const LoggedIn = props => { {({ loading, error, data }) => { console.log('USER_BY_AUTHID: ', loading, error, data) + if (loading) { + return
Loading...
+ } + if (error) { error.graphQLErrors.map(({ message }, i) => { - return + return
{message}
}) } return (

- +

) }} diff --git a/client/src/views/navbar/LoggedInMenu.js b/client/src/views/navbar/LoggedInMenu.js index dd0389983..6c1fb266c 100644 --- a/client/src/views/navbar/LoggedInMenu.js +++ b/client/src/views/navbar/LoggedInMenu.js @@ -1,16 +1,29 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' +import { withStyles } from '@material-ui/core/styles' + import Button from '@material-ui/core/Button' import Menu from '@material-ui/core/Menu' import MenuItem from '@material-ui/core/MenuItem' +import Drawer from '@material-ui/core/Drawer' import { Link } from 'react-router-dom' import ProfileIcon from '@material-ui/icons/Person' import LogoutIcon from '@material-ui/icons/DirectionsWalk' -const UserLink = props => +import UserProfile from './Profile' + const LogoutLink = props => +const styles = { + drawer: { + width: 250 + }, + fullList: { + width: 'auto' + } +} + class LoggedInUserMenu extends Component { constructor (props) { super(props) @@ -18,14 +31,16 @@ class LoggedInUserMenu extends Component { this.handleMenu = this.handleMenu.bind(this) this.handleClose = this.handleClose.bind(this) this.handleLogout = this.handleLogout.bind(this) + this.toggleDrawer = this.toggleDrawer.bind(this) this.state = { - menu: null + menu: null, + open: false } } render () { - const { menuLabel } = this.props + const { menuLabel, user, classes } = this.props return ( + + + { user.firstName } { user.lastName } ( {user.alias} ) + + + + + { user.role.name } + + + + + { user.bio } + + + + + + ) + } +} + +UserProfile.propTypes = { + classes: PropTypes.object.isRequired, + user: PropTypes.object, + toggleDrawer: PropTypes.func +} +export default withStyles(styles)(UserProfile) From 5596f54864499fbace8a33a244fdb43bbb3afeba Mon Sep 17 00:00:00 2001 From: Barry Low Date: Fri, 28 Sep 2018 17:31:19 -0700 Subject: [PATCH 07/12] Refactor upload-image to image-upload component --- api/src/index.js | 23 +- api/src/storage/azure.js | 2 +- client/src/views/common/ImageUpload.js | 264 ++++++++++++++++++ .../dashboard/components/ManageTeamEdit.js | 39 ++- 4 files changed, 306 insertions(+), 22 deletions(-) create mode 100644 client/src/views/common/ImageUpload.js diff --git a/api/src/index.js b/api/src/index.js index 6c61ceed8..5ec890d4b 100644 --- a/api/src/index.js +++ b/api/src/index.js @@ -13,18 +13,7 @@ const checkJwt = require('./auth/middleware/jwt') const { getMember } = require('./auth/middleware/getMember') const { validateIdToken } = require('./auth/validateIdToken') const { directiveResolvers } = require('./auth/authDirectives') -const { - createStoragePipline, - createServiceURL, - listContainers, - createContainer, - createBlob, - uploadBlob, - downloadBlob, - deleteBlob, - deleteContainer -} = require('./storage/azure') -//var azure = require('azure-storage') + const Azure = require("@azure/storage-blob") const azureAccount = process.env.AZURE_STORAGE_ACCOUNT const azureKey = process.env.AZURE_STORAGE_ACCESS_KEY @@ -160,6 +149,7 @@ const resolvers = { const containerName = teamSlug const containerURL = Azure.ContainerURL.fromServiceURL(serviceURL, containerName) + //list container and check if already exists. let marker let containerCheck = null do { @@ -167,25 +157,28 @@ const resolvers = { Azure.Aborter.None, marker, ) - console.log(`ContainerCheck marker: `, listContainersResponse) + // console.log(`ContainerCheck marker: `, listContainersResponse) marker = listContainersResponse.marker; for (const container of listContainersResponse.containerItems) { - console.log(`ContainerCheck: ${container.name} | ${containerName} | ${marker}`) + // console.log(`ContainerCheck: ${container.name} | ${containerName} | ${marker}`) if(container.name === containerName){ containerCheck = container.name } } } while (marker) + + // if container doesn't exist, create one let newContainer if(containerCheck === null){ newContainer = await containerURL.create(Azure.Aborter.None, { access: 'blob' }) } - console.log('getAzureSAS container: ', containerURL, containerCheck, newContainer) + // console.log('getAzureSAS container: ', containerURL, containerCheck, newContainer) // Set permissions for service, resource types and containers const SASServicePerms = Azure.AccountSASServices.parse('b') const SASResourceTypes = Azure.AccountSASResourceTypes.parse('co') const SASContainerPerms = Azure.ContainerSASPermissions.parse('rwl') + // Generate SAS url const SASQueryParameters = Azure.generateAccountSASQueryParameters( { diff --git a/api/src/storage/azure.js b/api/src/storage/azure.js index 324181b04..abeea558e 100644 --- a/api/src/storage/azure.js +++ b/api/src/storage/azure.js @@ -4,7 +4,7 @@ const Azure = require('@azure/storage-blob') * @accountName: Azure Blob Storage account name (required) - Environment variable * @accountKey: Azure Blob Storage storage key (required) - Environment variable */ -const createStoragePipline = async (accountNAme, accountKey) => { +const createStoragePipline = async (accountName, accountKey) => { return Azure.StorageURL.newPipeline( new Azure.SharedKeyCredential(accountName, accountKey) ) diff --git a/client/src/views/common/ImageUpload.js b/client/src/views/common/ImageUpload.js new file mode 100644 index 000000000..fd4d837bb --- /dev/null +++ b/client/src/views/common/ImageUpload.js @@ -0,0 +1,264 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { Mutation } from 'react-apollo' +import Dropzone from 'react-dropzone' +import { Cropper } from 'react-image-cropper' +import * as Azure from '@azure/storage-blob' +import readAndCompressImage from 'browser-image-resizer' +import { imgSrcToBlob } from 'blob-util' +import { withStyles } from '@material-ui/core/styles' + +import Dialog from '@material-ui/core/Dialog' +import DialogActions from '@material-ui/core/DialogActions' +import DialogContent from '@material-ui/core/DialogContent' +import Button from '@material-ui/core/Button' +import Typography from '@material-ui/core/Typography' + +const styles = theme => ({ + dropzoneContainer: { + display: 'block', + margin: '1em', + height: 200 + }, + dropzone: { + display: 'block', + padding: '3em', + height: 200, + borderWidth: 2, + borderColor: '#999', + backgroundColor: '#eee' + }, + dropzoneText: { + textAlign: 'center' + }, + existingImagePreview: { + width: 100, + height: 100 + }, + cropImageBox: { + width: 350, + height: 350 + } +}) + +// placeholder image if no existing image +const placeHolder = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22288%22%20height%3D%22225%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20288%20225%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_164edaf95ee%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A14pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_164edaf95ee%22%3E%3Crect%20width%3D%22288%22%20height%3D%22225%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%2296.32500076293945%22%20y%3D%22118.8%22%3EAvatar%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E' + +// config for image resize and quality +const imageResizeConfig = { + quality: 0.6, + maxWidth: 200, + maxHeight: 200, + autoRotate: true, + debug: true, + mimeType: 'image/jpeg' +} + +class UploadImage extends Component { + constructor (props) { + super(props) + + this.onDrop = this.onDrop.bind(this) + this.handleCrop = this.handleCrop.bind(this) + this.handleClickOpen = this.handleClickOpen.bind(this) + this.handleClose = this.handleClose.bind(this) + this.handleChangeImage = this.handleChangeImage.bind(this) + this.cancelChangeImage = this.cancelChangeImage.bind(this) + this.saveImage = this.saveImage.bind(this) + + this.state = { + open: false, + change: false, + file: null, + src: null, + crop: { + x: 10, + y: 10, + width: 200, + height: 200 + }, + cropping: false, + cropped: null, + uploaded: false, + image: null, + sasqr: null + } + } + + render () { + const { classes, cropContainer, saveImageConfig, containerName, fileName, existingImage, handleUrl, AZURE_SAS_QUERY } = this.props + let image = null + let resizeImage = null + if (existingImage !== undefined && existingImage !== null) { + image = existingImage + } else { + image = placeHolder + } + + if (saveImageConfig !== undefined && saveImageConfig !== null) { + resizeImage = saveImageConfig + } else { + resizeImage = imageResizeConfig + } + + return ( +
+ + {(getAzureSAS, { loading, error, data }) => { + if (this.state.cropping) { + return ( + Cropping... + ) + } else { + return ( + +
+ {(this.state.change || this.state.src === null) && ( + + this.onDrop(e, this.state.edit)} + className={classes.dropzone} + multiple={false} + > +

Drag-and-drop image here or click to select
Only *.jpeg and *.png images

+
+ +
+ )} + {!this.state.change && ( + + Existing Image Preview + + + )} + {this.state.image !== null && ( + Save profile to save image changes! + )} +
+ +
+ +
+ {this.state.src !== null && ( + { this.cropper = ref }} + onImgLoad={this.onImageLoaded} + ratio={1} + /> + )} +
+
+ + + + +
+
+
+ ) + } + }} +
+
+ ) + } + + onDrop (file, editImageType) { + console.log('onDrop', file) + this.onSelectFile(file, editImageType) + this.setState({ + file + }) + } + + async handleCrop (e, getAzureSAS, resizeImage, containerName, fileName, handleUrl) { + e.preventDefault() + const data = await getAzureSAS({ variables: { containerName } }) + + const cropped = this.cropper.crop() + const croppedBlob = await imgSrcToBlob(cropped, 'image/png', 'Anonymous') + let resizedBlob = await readAndCompressImage(croppedBlob, resizeImage) + + return this.setState({ cropped: resizedBlob, cropping: true, open: false }, () => { + this.saveImage(data.getAzureSAS, containerName, fileName, handleUrl) + }) + } + + async saveImage (SASQueryParameters, containerName, fileName, handleUrl) { + const SASurl = `${process.env.AZURE_STORAGE_URL}?${this.state.sasqr}` + + // create Azure BlockBlobURL to save image to + const pipeline = Azure.StorageURL.newPipeline(new Azure.AnonymousCredential()) + const serviceURL = new Azure.ServiceURL(SASurl, pipeline) + + const containerURL = Azure.ContainerURL.fromServiceURL(serviceURL, containerName) + const blockBlobURL = Azure.BlockBlobURL.fromContainerURL(containerURL, fileName) + + await Azure.uploadBrowserDataToBlockBlob(Azure.Aborter.None, this.state.cropped, blockBlobURL) + + const image = `${process.env.AZURE_STORAGE_URL}${containerName}/${fileName}?${Math.random()}` // random number keeps browser from using cached version of existing image + handleUrl(image) // pass image URL up to be save to DB + this.setState({ cropping: false, uploaded: true, change: false, image: image }) + return image + } + + handleChangeImage (e, cropContainer) { + e.preventDefault() + let crop = cropContainer // dimensions of cropContainer passed from above + this.setState({ + change: true, + crop: crop + }) + } + + cancelChangeImage () { + this.setState({ change: false }) + } + + handleClickOpen (e) { + e.preventDefault() + this.setState({ open: true }) + } + + handleClose () { + this.setState({ open: false, src: null, change: false }) + } +} + +UploadImage.propTypes = { + classes: PropTypes.object.isRequired, + cropContainer: PropTypes.object, // { x: 10, y: 10, width: 200, height: 200 } (default) + saveImageConfig: PropTypes.object, // see imageResizeConfig above for default (default) + containerName: PropTypes.string.isRequired, // name of Azure Container image will be saved in + fileName: PropTypes.string.isRequired, // filename to save image as + existingImage: PropTypes.string, // image url of existing image (optional) + handleUrl: PropTypes.func.isRequired, // function that handles uploaded image file url + AZURE_SAS_QUERY: PropTypes.any.isRequired // GraphQL for getting Azure SAS URL +} + +export default withStyles(styles)(UploadImage) diff --git a/client/src/views/dashboard/components/ManageTeamEdit.js b/client/src/views/dashboard/components/ManageTeamEdit.js index 6cb6596b8..de5e9aa83 100644 --- a/client/src/views/dashboard/components/ManageTeamEdit.js +++ b/client/src/views/dashboard/components/ManageTeamEdit.js @@ -20,7 +20,8 @@ import GET_TEAMS_BY_OWNER from '../../../graphql/teams/GetTeamsByOwnerQuery' import { checkGraphQLError } from '../../../utils/graphql-errors' -import { UploadImage } from '../../common' +import { ImageUpload } from '../../common' +import GET_AZURE_SAS from '../../graphql/teams/GetAzureSASMutation' const styles = theme => ({ dialogContainer: { @@ -111,11 +112,37 @@ export class ManageTeamEdit extends Component { Edit Team Details - + Date: Mon, 1 Oct 2018 08:26:29 +0200 Subject: [PATCH 08/12] Update LICENSE --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index a47c572e6..f513e6254 100644 --- a/LICENSE +++ b/LICENSE @@ -14,7 +14,7 @@ Advanced Algos LTD. is publishing the code of this software for several reasons. it shall not be considered free software yet. Advanced Algos LTD. is a for-profit company in the crypto space and this module is still proprietary software. -The reasons for opening up the code of AACloud at this stage are the following: +The reasons for opening up the code of this module at this stage are the following: * We believe that transparency is paramount to achieving our goals, so we want everyone to be able to understand what this module does and how. Opening up the code of this module allows From 836a9f2530d9c36b59c3fd2a05518b156a90f8a1 Mon Sep 17 00:00:00 2001 From: Barry Low Date: Tue, 2 Oct 2018 12:13:57 -0700 Subject: [PATCH 09/12] Client: switch common to @advancedalgos/web-components --- api/docker-compose.api.yml | 5 +- client/package-lock.json | 519 +++++++++++------- client/package.json | 6 +- client/src/App.js | 2 +- client/src/views/common/ImageUpload.js | 264 --------- client/src/views/common/MessageCard.js | 45 -- client/src/views/common/UploadImage.js | 345 ------------ client/src/views/common/index.js | 4 - .../dashboard/components/ManageTeamEdit.js | 80 +-- 9 files changed, 360 insertions(+), 910 deletions(-) delete mode 100644 client/src/views/common/ImageUpload.js delete mode 100644 client/src/views/common/MessageCard.js delete mode 100644 client/src/views/common/UploadImage.js delete mode 100644 client/src/views/common/index.js diff --git a/api/docker-compose.api.yml b/api/docker-compose.api.yml index 689c37fa6..6282e3a87 100644 --- a/api/docker-compose.api.yml +++ b/api/docker-compose.api.yml @@ -1,10 +1,7 @@ version: '3' services: api: - image: aateamapi-dev:v1 - build: - context: . - dockerfile: ./Dockerfile + image: advancedalgosteamsmodule/teammodule-api:v1 env_file: ./.env container_name: aateamapi tty: true diff --git a/client/package-lock.json b/client/package-lock.json index c0fbda736..4a43e9c8d 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -4,6 +4,133 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@advancedalgos/web-components": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@advancedalgos/web-components/-/web-components-0.0.1.tgz", + "integrity": "sha512-RDi7oR5oY4HbcXdzTDhtgYVHRO+/ApBMossZ/UkCrI2lYAWhQaq4mYaKaJ2mtVKnwV1BkeOLFE6rbPZwIkhXJA==", + "requires": { + "@azure/storage-blob": "^10.1.0-preview", + "@material-ui/core": "^3.0.3", + "@material-ui/icons": "^3.0.1", + "apollo-client": "^2.4.2", + "blob-util": "^2.0.2", + "browser-image-resizer": "^1.2.0", + "graphql": "^14.0.2", + "graphql-tag": "^2.9.2", + "prop-types": "^15.6.2", + "react-apollo": "^2.1.11", + "react-dropzone": "^5.1.0", + "react-image-crop": "^5.0.0", + "react-image-cropper": "^1.3.0" + }, + "dependencies": { + "@azure/storage-blob": { + "version": "10.1.0-preview", + "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-10.1.0-preview.tgz", + "integrity": "sha512-vKY4dF8VCRCOOQ03Z1ResyjZbWg4jPOnQtRtlSEKE6a7WwFE3EuIbhE7OQfEihuNYyE+XWLkTjrHxeHDFx1hFg==", + "requires": { + "events": "3.0.0", + "ms-rest-js": "0.22.425", + "tslib": "^1.9.3" + } + }, + "@types/node": { + "version": "9.6.32", + "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.32.tgz", + "integrity": "sha512-5+L3wQ+FHoQ589EaH6rYICleuj8gnunq+1CJkM9fxklirErIOv+kxm3s/vecYnpJOYnFowE5uUizcb3hgjHUug==" + }, + "apollo-cache": { + "version": "1.1.17", + "resolved": "https://registry.npmjs.org/apollo-cache/-/apollo-cache-1.1.17.tgz", + "integrity": "sha512-7sp24n2HZO4vXgTaKNomLyIfGxG4gDdDkBB0jkRzRi7HhnKmfwhiF/RCiKNbgLdrPX151INdls0KwIVliD0dHQ==", + "requires": { + "apollo-utilities": "^1.0.21" + } + }, + "apollo-client": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/apollo-client/-/apollo-client-2.4.2.tgz", + "integrity": "sha512-g1z23umaVSoKLj9xNl0aAnk2KBF4JeBi7MeKFc9CGTixH7TkqeQUQtxcjrC7j2h4KmDbuhOAHOFUGf8YshN+ag==", + "requires": { + "@types/async": "2.0.49", + "@types/zen-observable": "^0.8.0", + "apollo-cache": "1.1.17", + "apollo-link": "^1.0.0", + "apollo-link-dedup": "^1.0.0", + "apollo-utilities": "1.0.21", + "symbol-observable": "^1.0.2", + "zen-observable": "^0.8.0" + } + }, + "apollo-utilities": { + "version": "1.0.21", + "resolved": "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.0.21.tgz", + "integrity": "sha512-ZcxELlEl+sDCYBgEMdNXJAsZtRVm8wk4HIA58bMsqYfd1DSAJQEtZ93F0GZgYNAGy3QyaoBeZtbb0/01++G8JQ==", + "requires": { + "fast-json-stable-stringify": "^2.0.0", + "fclone": "^1.0.11" + } + }, + "events": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz", + "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==" + }, + "graphql": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-14.0.2.tgz", + "integrity": "sha512-gUC4YYsaiSJT1h40krG3J+USGlwhzNTXSb4IOZljn9ag5Tj+RkoXrWp+Kh7WyE3t1NCfab5kzCuxBIvOMERMXw==", + "requires": { + "iterall": "^1.2.2" + } + }, + "ms-rest-js": { + "version": "0.22.425", + "resolved": "https://registry.npmjs.org/ms-rest-js/-/ms-rest-js-0.22.425.tgz", + "integrity": "sha512-PM9l0Si+T3kFxce8hwB/LlCxPJSHwSlFUSpjdJHzPvF+2w8lw7huE/FCPnRfdaUm7IikQESkJhydnbqX6g2OuQ==", + "requires": { + "@types/express": "^4.11.1", + "@types/form-data": "^2.2.1", + "@types/node": "^9.4.6", + "@types/uuid": "^3.4.3", + "axios": "^0.18.0", + "form-data": "^2.3.2", + "tough-cookie": "^2.4.3", + "tslib": "^1.9.2", + "uuid": "^3.2.1", + "xml2js": "^0.4.19" + } + }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + } + }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + } + } + }, "@azure/storage-blob": { "version": "10.0.0-preview", "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-10.0.0-preview.tgz", @@ -22,18 +149,18 @@ } }, "@babel/core": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.0.1.tgz", - "integrity": "sha512-7Yy2vRB6KYbhWeIrrwJmKv9UwDxokmlo43wi6AV84oNs4Gi71NTNGh3YxY/hK3+CxuSc6wcKSl25F2tQOhm1GQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.1.2.tgz", + "integrity": "sha512-IFeSSnjXdhDaoysIlev//UzHZbdEmm7D0EIH2qtse9xK7mXEZQpYjs2P00XlP1qYsYvid79p+Zgg6tz1mp6iVw==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.0.0", - "@babel/helpers": "^7.0.0", - "@babel/parser": "^7.0.0", - "@babel/template": "^7.0.0", - "@babel/traverse": "^7.0.0", - "@babel/types": "^7.0.0", + "@babel/generator": "^7.1.2", + "@babel/helpers": "^7.1.2", + "@babel/parser": "^7.1.2", + "@babel/template": "^7.1.2", + "@babel/traverse": "^7.1.0", + "@babel/types": "^7.1.2", "convert-source-map": "^1.1.0", "debug": "^3.1.0", "json5": "^0.5.0", @@ -43,22 +170,13 @@ "source-map": "^0.5.0" }, "dependencies": { - "@babel/code-frame": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", - "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" - } - }, "@babel/generator": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.0.0.tgz", - "integrity": "sha512-/BM2vupkpbZXq22l1ALO7MqXJZH2k8bKVv8Y+pABFnzWdztDB/ZLveP5At21vLz5c2YtSE6p7j2FZEsqafMz5Q==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.1.2.tgz", + "integrity": "sha512-70A9HWLS/1RHk3Ck8tNHKxOoKQuSKocYgwDN85Pyl/RBduss6AKxUR7RIZ/lzduQMSYfWEM4DDBu6A+XGbkFig==", "dev": true, "requires": { - "@babel/types": "^7.0.0", + "@babel/types": "^7.1.2", "jsesc": "^2.5.1", "lodash": "^4.17.10", "source-map": "^0.5.0", @@ -66,67 +184,44 @@ } }, "@babel/helper-function-name": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.0.0.tgz", - "integrity": "sha512-Zo+LGvfYp4rMtz84BLF3bavFTdf8y4rJtMPTe2J+rxYmnDOIeH8le++VFI/pRJU+rQhjqiXxE4LMaIau28Tv1Q==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", + "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", "dev": true, "requires": { "@babel/helper-get-function-arity": "^7.0.0", - "@babel/template": "^7.0.0", + "@babel/template": "^7.1.0", "@babel/types": "^7.0.0" } }, - "@babel/helper-get-function-arity": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", - "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz", - "integrity": "sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/highlight": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", - "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", - "dev": true, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" - } + "@babel/parser": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.1.2.tgz", + "integrity": "sha512-x5HFsW+E/nQalGMw7hu+fvPqnBeBaIr0lWJ2SG0PPL2j+Pm9lYvCrsZJGIgauPIENx0v10INIyFjmSNUD/gSqQ==", + "dev": true }, "@babel/template": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.0.0.tgz", - "integrity": "sha512-VLQZik/G5mjYJ6u19U3W2u7eM+rA/NGzH+GtHDFFkLTKLW66OasFrxZ/yK7hkyQcswrmvugFyZpDFRW0DjcjCw==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.1.2.tgz", + "integrity": "sha512-SY1MmplssORfFiLDcOETrW7fCLl+PavlwMh92rrGcikQaRq4iWPVH0MpwPpY3etVMx6RnDjXtr6VZYr/IbP/Ag==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.0.0", - "@babel/types": "^7.0.0" + "@babel/parser": "^7.1.2", + "@babel/types": "^7.1.2" } }, "@babel/traverse": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.0.0.tgz", - "integrity": "sha512-ka/lwaonJZTlJyn97C4g5FYjPOx+Oxd3ab05hbDr1Mx9aP1FclJ+SUHyLx3Tx40sGmOVJApDxE6puJhd3ld2kw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.1.0.tgz", + "integrity": "sha512-bwgln0FsMoxm3pLOgrrnGaXk18sSM9JNf1/nHC/FksmNGFbYnPWY4GYCfLxyP1KRmfsxqkRpfoa6xr6VuuSxdw==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", "@babel/generator": "^7.0.0", - "@babel/helper-function-name": "^7.0.0", + "@babel/helper-function-name": "^7.1.0", "@babel/helper-split-export-declaration": "^7.0.0", - "@babel/parser": "^7.0.0", + "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0", "debug": "^3.1.0", "globals": "^11.1.0", @@ -134,21 +229,15 @@ } }, "@babel/types": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0.tgz", - "integrity": "sha512-5tPDap4bGKTLPtci2SUl/B7Gv8RnuJFuQoWx26RJobS0fFrz4reUA3JnwIM+HVHEmWE0C1mzKhDtTp8NsWY02Q==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.1.2.tgz", + "integrity": "sha512-pb1I05sZEKiSlMUV9UReaqsCPUpgbHHHu2n1piRm7JkuBkm6QxcaIzKu6FMnMtCbih/cEYTR+RGYYC96Yk9HAg==", "dev": true, "requires": { "esutils": "^2.0.2", "lodash": "^4.17.10", "to-fast-properties": "^2.0.0" } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true } } }, @@ -1183,100 +1272,55 @@ } }, "@babel/helpers": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.0.0.tgz", - "integrity": "sha512-jbvgR8iLZPnyk6m/UqdXYsSxbVtRi7Pd3CzB4OPwPBnmhNG1DWjiiy777NTuoyIcniszK51R40L5pgfXAfHDtw==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.1.2.tgz", + "integrity": "sha512-Myc3pUE8eswD73aWcartxB16K6CGmHDv9KxOmD2CeOs/FaEAQodr3VYGmlvOmog60vNQ2w8QbatuahepZwrHiA==", "dev": true, "requires": { - "@babel/template": "^7.0.0", - "@babel/traverse": "^7.0.0", - "@babel/types": "^7.0.0" + "@babel/template": "^7.1.2", + "@babel/traverse": "^7.1.0", + "@babel/types": "^7.1.2" }, "dependencies": { - "@babel/code-frame": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", - "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" - } - }, - "@babel/generator": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.0.0.tgz", - "integrity": "sha512-/BM2vupkpbZXq22l1ALO7MqXJZH2k8bKVv8Y+pABFnzWdztDB/ZLveP5At21vLz5c2YtSE6p7j2FZEsqafMz5Q==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0", - "jsesc": "^2.5.1", - "lodash": "^4.17.10", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" - } - }, "@babel/helper-function-name": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.0.0.tgz", - "integrity": "sha512-Zo+LGvfYp4rMtz84BLF3bavFTdf8y4rJtMPTe2J+rxYmnDOIeH8le++VFI/pRJU+rQhjqiXxE4LMaIau28Tv1Q==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", + "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", "dev": true, "requires": { "@babel/helper-get-function-arity": "^7.0.0", - "@babel/template": "^7.0.0", + "@babel/template": "^7.1.0", "@babel/types": "^7.0.0" } }, - "@babel/helper-get-function-arity": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", - "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz", - "integrity": "sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/highlight": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", - "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", - "dev": true, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" - } + "@babel/parser": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.1.2.tgz", + "integrity": "sha512-x5HFsW+E/nQalGMw7hu+fvPqnBeBaIr0lWJ2SG0PPL2j+Pm9lYvCrsZJGIgauPIENx0v10INIyFjmSNUD/gSqQ==", + "dev": true }, "@babel/template": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.0.0.tgz", - "integrity": "sha512-VLQZik/G5mjYJ6u19U3W2u7eM+rA/NGzH+GtHDFFkLTKLW66OasFrxZ/yK7hkyQcswrmvugFyZpDFRW0DjcjCw==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.1.2.tgz", + "integrity": "sha512-SY1MmplssORfFiLDcOETrW7fCLl+PavlwMh92rrGcikQaRq4iWPVH0MpwPpY3etVMx6RnDjXtr6VZYr/IbP/Ag==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.0.0", - "@babel/types": "^7.0.0" + "@babel/parser": "^7.1.2", + "@babel/types": "^7.1.2" } }, "@babel/traverse": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.0.0.tgz", - "integrity": "sha512-ka/lwaonJZTlJyn97C4g5FYjPOx+Oxd3ab05hbDr1Mx9aP1FclJ+SUHyLx3Tx40sGmOVJApDxE6puJhd3ld2kw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.1.0.tgz", + "integrity": "sha512-bwgln0FsMoxm3pLOgrrnGaXk18sSM9JNf1/nHC/FksmNGFbYnPWY4GYCfLxyP1KRmfsxqkRpfoa6xr6VuuSxdw==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", "@babel/generator": "^7.0.0", - "@babel/helper-function-name": "^7.0.0", + "@babel/helper-function-name": "^7.1.0", "@babel/helper-split-export-declaration": "^7.0.0", - "@babel/parser": "^7.0.0", + "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0", "debug": "^3.1.0", "globals": "^11.1.0", @@ -1284,21 +1328,15 @@ } }, "@babel/types": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0.tgz", - "integrity": "sha512-5tPDap4bGKTLPtci2SUl/B7Gv8RnuJFuQoWx26RJobS0fFrz4reUA3JnwIM+HVHEmWE0C1mzKhDtTp8NsWY02Q==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.1.2.tgz", + "integrity": "sha512-pb1I05sZEKiSlMUV9UReaqsCPUpgbHHHu2n1piRm7JkuBkm6QxcaIzKu6FMnMtCbih/cEYTR+RGYYC96Yk9HAg==", "dev": true, "requires": { "esutils": "^2.0.2", "lodash": "^4.17.10", "to-fast-properties": "^2.0.0" } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true } } }, @@ -3433,48 +3471,10 @@ } }, "babel-core": { - "version": "6.26.3", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", - "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", - "dev": true, - "requires": { - "babel-code-frame": "^6.26.0", - "babel-generator": "^6.26.0", - "babel-helpers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-register": "^6.26.0", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "convert-source-map": "^1.5.1", - "debug": "^2.6.9", - "json5": "^0.5.1", - "lodash": "^4.17.4", - "minimatch": "^3.0.4", - "path-is-absolute": "^1.0.1", - "private": "^0.1.8", - "slash": "^1.0.0", - "source-map": "^0.5.7" - }, - "dependencies": { - "babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } + "version": "7.0.0-bridge.0", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz", + "integrity": "sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==", + "dev": true }, "babel-eslint": { "version": "9.0.0", @@ -3728,15 +3728,14 @@ } }, "babel-loader": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.2.tgz", - "integrity": "sha512-Law0PGtRV1JL8Y9Wpzc0d6EE0GD7LzXWCfaeWwboUMcBWNG6gvaWTK1/+BK7a4X5EmeJiGEuDDFxUsOa8RSWCw==", + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-7.1.5.tgz", + "integrity": "sha512-iCHfbieL5d1LfOQeeVJEUyD9rTwBcP/fcEbRCfempxTDuqrKpu0AZjLAQHEQa3Yqyj9ORKe2iHfoj4rHLf7xpw==", "dev": true, "requires": { "find-cache-dir": "^1.0.0", "loader-utils": "^1.0.2", - "mkdirp": "^0.5.1", - "util.promisify": "^1.0.0" + "mkdirp": "^0.5.1" } }, "babel-messages": { @@ -3757,6 +3756,50 @@ "babel-core": "^6.26.0", "babel-preset-minify": "^0.3.0", "webpack-sources": "^1.0.1" + }, + "dependencies": { + "babel-core": { + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.1", + "debug": "^2.6.9", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.8", + "slash": "^1.0.0", + "source-map": "^0.5.7" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } } }, "babel-plugin-check-es2015-constants": { @@ -4534,6 +4577,50 @@ "lodash": "^4.17.4", "mkdirp": "^0.5.1", "source-map-support": "^0.4.15" + }, + "dependencies": { + "babel-core": { + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.1", + "debug": "^2.6.9", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.8", + "slash": "^1.0.0", + "source-map": "^0.5.7" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } } }, "babel-runtime": { @@ -4543,6 +4630,13 @@ "requires": { "core-js": "^2.4.0", "regenerator-runtime": "^0.11.0" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + } } }, "babel-template": { @@ -5799,10 +5893,13 @@ "dev": true }, "convert-source-map": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", - "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=", - "dev": true + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } }, "cookiejar": { "version": "2.1.2", @@ -7647,6 +7744,11 @@ } } }, + "fclone": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fclone/-/fclone-1.0.11.tgz", + "integrity": "sha1-EOhdo4v+p/xZk0HClu4ddyZu5kA=" + }, "figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", @@ -13614,6 +13716,12 @@ "integrity": "sha512-piXx9N2WT8hWb7PBbX1glAuJVIkEyUV9F5fMXFINpZ0x3otVOFKKeGmeuiclFJlP/UrgTckyV606VjH2rNK4bw==", "dev": true }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", + "dev": true + }, "regexpp": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", @@ -14242,11 +14350,6 @@ "regenerate": "^1.4.0" } }, - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" - }, "regenerator-transform": { "version": "0.13.3", "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.13.3.tgz", diff --git a/client/package.json b/client/package.json index 34a09ec19..8b5491695 100644 --- a/client/package.json +++ b/client/package.json @@ -18,13 +18,14 @@ "author": "Barry Low ", "license": "AGPL-3.0", "devDependencies": { - "@babel/core": "^7.0.1", + "@babel/core": "^7.1.2", "@babel/plugin-proposal-class-properties": "^7.0.0", "@babel/preset-env": "^7.0.0", "@babel/preset-react": "^7.0.0", "autoprefixer": "^8.6.5", + "babel-core": "^7.0.0-bridge.0", "babel-eslint": "^9.0.0", - "babel-loader": "^8.0.2", + "babel-loader": "^7.1.5", "babel-minify-webpack-plugin": "^0.3.1", "babel-plugin-dynamic-import-node": "^1.2.0", "babel-plugin-syntax-dynamic-import": "^6.18.0", @@ -67,6 +68,7 @@ "workbox-cli": "^3.4.1" }, "dependencies": { + "@advancedalgos/web-components": "0.0.1", "@azure/storage-blob": "^10.0.0-preview", "@material-ui/core": "^3.0.3", "@material-ui/icons": "^3.0.1", diff --git a/client/src/App.js b/client/src/App.js index 1fd6e8cb8..8ed26d1a8 100644 --- a/client/src/App.js +++ b/client/src/App.js @@ -64,7 +64,7 @@ wsClient.onReconnected(() => { }) const httpLink = new HttpLink({ uri: graphqlEndpoint }) -const httpUserLink = new HttpLink({ uri: 'http://104.43.240.255:4000/graphql' }) +const httpUserLink = new HttpLink({ uri: 'https://users-api.advancedalgos.net/graphql' }) const link = split( ({ query }) => { diff --git a/client/src/views/common/ImageUpload.js b/client/src/views/common/ImageUpload.js deleted file mode 100644 index fd4d837bb..000000000 --- a/client/src/views/common/ImageUpload.js +++ /dev/null @@ -1,264 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { Mutation } from 'react-apollo' -import Dropzone from 'react-dropzone' -import { Cropper } from 'react-image-cropper' -import * as Azure from '@azure/storage-blob' -import readAndCompressImage from 'browser-image-resizer' -import { imgSrcToBlob } from 'blob-util' -import { withStyles } from '@material-ui/core/styles' - -import Dialog from '@material-ui/core/Dialog' -import DialogActions from '@material-ui/core/DialogActions' -import DialogContent from '@material-ui/core/DialogContent' -import Button from '@material-ui/core/Button' -import Typography from '@material-ui/core/Typography' - -const styles = theme => ({ - dropzoneContainer: { - display: 'block', - margin: '1em', - height: 200 - }, - dropzone: { - display: 'block', - padding: '3em', - height: 200, - borderWidth: 2, - borderColor: '#999', - backgroundColor: '#eee' - }, - dropzoneText: { - textAlign: 'center' - }, - existingImagePreview: { - width: 100, - height: 100 - }, - cropImageBox: { - width: 350, - height: 350 - } -}) - -// placeholder image if no existing image -const placeHolder = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22288%22%20height%3D%22225%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20288%20225%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_164edaf95ee%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A14pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_164edaf95ee%22%3E%3Crect%20width%3D%22288%22%20height%3D%22225%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%2296.32500076293945%22%20y%3D%22118.8%22%3EAvatar%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E' - -// config for image resize and quality -const imageResizeConfig = { - quality: 0.6, - maxWidth: 200, - maxHeight: 200, - autoRotate: true, - debug: true, - mimeType: 'image/jpeg' -} - -class UploadImage extends Component { - constructor (props) { - super(props) - - this.onDrop = this.onDrop.bind(this) - this.handleCrop = this.handleCrop.bind(this) - this.handleClickOpen = this.handleClickOpen.bind(this) - this.handleClose = this.handleClose.bind(this) - this.handleChangeImage = this.handleChangeImage.bind(this) - this.cancelChangeImage = this.cancelChangeImage.bind(this) - this.saveImage = this.saveImage.bind(this) - - this.state = { - open: false, - change: false, - file: null, - src: null, - crop: { - x: 10, - y: 10, - width: 200, - height: 200 - }, - cropping: false, - cropped: null, - uploaded: false, - image: null, - sasqr: null - } - } - - render () { - const { classes, cropContainer, saveImageConfig, containerName, fileName, existingImage, handleUrl, AZURE_SAS_QUERY } = this.props - let image = null - let resizeImage = null - if (existingImage !== undefined && existingImage !== null) { - image = existingImage - } else { - image = placeHolder - } - - if (saveImageConfig !== undefined && saveImageConfig !== null) { - resizeImage = saveImageConfig - } else { - resizeImage = imageResizeConfig - } - - return ( -
- - {(getAzureSAS, { loading, error, data }) => { - if (this.state.cropping) { - return ( - Cropping... - ) - } else { - return ( - -
- {(this.state.change || this.state.src === null) && ( - - this.onDrop(e, this.state.edit)} - className={classes.dropzone} - multiple={false} - > -

Drag-and-drop image here or click to select
Only *.jpeg and *.png images

-
- -
- )} - {!this.state.change && ( - - Existing Image Preview - - - )} - {this.state.image !== null && ( - Save profile to save image changes! - )} -
- -
- -
- {this.state.src !== null && ( - { this.cropper = ref }} - onImgLoad={this.onImageLoaded} - ratio={1} - /> - )} -
-
- - - - -
-
-
- ) - } - }} -
-
- ) - } - - onDrop (file, editImageType) { - console.log('onDrop', file) - this.onSelectFile(file, editImageType) - this.setState({ - file - }) - } - - async handleCrop (e, getAzureSAS, resizeImage, containerName, fileName, handleUrl) { - e.preventDefault() - const data = await getAzureSAS({ variables: { containerName } }) - - const cropped = this.cropper.crop() - const croppedBlob = await imgSrcToBlob(cropped, 'image/png', 'Anonymous') - let resizedBlob = await readAndCompressImage(croppedBlob, resizeImage) - - return this.setState({ cropped: resizedBlob, cropping: true, open: false }, () => { - this.saveImage(data.getAzureSAS, containerName, fileName, handleUrl) - }) - } - - async saveImage (SASQueryParameters, containerName, fileName, handleUrl) { - const SASurl = `${process.env.AZURE_STORAGE_URL}?${this.state.sasqr}` - - // create Azure BlockBlobURL to save image to - const pipeline = Azure.StorageURL.newPipeline(new Azure.AnonymousCredential()) - const serviceURL = new Azure.ServiceURL(SASurl, pipeline) - - const containerURL = Azure.ContainerURL.fromServiceURL(serviceURL, containerName) - const blockBlobURL = Azure.BlockBlobURL.fromContainerURL(containerURL, fileName) - - await Azure.uploadBrowserDataToBlockBlob(Azure.Aborter.None, this.state.cropped, blockBlobURL) - - const image = `${process.env.AZURE_STORAGE_URL}${containerName}/${fileName}?${Math.random()}` // random number keeps browser from using cached version of existing image - handleUrl(image) // pass image URL up to be save to DB - this.setState({ cropping: false, uploaded: true, change: false, image: image }) - return image - } - - handleChangeImage (e, cropContainer) { - e.preventDefault() - let crop = cropContainer // dimensions of cropContainer passed from above - this.setState({ - change: true, - crop: crop - }) - } - - cancelChangeImage () { - this.setState({ change: false }) - } - - handleClickOpen (e) { - e.preventDefault() - this.setState({ open: true }) - } - - handleClose () { - this.setState({ open: false, src: null, change: false }) - } -} - -UploadImage.propTypes = { - classes: PropTypes.object.isRequired, - cropContainer: PropTypes.object, // { x: 10, y: 10, width: 200, height: 200 } (default) - saveImageConfig: PropTypes.object, // see imageResizeConfig above for default (default) - containerName: PropTypes.string.isRequired, // name of Azure Container image will be saved in - fileName: PropTypes.string.isRequired, // filename to save image as - existingImage: PropTypes.string, // image url of existing image (optional) - handleUrl: PropTypes.func.isRequired, // function that handles uploaded image file url - AZURE_SAS_QUERY: PropTypes.any.isRequired // GraphQL for getting Azure SAS URL -} - -export default withStyles(styles)(UploadImage) diff --git a/client/src/views/common/MessageCard.js b/client/src/views/common/MessageCard.js deleted file mode 100644 index fff952915..000000000 --- a/client/src/views/common/MessageCard.js +++ /dev/null @@ -1,45 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' - -import Typography from '@material-ui/core/Typography' -import { withStyles } from '@material-ui/core/styles' - -import Card from '@material-ui/core/Card' -import CardContent from '@material-ui/core/CardContent' - -const styles = theme => ({ - card: { - display: 'flex', - width: '100%', - margin: '1.5em', - padding: '3em' - }, - cardDetails: { - display: 'flex', - flex: 1 - }, - cardContent: { - flex: 1 - } -}) - -export const MessageCard = ({ classes, message, children }) => ( - -
- - - {message} - - {children} - -
-
-) - -MessageCard.propTypes = { - children: PropTypes.node, - classes: PropTypes.object.isRequired, - message: PropTypes.string -} - -export default withStyles(styles)(MessageCard) diff --git a/client/src/views/common/UploadImage.js b/client/src/views/common/UploadImage.js deleted file mode 100644 index 9aff6ce47..000000000 --- a/client/src/views/common/UploadImage.js +++ /dev/null @@ -1,345 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { Mutation } from 'react-apollo' -import Dropzone from 'react-dropzone' -import { Cropper } from 'react-image-cropper' -import * as Azure from '@azure/storage-blob' -import readAndCompressImage from 'browser-image-resizer' -import { imgSrcToBlob } from 'blob-util' -import { withStyles } from '@material-ui/core/styles' - -import Dialog from '@material-ui/core/Dialog' -import DialogActions from '@material-ui/core/DialogActions' -import DialogContent from '@material-ui/core/DialogContent' -import Button from '@material-ui/core/Button' -import Typography from '@material-ui/core/Typography' - -import MessageCard from './MessageCard' - -import GET_AZURE_SAS from '../../graphql/teams/GetAzureSASMutation' - -const styles = theme => ({ - dropzoneContainer: { - display: 'block', - margin: '1em', - minWidth: 200, - height: 200 - }, - dropzone: { - display: 'block', - padding: '3em', - minWidth: 200, - height: 200, - borderWidth: 2, - borderColor: '#999', - backgroundColor: '#eee' - }, - dropzoneText: { - textAlign: 'center' - }, - cropAvatarPreview: { - width: 100, - height: 100 - }, - cropBannerPreview: { - width: 400, - height: 100 - }, - cropAvatarBox: { - width: 350, - height: 350 - }, - cropBannerBox: { - width: 650, - height: 350 - }, - reactCrop: { - } -}) - -const imageAvatarConfig = { - quality: 0.6, - maxWidth: 200, - maxHeight: 200, - autoRotate: true, - debug: true, - mimeType: 'image/jpeg' -} - -const imageBannerConfig = { - quality: 0.6, - maxWidth: 800, - maxHeight: 200, - autoRotate: true, - debug: true, - mimeType: 'image/jpeg' -} - -class UploadImage extends Component { - constructor (props) { - super(props) - - this.onDrop = this.onDrop.bind(this) - this.handleCrop = this.handleCrop.bind(this) - this.handleClickOpen = this.handleClickOpen.bind(this) - this.handleClose = this.handleClose.bind(this) - this.handleSubmit = this.handleSubmit.bind(this) - this.handleChangeImage = this.handleChangeImage.bind(this) - this.cancelChangeImage = this.cancelChangeImage.bind(this) - this.saveImage = this.saveImage.bind(this) - - this.state = { - open: false, - edit: 'avatar', - change: false, - file: null, - src: null, - crop: { - x: 10, - y: 10, - width: 200, - height: 200 - }, - cropping: false, - cropped: null, - rejected: null, - uploaded: false, - avatarImage: null, - bannerImage: null, - sasqr: null - } - } - - render () { - const { classes, team, authId, handleAvatar, handleBanner } = this.props - let avatar = null - let banner = null - if ((team.profile !== null && team.profile.avatar !== undefined && team.profile.avatar !== null && team.profile.avatar !== 'a') || this.state.avatarImage !== null) { - avatar = this.state.avatarImage !== null ? this.state.avatarImage : `${team.profile.avatar}?${Math.random()}` - } else { - avatar = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22288%22%20height%3D%22225%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20288%20225%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_164edaf95ee%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A14pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_164edaf95ee%22%3E%3Crect%20width%3D%22288%22%20height%3D%22225%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%2296.32500076293945%22%20y%3D%22118.8%22%3EAvatar%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E' - } - if ((team.profile !== null && team.profile.banner !== undefined && team.profile.banner !== null && team.profile.banner !== '') || this.state.bannerImage !== null) { - banner = this.state.bannerImage !== null ? this.state.bannerImage : `${team.profile.banner}?${Math.random()}` - } else { - banner = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22800%22%20height%3D%22200%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Asvg%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23banner_164edaf95ee%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A14pt%20%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%3E%3Ctitle%3EBanner%20Placeholder%3C%2Ftitle%3E%3Crect%20stroke%3D%22null%22%20id%3D%22svg_1%22%20fill%3D%22%2355595c%22%20height%3D%22200%22%20width%3D%22800%22%2F%3E%3Cg%20stroke%3D%22null%22%20id%3D%22banner_164edaf95ee%22%3E%3Ctext%20stroke%3D%22null%22%20id%3D%22svg_3%22%20y%3D%22106.5%22%20x%3D%22367.73438%22%3EBanner%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E' - } - - let handleImage - this.state.edit === 'avatar' ? handleImage = handleAvatar : handleImage = handleBanner - - console.log('UploadImage', team, avatar, banner, authId, this.state) - return ( -
- - {(getAzureSAS, { loading, error, data }) => { - console.log('getAzureSAS: ', error, data) - if (!this.state.uploaded) { - if (data !== undefined && data.getAzureSAS !== null) { - this.saveImage(data.getAzureSAS, team.slug, handleImage) - } - // this.handleSubmit(getAzureSAS, team.slug) - } - if (this.state.cropping) { - return ( -
- -
- ) - } else { - return ( - -
- {(this.state.change || (avatar === null && banner === null && this.state.src === null)) && ( - - this.onDrop(e, this.state.edit)} - className={classes.dropzone} - multiple={false} - > -

Drag-and-drop team {this.state.edit} here or click to select files.
Only *.jpeg and *.png images

-
- -
- )} - {!this.state.change && ( - - Banner Crop Preview - - - )} - {!this.state.change && ( - - AvatarCrop Preview - - - )} - {(this.state.avatarImage !== null || this.state.bannerImage !== null) && ( - Click Update Team to save image changes! - )} -
- -
- -
- {this.state.src !== null && ( - { this.cropper = ref }} - onImgLoad={this.onImageLoaded} - ratio={this.state.edit === 'avatar' ? 1 : 4} - /> - )} -
-
- - - - -
-
-
- ) - } - }} -
-
- ) - } - - onDrop (file, editImageType) { - console.log('onDrop', file) - this.onSelectFile(file, editImageType) - this.setState({ - file - }) - } - - onSelectFile (file, editImageType) { - if (file && file.length > 0) { - const reader = new window.FileReader() - reader.addEventListener( - 'load', - () => - this.setState({ - src: reader.result, - open: true - }), - false - ) - reader.readAsDataURL(file[0]) - } - } - - async handleCrop (e, getAzureSAS, teamSlug, handleImage) { - e.preventDefault() - const data = await getAzureSAS({ variables: { teamSlug } }) - console.log('handleCrop', this.cropper, data) - const cropped = this.cropper.crop() - const croppedBlob = await imgSrcToBlob(cropped, 'image/png', 'Anonymous') - let imageConfig = this.state.edit === 'avatar' ? imageAvatarConfig : imageBannerConfig - let resizedBlob = await readAndCompressImage(croppedBlob, imageConfig) - - return this.setState({ cropped: resizedBlob, cropping: true, open: false }, () => { - this.saveImage(data.getAzureSAS, teamSlug, handleImage) - }) - } - - async handleSubmit (e, getAzureSAS, teamSlug) { - e.preventDefault() - let SAS = await getAzureSAS({ variables: { teamSlug } }) - console.log('handleSubmit: ', await SAS) - return this.setState({ sasqr: SAS }) - } - - async saveImage (SASQueryParameters, teamSlug, handleImage) { - const fileName = this.state.edit === 'avatar' ? `${teamSlug}-avatar.jpg` : `${teamSlug}-banner.jpg` - let SASurl - if (this.state.sasqr === null) { - SASurl = process.env.AZURE_SAS_URL - } else { - SASurl = `${process.env.AZURE_STORAGE_URL}?${this.state.sasqr}` - } - console.log('getAzureSAS2 SASurl: ', SASurl, this.state.sasqr) - - const pipeline = Azure.StorageURL.newPipeline(new Azure.AnonymousCredential()) - const serviceURL = new Azure.ServiceURL(SASurl, pipeline) - - const containerName = teamSlug - const containerURL = Azure.ContainerURL.fromServiceURL(serviceURL, containerName) - const blockBlobURL = Azure.BlockBlobURL.fromContainerURL(containerURL, fileName) - - await Azure.uploadBrowserDataToBlockBlob(Azure.Aborter.None, this.state.cropped, blockBlobURL) - - const image = `${process.env.AZURE_STORAGE_URL}${teamSlug}/${fileName}?${Math.random()}` - handleImage(image) - let avatarImage = this.state.edit === 'avatar' ? image : null - let bannerImage = this.state.edit === 'banner' ? image : null - this.setState({ cropping: false, uploaded: true, change: false, avatarImage: avatarImage, bannerImage: bannerImage }) - return image - } - - handleChangeImage (e, editImageType) { - e.preventDefault() - let crop = editImageType === 'avatar' ? { x: 10, y: 10, width: 200, height: 200 } : { x: 10, y: 10, width: 800, height: 200 } - this.setState({ - edit: editImageType, - change: true, - crop: crop - }) - } - - cancelChangeImage () { - this.setState({ change: false }) - } - - handleClickOpen (e, editImageType) { - e.preventDefault() - this.setState({ - open: true - }) - } - - handleClose () { - this.setState({ open: false, src: null, change: false }) - } -} - -UploadImage.propTypes = { - classes: PropTypes.object.isRequired, - team: PropTypes.object, - authId: PropTypes.string.isRequired, - handleAvatar: PropTypes.func.isRequired, - handleBanner: PropTypes.func.isRequired -} - -export default withStyles(styles)(UploadImage) diff --git a/client/src/views/common/index.js b/client/src/views/common/index.js deleted file mode 100644 index 12cfe68a0..000000000 --- a/client/src/views/common/index.js +++ /dev/null @@ -1,4 +0,0 @@ -import MessageCard from './MessageCard' -import UploadImage from './UploadImage' - -export { MessageCard, UploadImage } diff --git a/client/src/views/dashboard/components/ManageTeamEdit.js b/client/src/views/dashboard/components/ManageTeamEdit.js index de5e9aa83..a069787c4 100644 --- a/client/src/views/dashboard/components/ManageTeamEdit.js +++ b/client/src/views/dashboard/components/ManageTeamEdit.js @@ -13,6 +13,8 @@ import DialogContentText from '@material-ui/core/DialogContentText' import DialogTitle from '@material-ui/core/DialogTitle' import Typography from '@material-ui/core/Typography' +import { MessageCard, ImageUpload } from '@advancedalgos/web-components' + import { getItem } from '../../../utils/local-storage' import UPDATE_TEAM_PROFILE from '../../../graphql/teams/UpdateTeamProfileMutation' @@ -20,8 +22,7 @@ import GET_TEAMS_BY_OWNER from '../../../graphql/teams/GetTeamsByOwnerQuery' import { checkGraphQLError } from '../../../utils/graphql-errors' -import { ImageUpload } from '../../common' -import GET_AZURE_SAS from '../../graphql/teams/GetAzureSASMutation' +import GET_AZURE_SAS from '../../../graphql/teams/GetAzureSASMutation' const styles = theme => ({ dialogContainer: { @@ -62,14 +63,13 @@ export class ManageTeamEdit extends Component { render () { console.log(this.props, this.props.slug) - const { classes, team, authId } = this.props + const { classes, team } = this.props return ( @@ -112,38 +112,45 @@ export class ManageTeamEdit extends Component { Edit Team Details - + {(getAzureSAS, { loading, error, data }) => { + console.log('getAzureSAS: ', loading, error, data) + const AzureStorageUrl = process.env.AZURE_STORAGE_URL + const containerName = team.slug + let AzureSASURL + if (!loading && data !== undefined) { + AzureSASURL = data.getAzureSAS + } else { + getAzureSAS({ variables: { teamSlug: containerName } }) + } + + if (loading || data === undefined) { + return () + } else { + return ( + + + + ) + } }} - AZURE_SAS_QUERY={GET_AZURE_SAS} - /> - + Date: Thu, 4 Oct 2018 01:36:56 -0700 Subject: [PATCH 10/12] Integrate Image Upload Component --- api/src/index.js | 81 +++++-------------- api/src/storage/azure.js | 62 +++++++++++++- client/package-lock.json | 12 +-- client/package.json | 2 +- client/src/views/dashboard/FinancialBeings.js | 2 +- client/src/views/dashboard/TeamMembers.js | 2 +- .../views/dashboard/components/DashTeam.js | 2 +- .../dashboard/components/DashTeamItem.js | 4 +- .../dashboard/components/ManageTeamEdit.js | 57 ++++++++++--- .../dashboard/components/ManageTeamsList.js | 2 +- client/src/views/dashboard/settings.js | 2 +- 11 files changed, 139 insertions(+), 89 deletions(-) diff --git a/api/src/index.js b/api/src/index.js index 5ec890d4b..e6fc19e54 100644 --- a/api/src/index.js +++ b/api/src/index.js @@ -15,14 +15,11 @@ const { validateIdToken } = require('./auth/validateIdToken') const { directiveResolvers } = require('./auth/authDirectives') const Azure = require("@azure/storage-blob") +const { createSASQueryURL } = require('./storage/azure') const azureAccount = process.env.AZURE_STORAGE_ACCOUNT const azureKey = process.env.AZURE_STORAGE_ACCESS_KEY const azureStorageUrl = process.env.AZURE_STORAGE_URL -var debug = require('debug')('http') - , http = require('http') - , name = 'My App'; - const TEAMS_FRAGMENT = require('./graphql/TeamsFragment') const TEAMS_CONNECTIONS_FRAGMENT = require('./graphql/TeamsConnectionsFragment') @@ -77,7 +74,7 @@ const resolvers = { return ctx.db.query.team({ where: { name: name } }, `{ name }`) }, async teamsByOwner(parent, { ownerId }, ctx, info) { - console.log('teamsByOwner: ', ctx.user) + console.log('teamsByOwner: ', ctx) return ctx.db.query.teams({where: { owner: ownerId }, orderBy:'updatedAt_DESC'}, TEAMS_FRAGMENT) }, async owner(parent, args, ctx, info) { @@ -137,60 +134,9 @@ const resolvers = { }) }, async getAzureSAS(parent, { teamSlug }, ctx, info) { - // Create start and expiry times - let today = new Date() - let week = new Date() - week.setDate(today.getDate() + 7) - // Create SharedKeyCredential and attach to pipline - const SKC = new Azure.SharedKeyCredential(azureAccount, azureKey) - const pipeline = Azure.StorageURL.newPipeline(SKC) - // Create container URL - const serviceURL = new Azure.ServiceURL(azureStorageUrl, pipeline) - const containerName = teamSlug - const containerURL = Azure.ContainerURL.fromServiceURL(serviceURL, containerName) - - //list container and check if already exists. - let marker - let containerCheck = null - do { - const listContainersResponse = await serviceURL.listContainersSegment( - Azure.Aborter.None, - marker, - ) - // console.log(`ContainerCheck marker: `, listContainersResponse) - marker = listContainersResponse.marker; - for (const container of listContainersResponse.containerItems) { - // console.log(`ContainerCheck: ${container.name} | ${containerName} | ${marker}`) - if(container.name === containerName){ - containerCheck = container.name - } - } - } while (marker) - - // if container doesn't exist, create one - let newContainer - if(containerCheck === null){ - newContainer = await containerURL.create(Azure.Aborter.None, { access: 'blob' }) - } - // console.log('getAzureSAS container: ', containerURL, containerCheck, newContainer) - - // Set permissions for service, resource types and containers - const SASServicePerms = Azure.AccountSASServices.parse('b') - const SASResourceTypes = Azure.AccountSASResourceTypes.parse('co') - const SASContainerPerms = Azure.ContainerSASPermissions.parse('rwl') - - // Generate SAS url - const SASQueryParameters = Azure.generateAccountSASQueryParameters( - { - version: '2017-11-09', - permissions: SASContainerPerms, - startTime: today, - expiryTime: week, - protocol: 'https', - services: SASServicePerms, - resourceTypes: SASResourceTypes - }, SKC ) - return SASQueryParameters + const SASUrl = createSASQueryURL(teamSlug) + console.log('createSASQueryURL: ', SASUrl) + return SASUrl } } } @@ -235,13 +181,26 @@ server.express.use( server.express.post(server.options.endpoint, (req, res, done) => getUser(req, res, done, db)) -const whitelist = [ +let whitelist = [ 'http://localhost:3000', 'http://localhost:3001', 'http://localhost:3002', 'http://localhost:4000', - 'http://localhost:4002' + 'http://localhost:4002', + 'http://localhost:1337' ] +if(process.env.NODE_ENV === 'production'){ + whitelist = [ + 'https://teams.advancedalgos.net', + 'https://users.advancedalgos.net', + 'https://users-api.advancedalgos.net', + 'https://keyvault.advancedalgos.net', + 'https://keyvault-api.advancedalgos.net', + 'http://localhost:1337' + ] +} + + const corsOptionsDelegate = (req, callback) => { var corsOptions if (whitelist.indexOf(req.header('Origin')) !== -1) { diff --git a/api/src/storage/azure.js b/api/src/storage/azure.js index abeea558e..b718d6ef5 100644 --- a/api/src/storage/azure.js +++ b/api/src/storage/azure.js @@ -1,4 +1,7 @@ -const Azure = require('@azure/storage-blob') +const Azure = require("@azure/storage-blob") +const azureAccount = process.env.AZURE_STORAGE_ACCOUNT +const azureKey = process.env.AZURE_STORAGE_ACCESS_KEY +const azureStorageUrl = process.env.AZURE_STORAGE_URL /* Create Azure Storage pipeline * @accountName: Azure Blob Storage account name (required) - Environment variable @@ -120,6 +123,60 @@ const deleteContainer = async (containerURL) => { return true } +const createSASQueryURL = async (containerName) => { + let today = new Date() + let week = new Date() + week.setDate(today.getDate() + 7) + // Create SharedKeyCredential and attach to pipline + const SKC = new Azure.SharedKeyCredential(azureAccount, azureKey) + const pipeline = Azure.StorageURL.newPipeline(SKC) + // Create container URL + const serviceURL = new Azure.ServiceURL(azureStorageUrl, pipeline) + const containerURL = Azure.ContainerURL.fromServiceURL(serviceURL, containerName) + + //list container and check if already exists. + let marker + let containerCheck = null + do { + const listContainersResponse = await serviceURL.listContainersSegment( + Azure.Aborter.None, + marker, + ) + // console.log(`ContainerCheck marker: `, listContainersResponse) + marker = listContainersResponse.marker; + for (const container of listContainersResponse.containerItems) { + // console.log(`ContainerCheck: ${container.name} | ${containerName} | ${marker}`) + if(container.name === containerName){ + containerCheck = container.name + } + } + } while (marker) + + // if container doesn't exist, create one + let newContainer + if(containerCheck === null){ + newContainer = await containerURL.create(Azure.Aborter.None, { access: 'blob' }) + } + console.log('createSASQueryURL container: ', containerURL, containerCheck, newContainer) + + // Set permissions for service, resource types and containers + const SASContainerPerms = await Azure.AccountSASPermissions.parse('rwlacu') + const SASServicePerms = await Azure.AccountSASServices.parse('b') + const SASResourceTypes = await Azure.AccountSASResourceTypes.parse('co') + console.log('createSASQueryURL permissions: ', SASServicePerms, SASResourceTypes, SASContainerPerms) + // Generate SAS url + const SASQueryParameters = await Azure.generateBlobSASQueryParameters( + { + version: '2017-11-09', + protocol: 'https,http', + expiryTime: week, + permissions: 'rwlac', + containerName: containerName + }, SKC ) + console.log('createSASQueryURL SASQueryParameters: ', containerURL.url, SASQueryParameters) + return SASQueryParameters +} + module.exports = { createStoragePipline, createServiceURL, @@ -129,5 +186,6 @@ module.exports = { uploadBlob, downloadBlob, deleteBlob, - deleteContainer + deleteContainer, + createSASQueryURL } diff --git a/client/package-lock.json b/client/package-lock.json index 4a43e9c8d..b1ce31784 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -5,9 +5,9 @@ "requires": true, "dependencies": { "@advancedalgos/web-components": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/@advancedalgos/web-components/-/web-components-0.0.1.tgz", - "integrity": "sha512-RDi7oR5oY4HbcXdzTDhtgYVHRO+/ApBMossZ/UkCrI2lYAWhQaq4mYaKaJ2mtVKnwV1BkeOLFE6rbPZwIkhXJA==", + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@advancedalgos/web-components/-/web-components-0.0.5.tgz", + "integrity": "sha512-mgaS2AwtVcbP0W8/Rk95TNFTrDmp21rDmoqrO177WGLCcTH5n3rHisQtrdVaEvLiUGoZ/8us/GeclljVJP1edw==", "requires": { "@azure/storage-blob": "^10.1.0-preview", "@material-ui/core": "^3.0.3", @@ -35,9 +35,9 @@ } }, "@types/node": { - "version": "9.6.32", - "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.32.tgz", - "integrity": "sha512-5+L3wQ+FHoQ589EaH6rYICleuj8gnunq+1CJkM9fxklirErIOv+kxm3s/vecYnpJOYnFowE5uUizcb3hgjHUug==" + "version": "9.6.34", + "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.34.tgz", + "integrity": "sha512-PzJpSs2afoYqBA4yLBgaKUdZRk8+1yvkxcUBW6958h4vYOC+pc4k4C+QmQ6AO5Pt7uA4EIIboFog6YNCuITD0g==" }, "apollo-cache": { "version": "1.1.17", diff --git a/client/package.json b/client/package.json index 8b5491695..c40d1672e 100644 --- a/client/package.json +++ b/client/package.json @@ -68,7 +68,7 @@ "workbox-cli": "^3.4.1" }, "dependencies": { - "@advancedalgos/web-components": "0.0.1", + "@advancedalgos/web-components": "0.0.5", "@azure/storage-blob": "^10.0.0-preview", "@material-ui/core": "^3.0.3", "@material-ui/icons": "^3.0.1", diff --git a/client/src/views/dashboard/FinancialBeings.js b/client/src/views/dashboard/FinancialBeings.js index be6ac2aba..c4d13ee80 100644 --- a/client/src/views/dashboard/FinancialBeings.js +++ b/client/src/views/dashboard/FinancialBeings.js @@ -4,7 +4,7 @@ import PropTypes from 'prop-types' import Typography from '@material-ui/core/Typography' import { withStyles } from '@material-ui/core/styles' -import { MessageCard } from '../common/' +import { MessageCard } from '@advancedalgos/web-components' const styles = theme => ({ tableContainer: { diff --git a/client/src/views/dashboard/TeamMembers.js b/client/src/views/dashboard/TeamMembers.js index 8d86d4155..1bbb78295 100644 --- a/client/src/views/dashboard/TeamMembers.js +++ b/client/src/views/dashboard/TeamMembers.js @@ -4,7 +4,7 @@ import PropTypes from 'prop-types' import Typography from '@material-ui/core/Typography' import { withStyles } from '@material-ui/core/styles' -import { MessageCard } from '../common/' +import { MessageCard } from '@advancedalgos/web-components' const styles = theme => ({ tableContainer: { diff --git a/client/src/views/dashboard/components/DashTeam.js b/client/src/views/dashboard/components/DashTeam.js index 1f6fc42c7..cc2f3b1b4 100644 --- a/client/src/views/dashboard/components/DashTeam.js +++ b/client/src/views/dashboard/components/DashTeam.js @@ -11,7 +11,7 @@ import GET_TEAMS_BY_OWNER from '../../../graphql/teams/GetTeamsByOwnerQuery' import DashTeamItem from './DashTeamItem' import CreateTeamDialog from './CreateTeamDialog' -import { MessageCard } from '../../common/' +import { MessageCard } from '@advancedalgos/web-components' import { isDefined, isString } from '../../../utils/js-helpers' import { getItem } from '../../../utils/local-storage' diff --git a/client/src/views/dashboard/components/DashTeamItem.js b/client/src/views/dashboard/components/DashTeamItem.js index e917b9006..db7f1b481 100644 --- a/client/src/views/dashboard/components/DashTeamItem.js +++ b/client/src/views/dashboard/components/DashTeamItem.js @@ -35,13 +35,15 @@ const styles = theme => ({ } }) +const placeholder = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22288%22%20height%3D%22225%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20288%20225%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_164edaf95ee%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A14pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_164edaf95ee%22%3E%3Crect%20width%3D%22288%22%20height%3D%22225%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%2296.32500076293945%22%20y%3D%22118.8%22%3EThumbnail%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E' // eslint-disable-line max-len + export const DashTeamItem = ({ classes, team }) => (
diff --git a/client/src/views/dashboard/components/ManageTeamEdit.js b/client/src/views/dashboard/components/ManageTeamEdit.js index a069787c4..900dfa62f 100644 --- a/client/src/views/dashboard/components/ManageTeamEdit.js +++ b/client/src/views/dashboard/components/ManageTeamEdit.js @@ -15,8 +15,6 @@ import Typography from '@material-ui/core/Typography' import { MessageCard, ImageUpload } from '@advancedalgos/web-components' -import { getItem } from '../../../utils/local-storage' - import UPDATE_TEAM_PROFILE from '../../../graphql/teams/UpdateTeamProfileMutation' import GET_TEAMS_BY_OWNER from '../../../graphql/teams/GetTeamsByOwnerQuery' @@ -51,6 +49,7 @@ export class ManageTeamEdit extends Component { const description = props.team.profile.description || '' const avatar = props.team.profile.avatar || '' const banner = props.team.profile.banner || '' + this.authId = props.authId this.state = { open: false, @@ -62,14 +61,15 @@ export class ManageTeamEdit extends Component { } render () { - console.log(this.props, this.props.slug) + console.log('ManageTeamEdit ', this.props, this.props.slug, this.props.team) const { classes, team } = this.props return ( @@ -114,7 +114,7 @@ export class ManageTeamEdit extends Component { {(getAzureSAS, { loading, error, data }) => { - console.log('getAzureSAS: ', loading, error, data) + console.log('getAzureSAS: ', loading, error, data, team.profile) const AzureStorageUrl = process.env.AZURE_STORAGE_URL const containerName = team.slug let AzureSASURL @@ -124,16 +124,46 @@ export class ManageTeamEdit extends Component { getAzureSAS({ variables: { teamSlug: containerName } }) } + let avatar = null + if (this.state.avatar !== null) avatar = this.state.avatar + if (team.profile !== null && team.profile.avatar !== undefined && team.profile.avatar !== null) avatar = team.profile.avatar + + let banner = null + if (this.state.banner !== null) banner = this.state.banner + if (team.profile !== null && team.profile.banner !== undefined && team.profile.banner !== null) banner = team.profile.banner + console.log('team images: ', avatar, banner) + if (loading || data === undefined) { return () } else { return ( + ) @@ -230,24 +262,22 @@ export class ManageTeamEdit extends Component { handleAvatar (avatarUrl) { console.log('handleAvatar: ', avatarUrl) - this.setState({ avatar: avatarUrl }) + this.setState({ avatar: `${avatarUrl}?${Math.random()}` }) } handleBanner (bannerUrl) { console.log('handleBanner: ', bannerUrl) - this.setState({ banner: bannerUrl }) + this.setState({ banner: `${bannerUrl}?${Math.random()}` }) } async handleSubmit (e, updateTeamProfile, slug) { console.log('handleSubmit: ', this.state) e.preventDefault() - const currentUser = await getItem('user') - let authId = JSON.parse(currentUser) - authId = authId.authId + await updateTeamProfile({ variables: { slug, - owner: authId, + owner: this.authId, description: this.state.description, motto: this.state.motto, avatar: this.state.avatar, @@ -261,7 +291,8 @@ export class ManageTeamEdit extends Component { ManageTeamEdit.propTypes = { classes: PropTypes.object.isRequired, slug: PropTypes.string.isRequired, - team: PropTypes.object + team: PropTypes.object, + authId: PropTypes.string.isRequired } export default withStyles(styles)(ManageTeamEdit) diff --git a/client/src/views/dashboard/components/ManageTeamsList.js b/client/src/views/dashboard/components/ManageTeamsList.js index d72921486..816a4db88 100644 --- a/client/src/views/dashboard/components/ManageTeamsList.js +++ b/client/src/views/dashboard/components/ManageTeamsList.js @@ -4,6 +4,7 @@ import { Query } from 'react-apollo' import { withStateHandlers, lifecycle, compose } from 'recompose' import Grid from '@material-ui/core/Grid' +import { MessageCard } from '@advancedalgos/web-components' import { withStyles } from '@material-ui/core/styles' import { isDefined, isString } from '../../../utils/js-helpers' @@ -13,7 +14,6 @@ import GET_TEAMS_BY_OWNER from '../../../graphql/teams/GetTeamsByOwnerQuery' import ManageTeamsItem from './ManageTeamsItem' import CreateTeamDialog from './CreateTeamDialog' -import { MessageCard } from '../../common/' const styles = theme => ({ heroContent: { diff --git a/client/src/views/dashboard/settings.js b/client/src/views/dashboard/settings.js index d28df4d79..f427a01e2 100644 --- a/client/src/views/dashboard/settings.js +++ b/client/src/views/dashboard/settings.js @@ -4,7 +4,7 @@ import PropTypes from 'prop-types' import Typography from '@material-ui/core/Typography' import { withStyles } from '@material-ui/core/styles' -import { MessageCard } from '../common/' +import { MessageCard } from '@advancedalgos/web-components' const styles = theme => ({ tableContainer: { From f50bca125ded72326dba4791c358238ef0fddc43 Mon Sep 17 00:00:00 2001 From: Barry Low Date: Thu, 4 Oct 2018 01:57:04 -0700 Subject: [PATCH 11/12] Incorporate team images in public team profiles --- .../views/teams/components/TeamsDetails.js | 107 +++++++++++------- .../src/views/teams/components/TeamsItem.js | 82 ++++++++------ 2 files changed, 115 insertions(+), 74 deletions(-) diff --git a/client/src/views/teams/components/TeamsDetails.js b/client/src/views/teams/components/TeamsDetails.js index 5383895b6..9351197d7 100644 --- a/client/src/views/teams/components/TeamsDetails.js +++ b/client/src/views/teams/components/TeamsDetails.js @@ -42,49 +42,76 @@ const styles = theme => ({ maxWidth: 600, margin: '0 auto', padding: `${theme.spacing.unit * 8}px 0 ${theme.spacing.unit * 6}px` + }, + banner: { + maxWidth: '100%', + height: 'auto', + margin: '0 auto' + }, + avatar: { + maxWidth: 100, + height: 100, + margin: '0 1em 1em 0', + float: 'left' } }) -const TeamsDetails = ({ classes, team }) => ( - - -
- -
- ← Back to all teams - - {team.name} - - - {team.createdAt} - - - Motto: {team.profile.motto} - - - Description: {team.profile.description} - - - Members: {team.members.length} - - - Team Admin:  - {team.members.map(member => { - if (member.role === 'OWNER' || member.role === 'ADMIN') { - return member.member.alias - } - })} - -
-
-
-
-) +const TeamsDetails = ({ classes, team }) => { + let avatar + if (team.profile.avatar !== undefined && team.profile.avatar !== 'a') { + avatar = team.profile.avatar + } else { + avatar = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22288%22%20height%3D%22225%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20288%20225%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_164edaf95ee%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A14pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_164edaf95ee%22%3E%3Crect%20width%3D%22288%22%20height%3D%22225%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%2296.32500076293945%22%20y%3D%22118.8%22%3EThumbnail%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E' + } + let banner + if (team.profile.banner !== undefined && team.profile.banner !== 'a') { + banner = team.profile.banner + } else { + banner = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22288%22%20height%3D%22225%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20288%20225%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_164edaf95ee%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A14pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_164edaf95ee%22%3E%3Crect%20width%3D%22288%22%20height%3D%22225%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%2296.32500076293945%22%20y%3D%22118.8%22%3EThumbnail%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E' + } + return ( + + +
+ +
+ ← Back to all teams + {team.name} + {team.name} + + {team.name} + + + {team.createdAt} + + + Motto: {team.profile.motto} + + + Description: {team.profile.description} + + + Members: {team.members.length} + + + Team Admin:  + {team.members.map(member => { + if (member.role === 'OWNER' || member.role === 'ADMIN') { + return member.member.alias + } + })} + +
+
+
+
+ ) +} TeamsDetails.propTypes = { classes: PropTypes.object.isRequired, team: PropTypes.object.isRequired diff --git a/client/src/views/teams/components/TeamsItem.js b/client/src/views/teams/components/TeamsItem.js index 9f0d78bf9..b82242757 100644 --- a/client/src/views/teams/components/TeamsItem.js +++ b/client/src/views/teams/components/TeamsItem.js @@ -12,40 +12,54 @@ import Typography from '@material-ui/core/Typography' import { Link } from 'react-router-dom' -export const TeamsItem = ({ classes, team }) => ( - - - - - - - {team.name} - - - {team.createdAt} - - - Members: 1 - - - Now recruiting! - - - - - - - - - - -) +export const TeamsItem = ({ classes, team }) => { + /* let avatar + if (team.profile.avatar !== undefined && team.profile.avatar !== 'a') { + avatar = team.profile.avatar + } else { + avatar = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22288%22%20height%3D%22225%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20288%20225%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_164edaf95ee%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A14pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_164edaf95ee%22%3E%3Crect%20width%3D%22288%22%20height%3D%22225%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%2296.32500076293945%22%20y%3D%22118.8%22%3EThumbnail%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E' + } */ + let banner + if (team.profile.banner !== undefined && team.profile.banner !== null) { + banner = team.profile.banner + } else { + banner = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22288%22%20height%3D%22225%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20288%20225%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_164edaf95ee%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A14pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_164edaf95ee%22%3E%3Crect%20width%3D%22288%22%20height%3D%22225%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%2296.32500076293945%22%20y%3D%22118.8%22%3EThumbnail%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E' + } + return ( + + + + + + + {team.name} + + + {team.createdAt} + + + Members: 1 + + + Now recruiting! + + + + + + + + + + + ) +} TeamsItem.propTypes = { classes: PropTypes.object.isRequired, From f7ce9b550261f51428bfabb296959e34a2024f42 Mon Sep 17 00:00:00 2001 From: Barry Low Date: Thu, 4 Oct 2018 02:02:33 -0700 Subject: [PATCH 12/12] debug teams details --- api/src/graphql/TeamsConnectionsFragment.js | 1 + client/src/views/teams/components/TeamsDetails.js | 8 -------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/api/src/graphql/TeamsConnectionsFragment.js b/api/src/graphql/TeamsConnectionsFragment.js index 89a22a8c4..37efc6c13 100644 --- a/api/src/graphql/TeamsConnectionsFragment.js +++ b/api/src/graphql/TeamsConnectionsFragment.js @@ -15,6 +15,7 @@ const TEAMS_CONNECTIONS_FRAGMENT = gql`{ createdAt profile { avatar + banner description motto updatedAt diff --git a/client/src/views/teams/components/TeamsDetails.js b/client/src/views/teams/components/TeamsDetails.js index 9351197d7..5de7a2765 100644 --- a/client/src/views/teams/components/TeamsDetails.js +++ b/client/src/views/teams/components/TeamsDetails.js @@ -98,14 +98,6 @@ const TeamsDetails = ({ classes, team }) => { Members: {team.members.length} - - Team Admin:  - {team.members.map(member => { - if (member.role === 'OWNER' || member.role === 'ADMIN') { - return member.member.alias - } - })} -