diff --git a/registry/client/package-lock.json b/registry/client/package-lock.json
index 74c4ef00..4f5a318d 100644
--- a/registry/client/package-lock.json
+++ b/registry/client/package-lock.json
@@ -33,7 +33,6 @@
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
"integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==",
- "dev": true,
"requires": {
"@babel/highlight": "^7.8.3"
}
@@ -243,7 +242,6 @@
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz",
"integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==",
- "dev": true,
"requires": {
"@babel/types": "^7.8.3"
}
@@ -334,8 +332,7 @@
"@babel/helper-validator-identifier": {
"version": "7.9.0",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz",
- "integrity": "sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw==",
- "dev": true
+ "integrity": "sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw=="
},
"@babel/helper-wrap-function": {
"version": "7.8.3",
@@ -364,7 +361,6 @@
"version": "7.9.0",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz",
"integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==",
- "dev": true,
"requires": {
"@babel/helper-validator-identifier": "^7.9.0",
"chalk": "^2.0.0",
@@ -1117,18 +1113,70 @@
"version": "7.9.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.0.tgz",
"integrity": "sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng==",
- "dev": true,
"requires": {
"@babel/helper-validator-identifier": "^7.9.0",
"lodash": "^4.17.13",
"to-fast-properties": "^2.0.0"
}
},
+ "@emotion/cache": {
+ "version": "10.0.29",
+ "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.29.tgz",
+ "integrity": "sha512-fU2VtSVlHiF27empSbxi1O2JFdNWZO+2NFHfwO0pxgTep6Xa3uGb+3pVKfLww2l/IBGLNEZl5Xf/++A4wAYDYQ==",
+ "requires": {
+ "@emotion/sheet": "0.9.4",
+ "@emotion/stylis": "0.8.5",
+ "@emotion/utils": "0.11.3",
+ "@emotion/weak-memoize": "0.2.5"
+ }
+ },
"@emotion/hash": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
"integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow=="
},
+ "@emotion/memoize": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz",
+ "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw=="
+ },
+ "@emotion/serialize": {
+ "version": "0.11.16",
+ "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.11.16.tgz",
+ "integrity": "sha512-G3J4o8by0VRrO+PFeSc3js2myYNOXVJ3Ya+RGVxnshRYgsvErfAOglKAiy1Eo1vhzxqtUvjCyS5gtewzkmvSSg==",
+ "requires": {
+ "@emotion/hash": "0.8.0",
+ "@emotion/memoize": "0.7.4",
+ "@emotion/unitless": "0.7.5",
+ "@emotion/utils": "0.11.3",
+ "csstype": "^2.5.7"
+ }
+ },
+ "@emotion/sheet": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.4.tgz",
+ "integrity": "sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA=="
+ },
+ "@emotion/stylis": {
+ "version": "0.8.5",
+ "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz",
+ "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ=="
+ },
+ "@emotion/unitless": {
+ "version": "0.7.5",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
+ "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="
+ },
+ "@emotion/utils": {
+ "version": "0.11.3",
+ "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz",
+ "integrity": "sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw=="
+ },
+ "@emotion/weak-memoize": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz",
+ "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA=="
+ },
"@hapi/address": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz",
@@ -1424,6 +1472,11 @@
"integrity": "sha512-rouEWBImiRaSJsVA+ITTFM6ZxibuAlTuNOCyxVbwreu6k6+ujs7DfnU9o+PShFhET78pMBl3eH+AGSI5eOTkPA==",
"dev": true
},
+ "@types/parse-json": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
+ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA=="
+ },
"@types/prop-types": {
"version": "15.7.3",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz",
@@ -1979,6 +2032,38 @@
"object.assign": "^4.1.0"
}
},
+ "babel-plugin-emotion": {
+ "version": "10.0.33",
+ "resolved": "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-10.0.33.tgz",
+ "integrity": "sha512-bxZbTTGz0AJQDHm8k6Rf3RQJ8tX2scsfsRyKVgAbiUPUNIRtlK+7JxP+TAd1kRLABFxe0CFm2VdK4ePkoA9FxQ==",
+ "requires": {
+ "@babel/helper-module-imports": "^7.0.0",
+ "@emotion/hash": "0.8.0",
+ "@emotion/memoize": "0.7.4",
+ "@emotion/serialize": "^0.11.16",
+ "babel-plugin-macros": "^2.0.0",
+ "babel-plugin-syntax-jsx": "^6.18.0",
+ "convert-source-map": "^1.5.0",
+ "escape-string-regexp": "^1.0.5",
+ "find-root": "^1.1.0",
+ "source-map": "^0.5.7"
+ }
+ },
+ "babel-plugin-macros": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz",
+ "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==",
+ "requires": {
+ "@babel/runtime": "^7.7.2",
+ "cosmiconfig": "^6.0.0",
+ "resolve": "^1.12.0"
+ }
+ },
+ "babel-plugin-syntax-jsx": {
+ "version": "6.18.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
+ "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY="
+ },
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
@@ -2389,6 +2474,11 @@
"unset-value": "^1.0.0"
}
},
+ "callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
+ },
"camel-case": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz",
@@ -2415,7 +2505,6 @@
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
@@ -2702,7 +2791,6 @@
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz",
"integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==",
- "dev": true,
"requires": {
"safe-buffer": "~5.1.1"
}
@@ -3047,6 +3135,18 @@
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
"dev": true
},
+ "cosmiconfig": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
+ "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
+ "requires": {
+ "@types/parse-json": "^4.0.0",
+ "import-fresh": "^3.1.0",
+ "parse-json": "^5.0.0",
+ "path-type": "^4.0.0",
+ "yaml": "^1.7.2"
+ }
+ },
"create-ecdh": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz",
@@ -3057,6 +3157,17 @@
"elliptic": "^6.0.0"
}
},
+ "create-emotion": {
+ "version": "10.0.27",
+ "resolved": "https://registry.npmjs.org/create-emotion/-/create-emotion-10.0.27.tgz",
+ "integrity": "sha512-fIK73w82HPPn/RsAij7+Zt8eCE8SptcJ3WoRMfxMtjteYxud8GDTKKld7MYwAX2TVhrw29uR1N/bVGxeStHILg==",
+ "requires": {
+ "@emotion/cache": "^10.0.27",
+ "@emotion/serialize": "^0.11.15",
+ "@emotion/sheet": "0.9.4",
+ "@emotion/utils": "0.11.3"
+ }
+ },
"create-hash": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
@@ -3360,6 +3471,11 @@
"resolved": "https://registry.npmjs.org/diacritic/-/diacritic-0.0.2.tgz",
"integrity": "sha1-/CqIe1pbwKCoVPthTHwvIJBh7gQ="
},
+ "diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A=="
+ },
"diffie-hellman": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
@@ -3556,6 +3672,15 @@
"integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
"dev": true
},
+ "emotion": {
+ "version": "10.0.27",
+ "resolved": "https://registry.npmjs.org/emotion/-/emotion-10.0.27.tgz",
+ "integrity": "sha512-2xdDzdWWzue8R8lu4G76uWX5WhyQuzATon9LmNeCy/2BHVC6dsEpfhN1a0qhELgtDVdjyEA6J8Y/VlI5ZnaH0g==",
+ "requires": {
+ "babel-plugin-emotion": "^10.0.27",
+ "create-emotion": "^10.0.27"
+ }
+ },
"encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
@@ -3609,6 +3734,14 @@
"prr": "~1.0.1"
}
},
+ "error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "requires": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
"es-abstract": {
"version": "1.17.5",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz",
@@ -3657,8 +3790,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-scope": {
"version": "4.0.3",
@@ -4182,6 +4314,11 @@
"pkg-dir": "^3.0.0"
}
},
+ "find-root": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
+ "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
+ },
"find-up": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
@@ -5052,8 +5189,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-symbols": {
"version": "1.0.1",
@@ -5421,6 +5557,22 @@
"integrity": "sha512-QPMrUVdhTXUII2xcq1pGqXJvIz7qb77TlY9eejQdZJaE2bKMBMBtjkVbDTgovieV591tfZ9fSr6ejCWxzX/gzw==",
"dev": true
},
+ "import-fresh": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz",
+ "integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==",
+ "requires": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "dependencies": {
+ "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=="
+ }
+ }
+ },
"import-local": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz",
@@ -5563,6 +5715,11 @@
"integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==",
"dev": true
},
+ "is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
+ },
"is-binary-path": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
@@ -5825,6 +5982,11 @@
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
"dev": true
},
+ "json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
+ },
"json-schema": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
@@ -6015,6 +6177,11 @@
"leven": "^3.1.0"
}
},
+ "lines-and-columns": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz",
+ "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA="
+ },
"loader-runner": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz",
@@ -6163,6 +6330,11 @@
}
}
},
+ "memoize-one": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz",
+ "integrity": "sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA=="
+ },
"memory-fs": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
@@ -6941,6 +7113,14 @@
"no-case": "^2.2.0"
}
},
+ "parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "requires": {
+ "callsites": "^3.0.0"
+ }
+ },
"parse-asn1": {
"version": "5.1.5",
"resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz",
@@ -6955,6 +7135,17 @@
"safe-buffer": "^5.1.1"
}
},
+ "parse-json": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz",
+ "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==",
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ }
+ },
"parse-passwd": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
@@ -7012,8 +7203,7 @@
"path-parse": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
- "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
- "dev": true
+ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
},
"path-to-regexp": {
"version": "1.8.0",
@@ -7026,8 +7216,7 @@
"path-type": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
- "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
- "dev": true
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="
},
"pbkdf2": {
"version": "3.0.17",
@@ -7583,6 +7772,19 @@
"redux-saga": "^1.0.0"
}
},
+ "react-diff-viewer": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/react-diff-viewer/-/react-diff-viewer-3.1.1.tgz",
+ "integrity": "sha512-rmvwNdcClp6ZWdS11m1m01UnBA4OwYaLG/li0dB781e/bQEzsGyj+qewVd6W5ztBwseQ72pO7nwaCcq5jnlzcw==",
+ "requires": {
+ "classnames": "^2.2.6",
+ "create-emotion": "^10.0.14",
+ "diff": "^4.0.1",
+ "emotion": "^10.0.14",
+ "memoize-one": "^5.0.4",
+ "prop-types": "^15.6.2"
+ }
+ },
"react-dom": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz",
@@ -7985,7 +8187,6 @@
"version": "1.15.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz",
"integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==",
- "dev": true,
"requires": {
"path-parse": "^1.0.6"
}
@@ -8100,8 +8301,7 @@
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
- "dev": true
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"safe-regex": {
"version": "1.1.0",
@@ -8494,8 +8694,7 @@
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
- "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
- "dev": true
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
},
"source-map-resolve": {
"version": "0.5.3",
@@ -8911,7 +9110,6 @@
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "dev": true,
"requires": {
"has-flag": "^3.0.0"
}
@@ -9066,8 +9264,7 @@
"to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
- "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
- "dev": true
+ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
},
"to-object-path": {
"version": "0.3.0",
@@ -10011,6 +10208,11 @@
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"dev": true
},
+ "yaml": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz",
+ "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg=="
+ },
"yargs": {
"version": "13.2.4",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz",
diff --git a/registry/client/package.json b/registry/client/package.json
index 9b503a2f..2f283022 100755
--- a/registry/client/package.json
+++ b/registry/client/package.json
@@ -44,6 +44,7 @@
"ra-data-simple-rest": "^3.9.3",
"react": "^16.9.0",
"react-admin": "^3.9.5",
+ "react-diff-viewer": "^3.1.1",
"react-dom": "^16.9.0"
}
}
diff --git a/registry/client/src/authEntities/dataTransform.js b/registry/client/src/authEntities/dataTransform.js
new file mode 100644
index 00000000..11b30075
--- /dev/null
+++ b/registry/client/src/authEntities/dataTransform.js
@@ -0,0 +1,13 @@
+import {setOperations} from "../dataProvider";
+
+export function transformGet(entity) {
+
+}
+
+export function transformSet(entity, operation) {
+ if (operation === setOperations.update) {
+ delete entity.id;
+ delete entity.identifier;
+ delete entity.provider;
+ }
+}
diff --git a/registry/client/src/dataProvider.js b/registry/client/src/dataProvider.js
index 8ca31080..4b3a659d 100755
--- a/registry/client/src/dataProvider.js
+++ b/registry/client/src/dataProvider.js
@@ -6,11 +6,17 @@ import * as apps from './apps/dataTransform';
import * as templates from './templates/dataTransform';
import * as appRoutes from './appRoutes/dataTransform';
import * as settings from './settings/dataTransform';
+import * as authEntities from './authEntities/dataTransform';
import httpClient from './httpClient';
const dataProvider = simpleRestProvider('/api/v1', httpClient);
+export const setOperations = Object.freeze({
+ create: 'create',
+ update: 'update',
+});
+
const myDataProvider = {
...dataProvider,
getList: (resource, params) => {
@@ -42,7 +48,7 @@ const myDataProvider = {
update: (resource, params) => {
params.id = encodeURIComponent(params.id);
- transformSetter(resource, params.data);
+ transformSetter(resource, params.data, setOperations.update);
delete params.data.name;
return dataProvider.update(resource, params).then(v => {
@@ -54,7 +60,7 @@ const myDataProvider = {
transformSetter(resource, params.data);
return dataProvider.create(resource, params).then(v => {
- transformGetter(resource, v.data);
+ transformGetter(resource, v.data, setOperations.create);
return v;
});
},
@@ -94,26 +100,32 @@ function transformGetter(resource, data) {
case 'settings':
settings.transformGet(data);
break;
+ case 'auth_entities':
+ authEntities.transformGet(data);
+ break;
default:
}
}
-function transformSetter(resource, data) {
+function transformSetter(resource, data, operation) {
switch (resource) {
case 'app':
- apps.transformSet(data);
+ apps.transformSet(data, operation);
break;
case 'shared_props':
- sharedProps.transformSet(data);
+ sharedProps.transformSet(data, operation);
break;
case 'template':
- templates.transformSet(data);
+ templates.transformSet(data, operation);
break;
case 'route':
- appRoutes.transformSet(data);
+ appRoutes.transformSet(data, operation);
break;
case 'settings':
- settings.transformSet(data);
+ settings.transformSet(data, operation);
+ break;
+ case 'auth_entities':
+ authEntities.transformSet(data, operation);
break;
default:
}
diff --git a/registry/client/src/index.js b/registry/client/src/index.js
index 36483b81..208839bb 100755
--- a/registry/client/src/index.js
+++ b/registry/client/src/index.js
@@ -13,6 +13,7 @@ import templates from './templates';
import appRoutes from './appRoutes';
import authEntities from './authEntities';
import settings from './settings';
+import versioning from './versioning';
render(
,
,
,
+ ,
]}
,
document.getElementById('root')
diff --git a/registry/client/src/utils/json.js b/registry/client/src/utils/json.js
new file mode 100644
index 00000000..70319d01
--- /dev/null
+++ b/registry/client/src/utils/json.js
@@ -0,0 +1,31 @@
+
+import _ from 'lodash/fp';
+
+/**
+ * Original source code was taken from {@link https://github.com/prototypejs/prototype/blob/5fddd3e/src/prototype/lang/string.js#L702}
+ */
+const isJSON = (str) => {
+ if (/^\s*$/.test(str)) return false;
+
+ str = str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@');
+ str = str.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
+ str = str.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
+
+ return (/^[\],:{}\s]*$/).test(str);
+};
+
+const parse = (value) => {
+ if (_.isString(value) && isJSON(value)) {
+ return JSON.parse(value);
+ }
+
+ return value;
+}
+
+export function parseJSON(value) {
+ return _.cond([
+ [_.isArray, _.map(_.mapValues(parseJSON))],
+ [_.isObject, _.mapValues(parseJSON)],
+ [_.stubTrue, parse]
+ ])(value);
+};
diff --git a/registry/client/src/versioning/List.js b/registry/client/src/versioning/List.js
new file mode 100644
index 00000000..7561b8b7
--- /dev/null
+++ b/registry/client/src/versioning/List.js
@@ -0,0 +1,134 @@
+import React, {Children, cloneElement, useState, useCallback} from 'react';
+import {makeStyles} from '@material-ui/core';
+import ReactDiffViewer from 'react-diff-viewer';
+import {
+ List,
+ Datagrid,
+ Button,
+ TextField,
+ SelectInput,
+ TextInput,
+ Filter,
+ FunctionField,
+ useRefresh,
+ useNotify
+} from 'react-admin';
+import {parseJSON} from '../utils/json';
+import {SettingsBackupRestore} from "@material-ui/icons";
+
+const ListActionsToolbar = ({children, ...props}) => {
+ const classes = makeStyles({
+ toolbar: {
+ alignItems: 'center',
+ display: 'flex',
+ marginTop: -1,
+ marginBottom: -1,
+ },
+ });
+
+ return (
+
+ {Children.map(children, button => cloneElement(button, props))}
+
+ );
+};
+
+const MyFilter = (props) => (
+
+
+
+
+
+);
+
+const beautifyJson = v => JSON.stringify(parseJSON(JSON.parse(v)), null, 2);
+
+
+const MyPanel = ({ id, record, resource }) => (
+
+);
+
+const RevertButton = ({
+ record,
+ ...rest
+}) => {
+ const [disabled, setDisabled] = useState(false);
+ const refresh = useRefresh();
+ const notify = useNotify();
+
+ const handleClick = useCallback(() => {
+ if (confirm(`Are you sure that you want to revert change with ID "${record.id}"?`)) {
+ setDisabled(true);
+ fetch(`/api/v1/versioning/${record.id}/revert`, {method: 'POST'}).then(async res => {
+ setDisabled(false);
+ if (!res.ok) {
+ if (res.status < 500) {
+ const resInfo = await res.json();
+ return notify(resInfo.reason, 'error', { smart_count: 1 });
+ }
+ throw new Error(`Unexpected network error. Returned code "${res.status}"`);
+ }
+ notify('Change was successfully reverted', 'info', { smart_count: 1 });
+ refresh();
+ }).catch(err => {
+ setDisabled(false);
+ notify('Oops! Something went wrong.', 'error', { smart_count: 1 });
+ console.error(err);
+ });
+ }
+ }, []);
+
+ return (
+
+ );
+};
+
+const PostList = props => {
+ return (
+ }
+ exporter={false}
+ perPage={25}
+ bulkActionButtons={false}
+ >
+ } >
+
+
+
+ record.data && record.data_after ? 'UPDATE' : record.data ? 'DELETE' : 'CREATE'} />
+
+ new Date(record.created_at * 1000).toLocaleString()} />
+
+
+
+
+
+ );
+};
+
+export default PostList;
diff --git a/registry/client/src/versioning/dataTransform.js b/registry/client/src/versioning/dataTransform.js
new file mode 100644
index 00000000..d5369c7c
--- /dev/null
+++ b/registry/client/src/versioning/dataTransform.js
@@ -0,0 +1,7 @@
+export function transformGet(setting) {
+
+}
+
+export function transformSet(setting) {
+
+}
diff --git a/registry/client/src/versioning/index.js b/registry/client/src/versioning/index.js
new file mode 100644
index 00000000..d3a24a81
--- /dev/null
+++ b/registry/client/src/versioning/index.js
@@ -0,0 +1,10 @@
+import Icon from '@material-ui/icons/History';
+import React, {Children, cloneElement} from 'react';
+
+import List from './List';
+
+export default {
+ list: List,
+ icon: Icon,
+ options: {label: 'History'},
+};
diff --git a/registry/package.json b/registry/package.json
index f1ce4426..27b3ef05 100644
--- a/registry/package.json
+++ b/registry/package.json
@@ -51,9 +51,10 @@
"supertest": "4.0.2",
"timekeeper": "^2.2.0",
"ts-node": "^8.10.2",
- "typescript": "3.7.5"
+ "typescript": "^3.9.7"
},
"dependencies": {
+ "@namecheap/error-extender": "^1.2.0",
"axios": "^0.19.0",
"bcrypt": "^5.0.0",
"body-parser": "^1.19.0",
diff --git a/registry/server/app.ts b/registry/server/app.ts
index 02ede77c..7c12b364 100644
--- a/registry/server/app.ts
+++ b/registry/server/app.ts
@@ -38,6 +38,7 @@ export default (withAuth: boolean = true) => {
app.use('/api/v1/route', authMw, routes.appRoutes);
app.use('/api/v1/shared_props', authMw, routes.sharedProps);
app.use('/api/v1/auth_entities', authMw, routes.authEntities);
+ app.use('/api/v1/versioning', authMw, routes.versioning);
app.use('/api/v1/settings', routes.settings(authMw));
app.use(errorHandler);
diff --git a/registry/server/appRoutes/routes/createAppRoute.ts b/registry/server/appRoutes/routes/createAppRoute.ts
index eab794e7..1f73ded6 100644
--- a/registry/server/appRoutes/routes/createAppRoute.ts
+++ b/registry/server/appRoutes/routes/createAppRoute.ts
@@ -27,25 +27,27 @@ const createAppRoute = async (req: Request, res: Response) => {
...appRoute
} = req.body;
- const savedAppRouteId = await db.transaction(async (transaction) => {
- const [appRouteId] = await db('routes').insert(appRoute).transacting(transaction);
+ let savedAppRouteId: number;
+
+ await db.versioning(req.user, {type: 'routes'}, async (transaction) => {
+ [savedAppRouteId] = await db('routes').insert(appRoute).transacting(transaction);
await db.batchInsert('route_slots', _.compose(
_.map((appRouteSlotName) => _.compose(
stringifyJSON(['props']),
- _.assign({ name: appRouteSlotName, routeId: appRouteId }),
+ _.assign({ name: appRouteSlotName, routeId: savedAppRouteId }),
_.get(appRouteSlotName)
)(appRouteSlots)),
_.keys,
)(appRouteSlots)).transacting(transaction);
- return appRouteId;
+ return savedAppRouteId;
});
const savedAppRoute = await db
.select('routes.id as routeId', 'route_slots.id as routeSlotId', 'routes.*', 'route_slots.*')
.from('routes')
- .where('routeId', savedAppRouteId)
+ .where('routeId', savedAppRouteId!)
.join('route_slots', {
'route_slots.routeId': 'routes.id'
});
diff --git a/registry/server/appRoutes/routes/deleteAppRoute.ts b/registry/server/appRoutes/routes/deleteAppRoute.ts
index 35714f79..6281bee4 100644
--- a/registry/server/appRoutes/routes/deleteAppRoute.ts
+++ b/registry/server/appRoutes/routes/deleteAppRoute.ts
@@ -4,6 +4,7 @@ import {
} from 'express';
import Joi from 'joi';
import _ from 'lodash/fp';
+import * as httpErrors from '../../errorHandler/httpErrors';
import db from '../../db';
import validateRequestFactory from '../../common/services/validateRequest';
@@ -25,16 +26,15 @@ const validateRequestBeforeDeleteAppRoute = validateRequestFactory([{
const deleteAppRoute = async (req: Request, res: Response) => {
const appRouteId = req.params.id;
- const count = await db.transaction(async (transaction) => {
+ await db.versioning(req.user, {type: 'routes', id: appRouteId}, async (transaction) => {
await db('route_slots').where('routeId', appRouteId).delete().transacting(transaction);
- return await db('routes').where('id', appRouteId).delete().transacting(transaction);
+ const count = await db('routes').where('id', appRouteId).delete().transacting(transaction);
+ if (!count) {
+ throw new httpErrors.NotFoundError()
+ }
});
- if (count) {
- res.status(204).send();
- } else {
- res.status(404).send('Not found');
- }
+ res.status(204).send();
};
export default [validateRequestBeforeDeleteAppRoute, deleteAppRoute];
diff --git a/registry/server/appRoutes/routes/updateAppRoute.ts b/registry/server/appRoutes/routes/updateAppRoute.ts
index 985e316b..3cfb6f51 100644
--- a/registry/server/appRoutes/routes/updateAppRoute.ts
+++ b/registry/server/appRoutes/routes/updateAppRoute.ts
@@ -50,7 +50,7 @@ const updateAppRoute = async (req: Request, res: Re
return;
}
- await db.transaction(async (transaction) => {
+ await db.versioning(req.user, {type: 'routes', id: appRouteId}, async (transaction) => {
await db('routes').where('id', appRouteId).update(appRoute).transacting(transaction);
if (!_.isEmpty(appRouteSlots)) {
diff --git a/registry/server/apps/routes/createApp.ts b/registry/server/apps/routes/createApp.ts
index 2d3cb707..20c9669d 100644
--- a/registry/server/apps/routes/createApp.ts
+++ b/registry/server/apps/routes/createApp.ts
@@ -23,7 +23,11 @@ const validateRequestBeforeCreateApp = validateRequestFactory([{
const createApp = async (req: Request, res: Response): Promise => {
const app = req.body;
- await db('apps').insert(stringifyJSON(['dependencies', 'props', 'ssr', 'configSelector'], app));
+ await db.versioning(req.user, {type: 'apps', id: app.name}, async (trx) => {
+ await db('apps')
+ .insert(stringifyJSON(['dependencies', 'props', 'ssr', 'configSelector'], app))
+ .transacting(trx);
+ });
const [savedApp] = await db.select().from('apps').where('name', app.name);
diff --git a/registry/server/apps/routes/deleteApp.ts b/registry/server/apps/routes/deleteApp.ts
index b732dc1d..8c3703fb 100644
--- a/registry/server/apps/routes/deleteApp.ts
+++ b/registry/server/apps/routes/deleteApp.ts
@@ -4,6 +4,7 @@ import {
} from 'express';
import Joi from 'joi';
import _ from 'lodash/fp';
+import * as httpErrors from '../../errorHandler/httpErrors';
import db from '../../db';
import validateRequestFactory from '../../common/services/validateRequest';
@@ -25,13 +26,14 @@ const validateRequestBeforeDeleteApp = validateRequestFactory([{
const deleteApp = async (req: Request, res: Response): Promise => {
const appName = req.params.name;
- const count = await db('apps').where('name', appName).delete();
+ await db.versioning(req.user, {type: 'apps', id: appName}, async (trx) => {
+ const count = await db('apps').where('name', appName).delete().transacting(trx);
+ if (!count) {
+ throw new httpErrors.NotFoundError()
+ }
+ });
- if (count) {
- res.status(204).send();
- } else {
- res.status(404).send('Not found');
- }
+ res.status(204).send();
};
export default [validateRequestBeforeDeleteApp, deleteApp];
diff --git a/registry/server/apps/routes/updateApp.ts b/registry/server/apps/routes/updateApp.ts
index 59c988bb..a17395d7 100644
--- a/registry/server/apps/routes/updateApp.ts
+++ b/registry/server/apps/routes/updateApp.ts
@@ -44,7 +44,12 @@ const updateApp = async (req: Request, res: Response): P
return;
}
- await db('apps').where({ name: appName }).update(stringifyJSON(['dependencies', 'props', 'ssr', 'configSelector'], app));
+ await db.versioning(req.user, {type: 'apps', id: appName}, async (trx) => {
+ await db('apps')
+ .where({ name: appName })
+ .update(stringifyJSON(['dependencies', 'props', 'ssr', 'configSelector'], app))
+ .transacting(trx);
+ });
const [updatedApp] = await db.select().from('apps').where('name', appName);
diff --git a/registry/server/auth.ts b/registry/server/auth.ts
index 8326c143..b414e664 100644
--- a/registry/server/auth.ts
+++ b/registry/server/auth.ts
@@ -12,6 +12,12 @@ import {SettingsService} from "./settings/services/SettingsService";
import {SettingKeys} from "./settings/interfaces";
import urljoin from 'url-join';
+export interface User {
+ authEntityId: number;
+ identifier: string;
+ role: string;
+}
+
export default (app: Express, settingsService: SettingsService, config: any): RequestHandler => {
const SessionKnex = sessionKnex(session);
const sessionConfig = Object.assign({
@@ -241,7 +247,7 @@ export default (app: Express, settingsService: SettingsService, config: any): Re
};
}
-async function getEntityWithCreds(provider: string, identifier: string, secret: string|null):Promise